Greasy Fork is available in English.

crbug colorize

Colorize bug list based on status

// ==UserScript==
// @name           crbug colorize
// @description    Colorize bug list based on status
// @match          https://bugs.chromium.org/*
// @version        1.0.4
// @author         wOxxOm
// @namespace      wOxxOm.scripts
// @license        MIT License
// @run-at         document-start
// @grant          none
// ==/UserScript==

const prefix = 'wOxxOm-'
const sheet = new CSSStyleSheet();
sheet.replaceSync(`
  .${prefix}Starred { font-weight: bold }
  .${prefix}Archived { color: gray }
  .${prefix}Assigned { color: #3f71b1 }
  .${prefix}Available { color: #92479a }
  .${prefix}Duplicate,
  .${prefix}Invalid { opacity: 0.3 }
  .${prefix}ExternalDependency { color: #ababab }
  .${prefix}Fixed { color: #227700 }
  .${prefix}Started,
  .${prefix}FixPending { color: #06908b }
  .${prefix}Unconfirmed,
  .${prefix}New { color: black }
  .${prefix}Untriaged { color: #947911 }
  .${prefix}Verified, .${prefix}Accepted { color: #6a846f }
  .${prefix}WontFix { color: #d00 }
  tr[class*="${prefix}"] td[width="100%"] a {
    color: inherit;
    text-decoration: underline;
  }
`);

(async () => {
  const app = await added('mr-app');
  const main = await added('main', app);
  while (true) await colorize(main);
})();

async function colorize(main) {
  const page = await added('mr-list-page', main);
  const list = await shadowOf(await added('mr-issue-list', page));
  list.adoptedStyleSheets = [...new Set([...list.adoptedStyleSheets, sheet])];
  await added('td', list.host);
  const tbody = list.querySelector('tbody');
  while (true) {
    for (const el of tbody.getElementsByTagName('td')) {
      const text = el.textContent.trim();
      switch (text) {
        case '':
          continue;
        case 'Accepted':
        case 'Archived':
        case 'Assigned':
        case 'Available':
        case 'Duplicate':
        case 'ExternalDependency':
        case 'FixPending':
        case 'Fixed':
        case 'Invalid':
        case 'New':
        case 'Started':
        case 'Unconfirmed':
        case 'Untriaged':
        case 'Verified':
        case 'WontFix':
          setClass(el, text);
          continue;
        case '★':
          setClass(el, 'Starred');
          continue;
      }
      if (el.align === 'right' && (text === '1' || text === '0')) {
        el.textContent = '';
      }
      if (/% regression in|\b\d(\.\d)?%(-\d(\.\d)?%)? improvement in|test.*?is flaky|^(Android|Chrome)$/.test(text) && el.parentNode) {
        el.parentNode.remove();
      }
    }
    const winner = await Promise.race([
      removed(page),
      added('td', list.host, true),
    ]);
    if (!winner)
      return;
  }
}

function setClass(el, type) {
  const {className} = el.parentNode;
  const token = prefix + type;
  const allTokens = className
    .trim()
    .split(/\s+/)
    .filter(t => !t.startsWith(prefix) || t === token);
  const s = allTokens.join(' ') + (allTokens.includes(token) ? '' : ' ' + token);
  if (s !== className)
    el.parentNode.className = s;
}

async function added(tag, parent = document.documentElement, forceObserver) {
  const target =
    parent.shadowRoot ||
    !parent.localName.includes('-') && parent ||
    await shadowOf(parent);
  return !forceObserver && target.querySelector(tag) || new Promise(resolve => {
    const mo = new MutationObserver(mutations => {
      const el = forceObserver ? mutations.some(mutantAdded, {tag}) : target.querySelector(tag);
      if (el) {
        mo.disconnect();
        resolve(el);
      }
    });
    mo.observe(target, {childList: true, subtree: true});
  });
}

function removed(el) {
  const root = el.getRootNode();
  return root.contains(el) && new Promise(resolve => {
    const mo = new MutationObserver(() => {
      if (!root.contains(el)) {
        mo.disconnect();
        resolve();
      }
    });
    mo.observe(root, {childList: true, subtree: true});
  });
}

function shadowOf(el) {
  return el.shadowRoot || new Promise(resolve => {
    el.attachShadow = function (...args) {
      delete el.attachShadow;
      const root = el.attachShadow(...args);
      resolve(root);
      return root;
    };
  });
}

function mutantAdded({addedNodes, target}) {
  const {tag} = this;
  if (target.localName === tag)
    return true;
  for (const n of addedNodes)
    if (n.localName === tag || n.firstElementChild && n.getElementsByTagName(tag)[0])
      return true;
}