Enhance your Jira experience open any clicked issue in a new tab
// ==UserScript==
// @name Jira New Tab Flow: Open Tickets in a New Tab, Not in Popup
// @namespace http://tampermonkey.net/
// @version 2
// @description Enhance your Jira experience open any clicked issue in a new tab
// @icon https://static-00.iconduck.com/assets.00/jira-icon-512x512-kkop6eik.png
// @author Ameer Jamal
// @match https://*.atlassian.net/jira/*
// @match https://*.atlassian.com/jira/*
// @grant none
// @require https://unpkg.com/sweetalert@2/dist/sweetalert.min.js
// ==/UserScript==
(function() {
'use strict';
// Variables to store state information
let lastSelectedIssue = null; // The last issue that was selected
let isActive = true; // Whether the script is currently active
let escapeHitCounter = 0; // Count of consecutive "Ctrl" key presses
let justActivated = true; // Flag to indicate that the script was just activated
let button = null; // Button for toggling script activation
let buttonActiveText = 'Tickets Currently Open in New Tab'; // Text for when the script is enabled
let buttonInactiveText ='Tickets Currently Open in SideBar' ; // Text for when script is disabled
let Red = "#DE350B"
let Green = "#1F875A"
// Function to toggle the active state of the script
const toggleActiveState = () => {
isActive = !isActive; // Toggle active state
if (isActive) {
justActivated = true;
swal({ // Show a success alert when activated
title: "Clicking on a ticket now opens it in a new tab",
icon: "success",
buttons: false,
timer: 1500,
allowEscapeKey: false
});
} else {
lastSelectedIssue = null;
swal({ // Show an error alert when deactivated
title: "Clicking on a ticket opens in the sidebar as default",
icon: "error",
buttons: false,
timer: 1500,
allowEscapeKey: false
});
}
// Update the button text and color based on the current state
if (button) {
button.textContent = !isActive ? buttonInactiveText : buttonActiveText; // Set the button text content
button.style.color = 'white'; // Set the button text color
button.style.backgroundColor = !isActive ? Red : Green; // Set the button background color
}
// Reset the control key press counter
escapeHitCounter = 0;
};
// Function to check if a new issue is selected
const checkForSelectedIssue = () => {
if (!isActive) { // Skip if the script is not active
return;
}
// Get the currently selected issue
const urlParams = new URLSearchParams(window.location.search);
const urlDomain = window.location.hostname;
const selectedIssue = urlParams.get('selectedIssue');
// Open the selected issue in a new tab if it's different from the last one
if (selectedIssue && selectedIssue !== lastSelectedIssue && !justActivated) {
window.open(`https://${urlDomain}/browse/${selectedIssue}`, '_blank');
}
lastSelectedIssue = selectedIssue;
justActivated = false;
};
// Create a mutation observer to detect DOM changes
const observer = new MutationObserver(checkForSelectedIssue);
// Start observing the body of the page for changes in the child list and the subtree
observer.observe(document.querySelector('body'), {
childList: true,
subtree: true
});
// Add an event listener for the keyup event
window.addEventListener('keyup', (event) => {
// Check if the "Ctrl" key was pressed
if (event.key === 'Control') {
escapeHitCounter += 1;
// Toggle active state if the "Ctrl" key was pressed twice in a row
if (escapeHitCounter === 2) {
toggleActiveState();
}
// Reset the control key press counter after a short delay
setTimeout(() => {
escapeHitCounter = 0;
}, 300);
}
});
// Function to create the button for toggling script activation
const createButton = () => {
button = document.createElement('span'); // Create a new span element
button.setAttribute('class', 'css-1gd7hga'); // Set the class attribute
button.textContent = !isActive ? buttonInactiveText : buttonActiveText; // Set the button text content
button.style.color = 'white'; // Set the button text color
button.style.backgroundColor = !isActive ? Red : Green; // Set the button background color
button.style.borderRadius = '10px'; // Round the corners of the button
button.style.cursor = 'pointer'; // Change the cursor when hovering over the button
// Add styles to make it look more like a button
button.style.padding = "20px 15px"; // 20px top-bottom, 15px left-right
button.style.margin = "20px 0"; // 20px top-bottom, 0 left-right
button.style.cursor = "pointer"; // Change cursor to pointer on hover to indicate clickability
button.style.textDecoration = 'none'; // Remove underline from text
button.addEventListener('click', toggleActiveState); // Add an event listener for the click event
return button;
};
// Function to replace the "Learn more" button with our custom button
const replaceButton = () => {
const learnMoreButton = document.querySelector('[data-item-description=true] .css-8nt2sa'); // Get the "Learn more" button
if (learnMoreButton && learnMoreButton.textContent.includes('Learn more')) { // Check if the "Learn more" button exists
const newButton = createButton(); // Create our custom button
learnMoreButton.parentNode.replaceChild(newButton, learnMoreButton); // Replace the "Learn more" button with our custom button
}
};
replaceButton(); // Replace the "Learn more" button immediately when the script is loaded
// Create another mutation observer to detect when the "Learn more" button is added to the page
const buttonObserver = new MutationObserver(replaceButton);
// Start observing the body of the page for changes in the child list and the subtree
buttonObserver.observe(document.querySelector('body'), {
childList: true,
subtree: true
});
})();