-
-
Save gblazex/bf2902eeb3be45fae868717f21a6df9a to your computer and use it in GitHub Desktop.
Chrome tampermonkey helper for the elderly
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
// ==UserScript== | |
// @name Keyboard Shortcut Scripts | |
// @namespace http://tampermonkey.net/ | |
// @version 0.1 | |
// @description Run scripts with keyboard shortcuts | |
// @match *://*/* | |
// @grant none | |
// ==/UserScript== | |
// Parameters | |
const targetContrastRatio = 5.0; // WCAG 2.0 AA | |
const maxTextSaturation = 45; // Limit excessive colors (bad on light bg) | |
const backgroundColor = [255, 255, 255]; // rgb 0-255 | |
// something with less blue wavelength could be used at night: | |
// e.g. [255, 243, 220]; | |
// Color conversion functions | |
function rgbToHsl(r, g, b) { | |
r /= 255, g /= 255, b /= 255; | |
const max = Math.max(r, g, b), 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 * 360, s * 100, l * 100]; | |
} | |
function hslToRgb(h, s, l) { | |
h /= 360; | |
s /= 100; | |
l /= 100; | |
let r, g, b; | |
if (s === 0) { | |
r = g = b = l; | |
} else { | |
const hue2rgb = (p, q, t) => { | |
if (t < 0) t += 1; | |
if (t > 1) t -= 1; | |
if (t < 1/6) return p + (q - p) * 6 * t; | |
if (t < 1/2) return q; | |
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; | |
return p; | |
}; | |
const q = l < 0.5 ? l * (1 + s) : l + s - l * s; | |
const p = 2 * l - q; | |
r = hue2rgb(p, q, h + 1/3); | |
g = hue2rgb(p, q, h); | |
b = hue2rgb(p, q, h - 1/3); | |
} | |
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; | |
} | |
function getLuminance(r, g, b) { | |
const a = [r, g, b].map(v => { | |
v /= 255; | |
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); | |
}); | |
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; | |
} | |
function getContrastRatio(rgb1, rgb2) { | |
const lum1 = getLuminance(...rgb1); | |
const lum2 = getLuminance(...rgb2); | |
const brightest = Math.max(lum1, lum2); | |
const darkest = Math.min(lum1, lum2); | |
return (brightest + 0.05) / (darkest + 0.05); | |
} | |
function isNearlyGrayscale(rgb) { | |
const range = Math.max(...rgb) - Math.min(...rgb); | |
return range <= 13; // 5% of 255 | |
} | |
// Helper functions | |
function getRGB(color) { | |
const rgb = color.match(/\d+/g); | |
return rgb ? rgb.map(x => parseInt(x)) : [255, 255, 255]; | |
} | |
function rgbToString(rgb) { | |
return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; | |
} | |
function hasBackgroundColor(element) { | |
const bgColor = window.getComputedStyle(element).backgroundColor; | |
return bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent'; | |
} | |
// Adjust color for better contrast with a given background color | |
// Gray colors target a higher contrast ratio | |
// Saturation is increased linearly as lightness decreases below 50 | |
function adjustColor(rgb, bgRgb, colorContrastRatio = 4.5, grayContrastRatio = 6.5) { | |
grayContrastRatio = grayContrastRatio || colorContrastRatio + 2; | |
let [h, s, l] = rgbToHsl(...rgb); | |
const isGray = isNearlyGrayscale(rgb); | |
const adjustmentRatio = isGray ? grayContrastRatio : colorContrastRatio; | |
const originalS = s; | |
const originalL = l; | |
while (l >= 0 && l <= 100) { | |
if (!isGray) { | |
// Saturation increases linearly as lightness decreases below 50 | |
// But all saturation is capped at 50% to prevent excessive colors | |
// (which looks bad on a light background) | |
s = Math.min(maxTextSaturation, originalS + Math.max(0, 50 - l)); | |
} | |
const adjustedRgb = hslToRgb(h, s, l); | |
const ratio = getContrastRatio(adjustedRgb, bgRgb); | |
if (ratio >= adjustmentRatio) { | |
return adjustedRgb; | |
} | |
l -= 1; | |
} | |
return rgb; // Return original if no suitable adjustment found | |
} | |
function hasTextNodeChild(el) { | |
let some = Array.prototype.some; | |
return el.childNodes.length > 0 && | |
some.call(el.childNodes, ch => ch.nodeType === Node.TEXT_NODE); | |
} | |
// State variables | |
let isBackgroundModeActive = false; | |
let isTextModeActive = false; | |
let observer = null; | |
// Main styling functions | |
function setLightBackground(element) { | |
if (hasBackgroundColor(element)) { | |
element.style.setProperty('background-color', rgbToString(backgroundColor), 'important'); | |
} | |
} | |
function setDarkTextOnLightBackground(element) { | |
const color = window.getComputedStyle(element).color; | |
const rgb = getRGB(color); | |
const bgRgb = backgroundColor; | |
const ratio = getContrastRatio(rgb, bgRgb); | |
if (ratio < targetContrastRatio) { | |
const adjustedRgb = adjustColor(rgb, bgRgb, targetContrastRatio); | |
element.style.setProperty('color', rgbToString(adjustedRgb), 'important'); | |
} | |
} | |
// Apply styles to all elements | |
function applyBackgroundToAllElements() { | |
document.querySelectorAll('*').forEach(setLightBackground); | |
} | |
function applyTextColorToAllElements() { | |
document.querySelectorAll('*').forEach(el => | |
hasTextNodeChild(el) && setDarkTextOnLightBackground(el)); | |
} | |
// Mutation observer | |
function createObserver() { | |
if (observer) return; // Observer already exists | |
observer = new MutationObserver((mutations) => { | |
mutations.forEach((mutation) => { | |
if (mutation.type === 'childList') { | |
mutation.addedNodes.forEach((node) => { | |
if (node.nodeType === Node.ELEMENT_NODE) { | |
applyActiveStylesToElement(node); | |
node.querySelectorAll('*').forEach(applyActiveStylesToElement); | |
} | |
}); | |
} | |
}); | |
}); | |
observer.observe(document.body, { childList: true, subtree: true }); | |
} | |
function applyActiveStylesToElement(element) { | |
if (isBackgroundModeActive) { | |
setLightBackground(element); | |
} | |
if (isTextModeActive) { | |
setDarkTextOnLightBackground(element); | |
} | |
} | |
// Keyboard event listener | |
document.addEventListener('keydown', function(e) { | |
if (e.ctrlKey && e.shiftKey) { | |
if (e.keyCode === 49) { // Key code for '1' | |
isBackgroundModeActive = true; | |
applyBackgroundToAllElements(); | |
createObserver(); | |
} else if (e.keyCode === 50) { // Key code for '2' | |
isTextModeActive = true; | |
applyTextColorToAllElements(); | |
createObserver(); | |
} | |
} | |
}); | |
// Initial state: DOM mutation observer is off, colors are not changed | |
//console.log('Script loaded. Use Ctrl+Shift+1 to toggle background modifications or Ctrl+Shift+2 to toggle text color modifications.'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment