fix: 修复行缘、切分还原与语言解析并同步文档
保留逐字 margin/jiya.bias 字体后备方案;补齐 demo 重排、量测边界说明及共享规则 1.0.1 反例向量。 验证:完整构建通过,Node 376 项、WKWebView 64 项及原生 demo 9 项通过;Firefox 仍暂缓。
This commit is contained in:
Vendored
+2
@@ -7,6 +7,8 @@ export interface CreateOptions {
|
||||
/** 文本內容。 */
|
||||
text?: string;
|
||||
}
|
||||
/** 切分時記錄真實來源身份;須於 clone 的排版標記覆寫前呼叫。 */
|
||||
export declare function registerSplitoffSource(clone: Element, source: Element, pass: string, ownedElements: WeakSet<Element>): void;
|
||||
/** 完成受控解包後登記;只接受真實、已移除且空的受管產物。 */
|
||||
export declare function recordUnwrappedParent(el: Element, parent: Node, ownedElements: WeakSet<Element>): void;
|
||||
/** 其他 pass/實例合法撤回來源包裹後,搬移記錄沿受控解包鏈查詢現父。 */
|
||||
|
||||
Vendored
+2
-2
@@ -82,8 +82,8 @@ export declare class Finder {
|
||||
* collectRuns/jiyaAdjacency/jinze/orphan/split 各 pass 逐元素重算——以 categoryCache
|
||||
* 記憶化砍除跨 pass 重複。安全性:單次 render 內一元素之分類**恆定**(class/skip 屬性/
|
||||
* nodeName 皆不因 render 變更;jz-* 包裹為新建元素、按 nodeName 分類亦恆定),且 Finder
|
||||
* 隨 makeCtx 每次 render 重建 → 快取僅存活單次 render,無跨 render 失效問題(持久化屬 C1,
|
||||
* 另議)。
|
||||
* 隨實例持久化,跨 render/revert 復用;屬性或位置變化由 invalidateSubtree 清除受影響
|
||||
* 子樹,不能沿用舊祖先鏈結果。
|
||||
*/
|
||||
category(el: Element): InlineCategory;
|
||||
private computeCategory;
|
||||
|
||||
Vendored
+3
-2
@@ -20,12 +20,13 @@ export interface LocaleConfig {
|
||||
stylePolicy?: StyleRule[];
|
||||
}
|
||||
/**
|
||||
* 由節點向上找最近帶 lang 屬性之祖先,回傳小寫值;無則回傳 fallback。
|
||||
* 由節點(含元素自身)向上找最近 lang,回傳小寫值;空值表示語言未知,僅缺失時回退。
|
||||
*/
|
||||
export declare function effectiveLang(node: Node, fallback: string): string;
|
||||
/**
|
||||
* 將 BCP-47 語言標籤歸類。前綴以子標籤邊界比對('zh-tw' 命中 zh-Hant,
|
||||
* 'zhx' 不命中 zh)。香港/台灣繁體歸 zh-Hant;含 Hant/Hans 子標籤者優先。
|
||||
* 'zhx' 不命中 zh)。只讀 BCP-47 的 script/region 位置,不把 extension/private-use
|
||||
* 內容當成腳本或地區;本函數不是完整標籤驗證器。
|
||||
*/
|
||||
export declare function classify(lang: string): LangClass;
|
||||
/**
|
||||
|
||||
Vendored
+4
-4
@@ -43,7 +43,7 @@ export declare const BD_LIGA = "\u2026\u2014";
|
||||
/** 全寬標點之六子類鍵。 */
|
||||
export type BdSubclass = "stop" | "pause" | "open" | "close" | "middle" | "liga";
|
||||
/**
|
||||
* 字符分類規則集(§4.2):把「中文/西文/全寬標點各子類」各表為 regex 字元類片段。
|
||||
* 字符分類規則集(§4.2):中文/西文表為 regex 字元類片段,標點各子類為逐碼點字串(不解析範圍)。
|
||||
* 各 pass(spacing 之 CJK/ANS 正則、jiya 之 classifyBiaodian、jinze 之 anyCjk)皆由此
|
||||
* 取分類,不再各自硬編碼。內置默認見 DEFAULT_RULESET;消費端經 options.charClass 擴充
|
||||
* (resolveRuleset)。
|
||||
@@ -60,11 +60,11 @@ export interface Ruleset {
|
||||
}
|
||||
/** 消費端字符分類覆寫(追加語義;見 README「自定義字符分類」)。 */
|
||||
export interface CharClassOverride {
|
||||
/** 追加為中文(CJK)。 */
|
||||
/** 追加為中文(CJK):regex 字元類片段,特殊字元須轉義。 */
|
||||
cjk?: string;
|
||||
/** 追加為西文(半寬標點亦歸此,按西文處理間隙);併入 ansAfter/ansBefore。 */
|
||||
/** 追加為西文:regex 字元類片段,特殊字元須轉義;併入 ansAfter/ansBefore。 */
|
||||
western?: string;
|
||||
/** 追加為全寬標點,按子類。 */
|
||||
/** 追加為全寬標點,按子類列出逐碼點字串;例如 "①②③",不解析 "①-③" 為範圍。 */
|
||||
biaodian?: Partial<Record<BdSubclass, string>>;
|
||||
}
|
||||
/** 內置默認規則集(即現狀硬編碼值)。 */
|
||||
|
||||
Vendored
+84
-16
@@ -328,8 +328,8 @@ var Juzhen = (() => {
|
||||
* collectRuns/jiyaAdjacency/jinze/orphan/split 各 pass 逐元素重算——以 categoryCache
|
||||
* 記憶化砍除跨 pass 重複。安全性:單次 render 內一元素之分類**恆定**(class/skip 屬性/
|
||||
* nodeName 皆不因 render 變更;jz-* 包裹為新建元素、按 nodeName 分類亦恆定),且 Finder
|
||||
* 隨 makeCtx 每次 render 重建 → 快取僅存活單次 render,無跨 render 失效問題(持久化屬 C1,
|
||||
* 另議)。
|
||||
* 隨實例持久化,跨 render/revert 復用;屬性或位置變化由 invalidateSubtree 清除受影響
|
||||
* 子樹,不能沿用舊祖先鏈結果。
|
||||
*/
|
||||
category(el) {
|
||||
const cached = this.categoryCache.get(el);
|
||||
@@ -837,6 +837,45 @@ var Juzhen = (() => {
|
||||
var ATTRIBUTE_WRITERS = /* @__PURE__ */ new WeakMap();
|
||||
var OWNER_ATTRIBUTES = /* @__PURE__ */ new WeakMap();
|
||||
var UNWRAPPED_PARENTS = /* @__PURE__ */ new WeakMap();
|
||||
var SPLITOFF_SOURCES = /* @__PURE__ */ new WeakMap();
|
||||
function registerSplitoffSource(clone, source, pass, ownedElements) {
|
||||
if (!ownedElements.has(clone) || !SPLITOFF_TAGS.has(clone.nodeName) || clone.nodeName !== source.nodeName) {
|
||||
return;
|
||||
}
|
||||
const earlier = SPLITOFF_SOURCES.get(source);
|
||||
const inherited = earlier && source.getAttribute(PASS_ATTR) === earlier.pass && source.getAttribute(KIND_ATTR) === "splitoff" ? earlier : null;
|
||||
SPLITOFF_SOURCES.set(clone, {
|
||||
source,
|
||||
pass,
|
||||
originalPass: inherited ? inherited.originalPass : clone.getAttribute(PASS_ATTR),
|
||||
originalKind: inherited ? inherited.originalKind : clone.getAttribute(KIND_ATTR)
|
||||
});
|
||||
}
|
||||
function sameSplitAttributes(a, b) {
|
||||
const attrs = (el) => el.getAttributeNames().filter((name) => name !== PASS_ATTR && name !== KIND_ATTR);
|
||||
const names = attrs(a);
|
||||
return names.length === attrs(b).length && names.every((name) => a.getAttribute(name) === b.getAttribute(name));
|
||||
}
|
||||
function splitOrigin(node) {
|
||||
let current = resolveUnwrappedParent(node);
|
||||
const visited = /* @__PURE__ */ new Set();
|
||||
while (current) {
|
||||
if (visited.has(current)) {
|
||||
return null;
|
||||
}
|
||||
visited.add(current);
|
||||
if (current.nodeType !== 1) {
|
||||
return current;
|
||||
}
|
||||
const el = current;
|
||||
const record = SPLITOFF_SOURCES.get(el);
|
||||
if (!record || el.getAttribute(PASS_ATTR) !== record.pass || el.getAttribute(KIND_ATTR) !== "splitoff") {
|
||||
return current;
|
||||
}
|
||||
current = resolveUnwrappedParent(record.source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function recordUnwrappedParent(el, parent, ownedElements) {
|
||||
const pass = el.getAttribute(PASS_ATTR);
|
||||
if (ownedElements.has(el) && pass && knownProduct(pass, el.getAttribute(KIND_ATTR), el) && !el.parentNode && !el.hasChildNodes()) {
|
||||
@@ -1022,20 +1061,32 @@ var Juzhen = (() => {
|
||||
continue;
|
||||
}
|
||||
touched.add(parent);
|
||||
const record = SPLITOFF_SOURCES.get(so);
|
||||
if (!record || record.pass !== pass) {
|
||||
continue;
|
||||
}
|
||||
const source = resolveUnwrappedParent(record.source);
|
||||
const prev = so.previousSibling;
|
||||
if (prev && prev.nodeType === 1 && prev.nodeName === so.nodeName) {
|
||||
const origin = source ? splitOrigin(source) : null;
|
||||
if (origin && prev?.nodeType === 1 && splitOrigin(prev) === origin && prev.nodeName === so.nodeName && sameSplitAttributes(so, prev)) {
|
||||
while (so.firstChild) {
|
||||
prev.appendChild(so.firstChild);
|
||||
}
|
||||
parent.removeChild(so);
|
||||
recordUnwrappedParent(so, prev, ownedElements);
|
||||
} else {
|
||||
while (so.firstChild) {
|
||||
parent.insertBefore(so.firstChild, so);
|
||||
for (const [name, value] of [
|
||||
[PASS_ATTR, record.originalPass],
|
||||
[KIND_ATTR, record.originalKind]
|
||||
]) {
|
||||
if (value === null) {
|
||||
so.removeAttribute(name);
|
||||
} else {
|
||||
so.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
parent.removeChild(so);
|
||||
recordUnwrappedParent(so, parent, ownedElements);
|
||||
}
|
||||
SPLITOFF_SOURCES.delete(so);
|
||||
ownedElements.delete(so);
|
||||
}
|
||||
for (const p of touched) {
|
||||
@@ -1355,11 +1406,11 @@ var Juzhen = (() => {
|
||||
"other": "quanjiao"
|
||||
};
|
||||
function effectiveLang(node, fallback) {
|
||||
let p = node.parentNode;
|
||||
let p = node.nodeType === 1 ? node : node.parentNode;
|
||||
while (p && p.nodeType === 1) {
|
||||
const el = p;
|
||||
const v = el.getAttribute("lang");
|
||||
if (v) {
|
||||
if (v !== null) {
|
||||
return v.toLowerCase();
|
||||
}
|
||||
p = p.parentNode;
|
||||
@@ -1372,13 +1423,19 @@ var Juzhen = (() => {
|
||||
return "ja";
|
||||
}
|
||||
if (l === "zh" || l.indexOf("zh-") === 0) {
|
||||
if (/(?:^|-)hant(?:-|$)/.test(l)) {
|
||||
const subtags = l.split("-");
|
||||
let at = 1;
|
||||
for (let count = 0; count < 3 && /^[a-z]{3}$/.test(subtags[at] || ""); count++) {
|
||||
at++;
|
||||
}
|
||||
const script = /^[a-z]{4}$/.test(subtags[at] || "") ? subtags[at++] : void 0;
|
||||
if (script === "hant") {
|
||||
return "zh-Hant";
|
||||
}
|
||||
if (/(?:^|-)hans(?:-|$)/.test(l)) {
|
||||
if (script === "hans") {
|
||||
return "zh-Hans";
|
||||
}
|
||||
if (/-(tw|hk|mo)\b/.test(l)) {
|
||||
if (["tw", "hk", "mo"].includes(subtags[at] || "")) {
|
||||
return "zh-Hant";
|
||||
}
|
||||
if (l === "zh") {
|
||||
@@ -1753,9 +1810,10 @@ var Juzhen = (() => {
|
||||
if (rightAtLevel) {
|
||||
clone = anc.cloneNode(false);
|
||||
clone.removeAttribute("id");
|
||||
ownedElements.add(clone);
|
||||
registerSplitoffSource(clone, anc, pass, ownedElements);
|
||||
clone.setAttribute(PASS_ATTR2, pass);
|
||||
clone.setAttribute(KIND_ATTR2, "splitoff");
|
||||
ownedElements.add(clone);
|
||||
let n = rightAtLevel;
|
||||
while (n) {
|
||||
const next = n.nextSibling;
|
||||
@@ -2240,7 +2298,7 @@ var Juzhen = (() => {
|
||||
owners.delete(owner);
|
||||
}
|
||||
}
|
||||
var JZ_WRAP_TAGS = ["JZ-JINZE", "JZ-CHAR", "JZ-INNER", "JZ-HWS"];
|
||||
var JZ_WRAP_TAGS = ["JZ-JINZE", "JZ-CHAR", "JZ-INNER", "JZ-HWS", "JZ-ORPHAN"];
|
||||
function buildWrapSet(options) {
|
||||
const s = new Set(JZ_WRAP_TAGS);
|
||||
for (const name of options.finder.styleInlines) {
|
||||
@@ -2248,6 +2306,13 @@ var Juzhen = (() => {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
function inlineFlow(el, wrapSet) {
|
||||
const display = el.ownerDocument.defaultView?.getComputedStyle(el).display;
|
||||
if (display) {
|
||||
return display === "contents" || display.startsWith("inline") || display.startsWith("ruby");
|
||||
}
|
||||
return wrapSet.has(el.nodeName);
|
||||
}
|
||||
function rectOf(n, side) {
|
||||
if (n.nodeType === 3 && n.data.length > 0) {
|
||||
const t = n;
|
||||
@@ -2277,6 +2342,9 @@ var Juzhen = (() => {
|
||||
let parentRef = from.parentNode;
|
||||
for (; ; ) {
|
||||
while (n) {
|
||||
if (n.nodeType === 1 && n.nodeName === "BR" && n.ownerDocument.defaultView?.getComputedStyle(n).display !== "none") {
|
||||
return null;
|
||||
}
|
||||
const r = rectOf(n, side);
|
||||
if (r) {
|
||||
return r;
|
||||
@@ -2286,7 +2354,7 @@ var Juzhen = (() => {
|
||||
if (!parentRef || parentRef.nodeType !== 1) {
|
||||
return null;
|
||||
}
|
||||
if (!wrapSet.has(parentRef.nodeName)) {
|
||||
if (!inlineFlow(parentRef, wrapSet)) {
|
||||
return null;
|
||||
}
|
||||
n = side === "prev" ? parentRef.previousSibling : parentRef.nextSibling;
|
||||
@@ -3402,7 +3470,7 @@ var Juzhen = (() => {
|
||||
const scope = opts.scope || {};
|
||||
return {
|
||||
locale: {
|
||||
default: langOpt.default || "zh-Hant",
|
||||
default: langOpt.default === void 0 ? "zh-Hant" : langOpt.default,
|
||||
...langOpt.style ? { style: langOpt.style } : {},
|
||||
policy: langOpt.policy || {},
|
||||
stylePolicy: Array.isArray(langOpt.stylePolicy) ? langOpt.stylePolicy.slice() : []
|
||||
|
||||
Vendored
+84
-16
@@ -301,8 +301,8 @@ var Finder = class {
|
||||
* collectRuns/jiyaAdjacency/jinze/orphan/split 各 pass 逐元素重算——以 categoryCache
|
||||
* 記憶化砍除跨 pass 重複。安全性:單次 render 內一元素之分類**恆定**(class/skip 屬性/
|
||||
* nodeName 皆不因 render 變更;jz-* 包裹為新建元素、按 nodeName 分類亦恆定),且 Finder
|
||||
* 隨 makeCtx 每次 render 重建 → 快取僅存活單次 render,無跨 render 失效問題(持久化屬 C1,
|
||||
* 另議)。
|
||||
* 隨實例持久化,跨 render/revert 復用;屬性或位置變化由 invalidateSubtree 清除受影響
|
||||
* 子樹,不能沿用舊祖先鏈結果。
|
||||
*/
|
||||
category(el) {
|
||||
const cached = this.categoryCache.get(el);
|
||||
@@ -810,6 +810,45 @@ var SPLITOFF_TAGS = /* @__PURE__ */ new Set([
|
||||
var ATTRIBUTE_WRITERS = /* @__PURE__ */ new WeakMap();
|
||||
var OWNER_ATTRIBUTES = /* @__PURE__ */ new WeakMap();
|
||||
var UNWRAPPED_PARENTS = /* @__PURE__ */ new WeakMap();
|
||||
var SPLITOFF_SOURCES = /* @__PURE__ */ new WeakMap();
|
||||
function registerSplitoffSource(clone, source, pass, ownedElements) {
|
||||
if (!ownedElements.has(clone) || !SPLITOFF_TAGS.has(clone.nodeName) || clone.nodeName !== source.nodeName) {
|
||||
return;
|
||||
}
|
||||
const earlier = SPLITOFF_SOURCES.get(source);
|
||||
const inherited = earlier && source.getAttribute(PASS_ATTR) === earlier.pass && source.getAttribute(KIND_ATTR) === "splitoff" ? earlier : null;
|
||||
SPLITOFF_SOURCES.set(clone, {
|
||||
source,
|
||||
pass,
|
||||
originalPass: inherited ? inherited.originalPass : clone.getAttribute(PASS_ATTR),
|
||||
originalKind: inherited ? inherited.originalKind : clone.getAttribute(KIND_ATTR)
|
||||
});
|
||||
}
|
||||
function sameSplitAttributes(a, b) {
|
||||
const attrs = (el) => el.getAttributeNames().filter((name) => name !== PASS_ATTR && name !== KIND_ATTR);
|
||||
const names = attrs(a);
|
||||
return names.length === attrs(b).length && names.every((name) => a.getAttribute(name) === b.getAttribute(name));
|
||||
}
|
||||
function splitOrigin(node) {
|
||||
let current = resolveUnwrappedParent(node);
|
||||
const visited = /* @__PURE__ */ new Set();
|
||||
while (current) {
|
||||
if (visited.has(current)) {
|
||||
return null;
|
||||
}
|
||||
visited.add(current);
|
||||
if (current.nodeType !== 1) {
|
||||
return current;
|
||||
}
|
||||
const el = current;
|
||||
const record = SPLITOFF_SOURCES.get(el);
|
||||
if (!record || el.getAttribute(PASS_ATTR) !== record.pass || el.getAttribute(KIND_ATTR) !== "splitoff") {
|
||||
return current;
|
||||
}
|
||||
current = resolveUnwrappedParent(record.source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function recordUnwrappedParent(el, parent, ownedElements) {
|
||||
const pass = el.getAttribute(PASS_ATTR);
|
||||
if (ownedElements.has(el) && pass && knownProduct(pass, el.getAttribute(KIND_ATTR), el) && !el.parentNode && !el.hasChildNodes()) {
|
||||
@@ -995,20 +1034,32 @@ function revertPass(pass, root, ownedElements) {
|
||||
continue;
|
||||
}
|
||||
touched.add(parent);
|
||||
const record = SPLITOFF_SOURCES.get(so);
|
||||
if (!record || record.pass !== pass) {
|
||||
continue;
|
||||
}
|
||||
const source = resolveUnwrappedParent(record.source);
|
||||
const prev = so.previousSibling;
|
||||
if (prev && prev.nodeType === 1 && prev.nodeName === so.nodeName) {
|
||||
const origin = source ? splitOrigin(source) : null;
|
||||
if (origin && prev?.nodeType === 1 && splitOrigin(prev) === origin && prev.nodeName === so.nodeName && sameSplitAttributes(so, prev)) {
|
||||
while (so.firstChild) {
|
||||
prev.appendChild(so.firstChild);
|
||||
}
|
||||
parent.removeChild(so);
|
||||
recordUnwrappedParent(so, prev, ownedElements);
|
||||
} else {
|
||||
while (so.firstChild) {
|
||||
parent.insertBefore(so.firstChild, so);
|
||||
for (const [name, value] of [
|
||||
[PASS_ATTR, record.originalPass],
|
||||
[KIND_ATTR, record.originalKind]
|
||||
]) {
|
||||
if (value === null) {
|
||||
so.removeAttribute(name);
|
||||
} else {
|
||||
so.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
parent.removeChild(so);
|
||||
recordUnwrappedParent(so, parent, ownedElements);
|
||||
}
|
||||
SPLITOFF_SOURCES.delete(so);
|
||||
ownedElements.delete(so);
|
||||
}
|
||||
for (const p of touched) {
|
||||
@@ -1328,11 +1379,11 @@ var DEFAULT_POLICY = {
|
||||
"other": "quanjiao"
|
||||
};
|
||||
function effectiveLang(node, fallback) {
|
||||
let p = node.parentNode;
|
||||
let p = node.nodeType === 1 ? node : node.parentNode;
|
||||
while (p && p.nodeType === 1) {
|
||||
const el = p;
|
||||
const v = el.getAttribute("lang");
|
||||
if (v) {
|
||||
if (v !== null) {
|
||||
return v.toLowerCase();
|
||||
}
|
||||
p = p.parentNode;
|
||||
@@ -1345,13 +1396,19 @@ function classify(lang) {
|
||||
return "ja";
|
||||
}
|
||||
if (l === "zh" || l.indexOf("zh-") === 0) {
|
||||
if (/(?:^|-)hant(?:-|$)/.test(l)) {
|
||||
const subtags = l.split("-");
|
||||
let at = 1;
|
||||
for (let count = 0; count < 3 && /^[a-z]{3}$/.test(subtags[at] || ""); count++) {
|
||||
at++;
|
||||
}
|
||||
const script = /^[a-z]{4}$/.test(subtags[at] || "") ? subtags[at++] : void 0;
|
||||
if (script === "hant") {
|
||||
return "zh-Hant";
|
||||
}
|
||||
if (/(?:^|-)hans(?:-|$)/.test(l)) {
|
||||
if (script === "hans") {
|
||||
return "zh-Hans";
|
||||
}
|
||||
if (/-(tw|hk|mo)\b/.test(l)) {
|
||||
if (["tw", "hk", "mo"].includes(subtags[at] || "")) {
|
||||
return "zh-Hant";
|
||||
}
|
||||
if (l === "zh") {
|
||||
@@ -1726,9 +1783,10 @@ function splitTreeAfter(top, textNode, offset, pass, finder, ownedElements) {
|
||||
if (rightAtLevel) {
|
||||
clone = anc.cloneNode(false);
|
||||
clone.removeAttribute("id");
|
||||
ownedElements.add(clone);
|
||||
registerSplitoffSource(clone, anc, pass, ownedElements);
|
||||
clone.setAttribute(PASS_ATTR2, pass);
|
||||
clone.setAttribute(KIND_ATTR2, "splitoff");
|
||||
ownedElements.add(clone);
|
||||
let n = rightAtLevel;
|
||||
while (n) {
|
||||
const next = n.nextSibling;
|
||||
@@ -2213,7 +2271,7 @@ function uninstallLayout(root, owner, key) {
|
||||
owners.delete(owner);
|
||||
}
|
||||
}
|
||||
var JZ_WRAP_TAGS = ["JZ-JINZE", "JZ-CHAR", "JZ-INNER", "JZ-HWS"];
|
||||
var JZ_WRAP_TAGS = ["JZ-JINZE", "JZ-CHAR", "JZ-INNER", "JZ-HWS", "JZ-ORPHAN"];
|
||||
function buildWrapSet(options) {
|
||||
const s = new Set(JZ_WRAP_TAGS);
|
||||
for (const name of options.finder.styleInlines) {
|
||||
@@ -2221,6 +2279,13 @@ function buildWrapSet(options) {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
function inlineFlow(el, wrapSet) {
|
||||
const display = el.ownerDocument.defaultView?.getComputedStyle(el).display;
|
||||
if (display) {
|
||||
return display === "contents" || display.startsWith("inline") || display.startsWith("ruby");
|
||||
}
|
||||
return wrapSet.has(el.nodeName);
|
||||
}
|
||||
function rectOf(n, side) {
|
||||
if (n.nodeType === 3 && n.data.length > 0) {
|
||||
const t = n;
|
||||
@@ -2250,6 +2315,9 @@ function edgeRect(start, side, from, wrapSet) {
|
||||
let parentRef = from.parentNode;
|
||||
for (; ; ) {
|
||||
while (n) {
|
||||
if (n.nodeType === 1 && n.nodeName === "BR" && n.ownerDocument.defaultView?.getComputedStyle(n).display !== "none") {
|
||||
return null;
|
||||
}
|
||||
const r = rectOf(n, side);
|
||||
if (r) {
|
||||
return r;
|
||||
@@ -2259,7 +2327,7 @@ function edgeRect(start, side, from, wrapSet) {
|
||||
if (!parentRef || parentRef.nodeType !== 1) {
|
||||
return null;
|
||||
}
|
||||
if (!wrapSet.has(parentRef.nodeName)) {
|
||||
if (!inlineFlow(parentRef, wrapSet)) {
|
||||
return null;
|
||||
}
|
||||
n = side === "prev" ? parentRef.previousSibling : parentRef.nextSibling;
|
||||
@@ -3375,7 +3443,7 @@ function normalizeOptions(opts = {}) {
|
||||
const scope = opts.scope || {};
|
||||
return {
|
||||
locale: {
|
||||
default: langOpt.default || "zh-Hant",
|
||||
default: langOpt.default === void 0 ? "zh-Hant" : langOpt.default,
|
||||
...langOpt.style ? { style: langOpt.style } : {},
|
||||
policy: langOpt.policy || {},
|
||||
stylePolicy: Array.isArray(langOpt.stylePolicy) ? langOpt.stylePolicy.slice() : []
|
||||
|
||||
Vendored
+4
-5
@@ -33,7 +33,7 @@ export interface BlockRule {
|
||||
export interface JuzhenOptions {
|
||||
/** 語言與全角/開明政策。 */
|
||||
lang?: {
|
||||
/** 無 lang 祖先時之預設語言標籤(預設 "zh-Hant")。 */
|
||||
/** 無 lang 屬性(自身及祖先)時之預設語言標籤(預設 "zh-Hant");空字串表示未知。 */
|
||||
default?: string;
|
||||
/** 全域標點擠壓預設:quanjiao(全角式)|kaiming(開明式)|
|
||||
* banjiao(半角式)。覆寫內建逐語言預設,可被 policy 逐 lang/stylePolicy 逐元素覆寫。 */
|
||||
@@ -48,11 +48,11 @@ export interface JuzhenOptions {
|
||||
/** 自定義字符分類(擴充內置規則集)。把未分類字符或新符號歸入中文/西文/
|
||||
* 全寬標點各子類,以覆寫 spacing/jiya 等規則之分類判定(追加語義)。 */
|
||||
charClass?: CharClassOverride;
|
||||
/** 功能分級標記(§6.5.0a)。段落級功能(禁則/行端/懸掛/垂懸字/長詞斷字)
|
||||
/** 功能分級標記(§6.5.0a)。段落級功能(禁則/行端/垂懸字/長詞斷字)
|
||||
* 僅作用於段落級元素;文本級功能(中西間隙/標點擠壓)任意文本皆適用。
|
||||
* - `text`:選擇器,命中之子樹**僅文本級**(標題/按鈕/UI 文字典型);亦可
|
||||
* 經元素屬性 `data-jz-level="text"`/`"paragraph"` 標記(屬性優先、最近祖先勝)。
|
||||
* 預設:塊級元素(blockTags)→ 段落級(全功能)。 */
|
||||
* 自身向上找最近有效訊號,皆無才預設 paragraph;不按 HTML 標籤或 display 推斷。 */
|
||||
level?: {
|
||||
text?: string;
|
||||
};
|
||||
@@ -73,8 +73,7 @@ export interface JuzhenOptions {
|
||||
jinze?: boolean;
|
||||
orphan?: boolean | OrphanOptions;
|
||||
/** @deprecated 標點懸掛**已移除**(§6.5.6):純負 margin 機制經實測為機制級死路(寬度
|
||||
* 守恆矛盾,CSS 接觸不到斷行器),原生 hanging-punctuation 為唯一正解但僅 Safari 支援、
|
||||
* 且與本庫 inline-block 原子互斥。此選項仍被接受以保 API 相容,但**無任何效果**。 */
|
||||
* 守恆矛盾,CSS 接觸不到斷行器),未來原生 hanging-punctuation 路徑須另行設計及驗證。此選項仍被接受以保 API 相容,但**無任何效果**。 */
|
||||
hanging?: boolean;
|
||||
/** @reserved 標點修正(簡/繁正規化)——架構已預留(切片 #7),**當前無任何效果**。
|
||||
* 注意:此為功能開關,與 `charClass.biaodian`(標點子類擴充表,語義完全不同)同名,勿混。 */
|
||||
|
||||
Reference in New Issue
Block a user