Repository: darkreader/darkreader
Pull request #3496 (2020-08-21 20:02:24)

İETT timetable viewer fix
İETT bus timetables are unfortunately uploaded as images. This PR works around that.


Patch:
diff --git a/index.d.ts b/index.d.ts
index e7c110a4..5129cb44 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -126,6 +126,16 @@ declare namespace DarkReader {
          * ```
          */
         css: string;
+        /**
+         * List of CSS selectors where it's inline style should not be analyzed
+         * Mostly used for color pickers
+         */
+        ignoreInlineStyle: string[];
+        /**
+         * List of CSS selectors where it's image should not be analyzed
+         * Mostly used for wrongly inverted background-images
+         */
+        ignoreImageAnalysis: string[];
     }
 }
 
diff --git a/package-lock.json b/package-lock.json
index cc20a38c..749db5dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5477,7 +5477,7 @@
     },
     "path-is-absolute": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
       "dev": true
     },
diff --git a/src/background/extension.ts b/src/background/extension.ts
index c799971e..db636704 100644
--- a/src/background/extension.ts
+++ b/src/background/extension.ts
@@ -91,7 +91,7 @@ export class Extension {
         this.registerCommands();
 
         this.ready = true;
-        this.tabs.updateContentScript();
+        this.tabs.updateContentScript({runOnProtectedPages: this.user.settings.enableForProtectedPages});
 
         this.awaiting.forEach((ready) => ready());
         this.awaiting = null;
@@ -364,7 +364,7 @@ export class Extension {
     private getURLInfo(url: string): TabInfo {
         const {DARK_SITES} = this.config;
         const isInDarkList = isURLInList(url, DARK_SITES);
-        const isProtected = !canInjectScript(url);
+        const isProtected = !(this.user.settings.enableForProtectedPages || canInjectScript(url));
         return {
             url,
             isInDarkList,
diff --git a/src/background/tab-manager.ts b/src/background/tab-manager.ts
index 036f16e0..a02f7fd1 100644
--- a/src/background/tab-manager.ts
+++ b/src/background/tab-manager.ts
@@ -89,9 +89,9 @@ export default class TabManager {
         });
     }
 
-    async updateContentScript() {
+    async updateContentScript(options: {runOnProtectedPages: boolean}) {
         (await queryTabs({}))
-            .filter((tab) => canInjectScript(tab.url))
+            .filter((tab) => options.runOnProtectedPages || canInjectScript(tab.url))
             .filter((tab) => !this.ports.has(tab.id))
             .forEach((tab) => !tab.discarded && chrome.tabs.executeScript(tab.id, {
                 runAt: 'document_start',
diff --git a/src/config/dark-sites.config b/src/config/dark-sites.config
index 0cc2e731..835166fd 100644
--- a/src/config/dark-sites.config
+++ b/src/config/dark-sites.config
@@ -392,6 +392,7 @@ noisebridge.net
 notebooks.quantumstat.com
 null.media
 nulledbb.com
+obsidian.md
 obsproject.com
 offshorecorptalk.com
 ogusers.com
@@ -514,6 +515,7 @@ slither.io
 snazzah.com
 spacebattles.com
 spacespacespaaace.space
+spacestationgaming.com
 speedtest.net
 sportsurge.net
 spy-cards.lubar.me
diff --git a/src/config/dynamic-theme-fixes.config b/src/config/dynamic-theme-fixes.config
index 50d96790..f5a81ced 100644
--- a/src/config/dynamic-theme-fixes.config
+++ b/src/config/dynamic-theme-fixes.config
@@ -622,6 +622,27 @@ body[style] {
 
 ================================
 
+bestbuy.ca
+
+INVERT
+.centerContainer
+[class^="facetName"]
+[class^="title"]
+[class^="sortLabel"]
+[class^="headerText"]
+[class^="subTitle"]
+
+CSS
+div[class*="backgroundContainer"] {
+    z-index: 0 !important;
+}
+
+body {
+    background-image: initial; !important;
+}
+
+================================
+
 bfi.org.uk
 
 INVERT
@@ -1022,6 +1043,13 @@ INVERT
 
 ================================
 
+colorhunt.co
+
+IGNORE INLINE STYLE
+.palette > div
+
+================================
+
 community.ntppool.org
 
 INVERT
@@ -2655,6 +2683,21 @@ CSS
 
 ================================
 
+iett.istanbul
+
+CSS
+.LineTimeDuyuru > li:nth-child(1) {
+    display:none;
+}
+.LineTimeDuyuru::before {
+    content: "Kırmızı renkli seferler ÖHO ve OAŞ'a, beyaz renkli seferler İETT'ye aittir."
+}
+.DetailTable img {
+    filter: invert(100%) hue-rotate(180deg) !important;
+}
+
+================================
+
 ikea.*
 
 CSS
@@ -4911,6 +4954,14 @@ CSS
 
 ================================
 
+soylent.com
+
+INVERT
+.header-logo__image
+.d-header #site-logo
+
+================================
+
 speed.cloudflare.com
 
 INVERT
diff --git a/src/defaults.ts b/src/defaults.ts
index f6d182c7..09bf4855 100644
--- a/src/defaults.ts
+++ b/src/defaults.ts
@@ -55,4 +55,5 @@ export const DEFAULT_SETTINGS: UserSettings = {
     },
     previewNewDesign: false,
     enableForPDF: true,
+    enableForProtectedPages: false,
 };
diff --git a/src/definitions.d.ts b/src/definitions.d.ts
index 2e833b3e..3b20391d 100644
--- a/src/definitions.d.ts
+++ b/src/definitions.d.ts
@@ -87,6 +87,7 @@ export interface UserSettings {
     location: LocationSettings;
     previewNewDesign: boolean;
     enableForPDF: boolean;
+    enableForProtectedPages: boolean;
 }
 
 export interface TimeSettings {
diff --git a/src/ui/connect/mock.ts b/src/ui/connect/mock.ts
index 2a2c1427..14b38ea0 100644
--- a/src/ui/connect/mock.ts
+++ b/src/ui/connect/mock.ts
@@ -27,6 +27,7 @@ export function getMockData(override = {} as Partial<ExtensionData>): ExtensionD
             applyToListedOnly: false,
             changeBrowserTheme: false,
             enableForPDF: true,
+            enableForProtectedPages: false,
             notifyOfNews: false,
             syncSettings: true,
             automation: '',
diff --git a/src/ui/controls/slider/index.tsx b/src/ui/controls/slider/index.tsx
index e6eb3e66..2ea2d88f 100644
--- a/src/ui/controls/slider/index.tsx
+++ b/src/ui/controls/slider/index.tsx
@@ -1,5 +1,7 @@
 import {m} from 'malevic';
 import {getContext} from 'malevic/dom';
+import {throttle} from '../../../inject/utils/throttle';
+import {scale, clamp} from '../../../utils/math';
 
 interface SliderProps {
     value: number;
@@ -10,14 +12,6 @@ interface SliderProps {
     onChange: (value: number) => void;
 }
 
-function clamp(x: number, min: number, max: number) {
-    return Math.min(max, Math.max(min, x));
-}
-
-function scale(x: number, inLow: number, inHigh: number, outLow: number, outHigh: number) {
-    return (x - inLow) / (inHigh - inLow) * (outHigh - outLow) + outLow;
-}
-
 function stickToStep(x: number, step: number) {
     const s = Math.round(x / step) * step;
     const exp = Math.floor(Math.log10(step));
@@ -38,6 +32,8 @@ export default function Slider(props: SliderProps) {
         activeProps: SliderProps;
         trackNode: HTMLElement;
         thumbNode: HTMLElement;
+        wheelTimeoutId: number;
+        wheelValue: number;
     };
 
     store.activeProps = props;
@@ -168,11 +164,40 @@ export default function Slider(props: SliderProps) {
         stickToStep(getValue(), props.step)
     );
 
+    function scaleWheelDelta(delta: number) {
+        return scale(delta, 0, -1000, 0, props.max - props.min);
+    }
+
+    const refreshOnWheel = throttle(() => {
+        store.activeValue = stickToStep(store.wheelValue, props.step);
+        store.wheelTimeoutId = setTimeout(() => {
+            const {onChange} = store.activeProps;
+            onChange(store.activeValue);
+            store.isActive = false;
+            store.activeValue = null;
+            store.wheelValue = null;
+        }, 400);
+        context.refresh();
+    });
+
+    function onWheel(event: WheelEvent) {
+        if (store.wheelValue == null) {
+            store.wheelValue = getValue();
+        }
+        store.isActive = true;
+        clearTimeout(store.wheelTimeoutId);
+        event.preventDefault();
+        const accumulatedValue = store.wheelValue + scaleWheelDelta(event.deltaY);
+        store.wheelValue = clamp(accumulatedValue, props.min, props.max);
+        refreshOnWheel();
+    }
+
     return (
         <span
             class={{'slider': true, 'slider--active': store.isActive}}
             oncreate={onRootCreate}
             onmousedown={onPointerDown}
+            onwheel={onWheel}
         >
             <span
                 class="slider__track"
diff --git a/src/ui/popup/site-list-page/index.tsx b/src/ui/popup/site-list-page/index.tsx
index aee1b287..f45bf3d3 100644
--- a/src/ui/popup/site-list-page/index.tsx
+++ b/src/ui/popup/site-list-page/index.tsx
@@ -1,11 +1,15 @@
 import {m} from 'malevic';
 import {ViewProps} from '../types';
 import SiteList from './site-list';
+import CheckButton from '../check-button';
 
 export default function SiteListPage(props: ViewProps) {
     function onSiteListChange(sites: string[]) {
         props.actions.changeSettings({siteList: sites});
     }
+    function onEnableForProtectedPages(value: boolean) {
+        props.actions.changeSettings({enableForProtectedPages: value});
+    }
 
     const label = props.data.settings.applyToListedOnly ?
         'Enable on these websites' :
@@ -18,6 +22,14 @@ export default function SiteListPage(props: ViewProps) {
                 onChange={onSiteListChange}
             />
             <label class="site-list-page__description">Enter website name and press Enter</label>
+            <CheckButton
+                checked={props.data.settings.enableForProtectedPages}
+                onChange={onEnableForProtectedPages}
+                label={'Enable on protected pages'}
+                description={props.data.settings.enableForProtectedPages ?
+                    'Will try running on protected pages (browser settings etc)' :
+                    'Will not try running on protected pages (browser settings etc)'}
+            />
         </div>
     );
 }
diff --git a/src/utils/math.ts b/src/utils/math.ts
index 71124033..04d88d12 100644
--- a/src/utils/math.ts
+++ b/src/utils/math.ts
@@ -8,11 +8,11 @@ export function clamp(x: number, min: number, max: number) {
 
 export function multiplyMatrices(m1: number[][], m2: number[][]) {
     const result: number[][] = [];
-    for (let i = 0; i < m1.length; i++) {
+    for (let i = 0, len = m1.length; i < len; i++) {
         result[i] = [];
-        for (let j = 0; j < m2[0].length; j++) {
+        for (let j = 0, len2 = m2[0].length; j < len2; j++) {
             let sum = 0;
-            for (let k = 0; k < m1[0].length; k++) {
+            for (let k = 0, len3 = m1[0].length; k < len3; k++) {
                 sum += m1[i][k] * m2[k][j];
             }
             result[i][j] = sum;
diff --git a/src/utils/url.ts b/src/utils/url.ts
index fbae5101..7470ce7d 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -137,7 +137,7 @@ export function isPDF(url: string) {
 }
 
 export function isURLEnabled(url: string, userSettings: UserSettings, {isProtected, isInDarkList}) {
-    if (isProtected) {
+    if (isProtected && userSettings.enableForProtectedPages) {
         return false;
     }
     if (isPDF(url) && userSettings.enableForPDF) {


Test patch:
diff --git a/tests/utils/url.tests.ts b/tests/utils/url.tests.ts
index a901737f..743b107f 100644
--- a/tests/utils/url.tests.ts
+++ b/tests/utils/url.tests.ts
@@ -59,24 +59,39 @@ test('URL is enabled', () => {
     // Special URLs
     expect(isURLEnabled(
         'https://chrome.google.com/webstore',
-        {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: false} as UserSettings,
+        {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: true} as UserSettings,
         {isProtected: true, isInDarkList: false},
     )).toBe(false);
     expect(isURLEnabled(
         'https://chrome.google.com/webstore',
-        {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: true} as UserSettings,
+        {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: true, enableForProtectedPages: true} as UserSettings,
+        {isProtected: true, isInDarkList: false},
+    )).toBe(false);
+    expect(isURLEnabled(
+        'https://chrome.google.com/webstore',
+        {siteList: [], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: false} as UserSettings,
+        {isProtected: true, isInDarkList: false},
+    )).toBe(true);
+    expect(isURLEnabled(
+        'https://chrome.google.com/webstore',
+        {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: 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,
+        {siteList: ['microsoftedge.microsoft.com'], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: true} as UserSettings,
         {isProtected: true, isInDarkList: false},
     )).toBe(false);
     expect(isURLEnabled(
         'https://microsoftedge.microsoft.com/addons',
-        {siteList: ['microsoftedge.microsoft.com'], siteListEnabled: [], applyToListedOnly: true} as UserSettings,
+        {siteList: ['microsoftedge.microsoft.com'], siteListEnabled: [], applyToListedOnly: true, enableForProtectedPages: true} as UserSettings,
         {isProtected: true, isInDarkList: false},
     )).toBe(false);
+    expect(isURLEnabled(
+        'https://duckduckgo.com',
+        {siteList: [], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: true} as UserSettings,
+        {isProtected: false, isInDarkList: false},
+    )).toBe(true);
     expect(isURLEnabled(
         'https://darkreader.org/',
         {siteList: [], siteListEnabled: [], applyToListedOnly: false} as UserSettings,