VersionOne Super Title Always on Cards

try to take over the world!

// ==UserScript==
// @name         VersionOne Super Title Always on Cards
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description  try to take over the world!
// @author       You
// @match        https://your/instance/TeamRoom.mvc/Show/*
// @grant        GM_addStyle
// @require      https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery.countdown/2.2.0/jquery.countdown.js
// ==/UserScript==
(function() {
    'use strict';
    const v1Instance = 'instance';
    GM_addStyle(`
/*
 *
 * Team Room Top Bar(s)
 *
 */

.teamroom #top-bar, /* main navigation */
.teamroom .scroll, /* Panels */
.teamroom .caption, /* Storyboard refresh */
.teamroom .hide-header, /* collapse */
.teamroom .filter-by-anything, /* filters */
.teamroom .KanbanBoard .titlebar, /* Highlight owner */
.teamroom .navigation
{
    display:none !important;
}

/* teammebers and charts */
.teamroom .collapsed-header + .header {
    height: 0;
    overflow: hidden;
}

/* storyboard position */
.teamroom .panel-wrapper {
	top: 0 !important;
    margin-left: 42px;
}

/* sticky header position */
.teamroom .sticky-group-by-header.position-absolute {
}

/* gap */
.teamroom .board {
    margin: 0 !important;
}

/* easy S-1234 select */
.teamroom .number {
    user-select: all;
    cursor: pointer;
}

body.ultimate,
.teamroom table,
.teamroom .window {
   background-color: #162228 !important;
}

.teamroom .taskboard table {
    border-spacing: 4px !important;
}
/* Column */
.teamroom .status,
.rollup-status {
    background: #263238 !important
}

.rollup-status {
    height: 12px !important;
}

/* Add icon at bottom of column */
.teamroom .cell-add {
    background-image: none !important;
}
.teamroom .cell-add:before {
	content: '+';
    color: #eceff1;
    font-size: 3rem;
    cursor: pointer;
}

/* swimlanes */
.teamroom .sticky-group-by-header {
    border-color: #162228 !important;
}
/* swimlanes */
.teamroom .group-by-header td {
    background-color: #162228 !important;
    border-top-color: #162228 !important;
    color: white !important;
}

/*
 *
 * Card
 *
 */
.more-links {
    display: flex;
    align-items: center;
}

.more-links > *, .more-links img {
    width: 20px;
    height: 20px;
    min-width: 20px;
    min-height: 20px;
    max-width: 20px;
    max-height: 20px;
}

.more-links > * {
 	margin: 5px;
}

.teamroom .story-card-container {
    margin-bottom: 15px !important;
    position: relative;
}

.teamroom .story-card {
    box-shadow: none !important;
    border-bottom: none !important;
    background-color: #37474f !important;
}

.teamroom .story-card  + .bottom-card-tab {

    position: absolute;
    bottom: -1px;
    left: 0;
    right: 0;
    margin: 0 !important;
    padding: 0 !important;
    height: 2px;
}

.teamroom .story-card + .bottom-card-tab .multibar {
    width: 100%;
    height: 2px;
    border-radius: 0;
    overflow: hidden;
    position: absolute;
    top: 0;
}

.teamroom .story-card  + .bottom-card-tab img {
	width: 100% !important;
    height: 2px;
    position: absolute;
    top: 0;
}

.teamroom .story-card .asset-hover,
.teamroom .story-card .bottom-card-tab,
.teamroom .group-by-header td,
.teamroom .story-card .tag-holder a,
.teamroom .story-card .story-card-actions,
.teamroom .story-card .aging {
    color: #eceff1 !important;
}

.teamroom .story-card + .bottom-card-tab {
    background-color: #37474f !important;
    border-bottom: none !important;
    box-shadow: 0 2px 1px -1px rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 1px 3px 0 rgba(0,0,0,.12) !important;
}

.teamroom .story-card .identity-right,
.teamroom .story-card .identity-left .number {
    background-color: #162228 !important;
}

.teamroom .story-card .identity-left .number:before,
.teamroom .story-card .identity-right:before {
    border-color: #162228 #162228 transparent transparent !important;
}

.teamroom .story-card .identity-left .number:after {
 	border-color: #162228 transparent transparent #162228 !important;
}

.teamroom .story-card .tag-holder {
    background-color: #263238 !important;
}
/*
 *
 * Details Modal
 *
 */

/* reposition details modal */
.inline-asset-detail {
    top: 0;
    bottom: 0;
    left: 50px;
    right: 0;
}

.inline-asset-detail .toolbar {
    height: 0px;
    overflow: hidden;
}

.inline-asset-edit .toolbar .title-id {
	position: fixed;
    top: 0px;
    left: 76px;
    z-index: 2;
    color: black !important;
}

.asset-summary .toolbar h2 {
	user-select: all !important;
}

.asset-summary .toolbar .icon {
	visibility: hidden !important;
}

.side-panel {
    top: 0 !important;
}

/* color action dropdon */
.inline-asset-detail .asset-actions.tab-button {
    padding: 0 !important;
    background-color: #00a9e0 !important;
}

/* quick action "edit" button */
.inline-asset-detail .tab-buttons .quick-action-text {
    display: none;
}

.inline-asset-detail .action-menu-button {
    width: 100%;
    height: 28px;
    display: flex;
    justify-content: center;
    align-items: center;
}

/* Unwanted fields */
.inline-asset-detail .custom-fields,
.inline-asset-detail .layout-left .other-extended-fields,
.inline-asset-detail .tabbar .tabs {
    display: none !important;
}

/* open in tab link */
.inline-asset-detail .pop-out {
    position: fixed;
    bottom: 0;
    left: 50px;
    width: 42px;
    height: 42px;
    margin: 0;
    z-index: 2;
    background-size: auto;
    background-position: center;
    background-color: darkslategrey;
}

/*Grid*/
.grid .gridtable [_v1_updater="Test.Name"],
.grid .gridtable [_v1_updater="Story.Name"],
.grid .gridtable [_v1_updater="Epic.Name"]
{
    width: 35em !important;
}

/* shift board over to show team members */
.teamroom .window {
   width: calc(100% - 42px) !important;
}

/* All members filter */
.teamroom .mascot-wrapper {
 	position: fixed;
    left: 4px;
    top: 24px;
    width: 32px !important;
    height: 32px !important;
}

/* single member filter */
.teamroom .mascot-wrapper img {
    width: 32px !important;
    height: 32px !important;
}

.teamroom .persona-filters .owner-list ul {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 42px;
    margin: 0;
    position: fixed;
    top: 64px;
    left: 0;
    bottom: 0;
    z-index: 100;
    overflow: hidden;
}

.teamroom .persona-filters .owner-list ul:hover {
 	overflow: auto !important;
}

.teamroom .persona-filters .owner-list ul li {
	margin-right: 0 !important;
}

/* scollbars */
.teamroom .board {
    height: 100vh !important;
}

.teamroom .window::-webkit-scrollbar,
.teamroom .persona-filters .owner-list ul::-webkit-scrollbar {
    width: 4px;
    height: 0;
}

.teamroom .panel-wrapper::-webkit-scrollbar {
 	height: 4px;
}

.teamroom .panel-wrapper::-webkit-scrollbar-thumb,
.teamroom .window::-webkit-scrollbar-thumb,
.teamroom .persona-filters .owner-list ul::-webkit-scrollbar-thumb {
    background-color: #eceff1;
}

.teamroom .persona-filters .owner-list ul .name {
    display: none !important;
}

.teamroom .panel-wrapper .panels ol li {
 	border: none !important;
}


/*
 * No data state
*/
.teamroom .no-results {
    color: white;
    background: #162228;
    height: 100vh;
    top: -28px;
    position: absolute;
    right: 0;
    left: 0;
    height: 90vh;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 36px;
}


/**

**/
.taskboard {
    table-layout: fixed;
}

.rollup-status {
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    cursor: pointer;
}

/**
Toggle between list view and board view
**/

.list-view {
    width: 100%;
    position: fixed;
    top: 0;
    left: 45px;
    right: 0;
    bottom: 0;
    background: #162228;
    z-index: 9999;
    color: white;
    overflow-y: scroll;
}

.flex-row {
    display: flex;
    flex-direction: row;
}

.flex-column {
    display: flex;
    flex-direction: column;
}

.list-view-item {
    border-bottom: 1px solid white;
}

.list-view-item .icon {
 	width: 32px;
    height: 32px;
    margin: 8px;
    margin-top: 0;
}

.list-view-item .icon.Story {
    background: green;
}

.list-view-item .icon.Defect {
    background: red;
}

.list-view a {
    color: white !important;
}

.list-view > * {
    padding: 8px;
}

.list-view .number {
    user-select: all;
    cursor: pointer;
}

.toggleListView {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 99999999;
    background: green;
    border-radius: 50%;
    width: 30px;
    height: 30px;
}

.release-info {
    margin-left: auto;
    margin-right: auto;
    padding: 8px;
    border-color: black;
    text-align: center;
    font-weight: bold;
}

.release-info span {
    background-color: yellow;
    padding: 8px;
    border-color: yellow;
}`);
    const paperIcon = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Computer_icon_for_Dropbox_Paper_app.png/480px-Computer_icon_for_Dropbox_Paper_app.png';

    const queryV1 = `/${v1Instance}/query.v1`;

    $(document).arrive('.taskboard.sticky-header', function() {
        const taskboard = $(this);
        const query =`
                from: Milestone
                select:
                - Name
                - Date
                - Description
                where:
                 Scope.Name: VersionOne
                sort:
                - -Date
                page:
                 start: 0
                 size: 1`;
        try {
            axios.post(queryV1, query).then(resp => {
                const milestone = resp.data[0][0];
                const desc = milestone.Description;
                const releaseDate = moment(milestone.Date).format("MM/DD/YYYY");
                const releaseInfo = `${desc} ${milestone.Name}: <span id='release-counter'></span>`;
                taskboard.parent().prepend(`<div class='release-info'><span>${releaseInfo}</span></div>`);
                $('#release-counter').countdown(releaseDate).on('update.countdown', function(event) {
                    var $this = $(this).html(event.strftime(''
                                                            + '<span>%-w</span> week%!w '
                                                            + '<span>%-d</span> day%!d '
                                                            + '<span>%H</span> hr '
                                                            + '<span>%M</span> min '
                                                            + '<span>%S</span> sec'));
                });
            });
        } catch (e) {
        }
    });

    $(document).arrive(".story-card", function() {
        const story = $(this);
        const numberEl = story.find('.number');
        const number = numberEl.text().trim();
        axios.post(queryV1, {
            "from": "Workitem",
            "where": {
                "Number": number
            },
            "select": [
                "Super.Name",
                "Super.Number",
                {
                    "from": "Super.Attachments",
                    "where": {
                        "Name": "Avatar"
                    },
                    "select": [
                        "Filename"
                    ]
                },
                {
                    "from": "Super.Links",
                    "where": {
                        "Name": "Paper Doc"
                    },
                    "select": ["URL"]
                }
            ]
        })
        .then(function (response) {
            const resp = response.data[0][0];
            let superName = resp['Super.Name'];
            let superNumber = resp['Super.Number'];
            let img = '';
            let dblink = '';
            let paperUrl;
            try {
                const avatar = resp['Super.Attachments'][0];
                const attachmentId = avatar._oid.substring("Attachment:".length);
                const filename = avatar.Filename;
                var url = `/${v1Instance}/attachment.img/${attachmentId}/${filename}`;
                img = `<a href='/${v1Instance}/assetdetail.v1?Number=${superNumber}' target='_blank'><img src='${url}' title='${superName}' /></a>`;
            } catch (e) {
            }
            try {
                const link = resp['Super.Links'][0];
                paperUrl = link.URL;
            } catch(e) {
            }
            var paperLink = paperUrl ? `<a href='${paperUrl}' target='_blank'><img src='${paperIcon}' /></a>` : "";
            story.append(`<div class="more-links">${img}${paperLink}</div>`);
        })
        .catch(function (error) {
            console.log(error);
        });
    });

    var columnSelector = 'td.row-cell'
    var columnHeaderSelector = '.KanbanBoard .rollup-status';

    $(document).arrive(columnHeaderSelector, function() {
        const $headers = $(columnHeaderSelector);
        const isCollapsedByIndex = {};
        $headers.each((index, columnHeader) => {
            var $columnHeader = $(columnHeader);
            isCollapsedByIndex[index] = false;
            $columnHeader.on('click', function() {
                const nextState = !isCollapsedByIndex[index];
                isCollapsedByIndex[index] = nextState;

                var expandedCount = Object.keys(isCollapsedByIndex).reduce((acc, next) => {
                   return acc + (next ? 1 : 0)
                }, 0);

                var collapsedCount = Object.keys(isCollapsedByIndex).length - expandedCount;

                var collapsedColumnPercentage = 2;
                var remainingPrecentage = 100 - (collapsedCount * collapsedColumnPercentage);

                var newWidthPercentage = remainingPrecentage/expandedCount;

                $(`.taskboard .row-cell:nth-child(${index + 1}) *`).css('opacity', nextState ? 0 : 1);

                $('.taskboard colgroup col').each((jndex, col) => {
                    var $col = $(col);
                    $col.width(isCollapsedByIndex[jndex] ? `${collapsedColumnPercentage}%` : `${newWidthPercentage}%`)
                });
            });
        });
    });

    var swimlaneSelector = '.group-by-header';
    $(document).arrive(swimlaneSelector, function() {
        var $swimlanes = $(swimlaneSelector);

        var isCollapsedByIndex = {};
        $swimlanes.each((index, element) => {
            var $swimlaneHeader = $(element);
            isCollapsedByIndex[index] = false;

            $swimlaneHeader.on('click', function(event) {
                var $swimlane = $swimlaneHeader.next();

                var nextState = !isCollapsedByIndex[index];
                isCollapsedByIndex[index] = nextState;

                $swimlane.children().css('display', nextState ? 'none' : 'table-cell');
            })
        });
    });

    $(document).arrive('.KanbanBoard', function() {
        var typeAttr = '_v1_type';
        var rankAttr = '_v1_rank';
        var oidAttr = '_v1_asset';

        var $cards = $('.story-card-container');

        var cards = [];

        $cards.each((index, card) => {
            var $card = $(card);
            var type = $card.attr(typeAttr);
            var rank = $card.attr(rankAttr);
            var oid = $card.attr(oidAttr);
            var title = $card.find('.title').html();
            var number = $card.find('.number').html();

            cards.push({
                type: type,
                rank: rank,
                oid: oid,
                title: title,
                number: number,
            });
        });

        var sortedCards = cards.sort((prev, next) => prev.rank > next.rank);


        var lis = sortedCards.reduce((acc, next) => {
            return acc + `<div class="list-view-item flex-row">
<div class="icon ${next.type}"></div>
  <div class="flex-column">
    <div>
     <span class="number">${next.oid}</span>
     <span class="number">${next.number}</span>
    </div>
  <div>${next.title}</div>
</div>
</div>`
        }, '');

        $(document.body).append(`<div class="list-view hidden">${lis}</div>`);

        $(document.body).append('<div class="toggleListView"></div>');

        $('.toggleListView').on('click', function() {
            $('.list-view').toggleClass('hidden');
        });

    });
})();