Torn Utils Library

Shared utility functions for Torn userscripts

이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/569328/1772270/Torn%20Utils%20Library.js을(를) 사용하여 포함하는 라이브러리입니다.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Torn Utils Library
// @namespace    https://www.torn.com/
// @version      1.0
// @description  Shared utility functions for Torn userscripts
// @author       PFangy
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  // Create global namespace if it doesn't exist
  window.TornUtils = window.TornUtils || {};

  /**
   * Wait for a DOM element to appear
   * Useful for SPA / React pages like Torn
   *
   * @param {string} selector
   * @param {number} timeout
   * @returns {Promise<Element>}
   */
  window.TornUtils.waitForElement = function (selector, timeout = 10000) {
    return new Promise((resolve, reject) => {

      const element = document.querySelector(selector);
      if (element) {
        resolve(element);
        return;
      }

      const observer = new MutationObserver(() => {
        const el = document.querySelector(selector);
        if (el) {
          observer.disconnect();
          resolve(el);
        }
      });

      observer.observe(document.body, {
        childList: true,
        subtree: true
      });

      if (timeout) {
        setTimeout(() => {
          observer.disconnect();
          reject(new Error(`waitForElement timeout: ${selector}`));
        }, timeout);
      }
    });
  };

  /**
   * Wait for multiple elements
   */
  window.TornUtils.waitForElements = function (selector, timeout = 10000) {
    return new Promise((resolve, reject) => {

      const elements = document.querySelectorAll(selector);
      if (elements.length) {
        resolve(elements);
        return;
      }

      const observer = new MutationObserver(() => {
        const els = document.querySelectorAll(selector);
        if (els.length) {
          observer.disconnect();
          resolve(els);
        }
      });

      observer.observe(document.body, {
        childList: true,
        subtree: true
      });

      if (timeout) {
        setTimeout(() => {
          observer.disconnect();
          reject(new Error(`waitForElements timeout: ${selector}`));
        }, timeout);
      }
    });
  };

  /**
   * Observe element changes
   */
  window.TornUtils.observe = function (target, callback, options = { childList: true, subtree: true }) {
    const observer = new MutationObserver(callback);
    observer.observe(target, options);
    return observer;
  };

  /**
   * Wait until DOM ready
   */
  window.TornUtils.domReady = function () {
    return new Promise(resolve => {
      if (document.readyState === 'complete' || document.readyState === 'interactive') {
        resolve();
      } else {
        document.addEventListener('DOMContentLoaded', resolve);
      }
    });
  };

})();