2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

User:机智的小鱼君/gadget/MonacoEditor.js

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索

注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox/Safari:按住“Shift”的同时单击“刷新”,或按“Ctrl-F5”或“Ctrl-R”(Mac为“⌘-R”)
  • Google Chrome:按“Ctrl-Shift-R”(Mac为“⌘-Shift-R”)
  • Internet Explorer:按住“Ctrl”的同时单击“刷新”,或按“Ctrl-F5”
  • Opera:在“工具→首选项”中清除缓存
如果您已登录但该页面出现未登录状态,请尝试在地址栏的地址最后添加代码?_=1来访问最新页面。
添加代码后的本页地址如下:-{R|https://moegirl.icu/User:%E6%9C%BA%E6%99%BA%E7%9A%84%E5%B0%8F%E9%B1%BC%E5%90%9B/gadget/MonacoEditor.js?_=1}-
/**
 * MediaWiki Gadget MonacoEditor
 * @author Dragon-Fish <[email protected]>
 * @license MIT
 */
;(async () => {
  const textarea = document.querySelector('textarea#wpTextbox1')
  const language = getLangFromContentModel()
  if (!textarea || !language) return
  const initialValue = textarea.value
  const MONACO_CDN_BASE =
    window.MONACO_CDN_BASE ||
    'https://cdn.jsdelivr.net/npm/[email protected]/min'
  const MONACO_EXTRA_LIBS = [
    ...[window.MONACO_EXTRA_LIBS || []],
    [
      'https://cdn.jsdelivr.net/npm/@wikimedia/[email protected]/MediaWiki.d.ts',
      'MediaWiki.d.ts',
    ],
    [
      'https://cdn.jsdelivr.net/npm/@types/jquery/JQuery.d.ts',
      'jquery/JQuery.d.ts',
    ],
    [
      'https://cdn.jsdelivr.net/npm/@types/jquery/JQueryStatic.d.ts',
      'jquery/JQueryStatic.d.ts',
    ],
    ['declare const $: JQueryStatic', 'jquery/JQueryGlobal.d.ts'],
  ]

  window.MonacoEnvironment = {
    ...window.MonacoEnvironment,
    baseUrl: MONACO_CDN_BASE,
    getWorkerUrl(workerId, label) {
      let path = 'base/worker/workerMain.js'

      if (label === 'json') {
        path = 'language/json/jsonWorker.js'
      } else if (label === 'css' || label === 'scss' || label === 'less') {
        path = 'language/css/cssWorker.js'
      } else if (
        label === 'html' ||
        label === 'handlebars' ||
        label === 'razor'
      ) {
        path = 'language/html/htmlWorker.js'
      } else if (label === 'typescript' || label === 'javascript') {
        path = 'language/typescript/tsWorker.js'
      }

      return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
self.MonacoEnvironment = {
    baseUrl: '${MONACO_CDN_BASE}'
}
importScripts('${MONACO_CDN_BASE}/vs/${path}')
        `)}`
    },
  }

  await loadScript(`${MONACO_CDN_BASE}/vs/loader.js`)
  const require = window.require
  require.config({
    paths: {
      vs: `${MONACO_CDN_BASE}/vs`,
    },
  })
  require(['vs/editor/editor.main'], () => {
    mw?.hook('monaco').fire(monaco)

    const container = document.createElement('div')
    container.style.width = '100%'
    container.style.height = '75vh'
    document
      .querySelector('form#editform .editOptions')
      ?.insertAdjacentElement('beforebegin', container)
    document.querySelector('form#editform .wikiEditor-ui').style.display =
      'none'
    textarea.style.display = 'none'

    const model = monaco.editor.createModel(initialValue, language)
    const editor = monaco.editor.create(container, {
      model,
      automaticLayout: true,
      theme: 'vs-dark',
      tabSize: 2,
    })

    model.onDidChangeContent(() => {
      textarea.value = model.getValue()
    })

    console.info('[MONACO] Rendered', editor, model)
    mw?.hook('monaco.editor').fire({
      monaco,
      editor,
      model,
      language,
      addExtraLib: (...args) => addExtraLib(monaco, model, ...args),
      addExternalExtraLib: (...args) => addExternalExtraLib(monaco, model, ...args),
    })

    if (language === 'javascript') {
      addBatchExtraLibs(monaco, model, MONACO_EXTRA_LIBS)
    }
  })

  async function loadScript(src = '') {
    return new Promise((resolve, reject) => {
      const s = document.createElement('script')
      s.src = src
      document.body.appendChild(s)
      s.addEventListener('load', resolve)
      s.addEventListener('error', reject)
    })
  }

  function getLangFromContentModel() {
    const nsNumber = mw.config.get('wgNamespaceNumber')
    const pageName = mw.config.get('wgPageName')
    const contentModel = mw.config.get('wgPageContentModel', '').toLowerCase()
    if (pageName.endsWith('.js') || contentModel === 'javascript') {
      return 'javascript'
    } else if (pageName.endsWith('.css') || contentModel === 'css') {
      return 'css'
    } else if (
      (nsNumber === 828 || ['scribunto', 'lua'].includes(contentModel)) &&
      !pageName.endsWith('/doc')
    ) {
      return 'lua'
    } else if (nsNumber === 274) {
      return 'html'
    } else if (pageName.endsWith('.json')) {
      return 'json'
    }
  }

  /**
   * @param monaco
   * @param model
   * @param {string} libSource
   * @param {string?} fileName
   */
  function addExtraLib(monaco, model, libSource, fileName = '') {
    const URI_NS = 'ts:mw'
    fileName = fileName || `${crypto.randomUUID()}.d.ts`
    monaco.languages.typescript.javascriptDefaults.addExtraLib(
      libSource,
      `${URI_NS}/${fileName}`
    )
    model.updateOptions({
      uri: monaco.Uri.parse(`${URI_NS}/main.js`),
    })
  }
  /**
   * @param monaco
   * @param model
   * @param {string} libUrl
   * @param {string?} fileName
   */
  async function addExternalExtraLib(monaco, model, libUrl, fileName) {
    const libSource = await fetch(libUrl).then((i) => i.text())
    fileName = fileName || libSource.split('/').pop()?.split('?')[0]
    return addExtraLib(monaco, model, libSource, fileName)
  }
  /**
   * internal helper function
   * @param {(string | [string, string])[]} libs
   */
  async function addBatchExtraLibs(monaco, model, libs = []) {
    return Promise.all(
      libs.map((lib) => {
        if (typeof lib === 'string') {
          lib = [lib]
        }
        if (!Array.isArray(lib)) return Promise.resolve(null)
        if (typeof lib?.[0] !== 'string') return Promise.resolve(null)
        const helper = lib[0]?.startsWith('http')
          ? addExternalExtraLib
          : addExtraLib
        return helper(monaco, model, lib[0], lib[1])
      })
    )
  }
})()