nemesarial / Jira Subtask Candy

/*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+'&currentSubTaskSequence='+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);
    }
})();