darkreader 4.9.79 → 4.9.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/darkreader.js +377 -143
  2. package/package.json +11 -13
package/darkreader.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dark Reader v4.9.79
2
+ * Dark Reader v4.9.80
3
3
  * https://darkreader.org/
4
4
  */
5
5
 
@@ -936,7 +936,7 @@
936
936
  });
937
937
  }
938
938
  const cssCommentsRegex = /\/\*[\s\S]*?\*\//g;
939
- function removeCSSComments($css) {
939
+ function removeCSSComments$1($css) {
940
940
  return $css.replace(cssCommentsRegex, "");
941
941
  }
942
942
  const fontFaceRegex = /@font-face\s*{[^}]*}/g;
@@ -1014,51 +1014,29 @@
1014
1014
  }
1015
1015
  return matches;
1016
1016
  }
1017
- function formatCSS(text) {
1018
- function trimLeft(text) {
1019
- return text.replace(/^\s+/, "");
1020
- }
1021
- function getIndent(depth) {
1022
- if (depth === 0) {
1023
- return "";
1024
- }
1025
- return " ".repeat(4 * depth);
1026
- }
1027
- if (text.length < 50000) {
1028
- const emptyRuleRegexp = /[^{}]+{\s*}/;
1029
- while (emptyRuleRegexp.test(text)) {
1030
- text = text.replace(emptyRuleRegexp, "");
1031
- }
1032
- }
1033
- const css = text
1034
- .replace(/\s{2,}/g, " ")
1035
- .replace(/\{/g, "{\n")
1036
- .replace(/\}/g, "\n}\n")
1037
- .replace(/\;(?![^\(|\"]*(\)|\"))/g, ";\n")
1038
- .replace(/\,(?![^\(|\"]*(\)|\"))/g, ",\n")
1039
- .replace(/\n\s*\n/g, "\n")
1040
- .split("\n");
1041
- let depth = 0;
1042
- const formatted = [];
1043
- for (let x = 0, len = css.length; x < len; x++) {
1044
- const line = `${css[x]}\n`;
1045
- if (line.includes("{")) {
1046
- formatted.push(getIndent(depth++) + trimLeft(line));
1047
- } else if (line.includes("}")) {
1048
- formatted.push(getIndent(--depth) + trimLeft(line));
1049
- } else {
1050
- formatted.push(getIndent(depth) + trimLeft(line));
1051
- }
1052
- }
1053
- return formatted.join("").trim();
1054
- }
1055
1017
  function getParenthesesRange(input, searchStartIndex = 0) {
1056
- const length = input.length;
1018
+ return getOpenCloseRange(input, searchStartIndex, "(", ")", []);
1019
+ }
1020
+ function getOpenCloseRange(
1021
+ input,
1022
+ searchStartIndex,
1023
+ openToken,
1024
+ closeToken,
1025
+ excludeRanges
1026
+ ) {
1027
+ let indexOf;
1028
+ if (excludeRanges.length === 0) {
1029
+ indexOf = (token, pos) => input.indexOf(token, pos);
1030
+ } else {
1031
+ indexOf = (token, pos) =>
1032
+ indexOfExcluding(input, token, pos, excludeRanges);
1033
+ }
1034
+ const {length} = input;
1057
1035
  let depth = 0;
1058
1036
  let firstOpenIndex = -1;
1059
1037
  for (let i = searchStartIndex; i < length; i++) {
1060
1038
  if (depth === 0) {
1061
- const openIndex = input.indexOf("(", i);
1039
+ const openIndex = indexOf(openToken, i);
1062
1040
  if (openIndex < 0) {
1063
1041
  break;
1064
1042
  }
@@ -1066,17 +1044,17 @@
1066
1044
  depth++;
1067
1045
  i = openIndex;
1068
1046
  } else {
1069
- const closingIndex = input.indexOf(")", i);
1070
- if (closingIndex < 0) {
1047
+ const closeIndex = indexOf(closeToken, i);
1048
+ if (closeIndex < 0) {
1071
1049
  break;
1072
1050
  }
1073
- const openIndex = input.indexOf("(", i);
1074
- if (openIndex < 0 || closingIndex < openIndex) {
1051
+ const openIndex = indexOf(openToken, i);
1052
+ if (openIndex < 0 || closeIndex <= openIndex) {
1075
1053
  depth--;
1076
1054
  if (depth === 0) {
1077
- return {start: firstOpenIndex, end: closingIndex + 1};
1055
+ return {start: firstOpenIndex, end: closeIndex + 1};
1078
1056
  }
1079
- i = closingIndex;
1057
+ i = closeIndex;
1080
1058
  } else {
1081
1059
  depth++;
1082
1060
  i = openIndex;
@@ -1085,6 +1063,37 @@
1085
1063
  }
1086
1064
  return null;
1087
1065
  }
1066
+ function indexOfExcluding(input, search, position, excludeRanges) {
1067
+ const i = input.indexOf(search, position);
1068
+ const exclusion = excludeRanges.find((r) => i >= r.start && i < r.end);
1069
+ if (exclusion) {
1070
+ return indexOfExcluding(
1071
+ input,
1072
+ search,
1073
+ exclusion.end,
1074
+ excludeRanges
1075
+ );
1076
+ }
1077
+ return i;
1078
+ }
1079
+ function splitExcluding(input, separator, excludeRanges) {
1080
+ const parts = [];
1081
+ let commaIndex = -1;
1082
+ let currIndex = 0;
1083
+ while (
1084
+ (commaIndex = indexOfExcluding(
1085
+ input,
1086
+ separator,
1087
+ currIndex,
1088
+ excludeRanges
1089
+ )) >= 0
1090
+ ) {
1091
+ parts.push(input.substring(currIndex, commaIndex).trim());
1092
+ currIndex = commaIndex + 1;
1093
+ }
1094
+ parts.push(input.substring(currIndex).trim());
1095
+ return parts;
1096
+ }
1088
1097
 
1089
1098
  const hslaParseCache = new Map();
1090
1099
  const rgbaParseCache = new Map();
@@ -1923,11 +1932,11 @@
1923
1932
  poleBg
1924
1933
  );
1925
1934
  }
1926
- function modifyShadowColor(rgb, filter) {
1927
- return modifyBackgroundColor(rgb, filter);
1935
+ function modifyShadowColor(rgb, theme) {
1936
+ return modifyBackgroundColor(rgb, theme);
1928
1937
  }
1929
- function modifyGradientColor(rgb, filter) {
1930
- return modifyBackgroundColor(rgb, filter);
1938
+ function modifyGradientColor(rgb, theme) {
1939
+ return modifyBackgroundColor(rgb, theme);
1931
1940
  }
1932
1941
 
1933
1942
  function createTextStyle(config) {
@@ -2707,28 +2716,28 @@
2707
2716
  }
2708
2717
  return lines.join("\n");
2709
2718
  }
2710
- function getModifiedFallbackStyle(filter, {strict}) {
2719
+ function getModifiedFallbackStyle(theme, {strict}) {
2711
2720
  const factory = defaultFallbackFactory;
2712
- return factory(filter, {strict});
2721
+ return factory(theme, {strict});
2713
2722
  }
2714
- function defaultFallbackFactory(filter, {strict}) {
2723
+ function defaultFallbackFactory(theme, {strict}) {
2715
2724
  const lines = [];
2716
- const isMicrosoft = ["microsoft.com", "docs.microsoft.com"].includes(
2717
- location.hostname
2718
- );
2719
2725
  lines.push(
2720
- `html, body, ${strict ? `body :not(iframe)${isMicrosoft ? ':not(div[style^="position:absolute;top:0;left:-"]' : ""}` : "body > :not(iframe)"} {`
2726
+ `html, body, ${strict ? "body :not(iframe)" : "body > :not(iframe)"} {`
2721
2727
  );
2722
2728
  lines.push(
2723
- ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, filter)} !important;`
2729
+ ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;`
2724
2730
  );
2725
2731
  lines.push(
2726
- ` border-color: ${modifyBorderColor({r: 64, g: 64, b: 64}, filter)} !important;`
2732
+ ` border-color: ${modifyBorderColor({r: 64, g: 64, b: 64}, theme)} !important;`
2727
2733
  );
2728
2734
  lines.push(
2729
- ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, filter)} !important;`
2735
+ ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;`
2730
2736
  );
2731
2737
  lines.push("}");
2738
+ lines.push(`div[style*="background-color: rgb(135, 135, 135)"] {`);
2739
+ lines.push(` background-color: #878787 !important;`);
2740
+ lines.push("}");
2732
2741
  return lines.join("\n");
2733
2742
  }
2734
2743
  const unparsableColors = new Set([
@@ -2824,26 +2833,26 @@
2824
2833
  part = part.trim();
2825
2834
  let rgb = parseColorWithCache(part);
2826
2835
  if (rgb) {
2827
- return (filter) => modifyGradientColor(rgb, filter);
2836
+ return (theme) => modifyGradientColor(rgb, theme);
2828
2837
  }
2829
2838
  const space = part.lastIndexOf(" ");
2830
2839
  rgb = parseColorWithCache(part.substring(0, space));
2831
2840
  if (rgb) {
2832
- return (filter) =>
2833
- `${modifyGradientColor(rgb, filter)} ${part.substring(space + 1)}`;
2841
+ return (theme) =>
2842
+ `${modifyGradientColor(rgb, theme)} ${part.substring(space + 1)}`;
2834
2843
  }
2835
2844
  const colorStopMatch = part.match(colorStopRegex);
2836
2845
  if (colorStopMatch) {
2837
2846
  rgb = parseColorWithCache(colorStopMatch[3]);
2838
2847
  if (rgb) {
2839
- return (filter) =>
2840
- `${colorStopMatch[1]}(${colorStopMatch[2] ? `${colorStopMatch[2]}, ` : ""}${modifyGradientColor(rgb, filter)})`;
2848
+ return (theme) =>
2849
+ `${colorStopMatch[1]}(${colorStopMatch[2] ? `${colorStopMatch[2]}, ` : ""}${modifyGradientColor(rgb, theme)})`;
2841
2850
  }
2842
2851
  }
2843
2852
  return () => part;
2844
2853
  });
2845
- return (filter) => {
2846
- return `${typeGradient}(${parts.map((modify) => modify(filter)).join(", ")})${hasComma ? ", " : ""}`;
2854
+ return (theme) => {
2855
+ return `${typeGradient}(${parts.map((modify) => modify(theme)).join(", ")})${hasComma ? ", " : ""}`;
2847
2856
  };
2848
2857
  };
2849
2858
  const getURLModifier = (urlValue) => {
@@ -2858,10 +2867,10 @@
2858
2867
  const baseURL =
2859
2868
  parentStyleSheet && parentStyleSheet.href
2860
2869
  ? getCSSBaseBath(parentStyleSheet.href)
2861
- : parentStyleSheet.ownerNode?.baseURI ||
2870
+ : parentStyleSheet?.ownerNode?.baseURI ||
2862
2871
  location.origin;
2863
2872
  url = getAbsoluteURL(baseURL, url);
2864
- return async (filter) => {
2873
+ return async (theme) => {
2865
2874
  if (isURLEmpty) {
2866
2875
  return "url('')";
2867
2876
  }
@@ -2909,7 +2918,7 @@
2909
2918
  if (imageDetails) {
2910
2919
  const bgImageValue = getBgImageValue(
2911
2920
  imageDetails,
2912
- filter
2921
+ theme
2913
2922
  );
2914
2923
  if (bgImageValue) {
2915
2924
  return bgImageValue;
@@ -2924,7 +2933,7 @@
2924
2933
  return `url("${url}")`;
2925
2934
  };
2926
2935
  };
2927
- const getBgImageValue = (imageDetails, filter) => {
2936
+ const getBgImageValue = (imageDetails, theme) => {
2928
2937
  const {isDark, isLight, isTransparent, isLarge, width} =
2929
2938
  imageDetails;
2930
2939
  let result;
@@ -2937,25 +2946,25 @@
2937
2946
  } else if (
2938
2947
  isDark &&
2939
2948
  isTransparent &&
2940
- filter.mode === 1 &&
2949
+ theme.mode === 1 &&
2941
2950
  width > 2
2942
2951
  ) {
2943
2952
  logInfo(`Inverting dark image ${logSrc}`);
2944
2953
  const inverted = getFilteredImageURL(imageDetails, {
2945
- ...filter,
2946
- sepia: clamp(filter.sepia + 10, 0, 100)
2954
+ ...theme,
2955
+ sepia: clamp(theme.sepia + 10, 0, 100)
2947
2956
  });
2948
2957
  result = `url("${inverted}")`;
2949
- } else if (isLight && !isTransparent && filter.mode === 1) {
2958
+ } else if (isLight && !isTransparent && theme.mode === 1) {
2950
2959
  logInfo(`Dimming light image ${logSrc}`);
2951
- const dimmed = getFilteredImageURL(imageDetails, filter);
2960
+ const dimmed = getFilteredImageURL(imageDetails, theme);
2952
2961
  result = `url("${dimmed}")`;
2953
- } else if (filter.mode === 0 && isLight) {
2962
+ } else if (theme.mode === 0 && isLight) {
2954
2963
  logInfo(`Applying filter to image ${logSrc}`);
2955
2964
  const filtered = getFilteredImageURL(imageDetails, {
2956
- ...filter,
2957
- brightness: clamp(filter.brightness - 10, 5, 200),
2958
- sepia: clamp(filter.sepia + 10, 0, 100)
2965
+ ...theme,
2966
+ brightness: clamp(theme.brightness - 10, 5, 200),
2967
+ sepia: clamp(theme.sepia + 10, 0, 100)
2959
2968
  });
2960
2969
  result = `url("${filtered}")`;
2961
2970
  } else {
@@ -3010,10 +3019,10 @@
3010
3019
  }
3011
3020
  }
3012
3021
  );
3013
- return (filter) => {
3022
+ return (theme) => {
3014
3023
  const results = modifiers
3015
3024
  .filter(Boolean)
3016
- .map((modify) => modify(filter));
3025
+ .map((modify) => modify(theme));
3017
3026
  if (results.some((r) => r instanceof Promise)) {
3018
3027
  return Promise.all(results).then((asyncResults) => {
3019
3028
  return asyncResults.filter(Boolean).join("");
@@ -3048,12 +3057,12 @@
3048
3057
  notParsed++;
3049
3058
  return () => value.substring(prefixIndex, matchEnd);
3050
3059
  }
3051
- return (filter) =>
3052
- `${value.substring(prefixIndex, matchIndex)}${modifyShadowColor(rgb, filter)}${i === colorMatches.length - 1 ? value.substring(matchEnd) : ""}`;
3060
+ return (theme) =>
3061
+ `${value.substring(prefixIndex, matchIndex)}${modifyShadowColor(rgb, theme)}${i === colorMatches.length - 1 ? value.substring(matchEnd) : ""}`;
3053
3062
  });
3054
- return (filter) => {
3063
+ return (theme) => {
3055
3064
  const modified = modifiers
3056
- .map((modify) => modify(filter))
3065
+ .map((modify) => modify(theme))
3057
3066
  .join("");
3058
3067
  return {
3059
3068
  matchesLength: colorMatches.length,
@@ -3947,7 +3956,8 @@
3947
3956
  "stop-color",
3948
3957
  "stroke",
3949
3958
  "bgcolor",
3950
- "color"
3959
+ "color",
3960
+ "background"
3951
3961
  ];
3952
3962
  const INLINE_STYLE_SELECTOR = INLINE_STYLE_ATTRS.map(
3953
3963
  (attr) => `[${attr}]`
@@ -4101,18 +4111,12 @@
4101
4111
  attrObservers.clear();
4102
4112
  }
4103
4113
  const inlineStyleCache = new WeakMap();
4104
- const filterProps = [
4105
- "brightness",
4106
- "contrast",
4107
- "grayscale",
4108
- "sepia",
4109
- "mode"
4110
- ];
4114
+ const themeProps = ["brightness", "contrast", "grayscale", "sepia", "mode"];
4111
4115
  function getInlineStyleCacheKey(el, theme) {
4112
4116
  return INLINE_STYLE_ATTRS.map(
4113
4117
  (attr) => `${attr}="${el.getAttribute(attr)}"`
4114
4118
  )
4115
- .concat(filterProps.map((prop) => `${prop}="${theme[prop]}"`))
4119
+ .concat(themeProps.map((prop) => `${prop}="${theme[prop]}"`))
4116
4120
  .join(" ");
4117
4121
  }
4118
4122
  function shouldIgnoreInlineStyle(element, selectors) {
@@ -4173,7 +4177,7 @@
4173
4177
  setProps(mod.declarations);
4174
4178
  mod.onTypeChange.addListener(setProps);
4175
4179
  }
4176
- function setAsyncValue(promise) {
4180
+ function setAsyncValue(promise, sourceValue) {
4177
4181
  promise.then((value) => {
4178
4182
  if (
4179
4183
  value &&
@@ -4182,6 +4186,20 @@
4182
4186
  ) {
4183
4187
  setStaticValue(value);
4184
4188
  }
4189
+ if (value && targetCSSProp === "background-image") {
4190
+ if (
4191
+ (element === document.documentElement ||
4192
+ element === document.body) &&
4193
+ value === sourceValue
4194
+ ) {
4195
+ value = "none";
4196
+ }
4197
+ setStaticValue(value);
4198
+ }
4199
+ inlineStyleCache.set(
4200
+ element,
4201
+ getInlineStyleCacheKey(element, theme)
4202
+ );
4185
4203
  });
4186
4204
  }
4187
4205
  const value =
@@ -4189,7 +4207,7 @@
4189
4207
  if (typeof value === "string") {
4190
4208
  setStaticValue(value);
4191
4209
  } else if (value instanceof Promise) {
4192
- setAsyncValue(value);
4210
+ setAsyncValue(value, cssVal);
4193
4211
  } else if (typeof value === "object") {
4194
4212
  setVarDeclaration(value);
4195
4213
  }
@@ -4212,6 +4230,18 @@
4212
4230
  }
4213
4231
  setCustomProp("background-color", "background-color", value);
4214
4232
  }
4233
+ if (
4234
+ (element === document.documentElement ||
4235
+ element === document.body) &&
4236
+ element.hasAttribute("background")
4237
+ ) {
4238
+ const url = getAbsoluteURL(
4239
+ location.href,
4240
+ element.getAttribute("background") ?? ""
4241
+ );
4242
+ const value = `url("${url}")`;
4243
+ setCustomProp("background-image", "background-image", value);
4244
+ }
4215
4245
  if (element.hasAttribute("color") && element.rel !== "mask-icon") {
4216
4246
  let value = element.getAttribute("color");
4217
4247
  if (
@@ -4272,6 +4302,12 @@
4272
4302
  element.style &&
4273
4303
  iterateCSSDeclarations(element.style, (property, value) => {
4274
4304
  if (property === "background-image" && value.includes("url")) {
4305
+ if (
4306
+ element === document.documentElement ||
4307
+ element === document.body
4308
+ ) {
4309
+ setCustomProp(property, property, value);
4310
+ }
4275
4311
  return;
4276
4312
  }
4277
4313
  if (
@@ -4955,7 +4991,9 @@
4955
4991
  if (!(element instanceof HTMLStyleElement)) {
4956
4992
  return false;
4957
4993
  }
4958
- const cssText = removeCSSComments(element.textContent ?? "").trim();
4994
+ const cssText = removeCSSComments$1(
4995
+ element.textContent ?? ""
4996
+ ).trim();
4959
4997
  return cssText.match(cssImportRegex);
4960
4998
  }
4961
4999
  function hasImports(cssRules, checkCrossOrigin) {
@@ -5329,7 +5367,7 @@
5329
5367
  });
5330
5368
  }
5331
5369
  async function replaceCSSImports(cssText, basePath, cache = new Map()) {
5332
- cssText = removeCSSComments(cssText);
5370
+ cssText = removeCSSComments$1(cssText);
5333
5371
  cssText = replaceCSSFontFace(cssText);
5334
5372
  cssText = replaceCSSRelativeURLsWithAbsolute(cssText, basePath);
5335
5373
  const importMatches = getMatches(cssImportRegex, cssText);
@@ -6138,7 +6176,9 @@
6138
6176
  }
6139
6177
  );
6140
6178
  }
6141
- const shouldProxyChildNodes = location.hostname === "www.vy.no";
6179
+ const shouldProxyChildNodes = ["brilliant.org", "www.vy.no"].includes(
6180
+ location.hostname
6181
+ );
6142
6182
  if (shouldProxyChildNodes) {
6143
6183
  overrideProperty(Node, "childNodes", {
6144
6184
  get: (native) =>
@@ -6422,7 +6462,7 @@
6422
6462
  const adoptedStyleFallbacks = new Map();
6423
6463
  const adoptedStyleNodeIds = new WeakMap();
6424
6464
  const adoptedStyleChangeTokens = new WeakMap();
6425
- let filter = null;
6465
+ let theme = null;
6426
6466
  let fixes = null;
6427
6467
  let isIFrame$1 = null;
6428
6468
  let ignoredImageAnalysisSelectors = [];
@@ -6462,22 +6502,22 @@
6462
6502
  "darkreader--fallback",
6463
6503
  document
6464
6504
  );
6465
- fallbackStyle.textContent = getModifiedFallbackStyle(filter, {
6505
+ fallbackStyle.textContent = getModifiedFallbackStyle(theme, {
6466
6506
  strict: true
6467
6507
  });
6468
6508
  document.head.insertBefore(fallbackStyle, document.head.firstChild);
6469
6509
  setupNodePositionWatcher(fallbackStyle, "fallback");
6470
6510
  const userAgentStyle = createOrUpdateStyle("darkreader--user-agent");
6471
6511
  userAgentStyle.textContent = getModifiedUserAgentStyle(
6472
- filter,
6512
+ theme,
6473
6513
  isIFrame$1,
6474
- filter.styleSystemControls
6514
+ theme.styleSystemControls
6475
6515
  );
6476
6516
  document.head.insertBefore(userAgentStyle, fallbackStyle.nextSibling);
6477
6517
  setupNodePositionWatcher(userAgentStyle, "user-agent");
6478
6518
  const textStyle = createOrUpdateStyle("darkreader--text");
6479
- if (filter.useFont || filter.textStroke > 0) {
6480
- textStyle.textContent = createTextStyle(filter);
6519
+ if (theme.useFont || theme.textStroke > 0) {
6520
+ textStyle.textContent = createTextStyle(theme);
6481
6521
  } else {
6482
6522
  textStyle.textContent = "";
6483
6523
  }
@@ -6488,11 +6528,11 @@
6488
6528
  invertStyle.textContent = [
6489
6529
  `${fixes.invert.join(", ")} {`,
6490
6530
  ` filter: ${getCSSFilterValue({
6491
- ...filter,
6531
+ ...theme,
6492
6532
  contrast:
6493
- filter.mode === 0
6494
- ? filter.contrast
6495
- : clamp(filter.contrast - 10, 0, 100)
6533
+ theme.mode === 0
6534
+ ? theme.contrast
6535
+ : clamp(theme.contrast - 10, 0, 100)
6496
6536
  })} !important;`,
6497
6537
  "}"
6498
6538
  ].join("\n");
@@ -6511,25 +6551,25 @@
6511
6551
  document.head.appendChild(overrideStyle);
6512
6552
  setupNodePositionWatcher(overrideStyle, "override");
6513
6553
  const variableStyle = createOrUpdateStyle("darkreader--variables");
6514
- const selectionColors = getSelectionColor(filter);
6554
+ const selectionColors = getSelectionColor(theme);
6515
6555
  const {
6516
6556
  darkSchemeBackgroundColor,
6517
6557
  darkSchemeTextColor,
6518
6558
  lightSchemeBackgroundColor,
6519
6559
  lightSchemeTextColor,
6520
6560
  mode
6521
- } = filter;
6561
+ } = theme;
6522
6562
  let schemeBackgroundColor =
6523
6563
  mode === 0 ? lightSchemeBackgroundColor : darkSchemeBackgroundColor;
6524
6564
  let schemeTextColor =
6525
6565
  mode === 0 ? lightSchemeTextColor : darkSchemeTextColor;
6526
6566
  schemeBackgroundColor = modifyBackgroundColor(
6527
6567
  parseColorWithCache(schemeBackgroundColor),
6528
- filter
6568
+ theme
6529
6569
  );
6530
6570
  schemeTextColor = modifyForegroundColor(
6531
6571
  parseColorWithCache(schemeTextColor),
6532
- filter
6572
+ theme
6533
6573
  );
6534
6574
  variableStyle.textContent = [
6535
6575
  `:root {`,
@@ -6572,11 +6612,11 @@
6572
6612
  invertStyle.textContent = [
6573
6613
  `${fixes.invert.join(", ")} {`,
6574
6614
  ` filter: ${getCSSFilterValue({
6575
- ...filter,
6615
+ ...theme,
6576
6616
  contrast:
6577
- filter.mode === 0
6578
- ? filter.contrast
6579
- : clamp(filter.contrast - 10, 0, 100)
6617
+ theme.mode === 0
6618
+ ? theme.contrast
6619
+ : clamp(theme.contrast - 10, 0, 100)
6580
6620
  })} !important;`,
6581
6621
  "}"
6582
6622
  ].join("\n");
@@ -6610,9 +6650,9 @@
6610
6650
  observer.observe(root, {childList: true});
6611
6651
  }
6612
6652
  function createShadowStaticStyleOverrides(root) {
6613
- const uninit = root.firstChild === null;
6653
+ const delayed = root.firstChild === null;
6614
6654
  createShadowStaticStyleOverridesInner(root);
6615
- if (uninit) {
6655
+ if (delayed) {
6616
6656
  delayedCreateShadowStaticStyleOverrides(root);
6617
6657
  }
6618
6658
  }
@@ -6620,7 +6660,7 @@
6620
6660
  return $cssText.replace(/\${(.+?)}/g, (_, $color) => {
6621
6661
  const color = parseColorWithCache($color);
6622
6662
  if (color) {
6623
- return modifyColor(color, filter);
6663
+ return modifyColor(color, theme);
6624
6664
  }
6625
6665
  return $color;
6626
6666
  });
@@ -6646,12 +6686,12 @@
6646
6686
  variablesStore.matchVariablesAndDependents();
6647
6687
  variablesStore.setOnRootVariableChange(() => {
6648
6688
  const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars");
6649
- variablesStore.putRootVars(rootVarsStyle, filter);
6689
+ variablesStore.putRootVars(rootVarsStyle, theme);
6650
6690
  });
6651
6691
  const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars");
6652
- variablesStore.putRootVars(rootVarsStyle, filter);
6692
+ variablesStore.putRootVars(rootVarsStyle, theme);
6653
6693
  styleManagers.forEach((manager) =>
6654
- manager.render(filter, ignoredImageAnalysisSelectors)
6694
+ manager.render(theme, ignoredImageAnalysisSelectors)
6655
6695
  );
6656
6696
  if (loadingStyles.size === 0) {
6657
6697
  cleanFallbackStyle();
@@ -6672,7 +6712,7 @@
6672
6712
  inlineStyleElements.forEach((el) =>
6673
6713
  overrideInlineStyle(
6674
6714
  el,
6675
- filter,
6715
+ theme,
6676
6716
  ignoredInlineSelectors,
6677
6717
  ignoredImageAnalysisSelectors
6678
6718
  )
@@ -6736,7 +6776,7 @@
6736
6776
  );
6737
6777
  if (!fallbackStyle.textContent) {
6738
6778
  fallbackStyle.textContent = getModifiedFallbackStyle(
6739
- filter,
6779
+ theme,
6740
6780
  {strict: false}
6741
6781
  );
6742
6782
  }
@@ -6758,7 +6798,7 @@
6758
6798
  }
6759
6799
  variablesStore.addRulesForMatching(details.rules);
6760
6800
  variablesStore.matchVariablesAndDependents();
6761
- manager.render(filter, ignoredImageAnalysisSelectors);
6801
+ manager.render(theme, ignoredImageAnalysisSelectors);
6762
6802
  }
6763
6803
  const manager = manageStyle(element, {
6764
6804
  update,
@@ -6777,10 +6817,10 @@
6777
6817
  }
6778
6818
  const throttledRenderAllStyles = throttle((callback) => {
6779
6819
  styleManagers.forEach((manager) =>
6780
- manager.render(filter, ignoredImageAnalysisSelectors)
6820
+ manager.render(theme, ignoredImageAnalysisSelectors)
6781
6821
  );
6782
6822
  adoptedStyleManagers.forEach((manager) =>
6783
- manager.render(filter, ignoredImageAnalysisSelectors)
6823
+ manager.render(theme, ignoredImageAnalysisSelectors)
6784
6824
  );
6785
6825
  callback && callback();
6786
6826
  });
@@ -6799,16 +6839,15 @@
6799
6839
  }
6800
6840
  function createThemeAndWatchForUpdates() {
6801
6841
  createStaticStyleOverrides();
6802
- if (!documentIsVisible() && !filter.immediateModify) {
6842
+ if (!documentIsVisible() && !theme.immediateModify) {
6803
6843
  setDocumentVisibilityListener(runDynamicStyle);
6804
6844
  } else {
6805
6845
  runDynamicStyle();
6806
6846
  }
6807
- changeMetaThemeColorWhenAvailable(filter);
6847
+ changeMetaThemeColorWhenAvailable(theme);
6808
6848
  }
6809
6849
  let pendingAdoptedVarMatch = false;
6810
6850
  function handleAdoptedStyleSheets(node) {
6811
- const theme = filter;
6812
6851
  if (isFirefox) {
6813
6852
  const fallback = getAdoptedStyleSheetFallback(node);
6814
6853
  fallback.render(theme, ignoredImageAnalysisSelectors);
@@ -6920,7 +6959,7 @@
6920
6959
  });
6921
6960
  variablesStore.matchVariablesAndDependents();
6922
6961
  newManagers.forEach((manager) =>
6923
- manager.render(filter, ignoredImageAnalysisSelectors)
6962
+ manager.render(theme, ignoredImageAnalysisSelectors)
6924
6963
  );
6925
6964
  newManagers.forEach((manager) => manager.watch());
6926
6965
  stylesToRestore.forEach((style) =>
@@ -6937,7 +6976,7 @@
6937
6976
  (element) => {
6938
6977
  overrideInlineStyle(
6939
6978
  element,
6940
- filter,
6979
+ theme,
6941
6980
  ignoredInlineSelectors,
6942
6981
  ignoredImageAnalysisSelectors
6943
6982
  );
@@ -6948,7 +6987,7 @@
6948
6987
  const rootVarsStyle = createOrUpdateStyle(
6949
6988
  "darkreader--root-vars"
6950
6989
  );
6951
- variablesStore.putRootVars(rootVarsStyle, filter);
6990
+ variablesStore.putRootVars(rootVarsStyle, theme);
6952
6991
  }
6953
6992
  }
6954
6993
  },
@@ -6961,7 +7000,7 @@
6961
7000
  forEach(inlineStyleElements, (el) =>
6962
7001
  overrideInlineStyle(
6963
7002
  el,
6964
- filter,
7003
+ theme,
6965
7004
  ignoredInlineSelectors,
6966
7005
  ignoredImageAnalysisSelectors
6967
7006
  )
@@ -7011,11 +7050,11 @@
7011
7050
  return false;
7012
7051
  }
7013
7052
  function createOrUpdateDynamicThemeInternal(
7014
- filterConfig,
7053
+ themeConfig,
7015
7054
  dynamicThemeFixes,
7016
7055
  iframe
7017
7056
  ) {
7018
- filter = filterConfig;
7057
+ theme = themeConfig;
7019
7058
  fixes = dynamicThemeFixes;
7020
7059
  if (fixes) {
7021
7060
  ignoredImageAnalysisSelectors = Array.isArray(
@@ -7030,7 +7069,7 @@
7030
7069
  ignoredImageAnalysisSelectors = [];
7031
7070
  ignoredInlineSelectors = [];
7032
7071
  }
7033
- if (filter.immediateModify) {
7072
+ if (theme.immediateModify) {
7034
7073
  setIsDOMReady(() => {
7035
7074
  return true;
7036
7075
  });
@@ -7047,7 +7086,7 @@
7047
7086
  );
7048
7087
  document.documentElement.setAttribute(
7049
7088
  "data-darkreader-scheme",
7050
- filter.mode ? "dark" : "dimmed"
7089
+ theme.mode ? "dark" : "dimmed"
7051
7090
  );
7052
7091
  createThemeAndWatchForUpdates();
7053
7092
  } else {
@@ -7056,7 +7095,7 @@
7056
7095
  "darkreader--fallback"
7057
7096
  );
7058
7097
  document.documentElement.appendChild(fallbackStyle);
7059
- fallbackStyle.textContent = getModifiedFallbackStyle(filter, {
7098
+ fallbackStyle.textContent = getModifiedFallbackStyle(theme, {
7060
7099
  strict: true
7061
7100
  });
7062
7101
  }
@@ -7123,6 +7162,201 @@
7123
7162
  clearColorCache();
7124
7163
  }
7125
7164
 
7165
+ function parseCSS(cssText) {
7166
+ cssText = removeCSSComments(cssText);
7167
+ cssText = cssText.trim();
7168
+ if (!cssText) {
7169
+ return [];
7170
+ }
7171
+ const rules = [];
7172
+ const excludeRanges = getTokenExclusionRanges(cssText);
7173
+ const bracketRanges = getAllOpenCloseRanges(
7174
+ cssText,
7175
+ "{",
7176
+ "}",
7177
+ excludeRanges
7178
+ );
7179
+ let ruleStart = 0;
7180
+ bracketRanges.forEach((brackets) => {
7181
+ const key = cssText.substring(ruleStart, brackets.start).trim();
7182
+ const content = cssText.substring(
7183
+ brackets.start + 1,
7184
+ brackets.end - 1
7185
+ );
7186
+ if (key.startsWith("@")) {
7187
+ const typeEndIndex = key.search(/[\s\(]/);
7188
+ const rule = {
7189
+ type:
7190
+ typeEndIndex < 0 ? key : key.substring(0, typeEndIndex),
7191
+ query:
7192
+ typeEndIndex < 0
7193
+ ? ""
7194
+ : key.substring(typeEndIndex).trim(),
7195
+ rules: parseCSS(content)
7196
+ };
7197
+ rules.push(rule);
7198
+ } else {
7199
+ const rule = {
7200
+ selectors: parseSelectors(key),
7201
+ declarations: parseDeclarations(content)
7202
+ };
7203
+ rules.push(rule);
7204
+ }
7205
+ ruleStart = brackets.end + 1;
7206
+ });
7207
+ return rules;
7208
+ }
7209
+ function getAllOpenCloseRanges(
7210
+ input,
7211
+ openToken,
7212
+ closeToken,
7213
+ excludeRanges = []
7214
+ ) {
7215
+ const ranges = [];
7216
+ let i = 0;
7217
+ let range;
7218
+ while (
7219
+ (range = getOpenCloseRange(
7220
+ input,
7221
+ i,
7222
+ openToken,
7223
+ closeToken,
7224
+ excludeRanges
7225
+ ))
7226
+ ) {
7227
+ ranges.push(range);
7228
+ i = range.end;
7229
+ }
7230
+ return ranges;
7231
+ }
7232
+ function removeCSSComments(cssText) {
7233
+ return cssText.trim().replace(/\/\*.*?\*\//g, "");
7234
+ }
7235
+ function getTokenExclusionRanges(cssText) {
7236
+ const singleQuoteGoesFirst =
7237
+ cssText.indexOf("'") < cssText.indexOf('"');
7238
+ const firstQuote = singleQuoteGoesFirst ? "'" : '"';
7239
+ const secondQuote = singleQuoteGoesFirst ? '"' : "'";
7240
+ const excludeRanges = getAllOpenCloseRanges(
7241
+ cssText,
7242
+ firstQuote,
7243
+ firstQuote
7244
+ );
7245
+ excludeRanges.push(
7246
+ ...getAllOpenCloseRanges(
7247
+ cssText,
7248
+ secondQuote,
7249
+ secondQuote,
7250
+ excludeRanges
7251
+ )
7252
+ );
7253
+ excludeRanges.push(
7254
+ ...getAllOpenCloseRanges(cssText, "[", "]", excludeRanges)
7255
+ );
7256
+ excludeRanges.push(
7257
+ ...getAllOpenCloseRanges(cssText, "(", ")", excludeRanges)
7258
+ );
7259
+ return excludeRanges;
7260
+ }
7261
+ function parseSelectors(selectorText) {
7262
+ const excludeRanges = getTokenExclusionRanges(selectorText);
7263
+ return splitExcluding(selectorText, ",", excludeRanges);
7264
+ }
7265
+ function parseDeclarations(cssDeclarationsText) {
7266
+ const declarations = [];
7267
+ const excludeRanges = getTokenExclusionRanges(cssDeclarationsText);
7268
+ splitExcluding(cssDeclarationsText, ";", excludeRanges).forEach(
7269
+ (part) => {
7270
+ const colonIndex = part.indexOf(":");
7271
+ if (colonIndex > 0) {
7272
+ const importantIndex = part.indexOf("!important");
7273
+ declarations.push({
7274
+ property: part.substring(0, colonIndex).trim(),
7275
+ value: part
7276
+ .substring(
7277
+ colonIndex + 1,
7278
+ importantIndex > 0
7279
+ ? importantIndex
7280
+ : part.length
7281
+ )
7282
+ .trim(),
7283
+ important: importantIndex > 0
7284
+ });
7285
+ }
7286
+ }
7287
+ );
7288
+ return declarations;
7289
+ }
7290
+
7291
+ function formatCSS(cssText) {
7292
+ const parsed = parseCSS(cssText);
7293
+ const lines = [];
7294
+ const tab = " ";
7295
+ function formatRule(rule, indent) {
7296
+ if (isStyleRule(rule)) {
7297
+ formatStyleRule(rule, indent);
7298
+ } else {
7299
+ formatAtRule(rule, indent);
7300
+ }
7301
+ }
7302
+ function formatAtRule({type, query, rules}, indent) {
7303
+ lines.push(`${indent}${type} ${query} {`);
7304
+ rules.forEach((child) => formatRule(child, `${indent}${tab}`));
7305
+ lines.push(`${indent}}`);
7306
+ }
7307
+ function formatStyleRule({selectors, declarations}, indent) {
7308
+ const lastSelectorIndex = selectors.length - 1;
7309
+ selectors.forEach((selector, i) => {
7310
+ lines.push(
7311
+ `${indent}${selector}${i < lastSelectorIndex ? "," : " {"}`
7312
+ );
7313
+ });
7314
+ const sorted = sortDeclarations(declarations);
7315
+ sorted.forEach(({property, value, important}) => {
7316
+ lines.push(
7317
+ `${indent}${tab}${property}: ${value}${important ? " !important" : ""};`
7318
+ );
7319
+ });
7320
+ lines.push(`${indent}}`);
7321
+ }
7322
+ clearEmptyRules(parsed);
7323
+ parsed.forEach((rule) => formatRule(rule, ""));
7324
+ return lines.join("\n");
7325
+ }
7326
+ function sortDeclarations(declarations) {
7327
+ const prefixRegex = /^-[a-z]-/;
7328
+ return [...declarations].sort((a, b) => {
7329
+ const aProp = a.property;
7330
+ const bProp = b.property;
7331
+ const aPrefix = aProp.match(prefixRegex)?.[0] ?? "";
7332
+ const bPrefix = bProp.match(prefixRegex)?.[0] ?? "";
7333
+ const aNorm = aPrefix ? aProp.replace(prefixRegex, "") : aProp;
7334
+ const bNorm = bPrefix ? bProp.replace(prefixRegex, "") : bProp;
7335
+ if (aNorm === bNorm) {
7336
+ return aPrefix.localeCompare(bPrefix);
7337
+ }
7338
+ return aNorm.localeCompare(bNorm);
7339
+ });
7340
+ }
7341
+ function isStyleRule(rule) {
7342
+ return "selectors" in rule;
7343
+ }
7344
+ function clearEmptyRules(rules) {
7345
+ for (let i = rules.length - 1; i >= 0; i--) {
7346
+ const rule = rules[i];
7347
+ if (isStyleRule(rule)) {
7348
+ if (rule.declarations.length === 0) {
7349
+ rules.splice(i, 1);
7350
+ }
7351
+ } else {
7352
+ clearEmptyRules(rule.rules);
7353
+ if (rule.rules.length === 0) {
7354
+ rules.splice(i, 1);
7355
+ }
7356
+ }
7357
+ }
7358
+ }
7359
+
7126
7360
  const blobRegex = /url\(\"(blob\:.*?)\"\)/g;
7127
7361
  async function replaceBlobs(text) {
7128
7362
  const promises = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "darkreader",
3
- "version": "4.9.79",
3
+ "version": "4.9.80",
4
4
  "description": "Dark mode for every website",
5
5
  "scripts": {
6
6
  "api": "node --max-old-space-size=3072 tasks/cli.js build --api",
@@ -15,7 +15,7 @@
15
15
  "prepublishOnly": "npm test && npm run api",
16
16
  "release": "npm test && npm run lint && node tasks/cli.js build --release",
17
17
  "test": "npm run test:unit",
18
- "test:all": "npm run test:unit; npm run test:browser; npm run test:inject; npm run test:project",
18
+ "test:all": "npm run test:unit; npm run test:browser; npm run test:inject",
19
19
  "test:browser": "npm run test:chrome && npm run test:chrome-mv3 && npm run test:firefox",
20
20
  "test:chrome": "node tasks/cli.js build --debug --test --chrome-mv2 && jest --config=tests/browser/jest.config.mjs --runInBand",
21
21
  "test:chrome-mv3": "node tasks/cli.js build --debug --test --chrome-mv3 && jest --config=tests/browser/jest.config.chrome-mv3.mjs --runInBand",
@@ -24,10 +24,8 @@
24
24
  "test:firefox": "node tasks/cli.js build --debug --test --firefox-mv2 && jest --config=tests/browser/jest.config.firefox.mjs --runInBand",
25
25
  "test:inject": "node --max-old-space-size=3072 node_modules/.bin/karma start ./tests/inject/karma.conf.cjs",
26
26
  "test:inject:debug": "node --max-old-space-size=3072 node_modules/.bin/karma start ./tests/inject/karma.conf.cjs --debug",
27
- "test:project": "jest --config=tests/project/jest.config.mjs",
28
27
  "test:unit": "jest --config=tests/unit/jest.config.mjs",
29
28
  "test:unit:debug": "node --inspect-brk ./node_modules/jest/bin/jest --config=tests/unit/jest.config.mjs --runInBand --no-cache --watch",
30
- "test:update-snapshots": "npm run test -- --updateSnapshot && npm run test:project -- --updateSnapshot",
31
29
  "translate-en-message": "node ./tasks/translate.js --message",
32
30
  "translate-new-en-messages": "node ./tasks/translate.js --new-messages",
33
31
  "deno:bootstrap": "deno run --allow-read=./ --allow-sys=uid --allow-write=deno.json tasks/deno.js"
@@ -61,17 +59,17 @@
61
59
  "@rollup/plugin-replace": "5.0.5",
62
60
  "@rollup/plugin-typescript": "11.1.6",
63
61
  "@rollup/pluginutils": "5.1.0",
64
- "@types/chrome": "0.0.261",
65
- "@types/eslint": "8.56.3",
62
+ "@types/chrome": "0.0.263",
63
+ "@types/eslint": "8.56.5",
66
64
  "@types/jasmine": "5.1.4",
67
65
  "@types/jest": "29.5.12",
68
66
  "@types/karma": "6.3.8",
69
67
  "@types/karma-coverage": "2.0.3",
70
- "@types/node": "20.11.20",
68
+ "@types/node": "20.11.25",
71
69
  "@types/offscreencanvas": "2019.7.3",
72
70
  "@types/ws": "8.5.10",
73
- "@typescript-eslint/eslint-plugin": "7.0.2",
74
- "@typescript-eslint/parser": "7.0.2",
71
+ "@typescript-eslint/eslint-plugin": "7.1.1",
72
+ "@typescript-eslint/parser": "7.1.1",
75
73
  "chokidar": "3.6.0",
76
74
  "eslint": "8.57.0",
77
75
  "eslint-plugin-compat": "4.2.0",
@@ -85,7 +83,7 @@
85
83
  "karma": "6.4.3",
86
84
  "karma-chrome-launcher": "3.2.0",
87
85
  "karma-coverage": "2.2.1",
88
- "karma-firefox-launcher": "2.1.2",
86
+ "karma-firefox-launcher": "2.1.3",
89
87
  "karma-jasmine": "5.1.0",
90
88
  "karma-rollup-preprocessor": "7.0.8",
91
89
  "karma-safari-launcher": "1.0.0",
@@ -93,12 +91,12 @@
93
91
  "less": "4.2.0",
94
92
  "malevic": "0.20.1",
95
93
  "prettier": "3.2.5",
96
- "puppeteer-core": "22.2.0",
97
- "rollup": "4.12.0",
94
+ "puppeteer-core": "22.4.1",
95
+ "rollup": "4.12.1",
98
96
  "rollup-plugin-istanbul": "5.0.0",
99
97
  "ts-jest": "29.1.2",
100
98
  "tslib": "2.6.2",
101
- "typescript": "5.3.3",
99
+ "typescript": "5.4.2",
102
100
  "web-ext": "7.11.0",
103
101
  "ws": "8.16.0",
104
102
  "yazl": "2.5.1"