Repository: darkreader/darkreader
Pull request #2304 (2020-04-17 14:54:49)

fix all quickbase form pages


Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 7d1fb0c1..b6f98a3e 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -8,6 +8,10 @@ module.exports = {
         'indent': ['error', 4, {
             'SwitchCase': 1,
         }],
+        'keyword-spacing': ['error', {
+            after: true,
+            before: true,
+        }],
         'object-curly-spacing': ['error', 'never'],
         'no-multi-spaces': 'error',
         'no-trailing-spaces': 'error',
@@ -15,11 +19,15 @@ module.exports = {
         'semi': ['error', 'always'],
         'space-before-function-paren': ['error', {
             anonymous: 'always',
-            named: 'never',
             asyncArrow: 'always',
+            named: 'never',
         }],
         'space-in-parens': ['error', 'never'],
         'spaced-comment': ['error', 'always', {exceptions: ['-']}],
+        'quotes': ['error', 'single', {
+            allowTemplateLiterals: true,
+            avoidEscape: true,
+        }],
         '@typescript-eslint/camelcase': 'off',
         '@typescript-eslint/explicit-function-return-type': 'off',
         '@typescript-eslint/explicit-member-accessibility': 'off',
diff --git a/.github/ISSUE_TEMPLATE/broken-website.md b/.github/ISSUE_TEMPLATE/broken-website.md
new file mode 100644
index 00000000..e92c6a1c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/broken-website.md
@@ -0,0 +1,40 @@
+---
+name: Broken website
+about: Creates a broken website report about Darkreader
+title: "[Broken Website] "
+labels: Broken Website
+assignees: ''
+
+---
+
+<!--
+Thank you for taking the time to report a broken website.
+Please make sure there is no existing issue about this broken website.
+-->
+
+**Website**
+Give the link to the website
+
+**How to Reproduce**
+Steps to reproduce the behavior:
+An example for this is
+> 1. Go to x site
+> 2. Hover over x button
+> 3. See that when hovering it isn't changing colors
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Actual behavior**
+A clear and concise description of what actual happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**System info:**
+ - OS: [e.g. Windows, MacOS, Linux]
+ - Browser [e.g. chrome, safari]
+ - Darkreader Version [e.g. 4.9.4]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..2cfbf370
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,37 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG]"
+labels: bug
+assignees: ''
+
+---
+
+<!--
+Thank you for taking the time to report a bug.
+Please make sure there is no existing issue about this kind of bug.
+-->
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+> Go to '...'
+> Click on '....'
+> Scroll down to '....'
+> See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**System Information:**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..1e60b680
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,21 @@
+---
+name: Feature request
+about: Suggest a new Feature for Darkreader
+title: "[Feature Request]"
+labels: enhancement
+assignees: ''
+
+---
+<!--
+Thank you for taking the time to suggest a feature request.
+Please make sure there is no existing issue about this kind of feature.
+-->
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/README.md b/README.md
index e216221a..37166ec2 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
 # Dark Reader for Google Chrome and Mozilla Firefox
+[![Build Status](https://travis-ci.org/darkreader/darkreader.svg?branch=master)](https://travis-ci.org/darkreader/darkreader)
+
 ![Dark Reader screenshot](https://lh3.googleusercontent.com/tTJIIIAqfJWymqPM9LAmivl11kWmF-XXLABues4OwfjOED_ntsJZdLY0VTG0XFCW0W_wYSll7Q=w640-h400-e365)
 
 This extension **inverts brightness** of web pages and aims to **reduce eyestrain** while browsing the web.
diff --git a/package-lock.json b/package-lock.json
index 6a96bc08..18259cfa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "darkreader",
-  "version": "4.9.4",
+  "version": "4.9.7",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -434,17 +434,30 @@
       "dev": true
     },
     "@jest/console": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.3.0.tgz",
-      "integrity": "sha512-LvSDNqpmZIZyweFaEQ6wKY7CbexPitlsLHGJtcooNECo0An/w49rFhjCJzu6efeb6+a3ee946xss1Jcd9r03UQ==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.4.0.tgz",
+      "integrity": "sha512-CfE0erx4hdJ6t7RzAcE1wLG6ZzsHSmybvIBQDoCkDM1QaSeWL9wJMzID/2BbHHa7ll9SsbbK43HjbERbBaFX2A==",
       "dev": true,
       "requires": {
-        "@jest/source-map": "^25.2.6",
+        "@jest/types": "^25.4.0",
         "chalk": "^3.0.0",
-        "jest-util": "^25.3.0",
+        "jest-message-util": "^25.4.0",
+        "jest-util": "^25.4.0",
         "slash": "^3.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -498,33 +511,33 @@
       }
     },
     "@jest/core": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.3.0.tgz",
-      "integrity": "sha512-+D5a/tFf6pA/Gqft2DLBp/yeSRgXhlJ+Wpst0X/ZkfTRP54qDR3C61VfHwaex+GzZBiTcE9vQeoZ2v5T10+Mqw==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.4.0.tgz",
+      "integrity": "sha512-h1x9WSVV0+TKVtATGjyQIMJENs8aF6eUjnCoi4jyRemYZmekLr8EJOGQqTWEX8W6SbZ6Skesy9pGXrKeAolUJw==",
       "dev": true,
       "requires": {
-        "@jest/console": "^25.3.0",
-        "@jest/reporters": "^25.3.0",
-        "@jest/test-result": "^25.3.0",
-        "@jest/transform": "^25.3.0",
-        "@jest/types": "^25.3.0",
+        "@jest/console": "^25.4.0",
+        "@jest/reporters": "^25.4.0",
+        "@jest/test-result": "^25.4.0",
+        "@jest/transform": "^25.4.0",
+        "@jest/types": "^25.4.0",
         "ansi-escapes": "^4.2.1",
         "chalk": "^3.0.0",
         "exit": "^0.1.2",
         "graceful-fs": "^4.2.3",
-        "jest-changed-files": "^25.3.0",
-        "jest-config": "^25.3.0",
-        "jest-haste-map": "^25.3.0",
-        "jest-message-util": "^25.3.0",
+        "jest-changed-files": "^25.4.0",
+        "jest-config": "^25.4.0",
+        "jest-haste-map": "^25.4.0",
+        "jest-message-util": "^25.4.0",
         "jest-regex-util": "^25.2.6",
-        "jest-resolve": "^25.3.0",
-        "jest-resolve-dependencies": "^25.3.0",
-        "jest-runner": "^25.3.0",
-        "jest-runtime": "^25.3.0",
-        "jest-snapshot": "^25.3.0",
-        "jest-util": "^25.3.0",
-        "jest-validate": "^25.3.0",
-        "jest-watcher": "^25.3.0",
+        "jest-resolve": "^25.4.0",
+        "jest-resolve-dependencies": "^25.4.0",
+        "jest-runner": "^25.4.0",
+        "jest-runtime": "^25.4.0",
+        "jest-snapshot": "^25.4.0",
+        "jest-util": "^25.4.0",
+        "jest-validate": "^25.4.0",
+        "jest-watcher": "^25.4.0",
         "micromatch": "^4.0.2",
         "p-each-series": "^2.1.0",
         "realpath-native": "^2.0.0",
@@ -533,6 +546,18 @@
         "strip-ansi": "^6.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -610,40 +635,168 @@
       }
     },
     "@jest/environment": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.3.0.tgz",
-      "integrity": "sha512-vgooqwJTHLLak4fE+TaCGeYP7Tz1Y3CKOsNxR1sE0V3nx3KRUHn3NUnt+wbcfd5yQWKZQKAfW6wqbuwQLrXo3g==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.4.0.tgz",
+      "integrity": "sha512-KDctiak4mu7b4J6BIoN/+LUL3pscBzoUCP+EtSPd2tK9fqyDY5OF+CmkBywkFWezS9tyH5ACOQNtpjtueEDH6Q==",
       "dev": true,
       "requires": {
-        "@jest/fake-timers": "^25.3.0",
-        "@jest/types": "^25.3.0",
-        "jest-mock": "^25.3.0"
+        "@jest/fake-timers": "^25.4.0",
+        "@jest/types": "^25.4.0",
+        "jest-mock": "^25.4.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
     "@jest/fake-timers": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.3.0.tgz",
-      "integrity": "sha512-NHAj7WbsyR3qBJPpBwSwqaq2WluIvUQsyzpJTN7XDVk7VnlC/y1BAnaYZL3vbPIP8Nhm0Ae5DJe0KExr/SdMJQ==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.4.0.tgz",
+      "integrity": "sha512-lI9z+VOmVX4dPPFzyj0vm+UtaB8dCJJ852lcDnY0uCPRvZAaVGnMwBBc1wxtf+h7Vz6KszoOvKAt4QijDnHDkg==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
-        "jest-message-util": "^25.3.0",
-        "jest-mock": "^25.3.0",
-        "jest-util": "^25.3.0",
+        "@jest/types": "^25.4.0",
+        "jest-message-util": "^25.4.0",
+        "jest-mock": "^25.4.0",
+        "jest-util": "^25.4.0",
         "lolex": "^5.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
     "@jest/reporters": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.3.0.tgz",
-      "integrity": "sha512-1u0ZBygs0C9DhdYgLCrRfZfNKQa+9+J7Uo+Z9z0RWLHzgsxhoG32lrmMOtUw48yR6bLNELdvzormwUqSk4H4Vg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.4.0.tgz",
+      "integrity": "sha512-bhx/buYbZgLZm4JWLcRJ/q9Gvmd3oUh7k2V7gA4ZYBx6J28pIuykIouclRdiAC6eGVX1uRZT+GK4CQJLd/PwPg==",
       "dev": true,
       "requires": {
         "@bcoe/v8-coverage": "^0.2.3",
-        "@jest/console": "^25.3.0",
-        "@jest/test-result": "^25.3.0",
-        "@jest/transform": "^25.3.0",
-        "@jest/types": "^25.3.0",
+        "@jest/console": "^25.4.0",
+        "@jest/test-result": "^25.4.0",
+        "@jest/transform": "^25.4.0",
+        "@jest/types": "^25.4.0",
         "chalk": "^3.0.0",
         "collect-v8-coverage": "^1.0.0",
         "exit": "^0.1.2",
@@ -653,18 +806,30 @@
         "istanbul-lib-report": "^3.0.0",
         "istanbul-lib-source-maps": "^4.0.0",
         "istanbul-reports": "^3.0.2",
-        "jest-haste-map": "^25.3.0",
-        "jest-resolve": "^25.3.0",
-        "jest-util": "^25.3.0",
-        "jest-worker": "^25.2.6",
+        "jest-haste-map": "^25.4.0",
+        "jest-resolve": "^25.4.0",
+        "jest-util": "^25.4.0",
+        "jest-worker": "^25.4.0",
         "node-notifier": "^6.0.0",
         "slash": "^3.0.0",
         "source-map": "^0.6.0",
         "string-length": "^3.1.0",
         "terminal-link": "^2.0.0",
-        "v8-to-istanbul": "^4.0.1"
+        "v8-to-istanbul": "^4.1.3"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -737,45 +902,109 @@
       }
     },
     "@jest/test-result": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.3.0.tgz",
-      "integrity": "sha512-mqrGuiiPXl1ap09Mydg4O782F3ouDQfsKqtQzIjitpwv3t1cHDwCto21jThw6WRRE+dKcWQvLG70GpyLJICfGw==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.4.0.tgz",
+      "integrity": "sha512-8BAKPaMCHlL941eyfqhWbmp3MebtzywlxzV+qtngQ3FH+RBqnoSAhNEPj4MG7d2NVUrMOVfrwuzGpVIK+QnMAA==",
       "dev": true,
       "requires": {
-        "@jest/console": "^25.3.0",
-        "@jest/types": "^25.3.0",
+        "@jest/console": "^25.4.0",
+        "@jest/types": "^25.4.0",
         "@types/istanbul-lib-coverage": "^2.0.0",
         "collect-v8-coverage": "^1.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
     "@jest/test-sequencer": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.3.0.tgz",
-      "integrity": "sha512-Xvns3xbji7JCvVcDGvqJ/pf4IpmohPODumoPEZJ0/VgC5gI4XaNVIBET2Dq5Czu6Gk3xFcmhtthh/MBOTljdNg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.4.0.tgz",
+      "integrity": "sha512-240cI+nsM3attx2bMp9uGjjHrwrpvxxrZi8Tyqp/cfOzl98oZXVakXBgxODGyBYAy/UGXPKXLvNc2GaqItrsJg==",
       "dev": true,
       "requires": {
-        "@jest/test-result": "^25.3.0",
-        "jest-haste-map": "^25.3.0",
-        "jest-runner": "^25.3.0",
-        "jest-runtime": "^25.3.0"
+        "@jest/test-result": "^25.4.0",
+        "jest-haste-map": "^25.4.0",
+        "jest-runner": "^25.4.0",
+        "jest-runtime": "^25.4.0"
       }
     },
     "@jest/transform": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.3.0.tgz",
-      "integrity": "sha512-W01p8kTDvvEX6kd0tJc7Y5VdYyFaKwNWy1HQz6Jqlhu48z/8Gxp+yFCDVj+H8Rc7ezl3Mg0hDaGuFVkmHOqirg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.4.0.tgz",
+      "integrity": "sha512-t1w2S6V1sk++1HHsxboWxPEuSpN8pxEvNrZN+Ud/knkROWtf8LeUmz73A4ezE8476a5AM00IZr9a8FO9x1+j3g==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.1.0",
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "babel-plugin-istanbul": "^6.0.0",
         "chalk": "^3.0.0",
         "convert-source-map": "^1.4.0",
         "fast-json-stable-stringify": "^2.0.0",
         "graceful-fs": "^4.2.3",
-        "jest-haste-map": "^25.3.0",
+        "jest-haste-map": "^25.4.0",
         "jest-regex-util": "^25.2.6",
-        "jest-util": "^25.3.0",
+        "jest-util": "^25.4.0",
         "micromatch": "^4.0.2",
         "pirates": "^4.0.1",
         "realpath-native": "^2.0.0",
@@ -784,6 +1013,18 @@
         "write-file-atomic": "^3.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -1000,9 +1241,9 @@
       }
     },
     "@types/chrome": {
-      "version": "0.0.104",
-      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.104.tgz",
-      "integrity": "sha512-NDq+Vpphs5XT7IXrIHJcy27Rvf09Mes3XNsfpP2yOXUk6YiEU1QRcSLjj/A+y5jyq/kpUwC5shNET2jFeWAqEw==",
+      "version": "0.0.106",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.106.tgz",
+      "integrity": "sha512-sCQa+QJL/Kl+6ngU4xD1VjTFIkQDizOHN94zbb7byK9D3U79x/KXjqXyS7lHoa2q9xw3bWePb+giLhBKOILRuA==",
       "dev": true,
       "requires": {
         "@types/filesystem": "*",
@@ -1038,7 +1279,7 @@
     },
     "@types/filewriter": {
       "version": "0.0.28",
-      "resolved": "http://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.28.tgz",
+      "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.28.tgz",
       "integrity": "sha1-wFTor02d11205jq8dviFFocU1LM=",
       "dev": true
     },
@@ -1096,9 +1337,15 @@
       "dev": true
     },
     "@types/node": {
-      "version": "13.11.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.1.tgz",
-      "integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==",
+      "version": "13.13.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz",
+      "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==",
+      "dev": true
+    },
+    "@types/normalize-package-data": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+      "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
       "dev": true
     },
     "@types/prettier": {
@@ -1138,25 +1385,25 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "2.28.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.28.0.tgz",
-      "integrity": "sha512-w0Ugcq2iatloEabQP56BRWJowliXUP5Wv6f9fKzjJmDW81hOTBxRoJ4LoEOxRpz9gcY51Libytd2ba3yLmSOfg==",
+      "version": "2.30.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.30.0.tgz",
+      "integrity": "sha512-PGejii0qIZ9Q40RB2jIHyUpRWs1GJuHP1pkoCiaeicfwO9z7Fx03NQzupuyzAmv+q9/gFNHu7lo1ByMXe8PNyg==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "2.28.0",
+        "@typescript-eslint/experimental-utils": "2.30.0",
         "functional-red-black-tree": "^1.0.1",
         "regexpp": "^3.0.0",
         "tsutils": "^3.17.1"
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "2.28.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.28.0.tgz",
-      "integrity": "sha512-4SL9OWjvFbHumM/Zh/ZeEjUFxrYKtdCi7At4GyKTbQlrj1HcphIDXlje4Uu4cY+qzszR5NdVin4CCm6AXCjd6w==",
+      "version": "2.30.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.30.0.tgz",
+      "integrity": "sha512-L3/tS9t+hAHksy8xuorhOzhdefN0ERPDWmR9CclsIGOUqGKy6tqc/P+SoXeJRye5gazkuPO0cK9MQRnolykzkA==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
-        "@typescript-eslint/typescript-estree": "2.28.0",
+        "@typescript-eslint/typescript-estree": "2.30.0",
         "eslint-scope": "^5.0.0",
         "eslint-utils": "^2.0.0"
       },
@@ -1173,21 +1420,21 @@
       }
     },
     "@typescript-eslint/parser": {
-      "version": "2.28.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.28.0.tgz",
-      "integrity": "sha512-RqPybRDquui9d+K86lL7iPqH6Dfp9461oyqvlXMNtap+PyqYbkY5dB7LawQjDzot99fqzvS0ZLZdfe+1Bt3Jgw==",
+      "version": "2.30.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.30.0.tgz",
+      "integrity": "sha512-9kDOxzp0K85UnpmPJqUzdWaCNorYYgk1yZmf4IKzpeTlSAclnFsrLjfwD9mQExctLoLoGAUXq1co+fbr+3HeFw==",
       "dev": true,
       "requires": {
         "@types/eslint-visitor-keys": "^1.0.0",
-        "@typescript-eslint/experimental-utils": "2.28.0",
-        "@typescript-eslint/typescript-estree": "2.28.0",
+        "@typescript-eslint/experimental-utils": "2.30.0",
+        "@typescript-eslint/typescript-estree": "2.30.0",
         "eslint-visitor-keys": "^1.1.0"
       }
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "2.28.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.28.0.tgz",
-      "integrity": "sha512-HDr8MP9wfwkiuqzRVkuM3BeDrOC4cKbO5a6BymZBHUt5y/2pL0BXD6I/C/ceq2IZoHWhcASk+5/zo+dwgu9V8Q==",
+      "version": "2.30.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.30.0.tgz",
+      "integrity": "sha512-nI5WOechrA0qAhnr+DzqwmqHsx7Ulr/+0H7bWCcClDhhWkSyZR5BmTvnBEyONwJCTWHfc5PAQExX24VD26IAVw==",
       "dev": true,
       "requires": {
         "debug": "^4.1.1",
@@ -1433,20 +1680,32 @@
       "dev": true
     },
     "babel-jest": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.3.0.tgz",
-      "integrity": "sha512-qiXeX1Cmw4JZ5yQ4H57WpkO0MZ61Qj+YnsVUwAMnDV5ls+yHon11XjarDdgP7H8lTmiEi6biiZA8y3Tmvx6pCg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.4.0.tgz",
+      "integrity": "sha512-p+epx4K0ypmHuCnd8BapfyOwWwosNCYhedetQey1awddtfmEX0MmdxctGl956uwUmjwXR5VSS5xJcGX9DvdIog==",
       "dev": true,
       "requires": {
-        "@jest/transform": "^25.3.0",
-        "@jest/types": "^25.3.0",
+        "@jest/transform": "^25.4.0",
+        "@jest/types": "^25.4.0",
         "@types/babel__core": "^7.1.7",
         "babel-plugin-istanbul": "^6.0.0",
-        "babel-preset-jest": "^25.3.0",
+        "babel-preset-jest": "^25.4.0",
         "chalk": "^3.0.0",
         "slash": "^3.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -1513,9 +1772,9 @@
       }
     },
     "babel-plugin-jest-hoist": {
-      "version": "25.2.6",
-      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.6.tgz",
-      "integrity": "sha512-qE2xjMathybYxjiGFJg0mLFrz0qNp83aNZycWDY/SuHiZNq+vQfRQtuINqyXyue1ELd8Rd+1OhFSLjms8msMbw==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.4.0.tgz",
+      "integrity": "sha512-M3a10JCtTyKevb0MjuH6tU+cP/NVQZ82QPADqI1RQYY1OphztsCeIeQmTsHmF/NS6m0E51Zl4QNsI3odXSQF5w==",
       "dev": true,
       "requires": {
         "@types/babel__traverse": "^7.0.6"
@@ -1540,12 +1799,12 @@
       }
     },
     "babel-preset-jest": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.3.0.tgz",
-      "integrity": "sha512-tjdvLKNMwDI9r+QWz9sZUQGTq1dpoxjUqFUpEasAc7MOtHg9XuLT2fx0udFG+k1nvMV0WvHHVAN7VmCZ+1Zxbw==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.4.0.tgz",
+      "integrity": "sha512-PwFiEWflHdu3JCeTr0Pb9NcHHE34qWFnPQRVPvqQITx4CsDCzs6o05923I10XvLvn9nNsRHuiVgB72wG/90ZHQ==",
       "dev": true,
       "requires": {
-        "babel-plugin-jest-hoist": "^25.2.6",
+        "babel-plugin-jest-hoist": "^25.4.0",
         "babel-preset-current-node-syntax": "^0.1.2"
       }
     },
@@ -1765,9 +2024,9 @@
       "dev": true
     },
     "chokidar": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
-      "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
+      "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
       "dev": true,
       "requires": {
         "anymatch": "~3.1.1",
@@ -1777,7 +2036,7 @@
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
         "normalize-path": "~3.0.0",
-        "readdirp": "~3.3.0"
+        "readdirp": "~3.4.0"
       }
     },
     "ci-info": {
@@ -2153,6 +2412,15 @@
         "prr": "~1.0.1"
       }
     },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -2416,19 +2684,31 @@
       }
     },
     "expect": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/expect/-/expect-25.3.0.tgz",
-      "integrity": "sha512-buboTXML2h/L0Kh44Ys2Cx49mX20ISc5KDirkxIs3Q9AJv0kazweUAbukegr+nHDOvFRKmxdojjIHCjqAceYfg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-25.4.0.tgz",
+      "integrity": "sha512-7BDIX99BTi12/sNGJXA9KMRcby4iAmu1xccBOhyKCyEhjcVKS3hPmHdA/4nSI9QGIOkUropKqr3vv7WMDM5lvQ==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "ansi-styles": "^4.0.0",
         "jest-get-type": "^25.2.6",
