NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
/*eslint semi: ["error", "always"]*/ // ==UserScript== // @name Jira Subtask Candy // @namespace http://codeonfire.space/ // @version 1.1.0 // @description Adds User and Status filters to the jira subtask menu. Persistent via localstorage. Reorder Subtasks // @author Mark Holtzhausen <nemesarial@gmail.com> // @match https://*.atlassian.net/browse/* // @icon http://imgur.com/download/UIRWabs // @require https://openuserjs.org/src/libs/nemesarial/DOMHelper.js // @grant none // ==/UserScript== // Reorder Subtasks (function(){ var currentTicketId=DOM.query('#key-val').getAttribute('rel'); var issueOrder,newIssueOrder=[]; jQuery('#issuetable>tbody').sortable({ start:function(){issueOrder=DOM.queryAll('#issuetable>tbody>tr').map(elem=>elem.id);}, stop:function(e,ui){ newIssueOrder=DOM.queryAll('#issuetable>tbody>tr').map(elem=>elem.id); jQuery.ajax({url:'/jira/secure/MoveIssueLink.jspa?id='+currentTicketId+'¤tSubTaskSequence='+issueOrder.indexOf(ui.item.context.id)+'&subTaskSequence='+newIssueOrder.indexOf(ui.item.context.id)} );} }); })(); // Subtask Filtering (function() { let hasSubtasks = DOM.query('ul#subtask-view-options') !== null; if (hasSubtasks) { let flag = '#issuetable tr td'; function install() { let $all = DOM.queryAll('.subtask-table-container .issuerow'); let $menu = DOM.query('#subtask-view-options'); let aStatus = localStorage.getItem('myStatusses') ? localStorage.getItem('myStatusses').split(',') : []; let aAssignee = localStorage.getItem('myAssignees') ? localStorage.getItem('myAssignees').split(',') : []; function toggle(evt) { let $a = evt.target; let $li = $a.parentNode; if ($a.hasAttribute('data-status')) { let status = $a.getAttribute('data-status'); let pos = aStatus.indexOf(status); if (pos >= 0) { aStatus.splice(pos, 1); // $a.classList.remove('aui-checked'); } else { aStatus.push(status); // $a.classList.add('aui-checked'); } } else { let assignee = $a.getAttribute('data-assignee'); let pos = aAssignee.indexOf(assignee); if (pos >= 0) { aAssignee.splice(pos, 1); // $a.classList.remove('aui-checked'); } else { aAssignee.push(assignee); // $a.classList.add('aui-checked'); } } localStorage.setItem('myAssignees', aAssignee.join(',')); localStorage.setItem('myStatusses', aStatus.join(',')); render(); } function show($list) { $list.forEach(e => e.style.display = 'table-row'); } function hide($list) { $list.forEach(e => e.style.display = 'none'); } function getAssignee($li) { let $assignee = DOM.query('td.assignee a.user-hover', $li); return $assignee ? $assignee.getAttribute('rel') : 'Unassigned'; } function getStatus($li) { return status = DOM.query("td.status > span.jira-issue-status-lozenge", $li).textContent; } function render() { DOM.queryAll('a[data-assignee]', $menu).forEach(e => { let assignee = e.getAttribute('data-assignee'); if (aAssignee.indexOf(assignee) >= 0) { e.classList.add('aui-checked'); } else { e.classList.remove('aui-checked'); } }); DOM.queryAll('a[data-status]', $menu).forEach(e => { let status = e.getAttribute('data-status'); if (aStatus.indexOf(status) >= 0) { e.classList.add('aui-checked'); } else { e.classList.remove('aui-checked'); } }); if (aAssignee.length + aStatus.length > 0) { $all.forEach(e => { let canShow = true; let assignee = getAssignee(e); let status = getStatus(e); canShow = canShow && (aAssignee.length > 0 ? aAssignee.indexOf(assignee) >= 0 ? true : false : true); canShow = canShow && (aStatus.length > 0 ? aStatus.indexOf(status) >= 0 ? true : false : true); if (canShow) { show([e]); } else { hide([e]); } }); } else { show($all); } } if ($menu instanceof HTMLElement) { DOM.query(flag).setAttribute('data-jira-spice', 'true'); let statusses = []; let assignees = []; $all.forEach(e => { let status = getStatus(e); let assignee = getAssignee(e); if (statusses.indexOf(status) < 0) statusses.push(status); if (assignees.indexOf(assignee) < 0) assignees.push(assignee); }); let idx = 0; statusses.forEach(status => { let $a = DOM.create('a', 'aui-list-checked aui-list-item-link', {href: 'javascript:;', 'data-status': status, title: `Show all ${status} tickets.`}, `Is ${status}`); let $li = DOM.create('li', 'aui-list-item', {style: idx++ === 0 ? 'border-top: 1px solid #ccc' : ''}, $a); $menu.appendChild($li); $li.addEventListener('click', toggle); }); idx = 0; assignees.forEach(assignee => { let $a = DOM.create( 'a', 'aui-list-checked aui-list-item-link', {href: 'javascript:;', 'data-assignee': assignee, title: `Show all tickets belonging to ${assignee} tickets.`}, `For ${assignee}` ); let $li = DOM.create('li', 'aui-list-item', {style: idx++ === 0 ? 'border-top: 1px solid #ccc' : ''}, $a); $menu.appendChild($li); $li.addEventListener('click', toggle); }); render(); } } window.setInterval(() => { if (!DOM.query(flag).hasAttribute('data-jira-spice')) install(); }, 200); } })();