NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Pivotal Story ID Name Prefix // @namespace https://openuserjs.org/meta/nharward/pivotal // @description Adds the story ID in front of the story name like you'd expect // @author Nathaniel Harward // @include https://www.pivotaltracker.com/* // @copyright 2019, Nathaniel Harward // @license MIT; https://opensource.org/licenses/MIT // @version 1.0.1 // ==/UserScript== // ==OpenUserJS== // @author nharward // ==/OpenUserJS== var STORY_ID_PREFIX_CLASS = 'story_id_prefix'; var getAncestorAttributeValue = function (node, attributeName) { var attributeValue = null; var ancestor = node; while (ancestor && !ancestor.hasAttribute(attributeName)) { ancestor = ancestor.parentNode; } if (ancestor) { attributeValue = ancestor.getAttribute(attributeName); } return attributeValue; }; var hasStoryIDPrefix = function (node) { var firstChild = node.firstChild; return firstChild && firstChild.nodeType == Node.ELEMENT_NODE && firstChild.nodeName == 'SPAN' && firstChild.getAttribute('class') == STORY_ID_PREFIX_CLASS; }; var pivotalStoryNameNode = function (node) { return node.nodeType == Node.ELEMENT_NODE && node.nodeName == 'SPAN' && node.getAttribute('class') == 'story_name'; }; var pivotalEpicNameNode = function (node) { return node.nodeType == Node.ELEMENT_NODE && node.nodeName == 'SPAN' && node.getAttribute('class') == 'tracker_markup' && node.parentNode && node.parentNode.className == 'name epic_name'; }; var addIDToName = function (node) { var storyID = getAncestorAttributeValue(node, 'data-id'); if (storyID) { var spanElement = document.createElement('span'); spanElement.setAttribute('class', STORY_ID_PREFIX_CLASS); spanElement.appendChild(document.createTextNode(storyID + ' - ')); node.insertBefore(spanElement, node.firstChild); } }; var handleNode = function (node) { if ((pivotalStoryNameNode(node) || pivotalEpicNameNode(node)) && !hasStoryIDPrefix(node)) { addIDToName(node); } if (node.hasChildNodes()) { node.childNodes.forEach(handleNode); } }; var mutationCallback = function (mutationsList, observer) { mutationsList.forEach((mutationRecord) => { if (mutationRecord.addedNodes) { mutationRecord.addedNodes.forEach(handleNode); } }); } new MutationObserver(mutationCallback).observe(document.body, { childList: true, subtree: true });