crbug colorize

Colorize bug list based on status

目前為 2020-01-30 提交的版本,檢視 最新版本

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

const sheet = new CSSStyleSheet();
sheet.replaceSync(`
  .wOxxOm-Starred { font-weight: bold }
  .wOxxOm-Archived { color: gray }
  .wOxxOm-Assigned { color: #3f71b1 }
  .wOxxOm-Available { color: #92479a }
  .wOxxOm-Duplicate,
  .wOxxOm-Invalid { opacity: 0.3 }
  .wOxxOm-ExternalDependency { color: #ababab }
  .wOxxOm-Fixed { color: #227700 }
  .wOxxOm-Started,
  .wOxxOm-FixPending { color: #06908b }
  .wOxxOm-Unconfirmed,
  .wOxxOm-New { color: black }
  .wOxxOm-Untriaged { color: #947911 }
  .wOxxOm-Verified, .wOxxOm-Accepted { color: #6a846f }
  .wOxxOm-WontFix { color: #d00 }
  tr[class*="wOxxOm-"] 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));
  const sheets = list.adoptedStyleSheets;
  if (!sheets.includes(sheet))
    list.adoptedStyleSheets = [...sheets, sheet];
  for (const el of list.querySelectorAll('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':
        el.parentNode.classList.add('wOxxOm-' + text);
        continue;
      case '★':
        el.parentNode.classList.add('wOxxOm-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();
    }
  }
  await removed(page);
}

async function added(sel, parent = document.documentElement) {
  const target =
    parent.shadowRoot ||
    !parent.localName.includes('-') && parent ||
    await shadowOf(parent);
  return target.querySelector(sel) || new Promise(resolve => {
    const mo = new MutationObserver(() => {
      const el = target.querySelector(sel);
      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;
    };
  });
}