(function () {
    "use strict";

    const userAgent =
        typeof navigator === "undefined"
            ? "some useragent"
            : navigator.userAgent.toLowerCase();
    const platform =
        typeof navigator === "undefined"
            ? "some platform"
            : navigator.platform.toLowerCase();
    const isChromium =
        userAgent.includes("chrome") || userAgent.includes("chromium");
    const isThunderbird = userAgent.includes("thunderbird");
    const isFirefox = userAgent.includes("firefox") || isThunderbird;
    userAgent.includes("vivaldi");
    userAgent.includes("yabrowser");
    userAgent.includes("opr") || userAgent.includes("opera");
    const isEdge = userAgent.includes("edg");
    userAgent.includes("safari") && !isChromium;
    const isWindows = platform.startsWith("win");
    const isMacOS = platform.startsWith("mac");
    userAgent.includes("mobile");
    const chromiumVersion = (() => {
        const m = userAgent.match(/chrom[e|ium]\/([^ ]+)/);
        if (m && m[1]) {
            return m[1];
        }
        return "";
    })();
    (() => {
        try {
            document.querySelector(":defined");
            return true;
        } catch (err) {
            return false;
        }
    })();
    function compareChromeVersions($a, $b) {
        const a = $a.split(".").map((x) => parseInt(x));
        const b = $b.split(".").map((x) => parseInt(x));
        for (let i = 0; i < a.length; i++) {
            if (a[i] !== b[i]) {
                return a[i] < b[i] ? -1 : 1;
            }
        }
        return 0;
    }
    const isXMLHttpRequestSupported = typeof XMLHttpRequest === "function";
    const isFetchSupported = typeof fetch === "function";
    const isMV3 =
        globalThis.chrome &&
        globalThis.chrome.runtime &&
        globalThis.chrome.runtime.getManifest &&
        globalThis.chrome.runtime.getManifest().manifest_version === 3;

    async function getOKResponse(url, mimeType, origin) {
        const response = await fetch(url, {
            cache: "force-cache",
            credentials: "omit",
            referrer: origin
        });
        if (
            isFirefox &&
            mimeType === "text/css" &&
            url.startsWith("moz-extension://") &&
            url.endsWith(".css")
        ) {
            return response;
        }
        if (
            mimeType &&
            !response.headers.get("Content-Type").startsWith(mimeType)
        ) {
            throw new Error(`Mime type mismatch when loading ${url}`);
        }
        if (!response.ok) {
            throw new Error(
                `Unable to load ${url} ${response.status} ${response.statusText}`
            );
        }
        return response;
    }
    async function loadAsDataURL(url, mimeType) {
        const response = await getOKResponse(url, mimeType);
        return await readResponseAsDataURL(response);
    }
    async function readResponseAsDataURL(response) {
        const blob = await response.blob();
        const dataURL = await new Promise((resolve) => {
            const reader = new FileReader();
            reader.onloadend = () => resolve(reader.result);
            reader.readAsDataURL(blob);
        });
        return dataURL;
    }
    async function loadAsText(url, mimeType, origin) {
        const response = await getOKResponse(url, mimeType, origin);
        return await response.text();
    }

    function parseArray(text) {
        return text
            .replace(/\r/g, "")
            .split("\n")
            .map((s) => s.trim())
            .filter((s) => s);
    }
    function formatArray(arr) {
        return arr.concat("").join("\n");
    }
    function getStringSize(value) {
        return value.length * 2;
    }

    function parse24HTime(time) {
        return time.split(":").map((x) => parseInt(x));
    }
    function compareTime(time1, time2) {
        if (time1[0] === time2[0] && time1[1] === time2[1]) {
            return 0;
        }
        if (
            time1[0] < time2[0] ||
            (time1[0] === time2[0] && time1[1] < time2[1])
        ) {
            return -1;
        }
        return 1;
    }
    function nextTimeInterval(time0, time1, date = new Date()) {
        const a = parse24HTime(time0);
        const b = parse24HTime(time1);
        const t = [date.getHours(), date.getMinutes()];
        if (compareTime(a, b) > 0) {
            return nextTimeInterval(time1, time0, date);
        }
        if (compareTime(a, b) === 0) {
            return null;
        }
        if (compareTime(t, a) < 0) {
            date.setHours(a[0]);
            date.setMinutes(a[1]);
            return date.getTime();
        }
        if (compareTime(t, b) < 0) {
            date.setHours(b[0]);
            date.setMinutes(b[1]);
            return date.getTime();
        }
        return new Date(
            date.getFullYear(),
            date.getMonth(),
            date.getDate() + 1,
            a[0],
            a[1]
        ).getTime();
    }
    function isInTimeIntervalLocal(time0, time1, date = new Date()) {
        const a = parse24HTime(time0);
        const b = parse24HTime(time1);
        const t = [date.getHours(), date.getMinutes()];
        if (compareTime(a, b) > 0) {
            return compareTime(a, t) <= 0 || compareTime(t, b) < 0;
        }
        return compareTime(a, t) <= 0 && compareTime(t, b) < 0;
    }
    function isInTimeIntervalUTC(time0, time1, timestamp) {
        if (time1 < time0) {
            return timestamp <= time1 || time0 <= timestamp;
        }
        return time0 < timestamp && timestamp < time1;
    }
    function getDuration(time) {
        let duration = 0;
        if (time.seconds) {
            duration += time.seconds * 1000;
        }
        if (time.minutes) {
            duration += time.minutes * 60 * 1000;
        }
        if (time.hours) {
            duration += time.hours * 60 * 60 * 1000;
        }
        if (time.days) {
            duration += time.days * 24 * 60 * 60 * 1000;
        }
        return duration;
    }
    function getDurationInMinutes(time) {
        return getDuration(time) / 1000 / 60;
    }
    function getSunsetSunriseUTCTime(latitude, longitude, date) {
        const dec31 = Date.UTC(date.getUTCFullYear(), 0, 0, 0, 0, 0, 0);
        const oneDay = getDuration({days: 1});
        const dayOfYear = Math.floor((date.getTime() - dec31) / oneDay);
        const zenith = 90.83333333333333;
        const D2R = Math.PI / 180;
        const R2D = 180 / Math.PI;
        const lnHour = longitude / 15;
        function getTime(isSunrise) {
            const t = dayOfYear + ((isSunrise ? 6 : 18) - lnHour) / 24;
            const M = 0.9856 * t - 3.289;
            let L =
                M +
                1.916 * Math.sin(M * D2R) +
                0.02 * Math.sin(2 * M * D2R) +
                282.634;
            if (L > 360) {
                L -= 360;
            } else if (L < 0) {
                L += 360;
            }
            let RA = R2D * Math.atan(0.91764 * Math.tan(L * D2R));
            if (RA > 360) {
                RA -= 360;
            } else if (RA < 0) {
                RA += 360;
            }
            const Lquadrant = Math.floor(L / 90) * 90;
            const RAquadrant = Math.floor(RA / 90) * 90;
            RA += Lquadrant - RAquadrant;
            RA /= 15;
            const sinDec = 0.39782 * Math.sin(L * D2R);
            const cosDec = Math.cos(Math.asin(sinDec));
            const cosH =
                (Math.cos(zenith * D2R) - sinDec * Math.sin(latitude * D2R)) /
                (cosDec * Math.cos(latitude * D2R));
            if (cosH > 1) {
                return {
                    alwaysDay: false,
                    alwaysNight: true,
                    time: 0
                };
            } else if (cosH < -1) {
                return {
                    alwaysDay: true,
                    alwaysNight: false,
                    time: 0
                };
            }
            const H =
                (isSunrise
                    ? 360 - R2D * Math.acos(cosH)
                    : R2D * Math.acos(cosH)) / 15;
            const T = H + RA - 0.06571 * t - 6.622;
            let UT = T - lnHour;
            if (UT > 24) {
                UT -= 24;
            } else if (UT < 0) {
                UT += 24;
            }
            return {
                alwaysDay: false,
                alwaysNight: false,
                time: Math.round(UT * getDuration({hours: 1}))
            };
        }
        const sunriseTime = getTime(true);
        const sunsetTime = getTime(false);
        if (sunriseTime.alwaysDay || sunsetTime.alwaysDay) {
            return {
                alwaysDay: true
            };
        } else if (sunriseTime.alwaysNight || sunsetTime.alwaysNight) {
            return {
                alwaysNight: true
            };
        }
        return {
            sunriseTime: sunriseTime.time,
            sunsetTime: sunsetTime.time
        };
    }
    function isNightAtLocation(latitude, longitude, date = new Date()) {
        const time = getSunsetSunriseUTCTime(latitude, longitude, date);
        if (time.alwaysDay) {
            return false;
        } else if (time.alwaysNight) {
            return true;
        }
        const sunriseTime = time.sunriseTime;
        const sunsetTime = time.sunsetTime;
        const currentTime =
            date.getUTCHours() * getDuration({hours: 1}) +
            date.getUTCMinutes() * getDuration({minutes: 1}) +
            date.getUTCSeconds() * getDuration({seconds: 1}) +
            date.getUTCMilliseconds();
        return isInTimeIntervalUTC(sunsetTime, sunriseTime, currentTime);
    }
    function nextTimeChangeAtLocation(latitude, longitude, date = new Date()) {
        const time = getSunsetSunriseUTCTime(latitude, longitude, date);
        if (time.alwaysDay) {
            return date.getTime() + getDuration({days: 1});
        } else if (time.alwaysNight) {
            return date.getTime() + getDuration({days: 1});
        }
        const [firstTimeOnDay, lastTimeOnDay] =
            time.sunriseTime < time.sunsetTime
                ? [time.sunriseTime, time.sunsetTime]
                : [time.sunsetTime, time.sunriseTime];
        const currentTime =
            date.getUTCHours() * getDuration({hours: 1}) +
            date.getUTCMinutes() * getDuration({minutes: 1}) +
            date.getUTCSeconds() * getDuration({seconds: 1}) +
            date.getUTCMilliseconds();
        if (currentTime <= firstTimeOnDay) {
            return Date.UTC(
                date.getUTCFullYear(),
                date.getUTCMonth(),
                date.getUTCDate(),
                0,
                0,
                0,
                firstTimeOnDay
            );
        }
        if (currentTime <= lastTimeOnDay) {
            return Date.UTC(
                date.getUTCFullYear(),
                date.getUTCMonth(),
                date.getUTCDate(),
                0,
                0,
                0,
                lastTimeOnDay
            );
        }
        return Date.UTC(
            date.getUTCFullYear(),
            date.getUTCMonth(),
            date.getUTCDate() + 1,
            0,
            0,
            0,
            firstTimeOnDay
        );
    }

    async function readText(params) {
        return new Promise((resolve, reject) => {
            if (isXMLHttpRequestSupported) {
                const request = new XMLHttpRequest();
                request.overrideMimeType("text/plain");
                request.open("GET", params.url, true);
                request.onload = () => {
                    if (request.status >= 200 && request.status < 300) {
                        resolve(request.responseText);
                    } else {
                        reject(
                            new Error(
                                `${request.status}: ${request.statusText}`
                            )
                        );
                    }
                };
                request.onerror = () =>
                    reject(
                        new Error(`${request.status}: ${request.statusText}`)
                    );
                if (params.timeout) {
                    request.timeout = params.timeout;
                    request.ontimeout = () =>
                        reject(
                            new Error("File loading stopped due to timeout")
                        );
                }
                request.send();
            } else if (isFetchSupported) {
                let abortController;
                let signal;
                let timedOut = false;
                if (params.timeout) {
                    abortController = new AbortController();
                    signal = abortController.signal;
                    setTimeout(() => {
                        abortController.abort();
                        timedOut = true;
                    }, params.timeout);
                }
                fetch(params.url, {signal})
                    .then((response) => {
                        if (response.status >= 200 && response.status < 300) {
                            resolve(response.text());
                        } else {
                            reject(
                                new Error(
                                    `${response.status}: ${response.statusText}`
                                )
                            );
                        }
                    })
                    .catch((error) => {
                        if (timedOut) {
                            reject(
                                new Error("File loading stopped due to timeout")
                            );
                        } else {
                            reject(error);
                        }
                    });
            } else {
                reject(
                    new Error(
                        `Neither XMLHttpRequest nor Fetch API are accessible!`
                    )
                );
            }
        });
    }
    class LimitedCacheStorage {
        constructor() {
            this.bytesInUse = 0;
            this.records = new Map();
            this.alarmIsActive = false;
            chrome.alarms.onAlarm.addListener(async (alarm) => {
                if (alarm.name === LimitedCacheStorage.ALARM_NAME) {
                    this.alarmIsActive = false;
                    this.removeExpiredRecords();
                }
            });
        }
        ensureAlarmIsScheduled() {
            if (!this.alarmIsActive) {
                chrome.alarms.create(LimitedCacheStorage.ALARM_NAME, {
                    delayInMinutes: 1
                });
                this.alarmIsActive = true;
            }
        }
        has(url) {
            return this.records.has(url);
        }
        get(url) {
            if (this.records.has(url)) {
                const record = this.records.get(url);
                record.expires = Date.now() + LimitedCacheStorage.TTL;
                this.records.delete(url);
                this.records.set(url, record);
                return record.value;
            }
            return null;
        }
        set(url, value) {
            this.ensureAlarmIsScheduled();
            const size = getStringSize(value);
            if (size > LimitedCacheStorage.QUOTA_BYTES) {
                return;
            }
            for (const [url, record] of this.records) {
                if (this.bytesInUse + size > LimitedCacheStorage.QUOTA_BYTES) {
                    this.records.delete(url);
                    this.bytesInUse -= record.size;
                } else {
                    break;
                }
            }
            const expires = Date.now() + LimitedCacheStorage.TTL;
            this.records.set(url, {url, value, size, expires});
            this.bytesInUse += size;
        }
        removeExpiredRecords() {
            const now = Date.now();
            for (const [url, record] of this.records) {
                if (record.expires < now) {
                    this.records.delete(url);
                    this.bytesInUse -= record.size;
                } else {
                    break;
                }
            }
            if (this.records.size !== 0) {
                this.ensureAlarmIsScheduled();
            }
        }
    }
    LimitedCacheStorage.QUOTA_BYTES =
        (navigator.deviceMemory || 4) * 16 * 1024 * 1024;
    LimitedCacheStorage.TTL = getDuration({minutes: 10});
    LimitedCacheStorage.ALARM_NAME = "network";
    function createFileLoader() {
        const caches = {
            "data-url": new LimitedCacheStorage(),
            "text": new LimitedCacheStorage()
        };
        const loaders = {
            "data-url": loadAsDataURL,
            "text": loadAsText
        };
        async function get({url, responseType, mimeType, origin}) {
            const cache = caches[responseType];
            const load = loaders[responseType];
            if (cache.has(url)) {
                return cache.get(url);
            }
            const data = await load(url, mimeType, origin);
            cache.set(url, data);
            return data;
        }
        return {get};
    }

    function isIPV6(url) {
        const openingBracketIndex = url.indexOf("[");
        if (openingBracketIndex < 0) {
            return false;
        }
        const queryIndex = url.indexOf("?");
        if (queryIndex >= 0 && openingBracketIndex > queryIndex) {
            return false;
        }
        return true;
    }
    const ipV6HostRegex = /\[.*?\](\:\d+)?/;
    function compareIPV6(firstURL, secondURL) {
        const firstHost = firstURL.match(ipV6HostRegex)[0];
        const secondHost = secondURL.match(ipV6HostRegex)[0];
        return firstHost === secondHost;
    }

    function getURLHostOrProtocol($url) {
        const url = new URL($url);
        if (url.host) {
            return url.host;
        }
        return url.protocol;
    }
    function compareURLPatterns(a, b) {
        return a.localeCompare(b);
    }
    function isURLInList(url, list) {
        for (let i = 0; i < list.length; i++) {
            if (isURLMatched(url, list[i])) {
                return true;
            }
        }
        return false;
    }
    function isURLMatched(url, urlTemplate) {
        const isFirstIPV6 = isIPV6(url);
        const isSecondIPV6 = isIPV6(urlTemplate);
        if (isFirstIPV6 && isSecondIPV6) {
            return compareIPV6(url, urlTemplate);
        } else if (!isFirstIPV6 && !isSecondIPV6) {
            const regex = createUrlRegex(urlTemplate);
            return Boolean(url.match(regex));
        }
        return false;
    }
    function createUrlRegex(urlTemplate) {
        urlTemplate = urlTemplate.trim();
        const exactBeginning = urlTemplate[0] === "^";
        const exactEnding = urlTemplate[urlTemplate.length - 1] === "$";
        urlTemplate = urlTemplate
            .replace(/^\^/, "")
            .replace(/\$$/, "")
            .replace(/^.*?\/{2,3}/, "")
            .replace(/\?.*$/, "")
            .replace(/\/$/, "");
        let slashIndex;
        let beforeSlash;
        let afterSlash;
        if ((slashIndex = urlTemplate.indexOf("/")) >= 0) {
            beforeSlash = urlTemplate.substring(0, slashIndex);
            afterSlash = urlTemplate.replace(/\$/g, "").substring(slashIndex);
        } else {
            beforeSlash = urlTemplate.replace(/\$/g, "");
        }
        let result = exactBeginning
            ? "^(.*?\\:\\/{2,3})?"
            : "^(.*?\\:\\/{2,3})?([^/]*?\\.)?";
        const hostParts = beforeSlash.split(".");
        result += "(";
        for (let i = 0; i < hostParts.length; i++) {
            if (hostParts[i] === "*") {
                hostParts[i] = "[^\\.\\/]+?";
            }
        }
        result += hostParts.join("\\.");
        result += ")";
        if (afterSlash) {
            result += "(";
            result += afterSlash.replace("/", "\\/");
            result += ")";
        }
        result += exactEnding ? "(\\/?(\\?[^/]*?)?)$" : "(\\/?.*?)$";
        return new RegExp(result, "i");
    }
    function isPDF(url) {
        if (url.includes(".pdf")) {
            if (url.includes("?")) {
                url = url.substring(0, url.lastIndexOf("?"));
            }
            if (url.includes("#")) {
                url = url.substring(0, url.lastIndexOf("#"));
            }
            if (
                (url.match(/(wikipedia|wikimedia).org/i) &&
                    url.match(
                        /(wikipedia|wikimedia)\.org\/.*\/[a-z]+\:[^\:\/]+\.pdf/i
                    )) ||
                (url.match(/timetravel\.mementoweb\.org\/reconstruct/i) &&
                    url.match(/\.pdf$/i))
            ) {
                return false;
            }
            if (url.endsWith(".pdf")) {
                for (let i = url.length; i > 0; i--) {
                    if (url[i] === "=") {
                        return false;
                    } else if (url[i] === "/") {
                        return true;
                    }
                }
            } else {
                return false;
            }
        }
        return false;
    }
    function isURLEnabled(url, userSettings, {isProtected, isInDarkList}) {
        if (isProtected && !userSettings.enableForProtectedPages) {
            return false;
        }
        if (isThunderbird) {
            return true;
        }
        if (isPDF(url)) {
            return userSettings.enableForPDF;
        }
        const isURLInUserList = isURLInList(url, userSettings.siteList);
        const isURLInEnabledList = isURLInList(
            url,
            userSettings.siteListEnabled
        );
        if (userSettings.applyToListedOnly && !isURLInEnabledList) {
            return isURLInUserList;
        }
        if (isURLInEnabledList && isInDarkList) {
            return true;
        }
        return !isInDarkList && !isURLInUserList;
    }
    function isFullyQualifiedDomain(candidate) {
        return /^[a-z0-9.-]+$/.test(candidate);
    }

    function parseSitesFixesConfig(text, options) {
        const sites = [];
        const blocks = text.replace(/\r/g, "").split(/^\s*={2,}\s*$/gm);
        blocks.forEach((block) => {
            const lines = block.split("\n");
            const commandIndices = [];
            lines.forEach((ln, i) => {
                if (ln.match(/^[A-Z]+(\s[A-Z]+){0,2}$/)) {
                    commandIndices.push(i);
                }
            });
            if (commandIndices.length === 0) {
                return;
            }
            const siteFix = {
                url: parseArray(lines.slice(0, commandIndices[0]).join("\n"))
            };
            commandIndices.forEach((commandIndex, i) => {
                const command = lines[commandIndex].trim();
                const valueText = lines
                    .slice(
                        commandIndex + 1,
                        i === commandIndices.length - 1
                            ? lines.length
                            : commandIndices[i + 1]
                    )
                    .join("\n");
                const prop = options.getCommandPropName(command);
                if (!prop) {
                    return;
                }
                const value = options.parseCommandValue(command, valueText);
                siteFix[prop] = value;
            });
            sites.push(siteFix);
        });
        return sites;
    }
    function getDomain(url) {
        try {
            return new URL(url).hostname.toLowerCase();
        } catch (error) {
            return url.split("/")[0].toLowerCase();
        }
    }
    function encodeOffsets(offsets) {
        return offsets
            .map(([offset, length]) => {
                const stringOffset = offset.toString(36);
                const stringLength = length.toString(36);
                return (
                    "0".repeat(4 - stringOffset.length) +
                    stringOffset +
                    "0".repeat(3 - stringLength.length) +
                    stringLength
                );
            })
            .join("");
    }
    function decodeOffset(offsets, index) {
        const base = (4 + 3) * index;
        const offset = parseInt(offsets.substring(base + 0, base + 4), 36);
        const length = parseInt(offsets.substring(base + 4, base + 4 + 3), 36);
        return [offset, offset + length];
    }
    function indexSitesFixesConfig(text) {
        const domains = {};
        const domainPatterns = {};
        const offsets = [];
        function processBlock(recordStart, recordEnd, index) {
            const block = text.substring(recordStart, recordEnd);
            const lines = block.split("\n");
            const commandIndices = [];
            lines.forEach((ln, i) => {
                if (ln.match(/^[A-Z]+(\s[A-Z]+){0,2}$/)) {
                    commandIndices.push(i);
                }
            });
            if (commandIndices.length === 0) {
                return;
            }
            const urls = parseArray(
                lines.slice(0, commandIndices[0]).join("\n")
            );
            for (const url of urls) {
                const domain = getDomain(url);
                if (isFullyQualifiedDomain(domain)) {
                    if (!domains[domain]) {
                        domains[domain] = index;
                    } else if (
                        typeof domains[domain] === "number" &&
                        domains[domain] !== index
                    ) {
                        domains[domain] = [domains[domain], index];
                    } else if (
                        typeof domains[domain] === "object" &&
                        !domains[domain].includes(index)
                    ) {
                        domains[domain].push(index);
                    }
                    continue;
                }
                if (!domainPatterns[domain]) {
                    domainPatterns[domain] = index;
                } else if (
                    typeof domainPatterns[domain] === "number" &&
                    domainPatterns[domain] !== index
                ) {
                    domainPatterns[domain] = [domainPatterns[domain], index];
                } else if (
                    typeof domainPatterns[domain] === "object" &&
                    !domainPatterns[domain].includes(index)
                ) {
                    domainPatterns[domain].push(index);
                }
            }
            offsets.push([recordStart, recordEnd - recordStart]);
        }
        let recordStart = 0;
        const delimiterRegex = /^\s*={2,}\s*$/gm;
        let delimiter;
        let count = 0;
        while ((delimiter = delimiterRegex.exec(text))) {
            const nextDelimiterStart = delimiter.index;
            const nextDelimiterEnd = delimiter.index + delimiter[0].length;
            processBlock(recordStart, nextDelimiterStart, count);
            recordStart = nextDelimiterEnd;
            count++;
        }
        processBlock(recordStart, text.length, count);
        return {
            offsets: encodeOffsets(offsets),
            domains,
            domainPatterns,
            cache: {}
        };
    }
    function parseSiteFixConfig(text, options, recordStart, recordEnd) {
        const block = text.substring(recordStart, recordEnd);
        return parseSitesFixesConfig(block, options)[0];
    }
    function getSitesFixesFor(url, text, index, options) {
        const records = [];
        let recordIds = [];
        const domain = getDomain(url);
        for (const pattern of Object.keys(index.domainPatterns)) {
            if (isURLMatched(url, pattern)) {
                recordIds = recordIds.concat(index.domainPatterns[pattern]);
            }
        }
        const labels = domain.split(".");
        for (let i = 0; i < labels.length; i++) {
            const substring = labels.slice(i).join(".");
            if (index.domains[substring] && isURLMatched(url, substring)) {
                recordIds = recordIds.concat(index.domains[substring]);
            }
        }
        const set = new Set();
        for (const id of recordIds) {
            if (set.has(id)) {
                continue;
            }
            set.add(id);
            if (!index.cache[id]) {
                const [start, end] = decodeOffset(index.offsets, id);
                index.cache[id] = parseSiteFixConfig(text, options, start, end);
            }
            records.push(index.cache[id]);
        }
        return records;
    }

    const SEPERATOR = "=".repeat(32);
    const backgroundPropertyLength = "background: ".length;
    const textPropertyLength = "text: ".length;
    const humanizeNumber = (number) => {
        if (number > 3) {
            return `${number}th`;
        }
        switch (number) {
            case 0:
                return "0";
            case 1:
                return "1st";
            case 2:
                return "2nd";
            case 3:
                return "3rd";
        }
    };
    const isValidHexColor = (color) => {
        return /^#([0-9a-fA-F]{3}){1,2}$/.test(color);
    };
    function ParseColorSchemeConfig(config) {
        const sections = config.split(`${SEPERATOR}\n\n`);
        const definedColorSchemeNames = new Set();
        let lastDefinedColorSchemeName = "";
        const definedColorSchemes = {
            light: {},
            dark: {}
        };
        let interrupt = false;
        let error = null;
        const throwError = (message) => {
            if (!interrupt) {
                interrupt = true;
                error = message;
            }
        };
        sections.forEach((section) => {
            if (interrupt) {
                return;
            }
            const lines = section.split("\n");
            const name = lines[0];
            if (!name) {
                throwError("No color scheme name was found.");
                return;
            }
            if (definedColorSchemeNames.has(name)) {
                throwError(
                    `The color scheme name "${name}" is already defined.`
                );
                return;
            }
            if (
                lastDefinedColorSchemeName &&
                lastDefinedColorSchemeName !== "Default" &&
                name.localeCompare(lastDefinedColorSchemeName) < 0
            ) {
                throwError(
                    `The color scheme name "${name}" is not in alphabetical order.`
                );
                return;
            }
            lastDefinedColorSchemeName = name;
            definedColorSchemeNames.add(name);
            if (lines[1]) {
                throwError(
                    `The second line of the color scheme "${name}" is not empty.`
                );
                return;
            }
            const checkVariant = (lineIndex, isSecondVariant) => {
                const variant = lines[lineIndex];
                if (!variant) {
                    throwError(
                        `The third line of the color scheme "${name}" is not defined.`
                    );
                    return;
                }
                if (
                    variant !== "LIGHT" &&
                    variant !== "DARK" &&
                    isSecondVariant &&
                    variant === "Light"
                ) {
                    throwError(
                        `The ${humanizeNumber(
                            lineIndex
                        )} line of the color scheme "${name}" is not a valid variant.`
                    );
                    return;
                }
                const firstProperty = lines[lineIndex + 1];
                if (!firstProperty) {
                    throwError(
                        `The ${humanizeNumber(
                            lineIndex + 1
                        )} line of the color scheme "${name}" is not defined.`
                    );
                    return;
                }
                if (!firstProperty.startsWith("background: ")) {
                    throwError(
                        `The ${humanizeNumber(
                            lineIndex + 1
                        )} line of the color scheme "${name}" is not background-color property.`
                    );
                    return;
                }
                const backgroundColor = firstProperty.slice(
                    backgroundPropertyLength
                );
                if (!isValidHexColor(backgroundColor)) {
                    throwError(
                        `The ${humanizeNumber(
                            lineIndex + 1
                        )} line of the color scheme "${name}" is not a valid hex color.`
                    );
                    return;
                }
                const secondProperty = lines[lineIndex + 2];
                if (!secondProperty) {
                    throwError(
                        `The ${humanizeNumber(
                            lineIndex + 2
                        )} line of the color scheme "${name}" is not defined.`
                    );
                    return;
                }
                if (!secondProperty.startsWith("text: ")) {
                    throwError(
                        `The ${humanizeNumber(
                            lineIndex + 2
                        )} line of the color scheme "${name}" is not text-color property.`
                    );
                    return;
                }
                const textColor = secondProperty.slice(textPropertyLength);
                if (!isValidHexColor(textColor)) {
                    throwError(
                        `The ${humanizeNumber(
                            lineIndex + 2
                        )} line of the color scheme "${name}" is not a valid hex color.`
                    );
                    return;
                }
                return {
                    backgroundColor,
                    textColor,
                    variant
                };
            };
            const firstVariant = checkVariant(2, false);
            const isFirstVariantLight = firstVariant.variant === "LIGHT";
            delete firstVariant.variant;
            if (interrupt) {
                return;
            }
            let secondVariant = null;
            let isSecondVariantLight = false;
            if (lines[6]) {
                secondVariant = checkVariant(6, true);
                isSecondVariantLight = secondVariant.variant === "LIGHT";
                delete secondVariant.variant;
                if (interrupt) {
                    return;
                }
                if (lines.length > 11 || lines[9] || lines[10]) {
                    throwError(
                        `The color scheme "${name}" doesn't end with 1 new line.`
                    );
                    return;
                }
            } else if (lines.length > 7) {
                throwError(
                    `The color scheme "${name}" doesn't end with 1 new line.`
                );
                return;
            }
            if (secondVariant) {
                if (isFirstVariantLight === isSecondVariantLight) {
                    throwError(
                        `The color scheme "${name}" has the same variant twice.`
                    );
                    return;
                }
                if (isFirstVariantLight) {
                    definedColorSchemes.light[name] = firstVariant;
                    definedColorSchemes.dark[name] = secondVariant;
                } else {
                    definedColorSchemes.light[name] = secondVariant;
                    definedColorSchemes.dark[name] = firstVariant;
                }
            } else if (isFirstVariantLight) {
                definedColorSchemes.light[name] = firstVariant;
            } else {
                definedColorSchemes.dark[name] = firstVariant;
            }
        });
        return {result: definedColorSchemes, error: error};
    }

    function logInfo(...args) {}
    function logWarn(...args) {}

    var ThemeEngines = {
        cssFilter: "cssFilter",
        svgFilter: "svgFilter",
        staticTheme: "staticTheme",
        dynamicTheme: "dynamicTheme"
    };

    const DEFAULT_COLORS = {
        darkScheme: {
            background: "#181a1b",
            text: "#e8e6e3"
        },
        lightScheme: {
            background: "#dcdad7",
            text: "#181a1b"
        }
    };
    const DEFAULT_THEME = {
        mode: 1,
        brightness: 100,
        contrast: 100,
        grayscale: 0,
        sepia: 0,
        useFont: false,
        fontFamily: isMacOS
            ? "Helvetica Neue"
            : isWindows
            ? "Segoe UI"
            : "Open Sans",
        textStroke: 0,
        engine: ThemeEngines.dynamicTheme,
        stylesheet: "",
        darkSchemeBackgroundColor: DEFAULT_COLORS.darkScheme.background,
        darkSchemeTextColor: DEFAULT_COLORS.darkScheme.text,
        lightSchemeBackgroundColor: DEFAULT_COLORS.lightScheme.background,
        lightSchemeTextColor: DEFAULT_COLORS.lightScheme.text,
        scrollbarColor: isMacOS ? "" : "auto",
        selectionColor: "auto",
        styleSystemControls: true,
        lightColorScheme: "Default",
        darkColorScheme: "Default"
    };
    const DEFAULT_COLORSCHEME = {
        light: {
            Default: {
                backgroundColor: DEFAULT_COLORS.lightScheme.background,
                textColor: DEFAULT_COLORS.lightScheme.text
            }
        },
        dark: {
            Default: {
                backgroundColor: DEFAULT_COLORS.darkScheme.background,
                textColor: DEFAULT_COLORS.darkScheme.text
            }
        }
    };
    const DEFAULT_SETTINGS = {
        enabled: true,
        fetchNews: true,
        theme: DEFAULT_THEME,
        presets: [],
        customThemes: [],
        siteList: [],
        siteListEnabled: [],
        applyToListedOnly: false,
        changeBrowserTheme: false,
        syncSettings: true,
        syncSitesFixes: false,
        automation: "",
        time: {
            activation: "18:00",
            deactivation: "9:00"
        },
        location: {
            latitude: null,
            longitude: null
        },
        previewNewDesign: false,
        enableForPDF: true,
        enableForProtectedPages: false,
        enableContextMenus: false
    };

    const CONFIG_URLs = {
        darkSites: {
            remote: "https://raw.githubusercontent.com/darkreader/darkreader/master/src/config/dark-sites.config",
            local: "../config/dark-sites.config"
        },
        dynamicThemeFixes: {
            remote: "https://raw.githubusercontent.com/darkreader/darkreader/master/src/config/dynamic-theme-fixes.config",
            local: "../config/dynamic-theme-fixes.config"
        },
        inversionFixes: {
            remote: "https://raw.githubusercontent.com/darkreader/darkreader/master/src/config/inversion-fixes.config",
            local: "../config/inversion-fixes.config"
        },
        staticThemes: {
            remote: "https://raw.githubusercontent.com/darkreader/darkreader/master/src/config/static-themes.config",
            local: "../config/static-themes.config"
        },
        colorSchemes: {
            remote: "https://raw.githubusercontent.com/darkreader/darkreader/master/src/config/color-schemes.drconf",
            local: "../config/color-schemes.drconf"
        }
    };
    const REMOTE_TIMEOUT_MS = getDuration({seconds: 10});
    class ConfigManager {
        constructor() {
            this.raw = {
                darkSites: null,
                dynamicThemeFixes: null,
                inversionFixes: null,
                staticThemes: null,
                colorSchemes: null
            };
            this.overrides = {
                darkSites: null,
                dynamicThemeFixes: null,
                inversionFixes: null,
                staticThemes: null
            };
        }
        async loadConfig({name, local, localURL, remoteURL}) {
            let $config;
            const loadLocal = async () => await readText({url: localURL});
            if (local) {
                $config = await loadLocal();
            } else {
                try {
                    $config = await readText({
                        url: `${remoteURL}?nocache=${Date.now()}`,
                        timeout: REMOTE_TIMEOUT_MS
                    });
                } catch (err) {
                    console.error(`${name} remote load error`, err);
                    $config = await loadLocal();
                }
            }
            return $config;
        }
        async loadColorSchemes({local}) {
            const $config = await this.loadConfig({
                name: "Color Schemes",
                local,
                localURL: CONFIG_URLs.colorSchemes.local,
                remoteURL: CONFIG_URLs.colorSchemes.remote
            });
            this.raw.colorSchemes = $config;
            this.handleColorSchemes();
        }
        async loadDarkSites({local}) {
            const sites = await this.loadConfig({
                name: "Dark Sites",
                local,
                localURL: CONFIG_URLs.darkSites.local,
                remoteURL: CONFIG_URLs.darkSites.remote
            });
            this.raw.darkSites = sites;
            this.handleDarkSites();
        }
        async loadDynamicThemeFixes({local}) {
            const fixes = await this.loadConfig({
                name: "Dynamic Theme Fixes",
                local,
                localURL: CONFIG_URLs.dynamicThemeFixes.local,
                remoteURL: CONFIG_URLs.dynamicThemeFixes.remote
            });
            this.raw.dynamicThemeFixes = fixes;
            this.handleDynamicThemeFixes();
        }
        async loadInversionFixes({local}) {
            const fixes = await this.loadConfig({
                name: "Inversion Fixes",
                local,
                localURL: CONFIG_URLs.inversionFixes.local,
                remoteURL: CONFIG_URLs.inversionFixes.remote
            });
            this.raw.inversionFixes = fixes;
            this.handleInversionFixes();
        }
        async loadStaticThemes({local}) {
            const themes = await this.loadConfig({
                name: "Static Themes",
                local,
                localURL: CONFIG_URLs.staticThemes.local,
                remoteURL: CONFIG_URLs.staticThemes.remote
            });
            this.raw.staticThemes = themes;
            this.handleStaticThemes();
        }
        async load(config) {
            await Promise.all([
                this.loadColorSchemes(config),
                this.loadDarkSites(config),
                this.loadDynamicThemeFixes(config),
                this.loadInversionFixes(config),
                this.loadStaticThemes(config)
            ]).catch((err) => console.error("Fatality", err));
        }
        handleColorSchemes() {
            const $config = this.raw.colorSchemes;
            const {result, error} = ParseColorSchemeConfig($config);
            if (error) {
                this.COLOR_SCHEMES_RAW = DEFAULT_COLORSCHEME;
                return;
            }
            this.COLOR_SCHEMES_RAW = result;
        }
        handleDarkSites() {
            const $sites = this.overrides.darkSites || this.raw.darkSites;
            this.DARK_SITES = parseArray($sites);
        }
        handleDynamicThemeFixes() {
            const $fixes =
                this.overrides.dynamicThemeFixes || this.raw.dynamicThemeFixes;
            this.DYNAMIC_THEME_FIXES_INDEX = indexSitesFixesConfig($fixes);
            this.DYNAMIC_THEME_FIXES_RAW = $fixes;
        }
        handleInversionFixes() {
            const $fixes =
                this.overrides.inversionFixes || this.raw.inversionFixes;
            this.INVERSION_FIXES_INDEX = indexSitesFixesConfig($fixes);
            this.INVERSION_FIXES_RAW = $fixes;
        }
        handleStaticThemes() {
            const $themes =
                this.overrides.staticThemes || this.raw.staticThemes;
            this.STATIC_THEMES_INDEX = indexSitesFixesConfig($themes);
            this.STATIC_THEMES_RAW = $themes;
        }
    }

    function isArrayLike(items) {
        return items.length != null;
    }
    function forEach(items, iterator) {
        if (isArrayLike(items)) {
            for (let i = 0, len = items.length; i < len; i++) {
                iterator(items[i]);
            }
        } else {
            for (const item of items) {
                iterator(item);
            }
        }
    }
    function push(array, addition) {
        forEach(addition, (a) => array.push(a));
    }

    function formatSitesFixesConfig(fixes, options) {
        const lines = [];
        fixes.forEach((fix, i) => {
            push(lines, fix.url);
            options.props.forEach((prop) => {
                const command = options.getPropCommandName(prop);
                const value = fix[prop];
                if (options.shouldIgnoreProp(prop, value)) {
                    return;
                }
                lines.push("");
                lines.push(command);
                const formattedValue = options.formatPropValue(prop, value);
                if (formattedValue) {
                    lines.push(formattedValue);
                }
            });
            if (i < fixes.length - 1) {
                lines.push("");
                lines.push("=".repeat(32));
                lines.push("");
            }
        });
        lines.push("");
        return lines.join("\n");
    }

    function scale(x, inLow, inHigh, outLow, outHigh) {
        return ((x - inLow) * (outHigh - outLow)) / (inHigh - inLow) + outLow;
    }
    function clamp(x, min, max) {
        return Math.min(max, Math.max(min, x));
    }
    function multiplyMatrices(m1, m2) {
        const result = [];
        for (let i = 0, len = m1.length; i < len; i++) {
            result[i] = [];
            for (let j = 0, len2 = m2[0].length; j < len2; j++) {
                let sum = 0;
                for (let k = 0, len3 = m1[0].length; k < len3; k++) {
                    sum += m1[i][k] * m2[k][j];
                }
                result[i][j] = sum;
            }
        }
        return result;
    }

    function createFilterMatrix(config) {
        let m = Matrix.identity();
        if (config.sepia !== 0) {
            m = multiplyMatrices(m, Matrix.sepia(config.sepia / 100));
        }
        if (config.grayscale !== 0) {
            m = multiplyMatrices(m, Matrix.grayscale(config.grayscale / 100));
        }
        if (config.contrast !== 100) {
            m = multiplyMatrices(m, Matrix.contrast(config.contrast / 100));
        }
        if (config.brightness !== 100) {
            m = multiplyMatrices(m, Matrix.brightness(config.brightness / 100));
        }
        if (config.mode === 1) {
            m = multiplyMatrices(m, Matrix.invertNHue());
        }
        return m;
    }
    function applyColorMatrix([r, g, b], matrix) {
        const rgb = [[r / 255], [g / 255], [b / 255], [1], [1]];
        const result = multiplyMatrices(matrix, rgb);
        return [0, 1, 2].map((i) =>
            clamp(Math.round(result[i][0] * 255), 0, 255)
        );
    }
    const Matrix = {
        identity() {
            return [
                [1, 0, 0, 0, 0],
                [0, 1, 0, 0, 0],
                [0, 0, 1, 0, 0],
                [0, 0, 0, 1, 0],
                [0, 0, 0, 0, 1]
            ];
        },
        invertNHue() {
            return [
                [0.333, -0.667, -0.667, 0, 1],
                [-0.667, 0.333, -0.667, 0, 1],
                [-0.667, -0.667, 0.333, 0, 1],
                [0, 0, 0, 1, 0],
                [0, 0, 0, 0, 1]
            ];
        },
        brightness(v) {
            return [
                [v, 0, 0, 0, 0],
                [0, v, 0, 0, 0],
                [0, 0, v, 0, 0],
                [0, 0, 0, 1, 0],
                [0, 0, 0, 0, 1]
            ];
        },
        contrast(v) {
            const t = (1 - v) / 2;
            return [
                [v, 0, 0, 0, t],
                [0, v, 0, 0, t],
                [0, 0, v, 0, t],
                [0, 0, 0, 1, 0],
                [0, 0, 0, 0, 1]
            ];
        },
        sepia(v) {
            return [
                [
                    0.393 + 0.607 * (1 - v),
                    0.769 - 0.769 * (1 - v),
                    0.189 - 0.189 * (1 - v),
                    0,
                    0
                ],
                [
                    0.349 - 0.349 * (1 - v),
                    0.686 + 0.314 * (1 - v),
                    0.168 - 0.168 * (1 - v),
                    0,
                    0
                ],
                [
                    0.272 - 0.272 * (1 - v),
                    0.534 - 0.534 * (1 - v),
                    0.131 + 0.869 * (1 - v),
                    0,
                    0
                ],
                [0, 0, 0, 1, 0],
                [0, 0, 0, 0, 1]
            ];
        },
        grayscale(v) {
            return [
                [
                    0.2126 + 0.7874 * (1 - v),
                    0.7152 - 0.7152 * (1 - v),
                    0.0722 - 0.0722 * (1 - v),
                    0,
                    0
                ],
                [
                    0.2126 - 0.2126 * (1 - v),
                    0.7152 + 0.2848 * (1 - v),
                    0.0722 - 0.0722 * (1 - v),
                    0,
                    0
                ],
                [
                    0.2126 - 0.2126 * (1 - v),
                    0.7152 - 0.7152 * (1 - v),
                    0.0722 + 0.9278 * (1 - v),
                    0,
                    0
                ],
                [0, 0, 0, 1, 0],
                [0, 0, 0, 0, 1]
            ];
        }
    };

    function createTextStyle(config) {
        const lines = [];
        lines.push(
            '*:not(pre, pre *, code, .far, .fa, .glyphicon, [class*="vjs-"], .fab, .fa-github, .fas, .material-icons, .icofont, .typcn, mu, [class*="mu-"], .glyphicon, .icon) {'
        );
        if (config.useFont && config.fontFamily) {
            lines.push(`  font-family: ${config.fontFamily} !important;`);
        }
        if (config.textStroke > 0) {
            lines.push(
                `  -webkit-text-stroke: ${config.textStroke}px !important;`
            );
            lines.push(`  text-stroke: ${config.textStroke}px !important;`);
        }
        lines.push("}");
        return lines.join("\n");
    }

    var FilterMode;
    (function (FilterMode) {
        FilterMode[(FilterMode["light"] = 0)] = "light";
        FilterMode[(FilterMode["dark"] = 1)] = "dark";
    })(FilterMode || (FilterMode = {}));
    function hasPatchForChromiumIssue501582() {
        return Boolean(
            isChromium &&
                compareChromeVersions(chromiumVersion, "81.0.4035.0") >= 0
        );
    }
    function createCSSFilterStyleSheet(config, url, frameURL, fixes, index) {
        const filterValue = getCSSFilterValue(config);
        const reverseFilterValue = "invert(100%) hue-rotate(180deg)";
        return cssFilterStyleSheetTemplate(
            filterValue,
            reverseFilterValue,
            config,
            url,
            frameURL,
            fixes,
            index
        );
    }
    function cssFilterStyleSheetTemplate(
        filterValue,
        reverseFilterValue,
        config,
        url,
        frameURL,
        fixes,
        index
    ) {
        const fix = getInversionFixesFor(frameURL || url, fixes, index);
        const lines = [];
        lines.push("@media screen {");
        if (filterValue && !frameURL) {
            lines.push("");
            lines.push("/* Leading rule */");
            lines.push(createLeadingRule(filterValue));
        }
        if (config.mode === FilterMode.dark) {
            lines.push("");
            lines.push("/* Reverse rule */");
            lines.push(createReverseRule(reverseFilterValue, fix));
        }
        if (config.useFont || config.textStroke > 0) {
            lines.push("");
            lines.push("/* Font */");
            lines.push(createTextStyle(config));
        }
        lines.push("");
        lines.push("/* Text contrast */");
        lines.push("html {");
        lines.push("  text-shadow: 0 0 0 !important;");
        lines.push("}");
        lines.push("");
        lines.push("/* Full screen */");
        [":-webkit-full-screen", ":-moz-full-screen", ":fullscreen"].forEach(
            (fullScreen) => {
                lines.push(`${fullScreen}, ${fullScreen} * {`);
                lines.push("  -webkit-filter: none !important;");
                lines.push("  filter: none !important;");
                lines.push("}");
            }
        );
        if (!frameURL) {
            const light = [255, 255, 255];
            const bgColor =
                !hasPatchForChromiumIssue501582() &&
                config.mode === FilterMode.dark
                    ? applyColorMatrix(light, createFilterMatrix(config)).map(
                          Math.round
                      )
                    : light;
            lines.push("");
            lines.push("/* Page background */");
            lines.push("html {");
            lines.push(`  background: rgb(${bgColor.join(",")}) !important;`);
            lines.push("}");
        }
        if (fix.css && fix.css.length > 0 && config.mode === FilterMode.dark) {
            lines.push("");
            lines.push("/* Custom rules */");
            lines.push(fix.css);
        }
        lines.push("");
        lines.push("}");
        return lines.join("\n");
    }
    function getCSSFilterValue(config) {
        const filters = [];
        if (config.mode === FilterMode.dark) {
            filters.push("invert(100%) hue-rotate(180deg)");
        }
        if (config.brightness !== 100) {
            filters.push(`brightness(${config.brightness}%)`);
        }
        if (config.contrast !== 100) {
            filters.push(`contrast(${config.contrast}%)`);
        }
        if (config.grayscale !== 0) {
            filters.push(`grayscale(${config.grayscale}%)`);
        }
        if (config.sepia !== 0) {
            filters.push(`sepia(${config.sepia}%)`);
        }
        if (filters.length === 0) {
            return null;
        }
        return filters.join(" ");
    }
    function createLeadingRule(filterValue) {
        return [
            "html {",
            `  -webkit-filter: ${filterValue} !important;`,
            `  filter: ${filterValue} !important;`,
            "}"
        ].join("\n");
    }
    function joinSelectors(selectors) {
        return selectors.map((s) => s.replace(/\,$/, "")).join(",\n");
    }
    function createReverseRule(reverseFilterValue, fix) {
        const lines = [];
        if (fix.invert.length > 0) {
            lines.push(`${joinSelectors(fix.invert)} {`);
            lines.push(`  -webkit-filter: ${reverseFilterValue} !important;`);
            lines.push(`  filter: ${reverseFilterValue} !important;`);
            lines.push("}");
        }
        if (fix.noinvert.length > 0) {
            lines.push(`${joinSelectors(fix.noinvert)} {`);
            lines.push("  -webkit-filter: none !important;");
            lines.push("  filter: none !important;");
            lines.push("}");
        }
        if (fix.removebg.length > 0) {
            lines.push(`${joinSelectors(fix.removebg)} {`);
            lines.push("  background: white !important;");
            lines.push("}");
        }
        return lines.join("\n");
    }
    function getInversionFixesFor(url, fixes, index) {
        const inversionFixes = getSitesFixesFor(url, fixes, index, {
            commands: Object.keys(inversionFixesCommands),
            getCommandPropName: (command) => inversionFixesCommands[command],
            parseCommandValue: (command, value) => {
                if (command === "CSS") {
                    return value.trim();
                }
                return parseArray(value);
            }
        });
        const common = {
            url: inversionFixes[0].url,
            invert: inversionFixes[0].invert || [],
            noinvert: inversionFixes[0].noinvert || [],
            removebg: inversionFixes[0].removebg || [],
            css: inversionFixes[0].css || ""
        };
        if (url) {
            const matches = inversionFixes
                .slice(1)
                .filter((s) => isURLInList(url, s.url))
                .sort((a, b) => b.url[0].length - a.url[0].length);
            if (matches.length > 0) {
                const found = matches[0];
                return {
                    url: found.url,
                    invert: common.invert.concat(found.invert || []),
                    noinvert: common.noinvert.concat(found.noinvert || []),
                    removebg: common.removebg.concat(found.removebg || []),
                    css: [common.css, found.css].filter((s) => s).join("\n")
                };
            }
        }
        return common;
    }
    const inversionFixesCommands = {
        "INVERT": "invert",
        "NO INVERT": "noinvert",
        "REMOVE BG": "removebg",
        "CSS": "css"
    };
    function parseInversionFixes(text) {
        return parseSitesFixesConfig(text, {
            commands: Object.keys(inversionFixesCommands),
            getCommandPropName: (command) => inversionFixesCommands[command],
            parseCommandValue: (command, value) => {
                if (command === "CSS") {
                    return value.trim();
                }
                return parseArray(value);
            }
        });
    }
    function formatInversionFixes(inversionFixes) {
        const fixes = inversionFixes
            .slice()
            .sort((a, b) => compareURLPatterns(a.url[0], b.url[0]));
        return formatSitesFixesConfig(fixes, {
            props: Object.values(inversionFixesCommands),
            getPropCommandName: (prop) =>
                Object.entries(inversionFixesCommands).find(
                    ([, p]) => p === prop
                )[0],
            formatPropValue: (prop, value) => {
                if (prop === "css") {
                    return value.trim().replace(/\n+/g, "\n");
                }
                return formatArray(value).trim();
            },
            shouldIgnoreProp: (prop, value) => {
                if (prop === "css") {
                    return !value;
                }
                return !(Array.isArray(value) && value.length > 0);
            }
        });
    }

    const dynamicThemeFixesCommands = {
        "INVERT": "invert",
        "CSS": "css",
        "IGNORE INLINE STYLE": "ignoreInlineStyle",
        "IGNORE IMAGE ANALYSIS": "ignoreImageAnalysis"
    };
    function parseDynamicThemeFixes(text) {
        return parseSitesFixesConfig(text, {
            commands: Object.keys(dynamicThemeFixesCommands),
            getCommandPropName: (command) => dynamicThemeFixesCommands[command],
            parseCommandValue: (command, value) => {
                if (command === "CSS") {
                    return value.trim();
                }
                return parseArray(value);
            }
        });
    }
    function formatDynamicThemeFixes(dynamicThemeFixes) {
        const fixes = dynamicThemeFixes
            .slice()
            .sort((a, b) => compareURLPatterns(a.url[0], b.url[0]));
        return formatSitesFixesConfig(fixes, {
            props: Object.values(dynamicThemeFixesCommands),
            getPropCommandName: (prop) =>
                Object.entries(dynamicThemeFixesCommands).find(
                    ([, p]) => p === prop
                )[0],
            formatPropValue: (prop, value) => {
                if (prop === "css") {
                    return value.trim().replace(/\n+/g, "\n");
                }
                return formatArray(value).trim();
            },
            shouldIgnoreProp: (prop, value) => {
                if (prop === "css") {
                    return !value;
                }
                return !(Array.isArray(value) && value.length > 0);
            }
        });
    }
    function getDynamicThemeFixesFor(
        url,
        frameURL,
        text,
        index,
        enabledForPDF
    ) {
        const fixes = getSitesFixesFor(frameURL || url, text, index, {
            commands: Object.keys(dynamicThemeFixesCommands),
            getCommandPropName: (command) => dynamicThemeFixesCommands[command],
            parseCommandValue: (command, value) => {
                if (command === "CSS") {
                    return value.trim();
                }
                return parseArray(value);
            }
        });
        if (fixes.length === 0 || fixes[0].url[0] !== "*") {
            return null;
        }
        const genericFix = fixes[0];
        const common = {
            url: genericFix.url,
            invert: genericFix.invert || [],
            css: genericFix.css || "",
            ignoreInlineStyle: genericFix.ignoreInlineStyle || [],
            ignoreImageAnalysis: genericFix.ignoreImageAnalysis || []
        };
        if (enabledForPDF) {
            common.css +=
                '\nembed[type="application/pdf"] { filter: invert(100%) contrast(90%); }';
        }
        const sortedBySpecificity = fixes
            .slice(1)
            .map((theme) => {
                return {
                    specificity: isURLInList(frameURL || url, theme.url)
                        ? theme.url[0].length
                        : 0,
                    theme
                };
            })
            .filter(({specificity}) => specificity > 0)
            .sort((a, b) => b.specificity - a.specificity);
        if (sortedBySpecificity.length === 0) {
            return common;
        }
        const match = sortedBySpecificity[0].theme;
        return {
            url: match.url,
            invert: common.invert.concat(match.invert || []),
            css: [common.css, match.css].filter((s) => s).join("\n"),
            ignoreInlineStyle: common.ignoreInlineStyle.concat(
                match.ignoreInlineStyle || []
            ),
            ignoreImageAnalysis: common.ignoreImageAnalysis.concat(
                match.ignoreImageAnalysis || []
            )
        };
    }

    const darkTheme = {
        neutralBg: [16, 20, 23],
        neutralText: [167, 158, 139],
        redBg: [64, 12, 32],
        redText: [247, 142, 102],
        greenBg: [32, 64, 48],
        greenText: [128, 204, 148],
        blueBg: [32, 48, 64],
        blueText: [128, 182, 204],
        fadeBg: [16, 20, 23, 0.5],
        fadeText: [167, 158, 139, 0.5]
    };
    const lightTheme = {
        neutralBg: [255, 242, 228],
        neutralText: [0, 0, 0],
        redBg: [255, 85, 170],
        redText: [140, 14, 48],
        greenBg: [192, 255, 170],
        greenText: [0, 128, 0],
        blueBg: [173, 215, 229],
        blueText: [28, 16, 171],
        fadeBg: [0, 0, 0, 0.5],
        fadeText: [0, 0, 0, 0.5]
    };
    function rgb([r, g, b, a]) {
        if (typeof a === "number") {
            return `rgba(${r}, ${g}, ${b}, ${a})`;
        }
        return `rgb(${r}, ${g}, ${b})`;
    }
    function mix(color1, color2, t) {
        return color1.map((c, i) => Math.round(c * (1 - t) + color2[i] * t));
    }
    function createStaticStylesheet(
        config,
        url,
        frameURL,
        staticThemes,
        staticThemesIndex
    ) {
        const srcTheme = config.mode === 1 ? darkTheme : lightTheme;
        const theme = Object.entries(srcTheme).reduce((t, [prop, color]) => {
            const [r, g, b, a] = color;
            t[prop] = applyColorMatrix(
                [r, g, b],
                createFilterMatrix({...config, mode: 0})
            );
            if (a !== undefined) {
                t[prop].push(a);
            }
            return t;
        }, {});
        const commonTheme = getCommonTheme(staticThemes, staticThemesIndex);
        const siteTheme = getThemeFor(
            frameURL || url,
            staticThemes,
            staticThemesIndex
        );
        const lines = [];
        if (!siteTheme || !siteTheme.noCommon) {
            lines.push("/* Common theme */");
            lines.push(...ruleGenerators.map((gen) => gen(commonTheme, theme)));
        }
        if (siteTheme) {
            lines.push(`/* Theme for ${siteTheme.url.join(" ")} */`);
            lines.push(...ruleGenerators.map((gen) => gen(siteTheme, theme)));
        }
        if (config.useFont || config.textStroke > 0) {
            lines.push("/* Font */");
            lines.push(createTextStyle(config));
        }
        return lines.filter((ln) => ln).join("\n");
    }
    function createRuleGen(
        getSelectors,
        generateDeclarations,
        modifySelector = (s) => s
    ) {
        return (siteTheme, themeColors) => {
            const selectors = getSelectors(siteTheme);
            if (selectors == null || selectors.length === 0) {
                return null;
            }
            const lines = [];
            selectors.forEach((s, i) => {
                let ln = modifySelector(s);
                if (i < selectors.length - 1) {
                    ln += ",";
                } else {
                    ln += " {";
                }
                lines.push(ln);
            });
            const declarations = generateDeclarations(themeColors);
            declarations.forEach((d) => lines.push(`    ${d} !important;`));
            lines.push("}");
            return lines.join("\n");
        };
    }
    const mx = {
        bg: {
            hover: 0.075,
            active: 0.1
        },
        fg: {
            hover: 0.25,
            active: 0.5
        },
        border: 0.5
    };
    const ruleGenerators = [
        createRuleGen(
            (t) => t.neutralBg,
            (t) => [`background-color: ${rgb(t.neutralBg)}`]
        ),
        createRuleGen(
            (t) => t.neutralBgActive,
            (t) => [`background-color: ${rgb(t.neutralBg)}`]
        ),
        createRuleGen(
            (t) => t.neutralBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.neutralBg, [255, 255, 255], mx.bg.hover)
                )}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.neutralBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.neutralBg, [255, 255, 255], mx.bg.active)
                )}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.neutralText,
            (t) => [`color: ${rgb(t.neutralText)}`]
        ),
        createRuleGen(
            (t) => t.neutralTextActive,
            (t) => [`color: ${rgb(t.neutralText)}`]
        ),
        createRuleGen(
            (t) => t.neutralTextActive,
            (t) => [
                `color: ${rgb(
                    mix(t.neutralText, [255, 255, 255], mx.fg.hover)
                )}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.neutralTextActive,
            (t) => [
                `color: ${rgb(
                    mix(t.neutralText, [255, 255, 255], mx.fg.active)
                )}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.neutralBorder,
            (t) => [
                `border-color: ${rgb(
                    mix(t.neutralBg, t.neutralText, mx.border)
                )}`
            ]
        ),
        createRuleGen(
            (t) => t.redBg,
            (t) => [`background-color: ${rgb(t.redBg)}`]
        ),
        createRuleGen(
            (t) => t.redBgActive,
            (t) => [`background-color: ${rgb(t.redBg)}`]
        ),
        createRuleGen(
            (t) => t.redBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.redBg, [255, 0, 64], mx.bg.hover)
                )}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.redBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.redBg, [255, 0, 64], mx.bg.active)
                )}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.redText,
            (t) => [`color: ${rgb(t.redText)}`]
        ),
        createRuleGen(
            (t) => t.redTextActive,
            (t) => [`color: ${rgb(t.redText)}`]
        ),
        createRuleGen(
            (t) => t.redTextActive,
            (t) => [
                `color: ${rgb(mix(t.redText, [255, 255, 0], mx.fg.hover))}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.redTextActive,
            (t) => [
                `color: ${rgb(mix(t.redText, [255, 255, 0], mx.fg.active))}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.redBorder,
            (t) => [`border-color: ${rgb(mix(t.redBg, t.redText, mx.border))}`]
        ),
        createRuleGen(
            (t) => t.greenBg,
            (t) => [`background-color: ${rgb(t.greenBg)}`]
        ),
        createRuleGen(
            (t) => t.greenBgActive,
            (t) => [`background-color: ${rgb(t.greenBg)}`]
        ),
        createRuleGen(
            (t) => t.greenBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.greenBg, [128, 255, 182], mx.bg.hover)
                )}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.greenBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.greenBg, [128, 255, 182], mx.bg.active)
                )}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.greenText,
            (t) => [`color: ${rgb(t.greenText)}`]
        ),
        createRuleGen(
            (t) => t.greenTextActive,
            (t) => [`color: ${rgb(t.greenText)}`]
        ),
        createRuleGen(
            (t) => t.greenTextActive,
            (t) => [
                `color: ${rgb(mix(t.greenText, [182, 255, 224], mx.fg.hover))}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.greenTextActive,
            (t) => [
                `color: ${rgb(mix(t.greenText, [182, 255, 224], mx.fg.active))}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.greenBorder,
            (t) => [
                `border-color: ${rgb(mix(t.greenBg, t.greenText, mx.border))}`
            ]
        ),
        createRuleGen(
            (t) => t.blueBg,
            (t) => [`background-color: ${rgb(t.blueBg)}`]
        ),
        createRuleGen(
            (t) => t.blueBgActive,
            (t) => [`background-color: ${rgb(t.blueBg)}`]
        ),
        createRuleGen(
            (t) => t.blueBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.blueBg, [0, 128, 255], mx.bg.hover)
                )}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.blueBgActive,
            (t) => [
                `background-color: ${rgb(
                    mix(t.blueBg, [0, 128, 255], mx.bg.active)
                )}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.blueText,
            (t) => [`color: ${rgb(t.blueText)}`]
        ),
        createRuleGen(
            (t) => t.blueTextActive,
            (t) => [`color: ${rgb(t.blueText)}`]
        ),
        createRuleGen(
            (t) => t.blueTextActive,
            (t) => [
                `color: ${rgb(mix(t.blueText, [182, 224, 255], mx.fg.hover))}`
            ],
            (s) => `${s}:hover`
        ),
        createRuleGen(
            (t) => t.blueTextActive,
            (t) => [
                `color: ${rgb(mix(t.blueText, [182, 224, 255], mx.fg.active))}`
            ],
            (s) => `${s}:active, ${s}:focus`
        ),
        createRuleGen(
            (t) => t.blueBorder,
            (t) => [
                `border-color: ${rgb(mix(t.blueBg, t.blueText, mx.border))}`
            ]
        ),
        createRuleGen(
            (t) => t.fadeBg,
            (t) => [`background-color: ${rgb(t.fadeBg)}`]
        ),
        createRuleGen(
            (t) => t.fadeText,
            (t) => [`color: ${rgb(t.fadeText)}`]
        ),
        createRuleGen(
            (t) => t.transparentBg,
            () => ["background-color: transparent"]
        ),
        createRuleGen(
            (t) => t.noImage,
            () => ["background-image: none"]
        ),
        createRuleGen(
            (t) => t.invert,
            () => ["filter: invert(100%) hue-rotate(180deg)"]
        )
    ];
    const staticThemeCommands = {
        "NO COMMON": "noCommon",
        "NEUTRAL BG": "neutralBg",
        "NEUTRAL BG ACTIVE": "neutralBgActive",
        "NEUTRAL TEXT": "neutralText",
        "NEUTRAL TEXT ACTIVE": "neutralTextActive",
        "NEUTRAL BORDER": "neutralBorder",
        "RED BG": "redBg",
        "RED BG ACTIVE": "redBgActive",
        "RED TEXT": "redText",
        "RED TEXT ACTIVE": "redTextActive",
        "RED BORDER": "redBorder",
        "GREEN BG": "greenBg",
        "GREEN BG ACTIVE": "greenBgActive",
        "GREEN TEXT": "greenText",
        "GREEN TEXT ACTIVE": "greenTextActive",
        "GREEN BORDER": "greenBorder",
        "BLUE BG": "blueBg",
        "BLUE BG ACTIVE": "blueBgActive",
        "BLUE TEXT": "blueText",
        "BLUE TEXT ACTIVE": "blueTextActive",
        "BLUE BORDER": "blueBorder",
        "FADE BG": "fadeBg",
        "FADE TEXT": "fadeText",
        "TRANSPARENT BG": "transparentBg",
        "NO IMAGE": "noImage",
        "INVERT": "invert"
    };
    function parseStaticThemes($themes) {
        return parseSitesFixesConfig($themes, {
            commands: Object.keys(staticThemeCommands),
            getCommandPropName: (command) => staticThemeCommands[command],
            parseCommandValue: (command, value) => {
                if (command === "NO COMMON") {
                    return true;
                }
                return parseArray(value);
            }
        });
    }
    function camelCaseToUpperCase(text) {
        return text.replace(/([a-z])([A-Z])/g, "$1 $2").toUpperCase();
    }
    function formatStaticThemes(staticThemes) {
        const themes = staticThemes
            .slice()
            .sort((a, b) => compareURLPatterns(a.url[0], b.url[0]));
        return formatSitesFixesConfig(themes, {
            props: Object.values(staticThemeCommands),
            getPropCommandName: camelCaseToUpperCase,
            formatPropValue: (prop, value) => {
                if (prop === "noCommon") {
                    return "";
                }
                return formatArray(value).trim();
            },
            shouldIgnoreProp: (prop, value) => {
                if (prop === "noCommon") {
                    return !value;
                }
                return !(Array.isArray(value) && value.length > 0);
            }
        });
    }
    function getCommonTheme(staticThemes, staticThemesIndex) {
        const length = parseInt(
            staticThemesIndex.offsets.substring(4, 4 + 3),
            36
        );
        const staticThemeText = staticThemes.substring(0, length);
        return parseStaticThemes(staticThemeText)[0];
    }
    function getThemeFor(url, staticThemes, staticThemesIndex) {
        const themes = getSitesFixesFor(url, staticThemes, staticThemesIndex, {
            commands: Object.keys(staticThemeCommands),
            getCommandPropName: (command) => staticThemeCommands[command],
            parseCommandValue: (command, value) => {
                if (command === "NO COMMON") {
                    return true;
                }
                return parseArray(value);
            }
        });
        const sortedBySpecificity = themes
            .slice(1)
            .map((theme) => {
                return {
                    specificity: isURLInList(url, theme.url)
                        ? theme.url[0].length
                        : 0,
                    theme
                };
            })
            .filter(({specificity}) => specificity > 0)
            .sort((a, b) => b.specificity - a.specificity);
        if (sortedBySpecificity.length === 0) {
            return null;
        }
        return sortedBySpecificity[0].theme;
    }

    class PersistentStorageWrapper {
        constructor() {
            this.cache = {};
            this.dataIsMigrated = false;
        }
        setDataIsMigratedForTesting(value) {}
        async migrateFromLocalStorage() {
            if (typeof localStorage === "undefined") {
                this.dataIsMigrated = true;
                return;
            }
            return new Promise((resolve) => {
                chrome.storage.local.get(
                    [
                        DevTools.KEY_DYNAMIC,
                        DevTools.KEY_FILTER,
                        DevTools.KEY_STATIC
                    ],
                    (data) => {
                        if (chrome.runtime.lastError) {
                            console.error(
                                "DevTools failed to migrate data",
                                chrome.runtime.lastError
                            );
                            resolve();
                        }
                        if (
                            data[DevTools.KEY_DYNAMIC] ||
                            data[DevTools.KEY_FILTER] ||
                            data[DevTools.KEY_STATIC]
                        ) {
                            this.dataIsMigrated = true;
                            resolve();
                            return;
                        }
                        this.cache = {
                            [DevTools.KEY_DYNAMIC]: localStorage.getItem(
                                DevTools.KEY_DYNAMIC
                            ),
                            [DevTools.KEY_FILTER]: localStorage.getItem(
                                DevTools.KEY_FILTER
                            ),
                            [DevTools.KEY_STATIC]: localStorage.getItem(
                                DevTools.KEY_STATIC
                            )
                        };
                        chrome.storage.local.set(this.cache, () => {
                            if (chrome.runtime.lastError) {
                                console.error(
                                    "DevTools failed to migrate data",
                                    chrome.runtime.lastError
                                );
                                resolve();
                            }
                            this.dataIsMigrated = true;
                            localStorage.removeItem(DevTools.KEY_DYNAMIC);
                            localStorage.removeItem(DevTools.KEY_FILTER);
                            localStorage.removeItem(DevTools.KEY_STATIC);
                            resolve();
                        });
                    }
                );
            });
        }
        async get(key) {
            if (!this.dataIsMigrated) {
                await this.migrateFromLocalStorage();
            }
            if (key in this.cache) {
                return this.cache[key];
            }
            return new Promise((resolve) => {
                chrome.storage.local.get(key, (result) => {
                    if (key in this.cache) {
                        resolve(this.cache[key]);
                        return;
                    }
                    if (chrome.runtime.lastError) {
                        console.error(
                            "Failed to query DevTools data",
                            chrome.runtime.lastError
                        );
                        resolve(null);
                        return;
                    }
                    this.cache[key] = result.key;
                    resolve(result.key);
                });
            });
        }
        set(key, value) {
            this.cache[key] = value;
            chrome.storage.local.set({[key]: value}, () => {
                if (chrome.runtime.lastError) {
                    console.error(
                        "Failed to write DevTools data",
                        chrome.runtime.lastError
                    );
                }
            });
        }
        remove(key) {
            this.cache[key] = undefined;
            chrome.storage.local.remove(key, () => {
                if (chrome.runtime.lastError) {
                    console.error(
                        "Failed to delete DevTools data",
                        chrome.runtime.lastError
                    );
                }
            });
        }
        async has(key) {
            return Boolean(await this.get(key));
        }
    }
    class LocalStorageWrapper {
        setDataIsMigratedForTesting() {}
        async get(key) {
            try {
                return localStorage.getItem(key);
            } catch (err) {
                console.error(err);
                return null;
            }
        }
        set(key, value) {
            try {
                localStorage.setItem(key, value);
            } catch (err) {
                console.error(err);
            }
        }
        remove(key) {
            try {
                localStorage.removeItem(key);
            } catch (err) {
                console.error(err);
            }
        }
        async has(key) {
            try {
                return localStorage.getItem(key) != null;
            } catch (err) {
                console.error(err);
                return false;
            }
        }
    }
    class TempStorage {
        constructor() {
            this.map = new Map();
        }
        setDataIsMigratedForTesting() {}
        async get(key) {
            return this.map.get(key);
        }
        set(key, value) {
            this.map.set(key, value);
        }
        remove(key) {
            this.map.delete(key);
        }
        async has(key) {
            return this.map.has(key);
        }
    }
    class DevTools {
        constructor(config, onChange) {
            if (
                typeof chrome.storage.local !== "undefined" &&
                chrome.storage.local !== null
            ) {
                this.store = new PersistentStorageWrapper();
            } else if (
                typeof localStorage !== "undefined" &&
                localStorage != null
            ) {
                this.store = new LocalStorageWrapper();
            } else {
                this.store = new TempStorage();
            }
            this.config = config;
            this.loadConfigOverrides();
            this.onChange = onChange;
        }
        setDataIsMigratedForTesting(value) {
            this.store.setDataIsMigratedForTesting(value);
        }
        async loadConfigOverrides() {
            this.config.overrides.dynamicThemeFixes =
                (await this.getSavedDynamicThemeFixes()) || null;
            this.config.overrides.inversionFixes =
                (await this.getSavedInversionFixes()) || null;
            this.config.overrides.staticThemes =
                (await this.getSavedStaticThemes()) || null;
        }
        async getSavedDynamicThemeFixes() {
            return this.store.get(DevTools.KEY_DYNAMIC);
        }
        saveDynamicThemeFixes(text) {
            this.store.set(DevTools.KEY_DYNAMIC, text);
        }
        async hasCustomDynamicThemeFixes() {
            return this.store.has(DevTools.KEY_DYNAMIC);
        }
        async getDynamicThemeFixesText() {
            const $fixes = await this.getSavedDynamicThemeFixes();
            const fixes = $fixes
                ? parseDynamicThemeFixes($fixes)
                : parseDynamicThemeFixes(this.config.DYNAMIC_THEME_FIXES_RAW);
            return formatDynamicThemeFixes(fixes);
        }
        resetDynamicThemeFixes() {
            this.store.remove(DevTools.KEY_DYNAMIC);
            this.config.overrides.dynamicThemeFixes = null;
            this.config.handleDynamicThemeFixes();
            this.onChange();
        }
        applyDynamicThemeFixes(text) {
            try {
                const formatted = formatDynamicThemeFixes(
                    parseDynamicThemeFixes(text)
                );
                this.config.overrides.dynamicThemeFixes = formatted;
                this.config.handleDynamicThemeFixes();
                this.saveDynamicThemeFixes(formatted);
                this.onChange();
                return null;
            } catch (err) {
                return err;
            }
        }
        async getSavedInversionFixes() {
            return this.store.get(DevTools.KEY_FILTER);
        }
        saveInversionFixes(text) {
            this.store.set(DevTools.KEY_FILTER, text);
        }
        async hasCustomFilterFixes() {
            return this.store.has(DevTools.KEY_FILTER);
        }
        async getInversionFixesText() {
            const $fixes = await this.getSavedInversionFixes();
            const fixes = $fixes
                ? parseInversionFixes($fixes)
                : parseInversionFixes(this.config.INVERSION_FIXES_RAW);
            return formatInversionFixes(fixes);
        }
        resetInversionFixes() {
            this.store.remove(DevTools.KEY_FILTER);
            this.config.overrides.inversionFixes = null;
            this.config.handleInversionFixes();
            this.onChange();
        }
        applyInversionFixes(text) {
            try {
                const formatted = formatInversionFixes(
                    parseInversionFixes(text)
                );
                this.config.overrides.inversionFixes = formatted;
                this.config.handleInversionFixes();
                this.saveInversionFixes(formatted);
                this.onChange();
                return null;
            } catch (err) {
                return err;
            }
        }
        async getSavedStaticThemes() {
            return this.store.get(DevTools.KEY_STATIC);
        }
        saveStaticThemes(text) {
            this.store.set(DevTools.KEY_STATIC, text);
        }
        async hasCustomStaticFixes() {
            return this.store.has(DevTools.KEY_STATIC);
        }
        async getStaticThemesText() {
            const $themes = await this.getSavedStaticThemes();
            const themes = $themes
                ? parseStaticThemes($themes)
                : parseStaticThemes(this.config.STATIC_THEMES_RAW);
            return formatStaticThemes(themes);
        }
        resetStaticThemes() {
            this.store.remove(DevTools.KEY_STATIC);
            this.config.overrides.staticThemes = null;
            this.config.handleStaticThemes();
            this.onChange();
        }
        applyStaticThemes(text) {
            try {
                const formatted = formatStaticThemes(parseStaticThemes(text));
                this.config.overrides.staticThemes = formatted;
                this.config.handleStaticThemes();
                this.saveStaticThemes(formatted);
                this.onChange();
                return null;
            } catch (err) {
                return err;
            }
        }
    }
    DevTools.KEY_DYNAMIC = "dev_dynamic_theme_fixes";
    DevTools.KEY_FILTER = "dev_inversion_fixes";
    DevTools.KEY_STATIC = "dev_static_themes";

    const ICON_PATHS = {
        active_19: "../icons/dr_active_19.png",
        active_38: "../icons/dr_active_38.png",
        inactive_19: "../icons/dr_inactive_19.png",
        inactive_38: "../icons/dr_inactive_38.png"
    };
    class IconManager {
        setActive() {
            if (!chrome.browserAction.setIcon || isThunderbird) {
                return;
            }
            chrome.browserAction.setIcon({
                path: {
                    19: ICON_PATHS.active_19,
                    38: ICON_PATHS.active_38
                }
            });
        }
        setInactive() {
            if (!chrome.browserAction.setIcon || isThunderbird) {
                return;
            }
            chrome.browserAction.setIcon({
                path: {
                    19: ICON_PATHS.inactive_19,
                    38: ICON_PATHS.inactive_38
                }
            });
        }
        showImportantBadge() {
            chrome.browserAction.setBadgeBackgroundColor({color: "#e96c4c"});
            chrome.browserAction.setBadgeText({text: "!"});
        }
        hideBadge() {
            chrome.browserAction.setBadgeText({text: ""});
        }
    }

    const MessageType = {
        UI_GET_DATA: "ui-get-data",
        UI_GET_ACTIVE_TAB_INFO: "ui-get-active-tab-info",
        UI_SUBSCRIBE_TO_CHANGES: "ui-subscribe-to-changes",
        UI_UNSUBSCRIBE_FROM_CHANGES: "ui-unsubscribe-from-changes",
        UI_CHANGE_SETTINGS: "ui-change-settings",
        UI_SET_THEME: "ui-set-theme",
        UI_SET_SHORTCUT: "ui-set-shortcut",
        UI_TOGGLE_URL: "ui-toggle-url",
        UI_MARK_NEWS_AS_READ: "ui-mark-news-as-read",
        UI_LOAD_CONFIG: "ui-load-config",
        UI_APPLY_DEV_DYNAMIC_THEME_FIXES: "ui-apply-dev-dynamic-theme-fixes",
        UI_RESET_DEV_DYNAMIC_THEME_FIXES: "ui-reset-dev-dynamic-theme-fixes",
        UI_APPLY_DEV_INVERSION_FIXES: "ui-apply-dev-inversion-fixes",
        UI_RESET_DEV_INVERSION_FIXES: "ui-reset-dev-inversion-fixes",
        UI_APPLY_DEV_STATIC_THEMES: "ui-apply-dev-static-themes",
        UI_RESET_DEV_STATIC_THEMES: "ui-reset-dev-static-themes",
        UI_SAVE_FILE: "ui-save-file",
        UI_REQUEST_EXPORT_CSS: "ui-request-export-css",
        BG_CHANGES: "bg-changes",
        BG_ADD_CSS_FILTER: "bg-add-css-filter",
        BG_ADD_STATIC_THEME: "bg-add-static-theme",
        BG_ADD_SVG_FILTER: "bg-add-svg-filter",
        BG_ADD_DYNAMIC_THEME: "bg-add-dynamic-theme",
        BG_EXPORT_CSS: "bg-export-css",
        BG_UNSUPPORTED_SENDER: "bg-unsupported-sender",
        BG_CLEAN_UP: "bg-clean-up",
        BG_RELOAD: "bg-reload",
        BG_FETCH_RESPONSE: "bg-fetch-response",
        BG_UI_UPDATE: "bg-ui-update",
        BG_CSS_UPDATE: "bg-css-update",
        CS_COLOR_SCHEME_CHANGE: "cs-color-scheme-change",
        CS_FRAME_CONNECT: "cs-frame-connect",
        CS_FRAME_FORGET: "cs-frame-forget",
        CS_FRAME_FREEZE: "cs-frame-freeze",
        CS_FRAME_RESUME: "cs-frame-resume",
        CS_EXPORT_CSS_RESPONSE: "cs-export-css-response",
        CS_FETCH: "cs-fetch"
    };

    class Messenger {
        constructor(adapter) {
            this.adapter = adapter;
            this.changeListenerCount = 0;
            const allowedSenderURL = [
                chrome.runtime.getURL("/ui/popup/index.html"),
                chrome.runtime.getURL("/ui/devtools/index.html"),
                chrome.runtime.getURL("/ui/stylesheet-editor/index.html")
            ];
            chrome.runtime.onMessage.addListener(
                (message, sender, sendResponse) => {
                    if (allowedSenderURL.includes(sender.url)) {
                        this.onUIMessage(message, sendResponse);
                        this.adapter.onPopupOpen();
                        return [
                            MessageType.UI_GET_DATA,
                            MessageType.UI_GET_ACTIVE_TAB_INFO
                        ].includes(message.type);
                    }
                }
            );
            if (isFirefox) {
                chrome.runtime.onConnect.addListener((port) => {
                    let promise;
                    switch (port.name) {
                        case MessageType.UI_GET_DATA:
                            promise = this.adapter.collect();
                            break;
                        case MessageType.UI_GET_ACTIVE_TAB_INFO:
                            promise = this.adapter.getActiveTabInfo();
                            break;
                        case MessageType.UI_APPLY_DEV_DYNAMIC_THEME_FIXES:
                        case MessageType.UI_APPLY_DEV_INVERSION_FIXES:
                        case MessageType.UI_APPLY_DEV_STATIC_THEMES:
                            promise = new Promise((resolve, reject) => {
                                port.onMessage.addListener((message) => {
                                    const {data} = message;
                                    let error;
                                    switch (port.name) {
                                        case MessageType.UI_APPLY_DEV_DYNAMIC_THEME_FIXES:
                                            error =
                                                this.adapter.applyDevDynamicThemeFixes(
                                                    data
                                                );
                                            break;
                                        case MessageType.UI_APPLY_DEV_INVERSION_FIXES:
                                            error =
                                                this.adapter.applyDevInversionFixes(
                                                    data
                                                );
                                            break;
                                        case MessageType.UI_APPLY_DEV_STATIC_THEMES:
                                            error =
                                                this.adapter.applyDevStaticThemes(
                                                    data
                                                );
                                            break;
                                        default:
                                            throw new Error(
                                                `Unknown port name: ${port.name}`
                                            );
                                    }
                                    if (error) {
                                        reject(error);
                                    } else {
                                        resolve(null);
                                    }
                                });
                            });
                            break;
                        default:
                            return;
                    }
                    promise
                        .then((data) => port.postMessage({data}))
                        .catch((error) => port.postMessage({error}));
                });
            }
        }
        onUIMessage({type, data}, sendResponse) {
            switch (type) {
                case MessageType.UI_GET_DATA: {
                    this.adapter.collect().then((data) => sendResponse({data}));
                    break;
                }
                case MessageType.UI_GET_ACTIVE_TAB_INFO: {
                    this.adapter
                        .getActiveTabInfo()
                        .then((data) => sendResponse({data}));
                    break;
                }
                case MessageType.UI_SUBSCRIBE_TO_CHANGES: {
                    this.changeListenerCount++;
                    break;
                }
                case MessageType.UI_UNSUBSCRIBE_FROM_CHANGES: {
                    this.changeListenerCount--;
                    break;
                }
                case MessageType.UI_CHANGE_SETTINGS: {
                    this.adapter.changeSettings(data);
                    break;
                }
                case MessageType.UI_SET_THEME: {
                    this.adapter.setTheme(data);
                    break;
                }
                case MessageType.UI_SET_SHORTCUT: {
                    this.adapter.setShortcut(data);
                    break;
                }
                case MessageType.UI_TOGGLE_URL: {
                    this.adapter.toggleURL(data);
                    break;
                }
                case MessageType.UI_MARK_NEWS_AS_READ: {
                    this.adapter.markNewsAsRead(data);
                    break;
                }
                case MessageType.UI_LOAD_CONFIG: {
                    this.adapter.loadConfig(data);
                    break;
                }
                case MessageType.UI_APPLY_DEV_DYNAMIC_THEME_FIXES: {
                    const error = this.adapter.applyDevDynamicThemeFixes(data);
                    sendResponse({error: error ? error.message : null});
                    break;
                }
                case MessageType.UI_RESET_DEV_DYNAMIC_THEME_FIXES: {
                    this.adapter.resetDevDynamicThemeFixes();
                    break;
                }
                case MessageType.UI_APPLY_DEV_INVERSION_FIXES: {
                    const error = this.adapter.applyDevInversionFixes(data);
                    sendResponse({error: error ? error.message : null});
                    break;
                }
                case MessageType.UI_RESET_DEV_INVERSION_FIXES: {
                    this.adapter.resetDevInversionFixes();
                    break;
                }
                case MessageType.UI_APPLY_DEV_STATIC_THEMES: {
                    const error = this.adapter.applyDevStaticThemes(data);
                    sendResponse({error: error ? error.message : null});
                    break;
                }
                case MessageType.UI_RESET_DEV_STATIC_THEMES: {
                    this.adapter.resetDevStaticThemes();
                    break;
                }
            }
        }
        reportChanges(data) {
            if (this.changeListenerCount > 0) {
                chrome.runtime.sendMessage({
                    type: MessageType.BG_CHANGES,
                    data
                });
            }
        }
    }

    function getUILanguage() {
        let code;
        if (
            "i18n" in chrome &&
            "getUILanguage" in chrome.i18n &&
            typeof chrome.i18n.getUILanguage === "function"
        ) {
            code = chrome.i18n.getUILanguage();
        } else {
            code = navigator.language.split("-")[0];
        }
        if (code.endsWith("-mac")) {
            return code.substring(0, code.length - 4);
        }
        return code;
    }

    const BLOG_URL = "https://darkreader.org/blog/";
    const UNINSTALL_URL = "https://darkreader.org/goodluck/";
    const helpLocales = [
        "be",
        "cs",
        "de",
        "en",
        "es",
        "fr",
        "it",
        "nl",
        "pt",
        "ru",
        "tr",
        "zh-CN",
        "zh-TW"
    ];
    function getHelpURL() {
        const locale = getUILanguage();
        const matchLocale =
            helpLocales.find((hl) => hl === locale) ||
            helpLocales.find((hl) => locale.startsWith(hl)) ||
            "en";
        return `https://darkreader.org/help/${matchLocale}/`;
    }
    function getBlogPostURL(postId) {
        return `${BLOG_URL}${postId}/`;
    }

    class Mutex {
        constructor() {
            this.awaitingResolves = [];
            this.locked = false;
        }
        isLocked() {
            return this.locked;
        }
        async lock() {
            if (!this.locked) {
                this.locked = true;
                return;
            }
            return new Promise((resolve) => {
                this.awaitingResolves.push(resolve);
            });
        }
        unlock() {
            if (!this.locked) {
                return;
            }
            this.locked = false;
            setTimeout(() => this.executeNextOperation());
        }
        executeNextOperation() {
            if (this.awaitingResolves.length > 0) {
                const resolve = this.awaitingResolves.shift();
                resolve();
            }
        }
    }

    function canInjectScript(url) {
        if (isFirefox) {
            return (
                url &&
                !url.startsWith("about:") &&
                !url.startsWith("moz") &&
                !url.startsWith("view-source:") &&
                !url.startsWith("https://addons.mozilla.org/") &&
                !isPDF(url)
            );
        }
        if (isEdge) {
            return (
                url &&
                !url.startsWith("chrome") &&
                !url.startsWith("data") &&
                !url.startsWith("devtools") &&
                !url.startsWith("edge") &&
                !url.startsWith("https://chrome.google.com/webstore") &&
                !url.startsWith("https://microsoftedge.microsoft.com/addons") &&
                !url.startsWith("view-source")
            );
        }
        return (
            url &&
            !url.startsWith("chrome") &&
            !url.startsWith("https://chrome.google.com/webstore") &&
            !url.startsWith("data") &&
            !url.startsWith("devtools") &&
            !url.startsWith("view-source")
        );
    }
    const mutexStorageWriting = new Mutex();
    async function readSyncStorage(defaults) {
        return new Promise((resolve) => {
            chrome.storage.sync.get(null, (sync) => {
                if (chrome.runtime.lastError) {
                    console.error(chrome.runtime.lastError.message);
                    resolve(defaults);
                    return;
                }
                for (const key in sync) {
                    if (!sync[key]) {
                        continue;
                    }
                    const metaKeysCount = sync[key].__meta_split_count;
                    if (!metaKeysCount) {
                        continue;
                    }
                    let string = "";
                    for (let i = 0; i < metaKeysCount; i++) {
                        string += sync[`${key}_${i.toString(36)}`];
                        delete sync[`${key}_${i.toString(36)}`];
                    }
                    try {
                        sync[key] = JSON.parse(string);
                    } catch (error) {
                        console.error(
                            "Could not parse record from sync storage",
                            string
                        );
                    }
                }
                sync = {
                    ...defaults,
                    ...sync
                };
                resolve(sync);
            });
        });
    }
    async function readLocalStorage(defaults) {
        return new Promise((resolve) => {
            chrome.storage.local.get(defaults, (local) => {
                if (chrome.runtime.lastError) {
                    console.error(chrome.runtime.lastError.message);
                    resolve(defaults);
                    return;
                }
                resolve(local);
            });
        });
    }
    function prepareSyncStorage(values) {
        for (const key in values) {
            const value = values[key];
            const string = JSON.stringify(value);
            const totalLength = string.length + key.length;
            if (totalLength > chrome.storage.sync.QUOTA_BYTES_PER_ITEM) {
                const maxLength =
                    chrome.storage.sync.QUOTA_BYTES_PER_ITEM -
                    key.length -
                    1 -
                    2;
                const minimalKeysNeeded = Math.ceil(string.length / maxLength);
                for (let i = 0; i < minimalKeysNeeded; i++) {
                    values[`${key}_${i.toString(36)}`] = string.substring(
                        i * maxLength,
                        (i + 1) * maxLength
                    );
                }
                values[key] = {
                    __meta_split_count: minimalKeysNeeded
                };
            }
        }
        return values;
    }
    async function writeSyncStorage(values) {
        return new Promise(async (resolve, reject) => {
            const packaged = prepareSyncStorage(values);
            await mutexStorageWriting.lock();
            chrome.storage.sync.set(packaged, () => {
                if (chrome.runtime.lastError) {
                    reject(chrome.runtime.lastError);
                    mutexStorageWriting.unlock();
                    return;
                }
                resolve();
                setTimeout(() => mutexStorageWriting.unlock(), 500);
            });
        });
    }
    async function writeLocalStorage(values) {
        return new Promise(async (resolve) => {
            await mutexStorageWriting.lock();
            chrome.storage.local.set(values, () => {
                resolve();
                setTimeout(() => mutexStorageWriting.unlock(), 500);
            });
        });
    }
    const subscribeToOuterSettingsChange = (callback) => {
        chrome.storage.onChanged.addListener((_, storageArea) => {
            if (storageArea === "sync" && !mutexStorageWriting.isLocked()) {
                callback();
            }
        });
    };
    async function getCommands() {
        return new Promise((resolve) => {
            if (!chrome.commands) {
                resolve([]);
                return;
            }
            chrome.commands.getAll((commands) => {
                if (commands) {
                    resolve(commands);
                } else {
                    resolve([]);
                }
            });
        });
    }
    function setShortcut(command, shortcut) {
        if (
            typeof browser !== "undefined" &&
            browser.commands &&
            browser.commands.update
        ) {
            browser.commands.update({name: command, shortcut});
        }
    }

    class PromiseBarrier {
        constructor() {
            this.resolves = [];
            this.rejects = [];
            this.wasResolved = false;
            this.wasRejected = false;
        }
        async entry() {
            if (this.wasResolved) {
                return Promise.resolve(this.resolution);
            }
            if (this.wasRejected) {
                return Promise.reject(this.reason);
            }
            return new Promise((resolve, reject) => {
                this.resolves.push(resolve);
                this.rejects.push(reject);
            });
        }
        async resolve(value) {
            if (this.wasRejected || this.wasResolved) {
                return;
            }
            this.wasResolved = true;
            this.resolution = value;
            this.resolves.forEach((resolve) => resolve(value));
            this.resolves = null;
            this.rejects = null;
            return new Promise((resolve) => setTimeout(() => resolve()));
        }
        async reject(reason) {
            if (this.wasRejected || this.wasResolved) {
                return;
            }
            this.wasRejected = true;
            this.reason = reason;
            this.rejects.forEach((reject) => reject(reason));
            this.resolves = null;
            this.rejects = null;
            return new Promise((resolve) => setTimeout(() => resolve()));
        }
        isPending() {
            return !this.wasResolved && !this.wasRejected;
        }
        isFulfilled() {
            return this.wasResolved;
        }
        isRejected() {
            return this.wasRejected;
        }
    }

    function isNonPersistent() {
        if (!isChromium) {
            return false;
        }
        const background = chrome.runtime.getManifest().background;
        if ("persistent" in background) {
            return background.persistent === false;
        }
        if ("service_worker" in background) {
            return true;
        }
    }

    var StateManagerState;
    (function (StateManagerState) {
        StateManagerState[(StateManagerState["INITIAL"] = 0)] = "INITIAL";
        StateManagerState[(StateManagerState["DISABLED"] = 1)] = "DISABLED";
        StateManagerState[(StateManagerState["LOADING"] = 2)] = "LOADING";
        StateManagerState[(StateManagerState["READY"] = 3)] = "READY";
        StateManagerState[(StateManagerState["SAVING"] = 4)] = "SAVING";
        StateManagerState[(StateManagerState["SAVING_OVERRIDE"] = 5)] =
            "SAVING_OVERRIDE";
    })(StateManagerState || (StateManagerState = {}));
    class StateManager {
        constructor(localStorageKey, parent, defaults) {
            this.meta = StateManagerState.INITIAL;
            this.loadStateBarrier = null;
            if (!isNonPersistent()) {
                this.meta = StateManagerState.DISABLED;
                return;
            }
            this.localStorageKey = localStorageKey;
            this.parent = parent;
            this.defaults = defaults;
        }
        collectState() {
            const state = {};
            for (const key of Object.keys(this.defaults)) {
                state[key] = this.parent[key] || this.defaults[key];
            }
            return state;
        }
        async saveState() {
            switch (this.meta) {
                case StateManagerState.DISABLED:
                    return;
                case StateManagerState.LOADING:
                case StateManagerState.INITIAL:
                    if (this.loadStateBarrier) {
                        await this.loadStateBarrier.entry();
                    }
                    this.meta = StateManagerState.SAVING;
                    break;
                case StateManagerState.READY:
                    this.meta = StateManagerState.SAVING;
                    break;
                case StateManagerState.SAVING:
                    this.meta = StateManagerState.SAVING_OVERRIDE;
                    return;
                case StateManagerState.SAVING_OVERRIDE:
                    return;
            }
            chrome.storage.local.set(
                {[this.localStorageKey]: this.collectState()},
                () => {
                    switch (this.meta) {
                        case StateManagerState.INITIAL:
                        case StateManagerState.DISABLED:
                        case StateManagerState.LOADING:
                        case StateManagerState.READY:
                        case StateManagerState.SAVING:
                            this.meta = StateManagerState.READY;
                            break;
                        case StateManagerState.SAVING_OVERRIDE:
                            this.meta = StateManagerState.READY;
                            this.saveState();
                    }
                }
            );
        }
        async loadState() {
            switch (this.meta) {
                case StateManagerState.INITIAL:
                    this.meta = StateManagerState.LOADING;
                    this.loadStateBarrier = new PromiseBarrier();
                    return new Promise((resolve) => {
                        chrome.storage.local.get(
                            this.localStorageKey,
                            (data) => {
                                this.meta = StateManagerState.READY;
                                if (data[this.localStorageKey]) {
                                    Object.assign(
                                        this.parent,
                                        data[this.localStorageKey]
                                    );
                                } else {
                                    Object.assign(this.parent, this.defaults);
                                }
                                this.loadStateBarrier.resolve();
                                this.loadStateBarrier = null;
                                resolve();
                            }
                        );
                    });
                case StateManagerState.DISABLED:
                case StateManagerState.READY:
                case StateManagerState.SAVING:
                case StateManagerState.SAVING_OVERRIDE:
                    return;
                case StateManagerState.LOADING:
                    return this.loadStateBarrier.entry();
            }
        }
    }

    class Newsmaker {
        constructor(onUpdate) {
            this.alarmListener = (alarm) => {
                if (alarm.name === Newsmaker.ALARM_NAME) {
                    this.updateNews();
                }
            };
            this.stateManager = new StateManager(
                Newsmaker.LOCAL_STORAGE_KEY,
                this,
                {latest: [], latestTimestamp: null}
            );
            this.latest = [];
            this.latestTimestamp = null;
            this.onUpdate = onUpdate;
        }
        async getLatest() {
            await this.stateManager.loadState();
            return this.latest;
        }
        subscribe() {
            if (
                this.latestTimestamp === null ||
                this.latestTimestamp + Newsmaker.UPDATE_INTERVAL < Date.now()
            ) {
                this.updateNews();
            }
            chrome.alarms.onAlarm.addListener((alarm) =>
                this.alarmListener(alarm)
            );
            chrome.alarms.create(Newsmaker.ALARM_NAME, {
                periodInMinutes: Newsmaker.UPDATE_INTERVAL
            });
        }
        unSubscribe() {
            chrome.alarms.onAlarm.removeListener(this.alarmListener);
            chrome.alarms.clear(Newsmaker.ALARM_NAME);
        }
        async updateNews() {
            const news = await this.getNews();
            if (Array.isArray(news)) {
                this.latest = news;
                this.latestTimestamp = Date.now();
                this.onUpdate(this.latest);
                await this.stateManager.saveState();
            }
        }
        async getReadNews() {
            const sync = await readSyncStorage({readNews: []});
            const local = await readLocalStorage({readNews: []});
            return Array.from(
                new Set([
                    ...(sync ? sync.readNews : []),
                    ...(local ? local.readNews : [])
                ])
            );
        }
        async getNews() {
            try {
                const response = await fetch(
                    `https://darkreader.github.io/blog/posts.json`,
                    {cache: "no-cache"}
                );
                const $news = await response.json();
                const readNews = await this.getReadNews();
                const news = $news.map(({id, date, headline, important}) => {
                    const url = getBlogPostURL(id);
                    const read = this.isRead(id, readNews);
                    return {id, date, headline, url, important, read};
                });
                for (let i = 0; i < news.length; i++) {
                    const date = new Date(news[i].date);
                    if (isNaN(date.getTime())) {
                        throw new Error(`Unable to parse date ${date}`);
                    }
                }
                return news;
            } catch (err) {
                console.error(err);
                return null;
            }
        }
        async markAsRead(...ids) {
            const readNews = await this.getReadNews();
            const results = readNews.slice();
            let changed = false;
            ids.forEach((id) => {
                if (readNews.indexOf(id) < 0) {
                    results.push(id);
                    changed = true;
                }
            });
            if (changed) {
                this.latest = this.latest.map(
                    ({id, date, url, headline, important}) => {
                        const read = this.isRead(id, results);
                        return {id, date, url, headline, important, read};
                    }
                );
                this.onUpdate(this.latest);
                const obj = {readNews: results};
                await writeLocalStorage(obj);
                await writeSyncStorage(obj);
                await this.stateManager.saveState();
            }
        }
        isRead(id, readNews) {
            return readNews.includes(id);
        }
    }
    Newsmaker.UPDATE_INTERVAL = getDurationInMinutes({hours: 4});
    Newsmaker.ALARM_NAME = "newsmaker";
    Newsmaker.LOCAL_STORAGE_KEY = "Newsmaker-state";

    async function queryTabs(query) {
        return new Promise((resolve) => {
            chrome.tabs.query(query, (tabs) => resolve(tabs));
        });
    }
    var DocumentState;
    (function (DocumentState) {
        DocumentState[(DocumentState["ACTIVE"] = 0)] = "ACTIVE";
        DocumentState[(DocumentState["PASSIVE"] = 1)] = "PASSIVE";
        DocumentState[(DocumentState["HIDDEN"] = 2)] = "HIDDEN";
        DocumentState[(DocumentState["FROZEN"] = 3)] = "FROZEN";
        DocumentState[(DocumentState["TERMINATED"] = 4)] = "TERMINATED";
        DocumentState[(DocumentState["DISCARDED"] = 5)] = "DISCARDED";
    })(DocumentState || (DocumentState = {}));
    class TabManager {
        constructor({
            getConnectionMessage,
            onColorSchemeChange,
            getTabMessage
        }) {
            this.fileLoader = null;
            this.timestamp = null;
            this.stateManager = new StateManager(
                TabManager.LOCAL_STORAGE_KEY,
                this,
                {tabs: {}, timestamp: 0}
            );
            this.tabs = {};
            this.getTabMessage = getTabMessage;
            chrome.runtime.onMessage.addListener(
                async (message, sender, sendResponse) => {
                    function addFrame(
                        tabs,
                        tabId,
                        frameId,
                        senderURL,
                        timestamp
                    ) {
                        let frames;
                        if (tabs[tabId]) {
                            frames = tabs[tabId];
                        } else {
                            frames = {};
                            tabs[tabId] = frames;
                        }
                        frames[frameId] = {
                            url: senderURL,
                            state: DocumentState.ACTIVE,
                            timestamp
                        };
                    }
                    switch (message.type) {
                        case MessageType.CS_FRAME_CONNECT: {
                            await this.stateManager.loadState();
                            const reply = (options) => {
                                const message = getConnectionMessage(options);
                                if (message instanceof Promise) {
                                    message.then(
                                        (asyncMessage) =>
                                            asyncMessage &&
                                            chrome.tabs.sendMessage(
                                                sender.tab.id,
                                                asyncMessage,
                                                {frameId: sender.frameId}
                                            )
                                    );
                                } else if (message) {
                                    chrome.tabs.sendMessage(
                                        sender.tab.id,
                                        message,
                                        {frameId: sender.frameId}
                                    );
                                }
                            };
                            const isPanel =
                                typeof sender === "undefined" ||
                                typeof sender.tab === "undefined";
                            if (isPanel) {
                                if (isFirefox) {
                                    if (
                                        sender &&
                                        sender.tab &&
                                        typeof sender.tab.id === "number"
                                    ) {
                                        chrome.tabs.sendMessage(
                                            sender.tab.id,
                                            {
                                                type: MessageType.BG_UNSUPPORTED_SENDER
                                            },
                                            {
                                                frameId:
                                                    sender &&
                                                    typeof sender.frameId ===
                                                        "number"
                                                        ? sender.frameId
                                                        : undefined
                                            }
                                        );
                                    }
                                } else {
                                    sendResponse("unsupportedSender");
                                }
                                return;
                            }
                            const tabId = sender.tab.id;
                            const {frameId} = sender;
                            const senderURL = sender.url;
                            const tabURL = sender.tab.url;
                            addFrame(
                                this.tabs,
                                tabId,
                                frameId,
                                senderURL,
                                this.timestamp
                            );
                            reply({
                                url: tabURL,
                                frameURL: frameId === 0 ? null : senderURL
                            });
                            this.stateManager.saveState();
                            sendResponse({type: "¯\\_(ツ)_/¯"});
                            break;
                        }
                        case MessageType.CS_FRAME_FORGET: {
                            await this.stateManager.loadState();
                            if (!sender.tab) {
                                break;
                            }
                            const tabId = sender.tab.id;
                            const frameId = sender.frameId;
                            if (frameId === 0) {
                                delete this.tabs[tabId];
                            }
                            if (this.tabs[tabId] && this.tabs[tabId][frameId]) {
                                delete this.tabs[tabId][frameId];
                            }
                            this.stateManager.saveState();
                            break;
                        }
                        case MessageType.CS_FRAME_FREEZE:
                            await this.stateManager.loadState();
                            const info =
                                this.tabs[sender.tab.id][sender.frameId];
                            info.state = DocumentState.FROZEN;
                            info.url = null;
                            this.stateManager.saveState();
                            break;
                        case MessageType.CS_FRAME_RESUME: {
                            await this.stateManager.loadState();
                            const tabId = sender.tab.id;
                            const frameId = sender.frameId;
                            const frameURL = sender.url;
                            if (
                                this.tabs[tabId][frameId].timestamp <
                                this.timestamp
                            ) {
                                const message = this.getTabMessage(
                                    this.getTabURL(sender.tab),
                                    frameURL
                                );
                                chrome.tabs.sendMessage(tabId, message, {
                                    frameId
                                });
                            }
                            this.tabs[sender.tab.id][sender.frameId] = {
                                url: sender.url,
                                state: DocumentState.ACTIVE,
                                timestamp: this.timestamp
                            };
                            this.stateManager.saveState();
                            break;
                        }
                        case MessageType.CS_FETCH: {
                            const id = message.id;
                            const sendResponse = (response) =>
                                chrome.tabs.sendMessage(sender.tab.id, {
                                    type: MessageType.BG_FETCH_RESPONSE,
                                    id,
                                    ...response
                                });
                            if (isThunderbird) {
                                if (message.data.url.startsWith("chrome://")) {
                                    sendResponse({data: null});
                                    return;
                                }
                            }
                            try {
                                const {url, responseType, mimeType, origin} =
                                    message.data;
                                if (!this.fileLoader) {
                                    this.fileLoader = createFileLoader();
                                }
                                const response = await this.fileLoader.get({
                                    url,
                                    responseType,
                                    mimeType,
                                    origin
                                });
                                sendResponse({data: response});
                            } catch (err) {
                                sendResponse({
                                    error:
                                        err && err.message ? err.message : err
                                });
                            }
                            break;
                        }
                        case MessageType.CS_COLOR_SCHEME_CHANGE: {
                            onColorSchemeChange(message.data);
                            break;
                        }
                        case MessageType.UI_SAVE_FILE: {
                            const {content, name} = message.data;
                            const a = document.createElement("a");
                            a.href = URL.createObjectURL(new Blob([content]));
                            a.download = name;
                            a.click();
                            break;
                        }
                        case MessageType.UI_REQUEST_EXPORT_CSS: {
                            const activeTab = await this.getActiveTab();
                            chrome.tabs.sendMessage(
                                activeTab.id,
                                {type: MessageType.BG_EXPORT_CSS},
                                {frameId: 0}
                            );
                            break;
                        }
                    }
                }
            );
        }
        getTabURL(tab) {
            return tab.url || "about:blank";
        }
        async updateContentScript(options) {
            (await queryTabs({}))
                .filter(
                    (tab) =>
                        options.runOnProtectedPages || canInjectScript(tab.url)
                )
                .filter((tab) => !Boolean(this.tabs[tab.id]))
                .forEach((tab) => {
                    if (!tab.discarded) {
                        if (isMV3) {
                            chrome.scripting.executeScript({
                                target: {
                                    tabId: tab.id,
                                    allFrames: true
                                },
                                files: ["/inject/index.js"]
                            });
                        } else {
                            chrome.tabs.executeScript(tab.id, {
                                runAt: "document_start",
                                file: "/inject/index.js",
                                allFrames: true,
                                matchAboutBlank: true
                            });
                        }
                    }
                });
        }
        async registerMailDisplayScript() {
            await chrome.messageDisplayScripts.register({
                js: [{file: "/inject/fallback.js"}, {file: "/inject/index.js"}]
            });
        }
        async sendMessage() {
            this.timestamp++;
            (await queryTabs({}))
                .filter((tab) => Boolean(this.tabs[tab.id]))
                .forEach((tab) => {
                    const frames = this.tabs[tab.id];
                    Object.entries(frames)
                        .filter(
                            ([, {state}]) =>
                                state === DocumentState.ACTIVE ||
                                state === DocumentState.PASSIVE
                        )
                        .forEach(([, {url}], frameId) => {
                            const message = this.getTabMessage(
                                this.getTabURL(tab),
                                frameId === 0 ? null : url
                            );
                            if (tab.active && frameId === 0) {
                                chrome.tabs.sendMessage(tab.id, message, {
                                    frameId
                                });
                            } else {
                                setTimeout(() =>
                                    chrome.tabs.sendMessage(tab.id, message, {
                                        frameId
                                    })
                                );
                            }
                            if (this.tabs[tab.id][frameId]) {
                                this.tabs[tab.id][frameId].timestamp =
                                    this.timestamp;
                            }
                        });
                });
        }
        async canAccessActiveTab() {
            const tab = await this.getActiveTab();
            return Boolean(this.tabs[tab.id]);
        }
        async getActiveTabURL() {
            return this.getTabURL(await this.getActiveTab());
        }
        async getActiveTab() {
            let tab = (
                await queryTabs({
                    active: true,
                    lastFocusedWindow: true
                })
            )[0];
            const isExtensionPage = (url) =>
                url.startsWith("chrome-extension:") ||
                url.startsWith("moz-extension:");
            if (!tab || isExtensionPage(tab.url)) {
                const tabs = await queryTabs({active: true});
                tab = tabs.find((t) => !isExtensionPage(t.url)) || tab;
            }
            return tab;
        }
    }
    TabManager.LOCAL_STORAGE_KEY = "TabManager-state";

    function debounce(delay, fn) {
        let timeoutId = null;
        return (...args) => {
            if (timeoutId) {
                clearTimeout(timeoutId);
            }
            timeoutId = setTimeout(() => {
                timeoutId = null;
                fn(...args);
            }, delay);
        };
    }

    const SAVE_TIMEOUT = 1000;
    class UserStorage {
        constructor({onRemoteSettingsChange}) {
            this.saveSettingsIntoStorage = debounce(SAVE_TIMEOUT, async () => {
                if (this.saveStorageBarrier) {
                    await this.saveStorageBarrier.entry();
                    return;
                }
                this.saveStorageBarrier = new PromiseBarrier();
                const settings = this.settings;
                if (settings.syncSettings) {
                    try {
                        await writeSyncStorage(settings);
                    } catch (err) {
                        logWarn(
                            "Settings synchronization was disabled due to error:",
                            chrome.runtime.lastError
                        );
                        this.set({syncSettings: false});
                        await this.saveSyncSetting(false);
                        await writeLocalStorage(settings);
                    }
                } else {
                    await writeLocalStorage(settings);
                }
                this.saveStorageBarrier.resolve();
                this.saveStorageBarrier = null;
            });
            this.settings = null;
            subscribeToOuterSettingsChange(async () => {
                await this.loadSettings();
                onRemoteSettingsChange();
            });
        }
        async loadSettings() {
            this.settings = await this.loadSettingsFromStorage();
        }
        fillDefaults(settings) {
            settings.theme = {...DEFAULT_THEME, ...settings.theme};
            settings.time = {...DEFAULT_SETTINGS.time, ...settings.time};
            settings.presets.forEach((preset) => {
                preset.theme = {...DEFAULT_THEME, ...preset.theme};
            });
            settings.customThemes.forEach((site) => {
                site.theme = {...DEFAULT_THEME, ...site.theme};
            });
        }
        async loadSettingsFromStorage() {
            if (this.loadBarrier) {
                return await this.loadBarrier.entry();
            }
            this.loadBarrier = new PromiseBarrier();
            const local = await readLocalStorage(DEFAULT_SETTINGS);
            if (local.syncSettings == null) {
                local.syncSettings = DEFAULT_SETTINGS.syncSettings;
            }
            if (!local.syncSettings) {
                this.fillDefaults(local);
                this.loadBarrier.resolve(local);
                return local;
            }
            const $sync = await readSyncStorage(DEFAULT_SETTINGS);
            if (!$sync) {
                local.syncSettings = false;
                this.set({syncSettings: false});
                this.saveSyncSetting(false);
                this.loadBarrier.resolve(local);
                return local;
            }
            const sync = await readSyncStorage(DEFAULT_SETTINGS);
            this.fillDefaults(sync);
            this.loadBarrier.resolve(sync);
            return sync;
        }
        async saveSettings() {
            await this.saveSettingsIntoStorage();
        }
        async saveSyncSetting(sync) {
            const obj = {syncSettings: sync};
            await writeLocalStorage(obj);
            try {
                await writeSyncStorage(obj);
            } catch (err) {
                logWarn(
                    "Settings synchronization was disabled due to error:",
                    chrome.runtime.lastError
                );
                this.set({syncSettings: false});
            }
        }
        set($settings) {
            if ($settings.siteList) {
                if (!Array.isArray($settings.siteList)) {
                    const list = [];
                    for (const key in $settings.siteList) {
                        const index = Number(key);
                        if (!isNaN(index)) {
                            list[index] = $settings.siteList[key];
                        }
                    }
                    $settings.siteList = list;
                }
                const siteList = $settings.siteList.filter((pattern) => {
                    let isOK = false;
                    try {
                        isURLMatched("https://google.com/", pattern);
                        isURLMatched("[::1]:1337", pattern);
                        isOK = true;
                    } catch (err) {}
                    return isOK && pattern !== "/";
                });
                $settings = {...$settings, siteList};
            }
            this.settings = {...this.settings, ...$settings};
        }
    }

    function hslToRGB({h, s, l, a = 1}) {
        if (s === 0) {
            const [r, b, g] = [l, l, l].map((x) => Math.round(x * 255));
            return {r, g, b, a};
        }
        const c = (1 - Math.abs(2 * l - 1)) * s;
        const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
        const m = l - c / 2;
        const [r, g, b] = (
            h < 60
                ? [c, x, 0]
                : h < 120
                ? [x, c, 0]
                : h < 180
                ? [0, c, x]
                : h < 240
                ? [0, x, c]
                : h < 300
                ? [x, 0, c]
                : [c, 0, x]
        ).map((n) => Math.round((n + m) * 255));
        return {r, g, b, a};
    }
    function rgbToHSL({r: r255, g: g255, b: b255, a = 1}) {
        const r = r255 / 255;
        const g = g255 / 255;
        const b = b255 / 255;
        const max = Math.max(r, g, b);
        const min = Math.min(r, g, b);
        const c = max - min;
        const l = (max + min) / 2;
        if (c === 0) {
            return {h: 0, s: 0, l, a};
        }
        let h =
            (max === r
                ? ((g - b) / c) % 6
                : max === g
                ? (b - r) / c + 2
                : (r - g) / c + 4) * 60;
        if (h < 0) {
            h += 360;
        }
        const s = c / (1 - Math.abs(2 * l - 1));
        return {h, s, l, a};
    }
    function toFixed(n, digits = 0) {
        const fixed = n.toFixed(digits);
        if (digits === 0) {
            return fixed;
        }
        const dot = fixed.indexOf(".");
        if (dot >= 0) {
            const zerosMatch = fixed.match(/0+$/);
            if (zerosMatch) {
                if (zerosMatch.index === dot + 1) {
                    return fixed.substring(0, dot);
                }
                return fixed.substring(0, zerosMatch.index);
            }
        }
        return fixed;
    }
    function rgbToString(rgb) {
        const {r, g, b, a} = rgb;
        if (a != null && a < 1) {
            return `rgba(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)}, ${toFixed(
                a,
                2
            )})`;
        }
        return `rgb(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)})`;
    }
    function rgbToHexString({r, g, b, a}) {
        return `#${(a != null && a < 1
            ? [r, g, b, Math.round(a * 255)]
            : [r, g, b]
        )
            .map((x) => {
                return `${x < 16 ? "0" : ""}${x.toString(16)}`;
            })
            .join("")}`;
    }
    const rgbMatch = /^rgba?\([^\(\)]+\)$/;
    const hslMatch = /^hsla?\([^\(\)]+\)$/;
    const hexMatch = /^#[0-9a-f]+$/i;
    function parse($color) {
        const c = $color.trim().toLowerCase();
        if (c.match(rgbMatch)) {
            return parseRGB(c);
        }
        if (c.match(hslMatch)) {
            return parseHSL(c);
        }
        if (c.match(hexMatch)) {
            return parseHex(c);
        }
        if (knownColors.has(c)) {
            return getColorByName(c);
        }
        if (systemColors.has(c)) {
            return getSystemColor(c);
        }
        if ($color === "transparent") {
            return {r: 0, g: 0, b: 0, a: 0};
        }
        throw new Error(`Unable to parse ${$color}`);
    }
    function getNumbers($color) {
        const numbers = [];
        let prevPos = 0;
        let isMining = false;
        const startIndex = $color.indexOf("(");
        $color = $color.substring(startIndex + 1, $color.length - 1);
        for (let i = 0; i < $color.length; i++) {
            const c = $color[i];
            if ((c >= "0" && c <= "9") || c === "." || c === "+" || c === "-") {
                isMining = true;
            } else if (isMining && (c === " " || c === ",")) {
                numbers.push($color.substring(prevPos, i));
                isMining = false;
                prevPos = i + 1;
            } else if (!isMining) {
                prevPos = i + 1;
            }
        }
        if (isMining) {
            numbers.push($color.substring(prevPos, $color.length));
        }
        return numbers;
    }
    function getNumbersFromString(str, range, units) {
        const raw = getNumbers(str);
        const unitsList = Object.entries(units);
        const numbers = raw
            .map((r) => r.trim())
            .map((r, i) => {
                let n;
                const unit = unitsList.find(([u]) => r.endsWith(u));
                if (unit) {
                    n =
                        (parseFloat(r.substring(0, r.length - unit[0].length)) /
                            unit[1]) *
                        range[i];
                } else {
                    n = parseFloat(r);
                }
                if (range[i] > 1) {
                    return Math.round(n);
                }
                return n;
            });
        return numbers;
    }
    const rgbRange = [255, 255, 255, 1];
    const rgbUnits = {"%": 100};
    function parseRGB($rgb) {
        const [r, g, b, a = 1] = getNumbersFromString($rgb, rgbRange, rgbUnits);
        return {r, g, b, a};
    }
    const hslRange = [360, 1, 1, 1];
    const hslUnits = {"%": 100, "deg": 360, "rad": 2 * Math.PI, "turn": 1};
    function parseHSL($hsl) {
        const [h, s, l, a = 1] = getNumbersFromString($hsl, hslRange, hslUnits);
        return hslToRGB({h, s, l, a});
    }
    function parseHex($hex) {
        const h = $hex.substring(1);
        switch (h.length) {
            case 3:
            case 4: {
                const [r, g, b] = [0, 1, 2].map((i) =>
                    parseInt(`${h[i]}${h[i]}`, 16)
                );
                const a =
                    h.length === 3 ? 1 : parseInt(`${h[3]}${h[3]}`, 16) / 255;
                return {r, g, b, a};
            }
            case 6:
            case 8: {
                const [r, g, b] = [0, 2, 4].map((i) =>
                    parseInt(h.substring(i, i + 2), 16)
                );
                const a =
                    h.length === 6 ? 1 : parseInt(h.substring(6, 8), 16) / 255;
                return {r, g, b, a};
            }
        }
        throw new Error(`Unable to parse ${$hex}`);
    }
    function getColorByName($color) {
        const n = knownColors.get($color);
        return {
            r: (n >> 16) & 255,
            g: (n >> 8) & 255,
            b: (n >> 0) & 255,
            a: 1
        };
    }
    function getSystemColor($color) {
        const n = systemColors.get($color);
        return {
            r: (n >> 16) & 255,
            g: (n >> 8) & 255,
            b: (n >> 0) & 255,
            a: 1
        };
    }
    const knownColors = new Map(
        Object.entries({
            aliceblue: 0xf0f8ff,
            antiquewhite: 0xfaebd7,
            aqua: 0x00ffff,
            aquamarine: 0x7fffd4,
            azure: 0xf0ffff,
            beige: 0xf5f5dc,
            bisque: 0xffe4c4,
            black: 0x000000,
            blanchedalmond: 0xffebcd,
            blue: 0x0000ff,
            blueviolet: 0x8a2be2,
            brown: 0xa52a2a,
            burlywood: 0xdeb887,
            cadetblue: 0x5f9ea0,
            chartreuse: 0x7fff00,
            chocolate: 0xd2691e,
            coral: 0xff7f50,
            cornflowerblue: 0x6495ed,
            cornsilk: 0xfff8dc,
            crimson: 0xdc143c,
            cyan: 0x00ffff,
            darkblue: 0x00008b,
            darkcyan: 0x008b8b,
            darkgoldenrod: 0xb8860b,
            darkgray: 0xa9a9a9,
            darkgrey: 0xa9a9a9,
            darkgreen: 0x006400,
            darkkhaki: 0xbdb76b,
            darkmagenta: 0x8b008b,
            darkolivegreen: 0x556b2f,
            darkorange: 0xff8c00,
            darkorchid: 0x9932cc,
            darkred: 0x8b0000,
            darksalmon: 0xe9967a,
            darkseagreen: 0x8fbc8f,
            darkslateblue: 0x483d8b,
            darkslategray: 0x2f4f4f,
            darkslategrey: 0x2f4f4f,
            darkturquoise: 0x00ced1,
            darkviolet: 0x9400d3,
            deeppink: 0xff1493,
            deepskyblue: 0x00bfff,
            dimgray: 0x696969,
            dimgrey: 0x696969,
            dodgerblue: 0x1e90ff,
            firebrick: 0xb22222,
            floralwhite: 0xfffaf0,
            forestgreen: 0x228b22,
            fuchsia: 0xff00ff,
            gainsboro: 0xdcdcdc,
            ghostwhite: 0xf8f8ff,
            gold: 0xffd700,
            goldenrod: 0xdaa520,
            gray: 0x808080,
            grey: 0x808080,
            green: 0x008000,
            greenyellow: 0xadff2f,
            honeydew: 0xf0fff0,
            hotpink: 0xff69b4,
            indianred: 0xcd5c5c,
            indigo: 0x4b0082,
            ivory: 0xfffff0,
            khaki: 0xf0e68c,
            lavender: 0xe6e6fa,
            lavenderblush: 0xfff0f5,
            lawngreen: 0x7cfc00,
            lemonchiffon: 0xfffacd,
            lightblue: 0xadd8e6,
            lightcoral: 0xf08080,
            lightcyan: 0xe0ffff,
            lightgoldenrodyellow: 0xfafad2,
            lightgray: 0xd3d3d3,
            lightgrey: 0xd3d3d3,
            lightgreen: 0x90ee90,
            lightpink: 0xffb6c1,
            lightsalmon: 0xffa07a,
            lightseagreen: 0x20b2aa,
            lightskyblue: 0x87cefa,
            lightslategray: 0x778899,
            lightslategrey: 0x778899,
            lightsteelblue: 0xb0c4de,
            lightyellow: 0xffffe0,
            lime: 0x00ff00,
            limegreen: 0x32cd32,
            linen: 0xfaf0e6,
            magenta: 0xff00ff,
            maroon: 0x800000,
            mediumaquamarine: 0x66cdaa,
            mediumblue: 0x0000cd,
            mediumorchid: 0xba55d3,
            mediumpurple: 0x9370db,
            mediumseagreen: 0x3cb371,
            mediumslateblue: 0x7b68ee,
            mediumspringgreen: 0x00fa9a,
            mediumturquoise: 0x48d1cc,
            mediumvioletred: 0xc71585,
            midnightblue: 0x191970,
            mintcream: 0xf5fffa,
            mistyrose: 0xffe4e1,
            moccasin: 0xffe4b5,
            navajowhite: 0xffdead,
            navy: 0x000080,
            oldlace: 0xfdf5e6,
            olive: 0x808000,
            olivedrab: 0x6b8e23,
            orange: 0xffa500,
            orangered: 0xff4500,
            orchid: 0xda70d6,
            palegoldenrod: 0xeee8aa,
            palegreen: 0x98fb98,
            paleturquoise: 0xafeeee,
            palevioletred: 0xdb7093,
            papayawhip: 0xffefd5,
            peachpuff: 0xffdab9,
            peru: 0xcd853f,
            pink: 0xffc0cb,
            plum: 0xdda0dd,
            powderblue: 0xb0e0e6,
            purple: 0x800080,
            rebeccapurple: 0x663399,
            red: 0xff0000,
            rosybrown: 0xbc8f8f,
            royalblue: 0x4169e1,
            saddlebrown: 0x8b4513,
            salmon: 0xfa8072,
            sandybrown: 0xf4a460,
            seagreen: 0x2e8b57,
            seashell: 0xfff5ee,
            sienna: 0xa0522d,
            silver: 0xc0c0c0,
            skyblue: 0x87ceeb,
            slateblue: 0x6a5acd,
            slategray: 0x708090,
            slategrey: 0x708090,
            snow: 0xfffafa,
            springgreen: 0x00ff7f,
            steelblue: 0x4682b4,
            tan: 0xd2b48c,
            teal: 0x008080,
            thistle: 0xd8bfd8,
            tomato: 0xff6347,
            turquoise: 0x40e0d0,
            violet: 0xee82ee,
            wheat: 0xf5deb3,
            white: 0xffffff,
            whitesmoke: 0xf5f5f5,
            yellow: 0xffff00,
            yellowgreen: 0x9acd32
        })
    );
    const systemColors = new Map(
        Object.entries({
            "ActiveBorder": 0x3b99fc,
            "ActiveCaption": 0x000000,
            "AppWorkspace": 0xaaaaaa,
            "Background": 0x6363ce,
            "ButtonFace": 0xffffff,
            "ButtonHighlight": 0xe9e9e9,
            "ButtonShadow": 0x9fa09f,
            "ButtonText": 0x000000,
            "CaptionText": 0x000000,
            "GrayText": 0x7f7f7f,
            "Highlight": 0xb2d7ff,
            "HighlightText": 0x000000,
            "InactiveBorder": 0xffffff,
            "InactiveCaption": 0xffffff,
            "InactiveCaptionText": 0x000000,
            "InfoBackground": 0xfbfcc5,
            "InfoText": 0x000000,
            "Menu": 0xf6f6f6,
            "MenuText": 0xffffff,
            "Scrollbar": 0xaaaaaa,
            "ThreeDDarkShadow": 0x000000,
            "ThreeDFace": 0xc0c0c0,
            "ThreeDHighlight": 0xffffff,
            "ThreeDLightShadow": 0xffffff,
            "ThreeDShadow": 0x000000,
            "Window": 0xececec,
            "WindowFrame": 0xaaaaaa,
            "WindowText": 0x000000,
            "-webkit-focus-ring-color": 0xe59700
        }).map(([key, value]) => [key.toLowerCase(), value])
    );

    function getBgPole(theme) {
        const isDarkScheme = theme.mode === 1;
        const prop = isDarkScheme
            ? "darkSchemeBackgroundColor"
            : "lightSchemeBackgroundColor";
        return theme[prop];
    }
    function getFgPole(theme) {
        const isDarkScheme = theme.mode === 1;
        const prop = isDarkScheme
            ? "darkSchemeTextColor"
            : "lightSchemeTextColor";
        return theme[prop];
    }
    const colorModificationCache = new Map();
    const colorParseCache = new Map();
    function parseToHSLWithCache(color) {
        if (colorParseCache.has(color)) {
            return colorParseCache.get(color);
        }
        const rgb = parse(color);
        const hsl = rgbToHSL(rgb);
        colorParseCache.set(color, hsl);
        return hsl;
    }
    const rgbCacheKeys = ["r", "g", "b", "a"];
    const themeCacheKeys = [
        "mode",
        "brightness",
        "contrast",
        "grayscale",
        "sepia",
        "darkSchemeBackgroundColor",
        "darkSchemeTextColor",
        "lightSchemeBackgroundColor",
        "lightSchemeTextColor"
    ];
    function getCacheId(rgb, theme) {
        let resultId = "";
        rgbCacheKeys.forEach((key) => {
            resultId += `${rgb[key]};`;
        });
        themeCacheKeys.forEach((key) => {
            resultId += `${theme[key]};`;
        });
        return resultId;
    }
    function modifyColorWithCache(
        rgb,
        theme,
        modifyHSL,
        poleColor,
        anotherPoleColor
    ) {
        let fnCache;
        if (colorModificationCache.has(modifyHSL)) {
            fnCache = colorModificationCache.get(modifyHSL);
        } else {
            fnCache = new Map();
            colorModificationCache.set(modifyHSL, fnCache);
        }
        const id = getCacheId(rgb, theme);
        if (fnCache.has(id)) {
            return fnCache.get(id);
        }
        const hsl = rgbToHSL(rgb);
        const pole = poleColor == null ? null : parseToHSLWithCache(poleColor);
        const anotherPole =
            anotherPoleColor == null
                ? null
                : parseToHSLWithCache(anotherPoleColor);
        const modified = modifyHSL(hsl, pole, anotherPole);
        const {r, g, b, a} = hslToRGB(modified);
        const matrix = createFilterMatrix(theme);
        const [rf, gf, bf] = applyColorMatrix([r, g, b], matrix);
        const color =
            a === 1
                ? rgbToHexString({r: rf, g: gf, b: bf})
                : rgbToString({r: rf, g: gf, b: bf, a});
        fnCache.set(id, color);
        return color;
    }
    function modifyLightSchemeColor(rgb, theme) {
        const poleBg = getBgPole(theme);
        const poleFg = getFgPole(theme);
        return modifyColorWithCache(
            rgb,
            theme,
            modifyLightModeHSL,
            poleFg,
            poleBg
        );
    }
    function modifyLightModeHSL({h, s, l, a}, poleFg, poleBg) {
        const isDark = l < 0.5;
        let isNeutral;
        if (isDark) {
            isNeutral = l < 0.2 || s < 0.12;
        } else {
            const isBlue = h > 200 && h < 280;
            isNeutral = s < 0.24 || (l > 0.8 && isBlue);
        }
        let hx = h;
        let sx = l;
        if (isNeutral) {
            if (isDark) {
                hx = poleFg.h;
                sx = poleFg.s;
            } else {
                hx = poleBg.h;
                sx = poleBg.s;
            }
        }
        const lx = scale(l, 0, 1, poleFg.l, poleBg.l);
        return {h: hx, s: sx, l: lx, a};
    }
    const MAX_BG_LIGHTNESS = 0.4;
    function modifyBgHSL({h, s, l, a}, pole) {
        const isDark = l < 0.5;
        const isBlue = h > 200 && h < 280;
        const isNeutral = s < 0.12 || (l > 0.8 && isBlue);
        if (isDark) {
            const lx = scale(l, 0, 0.5, 0, MAX_BG_LIGHTNESS);
            if (isNeutral) {
                const hx = pole.h;
                const sx = pole.s;
                return {h: hx, s: sx, l: lx, a};
            }
            return {h, s, l: lx, a};
        }
        const lx = scale(l, 0.5, 1, MAX_BG_LIGHTNESS, pole.l);
        if (isNeutral) {
            const hx = pole.h;
            const sx = pole.s;
            return {h: hx, s: sx, l: lx, a};
        }
        let hx = h;
        const isYellow = h > 60 && h < 180;
        if (isYellow) {
            const isCloserToGreen = h > 120;
            if (isCloserToGreen) {
                hx = scale(h, 120, 180, 135, 180);
            } else {
                hx = scale(h, 60, 120, 60, 105);
            }
        }
        return {h: hx, s, l: lx, a};
    }
    function modifyBackgroundColor(rgb, theme) {
        if (theme.mode === 0) {
            return modifyLightSchemeColor(rgb, theme);
        }
        const pole = getBgPole(theme);
        return modifyColorWithCache(
            rgb,
            {...theme, mode: 0},
            modifyBgHSL,
            pole
        );
    }
    const MIN_FG_LIGHTNESS = 0.55;
    function modifyBlueFgHue(hue) {
        return scale(hue, 205, 245, 205, 220);
    }
    function modifyFgHSL({h, s, l, a}, pole) {
        const isLight = l > 0.5;
        const isNeutral = l < 0.2 || s < 0.24;
        const isBlue = !isNeutral && h > 205 && h < 245;
        if (isLight) {
            const lx = scale(l, 0.5, 1, MIN_FG_LIGHTNESS, pole.l);
            if (isNeutral) {
                const hx = pole.h;
                const sx = pole.s;
                return {h: hx, s: sx, l: lx, a};
            }
            let hx = h;
            if (isBlue) {
                hx = modifyBlueFgHue(h);
            }
            return {h: hx, s, l: lx, a};
        }
        if (isNeutral) {
            const hx = pole.h;
            const sx = pole.s;
            const lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS);
            return {h: hx, s: sx, l: lx, a};
        }
        let hx = h;
        let lx;
        if (isBlue) {
            hx = modifyBlueFgHue(h);
            lx = scale(l, 0, 0.5, pole.l, Math.min(1, MIN_FG_LIGHTNESS + 0.05));
        } else {
            lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS);
        }
        return {h: hx, s, l: lx, a};
    }
    function modifyForegroundColor(rgb, theme) {
        if (theme.mode === 0) {
            return modifyLightSchemeColor(rgb, theme);
        }
        const pole = getFgPole(theme);
        return modifyColorWithCache(
            rgb,
            {...theme, mode: 0},
            modifyFgHSL,
            pole
        );
    }
    function modifyBorderHSL({h, s, l, a}, poleFg, poleBg) {
        const isDark = l < 0.5;
        const isNeutral = l < 0.2 || s < 0.24;
        let hx = h;
        let sx = s;
        if (isNeutral) {
            if (isDark) {
                hx = poleFg.h;
                sx = poleFg.s;
            } else {
                hx = poleBg.h;
                sx = poleBg.s;
            }
        }
        const lx = scale(l, 0, 1, 0.5, 0.2);
        return {h: hx, s: sx, l: lx, a};
    }
    function modifyBorderColor(rgb, theme) {
        if (theme.mode === 0) {
            return modifyLightSchemeColor(rgb, theme);
        }
        const poleFg = getFgPole(theme);
        const poleBg = getBgPole(theme);
        return modifyColorWithCache(
            rgb,
            {...theme, mode: 0},
            modifyBorderHSL,
            poleFg,
            poleBg
        );
    }

    const themeColorTypes = {
        accentcolor: "bg",
        button_background_active: "text",
        button_background_hover: "text",
        frame: "bg",
        icons: "text",
        icons_attention: "text",
        ntp_background: "bg",
        ntp_text: "text",
        popup: "bg",
        popup_border: "bg",
        popup_highlight: "bg",
        popup_highlight_text: "text",
        popup_text: "text",
        sidebar: "bg",
        sidebar_border: "border",
        sidebar_text: "text",
        tab_background_text: "text",
        tab_line: "bg",
        tab_loading: "bg",
        tab_selected: "bg",
        textcolor: "text",
        toolbar: "bg",
        toolbar_bottom_separator: "border",
        toolbar_field: "bg",
        toolbar_field_border: "border",
        toolbar_field_border_focus: "border",
        toolbar_field_focus: "bg",
        toolbar_field_separator: "border",
        toolbar_field_text: "text",
        toolbar_field_text_focus: "text",
        toolbar_text: "text",
        toolbar_top_separator: "border",
        toolbar_vertical_separator: "border"
    };
    const $colors = {
        accentcolor: "#111111",
        frame: "#111111",
        ntp_background: "white",
        ntp_text: "black",
        popup: "#cccccc",
        popup_text: "black",
        sidebar: "#cccccc",
        sidebar_border: "#333",
        sidebar_text: "black",
        tab_background_text: "white",
        tab_loading: "#23aeff",
        textcolor: "white",
        toolbar: "#707070",
        toolbar_field: "lightgray",
        toolbar_field_text: "black"
    };
    function setWindowTheme(filter) {
        const colors = Object.entries($colors).reduce((obj, [key, value]) => {
            const type = themeColorTypes[key];
            const modify = {
                bg: modifyBackgroundColor,
                text: modifyForegroundColor,
                border: modifyBorderColor
            }[type];
            const rgb = parse(value);
            const modified = modify(rgb, filter);
            obj[key] = modified;
            return obj;
        }, {});
        if (
            typeof browser !== "undefined" &&
            browser.theme &&
            browser.theme.update
        ) {
            browser.theme.update({colors});
        }
    }
    function resetWindowTheme() {
        if (
            typeof browser !== "undefined" &&
            browser.theme &&
            browser.theme.reset
        ) {
            browser.theme.reset();
        }
    }

    function createSVGFilterStylesheet(config, url, frameURL, fixes, index) {
        let filterValue;
        let reverseFilterValue;
        if (isFirefox) {
            filterValue = getEmbeddedSVGFilterValue(
                getSVGFilterMatrixValue(config)
            );
            reverseFilterValue = getEmbeddedSVGFilterValue(
                getSVGReverseFilterMatrixValue()
            );
        } else {
            filterValue = "url(#dark-reader-filter)";
            reverseFilterValue = "url(#dark-reader-reverse-filter)";
        }
        return cssFilterStyleSheetTemplate(
            filterValue,
            reverseFilterValue,
            config,
            url,
            frameURL,
            fixes,
            index
        );
    }
    function getEmbeddedSVGFilterValue(matrixValue) {
        const id = "dark-reader-filter";
        const svg = [
            '<svg xmlns="http://www.w3.org/2000/svg">',
            `<filter id="${id}" style="color-interpolation-filters: sRGB;">`,
            `<feColorMatrix type="matrix" values="${matrixValue}" />`,
            "</filter>",
            "</svg>"
        ].join("");
        return `url(data:image/svg+xml;base64,${btoa(svg)}#${id})`;
    }
    function toSVGMatrix(matrix) {
        return matrix
            .slice(0, 4)
            .map((m) => m.map((m) => m.toFixed(3)).join(" "))
            .join(" ");
    }
    function getSVGFilterMatrixValue(config) {
        return toSVGMatrix(createFilterMatrix(config));
    }
    function getSVGReverseFilterMatrixValue() {
        return toSVGMatrix(Matrix.invertNHue());
    }

    const matchesMediaQuery = (query) => {
        if ("window" in globalThis) {
            return Boolean(window.matchMedia(query).matches);
        }
        return false;
    };
    const matchesDarkTheme = () =>
        matchesMediaQuery("(prefers-color-scheme: dark)");
    const matchesLightTheme = () =>
        matchesMediaQuery("(prefers-color-scheme: light)");
    const isColorSchemeSupported = matchesDarkTheme() || matchesLightTheme();
    function isSystemDarkModeEnabled() {
        if (!isColorSchemeSupported) {
            return false;
        }
        return matchesDarkTheme();
    }

    class Extension {
        constructor() {
            this.isEnabled = null;
            this.wasEnabledOnLastCheck = null;
            this.registeredContextMenus = null;
            this.popupOpeningListener = null;
            this.wasLastColorSchemeDark = null;
            this.startBarrier = null;
            this.stateManager = null;
            this.alarmListener = (alarm) => {
                if (alarm.name === Extension.ALARM_NAME) {
                    this.handleAutoCheck();
                }
            };
            this.onCommand = async (command, frameURL) => {
                if (this.startBarrier.isPending()) {
                    await this.startBarrier.entry();
                }
                this.stateManager.loadState();
                switch (command) {
                    case "toggle":
                        this.changeSettings({
                            enabled: !this.isEnabled,
                            automation: ""
                        });
                        break;
                    case "addSite":
                        const url =
                            frameURL || (await this.tabs.getActiveTabURL());
                        if (isPDF(url)) {
                            this.changeSettings({
                                enableForPDF: !this.user.settings.enableForPDF
                            });
                        } else {
                            this.toggleURL(url);
                        }
                        break;
                    case "switchEngine": {
                        const engines = Object.values(ThemeEngines);
                        const index = engines.indexOf(
                            this.user.settings.theme.engine
                        );
                        const next = engines[(index + 1) % engines.length];
                        this.setTheme({engine: next});
                        break;
                    }
                }
            };
            this.onColorSchemeChange = ({isDark}) => {
                if (isFirefox) {
                    this.wasLastColorSchemeDark = isDark;
                }
                if (this.user.settings.automation !== "system") {
                    return;
                }
                this.handleAutoCheck();
            };
            this.getTabMessage = (url, frameURL) => {
                const urlInfo = this.getURLInfo(url);
                if (
                    this.isEnabled &&
                    isURLEnabled(url, this.user.settings, urlInfo)
                ) {
                    const custom = this.user.settings.customThemes.find(
                        ({url: urlList}) => isURLInList(url, urlList)
                    );
                    const preset = custom
                        ? null
                        : this.user.settings.presets.find(({urls}) =>
                              isURLInList(url, urls)
                          );
                    const theme = custom
                        ? custom.theme
                        : preset
                        ? preset.theme
                        : this.user.settings.theme;
                    logInfo(`Custom theme ${
                        custom ? "was found" : "was not found"
                    }, Preset theme ${preset ? "was found" : "was not found"}
            The theme(${
                custom ? "custom" : preset ? "preset" : "global"
            } settings) used is: ${JSON.stringify(theme)}`);
                    switch (theme.engine) {
                        case ThemeEngines.cssFilter: {
                            return {
                                type: MessageType.BG_ADD_CSS_FILTER,
                                data: createCSSFilterStyleSheet(
                                    theme,
                                    url,
                                    frameURL,
                                    this.config.INVERSION_FIXES_RAW,
                                    this.config.INVERSION_FIXES_INDEX
                                )
                            };
                        }
                        case ThemeEngines.svgFilter: {
                            if (isFirefox) {
                                return {
                                    type: MessageType.BG_ADD_CSS_FILTER,
                                    data: createSVGFilterStylesheet(
                                        theme,
                                        url,
                                        frameURL,
                                        this.config.INVERSION_FIXES_RAW,
                                        this.config.INVERSION_FIXES_INDEX
                                    )
                                };
                            }
                            return {
                                type: MessageType.BG_ADD_SVG_FILTER,
                                data: {
                                    css: createSVGFilterStylesheet(
                                        theme,
                                        url,
                                        frameURL,
                                        this.config.INVERSION_FIXES_RAW,
                                        this.config.INVERSION_FIXES_INDEX
                                    ),
                                    svgMatrix: getSVGFilterMatrixValue(theme),
                                    svgReverseMatrix:
                                        getSVGReverseFilterMatrixValue()
                                }
                            };
                        }
                        case ThemeEngines.staticTheme: {
                            return {
                                type: MessageType.BG_ADD_STATIC_THEME,
                                data:
                                    theme.stylesheet && theme.stylesheet.trim()
                                        ? theme.stylesheet
                                        : createStaticStylesheet(
                                              theme,
                                              url,
                                              frameURL,
                                              this.config.STATIC_THEMES_RAW,
                                              this.config.STATIC_THEMES_INDEX
                                          )
                            };
                        }
                        case ThemeEngines.dynamicTheme: {
                            const filter = {...theme};
                            delete filter.engine;
                            const fixes = getDynamicThemeFixesFor(
                                url,
                                frameURL,
                                this.config.DYNAMIC_THEME_FIXES_RAW,
                                this.config.DYNAMIC_THEME_FIXES_INDEX,
                                this.user.settings.enableForPDF
                            );
                            const isIFrame = frameURL != null;
                            return {
                                type: MessageType.BG_ADD_DYNAMIC_THEME,
                                data: {filter, fixes, isIFrame}
                            };
                        }
                        default: {
                            throw new Error(`Unknown engine ${theme.engine}`);
                        }
                    }
                }
                return {
                    type: MessageType.BG_CLEAN_UP
                };
            };
            this.config = new ConfigManager();
            this.devtools = new DevTools(this.config, async () =>
                this.onSettingsChanged()
            );
            this.messenger = new Messenger(this.getMessengerAdapter());
            this.news = new Newsmaker((news) => this.onNewsUpdate(news));
            this.tabs = new TabManager({
                getConnectionMessage: ({url, frameURL}) =>
                    this.getConnectionMessage(url, frameURL),
                getTabMessage: this.getTabMessage,
                onColorSchemeChange: this.onColorSchemeChange
            });
            this.user = new UserStorage({
                onRemoteSettingsChange: () => this.onRemoteSettingsChange()
            });
            this.startBarrier = new PromiseBarrier();
            this.stateManager = new StateManager(
                Extension.LOCAL_STORAGE_KEY,
                this,
                {
                    isEnabled: null,
                    wasEnabledOnLastCheck: null,
                    registeredContextMenus: null
                }
            );
            chrome.alarms.onAlarm.addListener(this.alarmListener);
            if (chrome.permissions.onRemoved) {
                chrome.permissions.onRemoved.addListener((permissions) => {
                    if (!permissions.permissions.includes("contextMenus")) {
                        this.registeredContextMenus = false;
                    }
                });
            }
        }
        recalculateIsEnabled() {
            if (!this.user.settings) {
                return false;
            }
            const {automation} = this.user.settings;
            let nextCheck;
            switch (automation) {
                case "time":
                    this.isEnabled = isInTimeIntervalLocal(
                        this.user.settings.time.activation,
                        this.user.settings.time.deactivation
                    );
                    nextCheck = nextTimeInterval(
                        this.user.settings.time.activation,
                        this.user.settings.time.deactivation
                    );
                    break;
                case "system":
                    if (isMV3) {
                        this.isEnabled = true;
                        break;
                    }
                    if (isFirefox) {
                        this.isEnabled =
                            this.wasLastColorSchemeDark == null
                                ? isSystemDarkModeEnabled()
                                : this.wasLastColorSchemeDark;
                    } else {
                        this.isEnabled = isSystemDarkModeEnabled();
                    }
                    break;
                case "location": {
                    const {latitude, longitude} = this.user.settings.location;
                    if (latitude != null && longitude != null) {
                        this.isEnabled = isNightAtLocation(latitude, longitude);
                        nextCheck = nextTimeChangeAtLocation(
                            latitude,
                            longitude
                        );
                    }
                    break;
                }
                default:
                    this.isEnabled = this.user.settings.enabled;
                    break;
            }
            if (nextCheck) {
                chrome.alarms.create(Extension.ALARM_NAME, {when: nextCheck});
            }
            return this.isEnabled;
        }
        async start() {
            await this.config.load({local: true});
            await this.user.loadSettings();
            if (
                this.user.settings.enableContextMenus &&
                !this.registeredContextMenus
            ) {
                chrome.permissions.contains(
                    {permissions: ["contextMenus"]},
                    (permitted) => {
                        if (permitted) {
                            this.registerContextMenus();
                        }
                    }
                );
            }
            if (this.user.settings.syncSitesFixes) {
                await this.config.load({local: false});
            }
            this.onAppToggle();
            logInfo("loaded", this.user.settings);
            if (isThunderbird) {
                this.tabs.registerMailDisplayScript();
            } else {
                this.tabs.updateContentScript({
                    runOnProtectedPages:
                        this.user.settings.enableForProtectedPages
                });
            }
            this.user.settings.fetchNews && this.news.subscribe();
            this.startBarrier.resolve();
        }
        getMessengerAdapter() {
            return {
                collect: async () => {
                    return await this.collectData();
                },
                getActiveTabInfo: async () => {
                    if (!this.user.settings) {
                        await this.user.loadSettings();
                    }
                    await this.stateManager.loadState();
                    const url = await this.tabs.getActiveTabURL();
                    const info = this.getURLInfo(url);
                    info.isInjected = await this.tabs.canAccessActiveTab();
                    return info;
                },
                changeSettings: (settings) => this.changeSettings(settings),
                setTheme: (theme) => this.setTheme(theme),
                setShortcut: ({command, shortcut}) =>
                    this.setShortcut(command, shortcut),
                toggleURL: (url) => this.toggleURL(url),
                markNewsAsRead: async (ids) =>
                    await this.news.markAsRead(...ids),
                onPopupOpen: () =>
                    this.popupOpeningListener && this.popupOpeningListener(),
                loadConfig: async (options) => await this.config.load(options),
                applyDevDynamicThemeFixes: (text) =>
                    this.devtools.applyDynamicThemeFixes(text),
                resetDevDynamicThemeFixes: () =>
                    this.devtools.resetDynamicThemeFixes(),
                applyDevInversionFixes: (text) =>
                    this.devtools.applyInversionFixes(text),
                resetDevInversionFixes: () =>
                    this.devtools.resetInversionFixes(),
                applyDevStaticThemes: (text) =>
                    this.devtools.applyStaticThemes(text),
                resetDevStaticThemes: () => this.devtools.resetStaticThemes()
            };
        }
        registerContextMenus() {
            const onCommandToggle = async () => this.onCommand("toggle");
            const onCommandAddSite = async (data) =>
                this.onCommand("addSite", data.frameUrl);
            const onCommandSwitchEngine = async () =>
                this.onCommand("switchEngine");
            chrome.contextMenus.removeAll(() => {
                this.registeredContextMenus = false;
                chrome.contextMenus.create(
                    {
                        id: "DarkReader-top",
                        title: "Dark Reader"
                    },
                    () => {
                        if (chrome.runtime.lastError) {
                            return;
                        }
                        const msgToggle =
                            chrome.i18n.getMessage("toggle_extension");
                        const msgAddSite = chrome.i18n.getMessage(
                            "toggle_current_site"
                        );
                        const msgSwitchEngine = chrome.i18n.getMessage(
                            "theme_generation_mode"
                        );
                        chrome.contextMenus.create({
                            id: "DarkReader-toggle",
                            parentId: "DarkReader-top",
                            title: msgToggle || "Toggle everywhere",
                            onclick: onCommandToggle
                        });
                        chrome.contextMenus.create({
                            id: "DarkReader-addSite",
                            parentId: "DarkReader-top",
                            title: msgAddSite || "Toggle for current site",
                            onclick: onCommandAddSite
                        });
                        chrome.contextMenus.create({
                            id: "DarkReader-switchEngine",
                            parentId: "DarkReader-top",
                            title: msgSwitchEngine || "Switch engine",
                            onclick: onCommandSwitchEngine
                        });
                        this.registeredContextMenus = true;
                    }
                );
            });
        }
        async getShortcuts() {
            const commands = await getCommands();
            return commands.reduce(
                (map, cmd) => Object.assign(map, {[cmd.name]: cmd.shortcut}),
                {}
            );
        }
        setShortcut(command, shortcut) {
            setShortcut(command, shortcut);
        }
        async collectData() {
            if (!this.user.settings) {
                await this.user.loadSettings();
            }
            await this.stateManager.loadState();
            return {
                isEnabled: this.isEnabled,
                isReady: true,
                settings: this.user.settings,
                news: await this.news.getLatest(),
                shortcuts: await this.getShortcuts(),
                colorScheme: this.config.COLOR_SCHEMES_RAW,
                devtools: {
                    dynamicFixesText:
                        await this.devtools.getDynamicThemeFixesText(),
                    filterFixesText:
                        await this.devtools.getInversionFixesText(),
                    staticThemesText: await this.devtools.getStaticThemesText(),
                    hasCustomDynamicFixes:
                        await this.devtools.hasCustomDynamicThemeFixes(),
                    hasCustomFilterFixes:
                        await this.devtools.hasCustomFilterFixes(),
                    hasCustomStaticFixes:
                        await this.devtools.hasCustomStaticFixes()
                }
            };
        }
        onNewsUpdate(news) {
            if (!this.icon) {
                this.icon = new IconManager();
            }
            const latestNews = news.length > 0 && news[0];
            if (latestNews && latestNews.important && !latestNews.read) {
                this.icon.showImportantBadge();
                return;
            }
            this.icon.hideBadge();
        }
        getConnectionMessage(url, frameURL) {
            if (this.user.settings) {
                return this.getTabMessage(url, frameURL);
            }
            return new Promise((resolve) => {
                this.user
                    .loadSettings()
                    .then(() => resolve(this.getTabMessage(url, frameURL)));
            });
        }
        async handleAutoCheck() {
            if (!this.user.settings) {
                await this.user.loadSettings();
            }
            await this.stateManager.loadState();
            this.recalculateIsEnabled();
            const isEnabled = this.isEnabled;
            if (
                this.wasEnabledOnLastCheck === null ||
                this.wasEnabledOnLastCheck !== isEnabled
            ) {
                this.wasEnabledOnLastCheck = isEnabled;
                this.onAppToggle();
                this.tabs.sendMessage();
                this.reportChanges();
                this.stateManager.saveState();
            }
        }
        changeSettings($settings) {
            const prev = {...this.user.settings};
            this.user.set($settings);
            if (
                prev.enabled !== this.user.settings.enabled ||
                prev.automation !== this.user.settings.automation ||
                prev.time.activation !== this.user.settings.time.activation ||
                prev.time.deactivation !==
                    this.user.settings.time.deactivation ||
                prev.location.latitude !==
                    this.user.settings.location.latitude ||
                prev.location.longitude !==
                    this.user.settings.location.longitude
            ) {
                this.onAppToggle();
            }
            if (prev.syncSettings !== this.user.settings.syncSettings) {
                this.user.saveSyncSetting(this.user.settings.syncSettings);
            }
            if (
                this.isEnabled &&
                $settings.changeBrowserTheme != null &&
                prev.changeBrowserTheme !== $settings.changeBrowserTheme
            ) {
                if ($settings.changeBrowserTheme) {
                    setWindowTheme(this.user.settings.theme);
                } else {
                    resetWindowTheme();
                }
            }
            if (prev.fetchNews !== this.user.settings.fetchNews) {
                this.user.settings.fetchNews
                    ? this.news.subscribe()
                    : this.news.unSubscribe();
            }
            if (
                prev.enableContextMenus !==
                this.user.settings.enableContextMenus
            ) {
                if (this.user.settings.enableContextMenus) {
                    this.registerContextMenus();
                } else {
                    chrome.contextMenus.removeAll();
                }
            }
            this.onSettingsChanged();
        }
        setTheme($theme) {
            this.user.set({theme: {...this.user.settings.theme, ...$theme}});
            if (this.isEnabled && this.user.settings.changeBrowserTheme) {
                setWindowTheme(this.user.settings.theme);
            }
            this.onSettingsChanged();
        }
        async reportChanges() {
            const info = await this.collectData();
            this.messenger.reportChanges(info);
        }
        toggleURL(url) {
            const isInDarkList = isURLInList(url, this.config.DARK_SITES);
            const siteList = isInDarkList
                ? this.user.settings.siteListEnabled.slice()
                : this.user.settings.siteList.slice();
            const pattern = getURLHostOrProtocol(url);
            const index = siteList.indexOf(pattern);
            if (index < 0) {
                siteList.push(pattern);
            } else {
                siteList.splice(index, 1);
            }
            if (isInDarkList) {
                this.changeSettings({siteListEnabled: siteList});
            } else {
                this.changeSettings({siteList});
            }
        }
        async toggleCurrentSite() {
            const url = await this.tabs.getActiveTabURL();
            this.toggleURL(url);
        }
        onAppToggle() {
            if (!this.icon) {
                this.icon = new IconManager();
            }
            this.recalculateIsEnabled();
            if (this.isEnabled) {
                this.icon.setActive();
                if (this.user.settings.changeBrowserTheme) {
                    setWindowTheme(this.user.settings.theme);
                }
            } else {
                this.icon.setInactive();
                if (this.user.settings.changeBrowserTheme) {
                    resetWindowTheme();
                }
            }
        }
        async onSettingsChanged() {
            if (!this.user.settings) {
                await this.user.loadSettings();
            }
            await this.stateManager.loadState();
            this.wasEnabledOnLastCheck = this.isEnabled;
            this.tabs.sendMessage();
            this.saveUserSettings();
            this.reportChanges();
            this.stateManager.saveState();
        }
        onRemoteSettingsChange() {}
        getURLInfo(url) {
            const {DARK_SITES} = this.config;
            const isInDarkList = isURLInList(url, DARK_SITES);
            const isProtected = !canInjectScript(url);
            return {
                url,
                isInDarkList,
                isProtected,
                isInjected: null
            };
        }
        async saveUserSettings() {
            await this.user.saveSettings();
            logInfo("saved", this.user.settings);
        }
    }
    Extension.ALARM_NAME = "auto-time-alarm";
    Extension.LOCAL_STORAGE_KEY = "Extension-state";

    function makeChromiumHappy() {
        chrome.runtime.onMessage.addListener((message, _, sendResponse) => {
            if (
                ![
                    MessageType.CS_FRAME_CONNECT,
                    MessageType.UI_GET_DATA,
                    MessageType.UI_GET_ACTIVE_TAB_INFO,
                    MessageType.UI_APPLY_DEV_DYNAMIC_THEME_FIXES,
                    MessageType.UI_APPLY_DEV_INVERSION_FIXES,
                    MessageType.UI_APPLY_DEV_STATIC_THEMES
                ].includes(message.type)
            ) {
                sendResponse({type: "¯\\_(ツ)_/¯"});
            }
        });
    }

    const extension = new Extension();
    extension.start();
    if (chrome.commands) {
        chrome.commands.onCommand.addListener(async (command) =>
            extension.onCommand(command)
        );
    }
    const welcome = `  /''''\\
 (0)==(0)
/__||||__\\
Welcome to Dark Reader!`;
    console.log(welcome);
    {
        chrome.runtime.onInstalled.addListener(({reason}) => {
            if (reason === "install") {
                chrome.tabs.create({url: getHelpURL()});
            }
        });
        chrome.runtime.setUninstallURL(UNINSTALL_URL);
    }
    makeChromiumHappy();
})();