-        "jest-matcher-utils": "^25.3.0",
-        "jest-message-util": "^25.3.0",
+        "jest-matcher-utils": "^25.4.0",
+        "jest-message-util": "^25.4.0",
         "jest-regex-util": "^25.2.6"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -2439,6 +2719,16 @@
             "color-convert": "^2.0.1"
           }
         },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
         "color-convert": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2453,6 +2743,21 @@
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
           "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
           "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
@@ -2925,6 +3230,12 @@
         }
       }
     },
+    "hosted-git-info": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+      "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+      "dev": true
+    },
     "html-encoding-sniffer": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
@@ -3079,6 +3390,12 @@
         }
       }
     },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
     "is-binary-path": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -3339,16 +3656,28 @@
       }
     },
     "jest": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest/-/jest-25.3.0.tgz",
-      "integrity": "sha512-iKd5ShQSHzFT5IL/6h5RZJhApgqXSoPxhp5HEi94v6OAw9QkF8T7X+liEU2eEHJ1eMFYTHmeWLrpBWulsDpaUg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-25.4.0.tgz",
+      "integrity": "sha512-XWipOheGB4wai5JfCYXd6vwsWNwM/dirjRoZgAa7H2wd8ODWbli2AiKjqG8AYhyx+8+5FBEdpO92VhGlBydzbw==",
       "dev": true,
       "requires": {
-        "@jest/core": "^25.3.0",
+        "@jest/core": "^25.4.0",
         "import-local": "^3.0.2",
-        "jest-cli": "^25.3.0"
+        "jest-cli": "^25.4.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -3391,21 +3720,21 @@
           "dev": true
         },
         "jest-cli": {
-          "version": "25.3.0",
-          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.3.0.tgz",
-          "integrity": "sha512-XpNQPlW1tzpP7RGG8dxpkRegYDuLjzSiENu92+CYM87nEbmEPb3b4+yo8xcsHOnj0AG7DUt9b3uG8LuHI3MDzw==",
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.4.0.tgz",
+          "integrity": "sha512-usyrj1lzCJZMRN1r3QEdnn8e6E6yCx/QN7+B1sLoA68V7f3WlsxSSQfy0+BAwRiF4Hz2eHauf11GZG3PIfWTXQ==",
           "dev": true,
           "requires": {
-            "@jest/core": "^25.3.0",
-            "@jest/test-result": "^25.3.0",
-            "@jest/types": "^25.3.0",
+            "@jest/core": "^25.4.0",
+            "@jest/test-result": "^25.4.0",
+            "@jest/types": "^25.4.0",
             "chalk": "^3.0.0",
             "exit": "^0.1.2",
             "import-local": "^3.0.2",
             "is-ci": "^2.0.0",
-            "jest-config": "^25.3.0",
-            "jest-util": "^25.3.0",
-            "jest-validate": "^25.3.0",
+            "jest-config": "^25.4.0",
+            "jest-util": "^25.4.0",
+            "jest-validate": "^25.4.0",
             "prompts": "^2.0.1",
             "realpath-native": "^2.0.0",
             "yargs": "^15.3.1"
@@ -3423,16 +3752,63 @@
       }
     },
     "jest-changed-files": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.3.0.tgz",
-      "integrity": "sha512-eqd5hyLbUjIVvLlJ3vQ/MoPxsxfESVXG9gvU19XXjKzxr+dXmZIqCXiY0OiYaibwlHZBJl2Vebkc0ADEMzCXew==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.4.0.tgz",
+      "integrity": "sha512-VR/rfJsEs4BVMkwOTuStRyS630fidFVekdw/lBaBQjx9KK3VZFOZ2c0fsom2fRp8pMCrCTP6LGna00o/DXGlqA==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "execa": "^3.2.0",
         "throat": "^5.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
         "cross-spawn": {
           "version": "7.0.2",
           "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
@@ -3471,6 +3847,12 @@
             "pump": "^3.0.0"
           }
         },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
         "is-stream": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
@@ -3513,6 +3895,15 @@
           "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
           "dev": true
         },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
         "which": {
           "version": "2.0.2",
           "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3525,31 +3916,43 @@
       }
     },
     "jest-config": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.3.0.tgz",
-      "integrity": "sha512-CmF1JnNWFmoCSPC4tnU52wnVBpuxHjilA40qH/03IHxIevkjUInSMwaDeE6ACfxMPTLidBGBCO3EbxvzPbo8wA==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.4.0.tgz",
+      "integrity": "sha512-egT9aKYxMyMSQV1aqTgam0SkI5/I2P9qrKexN5r2uuM2+68ypnc+zPGmfUxK7p1UhE7dYH9SLBS7yb+TtmT1AA==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.1.0",
-        "@jest/test-sequencer": "^25.3.0",
-        "@jest/types": "^25.3.0",
-        "babel-jest": "^25.3.0",
+        "@jest/test-sequencer": "^25.4.0",
+        "@jest/types": "^25.4.0",
+        "babel-jest": "^25.4.0",
         "chalk": "^3.0.0",
         "deepmerge": "^4.2.2",
         "glob": "^7.1.1",
-        "jest-environment-jsdom": "^25.3.0",
-        "jest-environment-node": "^25.3.0",
+        "jest-environment-jsdom": "^25.4.0",
+        "jest-environment-node": "^25.4.0",
         "jest-get-type": "^25.2.6",
-        "jest-jasmine2": "^25.3.0",
+        "jest-jasmine2": "^25.4.0",
         "jest-regex-util": "^25.2.6",
-        "jest-resolve": "^25.3.0",
-        "jest-util": "^25.3.0",
-        "jest-validate": "^25.3.0",
+        "jest-resolve": "^25.4.0",
+        "jest-util": "^25.4.0",
+        "jest-validate": "^25.4.0",
         "micromatch": "^4.0.2",
-        "pretty-format": "^25.3.0",
+        "pretty-format": "^25.4.0",
         "realpath-native": "^2.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -3591,6 +3994,18 @@
           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
+        "pretty-format": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+          "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^25.4.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
         "supports-color": {
           "version": "7.1.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -3676,18 +4091,30 @@
       }
     },
     "jest-each": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.3.0.tgz",
-      "integrity": "sha512-aBfS4VOf/Qs95yUlX6d6WBv0szvOcTkTTyCIaLuQGj4bSHsT+Wd9dDngVHrCe5uytxpN8VM+NAloI6nbPjXfXw==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.4.0.tgz",
+      "integrity": "sha512-lwRIJ8/vQU/6vq3nnSSUw1Y3nz5tkYSFIywGCZpUBd6WcRgpn8NmJoQICojbpZmsJOJNHm0BKdyuJ6Xdx+eDQQ==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "chalk": "^3.0.0",
         "jest-get-type": "^25.2.6",
-        "jest-util": "^25.3.0",
-        "pretty-format": "^25.3.0"
+        "jest-util": "^25.4.0",
+        "pretty-format": "^25.4.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -3729,6 +4156,18 @@
           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
+        "pretty-format": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+          "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^25.4.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
         "supports-color": {
           "version": "7.1.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -3741,38 +4180,164 @@
       }
     },
     "jest-environment-jsdom": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.3.0.tgz",
-      "integrity": "sha512-jdE4bQN+k2QEZ9sWOxsqDJvMzbdFSCN/4tw8X0TQaCqyzKz58PyEf41oIr4WO7ERdp7WaJGBSUKF7imR3UW1lg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.4.0.tgz",
+      "integrity": "sha512-KTitVGMDrn2+pt7aZ8/yUTuS333w3pWt1Mf88vMntw7ZSBNDkRS6/4XLbFpWXYfWfp1FjcjQTOKzbK20oIehWQ==",
       "dev": true,
       "requires": {
-        "@jest/environment": "^25.3.0",
-        "@jest/fake-timers": "^25.3.0",
-        "@jest/types": "^25.3.0",
-        "jest-mock": "^25.3.0",
-        "jest-util": "^25.3.0",
+        "@jest/environment": "^25.4.0",
+        "@jest/fake-timers": "^25.4.0",
+        "@jest/types": "^25.4.0",
+        "jest-mock": "^25.4.0",
+        "jest-util": "^25.4.0",
         "jsdom": "^15.2.1"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
     "jest-environment-node": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.3.0.tgz",
-      "integrity": "sha512-XO09S29Nx1NU7TiMPHMoDIkxoGBuKSTbE+sHp0gXbeLDXhIdhysUI25kOqFFSD9AuDgvPvxWCXrvNqiFsOH33g==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.4.0.tgz",
+      "integrity": "sha512-wryZ18vsxEAKFH7Z74zi/y/SyI1j6UkVZ6QsllBuT/bWlahNfQjLNwFsgh/5u7O957dYFoXj4yfma4n4X6kU9A==",
       "dev": true,
       "requires": {
-        "@jest/environment": "^25.3.0",
-        "@jest/fake-timers": "^25.3.0",
-        "@jest/types": "^25.3.0",
-        "jest-mock": "^25.3.0",
-        "jest-util": "^25.3.0",
+        "@jest/environment": "^25.4.0",
+        "@jest/fake-timers": "^25.4.0",
+        "@jest/types": "^25.4.0",
+        "jest-mock": "^25.4.0",
+        "jest-util": "^25.4.0",
         "semver": "^6.3.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
         "semver": {
           "version": "6.3.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
           "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
           "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
@@ -3783,67 +4348,315 @@
       "dev": true
     },
     "jest-haste-map": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.3.0.tgz",
-      "integrity": "sha512-LjXaRa+F8wwtSxo9G+hHD/Cp63PPQzvaBL9XCVoJD2rrcJO0Zr2+YYzAFWWYJ5GlPUkoaJFJtOuk0sL6MJY80A==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.4.0.tgz",
+      "integrity": "sha512-5EoCe1gXfGC7jmXbKzqxESrgRcaO3SzWXGCnvp9BcT0CFMyrB1Q6LIsjl9RmvmJGQgW297TCfrdgiy574Rl9HQ==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "anymatch": "^3.0.3",
         "fb-watchman": "^2.0.0",
         "fsevents": "^2.1.2",
         "graceful-fs": "^4.2.3",
         "jest-serializer": "^25.2.6",
-        "jest-util": "^25.3.0",
-        "jest-worker": "^25.2.6",
+        "jest-util": "^25.4.0",
+        "jest-worker": "^25.4.0",
         "micromatch": "^4.0.2",
         "sane": "^4.0.3",
         "walker": "^1.0.7",
         "which": "^2.0.2"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
         "graceful-fs": {
           "version": "4.2.3",
           "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
           "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
           "dev": true
         },
-        "which": {
-          "version": "2.0.2",
-          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "which": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+          "dev": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        }
+      }
+    },
+    "jest-jasmine2": {
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.4.0.tgz",
+      "integrity": "sha512-QccxnozujVKYNEhMQ1vREiz859fPN/XklOzfQjm2j9IGytAkUbSwjFRBtQbHaNZ88cItMpw02JnHGsIdfdpwxQ==",
+      "dev": true,
+      "requires": {
+        "@babel/traverse": "^7.1.0",
+        "@jest/environment": "^25.4.0",
+        "@jest/source-map": "^25.2.6",
+        "@jest/test-result": "^25.4.0",
+        "@jest/types": "^25.4.0",
+        "chalk": "^3.0.0",
+        "co": "^4.6.0",
+        "expect": "^25.4.0",
+        "is-generator-fn": "^2.0.0",
+        "jest-each": "^25.4.0",
+        "jest-matcher-utils": "^25.4.0",
+        "jest-message-util": "^25.4.0",
+        "jest-runtime": "^25.4.0",
+        "jest-snapshot": "^25.4.0",
+        "jest-util": "^25.4.0",
+        "pretty-format": "^25.4.0",
+        "throat": "^5.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "pretty-format": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+          "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^25.4.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "jest-leak-detector": {
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.4.0.tgz",
+      "integrity": "sha512-7Y6Bqfv2xWsB+7w44dvZuLs5SQ//fzhETgOGG7Gq3TTGFdYvAgXGwV8z159RFZ6fXiCPm/szQ90CyfVos9JIFQ==",
+      "dev": true,
+      "requires": {
+        "jest-get-type": "^25.2.6",
+        "pretty-format": "^25.4.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "pretty-format": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+          "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
           "dev": true,
           "requires": {
-            "isexe": "^2.0.0"
+            "@jest/types": "^25.4.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "jest-jasmine2": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.3.0.tgz",
-      "integrity": "sha512-NCYOGE6+HNzYFSui52SefgpsnIzvxjn6KAgqw66BdRp37xpMD/4kujDHLNW5bS5i53os5TcMn6jYrzQRO8VPrQ==",
+    "jest-matcher-utils": {
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.4.0.tgz",
+      "integrity": "sha512-yPMdtj7YDgXhnGbc66bowk8AkQ0YwClbbwk3Kzhn5GVDrciiCr27U4NJRbrqXbTdtxjImONITg2LiRIw650k5A==",
       "dev": true,
       "requires": {
-        "@babel/traverse": "^7.1.0",
-        "@jest/environment": "^25.3.0",
-        "@jest/source-map": "^25.2.6",
-        "@jest/test-result": "^25.3.0",
-        "@jest/types": "^25.3.0",
         "chalk": "^3.0.0",
-        "co": "^4.6.0",
-        "expect": "^25.3.0",
-        "is-generator-fn": "^2.0.0",
-        "jest-each": "^25.3.0",
-        "jest-matcher-utils": "^25.3.0",
-        "jest-message-util": "^25.3.0",
-        "jest-runtime": "^25.3.0",
-        "jest-snapshot": "^25.3.0",
-        "jest-util": "^25.3.0",
-        "pretty-format": "^25.3.0",
-        "throat": "^5.0.0"
+        "jest-diff": "^25.4.0",
+        "jest-get-type": "^25.2.6",
+        "pretty-format": "^25.4.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -3885,6 +4698,30 @@
           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
+        "jest-diff": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.4.0.tgz",
+          "integrity": "sha512-kklLbJVXW0y8UKOWOdYhI6TH5MG6QAxrWiBMgQaPIuhj3dNFGirKCd+/xfplBXICQ7fI+3QcqHm9p9lWu1N6ug==",
+          "dev": true,
+          "requires": {
+            "chalk": "^3.0.0",
+            "diff-sequences": "^25.2.6",
+            "jest-get-type": "^25.2.6",
+            "pretty-format": "^25.4.0"
+          }
+        },
+        "pretty-format": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+          "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^25.4.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
         "supports-color": {
           "version": "7.1.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -3896,28 +4733,33 @@
         }
       }
     },
-    "jest-leak-detector": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.3.0.tgz",
-      "integrity": "sha512-jk7k24dMIfk8LUSQQGN8PyOy9+J0NAfHZWiDmUDYVMctY8FLJQ1eQ8+PjMoN8PgwhLIggUqgYJnyRFvUz3jLRw==",
-      "dev": true,
-      "requires": {
-        "jest-get-type": "^25.2.6",
-        "pretty-format": "^25.3.0"
-      }
-    },
-    "jest-matcher-utils": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.3.0.tgz",
-      "integrity": "sha512-ZBUJ2fchNIZt+fyzkuCFBb8SKaU//Rln45augfUtbHaGyVxCO++ANARdBK9oPGXU3hEDgyy7UHnOP/qNOJXFUg==",
+    "jest-message-util": {
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.4.0.tgz",
+      "integrity": "sha512-LYY9hRcVGgMeMwmdfh9tTjeux1OjZHMusq/E5f3tJN+dAoVVkJtq5ZUEPIcB7bpxDUt2zjUsrwg0EGgPQ+OhXQ==",
       "dev": true,
       "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@jest/types": "^25.4.0",
+        "@types/stack-utils": "^1.0.1",
         "chalk": "^3.0.0",
-        "jest-diff": "^25.3.0",
-        "jest-get-type": "^25.2.6",
-        "pretty-format": "^25.3.0"
+        "micromatch": "^4.0.2",
+        "slash": "^3.0.0",
+        "stack-utils": "^1.0.1"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -3970,21 +4812,27 @@
         }
       }
     },
-    "jest-message-util": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.3.0.tgz",
-      "integrity": "sha512-5QNy9Id4WxJbRITEbA1T1kem9bk7y2fD0updZMSTNHtbEDnYOGLDPAuFBhFgVmOZpv0n6OMdVkK+WhyXEPCcOw==",
+    "jest-mock": {
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.4.0.tgz",
+      "integrity": "sha512-MdazSfcYAUjJjuVTTnusLPzE0pE4VXpOUzWdj8sbM+q6abUjm3bATVPXFqTXrxSieR8ocpvQ9v/QaQCftioQFg==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.0.0",
-        "@jest/types": "^25.3.0",
-        "@types/stack-utils": "^1.0.1",
-        "chalk": "^3.0.0",
-        "micromatch": "^4.0.2",
-        "slash": "^3.0.0",
-        "stack-utils": "^1.0.1"
+        "@jest/types": "^25.4.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4037,15 +4885,6 @@
         }
       }
     },
-    "jest-mock": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.3.0.tgz",
-      "integrity": "sha512-yRn6GbuqB4j3aYu+Z1ezwRiZfp0o9om5uOcBovVtkcRLeBCNP5mT0ysdenUsxAHnQUgGwPOE1wwhtQYe6NKirQ==",
-      "dev": true,
-      "requires": {
-        "@jest/types": "^25.3.0"
-      }
-    },
     "jest-pnp-resolver": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz",
@@ -4059,19 +4898,33 @@
       "dev": true
     },
     "jest-resolve": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.3.0.tgz",
-      "integrity": "sha512-IHoQAAybulsJ+ZgWis+ekYKDAoFkVH5Nx/znpb41zRtpxj4fr2WNV9iDqavdSm8GIpMlsfZxbC/fV9DhW0q9VQ==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.4.0.tgz",
+      "integrity": "sha512-wOsKqVDFWUiv8BtLMCC6uAJ/pHZkfFgoBTgPtmYlsprAjkxrr2U++ZnB3l5ykBMd2O24lXvf30SMAjJIW6k2aA==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "browser-resolve": "^1.11.3",
         "chalk": "^3.0.0",
         "jest-pnp-resolver": "^1.2.1",
+        "read-pkg-up": "^7.0.1",
         "realpath-native": "^2.0.0",
-        "resolve": "^1.15.1"
+        "resolve": "^1.15.1",
+        "slash": "^3.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4114,9 +4967,9 @@
           "dev": true
         },
         "resolve": {
-          "version": "1.16.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz",
-          "integrity": "sha512-LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==",
+          "version": "1.17.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+          "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
           "dev": true,
           "requires": {
             "path-parse": "^1.0.6"
@@ -4134,43 +4987,119 @@
       }
     },
     "jest-resolve-dependencies": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.3.0.tgz",
-      "integrity": "sha512-bDUlLYmHW+f7J7KgcY2lkq8EMRqKonRl0XoD4Wp5SJkgAxKJnsaIOlrrVNTfXYf+YOu3VCjm/Ac2hPF2nfsCIA==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.4.0.tgz",
+      "integrity": "sha512-A0eoZXx6kLiuG1Ui7wITQPl04HwjLErKIJTt8GR3c7UoDAtzW84JtCrgrJ6Tkw6c6MwHEyAaLk7dEPml5pf48A==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "jest-regex-util": "^25.2.6",
-        "jest-snapshot": "^25.3.0"
+        "jest-snapshot": "^25.4.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
     "jest-runner": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.3.0.tgz",
-      "integrity": "sha512-csDqSC9qGHYWDrzrElzEgFbteztFeZJmKhSgY5jlCIcN0+PhActzRNku0DA1Xa1HxGOb0/AfbP1EGJlP4fGPtA==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.4.0.tgz",
+      "integrity": "sha512-wWQSbVgj2e/1chFdMRKZdvlmA6p1IPujhpLT7TKNtCSl1B0PGBGvJjCaiBal/twaU2yfk8VKezHWexM8IliBfA==",
       "dev": true,
       "requires": {
-        "@jest/console": "^25.3.0",
-        "@jest/environment": "^25.3.0",
-        "@jest/test-result": "^25.3.0",
-        "@jest/types": "^25.3.0",
+        "@jest/console": "^25.4.0",
+        "@jest/environment": "^25.4.0",
+        "@jest/test-result": "^25.4.0",
+        "@jest/types": "^25.4.0",
         "chalk": "^3.0.0",
         "exit": "^0.1.2",
         "graceful-fs": "^4.2.3",
-        "jest-config": "^25.3.0",
+        "jest-config": "^25.4.0",
         "jest-docblock": "^25.3.0",
-        "jest-haste-map": "^25.3.0",
-        "jest-jasmine2": "^25.3.0",
-        "jest-leak-detector": "^25.3.0",
-        "jest-message-util": "^25.3.0",
-        "jest-resolve": "^25.3.0",
-        "jest-runtime": "^25.3.0",
-        "jest-util": "^25.3.0",
-        "jest-worker": "^25.2.6",
+        "jest-haste-map": "^25.4.0",
+        "jest-jasmine2": "^25.4.0",
+        "jest-leak-detector": "^25.4.0",
+        "jest-message-util": "^25.4.0",
+        "jest-resolve": "^25.4.0",
+        "jest-runtime": "^25.4.0",
+        "jest-util": "^25.4.0",
+        "jest-worker": "^25.4.0",
         "source-map-support": "^0.5.6",
         "throat": "^5.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4230,38 +5159,50 @@
       }
     },
     "jest-runtime": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.3.0.tgz",
