GreasyFork Code: Monaco Editor

12/17/2023, 2:31:22 PM

// ==UserScript==
// @name        GreasyFork Code: Monaco Editor
// @namespace   Violentmonkey Scripts
// @match       https://greasyfork.org/*
// @match       https://sleazyfork.org/*
// @grant       none
// @version     0.1.12
// @author      CY Fung
// @description 12/17/2023, 2:31:22 PM
// @run-at document-start
// @unwrap
// @license     MIT
// ==/UserScript==

// localStorage.darkMode = 'true'
// localStorage.autoMonacoEditor = 'true'

(() => {

  const vsPath = "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs";

  const editorOptions = {
    automaticLayout: true,
    foldingStrategy: 'indentation',
    lineNumbers: 'on',
    readOnly: false,
    minimap: {
      enabled: false,
    },
    cursorStyle: 'line',
    scrollBeyondLastLine: false,
    showUnused: true,
    showDeprecated: true,
  };

  const compilerOptions = {
    allowNonTsExtensions: true,
    checkJs: true,
    noImplicitAny: true,

    allowJs: true,
    noUnusedLocals: false,
    noFallthroughCasesInSwitch: false,
    noImplicitThis: false,

  };

  const cssText01 = `
  .monaco-editor-container{
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      display: none;
  }
  [monaco-editor-status="1"] .monaco-editor-container{
    display: block;
  }
  [monaco-editor-status="1"] .monaco-controlled-textarea{
    display: none;
  }
  [monaco-editor-status="2"] .monaco-editor-container{
    display: none;
  }
  [monaco-editor-status="2"] .monaco-controlled-textarea{
    display: block;
  }
  `;

  const elmSet = {};

  HTMLInputElement.prototype.addEventListener177 = HTMLInputElement.prototype.addEventListener;
  HTMLInputElement.prototype.addEventListener = function () {
    if (arguments.length === 2 && arguments[0] === 'change' && (arguments[1] || 0).name === 'handleChange') {
      const rawF = arguments[1];
      if (this.id === 'enable-source-editor-code') {
        arguments[1] = function handleChange(e) {
          if (typeof ((e || 0).target || 0).checked === 'boolean' && document.getElementById('ace-editor')) return rawF.apply(this, arguments);
        }
      }
    }
    return this.addEventListener177.apply(this, arguments);
  }

  function loadResource(type, url) {
    if (type === 'css') {
      return new Promise(resolve => {
        var link = document.createElement('link');
        var onload = function () {
          link.removeEventListener('load', onload, false);
          resolve();
        }
        link.addEventListener('load', onload, false);
        link.rel = 'stylesheet';
        link.href = url;
        document.head.appendChild(link);
      });
    } else if (type === 'js') {
      return new Promise(resolve => {
        var script = document.createElement('script');
        var onload = function () {
          script.removeEventListener('load', onload, false);
          resolve();
        }
        script.addEventListener('load', onload, false);
        script.src = url;
        document.head.appendChild(script);
      })
    }
  }

  function onChange817(e) {

    const target = (e || 0).target || null;

    if (!target) return;

    let monacoStatus = target.getAttribute('monaco-status')
    if (monacoStatus) {

      e.stopImmediatePropagation();
      e.stopPropagation();
      e.preventDefault();

      const textAreaParent = elmSet.textAreaParent;
      const textArea = elmSet.textArea;
      const editor = elmSet.editor;

      // console.log(monacoStatus)
      if (monacoStatus === '1') {
        // elmSet.container.replaceWith(elmSet.textArea);



        textAreaParent.setAttribute('monaco-editor-status', '2')
        target.setAttribute('monaco-status', monacoStatus = '2')
        if (textArea.style.display) textArea.style.display = '';
        return;

      } else if (monacoStatus === '2') {

        // elmSet.textArea.replaceWith(elmSet.container);

        const currentCode = editor.getValue();
        const currentText = textArea.value;
        if (currentCode !== currentText) {
          editor.setValue(currentText);
        }

        textAreaParent.setAttribute('monaco-editor-status', '1')
        target.setAttribute('monaco-status', monacoStatus = '1')
        if (textArea.style.display) textArea.style.display = '';
        return;
      } else {
        return;
      }

    }

    const codeId = target.getAttribute('data-related-editor') || '';
    if (!codeId) return;

    const textArea = document.getElementById(codeId);

    if (!textArea) return;

    const codeLang = target.getAttribute('data-editor-language'); // 'javascript', 'css'

    if (!codeLang) return;

    if (document.getElementById('ace-editor')) return;

    target.setAttribute('monaco-status', '1');


    e.stopImmediatePropagation();
    e.stopPropagation();
    e.preventDefault();


    // Setting up Monaco Editor requirements
    let require = {
      paths: {
        vs: vsPath,
      },
    };

    window.require = (window.require || {});
    window.require.paths = (window.require.paths || {});
    Object.assign(window.require.paths, require.paths);


    const addCssText = (id, text) => {
      if (document.getElementById(id)) return;
      const style = document.createElement('style');
      style.id = id;
      style.textContent = text;
      document.head.appendChild(style);

    }


    (async function () {

      // Dynamically load CSS and JS
      await loadResource('css', `${vsPath}/editor/editor.main.css`);
      await loadResource('js', `${vsPath}/loader.js`);
      await loadResource('js', `${vsPath}/editor/editor.main.nls.js`);
      await loadResource('js', `${vsPath}/editor/editor.main.js`);

      addCssText('rmbnctzOOksi', cssText01);

      monaco.languages.typescript.javascriptDefaults.setCompilerOptions(Object.assign({
        target: monaco.languages.typescript.ScriptTarget.ES2018,
      }, compilerOptions));

      const container = document.createElement('div');
      container.className = 'monaco-editor-container';
      container.style.height = textArea.getBoundingClientRect().height + 'px';
      // textArea.replaceWith(container);
      textArea.classList.add('monaco-controlled-textarea');
      const textAreaParent = elmSet.textAreaParent = textArea.parentNode;
      textAreaParent.setAttribute('monaco-editor-status', '1');
      textAreaParent.insertBefore(container, textArea.nextSibling);

      elmSet.textArea = textArea;
      elmSet.container = container;

      if (textArea.style.display) textArea.style.display = '';

      const monacoLangs = {
        'javascript': 'javascript',
        'css': 'css',
      };

      const monacoLang = monacoLangs[codeLang];


      const editor = monaco.editor.create(container, Object.assign({
        value: textArea.value,
        language: monacoLang
      }, editorOptions));

      elmSet.editor = editor;

      if (document.documentElement.hasAttribute('dark')) monaco.editor.setTheme("vs-dark");

      editor.onDidChangeModelContent(e => {
        elmSet.textArea.value = editor.getValue()
      });


      // console.log(monaco, monaco.onDidChangeModelContent)

      //   window.editor.getModel().onDidChangeContent((event) => {
      //   render();
      // });

      //   editor.setTheme

      //   onDidChangeContent is attached to a model, and will only apply to that model
      // onDidChangeModelContent



    })();


  }

  function preloadResources() {

    const cssResources = [
      `${vsPath}/editor/editor.main.css`,
    ];
    const jsResources = [
      `${vsPath}/loader.js`,
      `${vsPath}/editor/editor.main.nls.js`,
      `${vsPath}/editor/editor.main.js`,
    ];
    const template = document.createElement('template');
    const frag = template.content;

    for (const url of cssResources) {
      const link = document.createElement('link');
      link.setAttribute('rel', 'preload');
      link.setAttribute('as', 'style');
      link.setAttribute('href', url);
      frag.appendChild(link);
    }
    for (const url of jsResources) {
      const link = document.createElement('link');
      link.setAttribute('rel', 'preload');
      link.setAttribute('as', 'script');
      link.setAttribute('href', url);
      frag.appendChild(link);
    }
    document.head.appendChild(frag);
  }



  function onReady() {
    window.removeEventListener("DOMContentLoaded", onReady, false);

    if (location.pathname.includes('/script')) {
      preloadResources();
    }

    // if (localStorage.darkMode === 'true') document.documentElement.setAttribute('dark', '')

    const checkerbox = document.querySelector('input#enable-source-editor-code[name="enable-source-editor-code"]');

    if (checkerbox) {
      checkerbox.addEventListener('change', onChange817, { once: false, capture: true, passive: false });
      if (localStorage.autoMonacoEditor === 'true' && requestIdleCallback) {

        requestIdleCallback(() => {
          if (checkerbox.checked === false && checkerbox.isConnected) checkerbox.click();
          else if (checkerbox.checked === true && document.getElementById('ace-editor') && checkerbox.isConnected) {

            Promise.resolve().then(() => checkerbox.click()).then(() => checkerbox.click())
          }

        });

      }
    }



  }


  Promise.resolve().then(() => {

    if (document.readyState !== 'loading') {
      onReady();
    } else {
      window.addEventListener("DOMContentLoaded", onReady, false);
    }

  });

})();