feat: Pass D 開明式標點擠壓(句內點號半形)
句內點號(,、;:)逐字包進 <span class="cjk-squeeze">,配合消費端 CSS `font-feature-settings: "halt"` 擠成半形;句末點號(。?!)保持全形—— 此即開明式。span 內含真標點字元,複製不污染原文。 clreq 之「連續標點擠壓」與「行端壓縮」屬 layout-aware 規則,交由原生 `text-spacing-trim` 處理(Chrome 123+ 預設生效,要求字型有 halt/chws), JS 不涉入以免重複擠壓。真·行尾懸掛(hanging-punctuation)僅 Safari 支援 且繁體橫排不宜,故不納入配方。 預設啟用,支援 `langWhitelist` 依 lang 前綴限定擠壓範圍、`marks` 與 `squeezeClass` 自訂。含 16 項 jsdom DOM 測試。
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# cjk-autospace
|
# cjk-autospace
|
||||||
|
|
||||||
CJK ↔ Latin / 數字 / 標點 / 引號 / 括號 / 運算子等邊界之自動間距 shim。Pangu.js v7.2.1 規則之 ES5 端口,加上「跨樣式標籤透明 + 行內塊邊界」兩 pass 架構,再加 Pass C 長英文段 `<span lang>` 包裝(讓瀏覽器原生連字斷詞)。工廠模式,由消費端注入配置。
|
CJK ↔ Latin / 數字 / 標點 / 引號 / 括號 / 運算子等邊界之自動間距 shim。Pangu.js v7.2.1 規則之 ES5 端口,加上「跨樣式標籤透明 + 行內塊邊界」兩 pass 架構,再加 Pass C 長英文段 `<span lang>` 包裝(讓瀏覽器原生連字斷詞)、Pass D 開明式標點擠壓(句內點號半形)。工廠模式,由消費端注入配置。
|
||||||
|
|
||||||
> **詳細文檔 + Changelog 位於 `docs` 分支**:架構解析、雙 pass 流程、整合範例見 [`index.html`](https://git.commilitia.net/admin/cjk-autospace/src/branch/docs/index.html);變更記錄見 [`CHANGELOG.html`](https://git.commilitia.net/admin/cjk-autospace/src/branch/docs/CHANGELOG.html)。本機並列檢出:`git worktree add ../cjk-autospace-docs docs`。
|
> **詳細文檔 + Changelog 位於 `docs` 分支**:架構解析、雙 pass 流程、整合範例見 [`index.html`](https://git.commilitia.net/admin/cjk-autospace/src/branch/docs/index.html);變更記錄見 [`CHANGELOG.html`](https://git.commilitia.net/admin/cjk-autospace/src/branch/docs/CHANGELOG.html)。本機並列檢出:`git worktree add ../cjk-autospace-docs docs`。
|
||||||
|
|
||||||
@@ -27,13 +27,31 @@ autospace.apply(document.body);
|
|||||||
|
|
||||||
`user-select: none` 讓選取與剪貼板跳過 marker;marker 內含真實 ASCII space(layout 視為 whitespace:行末 collapse、容許換行、justify 可拉伸)。
|
`user-select: none` 讓選取與剪貼板跳過 marker;marker 內含真實 ASCII space(layout 視為 whitespace:行末 collapse、容許換行、justify 可拉伸)。
|
||||||
|
|
||||||
|
開明式標點擠壓(Pass D)另需:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 句內點號半形——CSS 表達不了「只擠句內、不擠句末」,故由 lib 逐字包裝,
|
||||||
|
此處只負責把被包裝者擠成半形。需字型具 halt(Noto Sans/Serif CJK 皆有)。 */
|
||||||
|
.cjk-squeeze {
|
||||||
|
font-feature-settings: "halt" 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 連續標點擠壓 + 行端壓縮(clreq)——原生 layout-aware,套在正文容器上。
|
||||||
|
Chrome 123+ 預設即 normal,明確標注以防被 reset;同樣要求字型具 halt/chws。 */
|
||||||
|
.article {
|
||||||
|
text-spacing-trim: normal;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
不加 `.cjk-squeeze` 規則則 span 惰性(DOM 變更但無視覺效果)。`text-spacing-trim` 於 Chromium 不支援時優雅降級(句內半角仍由 `halt` 生效)。
|
||||||
|
|
||||||
## 標籤三分類
|
## 標籤三分類
|
||||||
|
|
||||||
- **STYLE_INLINES**(`<strong>` / `<em>` / `<a>` / `<b>` / `<i>` / `<u>` / `<s>` / `<mark>` / `<ins>` / `<del>` / `<sub>` / `<sup>` / `<small>` / `<abbr>` / `<cite>` / `<dfn>` / `<q>` / `<span>` / `<var>` / `<samp>` / `<time>` / `<bdo>` / `<bdi>` / `<ruby>` / `<rb>` / `<rt>` / `<rp>` / `<wbr>`)— shim 透明走過。跨樣式之 Pangu 邊界(如 `<strong>是</strong> Neovim`)由 Pass A 在剝離後識別。
|
- **STYLE_INLINES**(`<strong>` / `<em>` / `<a>` / `<b>` / `<i>` / `<u>` / `<s>` / `<mark>` / `<ins>` / `<del>` / `<sub>` / `<sup>` / `<small>` / `<abbr>` / `<cite>` / `<dfn>` / `<q>` / `<span>` / `<var>` / `<samp>` / `<time>` / `<bdo>` / `<bdi>` / `<ruby>` / `<rb>` / `<rt>` / `<rp>` / `<wbr>`)— shim 透明走過。跨樣式之 Pangu 邊界(如 `<strong>是</strong> Neovim`)由 Pass A 在剝離後識別。
|
||||||
- **PILL**(由 `pillSelector` 指定,預設 `code, kbd`)— 行內塊。邊界由 Pass B 處理,與內容鄰接(CJK / 字母數字)即插 marker。
|
- **PILL**(由 `pillSelector` 指定,預設 `code, kbd`)— 行內塊。邊界由 Pass B 處理,與內容鄰接(CJK / 字母數字)即插 marker。
|
||||||
- **BLOCK**(`<p>` / `<div>` / `<h1>`–`<h6>` / `<li>` / `<pre>` / `<table>` / `<br>` / `<hr>` / …)— 段落級。Pass A 把它們當作 run 邊界,子 block 各自一次。
|
- **BLOCK**(`<p>` / `<div>` / `<h1>`–`<h6>` / `<li>` / `<pre>` / `<table>` / `<br>` / `<hr>` / …)— 段落級。Pass A 把它們當作 run 邊界,子 block 各自一次。
|
||||||
|
|
||||||
## 三 pass
|
## 四 pass
|
||||||
|
|
||||||
- **Pass A — per-block Pangu on tag-stripped text**:對每個 BLOCK 走 DFS 收集邏輯文本(STYLE 透明、PILL 與子 BLOCK 中斷 run、既有 marker 也中斷),整段套 `panguSpace`,把 MARK 反映射回 textNode/offset。MARK 對應原文若是單 ASCII space 則 strip 後插 marker,否則純插。actions 倒序執行確保前面偏移不被後面動作影響。
|
- **Pass A — per-block Pangu on tag-stripped text**:對每個 BLOCK 走 DFS 收集邏輯文本(STYLE 透明、PILL 與子 BLOCK 中斷 run、既有 marker 也中斷),整段套 `panguSpace`,把 MARK 反映射回 textNode/offset。MARK 對應原文若是單 ASCII space 則 strip 後插 marker,否則純插。actions 倒序執行確保前面偏移不被後面動作影響。
|
||||||
- **Pass B — pill boundary**:對每個 PILL 透過 STYLE 鏈遞迴找邏輯鄰字(`adjacentLogicalChar`),鄰字若是 CJK / 字母數字直接插 marker;若是 1 個 ASCII space 且另一側為內容字則 strip 後插 marker;其餘(標點 / 多空格 / 換行 / 既有 marker)跳過。
|
- **Pass B — pill boundary**:對每個 PILL 透過 STYLE 鏈遞迴找邏輯鄰字(`adjacentLogicalChar`),鄰字若是 CJK / 字母數字直接插 marker;若是 1 個 ASCII space 且另一側為內容字則 strip 後插 marker;其餘(標點 / 多空格 / 換行 / 既有 marker)跳過。
|
||||||
@@ -49,6 +67,14 @@ autospace.apply(document.body);
|
|||||||
|
|
||||||
不加 CSS 則 span 為惰性(DOM 變更但無視覺效果)。
|
不加 CSS 則 span 為惰性(DOM 變更但無視覺效果)。
|
||||||
|
|
||||||
|
- **Pass D — 開明式標點擠壓**(**預設啟用**,`punctuationSqueeze: false` 顯式關閉):把句內點號(`,、;:`)逐字包進 `<span class="cjk-squeeze">`,配合上節 CSS 之 `font-feature-settings: "halt"` 擠成半形;句末點號(`。?!`)保持全形——此即「開明式」。span 內含**真標點字元本身**(非注入空白),故 Copy / Paste / 序列化返回原文不變,無需 `user-select` 特殊處理。冪等:parent 已為 `.cjk-squeeze` 直接跳過。可傳 `{ squeezeClass, marks, langWhitelist }` 自訂。
|
||||||
|
|
||||||
|
**職責分工(不重複擠壓)**:開明式「只擠句內、不擠句末」CSS 無法表達(`halt` 套整個元素會把句末也擠成半形 → 變半角式),故由 lib 逐字包裝。clreq 之「連續標點擠壓」「行端壓縮」屬 layout-aware(相鄰對之側別、斷行位置只有 layout 時才知),交由原生 `text-spacing-trim`(其 collapse 規則與 clreq 逐條對應)。兩層字符集/職責不相交。
|
||||||
|
|
||||||
|
**`langWhitelist`**:BCP-47 前綴陣列。非空時僅擠壓最近祖先 `lang` 前綴命中之文本(`"zh"` 命中 `"zh-Hant"`;無 `lang` 祖先視為不命中);空則不限。開明式於簡體字型下字面位於左下角、擠壓最自然,繁體點號居中視覺略異——欲限定簡體可傳 `{ langWhitelist: ["zh-Hans"] }`。
|
||||||
|
|
||||||
|
**`hanging-punctuation`(真·行尾懸掛)不納入配方**:Chromium 至今未實作(無 flag 可開),僅 Safari 支援;且 clreq 言繁體中文橫排不宜行尾懸掛。clreq 的「行尾結束括號縮減末側半形」屬壓縮,已由 `text-spacing-trim` 涵蓋。
|
||||||
|
|
||||||
## 配置選項
|
## 配置選項
|
||||||
|
|
||||||
| 選項 | 預設 | 說明 |
|
| 選項 | 預設 | 說明 |
|
||||||
@@ -61,6 +87,7 @@ autospace.apply(document.body);
|
|||||||
| `skipAttribute` | `"data-md-key"` | 任一祖先帶此屬性即整 subtree skip |
|
| `skipAttribute` | `"data-md-key"` | 任一祖先帶此屬性即整 subtree skip |
|
||||||
| `isSkipped` | (無) | 自訂 skip callback;先於內建判定檢查 |
|
| `isSkipped` | (無) | 自訂 skip callback;先於內建判定檢查 |
|
||||||
| `longWordWrap` | `true`(預設啟用) | Pass C 開關。傳 `false` 顯式關閉;`{minLength, lang}` 自訂;`true` / 省略等同 `{minLength: 6, lang: "en"}` |
|
| `longWordWrap` | `true`(預設啟用) | Pass C 開關。傳 `false` 顯式關閉;`{minLength, lang}` 自訂;`true` / 省略等同 `{minLength: 6, lang: "en"}` |
|
||||||
|
| `punctuationSqueeze` | `true`(預設啟用) | Pass D 開明式擠壓開關。傳 `false` 顯式關閉;`{squeezeClass, marks, langWhitelist}` 自訂;`true` / 省略等同句內點號 `[",","、",";",":"]` / class `"cjk-squeeze"` / 不限語言 |
|
||||||
|
|
||||||
## 模組形式
|
## 模組形式
|
||||||
|
|
||||||
|
|||||||
Vendored
+36
@@ -56,6 +56,42 @@ export interface CjkAutospaceOptions {
|
|||||||
/** 包裝 span 之 `lang` 屬性值。預設 `"en"`。 */
|
/** 包裝 span 之 `lang` 屬性值。預設 `"en"`。 */
|
||||||
lang?: string;
|
lang?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass D(**預設啟用**):開明式標點擠壓。把句內點號(`,、;:`)包進
|
||||||
|
* `<span class="…">`,配合消費端 CSS `font-feature-settings: "halt"` 擠
|
||||||
|
* 成半形;句末點號(`。?!`)保持全形——此即「開明式」。span 內含真
|
||||||
|
* 標點字元本身,故 Copy / Paste 返回原文不變。
|
||||||
|
*
|
||||||
|
* `font-feature-settings: "halt"` 套整個元素只會把所有標點變半形(半角
|
||||||
|
* 式),故必須逐字選擇性包裝——這是 CSS 表達不了、JS 不可替代的一層。
|
||||||
|
* clreq 之「連續標點擠壓」與「行端壓縮」交由原生 CSS(layout-aware):
|
||||||
|
*
|
||||||
|
* ```css
|
||||||
|
* .cjk-squeeze { font-feature-settings: "halt" 1; } // 句內點號半形
|
||||||
|
* .article { text-spacing-trim: normal; } // 連續標點 + 行端壓縮
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* `text-spacing-trim` 於 Chrome 123+ 預設生效(要求字型有 `halt`/`chws`,
|
||||||
|
* Noto CJK 滿足);`hanging-punctuation`(真·懸掛)僅 Safari 支援且繁體
|
||||||
|
* 橫排不宜,故不納入配方。
|
||||||
|
*
|
||||||
|
* 傳 `false` 顯式關閉;`true` 或省略等同預設(marks `[",","、",";",":"]`、
|
||||||
|
* class `"cjk-squeeze"`、不限語言)。
|
||||||
|
*/
|
||||||
|
punctuationSqueeze?: boolean | {
|
||||||
|
/** 包裝 span 之 class 名。預設 `"cjk-squeeze"`。 */
|
||||||
|
squeezeClass?: string;
|
||||||
|
/** 要擠壓之標點字元(單字元)陣列。預設句內點號 `[",", "、", ";", ":"]`。 */
|
||||||
|
marks?: readonly string[];
|
||||||
|
/**
|
||||||
|
* BCP-47 語言前綴白名單。非空時僅擠壓最近祖先 `lang` 前綴命中之文本
|
||||||
|
* (`"zh"` 命中 `"zh-Hant"`;無 `lang` 祖先視為不命中)。空陣列或省略
|
||||||
|
* 則不限語言。例:`["zh-Hans"]` 限定簡體(開明式於簡體字型下字面位
|
||||||
|
* 於左下角,擠壓最自然;繁體點號居中,視覺略異)。
|
||||||
|
*/
|
||||||
|
langWhitelist?: readonly string[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CjkAutospaceInstance {
|
export interface CjkAutospaceInstance {
|
||||||
|
|||||||
+139
-5
@@ -14,7 +14,7 @@
|
|||||||
* 處理。預設 'code, kbd',可由消費端擴充。
|
* 處理。預設 'code, kbd',可由消費端擴充。
|
||||||
* - BLOCK — 段落級標籤,Pass A 把它們當作 run 邊界。
|
* - BLOCK — 段落級標籤,Pass A 把它們當作 run 邊界。
|
||||||
*
|
*
|
||||||
* 三 pass:
|
* 四 pass:
|
||||||
* Pass A — 對每個 BLOCK 走 DFS 收集邏輯文本(STYLE 透明、PILL 與子
|
* Pass A — 對每個 BLOCK 走 DFS 收集邏輯文本(STYLE 透明、PILL 與子
|
||||||
* BLOCK 中斷 run),整段套 panguSpace,把 MARK 反映射回 textNode/
|
* BLOCK 中斷 run),整段套 panguSpace,把 MARK 反映射回 textNode/
|
||||||
* offset。MARK 對應原文若是單 ASCII space 則 strip 後插 marker,
|
* offset。MARK 對應原文若是單 ASCII space 則 strip 後插 marker,
|
||||||
@@ -22,10 +22,16 @@
|
|||||||
* Pass B — 對每個 PILL 透過 STYLE 鏈遞迴找邏輯鄰字(adjacentLogical-
|
* Pass B — 對每個 PILL 透過 STYLE 鏈遞迴找邏輯鄰字(adjacentLogical-
|
||||||
* Char),按 0/1/多+特殊規則處理(0 個或 1 個 ASCII space 插
|
* Char),按 0/1/多+特殊規則處理(0 個或 1 個 ASCII space 插
|
||||||
* marker;其餘跳過)。
|
* marker;其餘跳過)。
|
||||||
* Pass C — 可選(options.longWordWrap)。把 [A-Za-z]{N,} 之長英文段
|
* Pass C — 預設啟用(options.longWordWrap)。把 [A-Za-z]{N,} 之長英文
|
||||||
* 包進 <span lang="en">;配合消費端 CSS `[lang|="en"]{hyphens:auto}`
|
* 段包進 <span lang="en">;配合消費端 CSS `[lang|="en"]{hyphens:auto}`
|
||||||
* 讓瀏覽器原生連字斷詞。連字符純由 CSS 渲染(DOM 內不存在),複製
|
* 讓瀏覽器原生連字斷詞。連字符純由 CSS 渲染(DOM 內不存在),複製
|
||||||
* 不污染原文。預設關閉以保持向後相容。
|
* 不污染原文。
|
||||||
|
* Pass D — 預設啟用(options.punctuationSqueeze)。開明式標點擠壓:把
|
||||||
|
* 句內點號(,、;:)包進 <span class="cjk-squeeze">,配合消費端 CSS
|
||||||
|
* `font-feature-settings:"halt"` 擠成半形;句末點號(。?!)保持全
|
||||||
|
* 形。span 內含真標點字元本身,複製不污染原文。clreq 之「連續標點擠
|
||||||
|
* 壓」與「行端壓縮」屬 layout-aware 規則,交由原生 CSS
|
||||||
|
* `text-spacing-trim` 處理(見 README),JS 不涉入以免重複擠壓。
|
||||||
*
|
*
|
||||||
* Marker = <span class="cjk-autospace" aria-hidden="true">(空白)</span>。
|
* Marker = <span class="cjk-autospace" aria-hidden="true">(空白)</span>。
|
||||||
* 內含真實 ASCII 空白:layout 視為 whitespace(行末 collapse / 容許
|
* 內含真實 ASCII 空白:layout 視為 whitespace(行末 collapse / 容許
|
||||||
@@ -80,6 +86,36 @@ function createCjkAutospace(options)
|
|||||||
};
|
};
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
/* Pass D 設定——開明式標點擠壓。預設啟用。
|
||||||
|
開明式之定義:句內點號(,、;:)擠成半形,句末點號(。?!)保持
|
||||||
|
全形。`font-feature-settings: "halt"` 套在整個元素只會把所有標點變半
|
||||||
|
形(即半角式),故必須逐字選擇性包裝——這正是 JS 不可替代、CSS 無從
|
||||||
|
表達的那一層。包裝 span 內含真標點字元本身(非注入空白),故 Copy /
|
||||||
|
Paste 返回原文不變,無需 user-select 特殊處理。
|
||||||
|
clreq 之「連續標點擠壓」與「行端壓縮」屬 layout-aware 規則(相鄰對之
|
||||||
|
側別選擇、行端位置只有 layout 時才知),交由原生 CSS
|
||||||
|
`text-spacing-trim` 處理(Chrome 123+ 預設生效,要求字型有 halt/chws;
|
||||||
|
見 README)——JS 不涉入,避免與 CSS 重複擠壓。真·行尾懸掛
|
||||||
|
(hanging-punctuation)僅 Safari 支援,且 clreq 言繁體橫排不宜,故不納
|
||||||
|
入配方。
|
||||||
|
langWhitelist:BCP-47 前綴陣列。非空時僅擠壓最近祖先 lang 前綴命中之
|
||||||
|
文本;空陣列或省略則不限語言。 */
|
||||||
|
const squeezeCfg = (function ()
|
||||||
|
{
|
||||||
|
const v = options.punctuationSqueeze;
|
||||||
|
if (v === false) { return null; }
|
||||||
|
const DEFAULT_MARKS = [",", "、", ";", ":"]; // ,、;:
|
||||||
|
if (v === undefined || v === true || v === null)
|
||||||
|
{
|
||||||
|
return { cls: "cjk-squeeze", marks: DEFAULT_MARKS, langWhitelist: [] };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
cls: typeof v.squeezeClass === "string" && v.squeezeClass ? v.squeezeClass : "cjk-squeeze",
|
||||||
|
marks: Array.isArray(v.marks) && v.marks.length ? v.marks.slice() : DEFAULT_MARKS,
|
||||||
|
langWhitelist: Array.isArray(v.langWhitelist) ? v.langWhitelist.slice() : [],
|
||||||
|
};
|
||||||
|
}());
|
||||||
|
|
||||||
/* ----- Pangu 字符類與規則表 ----- */
|
/* ----- Pangu 字符類與規則表 ----- */
|
||||||
const PANGU_CJK = "⺀-⼀-"
|
const PANGU_CJK = "⺀-⼀-"
|
||||||
+ "-ゟ゠-ヺー-ヿ"
|
+ "-ゟ゠-ヺー-ヿ"
|
||||||
@@ -527,6 +563,101 @@ function createCjkAutospace(options)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----- Pass D: 開明式標點擠壓(句內點號半形) ----- */
|
||||||
|
/* 冪等性:parent 已是 cfg.cls 之 <span> 直接跳過。 */
|
||||||
|
function isInSqueezeSpan(node, cls)
|
||||||
|
{
|
||||||
|
const p = node.parentNode;
|
||||||
|
return !!(p && p.nodeType === 1 && p.nodeName === "SPAN"
|
||||||
|
&& p.classList && p.classList.contains(cls));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 由 node 向上找最近帶 lang 屬性之祖先,回傳小寫值(無則 null)。 */
|
||||||
|
function nearestLang(node)
|
||||||
|
{
|
||||||
|
let p = node.parentNode;
|
||||||
|
while (p && p.nodeType === 1)
|
||||||
|
{
|
||||||
|
if (p.hasAttribute && p.hasAttribute("lang"))
|
||||||
|
{
|
||||||
|
const v = p.getAttribute("lang");
|
||||||
|
if (v) { return v.toLowerCase(); }
|
||||||
|
}
|
||||||
|
p = p.parentNode;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空白名單 → 不限;非空 → 僅最近 lang 前綴命中者擠壓(無 lang 祖先視
|
||||||
|
為不命中)。前綴以 BCP-47 子標籤邊界比對:'zh' 命中 'zh-hant',但不
|
||||||
|
命中 'zhx'。 */
|
||||||
|
function langAllowed(node, whitelist)
|
||||||
|
{
|
||||||
|
if (!whitelist || whitelist.length === 0) { return true; }
|
||||||
|
const lang = nearestLang(node);
|
||||||
|
if (!lang) { return false; }
|
||||||
|
for (let i = 0; i < whitelist.length; i += 1)
|
||||||
|
{
|
||||||
|
const w = whitelist[i].toLowerCase();
|
||||||
|
if (lang === w || lang.indexOf(w + "-") === 0) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapSqueezeMarksIn(textNode, cfg, re)
|
||||||
|
{
|
||||||
|
const data = textNode.data;
|
||||||
|
re.lastIndex = 0;
|
||||||
|
const frag = document.createDocumentFragment();
|
||||||
|
let cursor = 0;
|
||||||
|
let m;
|
||||||
|
while ((m = re.exec(data)) !== null)
|
||||||
|
{
|
||||||
|
const idx = m.index;
|
||||||
|
if (idx > cursor)
|
||||||
|
{
|
||||||
|
frag.appendChild(document.createTextNode(data.slice(cursor, idx)));
|
||||||
|
}
|
||||||
|
const span = document.createElement("span");
|
||||||
|
span.className = cfg.cls;
|
||||||
|
span.textContent = m[0];
|
||||||
|
frag.appendChild(span);
|
||||||
|
cursor = idx + m[0].length;
|
||||||
|
}
|
||||||
|
if (cursor < data.length)
|
||||||
|
{
|
||||||
|
frag.appendChild(document.createTextNode(data.slice(cursor)));
|
||||||
|
}
|
||||||
|
textNode.parentNode.replaceChild(frag, textNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function processPunctuationSqueeze(root, cfg)
|
||||||
|
{
|
||||||
|
const escaped = cfg.marks.map(function (c)
|
||||||
|
{
|
||||||
|
return c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
}).join("");
|
||||||
|
if (!escaped) { return; }
|
||||||
|
const reTest = new RegExp("[" + escaped + "]");
|
||||||
|
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
|
||||||
|
const targets = [];
|
||||||
|
let n = walker.nextNode();
|
||||||
|
while (n)
|
||||||
|
{
|
||||||
|
if (!isSkipped(n) && !isInSqueezeSpan(n, cfg.cls)
|
||||||
|
&& reTest.test(n.data) && langAllowed(n, cfg.langWhitelist))
|
||||||
|
{
|
||||||
|
targets.push(n);
|
||||||
|
}
|
||||||
|
n = walker.nextNode();
|
||||||
|
}
|
||||||
|
const reAll = new RegExp("[" + escaped + "]", "g");
|
||||||
|
for (let i = 0; i < targets.length; i += 1)
|
||||||
|
{
|
||||||
|
wrapSqueezeMarksIn(targets[i], cfg, reAll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ----- 對外 API ----- */
|
/* ----- 對外 API ----- */
|
||||||
function apply(root)
|
function apply(root)
|
||||||
{
|
{
|
||||||
@@ -548,8 +679,11 @@ function createCjkAutospace(options)
|
|||||||
/* Pass B */
|
/* Pass B */
|
||||||
processPills(root);
|
processPills(root);
|
||||||
|
|
||||||
/* Pass C(opt-in) */
|
/* Pass C(預設啟用) */
|
||||||
if (longWordCfg) { processLongWords(root, longWordCfg); }
|
if (longWordCfg) { processLongWords(root, longWordCfg); }
|
||||||
|
|
||||||
|
/* Pass D(開明式標點擠壓,預設啟用) */
|
||||||
|
if (squeezeCfg) { processPunctuationSqueeze(root, squeezeCfg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { apply: apply };
|
return { apply: apply };
|
||||||
|
|||||||
Reference in New Issue
Block a user