-      "integrity": "sha512-gn5KYB1wxXRM3nfw8fVpthFu60vxQUCr+ShGq41+ZBFF3DRHZRKj3HDWVAVB4iTNBj2y04QeAo5cZ/boYaPg0w==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.4.0.tgz",
+      "integrity": "sha512-lgNJlCDULtXu9FumnwCyWlOub8iytijwsPNa30BKrSNtgoT6NUMXOPrZvsH06U6v0wgD/Igwz13nKA2wEKU2VA==",
       "dev": true,
       "requires": {
-        "@jest/console": "^25.3.0",
-        "@jest/environment": "^25.3.0",
+        "@jest/console": "^25.4.0",
+        "@jest/environment": "^25.4.0",
         "@jest/source-map": "^25.2.6",
-        "@jest/test-result": "^25.3.0",
-        "@jest/transform": "^25.3.0",
-        "@jest/types": "^25.3.0",
+        "@jest/test-result": "^25.4.0",
+        "@jest/transform": "^25.4.0",
+        "@jest/types": "^25.4.0",
         "@types/yargs": "^15.0.0",
         "chalk": "^3.0.0",
         "collect-v8-coverage": "^1.0.0",
         "exit": "^0.1.2",
         "glob": "^7.1.3",
         "graceful-fs": "^4.2.3",
-        "jest-config": "^25.3.0",
-        "jest-haste-map": "^25.3.0",
-        "jest-message-util": "^25.3.0",
-        "jest-mock": "^25.3.0",
+        "jest-config": "^25.4.0",
+        "jest-haste-map": "^25.4.0",
+        "jest-message-util": "^25.4.0",
+        "jest-mock": "^25.4.0",
         "jest-regex-util": "^25.2.6",
-        "jest-resolve": "^25.3.0",
-        "jest-snapshot": "^25.3.0",
-        "jest-util": "^25.3.0",
-        "jest-validate": "^25.3.0",
+        "jest-resolve": "^25.4.0",
+        "jest-snapshot": "^25.4.0",
+        "jest-util": "^25.4.0",
+        "jest-validate": "^25.4.0",
         "realpath-native": "^2.0.0",
         "slash": "^3.0.0",
         "strip-bom": "^4.0.0",
         "yargs": "^15.3.1"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4333,27 +5274,39 @@
       "dev": true
     },
     "jest-snapshot": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.3.0.tgz",
-      "integrity": "sha512-GGpR6Oro2htJPKh5RX4PR1xwo5jCEjtvSPLW1IS7N85y+2bWKbiknHpJJRKSdGXghElb5hWaeQASJI4IiRayGg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.4.0.tgz",
+      "integrity": "sha512-J4CJ0X2SaGheYRZdLz9CRHn9jUknVmlks4UBeu270hPAvdsauFXOhx9SQP2JtRzhnR3cvro/9N9KP83/uvFfRg==",
       "dev": true,
       "requires": {
         "@babel/types": "^7.0.0",
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "@types/prettier": "^1.19.0",
         "chalk": "^3.0.0",
-        "expect": "^25.3.0",
-        "jest-diff": "^25.3.0",
+        "expect": "^25.4.0",
+        "jest-diff": "^25.4.0",
         "jest-get-type": "^25.2.6",
-        "jest-matcher-utils": "^25.3.0",
-        "jest-message-util": "^25.3.0",
-        "jest-resolve": "^25.3.0",
+        "jest-matcher-utils": "^25.4.0",
+        "jest-message-util": "^25.4.0",
+        "jest-resolve": "^25.4.0",
         "make-dir": "^3.0.0",
         "natural-compare": "^1.4.0",
-        "pretty-format": "^25.3.0",
+        "pretty-format": "^25.4.0",
         "semver": "^6.3.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4395,6 +5348,30 @@
           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
+        "jest-diff": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.4.0.tgz",
+          "integrity": "sha512-kklLbJVXW0y8UKOWOdYhI6TH5MG6QAxrWiBMgQaPIuhj3dNFGirKCd+/xfplBXICQ7fI+3QcqHm9p9lWu1N6ug==",
+          "dev": true,
+          "requires": {
+            "chalk": "^3.0.0",
+            "diff-sequences": "^25.2.6",
+            "jest-get-type": "^25.2.6",
+            "pretty-format": "^25.4.0"
+          }
+        },
+        "pretty-format": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+          "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^25.4.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
         "semver": {
           "version": "6.3.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -4413,17 +5390,29 @@
       }
     },
     "jest-util": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.3.0.tgz",
-      "integrity": "sha512-dc625P/KS/CpWTJJJxKc4bA3A6c+PJGBAqS8JTJqx4HqPoKNqXg/Ec8biL2Z1TabwK7E7Ilf0/ukSEXM1VwzNA==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.4.0.tgz",
+      "integrity": "sha512-WSZD59sBtAUjLv1hMeKbNZXmMcrLRWcYqpO8Dz8b4CeCTZpfNQw2q9uwrYAD+BbJoLJlu4ezVPwtAmM/9/SlZA==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "chalk": "^3.0.0",
         "is-ci": "^2.0.0",
         "make-dir": "^3.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4477,19 +5466,31 @@
       }
     },
     "jest-validate": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.3.0.tgz",
-      "integrity": "sha512-3WuXgIZ4HXUvW6gk9twFFkT9j6zUorKnF2oEY8VEsHb7x5LGvVlN3WUsbqazVKuyXwvikO2zFJ/YTySMsMje2w==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.4.0.tgz",
+      "integrity": "sha512-hvjmes/EFVJSoeP1yOl8qR8mAtMR3ToBkZeXrD/ZS9VxRyWDqQ/E1C5ucMTeSmEOGLipvdlyipiGbHJ+R1MQ0g==",
       "dev": true,
       "requires": {
-        "@jest/types": "^25.3.0",
+        "@jest/types": "^25.4.0",
         "camelcase": "^5.3.1",
         "chalk": "^3.0.0",
         "jest-get-type": "^25.2.6",
         "leven": "^3.1.0",
-        "pretty-format": "^25.3.0"
+        "pretty-format": "^25.4.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4531,6 +5532,18 @@
           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
+        "pretty-format": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+          "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^25.4.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
         "supports-color": {
           "version": "7.1.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -4543,19 +5556,31 @@
       }
     },
     "jest-watcher": {
-      "version": "25.3.0",
-      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.3.0.tgz",
-      "integrity": "sha512-dtFkfidFCS9Ucv8azOg2hkiY3sgJEHeTLtGFHS+jfBEE7eRtrO6+2r1BokyDkaG2FOD7485r/SgpC1MFAENfeA==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.4.0.tgz",
+      "integrity": "sha512-36IUfOSRELsKLB7k25j/wutx0aVuHFN6wO94gPNjQtQqFPa2rkOymmx9rM5EzbF3XBZZ2oqD9xbRVoYa2w86gw==",
       "dev": true,
       "requires": {
-        "@jest/test-result": "^25.3.0",
-        "@jest/types": "^25.3.0",
+        "@jest/test-result": "^25.4.0",
+        "@jest/types": "^25.4.0",
         "ansi-escapes": "^4.2.1",
         "chalk": "^3.0.0",
-        "jest-util": "^25.3.0",
+        "jest-util": "^25.4.0",
         "string-length": "^3.1.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "25.4.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+          "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^3.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@@ -4609,9 +5634,9 @@
       }
     },
     "jest-worker": {
-      "version": "25.2.6",
-      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.6.tgz",
-      "integrity": "sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.4.0.tgz",
+      "integrity": "sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==",
       "dev": true,
       "requires": {
         "merge-stream": "^2.0.0",
@@ -4697,6 +5722,12 @@
       "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
       "dev": true
     },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
     "json-schema": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -4798,6 +5829,12 @@
         "type-check": "~0.3.2"
       }
     },
+    "lines-and-columns": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+      "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
+      "dev": true
+    },
     "locate-path": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -5068,6 +6105,18 @@
         }
       }
     },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
     "normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -5227,6 +6276,18 @@
         "callsites": "^3.0.0"
       }
     },
+    "parse-json": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
+      "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1",
+        "lines-and-columns": "^1.1.6"
+      }
+    },
     "parse5": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
@@ -5318,9 +6379,9 @@
       "dev": true
     },
     "prettier": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.4.tgz",
-      "integrity": "sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w==",
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz",
+      "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
       "dev": true
     },
     "pretty-format": {
@@ -5429,13 +6490,44 @@
       "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
       "dev": true
     },
+    "read-pkg": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+      "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+      "dev": true,
+      "requires": {
+        "@types/normalize-package-data": "^2.4.0",
+        "normalize-package-data": "^2.5.0",
+        "parse-json": "^5.0.0",
+        "type-fest": "^0.6.0"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+          "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+          "dev": true
+        }
+      }
+    },
+    "read-pkg-up": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+      "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+      "dev": true,
+      "requires": {
+        "find-up": "^4.1.0",
+        "read-pkg": "^5.2.0",
+        "type-fest": "^0.8.1"
+      }
+    },
     "readdirp": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
-      "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+      "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
       "dev": true,
       "requires": {
-        "picomatch": "^2.0.7"
+        "picomatch": "^2.2.1"
       }
     },
     "realpath-native": {
@@ -5638,9 +6730,9 @@
       }
     },
     "rollup": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.6.1.tgz",
-      "integrity": "sha512-1RhFDRJeg027YjBO6+JxmVWkEZY0ASztHhoEUEWxOwkh4mjO58TFD6Uo7T7Y3FbmDpRTfKhM5NVxJyimCn0Elg==",
+      "version": "2.7.3",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.7.3.tgz",
+      "integrity": "sha512-lAWJGZ5BQzcu/5fhMKGJrh5oy9LQUoaCid8cQV8k+E2vE9E/UWptzcM+bSBg+u8akORsvnybsqQUE/wVChIazg==",
       "dev": true,
       "requires": {
         "fsevents": "~2.1.2"
@@ -6188,9 +7280,9 @@
       }
     },
     "source-map-support": {
-      "version": "0.5.16",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
-      "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+      "version": "0.5.18",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.18.tgz",
+      "integrity": "sha512-9luZr/BZ2QeU6tO2uG8N2aZpVSli4TSAOAqFOyTO51AJcD9P99c0K1h6dD6r6qo5dyT44BR5exweOaLLeldTkQ==",
       "dev": true,
       "requires": {
         "buffer-from": "^1.0.0",
@@ -6209,6 +7301,38 @@
       "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==",
       "dev": true
     },
+    "spdx-correct": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+      "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
+      "dev": true
+    },
     "split-string": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -6554,9 +7678,9 @@
       }
     },
     "ts-jest": {
-      "version": "25.3.1",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.3.1.tgz",
-      "integrity": "sha512-O53FtKguoMUByalAJW+NWEv7c4tus5ckmhfa7/V0jBb2z8v5rDSLFC1Ate7wLknYPC1euuhY6eJjQq4FtOZrkg==",
+      "version": "25.4.0",
+      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.4.0.tgz",
+      "integrity": "sha512-+0ZrksdaquxGUBwSdTIcdX7VXdwLIlSRsyjivVA9gcO+Cvr6ByqDhu/mi5+HCcb6cMkiQp5xZ8qRO7/eCqLeyw==",
       "dev": true,
       "requires": {
         "bs-logger": "0.x",
@@ -6587,15 +7711,15 @@
       }
     },
     "ts-node": {
-      "version": "8.8.2",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.8.2.tgz",
-      "integrity": "sha512-duVj6BpSpUpD/oM4MfhO98ozgkp3Gt9qIp3jGxwU2DFvl/3IRaEAvbLa8G60uS7C77457e/m5TMowjedeRxI1Q==",
+      "version": "8.9.1",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.9.1.tgz",
+      "integrity": "sha512-yrq6ODsxEFTLz0R3BX2myf0WBCSQh9A+py8PBo1dCzWIOcvisbyH6akNKqDHMgXePF2kir5mm5JXJTH3OUJYOQ==",
       "dev": true,
       "requires": {
         "arg": "^4.1.0",
         "diff": "^4.0.1",
         "make-error": "^1.1.1",
-        "source-map-support": "^0.5.6",
+        "source-map-support": "^0.5.17",
         "yn": "3.1.1"
       }
     },
@@ -6804,6 +7928,16 @@
         }
       }
     },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
     "verror": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
@@ -6972,9 +8106,9 @@
       }
     },
     "ws": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
-      "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==",
+      "version": "7.2.5",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
+      "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==",
       "dev": true
     },
     "xml-name-validator": {
@@ -7015,9 +8149,9 @@
       }
     },
     "yargs-parser": {
-      "version": "18.1.2",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
-      "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
+      "version": "18.1.3",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+      "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
       "dev": true,
       "requires": {
         "camelcase": "^5.0.0",
diff --git a/package.json b/package.json
index 5deb7c4f..c43d1896 100644
--- a/package.json
+++ b/package.json
@@ -1,14 +1,15 @@
 {
   "name": "darkreader",
-  "version": "4.9.4",
+  "version": "4.9.7",
   "description": "Dark theme for every website",
   "scripts": {
     "api": "node -e \"require('./tasks/bundle-api')()\"",
-    "code-style": "eslint --fix 'src/**/*.ts' 'src/**/*.tsx' 'tasks/**/*.js' 'tests/**/*.ts' 'index.d.ts'",
+    "code-style": "eslint --fix \"src/**/*.ts\" \"src/**/*.tsx\" \"tasks/**/*.js\" \"tests/**/*.ts\" \"index.d.ts\"",
     "debug": "node tasks/debug.js",
     "prepublishOnly": "npm test && npm run api",
     "release": "npm test && node tasks/release.js",
-    "test": "jest --config=tests/jest.config.js"
+    "test": "jest --config=tests/jest.config.js",
+    "test:inject": "jest --config=tests/inject/jest.config.js"
   },
   "main": "darkreader.js",
   "repository": {
@@ -29,29 +30,29 @@
     "malevic": "0.18.2"
   },
   "devDependencies": {
-    "@types/chrome": "0.0.104",
+    "@types/chrome": "0.0.106",
     "@types/jest": "25.2.1",
-    "@types/node": "13.11.1",
-    "@typescript-eslint/eslint-plugin": "2.28.0",
-    "@typescript-eslint/parser": "2.28.0",
-    "chokidar": "3.3.1",
+    "@types/node": "13.13.4",
+    "@typescript-eslint/eslint-plugin": "2.30.0",
+    "@typescript-eslint/parser": "2.30.0",
+    "chokidar": "3.4.0",
     "eslint": "6.8.0",
     "eslint-plugin-local": "1.0.0",
     "fs-extra": "9.0.0",
     "globby": "11.0.0",
-    "jest": "25.3.0",
+    "jest": "25.4.0",
     "less": "3.11.1",
-    "prettier": "2.0.4",
-    "rollup": "2.6.1",
+    "prettier": "2.0.5",
+    "rollup": "2.7.3",
     "rollup-plugin-commonjs": "10.1.0",
     "rollup-plugin-node-resolve": "5.2.0",
     "rollup-plugin-replace": "2.2.0",
     "rollup-plugin-typescript2": "0.27.0",
-    "ts-jest": "25.3.1",
-    "ts-node": "8.8.2",
+    "ts-jest": "25.4.0",
+    "ts-node": "8.9.1",
     "tsconfig-paths": "3.9.0",
     "typescript": "3.8.3",
-    "ws": "7.2.3",
+    "ws": "7.2.5",
     "yazl": "2.5.1"
   }
 }
diff --git a/src/_locales/he.config b/src/_locales/he.config
new file mode 100644
index 00000000..330f27b8
--- /dev/null
+++ b/src/_locales/he.config
@@ -0,0 +1,183 @@
+@extension_description
+מצב אפל לכל אתר. תשמור על העיניים שלך, השתמש בעיצוב כהה לגלישה בלילה או אפי' ביום.
+
+@loading_please_wait
+טוען... נא המתן
+
+
+#==== Top section ====
+
+@on
+פעיל
+
+@off
+כבוי
+
+@toggle_current_site
+הוסף/הסר אתר נוכחי מהרשימה
+
+@setup_hotkey_toggle_site
+הוסף/הסר אתר נוכחי מהרשימה
+הגדר מקש קיצור
+
+@toggle_extension
+הפעל/כבה את התוסף
+
+@setup_hotkey_toggle_extension
+הפעל/כבה את התוסף
+הגדר מקש קיצור
+
+@automation
+הפעלה אוטומטית:
+
+@set_active_hours
+הגדר שעות פעילות
+
+@set_location
+הפעל לאחר השקיעה במיקום זה
+
+@longitude
+קו אורך
+
+@latitude
+קו רוחב
+
+@page_protected
+דף זה מוגן
+על ידי הדפדפן
+
+@page_in_dark_list
+אתר זה נמצא
+ברשימה אפלה גלובלית
+
+@system_dark_mode
+השתמש בסכמת הצבעים של המערכת
+
+@system_dark_mode_description
+הפעל כאשר מצב החשכה של המערכת פועל
+
+
+#==== Filter ====
+
+@filter
+מסנן
+
+@mode
+מצב
+
+@dark
+אפל
+
+@light
+מואר
+
+@brightness
+בהירות
+
+@contrast
+ניגודיות
+
+@grayscale
+גווני אפור
+
+@sepia
+גווני חום-אדמדם
+
+@only_for
+רק עבור
+
+@only_for_description
+החל הגדרות באתר הנוכחי בלבד
+
+
+#==== Site list ====
+
+@site_list
+רשימת אתרים
+
+@invert_listed_only
+רק מה שברשימה
+
+@not_invert_listed
+רק מה שלא ברשימה
+
+@add_site_to_list
+הוסף אתר נוכחי לרשימה
+
+@setup_add_site_hotkey
+הגדר מקש קיצור להוספת אתר
+
+
+#==== More settings ====
+
+@more
+עוד
+
+@select_font
+בחר גופן
+
+@text_stroke
+מריחת טקסט
+
+@try_experimental_theme_engines
+נסה מנועי ערכת נושא ניסיוניים:
+פילטר + שומר על רוויה בצבעים, משתמש ב- GPU
+נושא סטטי מייצר נושא מהיר פשוט
+נושא דינמי מנתח צבעים ותמונות
+
+@engine_filter
+מסנן
+
+@engine_filter_plus
+מסנן+
+
+@engine_static
+סטטי
+
+@engine_dynamic
+דינמי
+
+@theme_generation_mode
+מצב יצירת ערכת נושא
+
+@custom_browser_theme_on
+מותאם אישית
+
+@custom_browser_theme_off
+ברירת מחדל
+
+@change_browser_theme
+שנה את ערכת נושא הדפדפן
+
+
+#==== Footer ====
+
+@privacy
+פרטיות
+
+@help
+עזרה
+
+@donate
+לתרום
+
+@news
+חדשות
+
+@read_more
+קרא עוד
+
+@open_dev_tools
+Dev tools
+
+
+#==== Store listing ====
+
+@store_listing
+תוסף טיפוח לעיניים זה מאפשר למצב לילה ליצור ערכות נושא כהות לאתרי אינטרנט גלישה. קורא כהה ממיר צבעים בהירים מה שהופך אותם לניגודיות גבוהה וקל לקריאה בלילה.
+
+ניתן להתאים את הבהירות, הניגודיות, מסנן חום-אדמדם, מצב כהה, הגדרות גופן ורשימת אתרים להתעלמות.
+
+Reader Dark אינו מציג מודעות ואינו שולח את נתוני המשתמש לשום מקום. זהו קוד פתוח לחלוטין https://github.com/darkreader/darkreader
+
+לצורך פעולה תקינה, השבת תוספים דומים. צפייה נעימה!
diff --git a/src/background/user-storage.ts b/src/background/user-storage.ts
index 2481f87a..e2fdfacb 100644
--- a/src/background/user-storage.ts
+++ b/src/background/user-storage.ts
@@ -38,7 +38,9 @@ export default class UserStorage {
             location: {
                 latitude: null,
                 longitude: null,
-            }
+            },
+            previewNewDesign: false,
+            enableForPDF: true,
         };
         this.settings = null;
     }
