Master Duel Meta - Screenshot for Deck Builder

Take a nice shot of your deck!

// ==UserScript==
// @name         Master Duel Meta - Screenshot for Deck Builder
// @namespace    https://github.com/DonkeyBear
// @version      0.2.6
// @description  Take a nice shot of your deck!
// @author       DonkeyBear
// @match        http://www.masterduelmeta.com/deck-tester*
// @match        https://www.masterduelmeta.com/deck-tester*
// @icon         https://s3.duellinksmeta.com/img/icons/favicon-32x32.png
// @require      https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js
// @grant        none
// ==/UserScript==

const stylesheet = /* css */`
  .deck-container.taking-shot .new-card, .deck-container.taking-shot .adjust-buttons-container, .search-container.taking-shot {
    display: none !important;
  }
  .screenshot {
    object-fit: contain;
    max-width: 100vw;
    max-height: 100vh;
  }
  .overlay {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(0, 0, 0, .5);
    backdrop-filter: blur(3px);
    z-index: 999;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .info-container {
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: .75em;
    font-weight: 500;
  }
  .info-container .slot-info {
    color: white;
  }
  .slot-info, .card-count {
    flex-basis: auto !important;
  }
  .tag {
    color: white;
    padding: .1rem .4rem;
    border-radius: .25rem;
    margin-right: .25rem;
  }
  .tag.rarity-ur {
    background: linear-gradient(90deg, #c92ae7 0%, #7844fd 49%, #43bcd5 100%);
  }
  .tag.rarity-sr {
    background: linear-gradient(90deg, #e85504 0%, #f19d00 100%);
  }
  .tag.rarity-r {
    background: linear-gradient(90deg, #1831cc 0%, #017cfb 100%);
  }
  .tag.rarity-n {
    background: linear-gradient(90deg, #4f494b 0%, #969696 100%);
  }
`;
const style = document.createElement('style');
style.textContent = stylesheet;
document.head.appendChild(style);

// Append screenshot button
const tabButtonContainer = document.querySelector('ul.svelte-umfxo');
const newTabButton = document.createElement('li');
newTabButton.classList.add('svelte-umfxo', 'screenshot-button');
newTabButton.textContent = 'Screenshot';
newTabButton.onclick = () => { takeshot() };
tabButtonContainer.appendChild(newTabButton);

countCards();

const observer = {};

// Append screenshot button again if it's removed
observer.tabButtonContainer = new MutationObserver(() => {
  if (!tabButtonContainer.querySelector('.screenshot-button')) {
    tabButtonContainer.appendChild(newTabButton);
  }
});
observer.tabButtonContainer.observe(tabButtonContainer, { childList: true });

// Count cards and print
observer.mainDeck = new MutationObserver(() => { countCards() });
observer.extraDeck = new MutationObserver(() => { countCards() });
observer.deckContainer = new MutationObserver(() => {
  // Append count-info again if it's removed
  if (document.querySelector('.deck-container > .info-container').textContent.includes('cards')) { countCards() }
  const mainDeck = document.querySelector('.deck-container > .box-container');
  const extraDeck = document.querySelector('.extra-side-deck');
  if (mainDeck) { observer.mainDeck.observe(mainDeck, { childList: true, subtree: true, attributes: true }) }
  if (extraDeck) { observer.extraDeck.observe(extraDeck, { childList: true, subtree: true, attributes: true }) }
  if (mainDeck && mainDeck) { observer.deckContainer.disconnect() }
});
observer.deckContainer.observe(document.querySelector('.deck-container'), { childList: true });

function takeshot () {
  const deckContainer = document.querySelector('.deck-container');
  const searchContainer = document.querySelector('.search-container');

  deckContainer.classList.add('taking-shot');
  searchContainer.classList.add('taking-shot');

  const overlay = document.createElement('div');
  overlay.classList.add('overlay');
  overlay.onclick = () => { overlay.remove() };
  document.body.appendChild(overlay);

  const options = {
    allowTaint: false,
    useCORS: true,
    backgroundColor: '#001b35', // Background color of <body>
    logging: false
  };
  html2canvas(deckContainer.parentElement, options).then(canvas => { // eslint-disable-line no-undef
    canvas.classList.add('screenshot');
    overlay.appendChild(canvas);
  });

  deckContainer.classList.remove('taking-shot');
  searchContainer.classList.remove('taking-shot');
}

function countCards () {
  const counter = {
    main: 0,
    extra: 0,
    ur: 0,
    sr: 0,
    r: 0,
    n: 0
  };

  const mainDeckCards = document.querySelectorAll('.deck-container > .box-container > .card-container > .card');
  const extraDeckCards = document.querySelectorAll('.extra-side-deck .card-container > .card');

  for (const cards of [mainDeckCards, extraDeckCards]) {
    for (const card of cards) {
      if (!card.querySelector('img')) { break }

      let copies;
      const cardAmount = card.querySelector('img.card-amount');
      if (!cardAmount) {
        copies = 1;
      } else if (cardAmount.alt.includes('2')) {
        copies = 2;
      } else if (cardAmount.alt.includes('3')) {
        copies = 3;
      }

      cards === mainDeckCards ? counter.main += copies : counter.extra += copies;

      const rarityImage = card.querySelector('.rarity-image > img');
      if (!rarityImage) { break }
      switch (rarityImage.alt) {
        case 'UR Rarity':
          counter.ur += copies;
          break;
        case 'SR Rarity':
          counter.sr += copies;
          break;
        case 'R Rarity':
          counter.r += copies;
          break;
        case 'N Rarity':
          counter.n += copies;
      }
    }
  }

  const infoLeft = document.querySelector('.info-container .slot-info');
  const infoRight = document.querySelector('.info-container .card-count');
  infoLeft.innerHTML = /* html */`
    <span class="tag rarity-ur">${counter.ur} UR</span>
    <span class="tag rarity-sr">${counter.sr} SR</span>
    <span class="tag rarity-r">${counter.r} R</span>
    <span class="tag rarity-n">${counter.n} N</span>
  `;
  infoRight.textContent = `Main: ${counter.main} Extra: ${counter.extra}`;
}