- Install Tampermonkey for Chrome
- Click here
- Paste the snippet from
octopuscleanup.js
- You also might have to go to your extension settings (often found by pasting chrome://extensions/ into your url-bar) and select
Developer mode
in the top right corner
Last active
March 10, 2025 07:30
-
-
Save SanderBreivik/30cfd5b378c5e8db36acf76c4e33bf1b to your computer and use it in GitHub Desktop.
Octopus App Version Cleanup
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 Version Cleanup for Octopus | |
// @namespace http://tampermonkey.net/ | |
// @version 0.1 | |
// @description Remove equal versions from AcosSky production and preproduction rows | |
// @author You | |
// @match https://octopus.acos.no/app | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=acos.no | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// Create and style the wrapper element | |
function createWrapper() { | |
const wrapper = document.createElement('div'); | |
wrapper.classList.add("tm-wrapper"); | |
wrapper.style.cssText = 'width: 100%; padding: 0 1.5rem; position: fixed; display: flex; gap: 1rem; top: 0px; justify-content: flex-end; align-items: center; height: 35px; background: #1f303f;'; | |
document.body.appendChild(wrapper); | |
return wrapper; | |
} | |
// Create the range input for opacity control | |
function createOpacityControl(wrapper, defaultValue = 0.3) { | |
const rangeContainer = document.createElement("div"); | |
rangeContainer.id = 'tm-range-container'; | |
rangeContainer.style.display = 'none'; | |
rangeContainer.style.justifyContent = 'center'; | |
rangeContainer.style.gap = "0.5rem"; | |
const range = document.createElement('input'); | |
const label = document.createElement('label'); | |
range.type = 'range'; | |
range.id = 'opacity-range'; | |
range.min = 0; | |
range.max = 1; | |
range.step = 0.1; | |
range.value = defaultValue; | |
label.innerText = "Opacity:"; | |
label.htmlFor = range.id; | |
rangeContainer.appendChild(label); | |
rangeContainer.appendChild(range); | |
wrapper.appendChild(rangeContainer); | |
return range; | |
} | |
// Create the checkbox for toggling visibility | |
function createVisibilityToggle(wrapper, defaultValue = true) { | |
const container = document.createElement('div'); | |
container.style.display = "flex"; | |
container.style.justifyContent = "center"; | |
container.style.alignItems = "center"; | |
container.style.gap = "0.5rem"; | |
const checkbox = document.createElement('input'); | |
const slider = document.createElement('label'); | |
const label = document.createElement('label'); | |
label.htmlFor = 'tm-checkbox'; | |
label.textContent = 'Hide deployed versions'; | |
checkbox.type = 'checkbox'; | |
checkbox.id = 'tm-checkbox'; | |
checkbox.className = '' | |
checkbox.checked = defaultValue; | |
slider.className = 'switch' | |
const span = document.createElement('span'); | |
span.className = 'slider round'; | |
slider.appendChild(checkbox); | |
slider.appendChild(span); | |
container.appendChild(slider); | |
container.appendChild(label); | |
wrapper.appendChild(container); | |
return checkbox; | |
} | |
function createShowInProgressToggle(wrapper, defaultValue = true) { | |
const container = document.createElement('div'); | |
container.id = 'tm-progress-toggle'; | |
container.style.display = "flex"; | |
container.style.justifyContent = "center"; | |
container.style.alignItems = "center"; | |
container.style.gap = "0.5rem"; | |
const checkbox = document.createElement('input'); | |
const slider = document.createElement('label'); | |
const label = document.createElement('label'); | |
label.htmlFor = 'tm-checkbox-2'; | |
label.textContent = 'Show in progress processes'; | |
checkbox.type = 'checkbox'; | |
checkbox.id = 'tm-checkbox-2'; | |
checkbox.className = '' | |
checkbox.checked = defaultValue; | |
slider.className = 'switch' | |
const span = document.createElement('span'); | |
span.className = 'slider round'; | |
slider.appendChild(checkbox); | |
slider.appendChild(span); | |
container.appendChild(slider); | |
container.appendChild(label); | |
wrapper.appendChild(container); | |
return checkbox; | |
} | |
// Update elements' visibility based on the current filter settings | |
function updateElementsVisibility(opacity, isChecked, showInProgress) { | |
console.log('updating'); | |
var opacityRangeContainer = document.getElementById("tm-range-container"); | |
opacityRangeContainer.style.display = isChecked ? 'flex' : 'none'; | |
var showProgressToggle = document.getElementById("tm-progress-toggle"); | |
showProgressToggle.style.display = isChecked ? 'flex' : 'none'; | |
const containerElements = document.querySelectorAll('[class^="style-module_internalLink"]'); | |
const spans = Array.from(containerElements).map(c => c.querySelectorAll('span')) | |
const versionSpans = spans.filter(s => s.length >= 3); | |
const elements = Array.from(versionSpans.map(v => v.length == 3 ? v[1] : v[3])); | |
const uniqueVersions = new Set(); | |
const duplicateVersions = []; | |
elements.forEach(element => { | |
const versionText = element.innerText; | |
if (uniqueVersions.has(versionText)) { | |
duplicateVersions.push(versionText); | |
} else { | |
uniqueVersions.add(versionText); | |
} | |
}); | |
elements.forEach(element => { | |
const isDuplicate = duplicateVersions.includes(element.innerText); | |
const isPossiblyAFakePositive = isDuplicate && duplicateVersions.filter(x => element.innerText == x).length > 1; | |
const tableParent = element.closest('[class^="style-module_internalLink"]'); | |
const isFailed = tableParent.querySelectorAll('[class^="fa-solid fa-xmark"]').length != 0; | |
const isInProgress = tableParent.querySelectorAll('[class^="fa-solid fa-spinner"]').length != 0; | |
if (tableParent) { | |
tableParent.style.opacity = isDuplicate && isChecked ? opacity : '1'; | |
} | |
if (tableParent && isPossiblyAFakePositive) { | |
tableParent.style.opacity = isChecked ? '0.5' : '1'; | |
tableParent.style.background = 'rebeccapurple' | |
} | |
if (tableParent && isFailed ) { | |
tableParent.style.opacity = isChecked ? '0.5' : '1'; | |
tableParent.style.background = 'blue' | |
} | |
if (tableParent && isInProgress && showInProgress) { | |
tableParent.style.opacity = isChecked ? '0.5' : '1'; | |
tableParent.style.background = 'orange' | |
} | |
}); | |
} | |
function saveSettings(opacity, visibility, showInProgress) { | |
localStorage.setItem('opacity', opacity.toString()); | |
localStorage.setItem('visibility', visibility.toString()); | |
localStorage.setItem('showInProgress', showInProgress) | |
} | |
function getSavedSettings() { | |
return { | |
opacity: localStorage.getItem('opacity') || '0.3', // Default value | |
visibility: localStorage.getItem('visibility') === 'true', // Convert string to boolean | |
showInProgress: localStorage.getItem('showInProgress') === 'true' | |
}; | |
} | |
function init() { | |
const app = document.getElementById("app"); | |
if (!app) { | |
console.error("App element not found"); | |
return; | |
} | |
app.style.transform = 'translateY(35px)'; | |
const wrapper = createWrapper(); | |
const savedSettings = getSavedSettings(); | |
addSliderStyles(); | |
const opacityRange = createOpacityControl(wrapper, savedSettings.opacity); | |
const showInProgressToggle = createShowInProgressToggle(wrapper, savedSettings.showInProgress); | |
const visibilityCheckbox = createVisibilityToggle(wrapper, savedSettings.visibility); | |
// Update elements immediately based on saved settings | |
updateElementsVisibility(savedSettings.opacity, savedSettings.visibility); | |
// Save new settings when they change | |
opacityRange.addEventListener('input', () => { | |
saveSettings(opacityRange.value, visibilityCheckbox.checked, showInProgressToggle.checked); | |
updateElementsVisibility(opacityRange.value, visibilityCheckbox.checked, showInProgressToggle.checked); | |
}); | |
visibilityCheckbox.addEventListener('change', () => { | |
saveSettings(opacityRange.value, visibilityCheckbox.checked, showInProgressToggle.checked); | |
updateElementsVisibility(opacityRange.value, visibilityCheckbox.checked, showInProgressToggle.checked); | |
}); | |
showInProgressToggle.addEventListener('change', () => { | |
saveSettings(opacityRange.value, visibilityCheckbox.checked, showInProgressToggle.checked); | |
updateElementsVisibility(opacityRange.value, visibilityCheckbox.checked, showInProgressToggle.checked); | |
}); | |
observeRouteChanges(() => updateElementsVisibility(opacityRange.value, visibilityCheckbox.checked, showInProgressToggle.checked)); | |
} | |
function addSliderStyles() { | |
const style = document.createElement("style"); | |
style.textContent = '.switch { position: relative; display: inline-block; width: 30px; height: 17px;}.switch input { opacity: 0; width: 0; height: 0;}.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; -webkit-transition: .4s; transition: .4s;}.slider:before { position: absolute; content: ""; height: 13px; width: 13px; left: 2px; bottom: 2px; background-color: white; -webkit-transition: .4s; transition: .4s;}input:checked + .slider { background-color: #2196F3;}input:focus + .slider { box-shadow: 0 0 1px #2196F3;}input:checked + .slider:before { -webkit-transform: translateX(26px); -ms-transform: translateX(13px); transform: translateX(13px);}/* Rounded sliders */.slider.round { border-radius: 34px;}.slider.round:before { border-radius: 50%;}' | |
document.body.appendChild(style); | |
} | |
function observeRouteChanges(onRouteChange) { | |
const observer = new MutationObserver((mutations, obs) => { | |
// Assuming route change will cause relevant mutations, adjust as necessary | |
onRouteChange(); | |
}); | |
observer.observe(document, { | |
childList: true, | |
subtree: true | |
}); | |
} | |
init(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment