Skip to content

Instantly share code, notes, and snippets.

@joemiller
Last active May 28, 2025 21:47
Show Gist options
  • Save joemiller/1a89e7a25585d9b631d37882d2c3f8b9 to your computer and use it in GitHub Desktop.
Save joemiller/1a89e7a25585d9b631d37882d2c3f8b9 to your computer and use it in GitHub Desktop.
Tampermonkey script to add "select all / deselect all' buttons to Buildkite's `input` modal when used with multiple selections
// ==UserScript==
// @name Buildkite Select All Button
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Adds a "Select All" button to Buildkite pipeline input modals (skips "!! APPLY ALL STACKS !!")
// @author You
// @match https://buildkite.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Function to add select all button to a modal
function addSelectAllButton(modal) {
// Check if we already added a button to this modal
if (modal.querySelector('.select-all-button-container')) {
return;
}
// Find all checkbox inputs in the modal
const checkboxes = modal.querySelectorAll('input[type="checkbox"]');
// Only proceed if we found checkboxes
if (checkboxes.length === 0) {
return;
}
// Find a good place to insert the button - look for the fieldset or form group
let insertTarget = null;
// Try to find the fieldset containing the checkboxes
const fieldset = modal.querySelector('fieldset');
if (fieldset) {
insertTarget = fieldset;
} else {
// Fallback: find the parent of the first checkbox
const firstCheckbox = checkboxes[0];
let parent = firstCheckbox.parentElement;
while (parent && parent !== modal) {
if (parent.querySelector('input[type="checkbox"]') &&
parent.querySelectorAll('input[type="checkbox"]').length === checkboxes.length) {
insertTarget = parent;
break;
}
parent = parent.parentElement;
}
}
if (!insertTarget) {
console.warn('Could not find suitable place to insert Select All button');
return;
}
// Create button container
const buttonContainer = document.createElement('div');
buttonContainer.className = 'select-all-button-container';
buttonContainer.style.cssText = 'margin: 10px 0; display: flex; gap: 10px;';
// Create Select All button
const selectAllBtn = document.createElement('button');
selectAllBtn.textContent = 'Select All';
selectAllBtn.type = 'button';
selectAllBtn.className = 'btn btn-default';
selectAllBtn.style.cssText = 'padding: 5px 10px; font-size: 13px;';
// Create Deselect All button
const deselectAllBtn = document.createElement('button');
deselectAllBtn.textContent = 'Deselect All';
deselectAllBtn.type = 'button';
deselectAllBtn.className = 'btn btn-default';
deselectAllBtn.style.cssText = 'padding: 5px 10px; font-size: 13px;';
// Function to check if checkbox should be skipped
function shouldSkipCheckbox(checkbox) {
// Check parent label
const parentLabel = checkbox.closest('label');
if (parentLabel && parentLabel.textContent.includes('!! APPLY ALL STACKS !!')) {
return true;
}
// Check for label with for attribute
if (checkbox.id) {
const associatedLabel = document.querySelector(`label[for="${checkbox.id}"]`);
if (associatedLabel && associatedLabel.textContent.includes('!! APPLY ALL STACKS !!')) {
return true;
}
}
// Check siblings and parent for text content
const parent = checkbox.parentElement;
if (parent && parent.textContent.includes('!! APPLY ALL STACKS !!')) {
return true;
}
return false;
}
// Add click handlers
selectAllBtn.addEventListener('click', function(e) {
e.preventDefault();
checkboxes.forEach(checkbox => {
if (!checkbox.checked && !shouldSkipCheckbox(checkbox)) {
checkbox.click();
}
});
});
deselectAllBtn.addEventListener('click', function(e) {
e.preventDefault();
checkboxes.forEach(checkbox => {
if (checkbox.checked && !shouldSkipCheckbox(checkbox)) {
checkbox.click();
}
});
});
// Add buttons to container
buttonContainer.appendChild(selectAllBtn);
buttonContainer.appendChild(deselectAllBtn);
// Insert before the fieldset/checkbox container
insertTarget.parentNode.insertBefore(buttonContainer, insertTarget);
}
// Function to check for modals
function checkForModals() {
// Look for unblock dialogs or modals containing checkboxes
const modals = document.querySelectorAll('[id*="unblock_dialog"], [id*="modal_dialog"], .modal-content, [role="dialog"]');
modals.forEach(modal => {
// Check if this modal has checkboxes
if (modal.querySelector('input[type="checkbox"]')) {
addSelectAllButton(modal);
}
});
// Also check for any standalone forms with multiple checkboxes
const forms = document.querySelectorAll('form');
forms.forEach(form => {
const checkboxes = form.querySelectorAll('input[type="checkbox"]');
if (checkboxes.length > 1) {
addSelectAllButton(form);
}
});
}
// Set up MutationObserver to watch for dynamic content
const observer = new MutationObserver(function(mutations) {
// Debounce to avoid excessive checks
clearTimeout(observer.timeout);
observer.timeout = setTimeout(checkForModals, 100);
});
// Start observing
observer.observe(document.body, {
childList: true,
subtree: true
});
// Initial check
checkForModals();
// Also check when URL changes (for single-page apps)
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
setTimeout(checkForModals, 500);
}
}).observe(document, {subtree: true, childList: true});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment