Trello colouring (CSPL Reporting Solution)

Colour list for Scrum, referenced with Kanban Game

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name Trello colouring (CSPL Reporting Solution)
// @namespace https://trello.com/
// @version 1.6
// @description Colour list for Scrum, referenced with Kanban Game
// @match https://trello.com/*
// @require http://code.jquery.com/jquery-latest.js
// @author       Angie
// ==/UserScript==
// Ref: https://greasyfork.org/en/scripts/37171-trello-coloured-scrum-kanban

$(document).ready(function() {

    var red = '#fccbc2',
        blue = '#9ec4ff',
        green = '#dbffd1' ,
        tiffanyblue = '#a4fce6',
        lighttiffanyblue = '#e5fff8',
        grey = '#c4c4c4',
        black = '#898989',
        white = '#fff',
        yellow = '#ffd800',
        creamywhite = '#ffffb3',
        darkred = '#ff6363',
        lightblue = '#dbf3ff',
        lightpurple = '#5aa8fc',
        lightorange = '#ffd396',
        darkdarkred = '#c1150f',
        pink = '#fabaff',
        orange = '#ff8300';

    $('body').hover(function() {
        $("h2:contains('Analysis')").css('color', white).parents('.list').css('background', red);
        $("h2:contains('Development')").css('color', white).parents('.list').css('background', blue);
        $("h2:contains('QA')").css('color', white).parents('.list').css('background', green);
        $("h2:contains('Deployment')").css('color', white).parents('.list').css('background', creamywhite);
        $("h2:contains('Done (')").css('color', white).parents('.list').css('background', black);
        $("span:contains('(Parent)')").parents('.list-card-details').css('background', tiffanyblue).css('border-style', 'solid').css('border-color', lightpurple).css('border-width', '5px');
        $("span:contains('(Child)')").parents('.list-card-details').css('background', tiffanyblue).css('border-style', 'none');
        $(".card-short-id").append(" ").removeClass("hide").css('color', lightorange);
        $("span[class='list-card-title js-card-name']").filter(function () {
            return !new RegExp("\\(\\d+\\)").test($(this).text());
        }).find(".card-short-id").css('color', darkdarkred);
        $("span:contains('[VIP]')").parents('.list-card-details').css('background', darkred).css('border-style', 'none');
        $("span:contains('[Guide]')").parents('.list-card-details').css('background', lightblue).css('border-style', 'none');
    });

    /* Prevent multiple insert to DOM for performance */
    /* Construct whole WIP html string and append all to DOM at once */
    var completeWipHtml = "";
    var prevCompleteWipHtml = "";
    var finalMap = new Map();
    var finalPairMap = new Map();
    
    function printOneMemberWIP() {
        var WIP_LIMIT = 3;
        for (let key of finalMap.keys()) {
            if (finalMap.get(key) == 0) {
                completeWipHtml = completeWipHtml + "<b style='color: orange'>" + key + ": " + finalMap.get(key) + " </b>";
            } else if (finalMap.get(key) > WIP_LIMIT) {
                completeWipHtml = completeWipHtml + "<b style='color: red'>" + key + ": " + finalMap.get(key) + " </b>";
            } else {
                completeWipHtml = completeWipHtml + key + ": " + finalMap.get(key) + " ";
            }

            if (finalPairMap.get(key) == 0) {
                completeWipHtml = completeWipHtml + "<b style='color: orange'>(" + finalPairMap.get(key) + ") , </b>";
            } else {
                completeWipHtml = completeWipHtml + "(" + finalPairMap.get(key) + ") , ";
            }
        }
    }

    function printGroupAWIP(value, key, map) {
        var WIP_LIMIT = 5;
        if (value > WIP_LIMIT) {
            completeWipHtml = completeWipHtml + "<b style='color: red'>" + key + ": " + value + " , </b>";
        } else {
            completeWipHtml = completeWipHtml + key + ": " + value + " , ";
        }
    }

    function printWIP() {
        prevCompleteWipHtml = completeWipHtml;
        completeWipHtml = "";

        /*
            Common Selectors
        */
        var groupASelector = ".list-card.js-member-droppable.ui-droppable";

        var analysisSelector = 'textarea:contains("Analysis - In Progress"), textarea:contains("Analysis - Done")';
        var devSelector = 'textarea:contains("Development - In Progress"), textarea:contains("Development - Done")';
        var qaSelector = 'textarea:contains("QA - In Progress"), textarea:contains("QA - Done")';
        var deploySelector = 'textarea:contains("Deployment - In Progress")';
        
        var blockedSelector = "span[title='Keep Monitoring'], span[title^='Pending for']";
        var notExceptionSelector = ".list-card:not(:contains('(Parent)')):not(:contains('[VIP]'))";
        var exceptionSelector = ".list-card:contains('(Parent)'), .list-card:contains('[VIP]')";

        var memberIPSelector = 'textarea:contains("Analysis"), textarea:contains("Development"), textarea:contains("QA"), textarea:contains("Deployment"), textarea:contains("Environment Issue"), textarea:contains("Jail")';

        /*
            Common Selectors END
        */

        /*
            Print Single Member WIP
        */

        var text = "";

        completeWipHtml = completeWipHtml + "<div id='actualWIP' style='color: white'>Member WIP (excluded External Blocker): ";

        var allMember = $("div.list-wrapper")
        .find("img.member-avatar")
        .map(function() {
            return this.alt.replace(/\(.+\)/, "");
        })
        .toArray();

        finalMap.clear();
        for (var i in allMember) {
            finalMap.set(allMember[i], 0);
        }

        var normalMap = $(memberIPSelector)
        .closest("div.list-wrapper")
        .find("img.member-avatar")
        .map(function() {
            return this.alt.replace(/\(.+\)/, "");
        })
        .toArray()
        .reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());

        var blockedMap = $(memberIPSelector)
        .closest("div.list-wrapper")
        .find(blockedSelector).parent()
        .closest("a.list-card")
        .find("img.member-avatar")
        .map(function() {
            return this.alt.replace(/\(.+\)/, "");
        })
        .toArray()
        .reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());

        for (var [key, value] of finalMap.entries()) {
            var point = normalMap.get(key) === undefined ? 0 : normalMap.get(key);
            var blockedPoint = blockedMap.get(key) === undefined ? 0 : blockedMap.get(key);
            finalMap.set(key, point - blockedPoint);
        }

        /* Pair Count */

        finalPairMap.clear();
        for (var i in allMember) {
            finalPairMap.set(allMember[i], 0);
        }

        var normalPairMap = $(memberIPSelector)
        .closest("div.list-wrapper")
        .find("div.js-list-card-members")
        .filter(function(index) {
            return $("div.js-member-on-card-menu", this).length > 1;
        })
        .find("img.member-avatar")
        .map(function() {
            return this.alt.replace(/\(.+\)/, "");
        })
        .toArray()
        .reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());

        var blockedPairMap = $(memberIPSelector)
        .closest("div.list-wrapper")
        .find("div.js-list-card-members")
        .filter(function(index) {
            return $("div.js-member-on-card-menu", this).length > 1;
        })
        .closest("a.list-card")
        .find(blockedSelector).parent()
        .closest("a.list-card")
        .find("img.member-avatar")
        .map(function() {
            return this.alt.replace(/\(.+\)/, "");
        })
        .toArray()
        .reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());

        for (var [key, value] of finalPairMap.entries()) {
            var point = normalPairMap.get(key) === undefined ? 0 : normalPairMap.get(key);
            var blockedPoint = blockedPairMap.get(key) === undefined ? 0 : blockedPairMap.get(key);
            finalPairMap.set(key, point - blockedPoint);
        }
        
        /* Pair Count END */

        printOneMemberWIP();

        completeWipHtml = completeWipHtml + "</div>";

        /*
            Print Group WIP
        */
        completeWipHtml = completeWipHtml + "<div id='actualGroupAWIP' style='color: grey; background-color: yellowgreen; margin: 3px; padding: 3px; border-radius: 25px' align='middle'>WIP (Max. 5 for each): ";

        var finalGroupAMap = new Map();

        var analysisIP = $(analysisSelector).closest("div.list-wrapper").find(groupASelector).length;
        /* find blocker but excluding Parent & VIP (Exception) */
        /* ".find(blockedSelector).parent().length" instead of ".find(blockedSelector).length" to count blocked CARDS other than blocked LABELS */
        var analysisBlocked = $(analysisSelector).closest("div.list-wrapper").find(notExceptionSelector).find(groupASelector).parent().find(blockedSelector).parent().length;
        /* find Parent or VIP */
        var analysisException = $(analysisSelector).closest("div.list-wrapper").find(exceptionSelector).find(groupASelector).length;
        finalGroupAMap.set("Analysis", analysisIP - analysisBlocked - analysisException);

        var devIP = $(devSelector).closest("div.list-wrapper").find(groupASelector).length;
        /* find blocker but excluding Parent & VIP (Exception) */
        var devBlocked = $(devSelector).closest("div.list-wrapper").find(notExceptionSelector).find(groupASelector).parent().find(blockedSelector).parent().length;
        /* find Parent or VIP */
        var devException = $(devSelector).closest("div.list-wrapper").find(exceptionSelector).find(groupASelector).length;
        finalGroupAMap.set("Development", devIP - devBlocked - devException);

        var qaIP = $(qaSelector).closest("div.list-wrapper").find(groupASelector).length;
        /* find blocker but excluding Parent & VIP (Exception) */
        var qaBlocked = $(qaSelector).closest("div.list-wrapper").find(notExceptionSelector).find(groupASelector).parent().find(blockedSelector).parent().length;
        /* find Parent or VIP */
        var qaException = $(qaSelector).closest("div.list-wrapper").find(exceptionSelector).find(groupASelector).length;
        finalGroupAMap.set("QA", qaIP - qaBlocked - qaException);

        var deployIP = $(deploySelector).closest("div.list-wrapper").find(groupASelector).length;
        /* find blocker but excluding Parent & VIP (Exception) */
        var deployBlocked = $(deploySelector).closest("div.list-wrapper").find(notExceptionSelector).find(groupASelector).parent().find(blockedSelector).parent().length;
        /* find Parent or VIP */
        var deployException = $(deploySelector).closest("div.list-wrapper").find(exceptionSelector).find(groupASelector).length;
        finalGroupAMap.set("Deployment", deployIP - deployBlocked - deployException);

        finalGroupAMap.forEach(printGroupAWIP);

        completeWipHtml = completeWipHtml + "</div>";

        /*
            (1)
            Remove elements at once
            Append elements at once
            (2)
            Remove & Append only when there is change
        */

        if (prevCompleteWipHtml != completeWipHtml) {
            console.log("Update WIP!");
            $("#actualWIP").remove();
            $("#actualGroupAWIP").remove();
            $(".board-header-btns.mod-left").first().after(completeWipHtml);
        }

        /*
            Refresh data
        */
        setTimeout(function(){ printWIP(); }, 5000);
    }
    printWIP();

    function markdownAll() {
    var cards = document.getElementsByClassName('list-card-title');
    for (var i = 0; i < cards.length; i++) {
        cards[i].innerHTML = cards[i].innerHTML
            .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
            .replace(/\*(.+?)\*/g, '<em>$1</em>')
            .replace(/~~(.+?)~~/g, '<strike>$1</strike>')
            .replace(/\`(.+?)\`/g, '<code>$1</code>');
    }
    setTimeout(markdownAll, 5000);
    }
    markdownAll();
});