-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 26db015
Showing
15 changed files
with
8,044 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Main content script entry point | ||
|
|
||
| (function() { | ||
| 'use strict'; | ||
|
|
||
| // Initialize the extension | ||
| async function init() { | ||
| await changeTitle() | ||
| settings("apply") | ||
| parseTicket() | ||
| checkPath() | ||
| } | ||
|
|
||
| // Wait for DOM to be ready | ||
| if (document.readyState === 'loading') { | ||
| document.addEventListener('DOMContentLoaded', init); | ||
| } else { | ||
| init(); | ||
| } | ||
|
|
||
| // Listen for storage changes | ||
| addEventListener("storage", (event) => { | ||
| if (event.key == "userSettings") { | ||
| settings("apply") | ||
| } | ||
| }); | ||
|
|
||
| // Listen for messages from popup windows (color editor) | ||
| window.addEventListener("message", (event) => { | ||
| let data = event.data | ||
| let origin = event.origin | ||
| console.log("Message from:", origin, data, event) | ||
|
|
||
| if (data.colorsReady) { | ||
| let colors = settings('get', 'customHighlights') | ||
| event.source.postMessage({ customColors: colors }, "*") | ||
| } | ||
| if (data.colorData) { | ||
| let element = document.getElementById("customHighlights") | ||
| if (element) { | ||
| let strData = JSON.stringify(data.colorData) | ||
| element.value = strData | ||
| settings('update') | ||
| } | ||
| } | ||
| }, false); | ||
| })(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,346 @@ | ||
| //color manipulation via css filters: https://github.com/angel-rs/css-color-filter-generator | ||
| //modified to return an object instead of filter string | ||
|
|
||
| class Color { | ||
| constructor(r, g, b) { | ||
| this.set(r, g, b); | ||
| } | ||
|
|
||
| toString() { | ||
| return `rgb(${Math.round( | ||
| this.r | ||
| )}, ${Math.round(this.g)}, ${Math.round(this.b)})`; | ||
| } | ||
|
|
||
| set(r, g, b) { | ||
| this.r = this.clamp(r); | ||
| this.g = this.clamp(g); | ||
| this.b = this.clamp(b); | ||
| } | ||
|
|
||
| hueRotate(angle = 0) { | ||
| angle = (angle / 180) * Math.PI; | ||
| const sin = Math.sin(angle); | ||
| const cos = Math.cos(angle); | ||
|
|
||
| this.multiply([ | ||
| 0.213 + cos * 0.787 - sin * 0.213, | ||
| 0.715 - cos * 0.715 - sin * 0.715, | ||
| 0.072 - cos * 0.072 + sin * 0.928, | ||
| 0.213 - cos * 0.213 + sin * 0.143, | ||
| 0.715 + cos * 0.285 + sin * 0.14, | ||
| 0.072 - cos * 0.072 - sin * 0.283, | ||
| 0.213 - cos * 0.213 - sin * 0.787, | ||
| 0.715 - cos * 0.715 + sin * 0.715, | ||
| 0.072 + cos * 0.928 + sin * 0.072 | ||
| ]); | ||
| } | ||
|
|
||
| grayscale(value = 1) { | ||
| this.multiply([ | ||
| 0.2126 + 0.7874 * (1 - value), | ||
| 0.7152 - 0.7152 * (1 - value), | ||
| 0.0722 - 0.0722 * (1 - value), | ||
| 0.2126 - 0.2126 * (1 - value), | ||
| 0.7152 + 0.2848 * (1 - value), | ||
| 0.0722 - 0.0722 * (1 - value), | ||
| 0.2126 - 0.2126 * (1 - value), | ||
| 0.7152 - 0.7152 * (1 - value), | ||
| 0.0722 + 0.9278 * (1 - value) | ||
| ]); | ||
| } | ||
|
|
||
| sepia(value = 1) { | ||
| this.multiply([ | ||
| 0.393 + 0.607 * (1 - value), | ||
| 0.769 - 0.769 * (1 - value), | ||
| 0.189 - 0.189 * (1 - value), | ||
| 0.349 - 0.349 * (1 - value), | ||
| 0.686 + 0.314 * (1 - value), | ||
| 0.168 - 0.168 * (1 - value), | ||
| 0.272 - 0.272 * (1 - value), | ||
| 0.534 - 0.534 * (1 - value), | ||
| 0.131 + 0.869 * (1 - value) | ||
| ]); | ||
| } | ||
|
|
||
| saturate(value = 1) { | ||
| this.multiply([ | ||
| 0.213 + 0.787 * value, | ||
| 0.715 - 0.715 * value, | ||
| 0.072 - 0.072 * value, | ||
| 0.213 - 0.213 * value, | ||
| 0.715 + 0.285 * value, | ||
| 0.072 - 0.072 * value, | ||
| 0.213 - 0.213 * value, | ||
| 0.715 - 0.715 * value, | ||
| 0.072 + 0.928 * value | ||
| ]); | ||
| } | ||
|
|
||
| multiply(matrix) { | ||
| const newR = this.clamp( | ||
| this.r * matrix[0] + this.g * matrix[1] + this.b * matrix[2] | ||
| ); | ||
| const newG = this.clamp( | ||
| this.r * matrix[3] + this.g * matrix[4] + this.b * matrix[5] | ||
| ); | ||
| const newB = this.clamp( | ||
| this.r * matrix[6] + this.g * matrix[7] + this.b * matrix[8] | ||
| ); | ||
| this.r = newR; | ||
| this.g = newG; | ||
| this.b = newB; | ||
| } | ||
|
|
||
| brightness(value = 1) { | ||
| this.linear(value); | ||
| } | ||
| contrast(value = 1) { | ||
| this.linear(value, -(0.5 * value) + 0.5); | ||
| } | ||
|
|
||
| linear(slope = 1, intercept = 0) { | ||
| this.r = this.clamp(this.r * slope + intercept * 255); | ||
| this.g = this.clamp(this.g * slope + intercept * 255); | ||
| this.b = this.clamp(this.b * slope + intercept * 255); | ||
| } | ||
|
|
||
| invert(value = 1) { | ||
| this.r = this.clamp((value + (this.r / 255) * (1 - 2 * value)) * 255); | ||
| this.g = this.clamp((value + (this.g / 255) * (1 - 2 * value)) * 255); | ||
| this.b = this.clamp((value + (this.b / 255) * (1 - 2 * value)) * 255); | ||
| } | ||
|
|
||
| hsl() { | ||
| // Code taken from https://stackoverflow.com/a/9493060/2688027, licensed under CC BY-SA. | ||
| const r = this.r / 255; | ||
| const g = this.g / 255; | ||
| const b = this.b / 255; | ||
| const max = Math.max(r, g, b); | ||
| const min = Math.min(r, g, b); | ||
| let h, | ||
| s, | ||
| l = (max + min) / 2; | ||
|
|
||
| if (max === min) { | ||
| h = s = 0; | ||
| } else { | ||
| const d = max - min; | ||
| s = l > 0.5 ? d / (2 - max - min) : d / (max + min); | ||
| switch (max) { | ||
| case r: | ||
| h = (g - b) / d + (g < b ? 6 : 0); | ||
| break; | ||
|
|
||
| case g: | ||
| h = (b - r) / d + 2; | ||
| break; | ||
|
|
||
| case b: | ||
| h = (r - g) / d + 4; | ||
| break; | ||
| } | ||
| h /= 6; | ||
| } | ||
|
|
||
| return { | ||
| h: h * 100, | ||
| s: s * 100, | ||
| l: l * 100 | ||
| }; | ||
| } | ||
|
|
||
| clamp(value) { | ||
| if (value > 255) { | ||
| value = 255; | ||
| } else if (value < 0) { | ||
| value = 0; | ||
| } | ||
| return value; | ||
| } | ||
| } | ||
|
|
||
| class Solver { | ||
| constructor(target, baseColor) { | ||
| this.target = target; | ||
| this.targetHSL = target.hsl(); | ||
| this.reusedColor = new Color(0, 0, 0); | ||
| } | ||
|
|
||
| solve() { | ||
| const result = this.solveNarrow(this.solveWide()); | ||
| return { | ||
| values: result.values, | ||
| loss: result.loss, | ||
| filter: this.css(result.values) | ||
| }; | ||
| } | ||
|
|
||
| solveWide() { | ||
| const A = 5; | ||
| const c = 15; | ||
| const a = [60, 180, 18000, 600, 1.2, 1.2]; | ||
|
|
||
| let best = { loss: Infinity }; | ||
| for (let i = 0; best.loss > 25 && i < 3; i++) { | ||
| const initial = [50, 20, 3750, 50, 100, 100]; | ||
| const result = this.spsa(A, a, c, initial, 1000); | ||
| if (result.loss < best.loss) { | ||
| best = result; | ||
| } | ||
| } | ||
| return best; | ||
| } | ||
|
|
||
| solveNarrow(wide) { | ||
| const A = wide.loss; | ||
| const c = 2; | ||
| const A1 = A + 1; | ||
| const a = [0.25 * A1, 0.25 * A1, A1, 0.25 * A1, 0.2 * A1, 0.2 * A1]; | ||
| return this.spsa(A, a, c, wide.values, 500); | ||
| } | ||
|
|
||
| spsa(A, a, c, values, iters) { | ||
| const alpha = 1; | ||
| const gamma = 0.16666666666666666; | ||
|
|
||
| let best = null; | ||
| let bestLoss = Infinity; | ||
| const deltas = new Array(6); | ||
| const highArgs = new Array(6); | ||
| const lowArgs = new Array(6); | ||
|
|
||
| for (let k = 0; k < iters; k++) { | ||
| const ck = c / Math.pow(k + 1, gamma); | ||
| for (let i = 0; i < 6; i++) { | ||
| deltas[i] = Math.random() > 0.5 ? 1 : -1; | ||
| highArgs[i] = values[i] + ck * deltas[i]; | ||
| lowArgs[i] = values[i] - ck * deltas[i]; | ||
| } | ||
|
|
||
| const lossDiff = this.loss(highArgs) - this.loss(lowArgs); | ||
|
|
||
| for (let i = 0; i < 6; i++) { | ||
| const g = (lossDiff / (2 * ck)) * deltas[i]; | ||
| const ak = a[i] / Math.pow(A + k + 1, alpha); | ||
| values[i] = fix(values[i] - ak * g, i); | ||
| } | ||
|
|
||
| const loss = this.loss(values); | ||
| if (loss < bestLoss) { | ||
| best = values.slice(0); | ||
| bestLoss = loss; | ||
| } | ||
| } | ||
| return { values: best, loss: bestLoss }; | ||
|
|
||
| function fix(value, idx) { | ||
| let max = 100; | ||
| if (idx === 2 /* saturate */) { | ||
| max = 7500; | ||
| } else if (idx === 4 /* brightness */ || idx === 5 /* contrast */) { | ||
| max = 200; | ||
| } | ||
|
|
||
| if (idx === 3 /* hue-rotate */) { | ||
| if (value > max) { | ||
| value %= max; | ||
| } else if (value < 0) { | ||
| value = max + (value % max); | ||
| } | ||
| } else if (value < 0) { | ||
| value = 0; | ||
| } else if (value > max) { | ||
| value = max; | ||
| } | ||
| return value; | ||
| } | ||
| } | ||
|
|
||
| loss(filters) { | ||
| // Argument is array of percentages. | ||
| const color = this.reusedColor; | ||
| color.set(0, 0, 0); | ||
|
|
||
| color.invert(filters[0] / 100); | ||
| color.sepia(filters[1] / 100); | ||
| color.saturate(filters[2] / 100); | ||
| color.hueRotate(filters[3] * 3.6); | ||
| color.brightness(filters[4] / 100); | ||
| color.contrast(filters[5] / 100); | ||
|
|
||
| const colorHSL = color.hsl(); | ||
| return ( | ||
| Math.abs(color.r - this.target.r) + | ||
| Math.abs(color.g - this.target.g) + | ||
| Math.abs(color.b - this.target.b) + | ||
| Math.abs(colorHSL.h - this.targetHSL.h) + | ||
| Math.abs(colorHSL.s - this.targetHSL.s) + | ||
| Math.abs(colorHSL.l - this.targetHSL.l) | ||
| ); | ||
| } | ||
|
|
||
| css(filters) { | ||
| function fmt(idx, multiplier = 1) { | ||
| return Math.round(filters[idx] * multiplier); | ||
| } | ||
|
|
||
| //modified here | ||
| return { | ||
| invert: `${fmt(0)}%`, | ||
| sepia: `${fmt(1)}%`, | ||
| saturate: `${fmt(2)}%`, | ||
| hue_rotate: `${fmt(3, 3.6)}deg`, | ||
| brightness: `${fmt(4)}%`, | ||
| contrast: `${fmt(5)}%` | ||
| } | ||
|
|
||
| //return `filter: invert(${fmt( | ||
| // 0 | ||
| //)}%) sepia(${fmt(1)}%) saturate(${fmt(2)}%) hue-rotate(${fmt(3, 3.6)}deg) brightness(${fmt(4)}%) contrast(${fmt(5)}%);`; | ||
| } | ||
| } | ||
|
|
||
| function hexToRgb(hex) { | ||
| // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") | ||
| const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; | ||
| hex = hex.replace(shorthandRegex, (m, r, g, b) => { | ||
| return r + r + g + g + b + b; | ||
| }); | ||
|
|
||
| const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); | ||
| return result | ||
| ? [ | ||
| parseInt(result[1], 16), | ||
| parseInt(result[2], 16), | ||
| parseInt(result[3], 16) | ||
| ] | ||
| : null; | ||
| } | ||
|
|
||
| function getFilterForColor(rgbColor) { | ||
| let bestResult = null; | ||
| let iterationCount = 0; // Initialize a counter for iterations | ||
|
|
||
| for (let i = 0; i < 200; i++) { | ||
| iterationCount++; // Increment the counter for each iteration | ||
| const color = new Color(rgbColor[0], rgbColor[1], rgbColor[2]); | ||
| const solver = new Solver(color); | ||
| const result = solver.solve(); | ||
|
|
||
| if (!bestResult || result.loss < bestResult.loss) { | ||
| bestResult = result; | ||
| if (bestResult.loss <= 0.01) { | ||
| // exit early if good result is found | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| console.log("Total iterations: ", iterationCount); // Log the total iterations | ||
| return bestResult; | ||
| } | ||
|
|
||
| //end color manipulation | ||
|
|
Oops, something went wrong.