fokas.pokas / SSRemover

// ==UserScript==
// @namespace        http://openuserjs.org/users/fokas.pokas
// @name             SSRemover
// @version          0.9
// @description      Multi deleting files on Shutterstock
// @description:ru   Удаление нескольких файлов на Shutterstock
// @copyright        2020, fokas.pokas (https://openuserjs.org/users/fokas.pokas)
// @license          MIT
// @run-at           document-end
// @match            https://submit.shutterstock.com/catalog_manager/
// @match            https://submit.shutterstock.com/catalog_manager
// @match            https://submit.shutterstock.com/catalog_manager/?language*
// @match            https://submit.shutterstock.com/catalog_manager?language*
// @grant            none
// ==/UserScript==

// ==OpenUserJS==
// @author           fokas.pokas
// ==/OpenUserJS==

(function() {

    'use strict';




// ======== Настройки скрипта ===============
const SPEED = 4; // Количество параллельно удаляемых изображений
const HOLD_SESSIONS_INFORMATION = 10; // Сколько дней хранить информацию по удаленным id в хранилище
// ==========================================

let _notificationID;
let _userStorage;
let _visitedAuthorLink;
let _isRu;

const DAYS_BEFORE_AUTHOR_MESSAGE = 4;

setTimeout(() => {
  addCssRules();
  main();
}, 500);

function main() {
  let menu = document.querySelector(".dropdown-menu-right");
  _userStorage = new UserStorage("ssRemover");

  let languageNode = document.querySelector('#language-dropdown');
  _isRu = (languageNode) ? languageNode.innerText.includes('Русский') : false;

  addStaticticWindow();
  addRemoveButton();
  waitNewImages();
}

function waitNewImages() {
  let mediaItem = document.querySelector(
    '[data-test-ref="media-grid"] [data-test-ref^="media-grid-item"]'
  );

  if (mediaItem && !mediaItem.dataset.first) {
    mediaItem.dataset.first = true;
    checkAlreadyDeletedItems();
  }

  setTimeout(waitNewImages, 500);
}

function translate(enText, ruText="") {
  if (_isRu && ruText) return ruText;

  return enText;
}

function addStaticticWindow() {
  document.body.insertAdjacentHTML(
    "afterbegin",
    /* html */ `
    <div id="ssremover-statistic" hidden>
      <input id="show-removal-levels" type="checkbox" checked hidden/>
      <div class="statistic-window">

        <div class="statistic-window-header">
          <div class="statictic-title">${translate('Statistic','Статистика')}</div>
          <div class="total-count-title">${translate(
            'Total deleted <span id="total-count">?</span> file(s)',
            'Всего удалено <span id="total-count">?</span> файл(ов)'
          )}</div>
          <button id="statistic-close-button" class="close-button"></button>
        </div>

        <div id="level-table">
          <div class="table-header-row">
            <div class="table-col">
              <div class="image-levels"></div>
              <div class="header-text">${translate('Removal levels', 'Уровни удаления')}</div>
            </div>
            <div class="table-col">
              <div class="image-licenses"></div>
              <div class="header-text">${translate('Number of files deleted after 1th June of 2020', 'Количество файлов, удаленных после 1 Июня 2020 года')}</div>
            </div>
          </div>
          <div class="table-row" data-level="1" id="current-removal-level">
            <div class="table-col">${translate('Level', 'Уровень')} 1</div>
            <div class="table-col">${translate('Up to 100', 'До 100')}</div>
          </div>
          <div class="table-row" data-level="2">
            <div class="table-col">${translate('Level', 'Уровень')} 2</div>
            <div class="table-col">${translate('101 to 250', 'От 101 до 250')}</div>
          </div>
          <div class="table-row" data-level="3">
            <div class="table-col">${translate('Level', 'Уровень')} 3</div>
            <div class="table-col">${translate('251 to 500', 'От 251 до 500')}</div>
          </div>
          <div class="table-row" data-level="4">
            <div class="table-col">${translate('Level', 'Уровень')} 4</div>
            <div class="table-col">${translate('501 to 2,500', 'От 501 до 2500')}</div>
          </div>
          <div class="table-row" data-level="5">
            <div class="table-col">${translate('Level', 'Уровень')} 5</div>
            <div class="table-col">${translate('2,501 to 25,000', 'От 2501 до 25000')}</div>
          </div>
          <div class="table-row" data-level="6">
            <div class="table-col">${translate('Level', 'Уровень')} 6</div>
            <div class="table-col">${translate('Over 25,000', 'Более 25000')}</div>
          </div>
        </div>

        <div class="ssremover-message message-alert" id="about-shutterstock-terms">
          <div>${translate(
            `Please note that you can not upload your deleted files again due to Shutterstock's <a href="https://submit.shutterstock.com/legal/terms?language=en" target="_blank">terms</a>.`,
            `Пожалуйста, обратите внимание, что вы не сможете повторно загрузить ваши удаленные файлы из-за <a href="https://submit.shutterstock.com/legal/terms?language=ru" target="_blank">условий</a> Shutterstock.`
          )}</div>
          <button class="close-button"></button>
        </div>

        <div class="ssremover-message message-notice" id="disable-removal-levels">
          <div>${translate(
            'If you want to turn this ludicrous mode off, just <a href="#">click here</a>.',
            'Если вы хотите отключить это безумие, просто <a href="#">нажмите сюда</a>.'
          )}</div>
          <button class="close-button"></button>
        </div>

        <div class="ssremover-message message-notice" id="enable-removal-levels">
          <div>${translate(
            'If you want to see the rating table, <a href="#">click here</a>.',
            'Если вы хотите увидеть таблицу уровней, <a href="#">нажмите сюда</a>'
          )}</div>
          <button class="close-button"></button>
        </div>

        <div id="show-hidden-messages" hidden><a href="#">${translate('Show hidden notifications', 'Показать скрытые уведомления')}</a></div>
      </div>
      <div id="statistic-cover"></div>
    </div>
  `
  );

  let ssRemoverStatistic = document.querySelector("#ssremover-statistic");

  ssRemoverStatistic.querySelector(
    "#statistic-close-button"
  ).onclick = function () {
    ssRemoverStatistic.hidden = true;
  };
  ssRemoverStatistic.querySelector("#statistic-cover").onclick = function () {
    ssRemoverStatistic.hidden = true;
  };
  ssRemoverStatistic.querySelector(
    "#disable-removal-levels a"
  ).onclick = function () {
    ssRemoverStatistic.querySelector("#show-removal-levels").checked = false;
    _userStorage.setValue('showLevelsTable', false);
  };
  ssRemoverStatistic.querySelector(
    "#enable-removal-levels a"
  ).onclick = function () {
    ssRemoverStatistic.querySelector("#show-removal-levels").checked = true;
    _userStorage.setValue('showLevelsTable', true);
  };
  ssRemoverStatistic.querySelector(
    "#show-hidden-messages"
  ).onclick = function () {
    _userStorage.setValue("hiddenMessages", {});
    let messageNodes = ssRemoverStatistic.querySelectorAll(
      ".ssremover-message"
    );
    for (let messageNode of messageNodes) {
      messageNode.style = "";
    }
    ssRemoverStatistic.querySelector("#show-hidden-messages").hidden = true;
  };

  let showLevelsTable = _userStorage.getValue('showLevelsTable');
  if (showLevelsTable == null) showLevelsTable = true;
  ssRemoverStatistic.querySelector("#show-removal-levels").checked = showLevelsTable;

  let hiddenMessages = _userStorage.getValue("hiddenMessages");
  if (!hiddenMessages) hiddenMessages = {};

  let messageNodes = ssRemoverStatistic.querySelectorAll(".ssremover-message");
  for (let messageNode of messageNodes) {
    let isHidden = hiddenMessages[messageNode.id];
    if (isHidden) {
      messageNode.style.display = "none";
      ssRemoverStatistic.querySelector("#show-hidden-messages").hidden = false;
    }

    messageNode.querySelector(".close-button").onclick = function (event) {
      event.currentTarget.parentNode.style.display = "none";

      let messageId = event.currentTarget.parentNode.id;
      let hiddenMessages = _userStorage.getValue("hiddenMessages");
      if (!hiddenMessages) hiddenMessages = {};
      hiddenMessages[messageId] = true;
      _userStorage.setValue("hiddenMessages", hiddenMessages);

      ssRemoverStatistic.querySelector("#show-hidden-messages").hidden = false;
    };
  }

  //showStatistic();
}

function showStatistic() {
  let ssRemoverStatistic = document.querySelector("#ssremover-statistic");

  let totalDeleted = getCountOfDeletedIds();
  document.querySelector("#total-count").textContent = totalDeleted;

  let currentLevel = 1;
  if (totalDeleted > 100) currentLevel = 2;
  if (totalDeleted > 250) currentLevel = 3;
  if (totalDeleted > 500) currentLevel = 4;
  if (totalDeleted > 2500) currentLevel = 5;
  if (totalDeleted > 25000) currentLevel = 6;

  ssRemoverStatistic.querySelector("#current-removal-level").id = "";
  ssRemoverStatistic.querySelector(`[data-level="${currentLevel}"]`).id =
    "current-removal-level";

  ssRemoverStatistic.hidden = false;
}

function addRemoveButton() {
  var ssremoverButton = document.querySelector("#ssremover-button");
  if (ssremoverButton) return;

  document
    .querySelector(
      "#catalog-manager div.component-content div.flex-justify-end"
    )
    .insertAdjacentHTML(
      "afterbegin",
      /* html */ `
    <button type="button" class="btn btn-sm btn-default" id="ssremover-button" style="">${translate('Delete selected', 'Удалить выбранное')}</button>
    `
    );

  document.querySelector("#ssremover-button").onclick = ssremoveButton_onClick;
}

function needToShowAuthorMessage() {



}

async function ssremoveButton_onClick(event) {
  mergeSessions();

  let mediaItems = document.querySelectorAll(
    '[data-test-ref="media-grid"] [data-test-ref^="media-grid-item"]'
  );

  let ids = [];

  for (let mediaItem of mediaItems) {
    let isChecked = mediaItem.querySelector("input").checked;

    if (!isChecked) continue;

    let img = mediaItem.querySelector("img");
    let matchResult = img.src.match(/([0-9]+).jpg$/);

    if (!matchResult) continue;

    let id = img.src.match(/([0-9]+).jpg$/)[1];

    ids.push(id);
  }

  if (ids.length == 0) return;

  let result = confirm(translate(
    `You want to delete ${ids.length} file(s). Continue?`,
    `Вы собираетесь удалить ${ids.length} файл(ов). Продолжить?`
  ));

  if (!result) return;

  event.target.disabled = true;

  let flows = [];

  for (let i = 0; i < SPEED; i++) {
    document.body.insertAdjacentHTML(
      "beforeend",
      /* html */ `
      <iframe src="" id="iframe-${i}"></iframe>
    `
    );

    let iframe = document.querySelector("#iframe-" + i);

    iframe.dataset.flowIndex = i;
    flows.push({
      iframe,
      done: true,
    });
  }

  let totalDeleted = 0;

  for (let id of ids) {
    let flow = await getFreeFlow(500);
    flow.done = false;
    let url = "https://submit.shutterstock.com/catalog_manager/images/" + id;

    totalDeleted++;
    notification(translate(`Please wait: ${totalDeleted} of ${ids.length}`, `Пожалуйста подождите: ${totalDeleted} из ${ids.length}`));

    flow.iframe.contentWindow.location = url;
    flow.iframe.onload = function (event) {
      let iframe = event.target;
      let flowIndex = iframe.dataset.flowIndex;

      iframe.onload = null;

      doFunctionList(
        200,
        () => {
          let notFoundMessage = iframe.contentDocument.querySelector(
            ".o_Alert_Alert_alertMessage"
          );

          if (notFoundMessage) {
            flows[flowIndex].done = true;
            return;
          }

          let dialog = iframe.contentDocument.querySelector(
            '[data-react-toolbox="dialog"]'
          );

          if (dialog) return true;

          let deleteButton = iframe.contentDocument.querySelector(
            '[data-icon="trash"]'
          );
          if (deleteButton) deleteButton.click();

          return false;
        },
        () => {
          if (flows[flowIndex].done) return true;

          iframe.contentDocument
            .querySelector('[data-test-ref="delete-content-confirm"] button')
            .click();
        },
        () => {
          if (flows[flowIndex].done) return true;

          let comfirmMessage = iframe.contentDocument.querySelector(
            ".o_Alert_Alert_alertMessage"
          );

          if (!comfirmMessage) return false;

          flows[flowIndex].done = true;
        }
      );
    };
  }

  await waitFullDone(500);

  for (let flow of flows) {
    document.body.removeChild(flow.iframe);
  }

  updateDeletedItems(ids);

  checkAlreadyDeletedItems();

  event.target.disabled = false;

  setTimeout(showAuthorMessage, 5000);

  async function getFreeFlow(pause = 0) {
    return new Promise((resolve, reject) => {
      let timeID = setInterval(() => {
        for (let flow of flows) {
          if (flow.done) {
            clearInterval(timeID);
            resolve(flow);
          }
        }
      }, pause);
    });
  }

  async function waitFullDone(pause = 0) {
    return new Promise((resolve, reject) => {
      let timeID = setInterval(() => {
        for (let flow of flows) {
          if (!flow.done) return;
        }

        clearInterval(timeID);
        resolve(true);
      }, pause);
    });
  }
}

function checkAlreadyDeletedItems() {
  let allDeletedIds = getAllDeletedIds();

  let mediaItems = document.querySelectorAll(
    '[data-test-ref="media-grid"] [data-test-ref^="media-grid-item"]'
  );

  for (let mediaItem of mediaItems) {
    let img = mediaItem.querySelector("img");
    let matchResult = img.src.match(/([0-9]+).jpg$/);

    if (!matchResult) continue;

    let id = img.src.match(/([0-9]+).jpg$/)[1];

    if (!allDeletedIds.includes(id)) continue;

    let deleteCover = mediaItem.querySelector(".preview-delete-cover");

    if (deleteCover) continue;

    img.insertAdjacentHTML(
      "beforebegin",
      /* html */ `
      <div class="preview-delete-cover"></div>
    `
    );
  }
}

function updateDeletedItems(ids) {
  let allDeletedIds = getAllDeletedIds();
  let newDeletedIds = [];
  for (let id of ids) {
    if (!allDeletedIds.includes(id)) newDeletedIds.push(id);
  }

  if (newDeletedIds.length) {
    let sessionTime = new Date().toISOString();

    let sessionKey = `session[${sessionTime}]`;
    let sessionValue = _userStorage.getValue(sessionKey);

    if (!sessionValue) sessionValue = { ids: [], sessionTime };

    sessionValue.ids = [...new Set([...newDeletedIds, ...sessionValue.ids])];
    _userStorage.setValue(sessionKey, sessionValue);

    let deletedPerSession = _userStorage.getValue("deletedPerSession");
    if (!deletedPerSession) deletedPerSession = {};
    deletedPerSession[sessionTime] = { count: sessionValue.ids.length };
    _userStorage.setValue("deletedPerSession", deletedPerSession);
  }

  let countNowDeletedItems = newDeletedIds.length;
  let countDeletedItems = getAllDeletedIds().length;

  notification(translate(`You've deleted ${countNowDeletedItems} file(s)`,`Вы удалили ${countNowDeletedItems} файл(ов)`), true);
}

function mergeSessions() {
  let deletedPerSession = _userStorage.getValue("deletedPerSession");
  if (!deletedPerSession) deletedPerSession = {};

  let merged = {};

  for (let sessionKey in deletedPerSession) {
    let beginOfDay = new Date( Date.parse(sessionKey) );
    beginOfDay.setHours(0,0,0,0);

    let beginOfDayStr = beginOfDay.toISOString();

    if (!merged[beginOfDayStr]) merged[beginOfDayStr] = {count: 0};

    merged[beginOfDayStr].count += deletedPerSession[sessionKey].count;
  }

  _userStorage.setValue('deletedPerSession', merged);

  let sessions = _userStorage.getValues('session');
  if (!sessions) return;

  for (let sessionKey in sessions) {
    let sessionValue = sessions[sessionKey];
    let sessionTime = Date.parse(sessionValue.sessionTime);
    let currentTime = new Date();
    let daysLeft = (currentTime - sessionTime) / 1000 / 3600 / 24;

    if (daysLeft < HOLD_SESSIONS_INFORMATION) continue;

    _userStorage.removeValue(sessionKey);
  }
}

function getAllDeletedIds() {
  let ids = [];

  let sessions = _userStorage.getValues("session");
  if (!sessions) return ids;

  for (let sessionKey in sessions) {
    let sessionValue = sessions[sessionKey];
    ids = ids.concat(sessionValue.ids);
  }

  ids = [...new Set(ids)];

  return ids;
}

function getCountOfDeletedIds() {
  let count = 0;
  let deletedPerSession = _userStorage.getValue("deletedPerSession");
  if (!deletedPerSession) deletedPerSession = {};

  for (let sessionKey in deletedPerSession) {
    count += deletedPerSession[sessionKey].count;
  }

  return count;
}

function doFunctionList(wait = 100, ...functionList) {
  let isDone = functionList[0]();

  if (isDone != false) functionList = functionList.slice(1);

  if (functionList.length > 0)
    setTimeout(doFunctionList, wait, wait, ...functionList);
}

function notification(message, showMore = false) {
  let node = document.querySelector("#ssremover-notification");

  if (!node) {
    node = document.body.insertAdjacentHTML(
      "beforeend",
      /* html */ `
      <div id="ssremover-notification"></div>
    `
    );
    node = document.querySelector("#ssremover-notification");
  }

  node.innerHTML = message;
  if (showMore) {
    node.innerHTML += `. <a id="ssremover-show-statistic" href="#">${translate('Show more', 'Показать больше')}</a>`;

    document.querySelector("#ssremover-show-statistic").onclick = function () {
      showStatistic();
    };
  }

  node.classList.remove("hide");

  if (_notificationID) clearTimeout(_notificationID);

  _notificationID = setTimeout(() => {
    node.classList.add("hide");
  }, 5000);
}

function showAuthorMessage() {
  let dontShowAuthorMessage = _userStorage.getValue('dontShowAuthorMessage');
  if (dontShowAuthorMessage) return;

  let sessions = _userStorage.getValue('deletedPerSession');
  let totalUsingDays = Object.keys( sessions ).length;

  if (totalUsingDays < DAYS_BEFORE_AUTHOR_MESSAGE) return;

  document.body.insertAdjacentHTML(
    "afterbegin",
    /* html */ `
    <div id="ssremover-author">
      <div class="author-window">
        <div class="author-window-header">
          <div class="author-title">${translate('Srcipt author','Автор скрипта')}</div>
          <button id="author-close-button" class="close-button"></button>
        </div>

        <div class="ssremover-author-message message-notice">
        <div>${translate(
            `Hi! It seems that you really use my ssRemover script. I've written it to give authors more ways, besides delete their own portfolios. But you can just use it like handy tool for you work.
            <br><br>Anyway, if you enjoy my script and you want to write me something (for example translation errors into English) or donate me one of 0.10$ sales, <a href="https://fokas-pokas.livejournal.com/622.html" id="ssRemover-author-link">come here</a>. It will give me a reason to think about sharing my other tools for microstocks I usually create for my own needs.
            <br><br>P. S. Some of funny ideas were suggested by my friends who also suffered from these changes on Shutterstock. Thanks, guys!
            <br><br><a href="https://www.google.com/search?q=%23boycottshutterstock">#boycottshutterstock</a>`,
            `Привет. Похоже, вы действительно пользуетесь скриптом ssRemover. Я написал его, чтобы дать авторам больше выбора, помимо радикального удаления своих портфелей. Но вы можете использовать скрипт просто как удобный инструмент для вашей работы.
            <br><br>В любом случае, если скрипт вам понравился, у вас есть желание что-то мне написать или просто задонатить одну из 0.10$ продаж, <a href="https://fokas-pokas.livejournal.com/622.html" id="ssRemover-author-link">переходите сюда</a>. Это даст повод задуматься о публикации других инструментов для стоков, которые я делаю в собственных нуждах. Спасибо.
            <br><br>P.S. Некоторые забавные идеи были предложены моими друзьями, так же пострадавшими от изменений на Shutterstock. Ребята, спасибо!
            <br><br><a href="https://www.google.com/search?q=%23boycottshutterstock">#boycottshutterstock</a>`
            )}</div>
        </div>

        <div class="ssremover-author-message message-alert">
          <div>${translate(
            `You can just close this window. It won't never be shown again.`,
            `Вы можете просто закрыть это окно. Оно больше не будет вам показано.`
          )}</div>
        </div>

      </div>
      <div id="ssremover-author-cover"></div>
    </div>
  `);

  document.querySelector('#ssremover-author #ssRemover-author-link').onclick = function() {
    _visitedAuthorLink = true;
  }

  document.querySelector('#ssremover-author #author-close-button').onclick = function() {
    let node = document.querySelector('#ssremover-author');
    document.body.removeChild(node);
    _userStorage.setValue('dontShowAuthorMessage', true);

    if (_visitedAuthorLink) {
      notification(translate("Thanks for visiting my blog","Спасибо, что посетили мой блог"));
    } else {
      notification(translate("OK","ОК"));
    }
  }
}


class UserStorage {
  static TYPE = {
    LOCAL: "localStorage",
    SESSION: "sessionStorage",
  };

  _userStorage = null;
  _path = null;
  _subscribers = {};

  constructor(path = "default-storage", isLocal = true) {
    if (isLocal) {
      this._userStorage = localStorage;
    } else {
      this._userStorage = sessionStorage;
    }

    this._path = path + ".";

    window.addEventListener("storage", (event) =>
      this._informSubscribers(event)
    );
  }

  subscribe(handler, ...keys) {
    if (keys.length == 1 && Array.isArray(keys[0])) keys = [...keys[0]];
    if (keys.length == 0) keys.push("");

    for (let key of keys) {
      if (!this._subscribers[this._path + key])
        this._subscribers[this._path + key] = [];
      let handlers = this._subscribers[this._path + key];
      if (handlers.includes(handler)) continue;
      handlers.push(handler);
    }
  }

  unsubscribe(event, handler) {
  }

  _informSubscribers(event) {
    if (event.storageArea != this._userStorage) return;
    if (event.key.indexOf(this._path) != 0) return;

    let value = {
      key: event.key.replace(this._path, ""),
      value: JSON.parse(event.newValue),
    };

    let handlers = this._subscribers[event.key];
    if (handlers) handlers.forEach((handler) => handler(value));

    handlers = this._subscribers[null];
    if (handlers) handlers.forEach((handler) => handler(value));
  }

  getValue(key) {
    let stringValue = this._userStorage.getItem(this._path + key);
    if (stringValue == null) return null;

    let value = JSON.parse(stringValue);
    return value;
  }

  getValues(subKey_or_regexp) {
    if (typeof subKey_or_regexp == "string") {
      subKey_or_regexp = "(" + subKey_or_regexp + ")";
    }

    let result = null;
    let keys = Object.keys(localStorage);

    for (let key of keys) {
      if (!key.startsWith(this._path)) continue;

      key = key.slice(this._path.length);

      if (!key.match(subKey_or_regexp)) continue;
      if (!result) result = {};

      result[key] = this.getValue(key);
    }

    return result;
  }

  setValue(key, value) {
    let stringValue = JSON.stringify(value);
    this._userStorage.setItem(this._path + key, stringValue);
  }

  removeValue(key) {
    this._userStorage.removeItem(this._path + key);
  }
}

function addCssRules() {
  document.body.insertAdjacentHTML(
  "afterbegin", `
    <style>

    #ssremover-button {
      background-image: linear-gradient(to bottom, #abc2ea 0, #707e97 100%);
      color: #ffffff;
    }
    iframe {
      width: 100%;
      height: 800px;
      display: none;
    }
    #ssremover-notification {
      min-width: 100px;
      font-size: 15px;
      padding: 12px;
      text-align: center;
      border-radius: 4px;
      box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
      background-color: #eefaee;
      border: 1px solid #bdedbb;
      opacity: 100%;
      position: fixed;
      z-index: 10;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      transition: all 0.3s ease !important;
    }
    #ssremover-notification.hide {
      opacity: 0%;
      bottom: -30px;
      transition: all 0.3s ease !important;
    }
    [data-test-ref="media-grid"] a {
      min-width: 28px;
      min-height: 28px;
    }
    [data-test-ref="media-grid"] [data-deleted="true"] {
      border: 2px dashed red !important;
    }
    .preview-delete-cover {
      position: absolute;
      top: 0px;
      left: 0px;
      width: 100%;
      height: 100%;
      pointer-events: none;
      opacity: 50%;
      z-index: 2;
      margin: 0px;
      background-color: rgba(200, 200, 200, 0.5);
      background-position: center center;
      background-size: 80%;
      background-repeat: no-repeat;
      background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIYAAABlCAYAAACIqZu9AAAACXBIWXMAAAsSAAALEgHS3X78AAADvklEQVR42u2dwU0cQRBFf9XNJ28G5mrJkknAMiEQAqEQyjoDyAAyWCQkX3EGcPKxfHBbsjhYljye/r/rtzSC0XKYfvOG3dnp+hVVhVkjIi4BnI/dp6o6ovGIiCsAZ2P3VFU3045lhhgRcQBwA+Dzq5ceAFxU1XMzIQ4A7gB8fPXSPYDLGTx2F+MPEFrKwcojySBgvHY3/ra7FNN4JBmENnKw80hCCMvLocAjSSEsK4cKjySGsJwcSjySHMIycqjxSAEI8nIo8kgRCLJyqPJIIQhycijzSDEIMnKo80hBCPRyrMBjq/8Y1ztDoJVjkhS/87jeZB5bPESLiGcAbyeeD4oHb5Ol+DVequrAIkYRXKxT5SCRAgBQVUH5PcakMe1thUkKtruSL13lIJRim3NRVf+8ATgAOAEoku0E4LDF3LrO25A83/8rRic5OszTV5Lnt48YK8Pr9HbpD2aWYl8xVoLZ8a4LvqWzFFPEUIbbVYrdxFCE3FmKXcVQgt1dit3FUIBuKX5uM6vdmR48PQC4GL/THVeLandiOR7Hzw/dpZgqxqrrGFaQYvpCnTHxiwHCg0QKihVcloNPCgoxLAefFDRiWA6+eCmqxcBN5aDMHKNbJd5MDtogOsrygSZyUKcT0taVLC4HfWQldcHRonJI5JjSV6ItJodMuK1EieIickglHsvUrorLIReDLVXULCqHZDa6XLW7mByygfmSMQgickh3UZDNxyCXQ761xtSFOptMIOIdgK8A3pAc0ncA76vqmzLXFJfiAOCWSAqMY7lVzz5PcSnuwLksUD77PC2F5VhCDLEFxLJypKWwHNJiiJcayMmRlsJySIqxWFGSjBxpKSyHlBiLly/Sy5GWwnJIiNGs0JlWjrQUloNajOaRCHRypKWwHJRiWApOOdJSWA4qMUgzuB4tx0QxSFP7Po3twXLAOZ9wzqeTgeFkYGeJw1ni7j4Adx9wvxK4X4l+vxK4w5E7HLknmnuiuYuiuyganvuuGprn6d7unu8uYrS9pVt43lsBOrb9hpBPjiNNT7SIYElfmZJkw/a0uKpiiaV9ylKME7FcgvFWYrx0lYJQjhcmMY6dpSCT47jVZJQ/gE1ZxNKBRwpfKbSRiSvwSFEY9Dma6jxSEIZMuKoyjxSDIZe4q8ojhWDIxjAr8kgRGPLZ3Go8UgCGvBSKPJIcxjJSqPFIYhjLSaHEI0lhLCuFCo8khLG8FAo8ciKM+85S/KUc97N4TO1wFBGXAM7H7lNVHdF4RMQVgLOxe6qqm1nH8gN744vOBojCtwAAAABJRU5ErkJggg==");
    }
    #ssremover-statistic, #ssremover-author {
      position: fixed;
      top: 0px;
      left: 0px;
      right: 0px;
      bottom: 0px;
      background-color: #85888f;
      z-index: 100;
    }
    #ssremover-statistic .statistic-window,
    #ssremover-author .author-window {
      position: fixed;
      width: calc(100% - 60px);
      min-width: 500px;
      max-width: 800px;
      height: max-content;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      margin: auto;
      border-radius: 16px;
      background-color: white;
      box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
    }
    #ssremover-statistic .statistic-window-header,
    #ssremover-author .author-window-header {
      display: inline-block;
      width: 100%;
    }
    .statistic-window-header *,
    .author-window-header * {
      margin: 20px 20px 0 20px;
    }
    #ssremover-statistic .statictic-title,
    #ssremover-author .author-title {
      font-size: 3.5rem;
      font-weight: 600;
      float: left;
    }
    .statistic-window-header .total-count-title {
      float: left;
      font-size: 13pt;
      color: #85888f;
      padding: 15px 10px 11px 0;
    }
    #total-count {
      margin: 0;
    }
    #ssremover-statistic button.close-button,
    #ssremover-author button.close-button {
      height: 30px;
      width: 30px;
      background-size: 70%;
      background-position: center;
      background-repeat: no-repeat;
      opacity: 50%;
      float: right;
      background-color: rgba(12, 18, 28, 0.0);
      border: none;
      border-radius: 30px;
      outline: none;
      transition: all 0.2s ease;
      background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0i0KHQu9C+0LlfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCA2NCA2NCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjQgNjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjc7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQ0KPC9zdHlsZT4NCjxsaW5lIGNsYXNzPSJzdDAiIHgxPSI1NiIgeTE9IjgiIHgyPSI3LjUiIHkyPSI1Ni41Ii8+DQo8bGluZSBjbGFzcz0ic3QwIiB4MT0iNTYiIHkxPSI1Ni41IiB4Mj0iNy41IiB5Mj0iOCIvPg0KPC9zdmc+DQo=");
    }
    #ssremover-statistic button.close-button:hover,
    #ssremover-author button.close-button:hover {
      opacity: 100%;
    }
    #ssremover-statistic button.close-button:active,
    #ssremover-author button.close-button:active {
      background-color: rgba(12, 18, 28, 0.2);
    }
    #ssremover-statistic #level-table {
      margin: 10px 20px 20px 20px;
      border: 1px solid rgba(12, 18, 28, 0.12);
      border-bottom: none;
      display: none;
    }
    #show-removal-levels:checked ~ .statistic-window #level-table{
      display: block;
    }
    #level-table .table-row,
    #level-table .table-header-row {
      align-items: center;
      height: auto;
      border-bottom: 1px solid rgba(12, 18, 28, 0.12);
      display: flex;
      align-items: center;
      justify-content: center;
    }
    #level-table .table-header-row {
      padding-top: 10px;
      padding-bottom: 10px;
      font-size: 12px;
      color:rgba(12, 18, 28, 0.87);
    }
    #level-table .table-row {
      height: 50px;
      color:rgba(12, 18, 28, 0.6);
    }
    #level-table #current-removal-level {
      font-weight: 600;
      font-size: 14pt;
      color:rgba(12, 18, 28, 0.87);
    }
    #level-table .table-col {
      text-align: center;
      float: left;
      width: 50%;
      padding: 0 10px 0 10px;
    }
    .image-levels,
    .image-licenses {
      height: 50px;
      width: 50px;
      display: inline-block;
      background-size: 100%;
      background-repeat: no-repeat;
    }
    .image-levels {
      background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0i0KHQu9C+0LlfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCA0NCA0MCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDQgNDA7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzMzM7c3Ryb2tlLXdpZHRoOjU7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQ0KPC9zdHlsZT4NCjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03LjUsMTEuNWgyOXYxOWMwLDMuOS0zLjEsNy03LDdoLTIyVjExLjV6Ii8+DQo8bGluZSBjbGFzcz0ic3QwIiB4MT0iNDQiIHkxPSIxMS41IiB4Mj0iMCIgeTI9IjExLjUiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QwIiBwb2ludHM9IjMxLjcsMTEuNSAzMS43LDIuNSAxMi4zLDIuNSAxMi4zLDExLjUgIi8+DQo8bGluZSBjbGFzcz0ic3QwIiB4MT0iMjYuOCIgeTE9IjE4IiB4Mj0iMjYuOCIgeTI9IjMxIi8+DQo8bGluZSBjbGFzcz0ic3QwIiB4MT0iMTcuMiIgeTE9IjE4IiB4Mj0iMTcuMiIgeTI9IjMxIi8+DQo8L3N2Zz4NCg==");
    }
    .image-licenses {
      background-image: url(/public/static/image-licenses-9dc254dc4522879067c960106317af62.svg);
      background-position-y: 3px;
    }
    #ssremover-statistic .message-alert,
    #ssremover-statistic .message-notice,
    #ssremover-author .message-notice,
    #ssremover-author .message-alert {
      margin: 0 20px 20px 20px;
      font-size: 16px;
      border-radius: 4px;
    }
    #ssremover-statistic .message-alert div,
    #ssremover-statistic .message-notice div {
      width: calc(100% - 40px);
      display: inline-block;
      padding: 18px 0 18px 18px;
    }
    #ssremover-author .message-notice div,
    #ssremover-author .message-alert {
      display: block;
      padding: 18px;
    }
    #ssremover-statistic .message-alert,
    #ssremover-author .message-alert {
      background-color: #faeeee;
      border: 1px solid #edbbbc;
      color: rgb(51, 51, 51);
    }
    #ssremover-statistic .message-notice,
    #ssremover-author .message-notice {
      background-color: #eefaee;
      border: 1px solid #bdedbb;
    }
    #ssremover-statistic #disable-removal-levels,
    #show-removal-levels:checked ~ .statistic-window #enable-removal-levels {
      display: none;
    }
    #ssremover-statistic #enable-removal-levels,
    #show-removal-levels:checked ~ .statistic-window #disable-removal-levels{
      display: block;
    }
    #show-hidden-messages {
      text-align: center;
      padding: 0 0 5px 0;
    }
    .message-alert .close-button,
    .message-notice .close-button {
      /* font-size: 1.8rem !important; */
      width: 20px !important;
      height: 20px !important;
    }
    #statistic-cover, #ssremover-author-cover {
      width: 100%;
      height: 100%;
    }
    @font-face {
      font-family: 'Shutterstock-Icons';
      src: url('https://www.shutterstock.com/sstk-assets/static/Shutterstock-Icons.woff2')
        format('woff2');
      font-weight: normal;
      font-style: normal;
    }

    </style>
  `);
}


})();