TYPE-MOON Write Leave Alert

Show the browser's default leave-page warning while TYPE-MOON SmartEditor2 is present.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         TYPE-MOON Write Leave Alert
// @name:ko      TYPE-MOON 글쓰기 이탈 경고
// @namespace    https://www.typemoon.net/
// @version      0.1.1
// @description  Show the browser's default leave-page warning while TYPE-MOON SmartEditor2 is present.
// @description:ko TYPE-MOON SmartEditor2가 있는 동안 브라우저 기본 페이지 이탈 확인창을 표시합니다.
// @license      MIT
// @match        https://www.typemoon.net/*
// @match        http://www.typemoon.net/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(() => {
  'use strict';

  const EDITOR_IFRAME_SRC =
    'https://www.typemoon.net/plugin/editor/smarteditor2/SmartEditor2Skin.html';
  const EDITOR_IFRAME_SELECTOR = [
    `iframe[src="${EDITOR_IFRAME_SRC}"]`,
    `iframe[src^="${EDITOR_IFRAME_SRC}?"]`,
  ].join(',');
  const WRITE_UPDATE_URL = 'https://www.typemoon.net/bbs/write_update.php';

  let leaveWarningEnabled = false;
  let allowNextUnload = false;

  function hasSmartEditorIframe() {
    return document.querySelector(EDITOR_IFRAME_SELECTOR) !== null;
  }

  function syncLeaveWarningState() {
    leaveWarningEnabled = hasSmartEditorIframe();
  }

  function isWriteCompletionForm(form) {
    if (!(form instanceof HTMLFormElement)) {
      return false;
    }

    const name = form.getAttribute('name') || form.name || '';
    const method = form.getAttribute('method') || form.method || '';
    const action = form.getAttribute('action') || form.action || '';

    if (name.toLowerCase() !== 'fwrite') {
      return false;
    }

    if (method.toLowerCase() !== 'post') {
      return false;
    }

    try {
      return new URL(action, window.location.href).href === WRITE_UPDATE_URL;
    } catch (_error) {
      return false;
    }
  }

  function handleSubmit(event) {
    if (!isWriteCompletionForm(event.target)) {
      return;
    }

    allowNextUnload = true;
    window.setTimeout(() => {
      allowNextUnload = false;
    }, 1000);
  }

  function handleBeforeUnload(event) {
    if (!leaveWarningEnabled || allowNextUnload) {
      allowNextUnload = false;
      return undefined;
    }

    event.preventDefault();
    event.returnValue = '';
    return '';
  }

  function startWatchingEditorPresence() {
    const root = document.documentElement || document.body;
    if (!root) {
      document.addEventListener('DOMContentLoaded', startWatchingEditorPresence, {
        once: true,
      });
      return;
    }

    syncLeaveWarningState();

    const observer = new MutationObserver(syncLeaveWarningState);
    observer.observe(root, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['src'],
    });
  }

  document.addEventListener('submit', handleSubmit, { capture: true });
  window.addEventListener('beforeunload', handleBeforeUnload, { capture: true });
  startWatchingEditorPresence();
})();