NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Colorful Google Docs FavIcon // @namespace https://openuserjs.org/users/ewino // @version 0.95 // @description Color the icon on a Google Docs (document/spreadsheet/presentation) tab according to the specific file (different colors for different files). // @copyright 2023, ewino (https://openuserjs.org/users/ewino) // @author ewino // @license MIT // @match https://docs.google.com/document/d/* // @match https://docs.google.com/spreadsheets/d/* // @match https://docs.google.com/presentation/d/* // @icon https://www.google.com/s2/favicons?domain=google.com // @grant none // ==/UserScript== // ==OpenUserJS== // @author ewino // ==/OpenUserJS== /* jshint esversion: 6 */ (function () { 'use strict'; const globals = {}; const percentOfIconColored = 60; const persistColor = (color) => localStorage.setItem('color-' + location.pathname, color); const fetchPersistedColor = () => localStorage.getItem('color-' + location.pathname); const getMostCommonColorHex = (imageData) => { const colorCounts = new Map(); let mostCommonColor = null; let mostCommonColorCount = 0; for (var i = 0; i < imageData.data.length; i += 4) { var pixelColor = rgbToHex([imageData.data[i], imageData.data[i + 1], imageData.data[i + 2]]); if (!colorCounts.has(pixelColor)) { colorCounts.set(pixelColor, 0); } const newCount = colorCounts.get(pixelColor) + 1; colorCounts.set(pixelColor, newCount); if (newCount > mostCommonColorCount) { mostCommonColorCount = newCount; mostCommonColor = pixelColor; } } return mostCommonColor; }; const replaceColorHex = (imageData, originalColorHex, newColorHex) => { const originalColorRgb = hexToRgb(originalColorHex); const newColorRgb = hexToRgb(newColorHex); // Examine every pixel, change any old rgb to the new-rgb for (var i = 0; i < imageData.data.length; i += 4) { // is this pixel the old rgb? if (imageData.data[i] == originalColorRgb[0] && imageData.data[i + 1] == originalColorRgb[1] && imageData.data[i + 2] == originalColorRgb[2] ) { // change to your new rgb imageData.data[i] = newColorRgb[0]; imageData.data[i + 1] = newColorRgb[1]; imageData.data[i + 2] = newColorRgb[2]; } } }; const rgbToHex = (rgb) => '#' + ((rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).padStart(6, '0'); const hexToRgb = (hex) => [parseInt(hex.substring(1, 3), 16), parseInt(hex.substring(3, 5), 16), parseInt(hex.substring(5, 7), 16)]; const hashStringToColor = (str) => { var hash = 0; if (str.length === 0) return hash; for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); hash = hash & hash; } hash = hash % Math.pow(256, 3); var rgb = [0, 0, 0]; for (let i = 0; i < 3; i++) { var value = (hash >> (i * 8)) & 255; rgb[i] = value; } console.log('hash', hash, rgb); return rgbToHex(rgb); }; const refreshCustomColor = (newCustomColor) => { globals.customColor = newCustomColor; globals.canvasCtx.drawImage(globals.origFavIconImg, 0, 0); const startX = 0; const endX = globals.origFavIconImg.width * percentOfIconColored / 100; const imageData = globals.canvasCtx.getImageData(startX, 0, endX, globals.origFavIconImg.height); replaceColorHex(imageData, globals.baseColor, newCustomColor); globals.canvasCtx.putImageData(imageData, startX, 0); lnk.href = globals.canvas.toDataURL(); globals.cornerEl.style.backgroundColor = newCustomColor; console.log('tab color for favicon is', newCustomColor); }; const init = (img) => { const canvas = document.createElement('canvas'); canvas.height = img.height; canvas.width = img.width; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); globals.origFavIconImg = img; globals.canvas = canvas; globals.canvasCtx = ctx; globals.baseColor = getMostCommonColorHex(ctx.getImageData(0, 0, img.width, img.height)); globals.customColor = 'transparent'; var colorPicker = document.createElement('input'); colorPicker.type = 'color'; colorPicker.style = 'display: none;'; colorPicker.addEventListener('change', (e) => { persistColor(colorPicker.value) refreshCustomColor(colorPicker.value); }); var corner = document.createElement('div'); corner.setAttribute('style', 'width: 20px; height: 20px; position: absolute; top: -10px; right: -10px; transform: rotate(45deg); z-index: 100000; cursor: pointer;'); corner.setAttribute('title', 'Change FavIcon Color'); document.body.appendChild(corner); document.body.appendChild(colorPicker); corner.addEventListener('click', () => { colorPicker.focus(); colorPicker.value = globals.customColor; colorPicker.click(); }); globals.cornerEl = corner; refreshCustomColor(fetchPersistedColor() || hashStringToColor(location.pathname)); } const lnk = document.querySelector('link[rel*="icon"]'); if (lnk) { const img = new Image(); img.crossOrigin = "anonymous"; img.src = lnk.href; img.onload = function () { init(img); } } })();