Skip to content

Instantly share code, notes, and snippets.

@rondefreitas
Last active December 14, 2023 21:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rondefreitas/05ae8c35bac3e429ec9624255fd88a2f to your computer and use it in GitHub Desktop.
Save rondefreitas/05ae8c35bac3e429ec9624255fd88a2f to your computer and use it in GitHub Desktop.
Databricks Environment Banner User Script
// ==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;
}
})();
// ==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