Instantly share code, notes, and snippets.
Last active
December 16, 2024 23:38
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save kingspp/046da64aa7b97240ada6c4602b51283a to your computer and use it in GitHub Desktop.
Databricks Environment Banner | Credits @rondefreitas
This file contains hidden or 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 Databricks Environment Banner | |
// @source https://gist.githubusercontent.com/rondefreitas/05ae8c35bac3e429ec9624255fd88a2f | |
// @namespace https://gist.githubusercontent.com/rondefreitas/05ae8c35bac3e429ec9624255fd88a2f | |
// @updateURL https://gist.githubusercontent.com/rondefreitas/05ae8c35bac3e429ec9624255fd88a2f/raw/DBENV.user.js | |
// @downloadURL https://gist.githubusercontent.com/rondefreitas/05ae8c35bac3e429ec9624255fd88a2f/raw/DBENV.user.js | |
// @supportURL https://gist.githubusercontent.com/rondefreitas/05ae8c35bac3e429ec9624255fd88a2f#new_comment_field | |
// @version 0.50 | |
// @description Display Colored Banners for Databricks AWS Workspaces based on Subdomain | |
// @author Ron DeFreitas | |
// @match https://*.cloud.databricks.com/* | |
// @exclude https://accounts.cloud.databricks.com/* | |
// @exclude https://*/aad/* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=databricks.com | |
// @icon64URL https://www.google.com/s2/favicons?sz=64&domain=databricks.com | |
// @grant GM_addStyle | |
// @grant GM_getValue | |
// @grant GM_setValue | |
// @grant GM_registerMenuCommand | |
// @noframes | |
// @run-at document-end | |
// @license MIT | |
// | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
if (window.top != window.self) { | |
return; | |
} | |
const envColorMap = new Map(); | |
envColorMap.set("sandbox", "#855f16"); | |
envColorMap.set("development", "#108518"); | |
envColorMap.set("qa", "#5c606b"); | |
envColorMap.set("uac", "#1b4d9e"); | |
envColorMap.set("onboarding", "#1b4d9e"); | |
envColorMap.set("integration", "#5d1ca3"); | |
envColorMap.set("staging", "#5d1ca3"); | |
envColorMap.set("production", "#DD3333"); | |
const envMap = new Map(); | |
envMap.set("sandbox", ["alpha", "sandbox"]); | |
envMap.set("development", ["dev", "development"]); | |
envMap.set("qa", ["qa", "test"]); | |
envMap.set("uac", ["uac", "beta"]); | |
envMap.set("onboarding", ["onboarding", "onboard"]); | |
envMap.set("integration", ["int", "integration", "int1"]); | |
envMap.set("staging", ["stage", "staging", "stg"]); | |
envMap.set("production", ["prod", "production", "live"]); | |
function hexToRGB(h, asString) { | |
let r = 0, | |
g = 0, | |
b = 0; | |
// 3 digits | |
if (h.length == 4) { | |
r = "0x" + h[1] + h[1]; | |
g = "0x" + h[2] + h[2]; | |
b = "0x" + h[3] + h[3]; | |
// 6 digits | |
} else if (h.length == 7) { | |
r = "0x" + h[1] + h[2]; | |
g = "0x" + h[3] + h[4]; | |
b = "0x" + h[5] + h[6]; | |
} | |
r = +r | |
g = +g | |
b = +b | |
if (asString) { | |
return "rgb(" + r + ", " + g + ", " + b + ")"; | |
} | |
return [r, g, b]; | |
} | |
function RGBToHSL(r, g, b, adjustLightness) { | |
// Make r, g, and b fractions of 1 | |
r /= 255; | |
g /= 255; | |
b /= 255; | |
// Find greatest and smallest channel values | |
let cmin = Math.min(r, g, b), | |
cmax = Math.max(r, g, b), | |
delta = cmax - cmin, | |
h = 0, | |
s = 0, | |
l = 0; | |
// Calculate hue | |
// No difference | |
if (delta == 0) { | |
h = 0; | |
} | |
// Red is max | |
else if (cmax == r) { | |
h = ((g - b) / delta) % 6; | |
} | |
// Green is max | |
else if (cmax == g) { | |
h = (b - r) / delta + 2; | |
} | |
// Blue is max | |
else { | |
h = (r - g) / delta + 4; | |
} | |
h = Math.round(h * 60); | |
// Make negative hues positive behind 360° | |
if (h < 0) { | |
h += 360; | |
} | |
// Calculate lightness | |
l = (cmax + cmin) / 2; | |
// Calculate saturation | |
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); | |
// Multiply l and s by 100 | |
s = +(s * 100).toFixed(1); | |
l = +(l * 100).toFixed(1); | |
if (adjustLightness) { | |
l = Math.max(0, Math.min(100, l + adjustLightness)); | |
} | |
return "hsl(" + h + "," + s + "%," + l + "%)"; | |
} | |
function lightenDarkenColor(col, amt) { | |
if (col == null) return col; | |
if (col.startsWith("rgb(")) { | |
col = col.slice(4, -1).split(","); | |
} else { | |
if (!col.startsWith("#")) { | |
col = "#" + col; | |
} | |
col = hexToRGB(col); | |
} | |
return RGBToHSL(col[0], col[1], col[2], amt); | |
} | |
function normalizeColor(col) { | |
if (col == null) return col; | |
if (col.startsWith("#")) { | |
col = hexToRGB(col, true); | |
} | |
return col; | |
} | |
function waitForElm(selector) { | |
return new Promise(resolve => { | |
if (document.querySelector(selector)) { | |
return resolve(document.querySelector(selector)); | |
} | |
const observer = new MutationObserver(mutations => { | |
if (document.querySelector(selector)) { | |
resolve(document.querySelector(selector)); | |
observer.disconnect(); | |
} | |
}); | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
}); | |
} | |
let host = window.location.host; | |
let subdomain = host.split('.')[0]; | |
// databricks aws instances are usually formatted as such: customer-[workspace].cloud.databricks.com | |
// as such, we'll assume that we're using a naming pattern of the workspace like so: | |
// [environment](-[suffix]) where suffix is optional. We should be able come up with some common | |
// names for environments | |
let subdomainEnv = subdomain.toLowerCase().split("-")[1]; | |
let arr = Array.from(envMap, ([name, value]) => ({ | |
name, | |
value | |
})); | |
// which environment matches? | |
let matchEnv = arr.filter(env => { | |
return env.value.includes(subdomainEnv) | |
}); | |
let currentEnv = matchEnv.length > 0 ? matchEnv[0].name : "unknown" | |
console.log(`Databricks Environment Banner Loaded: Detected "${currentEnv}" Environment`); | |
// allow users to configure the showNameAtBottom | |
const showNameAtBottom = GM_getValue("showNameAtBottom", true); | |
const showNameButtonText = showNameAtBottom ? "Hide" : "Show"; | |
const menu_command_id_1 = GM_registerMenuCommand(showNameButtonText + " Environment Name", () => { | |
GM_setValue("showNameAtBottom", !showNameAtBottom); | |
if(confirm('Reload page for setting to take effect.')){ | |
location.reload(); | |
} | |
}); | |
// add colors by environment | |
let currentEnvColor = envColorMap.get(currentEnv); | |
if (currentEnvColor != null) { | |
GM_addStyle(` | |
div.databricks-subdomain-banner, nav.databricks-subdomain-banner { | |
background-color: ${normalizeColor(envColorMap.get(currentEnv))}; | |
border-color: ${lightenDarkenColor(envColorMap.get(currentEnv),-80)}; | |
} `); | |
// support 2023 design changes: | |
GM_addStyle(` | |
[data-testid="top-nav-account-menu-dropdown"] { | |
max-width: none !important; | |
} | |
`); | |
waitForElm('body :last-child').then((body) => {if(showNameAtBottom){ | |
GM_addStyle(` | |
body > div:first-of-type:before { | |
content: \"${currentEnv}\"; | |
pointer-events: none; | |
display: flex; | |
height: 98%; | |
width: 98%; | |
position: fixed; | |
flex-wrap: wrap; | |
z-index: 99999; | |
align-content: flex-end; | |
align-self: flex-end; | |
align-items: self-end; | |
justify-content: right; | |
font-weight: 750; | |
font-size: 700%; | |
opacity: 20%; | |
font-variant: all-small-caps; | |
margin: -30px; | |
} `); | |
}}); | |
waitForElm('div#toolbar :first-child').then((toolbar) => { | |
console.log("Toolbar classname old: " + toolbar.className); | |
var list = toolbar.className.split(" "); | |
list.push("databricks-subdomain-banner"); //unshift if at beginning / else push | |
console.log(list); | |
toolbar.className = list.join(" "); | |
console.log("Toolbar classname new: " + toolbar.className); | |
}); | |
// now deal with the favicon | |
// delete any excess favicons since chrome can ignore them | |
var allIcons = document.querySelectorAll("link[rel~='icon']"); | |
Array.from(allIcons).slice(1).forEach(icon => { | |
icon.remove(); | |
}); | |
var link = document.querySelector("link[rel~='icon']"); | |
if (!link) { | |
link = document.createElement("link"); | |
link.setAttribute("rel", "shortcut icon"); | |
document.head.appendChild(link); | |
} | |
var faviconUrl = link.href || window.location.origin + "/favicon.ico"; | |
function onImageLoaded() { | |
var canvas = document.createElement("canvas"); | |
canvas.width = this.width; | |
canvas.height = this.height; | |
var context = canvas.getContext("2d"); | |
context.drawImage(img, 0, 0); | |
context.globalCompositeOperation = "source-in"; | |
context.fillStyle = lightenDarkenColor(envColorMap.get(currentEnv), 10); | |
context.fillRect(0, 0, canvas.width, canvas.height); | |
context.fill(); | |
link.type = "image/x-icon"; | |
link.href = canvas.toDataURL(); | |
}; | |
var img = document.createElement("img"); | |
img.addEventListener("load", onImageLoaded); | |
img.src = faviconUrl; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment