@pixui-dev/pixui-richtext-helper 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/RichText.d.ts +22 -0
- package/dist/RichText.js +24 -0
- package/dist/RichTextUtils.d.ts +6 -2
- package/dist/RichTextUtils.js +248 -73
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/richtext/RichText_Refactor.d.ts +77 -0
- package/dist/richtext/RichText_Refactor.js +679 -0
- package/package.json +3 -3
- package/readme.md +52 -2
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
36
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
37
|
+
if (ar || !(i in from)) {
|
|
38
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
39
|
+
ar[i] = from[i];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.RichText_Refactor = exports.LinkNodeType = void 0;
|
|
46
|
+
var cheerio = __importStar(require("cheerio"));
|
|
47
|
+
/*****************************************************
|
|
48
|
+
* 类型、枚举定义
|
|
49
|
+
*****************************************************/
|
|
50
|
+
/** 链接节点的类型 */
|
|
51
|
+
var LinkNodeType;
|
|
52
|
+
(function (LinkNodeType) {
|
|
53
|
+
LinkNodeType["IMG"] = "img";
|
|
54
|
+
LinkNodeType["DIV"] = "div";
|
|
55
|
+
})(LinkNodeType || (exports.LinkNodeType = LinkNodeType = {}));
|
|
56
|
+
/*****************************************************
|
|
57
|
+
* 工具函数
|
|
58
|
+
*****************************************************/
|
|
59
|
+
/**
|
|
60
|
+
* 解析 style 字符串为对象
|
|
61
|
+
*/
|
|
62
|
+
var parseStyleString = function (styleStr) {
|
|
63
|
+
var result = {};
|
|
64
|
+
if (!styleStr || !styleStr.trim())
|
|
65
|
+
return result;
|
|
66
|
+
// 分割多个样式声明
|
|
67
|
+
var declarations = styleStr.split(";").filter(function (decl) { return decl.trim(); });
|
|
68
|
+
declarations.forEach(function (decl) {
|
|
69
|
+
var colonIndex = decl.indexOf(":");
|
|
70
|
+
if (colonIndex > 0) {
|
|
71
|
+
var property = decl.substring(0, colonIndex).trim();
|
|
72
|
+
var value = decl.substring(colonIndex + 1).trim();
|
|
73
|
+
if (property && value) {
|
|
74
|
+
result[property] = value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return result;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* 将样式对象序列化为字符串
|
|
82
|
+
*/
|
|
83
|
+
var serializeStyleObject = function (styleObj) {
|
|
84
|
+
return (Object.entries(styleObj)
|
|
85
|
+
.map(function (_a) {
|
|
86
|
+
var property = _a[0], value = _a[1];
|
|
87
|
+
return "".concat(property, ": ").concat(value);
|
|
88
|
+
})
|
|
89
|
+
.join("; ") + (Object.keys(styleObj).length > 0 ? ";" : ""));
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* 合并多个 style 字符串,自动处理分号与空格,后合并的覆盖前面的(符合CSS优先级规则)
|
|
93
|
+
*/
|
|
94
|
+
var mergeStyles = function () {
|
|
95
|
+
var styles = [];
|
|
96
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
97
|
+
styles[_i] = arguments[_i];
|
|
98
|
+
}
|
|
99
|
+
var mergedStyleObj = {};
|
|
100
|
+
// 过滤空值并解析每个样式字符串
|
|
101
|
+
var validStyles = styles.filter(function (s) { return !!s && s.trim().length > 0; });
|
|
102
|
+
// 按顺序合并,后面的覆盖前面的
|
|
103
|
+
validStyles.forEach(function (styleStr) {
|
|
104
|
+
var styleObj = parseStyleString(styleStr);
|
|
105
|
+
Object.assign(mergedStyleObj, styleObj);
|
|
106
|
+
});
|
|
107
|
+
return serializeStyleObject(mergedStyleObj);
|
|
108
|
+
};
|
|
109
|
+
/*****************************************************
|
|
110
|
+
* RichText_Refactor 主类
|
|
111
|
+
*****************************************************/
|
|
112
|
+
var RichText_Refactor = /** @class */ (function () {
|
|
113
|
+
function RichText_Refactor() {
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 富文本转换为 PixUI innerHTML 兼容格式
|
|
117
|
+
* @param str 原始富文本 HTML
|
|
118
|
+
* @param config 可选配置
|
|
119
|
+
*/
|
|
120
|
+
RichText_Refactor.convertRichTextToPixuiStyle = function (str, config) {
|
|
121
|
+
// 重置全局状态
|
|
122
|
+
RichText_Refactor.linkNodes = [];
|
|
123
|
+
RichText_Refactor.hrefIdCnt = 0;
|
|
124
|
+
var $1 = cheerio.load(str, null, false);
|
|
125
|
+
//检查html,如果有text节点,说明是转换过的,直接返回
|
|
126
|
+
if ($1("text").length > 0) {
|
|
127
|
+
return str;
|
|
128
|
+
}
|
|
129
|
+
// ---------- 第 1 步:段落分割(基础实现:根节点切分) ----------
|
|
130
|
+
var segments = RichText_Refactor.splitIntoSegments($1);
|
|
131
|
+
// ---------- 第 2/3 步:遍历 + 展平 ----------
|
|
132
|
+
// 为保证兼容性,我们直接对 HTML 进行逐段处理并在内部构建 PixUI 结构。
|
|
133
|
+
// 这样既保持分段概念,又可复用旧逻辑的稳定性。
|
|
134
|
+
var processedSegments = segments.map(function (seg) { return RichText_Refactor.processSegment(seg, config); });
|
|
135
|
+
// ---------- 第 4 步:拼装 ----------
|
|
136
|
+
var Result = processedSegments.join("");
|
|
137
|
+
// console.log(Result);
|
|
138
|
+
return Result;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* 绑定链接点击事件
|
|
142
|
+
*/
|
|
143
|
+
RichText_Refactor.bindLinkClickEvents = function (linkClickHandler) {
|
|
144
|
+
RichText_Refactor.linkNodes.forEach(function (nodeInfo) {
|
|
145
|
+
var element = document.getElementById(nodeInfo.id);
|
|
146
|
+
if (element) {
|
|
147
|
+
element.onclick = function (e) {
|
|
148
|
+
e.stopPropagation();
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
linkClickHandler({
|
|
151
|
+
type: nodeInfo.type,
|
|
152
|
+
href: nodeInfo.href,
|
|
153
|
+
id: nodeInfo.id,
|
|
154
|
+
});
|
|
155
|
+
return false;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
/*****************************************************
|
|
161
|
+
* 内部流程函数
|
|
162
|
+
*****************************************************/
|
|
163
|
+
/**
|
|
164
|
+
* 将 HTML 切分为基础段落片段。
|
|
165
|
+
* 简易实现:以顶级节点为粒度切分,再根据最外层列表节点进行分割。
|
|
166
|
+
*/
|
|
167
|
+
RichText_Refactor.splitIntoSegments = function ($) {
|
|
168
|
+
var segments = [];
|
|
169
|
+
// 如果有 body,取其直接子节点;否则取根节点下的直接子节点
|
|
170
|
+
var roots = $("body").length > 0 ? $("body").children() : $.root().children();
|
|
171
|
+
roots.each(function () {
|
|
172
|
+
var nodeHtml = $(this).toString();
|
|
173
|
+
// 切分包含最外层列表的片段
|
|
174
|
+
if (/^<\s*(ol|ul)/i.test(nodeHtml)) {
|
|
175
|
+
segments.push(nodeHtml);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// 进一步检查内部是否存在最外层的 ol/ul,需要分成 前段 + 列表 + 后段
|
|
179
|
+
var inner$_1 = cheerio.load(nodeHtml, null, false);
|
|
180
|
+
var outerLists_1 = [];
|
|
181
|
+
inner$_1("ol, ul").each(function () {
|
|
182
|
+
if (inner$_1(this).parents("ol, ul").length === 0) {
|
|
183
|
+
outerLists_1.push(inner$_1(this).toString());
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
if (outerLists_1.length === 0) {
|
|
187
|
+
segments.push(nodeHtml);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
var tempHtml_1 = nodeHtml;
|
|
191
|
+
outerLists_1.forEach(function (listHtml) {
|
|
192
|
+
var idx = tempHtml_1.indexOf(listHtml);
|
|
193
|
+
if (idx >= 0) {
|
|
194
|
+
var before = tempHtml_1.substring(0, idx);
|
|
195
|
+
if (before.trim())
|
|
196
|
+
segments.push(before);
|
|
197
|
+
segments.push(listHtml);
|
|
198
|
+
tempHtml_1 = tempHtml_1.substring(idx + listHtml.length);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
if (tempHtml_1.trim())
|
|
202
|
+
segments.push(tempHtml_1);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
// fallback:原样返回
|
|
207
|
+
if (segments.length === 0)
|
|
208
|
+
segments.push($.html());
|
|
209
|
+
return segments;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* 处理单个段落片段,返回转换后的片段 HTML
|
|
213
|
+
*/
|
|
214
|
+
RichText_Refactor.processSegment = function (segmentHtml, config) {
|
|
215
|
+
var $ = cheerio.load(segmentHtml, null, false);
|
|
216
|
+
// 按旧实现顺序进行处理,但重构为独立函数划分
|
|
217
|
+
this.preprocess($);
|
|
218
|
+
this.fixImgSizeUnit($);
|
|
219
|
+
this.handleLists($, (config === null || config === void 0 ? void 0 : config.lineHeightScale) || 1);
|
|
220
|
+
this.replaceBrWithPlaceholder($);
|
|
221
|
+
this.handleAnchorTag($);
|
|
222
|
+
this.wrapTextNodes($);
|
|
223
|
+
this.replaceStyleTags($);
|
|
224
|
+
this.replacePTag($);
|
|
225
|
+
this.handleImgHref($);
|
|
226
|
+
this.addFlexShrink($);
|
|
227
|
+
this.flattenNestedText($);
|
|
228
|
+
this.convertSiblingTextToDiv($);
|
|
229
|
+
this.handleTextIndent($);
|
|
230
|
+
this.adjustLineHeightAndLetterSpacing($, config);
|
|
231
|
+
this.transferTextAlign($);
|
|
232
|
+
this.removeEmptyText($);
|
|
233
|
+
// this.fixHeadingFontSize($);
|
|
234
|
+
// 最后:修正 img 结束标签、nbsp
|
|
235
|
+
var res = $.html();
|
|
236
|
+
res = res
|
|
237
|
+
.replaceAll(/<img([^>]+)>/g, "<img$1 />")
|
|
238
|
+
.replaceAll(/ /g, " ")
|
|
239
|
+
.replaceAll(/&/g, "&")
|
|
240
|
+
.replaceAll(/"/g, '"')
|
|
241
|
+
.replaceAll("\"", "\"");
|
|
242
|
+
return res;
|
|
243
|
+
};
|
|
244
|
+
// ------------------------------
|
|
245
|
+
// 各独立处理步骤实现
|
|
246
|
+
// ------------------------------
|
|
247
|
+
RichText_Refactor.preprocess = function ($) {
|
|
248
|
+
var _loop_1 = function (i) {
|
|
249
|
+
var indent = "ql-indent-".concat(i);
|
|
250
|
+
var extStyle = "padding-left: ".concat(i * 2, "em");
|
|
251
|
+
$(".".concat(indent)).each(function () {
|
|
252
|
+
var ori = $(this).attr("style") || "";
|
|
253
|
+
$(this).attr("style", mergeStyles(ori, extStyle));
|
|
254
|
+
$(this).removeClass(indent);
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
for (var i = 1; i <= 10; i++) {
|
|
258
|
+
_loop_1(i);
|
|
259
|
+
}
|
|
260
|
+
// 在所有标题标签后插入换行 <br>
|
|
261
|
+
$("h1, h2, h3, h4, h5, h6").each(function () {
|
|
262
|
+
// 若标题后紧跟已有换行则不再追加
|
|
263
|
+
var next = $(this).next();
|
|
264
|
+
if (!next.is("br")) {
|
|
265
|
+
$(this).after("<br>");
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
// 将所有的margin-left转换为padding-left
|
|
269
|
+
$("*").each(function () {
|
|
270
|
+
var oriStyle = $(this).attr("style") || "";
|
|
271
|
+
if (!oriStyle)
|
|
272
|
+
return;
|
|
273
|
+
var styleObj = parseStyleString(oriStyle);
|
|
274
|
+
var marginLeftVal = styleObj["margin-left"];
|
|
275
|
+
if (marginLeftVal !== undefined) {
|
|
276
|
+
// 移除 margin-left
|
|
277
|
+
delete styleObj["margin-left"];
|
|
278
|
+
// 如果原本没有 padding-left,则直接设置;若已有,简单覆盖(遵循后写优先规则)
|
|
279
|
+
styleObj["padding-left"] = styleObj["padding-left"] || marginLeftVal;
|
|
280
|
+
// 写回合并后的样式
|
|
281
|
+
$(this).attr("style", serializeStyleObject(styleObj));
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
};
|
|
285
|
+
RichText_Refactor.fixImgSizeUnit = function ($) {
|
|
286
|
+
$("img").each(function () {
|
|
287
|
+
var width = $(this).attr("width");
|
|
288
|
+
var height = $(this).attr("height");
|
|
289
|
+
if (width && width + "" === parseInt(width) + "") {
|
|
290
|
+
$(this).attr("width", "".concat(width, "px"));
|
|
291
|
+
}
|
|
292
|
+
if (height && height + "" === parseInt(height) + "") {
|
|
293
|
+
$(this).attr("height", "".concat(height, "px"));
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
};
|
|
297
|
+
/** 列表处理(将 ol/ul/li 替换为 div 结构) */
|
|
298
|
+
RichText_Refactor.handleLists = function ($, lineHeightScale) {
|
|
299
|
+
if (lineHeightScale === void 0) { lineHeightScale = 1; }
|
|
300
|
+
var topLevelLists = [];
|
|
301
|
+
var collectListItems = function ($container, currentLevel) {
|
|
302
|
+
if (currentLevel === void 0) { currentLevel = 0; }
|
|
303
|
+
var items = [];
|
|
304
|
+
$container.children().each(function () {
|
|
305
|
+
var $child = $(this);
|
|
306
|
+
if ($child.is("li")) {
|
|
307
|
+
var isOrdered = $child.parent().is("ol");
|
|
308
|
+
var orderIndex = 1;
|
|
309
|
+
if (isOrdered) {
|
|
310
|
+
orderIndex = $child.prevAll("li").length + 1;
|
|
311
|
+
}
|
|
312
|
+
var marker = isOrdered ? "".concat(orderIndex, ".") : "•";
|
|
313
|
+
var paddingLeft = currentLevel * 2; // 每层缩进 2em
|
|
314
|
+
var $liClone = $child.clone();
|
|
315
|
+
$liClone.find("ol, ul").remove();
|
|
316
|
+
var textContent = $liClone.text().trim() || "";
|
|
317
|
+
var style = $child.attr("style") || "";
|
|
318
|
+
items.push({
|
|
319
|
+
level: currentLevel,
|
|
320
|
+
isOrdered: isOrdered,
|
|
321
|
+
orderIndex: orderIndex,
|
|
322
|
+
marker: marker,
|
|
323
|
+
paddingLeft: paddingLeft,
|
|
324
|
+
textContent: textContent,
|
|
325
|
+
style: style,
|
|
326
|
+
});
|
|
327
|
+
// 处理子列表
|
|
328
|
+
$child.find("ol, ul").each(function () {
|
|
329
|
+
var $nestedList = $(this);
|
|
330
|
+
if ($nestedList.parent().closest("li")[0] === $child[0]) {
|
|
331
|
+
var childItems = collectListItems($nestedList, currentLevel + 1);
|
|
332
|
+
items.push.apply(items, childItems);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
return items;
|
|
338
|
+
};
|
|
339
|
+
// 找到所有顶层列表
|
|
340
|
+
$("ol, ul").each(function () {
|
|
341
|
+
var $list = $(this);
|
|
342
|
+
if ($list.parents("ol, ul").length === 0) {
|
|
343
|
+
var placeholder = $("<div data-list-placeholder=\"true\"></div>");
|
|
344
|
+
$list.before(placeholder);
|
|
345
|
+
var items = collectListItems($list, 1);
|
|
346
|
+
topLevelLists.push({
|
|
347
|
+
element: $list,
|
|
348
|
+
placeholder: placeholder,
|
|
349
|
+
items: items,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
// 移除原列表结构
|
|
354
|
+
$("ol, ul, li").remove();
|
|
355
|
+
// 插入新列表
|
|
356
|
+
topLevelLists.forEach(function (listInfo) {
|
|
357
|
+
var placeholder = listInfo.placeholder, items = listInfo.items;
|
|
358
|
+
var html = items
|
|
359
|
+
.map(function (it, idx) {
|
|
360
|
+
// 所有列表项默认上下 0.5 * lineHeightScale em
|
|
361
|
+
var marginTopVal = idx === 0 ? 1 * lineHeightScale : 0.5 * lineHeightScale;
|
|
362
|
+
var marginBottomVal = idx === items.length - 1 ? 1 * lineHeightScale : 0.5 * lineHeightScale;
|
|
363
|
+
var marginAdjust = ["margin-top: ".concat(marginTopVal, "em"), "margin-bottom: ".concat(marginBottomVal, "em")];
|
|
364
|
+
return ("<div style=\"".concat(mergeStyles.apply(void 0, __spreadArray(__spreadArray([it.style], marginAdjust, false), ["padding-left: ".concat(it.paddingLeft, "em"), "flex-shrink: 0",
|
|
365
|
+
"width: 100%",
|
|
366
|
+
"flex-direction: row",
|
|
367
|
+
"display: flex"], false)), "\" data-list-item=\"true\">") +
|
|
368
|
+
"<text style=\"".concat(mergeStyles("flex-shrink: 0", "width: 100%", "line-height: 1", "display: flex", "flex-direction: row"), "\">") +
|
|
369
|
+
"<div style=\"".concat(mergeStyles("word-break: break-word", "flex-shrink: 0", "padding-right: 0.5em"), "\">").concat(it.marker, "</div>") +
|
|
370
|
+
"<div style=\"".concat(mergeStyles("word-break: break-word", "flex-shrink: 0", "flex: 1"), "\">").concat(it.textContent, "</div>") +
|
|
371
|
+
"</text></div>");
|
|
372
|
+
})
|
|
373
|
+
.join("");
|
|
374
|
+
placeholder.after(html);
|
|
375
|
+
placeholder.remove();
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
/** 将连续 <br> 替换为透明占位文字,确保 PixUI 换行 */
|
|
379
|
+
RichText_Refactor.replaceBrWithPlaceholder = function ($) {
|
|
380
|
+
$("br").each(function () {
|
|
381
|
+
$(this).replaceWith("<div style=\"".concat(mergeStyles("color: transparent", "flex-shrink: 0", "font-size: 20px"), "\" class='pixui-richtext-br-placeholder'>1</div>"));
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
/** 处理 <a> 标签:转为 div、保留样式、记录链接 */
|
|
385
|
+
RichText_Refactor.handleAnchorTag = function ($) {
|
|
386
|
+
var self = this;
|
|
387
|
+
$("a").each(function () {
|
|
388
|
+
var id = "PA_RichTextHrefId_".concat(self.hrefIdCnt++);
|
|
389
|
+
var href = $(this).attr("href");
|
|
390
|
+
var originalStyle = $(this).attr("style") || "";
|
|
391
|
+
var textContent = $(this).text();
|
|
392
|
+
var mergedStyles = [];
|
|
393
|
+
// 收集子元素的样式
|
|
394
|
+
if ($(this).find("strong").length > 0)
|
|
395
|
+
mergedStyles.push("font-weight: bold;");
|
|
396
|
+
if ($(this).find("em").length > 0)
|
|
397
|
+
mergedStyles.push("font-style: italic;");
|
|
398
|
+
if ($(this).find("u").length > 0)
|
|
399
|
+
mergedStyles.push("text-decoration: underline;");
|
|
400
|
+
if ($(this).find("s").length > 0)
|
|
401
|
+
mergedStyles.push("text-decoration: line-through;");
|
|
402
|
+
$(this)
|
|
403
|
+
.find("strong, em, u, s, span")
|
|
404
|
+
.each(function () {
|
|
405
|
+
var innerStyle = $(this).attr("style");
|
|
406
|
+
if (innerStyle)
|
|
407
|
+
mergedStyles.push(innerStyle);
|
|
408
|
+
});
|
|
409
|
+
var finalStyle = mergeStyles.apply(void 0, __spreadArray([originalStyle], mergedStyles, false));
|
|
410
|
+
if (href) {
|
|
411
|
+
self.linkNodes.push({ type: LinkNodeType.DIV, href: href, id: id });
|
|
412
|
+
}
|
|
413
|
+
$(this).replaceWith("<div style=\"".concat(finalStyle, "\" id=\"").concat(id, "\" class=\"PA_RichTextHref_ATag\">").concat(textContent, "</div>"));
|
|
414
|
+
});
|
|
415
|
+
};
|
|
416
|
+
/** 用 text 标签包裹纯文本节点 */
|
|
417
|
+
RichText_Refactor.wrapTextNodes = function ($) {
|
|
418
|
+
var self = this;
|
|
419
|
+
$("*").each(function () {
|
|
420
|
+
var nodeId = $(this).attr("id") || "";
|
|
421
|
+
if (nodeId.startsWith("PA_RichTextHrefId_"))
|
|
422
|
+
return;
|
|
423
|
+
var isListItem = $(this).attr("data-list-item") === "true";
|
|
424
|
+
if (isListItem)
|
|
425
|
+
return;
|
|
426
|
+
$(this)
|
|
427
|
+
.contents()
|
|
428
|
+
.each(function () {
|
|
429
|
+
var linkParent = $(this).closest("[id^='PA_RichTextHrefId_']");
|
|
430
|
+
if (linkParent.length > 0)
|
|
431
|
+
return;
|
|
432
|
+
var listParent = $(this).closest("[data-list-item='true']");
|
|
433
|
+
if (listParent.length > 0)
|
|
434
|
+
return;
|
|
435
|
+
if (this.type === "text") {
|
|
436
|
+
var text = this.data.trim();
|
|
437
|
+
if (text.length > 0) {
|
|
438
|
+
$(this).replaceWith("<text style=\"".concat(mergeStyles("word-break: break-word", "flex-shrink: 0", "flex-direction: row"), "\">").concat(text, "</text>"));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
/** strong / em / u / s / span 等标签替换成 text */
|
|
445
|
+
RichText_Refactor.replaceStyleTags = function ($) {
|
|
446
|
+
var tagToStyle = {
|
|
447
|
+
strong: "font-weight: bold",
|
|
448
|
+
em: "font-style: italic",
|
|
449
|
+
u: "text-decoration: underline",
|
|
450
|
+
s: "text-decoration: line-through",
|
|
451
|
+
};
|
|
452
|
+
var hasLeftover = true;
|
|
453
|
+
while (hasLeftover) {
|
|
454
|
+
hasLeftover = false;
|
|
455
|
+
// 处理 strong / em / u / s
|
|
456
|
+
Object.entries(tagToStyle).forEach(function (_a) {
|
|
457
|
+
var tag = _a[0], style = _a[1];
|
|
458
|
+
$(tag).each(function () {
|
|
459
|
+
var html = $(this).html();
|
|
460
|
+
var oriStyle = $(this).attr("style") || "";
|
|
461
|
+
$(this).replaceWith("<text style=\"".concat(mergeStyles(oriStyle, style, "flex-shrink: 0"), "\">").concat(html, "</text>"));
|
|
462
|
+
hasLeftover = true;
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
// 处理 span
|
|
466
|
+
$("span").each(function () {
|
|
467
|
+
var html = $(this).html();
|
|
468
|
+
var oriStyle = $(this).attr("style") || "";
|
|
469
|
+
$(this).replaceWith("<text style=\"".concat(mergeStyles(oriStyle, "flex-shrink: 0"), "\">").concat(html, "</text>"));
|
|
470
|
+
hasLeftover = true;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
/** p → div */
|
|
475
|
+
RichText_Refactor.replacePTag = function ($) {
|
|
476
|
+
$("p").each(function () {
|
|
477
|
+
var html = $(this).html();
|
|
478
|
+
var style = $(this).attr("style") || "";
|
|
479
|
+
$(this).replaceWith("<div style=\"".concat(mergeStyles(style, "flex-shrink: 0", "width:100%"), "\">").concat(html, "</div>"));
|
|
480
|
+
});
|
|
481
|
+
};
|
|
482
|
+
/** img 的 href 转移处理 & 记录链接 */
|
|
483
|
+
RichText_Refactor.handleImgHref = function ($) {
|
|
484
|
+
var self = this;
|
|
485
|
+
$("img").each(function () {
|
|
486
|
+
var id = "PA_RichTextHrefId_".concat(self.hrefIdCnt++);
|
|
487
|
+
$(this).attr("id", id);
|
|
488
|
+
var href = $(this).attr("href");
|
|
489
|
+
if (href) {
|
|
490
|
+
self.linkNodes.push({ type: LinkNodeType.IMG, href: href, id: id });
|
|
491
|
+
}
|
|
492
|
+
$(this).attr("href", "");
|
|
493
|
+
var prevClass = $(this).attr("class") || "";
|
|
494
|
+
var newClasses = new Set(prevClass.split(/\s+/).filter(Boolean));
|
|
495
|
+
newClasses.add("PA_RichTextHref_ImgTag"); // 超链接专用标记(保持原有逻辑)
|
|
496
|
+
newClasses.add("PA_RichTextImgTag"); // 富文本图片通用标记
|
|
497
|
+
$(this).attr("class", Array.from(newClasses).join(" "));
|
|
498
|
+
});
|
|
499
|
+
};
|
|
500
|
+
/** 给指定标签补 flex-shrink:0 */
|
|
501
|
+
RichText_Refactor.addFlexShrink = function ($) {
|
|
502
|
+
["h1", "h2", "h3", "h4", "h5", "h6", "a", "img", "div"].forEach(function (tag) {
|
|
503
|
+
$(tag).each(function () {
|
|
504
|
+
var isListItem = $(this).attr("data-list-item") === "true";
|
|
505
|
+
var inListItem = $(this).closest("[data-list-item='true']").length > 0;
|
|
506
|
+
if (!isListItem && !inListItem) {
|
|
507
|
+
var style = $(this).attr("style") || "";
|
|
508
|
+
$(this).attr("style", mergeStyles(style, "flex-shrink: 0"));
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
};
|
|
513
|
+
/** 把嵌套的 text 展平 */
|
|
514
|
+
RichText_Refactor.flattenNestedText = function ($) {
|
|
515
|
+
while ($("text > text").length > 0) {
|
|
516
|
+
$("text").each(function () {
|
|
517
|
+
var $this = $(this);
|
|
518
|
+
var parentStyle = $this.attr("style") || "";
|
|
519
|
+
$this.children("text").each(function () {
|
|
520
|
+
var $child = $(this);
|
|
521
|
+
var childStyle = $child.attr("style") || "";
|
|
522
|
+
var mergedStyle = mergeStyles(parentStyle, childStyle);
|
|
523
|
+
$child.attr("style", mergedStyle);
|
|
524
|
+
});
|
|
525
|
+
if ($this.children("text").length > 0) {
|
|
526
|
+
$this.replaceWith($this.html() || "");
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
/** 将同一行的 text 节点转为 div 并外包一层 text */
|
|
532
|
+
RichText_Refactor.convertSiblingTextToDiv = function ($) {
|
|
533
|
+
var nodeArr = [];
|
|
534
|
+
["h1", "h2", "h3", "h4", "h5", "h6", "div"].forEach(function (tag) {
|
|
535
|
+
$(tag).each(function () {
|
|
536
|
+
var nodeId = $(this).attr("id") || "";
|
|
537
|
+
var isListItem = $(this).attr("data-list-item") === "true";
|
|
538
|
+
var inListItem = $(this).closest("[data-list-item='true']").length > 0;
|
|
539
|
+
if (!nodeId.startsWith("PA_RichTextHrefId_") && !isListItem && !inListItem) {
|
|
540
|
+
nodeArr.push(this);
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
nodeArr.forEach(function (node) {
|
|
545
|
+
var tag = $(node).prop("tagName");
|
|
546
|
+
var nodeId = $(node).attr("id") || "";
|
|
547
|
+
// 处理子元素:将text转为div,但保留link节点
|
|
548
|
+
$(node)
|
|
549
|
+
.children("text")
|
|
550
|
+
.each(function () {
|
|
551
|
+
var inListItem = $(this).closest("[data-list-item='true']").length > 0;
|
|
552
|
+
if (inListItem)
|
|
553
|
+
return;
|
|
554
|
+
var text = $(this).html();
|
|
555
|
+
var style = $(this).attr("style") || "";
|
|
556
|
+
// 检查是否包含link节点
|
|
557
|
+
var hasLink = $(this).find('[id^="PA_RichTextHrefId_"]').length > 0;
|
|
558
|
+
if (hasLink) {
|
|
559
|
+
// 如果包含link节点,需要特殊处理以保持link在正确位置
|
|
560
|
+
var $tempDiv = $("<div style=\"".concat(mergeStyles(style, "word-break: break-word", "flex-shrink: 0", "flex-direction: row"), "\">").concat(text, "</div>"));
|
|
561
|
+
$(this).replaceWith($tempDiv);
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
// 普通text节点直接转换
|
|
565
|
+
$(this).replaceWith("<div style=\"".concat(mergeStyles(style, "word-break: break-word", "flex-shrink: 0", "flex-direction: row"), "\">").concat(text, "</div>"));
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
var tagHtml = $(node).html() || "";
|
|
569
|
+
var tagStyle = $(node).attr("style") || "";
|
|
570
|
+
if (tagHtml.trim()) {
|
|
571
|
+
var idAttr = nodeId ? " id=\"".concat(nodeId, "\"") : "";
|
|
572
|
+
var customAttrs = "";
|
|
573
|
+
var attribs = ($(node).get(0) || {}).attribs || {};
|
|
574
|
+
for (var _i = 0, _a = Object.entries(attribs); _i < _a.length; _i++) {
|
|
575
|
+
var _b = _a[_i], k = _b[0], v = _b[1];
|
|
576
|
+
if (k !== "style" && k !== "id") {
|
|
577
|
+
customAttrs += " ".concat(k, "=\"").concat(v, "\"");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// 保证外层tag的style不合并子div的style
|
|
581
|
+
$(node).replaceWith("<".concat(tag, " style=\"").concat(mergeStyles(tagStyle, "flex-direction: row"), "\"").concat(idAttr).concat(customAttrs, "><text style=\"").concat(mergeStyles("flex-shrink: 0", "width: 100%", "line-height: 1"), "\">").concat(tagHtml, "</text></").concat(tag, ">"));
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
};
|
|
585
|
+
/** text-indent 首行缩进处理 */
|
|
586
|
+
RichText_Refactor.handleTextIndent = function ($) {
|
|
587
|
+
$("*").each(function () {
|
|
588
|
+
var textIndent = $(this).css("text-indent");
|
|
589
|
+
if (textIndent) {
|
|
590
|
+
var repeatCnt = parseInt(textIndent);
|
|
591
|
+
if (repeatCnt > 0) {
|
|
592
|
+
var spacer = "<div style=\"".concat(mergeStyles("color: transparent", "flex-shrink: 0", "font-size: 16px"), "\">").concat("一".repeat(repeatCnt), "</div>");
|
|
593
|
+
var textChild = $(this).children("text").first();
|
|
594
|
+
if (textChild.length > 0) {
|
|
595
|
+
var first = textChild.children().first();
|
|
596
|
+
if (first.length > 0)
|
|
597
|
+
first.before(spacer);
|
|
598
|
+
else
|
|
599
|
+
textChild.append(spacer);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
};
|
|
605
|
+
/** line-height / letter-spacing 调整 */
|
|
606
|
+
RichText_Refactor.adjustLineHeightAndLetterSpacing = function ($, config) {
|
|
607
|
+
$("text").each(function () {
|
|
608
|
+
var $text = $(this);
|
|
609
|
+
var maxLineHeight = 0;
|
|
610
|
+
$text.children("div").each(function () {
|
|
611
|
+
var lh = parseInt($(this).css("line-height") || "1");
|
|
612
|
+
maxLineHeight = Math.max(maxLineHeight, lh);
|
|
613
|
+
});
|
|
614
|
+
var scale = (config === null || config === void 0 ? void 0 : config.lineHeightScale) || 1;
|
|
615
|
+
if (maxLineHeight > 0) {
|
|
616
|
+
var currentStyle = $text.attr("style") || "";
|
|
617
|
+
$text.attr("style", mergeStyles(currentStyle, "line-height: ".concat(maxLineHeight * scale)));
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
$("text").each(function () {
|
|
621
|
+
var $text = $(this);
|
|
622
|
+
var maxLS = 0;
|
|
623
|
+
$text.children("div").each(function () {
|
|
624
|
+
var ls = $(this).css("letter-spacing") || "0";
|
|
625
|
+
var lsNum = parseInt(ls);
|
|
626
|
+
maxLS = Math.max(maxLS, lsNum);
|
|
627
|
+
});
|
|
628
|
+
if (maxLS > 0) {
|
|
629
|
+
var currentStyle = $text.attr("style") || "";
|
|
630
|
+
$text.attr("style", mergeStyles(currentStyle, "letter-spacing: ".concat(maxLS, "em")));
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
};
|
|
634
|
+
/** 把 div 的 text-align 转移到子 text 上 */
|
|
635
|
+
RichText_Refactor.transferTextAlign = function ($) {
|
|
636
|
+
$("div").each(function () {
|
|
637
|
+
var ta = $(this).css("text-align") || "";
|
|
638
|
+
if (ta) {
|
|
639
|
+
$(this)
|
|
640
|
+
.children("text")
|
|
641
|
+
.each(function () {
|
|
642
|
+
var currentStyle = $(this).attr("style") || "";
|
|
643
|
+
$(this).attr("style", mergeStyles(currentStyle, "text-align: ".concat(ta)));
|
|
644
|
+
});
|
|
645
|
+
// 清除div的text-align
|
|
646
|
+
var currentStyle = $(this).attr("style") || "";
|
|
647
|
+
var styleObj = parseStyleString(currentStyle);
|
|
648
|
+
delete styleObj["text-align"];
|
|
649
|
+
$(this).attr("style", serializeStyleObject(styleObj));
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
};
|
|
653
|
+
/** 删除空 text 节点 */
|
|
654
|
+
RichText_Refactor.removeEmptyText = function ($) {
|
|
655
|
+
$("text").each(function () {
|
|
656
|
+
var t = $(this).html() || "";
|
|
657
|
+
if (t.trim() === "")
|
|
658
|
+
$(this).remove();
|
|
659
|
+
});
|
|
660
|
+
};
|
|
661
|
+
/** 补默认 heading 字体大小 */
|
|
662
|
+
RichText_Refactor.fixHeadingFontSize = function ($) {
|
|
663
|
+
var hRem = ["2em", "1.5em", "1.17em", "1em", "0.83em", "0.67em"];
|
|
664
|
+
["h1", "h2", "h3", "h4", "h5", "h6"].forEach(function (tag) {
|
|
665
|
+
$(tag).each(function () {
|
|
666
|
+
var idx = parseInt(tag.replace("h", "")) - 1;
|
|
667
|
+
var style = $(this).attr("style") || "";
|
|
668
|
+
if (!style.includes("font-size")) {
|
|
669
|
+
$(this).attr("style", mergeStyles(style, "font-size:".concat(hRem[idx])));
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
};
|
|
674
|
+
// 存储需要绑定点击事件的节点
|
|
675
|
+
RichText_Refactor.linkNodes = [];
|
|
676
|
+
RichText_Refactor.hrefIdCnt = 0;
|
|
677
|
+
return RichText_Refactor;
|
|
678
|
+
}());
|
|
679
|
+
exports.RichText_Refactor = RichText_Refactor;
|