diff --git a/src/background/utils/extension-api.ts b/src/background/utils/extension-api.ts
index bc73016d..10e0dc75 100644
--- a/src/background/utils/extension-api.ts
+++ b/src/background/utils/extension-api.ts
@@ -13,6 +13,7 @@ export function canInjectScript(url: string) {
             && !url.startsWith('moz')
             && !url.startsWith('view-source:')
             && !url.startsWith('https://addons.mozilla.org')
+            && !url.endsWith('.pdf')
         );
     }
     if (isEdge()) {
@@ -20,6 +21,7 @@ export function canInjectScript(url: string) {
             && !url.startsWith('chrome')
             && !url.startsWith('edge')
             && !url.startsWith('https://chrome.google.com/webstore')
+            && !url.startsWith('https://microsoftedge.microsoft.com/addons')
         );
     }
     return (url
diff --git a/src/config/dark-sites.config b/src/config/dark-sites.config
index 46a8eea2..35c652bf 100644
--- a/src/config/dark-sites.config
+++ b/src/config/dark-sites.config
@@ -95,10 +95,12 @@ energized.pro
 enlightenment.org
 entity.works
 envs.net
+epicgames.com
 erai-raws.info
 esportal.com
 eternal.abimon.org
 faceit.com
+factorio.com
 femto.pw
 filterblade.xyz
 flooxer.com
@@ -107,6 +109,7 @@ forum.snahp.it
 frankerfacez.com
 gamebanana.com
 gamejolt.com
+gatsbyjs.org
 geekabit.nl
 geektyper.com
 getsharex.com
@@ -148,6 +151,7 @@ jsfiddle.net
 killtheradio.net
 kissasian.*
 kissmanga.com
+knockout.chat
 kodenames.io
 lainchan.org
 letterboxd.com
@@ -196,6 +200,7 @@ ptt.cc
 raidtime.net
 rawg.io
 realgamernewz.com
+redbox.com
 runicgames.com
 saidit.net
 sanctum.geek.nz/arabesque
@@ -265,7 +270,6 @@ w0rp.com
 w41k3r.com
 warcraftdaily.com
 weboas.is
-wiki.factorio.com
 wowhead.com
 wowinterface.com
 www.directvnow.com
diff --git a/src/config/dynamic-theme-fixes.config b/src/config/dynamic-theme-fixes.config
index 2fc4c654..21cdecc0 100644
--- a/src/config/dynamic-theme-fixes.config
+++ b/src/config/dynamic-theme-fixes.config
@@ -2,6 +2,7 @@
 
 INVERT
 .jfk-bubble
+embed[type="application/pdf"]
 
 CSS
 .vimvixen-hint {
@@ -26,14 +27,30 @@ superuser.com
 
 INVERT
 ._glyph
-.h-auto[alt="Mathematics"]
+.favicon-mathoverflow
+.favicon-mathoverflowmeta
+.favicon-stackoverflowmeta
+.h-auto[alt="Academia"]
+.h-auto[alt="Anime & Manga"]
+.h-auto[alt="Ask Different"]
+.h-auto[alt="Aviation"]
+.h-auto[alt="Code Review"]
+.h-auto[alt="Electrical Engineering"]
+.h-auto[alt="English Language Learners"]
+.h-auto[alt="Japanese Language"]
 .h-auto[alt="MathOverflow"]
+.h-auto[alt="Mathematics"]
 .h-auto[alt="Server Fault"]
+.h-auto[alt="Skeptics"]
+.h-auto[alt="Software Engineering"]
+.h-auto[alt="Stack Apps"]
 .h-auto[alt="Super User"]
-.favicon-stackoverflowmeta
-.favicon-mathoverflow
-.favicon-mathoverflowmeta
+.h-auto[alt="The Workplace"]
+.h-auto[alt="Theoretical Computer Science"]
+.h-auto[alt="Unix & Linux"]
+.h-auto[alt="Web Applications"]
 a.js-gps-track::before
+img[alt="The Stack Exchange Network"]
 
 CSS
 body {
@@ -142,16 +159,18 @@ CSS
 allegro.pl
 
 CSS
-._88e3e06 {
+#opbox-listing--base i {
     background-size: 100% 100% !important;
 }
 
 ================================
 
 amazon.com
+amazon.co.uk
 
 INVERT
 #banner-image
+#ordersContainer .a-box.order-attributes img
 
 CSS
 .banner-border {
@@ -159,6 +178,10 @@ CSS
     background-color: ${white} !important;
 }
 
+img {
+    border-radius: 5% !important;
+}
+
 ================================
 
 angrymetalguy.com
@@ -451,6 +474,21 @@ input
 
 ================================
 
+code.visualstudio.com
+
+CSS
+.home.linux .jumbotron .screenshot {
+ background-image:url("/assets/home/home-screenshot-linux.png");
+ padding-bottom:82.65802%
+}
+@media only screen and (min-width: 1200px) {
+ .home.linux .jumbotron .screenshot {
+  background-image:url("/assets/home/home-screenshot-linux-lg.png");
+  padding-bottom:68%
+ }
+
+================================
+
 codecademy.com
 
 INVERT
@@ -479,6 +517,23 @@ INVERT
 
 ================================
 
+coolblue.be
+coolblue.nl
+hotorangemedia.nl
+
+CSS
+path[fill="#F60"] {
+    --darkreader-inline-fill: #F60 !important
+}
+path[fill="#FFF"] {
+    --darkreader-inline-fill: #FFF !important
+}
+path[fill="#285DAB"] {
+    --darkreader-inline-fill: #285DAB !important
+}
+
+================================
+
 coopgames.eu
 
 INVERT
@@ -578,6 +633,20 @@ INVERT
 
 ================================
 
+dev.to
+
+INVERT
+img[src$=".svg"]
+
+CSS
+body, h1, h2, h3,
+h4, h5, h6, a,
+.content-classification-text {
+    color: #e8e6e3 !important;
+}
+
+================================
+
 developer.mozilla.org
 
 INVERT
@@ -629,6 +698,7 @@ INVERT
 .link-management-drop-down-icon
 .vs-icon
 .vpc-icon
+.docs-analytics-img
 
 CSS
 .docs-preview-palette-item {
@@ -813,14 +883,15 @@ INVERT
 .editPhoto i._3-8_
 .sx_08856a
 .sx_ac12f7
-.sp_NfKX-XV69KF
+.sp_hk4DJV_EEeW
 .sp_V53xxlprDHX_1_5x
 .sp_V53xxlprDHX_2x
 #pagelet_ego_pane button .img._3-8_
+#homepage_panel_promote_footer_pagelet button .img._3-8_
 #event_tabs #reaction_units span img
-.fbAggregatedMap
 .fbPlaceFlyoutWrap img
-#PagesProfileHomeSecondaryColumnPagelet img[src*="map"]
+img[src*="map"]
+[style*="map"]
 
 CSS
 .fbNubButton {
@@ -951,29 +1022,10 @@ INVERT
 .detail-data-panel__icon
 .sleep-chart-yaxis.end
 .supergraph-canvas
+.leaflet-pane.leaflet-map-pane img:not([src*="satellite"])
+.zone1desc
 
 CSS
-.highlight {
-    color: ${#ffffff} !important;
-}
-.altitudetitle {
-    color: ${#ffffff} !important;
-}
-.altdescmax {
-    color: ${#ffffff} !important;
-}
-.altdescmin {
-    color: ${#ffffff} !important;
-}
-.zonedesctitle {
-    color: ${#ffffff} !important;
-}
-.zonedescmax {
-    color: ${#ffffff} !important;
-}
-.zonedesclight {
-    color: ${#ffffff} !important;
-}
 .highcharts-container svg {
     fill: ${#3f3f3f} !important;
 }
@@ -981,6 +1033,9 @@ CSS
     background-color: rgba(255, 255, 255, 0.15) !important;
     background-blend-mode: color;
 }
+.zone1desc {
+    color: ${#ffffff} !important;
+}
 
 ================================
 
@@ -1134,6 +1189,12 @@ CSS
 .jfk-bubble, .octotree-sidebar, .cc-pr__logo, .cc-octicon, #network canvas, img.network-tree {
     filter: invert(94.4%) hue-rotate(180deg) contrast(90%) !important;
 }
+.progress-bar {
+    background-color: rgba(196, 191, 183, 0.25) !important;
+}
+.progress{
+    background-color: green !important; 
+}
 
 ================================
 
@@ -1191,7 +1252,16 @@ a.gb_xc
 .i4ewOd-LQLjdd li::before
 .un1lmc-j4gsHd
 .maps-sprite-settings-languages
-.gb_D.gb_qc
+a[href*="about/products"]
+
+================================
+
+grubhub.com
+
+CSS
+.s-checkbox-filler {
+    color: rgb(24, 26, 27) !important;
+}
 
 ================================
 
@@ -1290,6 +1360,13 @@ span[role="presentation"] > .cm-variable-2 {
 
 ================================
 
+hootsuite.com
+
+INVERT
+img[src$="hootsuite_white_form3.png"]
+
+================================
+
 imdb.com
 
 CSS
@@ -1437,6 +1514,22 @@ CSS
 
 ================================
 
+isbgpsafeyet.com
+
+CSS
+h1, h2, h3,
+h4, h5, h6 {
+    color: rgb(255, 255, 255) !important;
+}
+.Hero, .Footer {
+    background: None !important;
+}
+.Button-is-primary {
+    background: var(--primary-background-color);
+}
+
+================================
+
 jailbreak.fce365.info
 
 CSS
@@ -1555,6 +1648,30 @@ CSS
 
 ================================
 
+lichess.org
+
+CSS
+.is2d .pawn.black {
+    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PHBhdGggZD0iTTIyLjUgOWMtMi4yMSAwLTQgMS43OS00IDQgMCAuODkuMjkgMS43MS43OCAyLjM4QzE3LjMzIDE2LjUgMTYgMTguNTkgMTYgMjFjMCAyLjAzLjk0IDMuODQgMi40MSA1LjAzLTMgMS4wNi03LjQxIDUuNTUtNy40MSAxMy40N2gyM2MwLTcuOTItNC40MS0xMi40MS03LjQxLTEzLjQ3IDEuNDctMS4xOSAyLjQxLTMgMi40MS01LjAzIDAtMi40MS0xLjMzLTQuNS0zLjI4LTUuNjIuNDktLjY3Ljc4LTEuNDkuNzgtMi4zOCAwLTIuMjEtMS43OS00LTQtNHoiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==') !important
+}
+.is2d .knight.black {
+    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMiAxMGMxMC41IDEgMTYuNSA4IDE2IDI5SDE1YzAtOSAxMC02LjUgOC0yMSIgZmlsbD0iIzAwMCIvPjxwYXRoIGQ9Ik0yNCAxOGMuMzggMi45MS01LjU1IDcuMzctOCA5LTMgMi0yLjgyIDQuMzQtNSA0LTEuMDQyLS45NCAxLjQxLTMuMDQgMC0zLTEgMCAuMTkgMS4yMy0xIDItMSAwLTQuMDAzIDEtNC00IDAtMiA2LTEyIDYtMTJzMS44OS0xLjkgMi0zLjVjLS43My0uOTk0LS41LTItLjUtMyAxLTEgMyAyLjUgMyAyLjVoMnMuNzgtMS45OTIgMi41LTNjMSAwIDEgMyAxIDMiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNOS41IDI1LjVhLjUuNSAwIDEgMS0xIDAgLjUuNSAwIDEgMSAxIDB6bTUuNDMzLTkuNzVhLjUgMS41IDMwIDEgMS0uODY2LS41LjUgMS41IDMwIDEgMSAuODY2LjV6IiBmaWxsPSIjZWNlY2VjIiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMjQuNTUgMTAuNGwtLjQ1IDEuNDUuNS4xNWMzLjE1IDEgNS42NSAyLjQ5IDcuOSA2Ljc1UzM1Ljc1IDI5LjA2IDM1LjI1IDM5bC0uMDUuNWgyLjI1bC4wNS0uNWMuNS0xMC4wNi0uODgtMTYuODUtMy4yNS0yMS4zNC0yLjM3LTQuNDktNS43OS02LjY0LTkuMTktNy4xNmwtLjUxLS4xeiIgZmlsbD0iI2VjZWNlYyIgc3Ryb2tlPSJub25lIi8+PC9nPjwvc3ZnPg==') !important
+}
+.is2d .bishop.black {
+    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIGZpbGw9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJidXR0Ij48cGF0aCBkPSJNOSAzNmMzLjM5LS45NyAxMC4xMS40MyAxMy41LTIgMy4zOSAyLjQzIDEwLjExIDEuMDMgMTMuNSAyIDAgMCAxLjY1LjU0IDMgMi0uNjguOTctMS42NS45OS0zIC41LTMuMzktLjk3LTEwLjExLjQ2LTEzLjUtMS0zLjM5IDEuNDYtMTAuMTEuMDMtMTMuNSAxLTEuMzU0LjQ5LTIuMzIzLjQ3LTMtLjUgMS4zNTQtMS45NCAzLTIgMy0yeiIvPjxwYXRoIGQ9Ik0xNSAzMmMyLjUgMi41IDEyLjUgMi41IDE1IDAgLjUtMS41IDAtMiAwLTIgMC0yLjUtMi41LTQtMi41LTQgNS41LTEuNSA2LTExLjUtNS0xNS41LTExIDQtMTAuNSAxNC01IDE1LjUgMCAwLTIuNSAxLjUtMi41IDQgMCAwLS41LjUgMCAyeiIvPjxwYXRoIGQ9Ik0yNSA4YTIuNSAyLjUgMCAxIDEtNSAwIDIuNSAyLjUgMCAxIDEgNSAweiIvPjwvZz48cGF0aCBkPSJNMTcuNSAyNmgxME0xNSAzMGgxNW0tNy41LTE0LjV2NU0yMCAxOGg1IiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48L2c+PC9zdmc+') !important
+}
+.is2d .rook.black {
+    background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik05IDM5aDI3di0zSDl2M3ptMy41LTdsMS41LTIuNWgxN2wxLjUgMi41aC0yMHptLS41IDR2LTRoMjF2NEgxMnoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTE0IDI5LjV2LTEzaDE3djEzSDE0eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMTQgMTYuNUwxMSAxNGgyM2wtMyAyLjVIMTR6TTExIDE0VjloNHYyaDVWOWg1djJoNVY5aDR2NUgxMXoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTEyIDM1LjVoMjFtLTIwLTRoMTltLTE4LTJoMTdtLTE3LTEzaDE3TTExIDE0aDIzIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjwvZz48L3N2Zz4=') !important
+}
+.is2d .queen.black {
+    background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIHN0cm9rZT0ibm9uZSI+PGNpcmNsZSBjeD0iNiIgY3k9IjEyIiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMTQiIGN5PSI5IiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMjIuNSIgY3k9IjgiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzMSIgY3k9IjkiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzOSIgY3k9IjEyIiByPSIyLjc1Ii8+PC9nPjxwYXRoIGQ9Ik05IDI2YzguNS0xLjUgMjEtMS41IDI3IDBsMi41LTEyLjVMMzEgMjVsLS4zLTE0LjEtNS4yIDEzLjYtMy0xNC41LTMgMTQuNS01LjItMTMuNkwxNCAyNSA2LjUgMTMuNSA5IDI2eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNOSAyNmMwIDIgMS41IDIgMi41IDQgMSAxLjUgMSAxIC41IDMuNS0xLjUgMS0xLjUgMi41LTEuNSAyLjUtMS41IDEuNS41IDIuNS41IDIuNSA2LjUgMSAxNi41IDEgMjMgMCAwIDAgMS41LTEgMC0yLjUgMCAwIC41LTEuNS0xLTIuNS0uNS0yLjUtLjUtMiAuNS0zLjUgMS0yIDIuNS0yIDIuNS00LTguNS0xLjUtMTguNS0xLjUtMjcgMHoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTExIDM4LjVhMzUgMzUgMSAwIDAgMjMgMCIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMTEgMjlhMzUgMzUgMSAwIDEgMjMgMG0tMjEuNSAyLjVoMjBtLTIxIDNhMzUgMzUgMSAwIDAgMjIgMG0tMjMgM2EzNSAzNSAxIDAgMCAyNCAwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiLz48L2c+PC9zdmc+') !important
+}
+.is2d .king.black {
+    background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMi41IDExLjYzVjYiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMjIuNSAyNXM0LjUtNy41IDMtMTAuNWMwIDAtMS0yLjUtMy0yLjVzLTMgMi41LTMgMi41Yy0xLjUgMyAzIDEwLjUgMyAxMC41IiBmaWxsPSIjMDAwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjxwYXRoIGQ9Ik0xMS41IDM3YzUuNSAzLjUgMTUuNSAzLjUgMjEgMHYtN3M5LTQuNSA2LTEwLjVjLTQtNi41LTEzLjUtMy41LTE2IDRWMjd2LTMuNWMtMy41LTcuNS0xMy0xMC41LTE2LTQtMyA2IDUgMTAgNSAxMFYzN3oiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNMjAgOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTMyIDI5LjVzOC41LTQgNi4wMy05LjY1QzM0LjE1IDE0IDI1IDE4IDIyLjUgMjQuNWwuMDEgMi4xLS4wMS0yLjFDMjAgMTggOS45MDYgMTQgNi45OTcgMTkuODVjLTIuNDk3IDUuNjUgNC44NTMgOSA0Ljg1MyA5IiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMTEuNSAzMGM1LjUtMyAxNS41LTMgMjEgMG0tMjEgMy41YzUuNS0zIDE1LjUtMyAyMSAwbS0yMSAzLjVjNS41LTMgMTUuNS0zIDIxIDAiIHN0cm9rZT0iI2VjZWNlYyIvPjwvZz48L3N2Zz4=') !important
+}
+
+================================
+
 linkedin.com
 
 CSS
@@ -1597,10 +1714,35 @@ img.Ha
 .ita-icon-0
 .gb_df
 img[src$="google_gsuite"]
+.rY>.sa
+.buh
 
 CSS
+@media (min-resolution: 144dpi), (-webkit-min-device-pixel-ratio: 1.5) {
+    .buk {
+        background-image: url(//ssl.gstatic.com/ui/v1/icons/mail/rfr/density_default_v1_2x.png) !important;
+    }
+    .bui {
+        background-image: url(//ssl.gstatic.com/ui/v1/icons/mail/rfr/density_comfortable_v1_2x.png) !important;
+    }
+    .buj {
+        background-image: url(//ssl.gstatic.com/ui/v1/icons/mail/rfr/density_compact_v1_2x.png) !important;
+    }
+}
 .buk {
-    background-image: url(blob:https://mail.google.com/b6c63cb3-4ff2-4533-9c1f-8dc753946a1f) !important;
+    background-image: url(//ssl.gstatic.com/ui/v1/icons/mail/rfr/density_default_v1_1x.png) !important;
+}
+.bui {
+    background-image: url(//ssl.gstatic.com/ui/v1/icons/mail/rfr/density_comfortable_v1_1x.png) !important;
+}
+.buj {
+    background-image: url(//ssl.gstatic.com/ui/v1/icons/mail/rfr/density_compact_v1_1x.png) !important;
+}
+::-webkit-scrollbar-thumb {
+    background-color: #424242 !important;
+}
+::-webkit-scrollbar {
+    background-color: transparent !important
 }
 
 ================================
@@ -1626,6 +1768,13 @@ nav {
 
 ================================
 
+marinij.com
+
+INVERT
+.custom-logo.logo > a > img
+
+================================
+
 marketplace.visualstudio.com
 
 CSS
@@ -1787,12 +1936,22 @@ INVERT
 img[src="https://bi.im-g.pl/im/0/24525/m24525630.png"]
 
 CSS
-.main_wrapper {
+.main_wrapper,
+.top_section_bg, .bottom_section_bg {
     background-color: ${#e5e5e5} !important;
 }
 
 ================================
 
+mullvad.net
+
+CSS
+#bg {
+    fill: #192E45;
+}
+
+================================
+
 my.nintendo.com
 
 CSS
@@ -1812,6 +1971,16 @@ INVERT
 
 ================================
 
+neowin.net
+
+CSS
+.select option {
+    background-color: #1c1e1f !important;
+    color: white !important;
+}
+
+================================
+
 news.ycombinator.com
 
 INVERT
@@ -2047,13 +2216,17 @@ CSS
 
 ================================
 
-openstreetmap.org/#map
+openstreetmap.org
 
 INVERT
-#map
+.map-layout #map
 
 CSS
-#map {
+div[dir="ltr", id="map"] {
+    filter: none !important;
+    background: #000 !important;
+}
+.map-layout #map {
     background-color: ${#1b1b1b} !important;
 }
 
@@ -2091,7 +2264,7 @@ INVERT
 .bUWb7c
 .WF1WQd
 img[src="https://www.gstatic.com/android/market_images/web/play_prism_hlock_2x.png"]
-.gb_D.gb_qc
+a[href*="about/products"]
 
 ================================
 
@@ -2110,7 +2283,7 @@ plus.google.com
 
 INVERT
 a.gb_b > div
-.gb_D.gb_qc
+a[href*="about/products"]
 
 ================================
 
@@ -2207,6 +2380,15 @@ img[src^="/static/glyphicons/"]
 
 ================================
 
+quickbase.com
+
+CSS
+td.cell, td.label {
+   border-color: rgb(24, 26, 27) !important;
+}
+
+================================
+
 reddit.com
 
 INVERT
@@ -2231,6 +2413,15 @@ p, li {
 
 ================================
 
+repl.it
+
+CSS
+.monaco-editor .cursor {
+    background-color: ${#000};
+}
+
+================================
+
 richie-bendall.ml
 
 CSS
@@ -2480,6 +2671,15 @@ CSS
 
 ================================
 
+softorage.com
+
+CSS
+.tg {
+    background-image: linear-gradient(36deg,#fc466b,#3f5efb) !important;
+}
+
+================================
+
 soundcloud.com
 
 INVERT
@@ -2519,6 +2719,18 @@ CSS
 
 ================================
 
+sport.pl
+
+INVERT
+#pageHead img
+
+CSS
+.top_section_bg, .bottom_section_bg {
+    background: ${#e5e5e5} !important;
+}
+
+================================
+
 strava.com
 
 INVERT
@@ -3148,19 +3360,24 @@ CSS
 web.whatsapp.com
 
 INVERT
-.tail-container
 .selectable-text > span._3PxOr
 
 CSS
-._2RT36 {
-    outline: solid !important;
-}
 ._3Ye_R {
     color: gray !important
 }
 [data-asset-intro-image] {
     background-image: url(/img/intro-connection_c98cc75f2aa905314d74375a975d2cf2.jpg) !important;
 }
+._11ozL {
+    border: 5px solid white !important;
+}
+html[dir=ltr] .message-out .tail-container {
+    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAYAAADeB1slAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA0LTE5VDEyOjUzOjEzKzAyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wNC0xOVQxMjo1NzowNSswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNC0xOVQxMjo1NzowNSswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplNDljZjc5OS1jN2VhLTQ2NDctOWYyZC1iYTc5MGRiYjJkYmQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZTQ5Y2Y3OTktYzdlYS00NjQ3LTlmMmQtYmE3OTBkYmIyZGJkIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZTQ5Y2Y3OTktYzdlYS00NjQ3LTlmMmQtYmE3OTBkYmIyZGJkIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplNDljZjc5OS1jN2VhLTQ2NDctOWYyZC1iYTc5MGRiYjJkYmQiIHN0RXZ0OndoZW49IjIwMjAtMDQtMTlUMTI6NTM6MTMrMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5VX0C4AAABTElEQVRIie3Vv0rEQBAG8G+yu5fEzlexsNUHsLUVBS0sbYUDixMs7LQUVARB7yksDRoFfQlBvRPN7d5d7sZOlCDZzW26TD35fuzsn9D84gLDsYjohpmX32/vS3sD13AAYOYlMK/a9FYCAIDzSVgrAKJpbUDUki8kxWVtgBmYfdteZ0CBzyHlUS3AXEs+j0HrvSQl22+kbWMUqs9MDzv9u0dyuTvWK9BfepeC4Mr1YloBEnxBSh67BFsDcUs+5aC1KuGlQBSqDz0cd1w21QkwmW6D6LrKg1gKCPCZy3l3AmIl0gloY5bR/AtEoerrUX7QS1Kn824NmMy0QdT1EV4AJE9PIEWl814KxEokOQVbPuZeAOJQvelRfggAvkbzB9CZ2QNR12fwDyDAp77n/rvka/KwubK9U1f+DD99yyJmr3taqNpX0AAN0AANYFHfejdtRQswUf0AAAAASUVORK5CYII=') !important
+}
+html[dir=ltr] .message-in .tail-container {
+    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAYAAADeB1slAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA0LTE5VDEyOjUzOjEzKzAyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wNC0xOVQxMjo1NiswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNC0xOVQxMjo1NiswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MzYwY2FhOC1kNjZmLTI2NDctOWRhMi1lODUzMDg4NDA5NjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODM2MGNhYTgtZDY2Zi0yNjQ3LTlkYTItZTg1MzA4ODQwOTYwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ODM2MGNhYTgtZDY2Zi0yNjQ3LTlkYTItZTg1MzA4ODQwOTYwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo4MzYwY2FhOC1kNjZmLTI2NDctOWRhMi1lODUzMDg4NDA5NjAiIHN0RXZ0OndoZW49IjIwMjAtMDQtMTlUMTI6NTM6MTMrMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7wnTSaAAAByElEQVRIie3Uu0sDMRwH8N/vesnltC+LCIJDnQSpUwUnH7g76+qioLgo+G+IOIj+B66iuIn4QBQrioeT4FGpb3GwWj1zFwc5qPVxaRu3freEXD78kvsFhRDwV1o7OgERN4QQvcXziAhB3wIA6IErhBgQAL3fp4M3BwDQghbwd+cZEaU2qwhwOc9SQrx/A4y6+rOGWGS/0ioCAQCArG2PUEr4vwG0PnKiuXxS06SWlw/YVgYL3JtLRMPL5R6VFJBMpYVtZTB3dTWaiMcuykFQptGK47zku2KJxu3C62twD4FEBanuni9jWhfe897fpnVdl+o0qSMqRd5cMRM22ZrMUZX/W8DnpT88Po7Ho5GcMqC4imQqLUI6se/v70YJIa4S4KeEiLEqOJ8N/dEfVQG2lUEuxJTJjPXf7qMqwO+Pp/zzRDwauVAO+Ahq2und7c2gyZijHPCjG+au6xQmTMa84uNSBthWBh0PF8MmXTAoVQ8kU2kBAHB5fTvd0ty04yPKAD/EYPmTo6Ohjva2HGNG8GM3MDYptbG1tfllHKPY093Xv6SsgtL36vjwYPsyez6srILSrMzPaACAgUC1UX7JNaAG1IAaUEE+AFQnnoGbc+MiAAAAAElFTkSuQmCC') !important
+}
 
 ================================
 
@@ -3217,12 +3434,16 @@ INVERT
 
 wikipedia.org
 
+IGNORE INLINE STYLE
+.legend-color
+
 INVERT
 .mwe-math-element
 .mw-ext-score
 .mw-wiki-logo
 .central-textlogo__image
 .svg-Wikimedia-logo_black
+.header .branding-box > a > span > img
 
 CSS
 div .mw-parser-output ul {
@@ -3232,6 +3453,12 @@ div .thumbimage[src$=".png"],
 div .thumbimage img[src$=".png"] {
     background-color: white;
 }
+.mw-mmv-image .svg,
+.fullImageLink [src*=".svg"],
+a[href$=".svg"]:hover > img {
+    background-color: rgba(255, 255, 255, 0.75) !important;
+    background-blend-mode: color;
+}
 
 ================================
 
@@ -3255,6 +3482,15 @@ div.NavFrame div.NavHead {
 
 ================================
 
+wunderground.com
+
+CSS
+.bar-on  {
+   fill: rgba(0, 0, 0, 0.4) !important;
+}
+
+================================
+
 www.bing.com
 
 CSS
@@ -3318,7 +3554,7 @@ INVERT
 .gb_0b
 #dictionary-modules img[src*="png"]
 a.gb_b > div
-.gb_D.gb_qc
+a[href*="about/products"]
 
 CSS
 .RNNXgb {
@@ -3376,6 +3612,18 @@ INVERT
 
 ================================
 
+xda-developers.com
+
+CSS
+.hb-trigger-inner, .hb-trigger-inner::before, .hb-trigger-inner::after {
+    background-color: ${rgb(48, 51, 53)} !important;
+}
+.hb-trigger:hover .hb-trigger-inner, .hb-trigger:hover .hb-trigger-inner::before, .hb-trigger:hover .hb-trigger-inner::after, .hb-trigger:active .hb-trigger-inner, .hb-trigger:active .hb-trigger-inner::before, .hb-trigger:active .hb-trigger-inner::after, .hb-trigger:focus .hb-trigger-inner, .hb-trigger:focus .hb-trigger-inner::before, .hb-trigger:focus .hb-trigger-inner::after {
+    background-color: ${black} !important;
+}
+
+================================
+
 youtube.com
 
 CSS
@@ -3601,9 +3849,16 @@ ytd-toggle-button-renderer.style-default-active[is-icon-button] {
 }
 yt-formatted-string[has-link-only_]:not([force-default-style]) a.yt-simple-endpoint.yt-formatted-string {
     color: var(--yt-endpoint-color, var(--yt-spec-text-primary)) !important;
+}
 paper-button.ytd-subscribe-button-renderer[subscribed] {
     background-color: var(--yt-spec-10-percent-layer) !important
 }
+.badge-style-type-live-now.ytd-badge-supported-renderer {
+    border: 1px solid rgb(255, 78, 69) !important;
+}
+.ytp-load-progress {
+    background: rgba(255,255,255,0.3) !important;
+}
 
 ================================
 
diff --git a/src/config/inversion-fixes.config b/src/config/inversion-fixes.config
index ea4abc97..4787ef15 100644
--- a/src/config/inversion-fixes.config
+++ b/src/config/inversion-fixes.config
@@ -1265,6 +1265,13 @@ html,
 
 ================================
 
+mullvad.net
+
+INVERT
+#Logo
+
+================================
+
 my.mail.ru/(music|mail|vk|ok|bk|list|gmail.com|inbox)
 
 INVERT
diff --git a/src/definitions.d.ts b/src/definitions.d.ts
index 9d2a494f..763217b0 100644
--- a/src/definitions.d.ts
+++ b/src/definitions.d.ts
@@ -49,6 +49,8 @@ export interface FilterConfig {
     stylesheet: string;
 }
 
+export type Theme = FilterConfig;
+
 export interface CustomSiteConfig {
     url: string[];
     theme: FilterConfig;
@@ -67,6 +69,8 @@ export interface UserSettings {
     automation: '' | 'time' | 'system' | 'location';
     time: TimeSettings;
     location: LocationSettings;
+    previewNewDesign: boolean;
+    enableForPDF: boolean;
 }
 
 export interface TimeSettings {
@@ -98,6 +102,7 @@ export interface Shortcuts {
 
 export interface DynamicThemeFix {
     url: string[];
+    ignoreInlineStyle: string[];
     invert: string[];
     css: string;
 }
diff --git a/src/generators/css-filter.ts b/src/generators/css-filter.ts
index 4b6c7b79..af8315b8 100644
--- a/src/generators/css-filter.ts
+++ b/src/generators/css-filter.ts
@@ -5,12 +5,28 @@ import {parseArray, formatArray} from '../utils/text';
 import {compareURLPatterns, isURLInList} from '../utils/url';
 import {createTextStyle} from './text-style';
 import {FilterConfig, InversionFix} from '../definitions';
+import {compareChromeVersions, getChromeVersion, isChromiumBased} from '../utils/platform';
 
 export enum FilterMode {
     light = 0,
     dark = 1
 }
 
+/**
+ * This checks if the current chromium version has the patch in it.
+ * As of Chromium v81.0.4035.0 this has been the situation
+ *
+ * Bug report: https://bugs.chromium.org/p/chromium/issues/detail?id=501582
+ * Patch: https://chromium-review.googlesource.com/c/chromium/src/+/1979258
+ */
+export function hasChromiumIssue501582() {
+    const chromeVersion = getChromeVersion();
+    return Boolean(
+        isChromiumBased() &&
+        compareChromeVersions(chromeVersion, '81.0.4035.0') >= 0
+    );
+}
+
 export default function createCSSFilterStyleheet(config: FilterConfig, url: string, frameURL: string, inversionFixes: InversionFix[]) {
     const filterValue = getCSSFilterValue(config);
     const reverseFilterValue = 'invert(100%) hue-rotate(180deg)';
@@ -63,7 +79,9 @@ export function cssFilterStyleheetTemplate(filterValue: string, reverseFilterVal
     });
 
     if (!frameURL) {
-        const [r, g, b] = applyColorMatrix([255, 255, 255], createFilterMatrix(config));
+        // If user has the chrome issue the colors should be the other way around as of the rootcolors will affect the whole background color of the page
+        const rootColors = hasChromiumIssue501582() && config.mode === FilterMode.dark ? [0, 0, 0] : [255, 255, 255];
+        const [r, g, b] = applyColorMatrix(rootColors, createFilterMatrix(config));
         const bgColor = {
             r: Math.round(r),
             g: Math.round(g),
diff --git a/src/generators/dynamic-theme.ts b/src/generators/dynamic-theme.ts
index 3dd3e3ea..ff66bb98 100644
--- a/src/generators/dynamic-theme.ts
+++ b/src/generators/dynamic-theme.ts
@@ -5,6 +5,7 @@ import {compareURLPatterns, isURLInList} from '../utils/url';
 import {DynamicThemeFix} from '../definitions';
 
 const dynamicThemeFixesCommands = {
+    'IGNORE INLINE STYLE': 'ignoreInlineStyle',
     'INVERT': 'invert',
     'CSS': 'css',
 };
@@ -50,6 +51,7 @@ export function getDynamicThemeFixesFor(url: string, frameURL: string, fixes: Dy
 
     const common = {
         url: fixes[0].url,
+        ignoreInlineStyle: fixes[0].ignoreInlineStyle || [],
         invert: fixes[0].invert || [],
         css: fixes[0].css || [],
     };
@@ -73,6 +75,7 @@ export function getDynamicThemeFixesFor(url: string, frameURL: string, fixes: Dy
 
     return {
         url: match.url,
+        ignoreInlineStyle: common.ignoreInlineStyle.concat(match.ignoreInlineStyle || []),
         invert: common.invert.concat(match.invert || []),
         css: [common.css, match.css].filter((s) => s).join('\n'),
     };
diff --git a/src/inject/dynamic-theme/index.ts b/src/inject/dynamic-theme/index.ts
index 85f090aa..5421191d 100644
--- a/src/inject/dynamic-theme/index.ts
+++ b/src/inject/dynamic-theme/index.ts
@@ -45,26 +45,25 @@ function stopStylePositionWatchers() {
 
 function createStaticStyleOverrides() {
     const fallbackStyle = createOrUpdateStyle('darkreader--fallback');
-    document.head.insertBefore(fallbackStyle, document.head.firstChild);
     fallbackStyle.textContent = getModifiedFallbackStyle(filter, {strict: true});
+    document.head.insertBefore(fallbackStyle, document.head.firstChild);
     setupStylePositionWatcher(fallbackStyle, 'fallback');
 
     const userAgentStyle = createOrUpdateStyle('darkreader--user-agent');
-    document.head.insertBefore(userAgentStyle, fallbackStyle.nextSibling);
     userAgentStyle.textContent = getModifiedUserAgentStyle(filter, isIFrame);
+    document.head.insertBefore(userAgentStyle, fallbackStyle.nextSibling);
     setupStylePositionWatcher(userAgentStyle, 'user-agent');
 
     const textStyle = createOrUpdateStyle('darkreader--text');
-    document.head.insertBefore(textStyle, fallbackStyle.nextSibling);
     if (filter.useFont || filter.textStroke > 0) {
         textStyle.textContent = createTextStyle(filter);
     } else {
         textStyle.textContent = '';
     }
+    document.head.insertBefore(textStyle, fallbackStyle.nextSibling);
     setupStylePositionWatcher(textStyle, 'text');
 
     const invertStyle = createOrUpdateStyle('darkreader--invert');
-    document.head.insertBefore(invertStyle, textStyle.nextSibling);
     if (fixes && Array.isArray(fixes.invert) && fixes.invert.length > 0) {
         invertStyle.textContent = [
             `${fixes.invert.join(', ')} {`,
@@ -77,16 +76,17 @@ function createStaticStyleOverrides() {
     } else {
         invertStyle.textContent = '';
     }
+    document.head.insertBefore(invertStyle, textStyle.nextSibling);
     setupStylePositionWatcher(invertStyle, 'invert');
 
     const inlineStyle = createOrUpdateStyle('darkreader--inline');
-    document.head.insertBefore(inlineStyle, invertStyle.nextSibling);
     inlineStyle.textContent = getInlineOverrideStyle();
+    document.head.insertBefore(inlineStyle, invertStyle.nextSibling);
     setupStylePositionWatcher(inlineStyle, 'inline');
 
     const overrideStyle = createOrUpdateStyle('darkreader--override');
-    document.head.appendChild(overrideStyle);
     overrideStyle.textContent = fixes && fixes.css ? replaceCSSTemplates(fixes.css) : '';
+    document.head.appendChild(overrideStyle);
     setupStylePositionWatcher(overrideStyle, 'override');
 }
 
@@ -94,8 +94,8 @@ const shadowRootsWithOverrides = new Set<ShadowRoot>();
 
 function createShadowStaticStyleOverrides(root: ShadowRoot) {
     const inlineStyle = createOrUpdateStyle('darkreader--inline', root);
-    root.insertBefore(inlineStyle, root.firstChild);
     inlineStyle.textContent = getInlineOverrideStyle();
+    root.insertBefore(inlineStyle, root.firstChild);
     shadowRootsWithOverrides.add(root);
 }
 
@@ -161,7 +161,7 @@ function createDynamicStyleOverrides() {
             inlineStyleElements.push(...Array.from(elements as NodeListOf<HTMLLinkElement | HTMLStyleElement>));
         }
     });
-    inlineStyleElements.forEach((el) => overrideInlineStyle(el as HTMLElement, filter));
+    inlineStyleElements.forEach((el) => overrideInlineStyle(el as HTMLElement, filter, fixes.ignoreInlineStyle));
 }
 
 let loadingStylesCounter = 0;
@@ -313,7 +313,7 @@ function watchForUpdates() {
     });
 
     watchForInlineStyles((element) => {
-        overrideInlineStyle(element, filter);
+        overrideInlineStyle(element, filter, fixes.ignoreInlineStyle);
         if (element === document.documentElement) {
             const rootVariables = getElementCSSVariables(document.documentElement);
             if (rootVariables.size > 0) {
@@ -325,7 +325,7 @@ function watchForUpdates() {
         const inlineStyleElements = root.querySelectorAll(INLINE_STYLE_SELECTOR);
         if (inlineStyleElements.length > 0) {
             createShadowStaticStyleOverrides(root);
-            inlineStyleElements.forEach((el) => overrideInlineStyle(el as HTMLElement, filter));
+            inlineStyleElements.forEach((el) => overrideInlineStyle(el as HTMLElement, filter, fixes.ignoreInlineStyle));
         }
     });
 
diff --git a/src/inject/dynamic-theme/inline-style.ts b/src/inject/dynamic-theme/inline-style.ts
index c5287505..0ff66696 100644
--- a/src/inject/dynamic-theme/inline-style.ts
+++ b/src/inject/dynamic-theme/inline-style.ts
@@ -185,7 +185,16 @@ function getInlineStyleCacheKey(el: HTMLElement, theme: FilterConfig) {
         .join(' ');
 }
 
-export function overrideInlineStyle(element: HTMLElement, theme: FilterConfig) {
+function shouldIgnoreInlineStyle(element: HTMLElement, selectors: string[]) {
+    for (const ingnoredSelector of selectors) {
+        if (element.matches(ingnoredSelector)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+export function overrideInlineStyle(element: HTMLElement, theme: FilterConfig, ignoreSelectors: string[]) {
     const cacheKey = getInlineStyleCacheKey(element, theme);
     if (cacheKey === inlineStyleCache.get(element)) {
         return;
@@ -211,6 +220,17 @@ export function overrideInlineStyle(element: HTMLElement, theme: FilterConfig) {
         unsetProps.delete(targetCSSProp);
     }
 
+    if (ignoreSelectors.length > 0) {
+        if (shouldIgnoreInlineStyle(element, ignoreSelectors)) {
+            unsetProps.forEach((cssProp) => {
+                const {store, dataAttr} = overrides[cssProp];
+                store.delete(element);
+                element.removeAttribute(dataAttr);
+            });
+            return;
+        }
+    }
+
     if (element.hasAttribute('bgcolor')) {
         let value = element.getAttribute('bgcolor');
         if (value.match(/^[0-9a-f]{3}$/i) || value.match(/^[0-9a-f]{6}$/i)) {
diff --git a/src/inject/dynamic-theme/style-manager.ts b/src/inject/dynamic-theme/style-manager.ts
index 98e046cb..914bfbf3 100644
--- a/src/inject/dynamic-theme/style-manager.ts
+++ b/src/inject/dynamic-theme/style-manager.ts
@@ -374,6 +374,15 @@ export function manageStyle(element: HTMLLinkElement | HTMLStyleElement, {update
             syncStylePositionWatcher && syncStylePositionWatcher.stop();
             insertStyle();
 
+            // Firefox issue: Some websites get CSP warning,
+            // when `textContent` is not set (e.g. pypi.org).
+            // But for other websites (e.g. facebook.com)
+            // some images disappear when `textContent`
+            // is initially set to an empty string.
+            if (syncStyle.sheet == null) {
+                syncStyle.textContent = '';
+            }
+
             const sheet = syncStyle.sheet;
             for (let i = sheet.cssRules.length - 1; i >= 0; i--) {
                 sheet.deleteRule(i);
diff --git a/src/inject/dynamic-theme/url.ts b/src/inject/dynamic-theme/url.ts
index 08691385..63c5dd52 100644
--- a/src/inject/dynamic-theme/url.ts
+++ b/src/inject/dynamic-theme/url.ts
@@ -16,7 +16,12 @@ export function getAbsoluteURL($base: string, $relative: string) {
         const u = parseURL(`${b.protocol}//${b.host}${$relative}`);
         return u.href;
     }
-    const pathParts = b.pathname.split('/').concat($relative.split('/')).filter((p) => p);
+    let pathParts = b.pathname.split('/');
+    const lastPathPart = pathParts[pathParts.length - 1];
+    if (lastPathPart.match(/\.[a-z]+$/i)) {
+        pathParts.pop();
+    }
+    pathParts = pathParts.concat(...$relative.split('/')).filter((p) => p);
     let backwardIndex: number;
     while ((backwardIndex = pathParts.indexOf('..')) > 0) {
         pathParts.splice(backwardIndex - 1, 2);
diff --git a/src/manifest.json b/src/manifest.json
index a3c60f4c..57992815 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -1,7 +1,7 @@
 {
     "manifest_version": 2,
     "name": "Dark Reader",
-    "version": "4.9.4",
+    "version": "4.9.7",
     "author": "Alexander Shutau",
     "description": "__MSG_extension_description__",
     "default_locale": "en",
diff --git a/src/ui/controls/dropdown/index.tsx b/src/ui/controls/dropdown/index.tsx
index d4919476..eae08b11 100644
--- a/src/ui/controls/dropdown/index.tsx
+++ b/src/ui/controls/dropdown/index.tsx
@@ -2,6 +2,7 @@ import {m} from 'malevic';
 import {getContext} from 'malevic/dom';
 
 interface DropDownProps {
+    class?: string;
     selected: string;
     values: string[];
     onChange: (value: string) => void;
@@ -54,6 +55,7 @@ export default function DropDown(props: DropDownProps) {
                 class={{
                     'dropdown__list__item': true,
                     'dropdown__list__item--selected': value === props.selected,
+                    [props.class]: props.class != null,
                 }}
                 onclick={() => {
                     store.isOpen = false;
@@ -71,6 +73,7 @@ export default function DropDown(props: DropDownProps) {
             class={{
                 'dropdown': true,
                 'dropdown--open': store.isOpen,
+                [props.class]: Boolean(props.class),
             }}
         >
             <span
diff --git a/src/ui/controls/dropdown/style.less b/src/ui/controls/dropdown/style.less
index 952240d9..adc509d7 100644
--- a/src/ui/controls/dropdown/style.less
+++ b/src/ui/controls/dropdown/style.less
@@ -51,6 +51,11 @@
             line-height: @dropdown-item-height;
             text-align: center;
             width: 100%;
+            
+            &:hover {
+                background-color: @color-control-hover;
+                transition: background-color @time-fast;
+            }
 
             &--selected {
                 overflow: hidden;
@@ -80,3 +85,7 @@
         z-index: 2;
     }
 }
+
+.preview .dropdown {
+    cursor: pointer;
+}
diff --git a/src/ui/controls/slider/style.less b/src/ui/controls/slider/style.less
index 1bc357ac..d0feb147 100644
--- a/src/ui/controls/slider/style.less
+++ b/src/ui/controls/slider/style.less
@@ -70,6 +70,7 @@
     &__thumb {
         background-color: @slider-thumb-color;
         border-radius: 50%;
+        cursor: pointer;
         display: inline-block;
         height: @slider-thumb-size;
         margin-left: -@slider-thumb-size / 2;
@@ -79,6 +80,7 @@
 
         &__value {
             color: @color-control-fore;
+            cursor: default;
             left: calc(100% + @size-text-normal / 2);
             line-height: @slider-thumb-size;
             position: absolute;
diff --git a/src/ui/devtools/components/body.tsx b/src/ui/devtools/components/body.tsx
index d9c45fda..ec92530e 100644
--- a/src/ui/devtools/components/body.tsx
+++ b/src/ui/devtools/components/body.tsx
@@ -10,6 +10,7 @@ type BodyProps = ExtWrapper;
 function Body({data, actions}: BodyProps) {
     const {state, setState} = useState({errorText: null as string});
     let textNode: HTMLTextAreaElement;
+    const previewButtonText = data.settings.previewNewDesign ? 'Switch to old design' : 'Preview new design';
 
     const wrapper = (data.settings.theme.engine === ThemeEngines.staticTheme
         ? {
@@ -53,6 +54,10 @@ function Body({data, actions}: BodyProps) {
         setState({errorText: null});
     }
 
+    function toggleDesign() {
+        actions.changeSettings({previewNewDesign: !data.settings.previewNewDesign});
+    }
+
     return (
         <body>
             <header>
@@ -68,6 +73,7 @@ function Body({data, actions}: BodyProps) {
             <div id="buttons">
                 <Button onclick={reset}>Reset</Button>
                 <Button onclick={apply}>Apply</Button>
+                <Button class="preview-design-button" onclick={toggleDesign}>{previewButtonText}</Button>
             </div>
             <p id="description">
                 Read about this tool <strong><a href={DEVTOOLS_DOCS_URL} target="_blank" rel="noopener noreferrer">here</a></strong>.
diff --git a/src/ui/devtools/style.less b/src/ui/devtools/style.less
index 3936c23b..ea42bb60 100644
--- a/src/ui/devtools/style.less
+++ b/src/ui/devtools/style.less
@@ -112,10 +112,15 @@ header {
     }
 
     .button {
-        min-width: 4rem;
+        min-width: 3rem;
         max-width: 6rem;
         width: 100%;
     }
+
+    .preview-design-button {
+        border-color: gray;
+        max-width: 8rem;
+    }
 }
 
 #description {
diff --git a/src/ui/popup/body/index.tsx b/src/ui/popup/body/index.tsx
new file mode 100644
index 00000000..78f965d0
--- /dev/null
+++ b/src/ui/popup/body/index.tsx
@@ -0,0 +1,111 @@
+import {m} from 'malevic';
+import {getContext} from 'malevic/dom';
+import {DONATE_URL} from '../../../utils/links';
+import {getLocalMessage} from '../../../utils/locales';
+import {isMobile} from '../../../utils/platform';
+import MainPage from '../main-page';
+import {Page, PageViewer} from '../page-viewer';
+import SettingsPage from '../settings-page';
+import ThemePage from '../theme/page';
+import {ViewProps} from '../types';
+
+function Logo() {
+    return (
+        <a
+            class="m-logo"
+            href="https://darkreader.org/"
+            target="_blank"
+            rel="noopener noreferrer"
+        >
+            Dark Reader
+        </a>
+    );
+}
+
+function Pages(props: ViewProps) {
+    const context = getContext();
+    const store = context.store as {
+        activePage: 'main' | 'theme' | 'settings';
+    };
+    if (store.activePage == null) {
+        store.activePage = 'main';
+    }
+
+    function onThemeNavClick() {
+        store.activePage = 'theme';
+        context.refresh();
+    }
+
+    function onSettingsNavClick() {
+        store.activePage = 'settings';
+        context.refresh();
+    }
+
+    function onBackClick() {
+        store.activePage = 'main';
+        context.refresh();
+    }
+
+    return (
+        <PageViewer
+            activePage={store.activePage}
+            onBackButtonClick={onBackClick}
+        >
+            <Page id="main">
+                <MainPage
+                    {...props}
+                    onThemeNavClick={onThemeNavClick}
+                    onSettingsNavClick={onSettingsNavClick}
+                />
+            </Page>
+            <Page id="theme">
+                <ThemePage {...props} />
+            </Page>
+            <Page id="settings">
+                <SettingsPage {...props} />
+            </Page>
+        </PageViewer>
+    );
+}
+
+function DonateGroup() {
+    return (
+        <div class="m-donate-group">
+            <a class="m-donate-button" href={DONATE_URL} target="_blank" rel="noopener noreferrer">
+                <span class="m-donate-button__text">
+                    {getLocalMessage('donate')}
+                </span>
+            </a>
+            <label class="m-donate-description">
+                This project is sponsored by you
+            </label>
+        </div>
+    );
+}
+
+export default function MobileBody(props: ViewProps) {
+    const context = getContext();
+    context.onCreate(() => {
+        document.documentElement.classList.add('preview');
+        if (isMobile()) {
+            window.addEventListener('contextmenu', (e) => e.preventDefault());
+        }
+    });
+    context.onRemove(() => {
+        document.documentElement.classList.remove('preview');
+    });
+
+    return (
+        <body>
+            <section class="m-section">
+                <Logo />
+            </section>
+            <section class="m-section pages-section">
+                <Pages {...props} />
+            </section>
+            <section class="m-section">
+                <DonateGroup />
+            </section>
+        </body>
+    );
+}
diff --git a/src/ui/popup/components/mobile/style.less b/src/ui/popup/body/style.less
similarity index 57%
rename from src/ui/popup/components/mobile/style.less
rename to src/ui/popup/body/style.less
index 292b0db8..0c9b5742 100644
--- a/src/ui/popup/components/mobile/style.less
+++ b/src/ui/popup/body/style.less
@@ -6,7 +6,10 @@
         margin: @popup-content-padding auto;
         padding: 0;
     }
+}
 
+.mobile,
+.preview {
     .m-section {
         width: 100%;
 
@@ -19,6 +22,10 @@
         }
     }
 
+    .pages-section {
+        height: 22.5rem;
+    }
+
     .m-logo {
         background-image: url('../assets/images/darkreader-type.svg');
         background-position: center;
@@ -32,77 +39,6 @@
         width: @popup-content-width;
     }
 
-    .m-simple-control {
-        align-items: stretch;
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-
-        &__description {
-            font-size: @size-text-small;
-            line-height: @size-text-small-height;
-            text-align: center;
-            -moz-user-select: none;
-            user-select: none;
-        }
-    }
-
-    .m-theme-control {
-        align-items: center;
-        display: flex;
-        flex-direction: row;
-        justify-content: center;
-        min-height: @size-control-inner;
-        width: 100%;
-
-        &__label {
-            font-size: @size-text-normal;
-            line-height: @size-text-normal;
-            text-align: left;
-            -moz-user-select: none;
-            user-select: none;
-            width: 50%;
-        }
-    }
-
-    .m-theme-group {
-        &__presets-wrapper {
-            background-color: @color-control-active;
-            border: @size-border solid @color-border;
-            height: @size-control-inner;
-
-            .dropdown {
-                &__selected {
-                    border-radius: unset;
-                    height: @size-control-inner;
-                    line-height: @size-control-inner;
-                    margin-top: 0;
-                }
-
-                &__list {
-                    border-radius: unset;
-                }
-            }
-        }
-
-        &__controls-wrapper {
-            border-bottom: @size-border solid @color-border;
-            border-left: @size-border solid @color-border;
-            border-right: @size-border solid @color-border;
-            padding: @indent-small;
-        }
-
-        &__description {
-            display: inline-block;
-            font-size: @size-text-small;
-            line-height: @size-text-small-height;
-            text-align: center;
-            -moz-user-select: none;
-            user-select: none;
-            width: 100%;
-        }
-    }
-
     .m-settings-button {
         background-image: @icon-right;
         background-position: center right 0.25rem;
@@ -133,40 +69,6 @@
         }
     }
 
-    .m-check-button {
-        .checkbox {
-            width: calc(100% - 2 * @size-border);
-
-            &__checkmark {
-                background-color: @color-control-active;
-            }
-
-            &__content {
-                align-items: center;
-                display: flex;
-                flex: auto;
-                height: @size-control-inner;
-                justify-content: center;
-                -moz-user-select: none;
-                user-select: none;
-            }
-
-            &__input:checked ~ * {
-                color: @color-control-fore;
-            }
-        }
-
-        &__description {
-            display: inline-block;
-            font-size: @size-text-small;
-            line-height: @size-text-small-height;
-            text-align: center;
-            -moz-user-select: none;
-            user-select: none;
-            width: 100%;
-        }
-    }
-
     .m-help-button {
         align-items: center;
         background-image: @icon-right;
@@ -180,6 +82,11 @@
         text-decoration: none;
         width: calc(100% - 2 * @size-border);
 
+        &:hover {
+            background-color: @color-control-hover;
+            transition: background-color @time-fast;
+        }
+
         &__text {
             color: @color-control-fore;
             display: inline-block;
@@ -206,32 +113,6 @@
         }
     }
 
-    .m-back-button {
-        background-image: @icon-left;
-        background-position: center left 0.25rem;
-        background-repeat: no-repeat;
-        background-size: 1rem;
-        width: calc(100% - 2 * @size-border);
-    }
-
-    .m-paginator {
-        position: relative;
-
-        &__settings-page {
-            background-color: @color-back;
-            display: none;
-            height: 100%;
-            left: 0;
-            position: absolute;
-            top: 0;
-            width: 100%;
-
-            &--active {
-                display: initial;
-            }
-        }
-    }
-
     .m-donate-button {
         align-items: center;
         background-color: @color-control-active;
@@ -254,7 +135,6 @@
         }
     }
 
-    .m-help-description,
     .m-donate-description {
         display: inline-block;
         font-size: @size-text-small;
@@ -266,6 +146,39 @@
     }
 }
 
+.preview .dev-tools-button {
+    overflow: visible;
+    position: relative;
+    width: calc(100% - 2 * 0.125rem);
+
+    &--has-custom-fixes {
+        &::after {
+            background-color: @color-heading;
+            border-radius: @size-text-small-height / 2;
+            color: @color-control-fore;
+            content: "!";
+            font-size: @size-text-small;
+            line-height: @size-text-small-height;
+            min-width: @size-text-small-height;
+            position: absolute;
+            right: -@size-text-small-height / 3;
+            top: -@size-text-small-height / 3;
+            z-index: 3;
+        }
+    }
+}
+
+.mobile,
+.preview {
+    @import "../control-group/style.less";
+    @import "../check-button/style.less";
+    @import "../main-page/style.less";
+    @import "../page-viewer/style.less";
+    @import "../theme/controls/theme-control.less";
+    @import "../theme/page/style.less";
+    @import "../theme/preset-picker/style.less";
+}
+
 .mobile.firefox {
     * {
         font-family: Inter, Open Sans, Segoe UI, Helvetica Neue, Ubuntu, sans-serif;
diff --git a/src/ui/popup/check-button/index.tsx b/src/ui/popup/check-button/index.tsx
new file mode 100644
index 00000000..df78a735
--- /dev/null
+++ b/src/ui/popup/check-button/index.tsx
@@ -0,0 +1,22 @@
+import {m} from 'malevic';
+import {CheckBox} from '../../controls';
+import ControlGroup from '../control-group';
+
+export default function CheckButton(props: {checked: boolean; label: string; description: string; onChange: (checked: boolean) => void}) {
+    return (
+        <ControlGroup class="check-button">
+            <ControlGroup.Control>
+                <CheckBox
+                    class="check-button__checkbox"
+                    checked={props.checked}
+                    onchange={(e: {target: HTMLInputElement}) => props.onChange(e.target.checked)}
+                >
+                    {props.label}
+                </CheckBox>
+            </ControlGroup.Control>
+            <ControlGroup.Description class="check-button__description">
+                {props.description}
+            </ControlGroup.Description>
+        </ControlGroup>
+    );
+}
diff --git a/src/ui/popup/check-button/style.less b/src/ui/popup/check-button/style.less
new file mode 100644
index 00000000..b5eb34b9
--- /dev/null
+++ b/src/ui/popup/check-button/style.less
@@ -0,0 +1,35 @@
+@import "../../theme.less";
+
+.check-button {
+    .checkbox {
+        width: calc(100% - 2 * @size-border);
+
+        &__checkmark {
+            background-color: @color-control-active;
+        }
+
+        &__content {
+            align-items: center;
+            display: flex;
+            flex: auto;
+            height: @size-control-inner;
+            justify-content: center;
+            -moz-user-select: none;
+            user-select: none;
+        }
+
+        &__input:checked ~ * {
+            color: @color-control-fore;
+        }
+    }
+
+    &__description {
+        display: inline-block;
+        font-size: @size-text-small;
+        line-height: @size-text-small-height;
+        text-align: center;
+        -moz-user-select: none;
+        user-select: none;
+        width: 100%;
+    }
+}
diff --git a/src/ui/popup/components/body.tsx b/src/ui/popup/components/body.tsx
index 8952b50e..14e1aeaa 100644
--- a/src/ui/popup/components/body.tsx
+++ b/src/ui/popup/components/body.tsx
@@ -5,7 +5,7 @@ import {TabPanel, Button} from '../../controls';
 import FilterSettings from './filter-settings';
 import {Header, MoreToggleSettings} from './header';
 import Loader from './loader';
-import MobileBody from './mobile/body';
+import NewBody from '../body';
 import MoreSettings from './more-settings';
 import {News, NewsButton} from './news';
 import SiteListSettings from './site-list-settings';
@@ -53,8 +53,8 @@ function Body(props: BodyProps) {
         );
     }
 
-    if (isMobile()) {
-        return <MobileBody {...props} />;
+    if (isMobile() || props.data.settings.previewNewDesign) {
+        return <NewBody {...props} />;
     }
 
     const unreadNews = props.data.news.filter(({read}) => !read);
diff --git a/src/ui/popup/components/mobile/body.tsx b/src/ui/popup/components/mobile/body.tsx
deleted file mode 100644
index 8d4a8e2b..00000000
--- a/src/ui/popup/components/mobile/body.tsx
+++ /dev/null
@@ -1,459 +0,0 @@
-import {m} from 'malevic';
-import {getContext} from 'malevic/dom';
-import {Button, CheckBox, DropDown, MultiSwitch, Slider} from '../../../controls';
-import SiteToggle from '../site-toggle';
-import ThemeEngines from '../../../../generators/theme-engines';
-import {DONATE_URL, getHelpURL} from '../../../../utils/links';
-import {getLocalMessage} from '../../../../utils/locales';
-import {isURLEnabled, isURLInList, getURLHost} from '../../../../utils/url';
-import {ExtensionData, ExtensionActions, TabInfo, FilterConfig} from '../../../../definitions';
-
-type Theme = FilterConfig;
-
-interface MobileBodyProps {
-    data: ExtensionData;
-    tab: TabInfo;
-    actions: ExtensionActions;
-}
-
-function Logo() {
-    return (
-        <a
-            class="m-logo"
-            href="https://darkreader.org/"
-            target="_blank"
-            rel="noopener noreferrer"
-        >
-            Dark Reader
-        </a>
-    );
-}
-
-function SimpleControl(props: {class?: string}, control: Malevic.Spec, description: string) {
-    return (
-        <div class={['m-simple-control', props.class]}>
-            {control}
-            <label class="m-simple-control__description">
-                {description}
-            </label>
-        </div>
-    );
-}
-
-function ThemeControl(props: {class?: string; label: string}, control: Malevic.Spec) {
-    return (
-        <div class={['m-theme-control', props.class]}>
-            <label class="m-theme-control__label">
-                {props.label}
-            </label>
-            {control}
-        </div>
-    );
-}
-
-interface AppSwitchOptions {
-    isOn: boolean;
-    isOff: boolean;
-    isAuto: boolean;
-}
-
-function AppSwitch(props: AppSwitchOptions & {onChange: (opt: AppSwitchOptions) => void}) {
-    const values = [
-        getLocalMessage('on'),
-        'Auto',
-        getLocalMessage('off'),
-    ];
-    const value = props.isOn ? values[0] : props.isOff ? values[2] : values[1];
-
-    function onSwitchChange(v: string) {
-        const index = values.indexOf(v);
-        props.onChange({
-            isOn: index === 0,
-            isOff: index === 2,
-            isAuto: index === 1,
-        });
-    }
-
-    return (
-        <SimpleControl>
-            <MultiSwitch
-                class="m-app-switch__control"
-                options={values}
-                value={value}
-                onChange={onSwitchChange}
-            />
-            {props.isOn ?
-                'Extension is enabled' :
-                props.isOff ?
-                    'Extension is disabled' :
-                    'Switches according to system dark mode'}
-        </SimpleControl>
-    );
-}
-
-function SiteToggleGroup(props: MobileBodyProps) {
-    const isSiteEnabled = isURLEnabled(props.tab.url, props.data.settings, props.tab);
-    return (
-        <SimpleControl>
-            <SiteToggle
-                {...props}
-            />
-            {isSiteEnabled ?
-                'Enabled for current website' :
-                'Disabled for current website'}
-        </SimpleControl>
-    );
-}
-
-function SwitchGroup(props: MobileBodyProps) {
-    function onAppSwitchChange(opt: AppSwitchOptions) {
-        if (opt.isOn) {
-            props.actions.changeSettings({
-                enabled: true,
-                automation: '',
-            });
-        } else if (opt.isOff) {
-            props.actions.changeSettings({
-                enabled: false,
-                automation: '',
-            });
-        } else if (opt.isAuto) {
-            props.actions.changeSettings({
-                automation: 'system',
-            });
-        }
-    }
-
-    return (
-        <Array>
-            <AppSwitch
-                isOn={props.data.settings.enabled === true && !props.data.settings.automation}
-                isOff={props.data.settings.enabled === false && !props.data.settings.automation}
-                isAuto={Boolean(props.data.settings.automation)}
-                onChange={onAppSwitchChange}
-            />
-            <SiteToggleGroup
-                {...props}
-            />
-        </Array>
-    );
-}
-
-function formatPercent(v: number) {
-    return `${v}%`;
-}
-
-function Brightness(props: {value: number; onChange: (v: number) => void}) {
-    return (
-        <ThemeControl label={getLocalMessage('brightness')}>
-            <Slider
-                value={props.value}
-                min={50}
-                max={150}
-                step={1}
-                formatValue={formatPercent}
-                onChange={props.onChange}
-            />
-        </ThemeControl>
-    );
-}
-
-function Contrast(props: {value: number; onChange: (v: number) => void}) {
-    return (
-        <ThemeControl label={getLocalMessage('contrast')}>
-            <Slider
-                value={props.value}
-                min={50}
-                max={150}
-                step={1}
-                formatValue={formatPercent}
-                onChange={props.onChange}
-            />
-        </ThemeControl>
-    );
-}
-
-function Scheme(props: {isDark: boolean; onChange: (dark: boolean) => void}) {
-    const valDark = getLocalMessage('Dark');
-    const valLight = getLocalMessage('Light');
-    return (
-        <ThemeControl label="Scheme">
-            <DropDown
-                selected={props.isDark ? valDark : valLight}
-                values={[
-                    valDark,
-                    valLight,
-                ]}
-                onChange={(v) => props.onChange(v === valDark)}
-            />
-        </ThemeControl>
-    );
-}
-
-function Mode(props: {mode: string; onChange: (mode: string) => void}) {
-    const modes = [
-        [ThemeEngines.dynamicTheme, getLocalMessage('engine_dynamic')],
-        [ThemeEngines.cssFilter, getLocalMessage('engine_filter')],
-        [ThemeEngines.staticTheme, getLocalMessage('engine_static')],
-    ];
-    return (
-        <ThemeControl label="Mode">
-            <DropDown
-                selected={modes.find((m) => m[0] === props.mode)[1]}
-                values={modes.map((m) => m[1])}
-                onChange={(v) => {
-                    const mode = modes.find((m) => m[1] === v)[0];
-                    props.onChange(mode);
-                }}
-            />
-        </ThemeControl>
-    );
-}
-
-function ThemeControls(props: {theme: Theme; onChange: (theme: Partial<Theme>) => void}) {
-    const {theme, onChange} = props;
-    return (
-        <section class="m-section m-theme-controls">
-            <Brightness
-                value={theme.brightness}
-                onChange={(v) => onChange({brightness: v})}
-            />
-            <Contrast
-                value={theme.contrast}
-                onChange={(v) => onChange({contrast: v})}
-            />
-            <Scheme
-                isDark={theme.mode === 1}
-                onChange={(isDark) => onChange({mode: isDark ? 1 : 0})}
-            />
-            <Mode
-                mode={theme.engine}
-                onChange={(mode) => onChange({engine: mode})}
-            />
-        </section>
-    );
-}
-
-function ThemeGroup(props: MobileBodyProps) {
-    const host = getURLHost(props.tab.url || '');
-    const custom = props.data.settings.customThemes.find(
-        ({url}) => isURLInList(props.tab.url, url)
-    );
-    const theme = custom ?
-        custom.theme :
-        props.data.settings.theme;
-
-    function setTheme(config: Partial<Theme>) {
-        if (custom) {
-            custom.theme = {...custom.theme, ...config};
-            props.actions.changeSettings({
-                customThemes: props.data.settings.customThemes,
-            });
-        } else {
-            props.actions.setTheme(config);
-        }
-    }
-
-    const defaultPresetName = 'Default theme';
-    const customPresetName = `Custom for ${host}`;
-
-    function onPresetChange(name: string) {
-        const filteredCustomThemes = props.data.settings.customThemes.filter(({url}) => !isURLInList(props.tab.url, url));
-        if (name === defaultPresetName) {
-            props.actions.changeSettings({customThemes: filteredCustomThemes});
-        } else if (name === customPresetName) {
-            const extended = filteredCustomThemes.concat({
-                url: [host],
-                theme: {...props.data.settings.theme},
-            });
-            props.actions.changeSettings({customThemes: extended});
-        }
-    }
-
-    return (
-        <div class="m-theme-group">
-            <div class="m-theme-group__presets-wrapper">
-                <DropDown
-                    selected={custom ? customPresetName : defaultPresetName}
-                    values={[
-                        defaultPresetName,
-                        customPresetName,
-                    ]}
-                    onChange={onPresetChange}
-                />
-            </div>
-            <div class="m-theme-group__controls-wrapper">
-                <ThemeControls
-                    theme={theme}
-                    onChange={setTheme}
-                />
-            </div>
-            <label class="m-theme-group__description">
-                Configure theme
-            </label>
-        </div>
-    );
-}
-
-function SettingsNavButton(props: {onClick: () => void}) {
-    return (
-        <Button class="m-settings-button" onclick={props.onClick}>
-            <span class="m-settings-button__content">
-                <span class="m-settings-button__icon" />
-                <span class="m-settings-button__text">Settings</span>
-            </span>
-        </Button>
-    );
-}
-
-function MainPage(props: MobileBodyProps & {onSettingsNavClick: () => void}) {
-    return (
-        <Array>
-            <section class="m-section">
-                <SwitchGroup {...props} />
-            </section>
-            <section class="m-section">
-                <ThemeGroup {...props} />
-            </section>
-            <section class="m-section">
-                <SettingsNavButton onClick={props.onSettingsNavClick} />
-            </section>
-        </Array>
-    );
-}
-
-function CheckButton(props: {checked: boolean; label: string; description: string; onChange: (checked: boolean) => void}) {
-    return (
-        <span class="m-check-button">
-            <CheckBox
-                class="m-check-button__checkbox"
-                checked={props.checked}
-                onchange={(e: {target: HTMLInputElement}) => props.onChange(e.target.checked)}
-            >
-                {props.label}
-            </CheckBox>
-            <label class="m-check-button__description">
-                {props.description}
-            </label>
-        </span>
-    );
-}
-
-function HelpGroup() {
-    return (
-        <div class="m-help-group">
-            <a class="m-help-button" href={getHelpURL()} target="_blank" rel="noopener noreferrer">
-                <span class="m-help-button__text">
-                    {getLocalMessage('help')}
-                </span>
-            </a>
-            <label class="m-help-description">
-                Read the manual
-            </label>
-        </div>
-    );
-}
-
-function BackButton(props: {onClick: () => void}) {
-    return (
-        <Button class="m-back-button" onclick={props.onClick}>
-            Back
-        </Button>
-    );
-}
-
-function SettingsPage(props: MobileBodyProps & {onBackClick: () => void}) {
-    function onEnabledByDefaultChange(checked: boolean) {
-        props.actions.changeSettings({applyToListedOnly: !checked});
-    }
-
-    return (
-        <Array>
-            <section class="m-section">
-                <CheckButton
-                    checked={!props.data.settings.applyToListedOnly}
-                    label="Enable by default"
-                    description={props.data.settings.applyToListedOnly ?
-                        'Disabled on all websites by default' :
-                        'Enabled on all websites by default'}
-                    onChange={onEnabledByDefaultChange}
-                />
-                <HelpGroup />
-            </section>
-            <section class="m-section">
-                <BackButton onClick={props.onBackClick} />
-            </section>
-        </Array>
-    );
-}
-
-function Paginator(props: MobileBodyProps) {
-    const context = getContext();
-    const store = context.store as {
-        activePage: 'main' | 'settings';
-    };
-
-    function onSettingsNavClick() {
-        store.activePage = 'settings';
-        context.refresh();
-    }
-
-    function onSettingsBackClick() {
-        store.activePage = 'main';
-        context.refresh();
-    }
-
-    return (
-        <div class="m-paginator">
-            <div class="m-paginator__main-page">
-                <MainPage
-                    {...props}
-                    onSettingsNavClick={onSettingsNavClick}
-                />
-            </div>
-            <div
-                class={{
-                    'm-paginator__settings-page': true,
-                    'm-paginator__settings-page--active': store.activePage === 'settings',
-                }}
-            >
-                <SettingsPage {...props} onBackClick={onSettingsBackClick} />
-            </div>
-        </div>
-    );
-}
-
-function DonateGroup() {
-    return (
-        <div class="m-donate-group">
-            <a class="m-donate-button" href={DONATE_URL} target="_blank" rel="noopener noreferrer">
-                <span class="m-donate-button__text">
-                    {getLocalMessage('donate')}
-                </span>
-            </a>
-            <label class="m-donate-description">
-                This project is sponsored by you
-            </label>
-        </div>
-    );
-}
-
-export default function MobileBody(props: MobileBodyProps) {
-    function preventContextMenu() {
-        window.addEventListener('contextmenu', (e) => e.preventDefault());
-    }
-
-    return (
-        <body oncreate={preventContextMenu}>
-            <section class="m-section">
-                <Logo />
-            </section>
-            <section class="m-section">
-                <Paginator {...props} />
-            </section>
-            <section class="m-section">
-                <DonateGroup />
-            </section>
-        </body>
-    );
-}
diff --git a/src/ui/popup/control-group/index.tsx b/src/ui/popup/control-group/index.tsx
new file mode 100644
index 00000000..35c18fba
--- /dev/null
+++ b/src/ui/popup/control-group/index.tsx
@@ -0,0 +1,32 @@
+import {m} from 'malevic';
+
+function ControlGroup(
+    props: {class?: string},
+    control: Malevic.Spec,
+    description: Malevic.Spec,
+) {
+    return (
+        <span class={['control-group', props.class]}>
+            {control}
+            {description}
+        </span>
+    );
+}
+
+function Control(props: {class?: string}, control: Malevic.Child) {
+    return (
+        <span class={['control-group__control', props.class]} >
+            {control}
+        </span>
+    );
+}
+
+function Description(props: {class?: string}, description: Malevic.Child) {
+    return (
+        <span class={['control-group__description', props.class]} >
+            {description}
+        </span>
+    );
+}
+
+export default Object.assign(ControlGroup, {Control, Description});
diff --git a/src/ui/popup/control-group/style.less b/src/ui/popup/control-group/style.less
new file mode 100644
index 00000000..088d33f9
--- /dev/null
+++ b/src/ui/popup/control-group/style.less
@@ -0,0 +1,16 @@
+@import "../../theme.less";
+
+.control-group {
+    align-items: stretch;
+    display: inline-flex;
+    flex-direction: column;
+    width: 100%;
+
+    &__description {
+        font-size: @size-text-small;
+        line-height: @size-text-small-height;
+        text-align: center;
+        -moz-user-select: none;
+        user-select: none;
+    }
+}
diff --git a/src/ui/popup/main-page/app-switch.tsx b/src/ui/popup/main-page/app-switch.tsx
new file mode 100644
index 00000000..91e7cb85
--- /dev/null
+++ b/src/ui/popup/main-page/app-switch.tsx
@@ -0,0 +1,60 @@
+import {m} from 'malevic';
+import {getLocalMessage} from '../../../utils/locales';
+import {MultiSwitch} from '../../controls';
+import ControlGroup from '../control-group';
+import {ViewProps} from '../types';
+
+export default function AppSwitch(props: ViewProps) {
+    const isOn = props.data.settings.enabled === true && !props.data.settings.automation;
+    const isOff = props.data.settings.enabled === false && !props.data.settings.automation;
+
+    // TODO: Replace messages with some IDs.
+    const values = [
+        getLocalMessage('on'),
+        'Auto',
+        getLocalMessage('off'),
+    ];
+    const value = isOn ? values[0] : isOff ? values[2] : values[1];
+
+    function onSwitchChange(v: string) {
+        const index = values.indexOf(v);
+        if (index === 0) {
+            props.actions.changeSettings({
+                enabled: true,
+                automation: '',
+            });
+        } else if (index === 2) {
+            props.actions.changeSettings({
+                enabled: false,
+                automation: '',
+            });
+        } else if (index === 1) {
+            props.actions.changeSettings({
+                automation: 'system',
+            });
+        }
+    }
+
+    const description = isOn ?
+        'Extension is enabled' :
+        isOff ?
+            'Extension is disabled' :
+            // TODO: More messages (location, time etc).
+            'Switches according to system dark mode';
+
+    return (
+        <ControlGroup class="app-switch">
+            <ControlGroup.Control>
+                <MultiSwitch
+                    class="app-switch__control"
+                    options={values}
+                    value={value}
+                    onChange={onSwitchChange}
+                />
+            </ControlGroup.Control>
+            <ControlGroup.Description>
+                {description}
+            </ControlGroup.Description>
+        </ControlGroup>
+    );
+}
diff --git a/src/ui/popup/main-page/index.tsx b/src/ui/popup/main-page/index.tsx
new file mode 100644
index 00000000..99d602d9
--- /dev/null
+++ b/src/ui/popup/main-page/index.tsx
@@ -0,0 +1,47 @@
+import {m} from 'malevic';
+import {Button} from '../../controls';
+import {ViewProps} from '../types';
+import AppSwitch from './app-switch';
+import SiteToggleGroup from './site-toggle';
+import ThemeGroup from './theme-group';
+
+function SwitchGroup(props: ViewProps) {
+    return (
+        <Array>
+            <AppSwitch {...props} />
+            <SiteToggleGroup {...props} />
+        </Array>
+    );
+}
+
+function SettingsNavButton(props: {onClick: () => void}) {
+    return (
+        <Button class="m-settings-button" onclick={props.onClick}>
+            <span class="m-settings-button__content">
+                <span class="m-settings-button__icon" />
+                <span class="m-settings-button__text">Settings</span>
+            </span>
+        </Button>
+    );
+}
+
+type MainPageProps = ViewProps & {
+    onThemeNavClick: () => void;
+    onSettingsNavClick: () => void;
+};
+
+export default function MainPage(props: MainPageProps) {
+    return (
+        <Array>
+            <section class="m-section">
+                <SwitchGroup {...props} />
+            </section>
+            <section class="m-section">
+                <ThemeGroup {...props} />
+            </section>
+            <section class="m-section">
+                <SettingsNavButton onClick={props.onSettingsNavClick} />
+            </section>
+        </Array>
+    );
+}
diff --git a/src/ui/popup/main-page/site-toggle.tsx b/src/ui/popup/main-page/site-toggle.tsx
new file mode 100644
index 00000000..3ad81577
--- /dev/null
+++ b/src/ui/popup/main-page/site-toggle.tsx
@@ -0,0 +1,21 @@
+import {m} from 'malevic';
+import {isURLEnabled} from '../../../utils/url';
+import SiteToggle from '../components/site-toggle';
+import ControlGroup from '../control-group';
+import {ViewProps} from '../types';
+
+export default function SiteToggleGroup(props: ViewProps) {
+    const isSiteEnabled = isURLEnabled(props.tab.url, props.data.settings, props.tab);
+    return (
+        <ControlGroup class="site-toggle-group">
+            <ControlGroup.Control class="site-toggle-group__control">
+                <SiteToggle {...props} />
+            </ControlGroup.Control>
+            <ControlGroup.Description>
+                {isSiteEnabled ?
+                    'Enabled for current website' :
+                    'Disabled for current website'}
+            </ControlGroup.Description>
+        </ControlGroup>
+    );
+}
diff --git a/src/ui/popup/main-page/style.less b/src/ui/popup/main-page/style.less
new file mode 100644
index 00000000..e9cfba89
--- /dev/null
+++ b/src/ui/popup/main-page/style.less
@@ -0,0 +1,52 @@
+@import "../../theme.less";
+
+.app-switch {
+    &__control {
+        width: calc(100% - @size-border * 2);
+    }
+}
+
+.site-toggle-group {
+    .site-toggle {
+        width: calc(100% - @size-border * 2);
+    }
+}
+
+.theme-group {
+    &__presets-wrapper {
+        background-color: @color-control-active;
+        border: @size-border solid @color-border;
+        height: @size-control-inner;
+    }
+
+    &__controls-wrapper {
+        border-bottom: @size-border solid @color-border;
+        border-left: @size-border solid @color-border;
+        border-right: @size-border solid @color-border;
+        padding: @indent-small;
+    }
+
+    &__more-button {
+        background-color: @color-control-active;
+        background-image: @icon-right;
+        background-position: center right 0.25rem;
+        background-repeat: no-repeat;
+        background-size: 1rem;
+        border: none;
+        border-radius: @size-control-inner / 2;
+        height: @size-control-inner - 0.25rem;
+        margin-bottom: 0.125rem;
+        margin-top: @indent-small + 0.125rem;
+        width: 100%;
+    }
+
+    &__description {
+        display: inline-block;
+        font-size: @size-text-small;
+        line-height: @size-text-small-height;
+        text-align: center;
+        -moz-user-select: none;
+        user-select: none;
+        width: 100%;
+    }
+}
diff --git a/src/ui/popup/main-page/theme-group.tsx b/src/ui/popup/main-page/theme-group.tsx
new file mode 100644
index 00000000..a2cc59c4
--- /dev/null
+++ b/src/ui/popup/main-page/theme-group.tsx
@@ -0,0 +1,57 @@
+import {m} from 'malevic';
+import {Theme} from '../../../definitions';
+import {Button} from '../../controls';
+import {Brightness, Contrast, Scheme, Mode} from '../theme/controls';
+import ThemePresetPicker from '../theme/preset-picker';
+import {getCurrentThemePreset} from '../theme/utils';
+import {ViewProps} from '../types';
+
+function ThemeControls(props: {theme: Theme; onChange: (theme: Partial<Theme>) => void}) {
+    const {theme, onChange} = props;
+    return (
+        <section class="m-section m-theme-controls">
+            <Brightness
+                value={theme.brightness}
+                onChange={(v) => onChange({brightness: v})}
+            />
+            <Contrast
+                value={theme.contrast}
+                onChange={(v) => onChange({contrast: v})}
+            />
+            <Scheme
+                isDark={theme.mode === 1}
+                onChange={(isDark) => onChange({mode: isDark ? 1 : 0})}
+            />
+            <Mode
+                mode={theme.engine}
+                onChange={(mode) => onChange({engine: mode})}
+            />
+        </section>
+    );
+}
+
+export default function ThemeGroup(props: ViewProps & {onThemeNavClick: () => void}) {
+    const preset = getCurrentThemePreset(props);
+
+    return (
+        <div class="theme-group">
+            <div class="theme-group__presets-wrapper">
+                <ThemePresetPicker {...props} />
+            </div>
+            <div class="theme-group__controls-wrapper">
+                <ThemeControls
+                    theme={preset.theme}
+                    onChange={preset.change}
+                />
+                <Button class="theme-group__more-button" onclick={props.onThemeNavClick}>
+                    See all options
+                </Button>
+            </div>
+            <label class="theme-group__description">
+                Configure theme
+            </label>
+        </div>
+    );
+}
+
+
diff --git a/src/ui/popup/page-viewer/index.tsx b/src/ui/popup/page-viewer/index.tsx
new file mode 100644
index 00000000..e8d34e0f
--- /dev/null
+++ b/src/ui/popup/page-viewer/index.tsx
@@ -0,0 +1,50 @@
+import {m} from 'malevic';
+import {Button} from '../../controls';
+
+interface PageProps {
+    id: string;
+    active?: boolean;
+    first?: boolean;
+    onBackButtonClick?: () => void;
+}
+
+export function Page(props: PageProps, content: Malevic.Spec) {
+    return (
+        <div class={{'page': true, 'page--active': props.active}}>
+            <div class="page__content">
+                {content}
+            </div>
+            {props.first ? null : (
+                <Button class="page__back-button" onclick={props.onBackButtonClick}>
+                    Back
+                </Button>
+            )}
+        </div>
+    );
+}
+
+interface PageViewerProps {
+    activePage: string;
+    onBackButtonClick: () => void;
+}
+
+export function PageViewer(
+    props: PageViewerProps,
+    ...pages: Malevic.ComponentSpec<PageProps>[]
+) {
+    return (
+        <div class="page-viewer">
+            {pages.map((pageSpec, i) => {
+                return {
+                    ...pageSpec,
+                    props: {
+                        ...pageSpec.props,
+                        active: props.activePage === pageSpec.props.id,
+                        first: i === 0,
+                        onBackButtonClick: props.onBackButtonClick,
+                    },
+                } as Malevic.ComponentSpec<PageProps>;
+            })}
+        </div>
+    );
+}
diff --git a/src/ui/popup/page-viewer/style.less b/src/ui/popup/page-viewer/style.less
new file mode 100644
index 00000000..499d2f96
--- /dev/null
+++ b/src/ui/popup/page-viewer/style.less
@@ -0,0 +1,33 @@
+@import "../../theme.less";
+
+.page-viewer {
+    height: 100%;
+    position: relative;
+}
+
+.page {
+    align-items: stretch;
+    background-color: @color-back;
+    display: none;
+    flex-direction: column;
+    height: 100%;
+    justify-content: stretch;
+    left: 0;
+    position: absolute;
+    top: 0;
+    width: 100%;
+
+    &--active {
+        display: flex;
+    }
+
+    &__back-button {
+        background-image: @icon-left;
+        background-position: center left 0.25rem;
+        background-repeat: no-repeat;
+        background-size: 1rem;
+        flex: none;
+        margin-top: @indent-small;
+        width: calc(100% - 2 * @size-border);
+    }
+}
diff --git a/src/ui/popup/settings-page/devtools.tsx b/src/ui/popup/settings-page/devtools.tsx
new file mode 100644
index 00000000..a4a07be8
--- /dev/null
+++ b/src/ui/popup/settings-page/devtools.tsx
@@ -0,0 +1,67 @@
+import {m} from 'malevic';
+import ThemeEngines from '../../../generators/theme-engines';
+import {getLocalMessage} from '../../../utils/locales';
+import {Button} from '../../controls';
+import ControlGroup from '../control-group';
+import {ViewProps} from '../types';
+import {isFirefox} from '../../../utils/platform';
+
+function getExistingDevToolsWindow() {
+    return new Promise<chrome.windows.Window>((resolve) => {
+        chrome.windows.getAll({
+            populate: true,
+            windowTypes: ['popup']
+        }, (w) => {
+            for (const window of w) {
+                if (window.tabs[0].url.endsWith('ui/devtools/index.html')) {
+                    resolve(window);
+                    return;
+                }
+            }
+            resolve(null);
+        });
+    });
+}
+
+async function openDevTools() {
+    const devToolsWindow = await getExistingDevToolsWindow();
+    if (devToolsWindow) {
+        chrome.windows.update(devToolsWindow.id, {'focused': true});
+    } else {
+        chrome.windows.create({
+            type: 'popup',
+            url: isFirefox() ? '../devtools/index.html' : 'ui/devtools/index.html',
+            width: 600,
+            height: 600,
+        });
+    }
+}
+
+export default function DevToolsGroup(props: ViewProps) {
+    const globalThemeEngine = props.data.settings.theme.engine;
+    const devtoolsData = props.data.devtools;
+    const hasCustomFixes = (
+        (globalThemeEngine === ThemeEngines.dynamicTheme && devtoolsData.hasCustomDynamicFixes) ||
+        ([ThemeEngines.cssFilter, ThemeEngines.svgFilter].includes(globalThemeEngine) && devtoolsData.hasCustomFilterFixes) ||
+        (globalThemeEngine === ThemeEngines.staticTheme && devtoolsData.hasCustomStaticFixes)
+    );
+
+    return (
+        <ControlGroup>
+            <ControlGroup.Control>
+                <Button
+                    onclick={openDevTools}
+                    class={{
+                        'dev-tools-button': true,
+                        'dev-tools-button--has-custom-fixes': hasCustomFixes,
+                    }}
+                >
+                    🛠 {getLocalMessage('open_dev_tools')}
+                </Button>
+            </ControlGroup.Control>
+            <ControlGroup.Description>
+                Make a fix for a website
+            </ControlGroup.Description>
+        </ControlGroup>
+    );
+}
diff --git a/src/ui/popup/settings-page/enabled-by-default.tsx b/src/ui/popup/settings-page/enabled-by-default.tsx
new file mode 100644
index 00000000..6da8d66d
--- /dev/null
+++ b/src/ui/popup/settings-page/enabled-by-default.tsx
@@ -0,0 +1,20 @@
+import {m} from 'malevic';
+import CheckButton from '../check-button';
+import {ViewProps} from '../types';
+
+export default function EnabledByDefaultGroup(props: ViewProps) {
+    function onEnabledByDefaultChange(checked: boolean) {
+        props.actions.changeSettings({applyToListedOnly: !checked});
+    }
+
+    return (
+        <CheckButton
+            checked={!props.data.settings.applyToListedOnly}
+            label="Enable by default"
+            description={props.data.settings.applyToListedOnly ?
+                'Disabled on all websites by default' :
+                'Enabled on all websites by default'}
+            onChange={onEnabledByDefaultChange}
+        />
+    );
+}
diff --git a/src/ui/popup/settings-page/help.tsx b/src/ui/popup/settings-page/help.tsx
new file mode 100644
index 00000000..f0c7655e
--- /dev/null
+++ b/src/ui/popup/settings-page/help.tsx
@@ -0,0 +1,21 @@
+import {m} from 'malevic';
+import {getHelpURL} from '../../../utils/links';
+import {getLocalMessage} from '../../../utils/locales';
+import ControlGroup from '../control-group';
+
+export default function HelpGroup() {
+    return (
+        <ControlGroup>
+            <ControlGroup.Control>
+                <a class="m-help-button" href={getHelpURL()} target="_blank" rel="noopener noreferrer">
+                    <span class="m-help-button__text">
+                        {getLocalMessage('help')}
+                    </span>
+                </a>
+            </ControlGroup.Control>
+            <ControlGroup.Description>
+                Read the manual
+            </ControlGroup.Description>
+        </ControlGroup>
+    );
+}
diff --git a/src/ui/popup/settings-page/index.tsx b/src/ui/popup/settings-page/index.tsx
new file mode 100644
index 00000000..7c53bd49
--- /dev/null
+++ b/src/ui/popup/settings-page/index.tsx
@@ -0,0 +1,17 @@
+import {m} from 'malevic';
+import {ViewProps} from '../types';
+import DevToolsGroup from './devtools';
+import EnabledByDefaultGroup from './enabled-by-default';
+import HelpGroup from './help';
+import InvertPDF from './invert-pdf';
+
+export default function SettingsPage(props: ViewProps) {
+    return (
+        <section class="m-section">
+            <EnabledByDefaultGroup {...props} />
+            <InvertPDF {...props} />
+            <DevToolsGroup {...props} />
+            <HelpGroup />
+        </section>
+    );
+}
diff --git a/src/ui/popup/settings-page/invert-pdf.tsx b/src/ui/popup/settings-page/invert-pdf.tsx
new file mode 100644
index 00000000..66b3407f
--- /dev/null
+++ b/src/ui/popup/settings-page/invert-pdf.tsx
@@ -0,0 +1,20 @@
+import {m} from 'malevic';
+import {ViewProps} from '../types';
+import CheckButton from '../check-button';
+
+export default function InvertPDF(props: ViewProps) {
+    function onInvertPDFChange(checked: boolean) {
+        props.actions.changeSettings({enableForPDF: checked});
+    }
+
+    return (
+        <CheckButton
+            checked={props.data.settings.enableForPDF}
+            label='Enable for PDF files'
+            description={props.data.settings.enableForPDF ?
+                'Enabled for PDF documents' :
+                'Disabled for PDF documents'}
+            onChange={onInvertPDFChange}
+        />
+    );
+}
diff --git a/src/ui/popup/style.less b/src/ui/popup/style.less
index d0d954dc..a967c4ac 100644
--- a/src/ui/popup/style.less
+++ b/src/ui/popup/style.less
@@ -188,4 +188,4 @@ body {
     }
 }
 
-@import "./components/mobile/style";
+@import "./body/style";
diff --git a/src/ui/popup/theme/controls/brightness.tsx b/src/ui/popup/theme/controls/brightness.tsx
new file mode 100644
index 00000000..d5642118
--- /dev/null
+++ b/src/ui/popup/theme/controls/brightness.tsx
@@ -0,0 +1,20 @@
+import {m} from 'malevic';
+import {getLocalMessage} from '../../../../utils/locales';
+import {Slider} from '../../../controls';
+import {formatPercent} from './format';
+import ThemeControl from './theme-control';
+
+export default function Brightness(props: {value: number; onChange: (v: number) => void}) {
+    return (
+        <ThemeControl label={getLocalMessage('brightness')}>
+            <Slider
+                value={props.value}
+                min={50}
+                max={150}
+                step={1}
+                formatValue={formatPercent}
+                onChange={props.onChange}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/contrast.tsx b/src/ui/popup/theme/controls/contrast.tsx
new file mode 100644
index 00000000..b03e83b0
--- /dev/null
+++ b/src/ui/popup/theme/controls/contrast.tsx
@@ -0,0 +1,20 @@
+import {m} from 'malevic';
+import {getLocalMessage} from '../../../../utils/locales';
+import {Slider} from '../../../controls';
+import {formatPercent} from './format';
+import ThemeControl from './theme-control';
+
+export default function Contrast(props: {value: number; onChange: (v: number) => void}) {
+    return (
+        <ThemeControl label={getLocalMessage('contrast')}>
+            <Slider
+                value={props.value}
+                min={50}
+                max={150}
+                step={1}
+                formatValue={formatPercent}
+                onChange={props.onChange}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/format.ts b/src/ui/popup/theme/controls/format.ts
new file mode 100644
index 00000000..c676f2b3
--- /dev/null
+++ b/src/ui/popup/theme/controls/format.ts
@@ -0,0 +1,3 @@
+export function formatPercent(v: number) {
+    return `${v}%`;
+}
diff --git a/src/ui/popup/theme/controls/grayscale.tsx b/src/ui/popup/theme/controls/grayscale.tsx
new file mode 100644
index 00000000..5b7bd28c
--- /dev/null
+++ b/src/ui/popup/theme/controls/grayscale.tsx
@@ -0,0 +1,20 @@
+import {m} from 'malevic';
+import {getLocalMessage} from '../../../../utils/locales';
+import {Slider} from '../../../controls';
+import {formatPercent} from './format';
+import ThemeControl from './theme-control';
+
+export default function Grayscale(props: {value: number; onChange: (v: number) => void}) {
+    return (
+        <ThemeControl label={getLocalMessage('grayscale')}>
+            <Slider
+                value={props.value}
+                min={0}
+                max={100}
+                step={1}
+                formatValue={formatPercent}
+                onChange={props.onChange}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/index.tsx b/src/ui/popup/theme/controls/index.tsx
new file mode 100644
index 00000000..10fb8318
--- /dev/null
+++ b/src/ui/popup/theme/controls/index.tsx
@@ -0,0 +1,15 @@
+import Brightness from './brightness';
+import Contrast from './contrast';
+import Grayscale from './grayscale';
+import Mode from './mode';
+import Scheme from './scheme';
+import Sepia from './sepia';
+
+export {
+    Brightness,
+    Contrast,
+    Grayscale,
+    Mode,
+    Scheme,
+    Sepia,
+};
diff --git a/src/ui/popup/theme/controls/mode.tsx b/src/ui/popup/theme/controls/mode.tsx
new file mode 100644
index 00000000..55fe7b0f
--- /dev/null
+++ b/src/ui/popup/theme/controls/mode.tsx
@@ -0,0 +1,25 @@
+import {m} from 'malevic';
+import ThemeEngines from '../../../../generators/theme-engines';
+import {getLocalMessage} from '../../../../utils/locales';
+import {DropDown} from '../../../controls';
+import ThemeControl from './theme-control';
+
+export default function Mode(props: {mode: string; onChange: (mode: string) => void}) {
+    const modes = [
+        [ThemeEngines.dynamicTheme, getLocalMessage('engine_dynamic')],
+        [ThemeEngines.cssFilter, getLocalMessage('engine_filter')],
+        [ThemeEngines.staticTheme, getLocalMessage('engine_static')],
+    ];
+    return (
+        <ThemeControl label="Mode">
+            <DropDown
+                selected={modes.find((m) => m[0] === props.mode)[1]}
+                values={modes.map((m) => m[1])}
+                onChange={(v) => {
+                    const mode = modes.find((m) => m[1] === v)[0];
+                    props.onChange(mode);
+                }}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/scheme.tsx b/src/ui/popup/theme/controls/scheme.tsx
new file mode 100644
index 00000000..be06dd0e
--- /dev/null
+++ b/src/ui/popup/theme/controls/scheme.tsx
@@ -0,0 +1,21 @@
+import {m} from 'malevic';
+import {getLocalMessage} from '../../../../utils/locales';
+import {DropDown} from '../../../controls';
+import ThemeControl from './theme-control';
+
+export default function Scheme(props: {isDark: boolean; onChange: (dark: boolean) => void}) {
+    const valDark = getLocalMessage('dark');
+    const valLight = getLocalMessage('light');
+    return (
+        <ThemeControl label="Scheme">
+            <DropDown
+                selected={props.isDark ? valDark : valLight}
+                values={[
+                    valDark,
+                    valLight,
+                ]}
+                onChange={(v) => props.onChange(v === valDark)}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/sepia.tsx b/src/ui/popup/theme/controls/sepia.tsx
new file mode 100644
index 00000000..9fa1406f
--- /dev/null
+++ b/src/ui/popup/theme/controls/sepia.tsx
@@ -0,0 +1,20 @@
+import {m} from 'malevic';
+import {getLocalMessage} from '../../../../utils/locales';
+import {Slider} from '../../../controls';
+import {formatPercent} from './format';
+import ThemeControl from './theme-control';
+
+export default function Sepia(props: {value: number; onChange: (v: number) => void}) {
+    return (
+        <ThemeControl label={getLocalMessage('sepia')}>
+            <Slider
+                value={props.value}
+                min={0}
+                max={100}
+                step={1}
+                formatValue={formatPercent}
+                onChange={props.onChange}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/theme-control.less b/src/ui/popup/theme/controls/theme-control.less
new file mode 100644
index 00000000..581f49c0
--- /dev/null
+++ b/src/ui/popup/theme/controls/theme-control.less
@@ -0,0 +1,17 @@
+.theme-control {
+    align-items: center;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    min-height: @size-control-inner;
+    width: 100%;
+
+    &__label {
+        font-size: @size-text-normal;
+        line-height: @size-text-normal;
+        text-align: left;
+        -moz-user-select: none;
+        user-select: none;
+        width: 50%;
+    }
+}
diff --git a/src/ui/popup/theme/controls/theme-control.tsx b/src/ui/popup/theme/controls/theme-control.tsx
new file mode 100644
index 00000000..64f5a0d7
--- /dev/null
+++ b/src/ui/popup/theme/controls/theme-control.tsx
@@ -0,0 +1,12 @@
+import {m} from 'malevic';
+
+export default function ThemeControl(props: {label: Malevic.Child}, control: Malevic.Child) {
+    return (
+        <span class="theme-control">
+            <label class="theme-control__label">
+                {props.label}
+            </label>
+            {control}
+        </span>
+    );
+}
diff --git a/src/ui/popup/theme/page/index.tsx b/src/ui/popup/theme/page/index.tsx
new file mode 100644
index 00000000..94f80ca5
--- /dev/null
+++ b/src/ui/popup/theme/page/index.tsx
@@ -0,0 +1,38 @@
+import {m} from 'malevic';
+import {ViewProps} from '../../types';
+import {Brightness, Contrast, Grayscale, Mode, Scheme, Sepia} from '../controls';
+import ThemePresetPicker from '../preset-picker';
+import {getCurrentThemePreset} from '../utils';
+
+export default function ThemePage(props: ViewProps) {
+    const {theme, change} = getCurrentThemePreset(props);
+
+    return (
+        <section class="m-section theme-page">
+            <ThemePresetPicker {...props} /><Brightness
+                value={theme.brightness}
+                onChange={(v) => change({brightness: v})}
+            />
+            <Contrast
+                value={theme.contrast}
+                onChange={(v) => change({contrast: v})}
+            />
+            <Sepia
+                value={theme.sepia}
+                onChange={(v) => change({sepia: v})}
+            />
+            <Grayscale
+                value={theme.grayscale}
+                onChange={(v) => change({grayscale: v})}
+            />
+            <Scheme
+                isDark={theme.mode === 1}
+                onChange={(isDark) => change({mode: isDark ? 1 : 0})}
+            />
+            <Mode
+                mode={theme.engine}
+                onChange={(mode) => change({engine: mode})}
+            />
+        </section>
+    );
+}
diff --git a/src/ui/popup/theme/page/style.less b/src/ui/popup/theme/page/style.less
new file mode 100644
index 00000000..f0d75890
--- /dev/null
+++ b/src/ui/popup/theme/page/style.less
@@ -0,0 +1,25 @@
+@import "../../../theme.less";
+
+.theme-page {
+    .theme-preset-picker.dropdown {
+        border-radius: unset;
+        height: @size-control-inner + 2 * @size-border;
+    }
+
+    .theme-preset-picker {
+        .dropdown {
+            &__selected {
+                border-radius: unset;
+                height: @size-control-inner + 2 * @size-border;
+                line-height: @size-control-inner + 2 * @size-border;
+            }
+        
+            &__list {
+                &__item {
+                    height: @size-control-inner + 2 * @size-border;
+                    line-height: @size-control-inner + 2 * @size-border;
+                }
+            }
+        }
+    }
+}
diff --git a/src/ui/popup/theme/preset-picker/index.tsx b/src/ui/popup/theme/preset-picker/index.tsx
new file mode 100644
index 00000000..cd2f75eb
--- /dev/null
+++ b/src/ui/popup/theme/preset-picker/index.tsx
@@ -0,0 +1,38 @@
+import {m} from 'malevic';
+import {isURLInList, getURLHost} from '../../../../utils/url';
+import {DropDown} from '../../../controls';
+import {ViewProps} from '../../types';
+
+export default function PresetPicker(props: ViewProps) {
+    const host = getURLHost(props.tab.url || '');
+    const custom = props.data.settings.customThemes.find(
+        ({url}) => isURLInList(props.tab.url, url)
+    );
+
+    const defaultPresetName = 'Default theme';
+    const customPresetName = `Custom for ${host}`;
+
+    function onPresetChange(name: string) {
+        const filteredCustomThemes = props.data.settings.customThemes.filter(({url}) => !isURLInList(props.tab.url, url));
+        if (name === defaultPresetName) {
+            props.actions.changeSettings({customThemes: filteredCustomThemes});
+        } else if (name === customPresetName) {
+            const extended = filteredCustomThemes.concat({
+                url: [host],
+                theme: {...props.data.settings.theme},
+            });
+            props.actions.changeSettings({customThemes: extended});
+        }
+    }
+    return (
+        <DropDown
+            class="theme-preset-picker"
+            selected={custom ? customPresetName : defaultPresetName}
+            values={[
+                defaultPresetName,
+                customPresetName,
+            ]}
+            onChange={onPresetChange}
+        />
+    );
+}
diff --git a/src/ui/popup/theme/preset-picker/style.less b/src/ui/popup/theme/preset-picker/style.less
new file mode 100644
index 00000000..ac0d25f1
--- /dev/null
+++ b/src/ui/popup/theme/preset-picker/style.less
@@ -0,0 +1,13 @@
+.theme-preset-picker {
+    .dropdown {
+        &__selected {
+            border-radius: unset;
+            height: @size-control-inner;
+            line-height: @size-control-inner;
+            margin-top: 0;
+        }
+        &__list {
+            border-radius: unset;
+        }
+    }
+}
diff --git a/src/ui/popup/theme/utils.ts b/src/ui/popup/theme/utils.ts
new file mode 100644
index 00000000..e81a6c23
--- /dev/null
+++ b/src/ui/popup/theme/utils.ts
@@ -0,0 +1,28 @@
+import {Theme} from '../../../definitions';
+import {isURLInList} from '../../../utils/url';
+import {ViewProps} from '../types';
+
+export function getCurrentThemePreset(props: ViewProps) {
+    const custom = props.data.settings.customThemes.find(
+        ({url}) => isURLInList(props.tab.url, url)
+    );
+    const theme = custom ?
+        custom.theme :
+        props.data.settings.theme;
+
+    function setTheme(config: Partial<Theme>) {
+        if (custom) {
+            custom.theme = {...custom.theme, ...config};
+            props.actions.changeSettings({
+                customThemes: props.data.settings.customThemes,
+            });
+        } else {
+            props.actions.setTheme(config);
+        }
+    }
+
+    return {
+        theme,
+        change: setTheme,
+    };
+}
\ No newline at end of file
diff --git a/src/ui/popup/types.d.ts b/src/ui/popup/types.d.ts
new file mode 100644
index 00000000..64c90090
--- /dev/null
+++ b/src/ui/popup/types.d.ts
@@ -0,0 +1,7 @@
+import {ExtensionData, ExtensionActions, TabInfo} from '../../definitions';
+
+export interface ViewProps {
+    actions: ExtensionActions;
+    data: ExtensionData;
+    tab: TabInfo;
+}
diff --git a/src/utils/platform.ts b/src/utils/platform.ts
index 806fc78f..31cec82e 100644
--- a/src/utils/platform.ts
+++ b/src/utils/platform.ts
@@ -1,3 +1,7 @@
+export function isChromiumBased() {
+    return navigator.userAgent.toLowerCase().includes('chrome') || navigator.userAgent.toLowerCase().includes('chromium');
+}
+
 export function isFirefox() {
     return navigator.userAgent.includes('Firefox');
 }
diff --git a/src/utils/url.ts b/src/utils/url.ts
index 5395384a..6a295aac 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -96,7 +96,36 @@ function createUrlRegex(urlTemplate: string): RegExp {
     return new RegExp(result, 'i');
 }
 
+export function isPDF(url: string) {
+    if (url.includes('.pdf')) {
+        if (url.includes('?')) {
+            url = url.substring(0, url.lastIndexOf('?'));
+        }
+        if (url.includes('#')) {
+            url = url.substring(0, url.lastIndexOf('#'));
+        }
+        if (url.endsWith('.pdf')) {
+            for (let i = url.length; 0 < i; i--) {
+                if (url[i] === '=') {
+                    return false;
+                } else if (url[i] === '/') {
+                    return true;
+                }
+            }
+        } else {
+            return false;
+        }
+    }
+    return false;
+}
+
 export function isURLEnabled(url: string, userSettings: UserSettings, {isProtected, isInDarkList}) {
+    if (isPDF(url) && userSettings.enableForPDF) {
+        return true;
+    }
+    if (isPDF(url) && !userSettings.enableForPDF) {
+        return false;
+    }
     if (isProtected) {
         return false;
     }


Test patch:
diff --git a/tests/config.tests.ts b/tests/config.tests.ts
index 29ddeff0..36aa7548 100644
--- a/tests/config.tests.ts
+++ b/tests/config.tests.ts
@@ -62,7 +62,7 @@ test('Dynamic Theme Fixes config', async () => {
     expect(fixes.map(({url}) => url[0])).toEqual(fixes.map(({url}) => url[0]).sort(compareURLPatterns));
 
     // selectors should have no comma
-    expect(fixes.every(({invert}) => (invert || []).every((s) => s.indexOf(',') < 0))).toBe(true);
+    expect(fixes.every(({invert, ignoreInlineStyle}) => (invert || []).concat(ignoreInlineStyle || []).every((s) => s.indexOf(',') < 0))).toBe(true);
 
     // fixes are properly formatted
     expect(throwIfDifferent(file, formatDynamicThemeFixes(fixes), 'Dynamic fixes format error')).not.toThrow();
@@ -81,9 +81,13 @@ test('Dynamic Theme Fixes config', async () => {
         'twitter.com',
         'UNSUPPORTED', 'a', 'b',
         'INVERT', 'c', 'd',
+        '========',
+        'wikipedia.org',
+        'IGNORE INLINE STYLE', 'a', 'b',
     ].join('\r\n'))).toEqual([
         {url: ['inbox.google.com', 'mail.google.com'], invert: ['a', 'b'], css: '.x { color: white !important; }'},
         {url: ['twitter.com'], invert: ['c', 'd']},
+        {url: ['wikipedia.org'], ignoreInlineStyle: ['a', 'b']},
     ]);
 });
 
diff --git a/tests/inject/dynamic.tests.ts b/tests/inject/dynamic.tests.ts
new file mode 100644
index 00000000..45fcc458
--- /dev/null
+++ b/tests/inject/dynamic.tests.ts
@@ -0,0 +1,10 @@
+import {getAbsoluteURL} from '../../src/inject/dynamic-theme/url';
+
+test('Absolute URL', () => {
+    expect(getAbsoluteURL('https://www.google.com', '/image.jpg')).toBe('https://www.google.com/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/path', '/image.jpg')).toBe('https://www.google.com/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/path', 'image.jpg')).toBe('https://www.google.com/path/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/long/path', '../image.jpg')).toBe('https://www.google.com/long/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/path/page.html', 'image.jpg')).toBe('https://www.google.com/path/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/path/page.html', '../image.jpg')).toBe('https://www.google.com/image.jpg');
+});
diff --git a/tests/inject/jest.config.js b/tests/inject/jest.config.js
new file mode 100644
index 00000000..bb1ff3e4
--- /dev/null
+++ b/tests/inject/jest.config.js
@@ -0,0 +1,26 @@
+module.exports = {
+    verbose: true,
+    testEnvironment: 'jsdom',
+    transform: {
+        '^.+\\.ts(x?)$': 'ts-jest'
+    },
+    testRegex: 'tests/inject/.*\\.tests\\.ts(x?)$',
+    moduleFileExtensions: [
+        'ts',
+        'tsx',
+        'js'
+    ],
+    collectCoverage: false,
+    coverageDirectory: 'coverage',
+    collectCoverageFrom: [
+        'src/**/*.{ts,tsx}'
+    ],
+    coveragePathIgnorePatterns: [
+        '^.+\\.d\\.ts$'
+    ],
+    globals: {
+        'ts-jest': {
+            tsConfig: './tests/inject/tsconfig.json'
+        }
+    }
+};
diff --git a/tests/inject/tsconfig.json b/tests/inject/tsconfig.json
new file mode 100644
index 00000000..8b2052d4
--- /dev/null
+++ b/tests/inject/tsconfig.json
@@ -0,0 +1,10 @@
+{
+    "extends": "../../src/tsconfig",
+    "compilerOptions": {
+        "module": "commonjs",
+        "outDir": "",
+        "types": [
+            "jest"
+        ]
+    }
+}
diff --git a/tests/jest.config.js b/tests/jest.config.js
index e4f41732..5482690a 100644
--- a/tests/jest.config.js
+++ b/tests/jest.config.js
@@ -4,7 +4,7 @@ module.exports = {
     transform: {
         '^.+\\.ts(x?)$': 'ts-jest'
     },
-    testRegex: 'tests/.*\\.tests\\.ts(x?)$',
+    testRegex: 'tests/[^/]*\\.tests\\.ts(x?)$',
     moduleFileExtensions: [
         'ts',
         'tsx',
diff --git a/tests/url.tests.ts b/tests/url.tests.ts
index ca6b1d46..a0b78348 100644
--- a/tests/url.tests.ts
+++ b/tests/url.tests.ts
@@ -1,4 +1,4 @@
-import {isURLEnabled} from '../src/utils/url';
+import {isURLEnabled, isPDF} from '../src/utils/url';
 import {UserSettings} from '../src/definitions';
 
 test('URL is enabled', () => {
@@ -67,6 +67,16 @@ test('URL is enabled', () => {
         {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: true} as UserSettings,
         {isProtected: true, isInDarkList: false},
     )).toBe(false);
+    expect(isURLEnabled(
+        'https://microsoftedge.microsoft.com/addons',
+        {siteList: ['microsoftedge.microsoft.com'], siteListEnabled: [], applyToListedOnly: false} as UserSettings,
+        {isProtected: true, isInDarkList: false},
+    )).toBe(false);
+    expect(isURLEnabled(
+        'https://microsoftedge.microsoft.com/addons',
+        {siteList: ['microsoftedge.microsoft.com'], siteListEnabled: [], applyToListedOnly: true} as UserSettings,
+        {isProtected: true, isInDarkList: false},
+    )).toBe(false);
     expect(isURLEnabled(
         'https://darkreader.org/',
         {siteList: [], siteListEnabled: [], applyToListedOnly: false} as UserSettings,
@@ -77,6 +87,43 @@ test('URL is enabled', () => {
         {siteList: ['darkreader.org'], siteListEnabled: [], applyToListedOnly: true} as UserSettings,
         {isProtected: false, isInDarkList: true},
     )).toBe(true);
+    expect(isURLEnabled(
+        'https://www.google.com/file.pdf',
+        {enableForPDF: true, siteList: ['darkreader.org'], siteListEnabled: [], applyToListedOnly: true} as UserSettings,
+        {isProtected: false, isInDarkList: false},
+    )).toBe(true);
+    expect(isURLEnabled(
+        'https://www.google.com/file.pdf/resource',
+        {enableForPDF: true, siteList: ['darkreader.org'], siteListEnabled: [], applyToListedOnly: true} as UserSettings,
+        {isProtected: false, isInDarkList: false},
+    )).toBe(false);
+    expect(isURLEnabled(
+        'https://www.google.com/file.pdf/resource',
+        {enableForPDF: true, siteList: ['darkreader.org'], siteListEnabled: [], applyToListedOnly: false} as UserSettings,
+        {isProtected: false, isInDarkList: false},
+    )).toBe(true);
+    expect(isURLEnabled(
+        'https://www.google.com/very/good/hidden/folder/pdf#file.pdf',
+        {enableForPDF: true, siteList: ['https://www.google.com/very/good/hidden/folder/pdf#file.pdf'], siteListEnabled: [], applyToListedOnly: false} as UserSettings,
+        {isProtected: false, isInDarkList: false},
+    )).toBe(false);
+
+    // Test for PDF enabling
+    expect(isPDF(
+        'https://www.google.com/file.pdf'
+    )).toBe(true);
+    expect(isPDF(
+        'https://www.google.com/file.pdf?id=2'
+    )).toBe(true);
+    expect(isPDF(
+        'https://www.google.com/file.pdf/resource'
+    )).toBe(false);
+    expect(isPDF(
+        'https://www.google.com/resource?file=file.pdf'
+    )).toBe(false);
+    expect(isPDF(
+        'https://www.google.com/very/good/hidden/folder/pdf#file.pdf'
+    )).toBe(false);
 
     // Temporary Dark Sites list fix
     expect(isURLEnabled(