前甲「乙丙」
import assert from "node:assert/strict";
import { test } from "node:test";
import { setupDom } from "./helpers.mjs";
import { createJuzhen } from "../dist/juzhen.mjs";
function sameLine(document)
{
const view = document.defaultView;
const previous = globalThis.ResizeObserver;
const elementRects = view.Element.prototype.getClientRects;
const rangeRects = view.Range.prototype.getClientRects;
const rect = { top: 0, bottom: 20, left: 0, right: 20, width: 20, height: 20 };
globalThis.ResizeObserver = class { observe() {} disconnect() {} };
view.Element.prototype.getClientRects = () => [ rect ];
view.Range.prototype.getClientRects = () => [ rect ];
return () =>
{
view.Element.prototype.getClientRects = elementRects;
view.Range.prototype.getClientRects = rangeRects;
if (previous === undefined) { delete globalThis.ResizeObserver; }
else { globalThis.ResizeObserver = previous; }
};
}
for (const orphan of [ false, true ])
{
test("line edge:同行 orphan 不製造行首 orphan=" + orphan, () =>
{
const { dom, document } = setupDom(' 前甲「乙丙」
前甲
甲」「乙
'); const restore = sameLine(document); const jz = createJuzhen({ orphan: false, spacing: false, longWord: false }); try { jz.render(); for (const char of document.querySelectorAll("jz-char")) { assert.equal(char.classList.contains("jz-half-le"), !hidden); } } finally { jz.revert(); restore(); dom.window.close(); } }); } for (const action of [ "insert", "replace-source", "edit-fragment", "edit-source" ]) { test("splitoff:回退須驗來源身份並保留外部樣式 action=" + action, () => { const { dom, document } = setupDom('甲乙丙,丁
'); const p = document.querySelector("p"); const source = p.querySelector("strong"); const jz = createJuzhen({ orphan: false, spacing: false, longWord: false }); try { jz.render(p); const fragment = p.querySelector('[data-jz-kind="splitoff"]'); assert.ok(fragment); if (action === "insert") { const added = document.createElement("strong"); added.className = "external"; added.textContent = "新"; fragment.before(added); } else if (action === "replace-source") { const replacement = source.cloneNode(false); replacement.textContent = "替"; source.replaceWith(replacement); } else if (action === "edit-fragment") { fragment.className = "edited"; } else { source.className = "edited"; } jz.revert(p); assert.equal(fragment.parentNode, p, "來源失效時保留片段當前父與樣式"); assert.equal(fragment.textContent, "丙"); assert.equal(fragment.className, action === "edit-fragment" ? "edited" : "original"); assert.equal(p.querySelector("[data-jz]"), null); if (action === "insert") { assert.equal(p.querySelector(".external").textContent, "新"); assert.equal(p.textContent, "甲乙新丙,丁"); } if (action === "replace-source") { assert.equal(p.textContent, "替丙,丁"); } jz.revert(p); assert.equal(fragment.textContent, "丙", "撤回所有權後再次 revert 不碰片段"); } finally { jz.revert(p); dom.window.close(); } }); } test("splitoff:同來源多層切分仍完整還原作者結構", () => { const { dom, document } = setupDom('「甲乙丙丁」
'); const p = document.querySelector("p"); const original = p.innerHTML; const jz = createJuzhen(); try { jz.render(p); jz.revert(p); assert.equal(p.innerHTML, original); } finally { jz.revert(p); dom.window.close(); } }); for (const [ html, defaultLang, half ] of [ [ '甲,乙
甲,乙
甲,乙
甲,乙
' + html + '
'); const p = document.querySelector("p"); const original = p.innerHTML; const owners = [ createJuzhen({ jiya: false, spacing: false, orphan: false, longWord: false }), createJuzhen({ jiya: false, spacing: false, jinze: false, longWord: false }), createJuzhen({ spacing: false, orphan: false, longWord: false }), ]; try { owners.forEach((owner) => owner.render(p)); order.forEach((index) => owners[index].revert(p)); assert.equal(p.innerHTML, original, html + " order=" + order.join(",")); } finally { owners.forEach((owner) => owner.disconnect()); dom.window.close(); } } } });