Skip to content

Commit be6c314

Browse files
Merge pull request #3867 from wangeditor-team/fix-link-text
fix: url或者常规文本中HTML转义字符显示异常
2 parents 93c71c4 + 03310ee commit be6c314

File tree

4 files changed

+78
-20
lines changed

4 files changed

+78
-20
lines changed

src/menus/link/create-panel-conf.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,19 @@ export default function (editor: Editor, text: string, link: string): PanelConf
5353
*
5454
* 同上,列表无法插入链接的原因,是因为在insertLink, 处理text时有问题。
5555
*/
56+
57+
const $elem: DomElement = $(`<a href="${link}" target="_blank">${text}</a>`)
58+
59+
// fix: 字符转义问题,https://xxx.org?bar=1&macro=2 => https://xxx.org?bar=1¯o=2
60+
$elem.elems[0].innerText = text
61+
5662
if (isActive(editor)) {
5763
// 选区处于链接中,则选中整个菜单,再执行 insertHTML
5864
selectLinkElem()
59-
editor.cmd.do('insertHTML', `<a href="${link}" target="_blank">${text}</a>`)
65+
editor.cmd.do('insertElem', $elem)
6066
} else {
6167
// 选区未处于链接中,直接插入即可
62-
editor.cmd.do('insertHTML', `<a href="${link}" target="_blank">${text}</a>`)
68+
editor.cmd.do('insertElem', $elem)
6369
}
6470
}
6571

src/text/event-hooks/paste-text-html.ts

+35-7
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function pasteTextHtml(editor: Editor, pasteEvents: Function[]) {
113113
return
114114
}
115115

116-
// 如果用户开启闭粘贴样式注释则将复制进来为url的直接转为链接 否则不转换
116+
// 如果用户开启闭粘贴样式注释则将复制进来为url的直接转为链接 否则不转换
117117
// 在群中有用户提到关闭样式粘贴复制的文字进来后链接直接转为文字了,不符合预期,这里优化下
118118
if (urlRegex.test(pasteText) && pasteFilterStyle) {
119119
//当复制的内容为链接时,也应该判断用户是否定义了处理粘贴的事件
@@ -122,13 +122,41 @@ function pasteTextHtml(editor: Editor, pasteEvents: Function[]) {
122122
pasteText = '' + (pasteTextHandle(pasteText) || '') // html
123123
}
124124

125-
const insertUrl = urlRegex.exec(pasteText)![0]
126-
const otherText = pasteText.replace(urlRegex, '')
125+
// 当复制一个链接和文本时,需要区分出文本和a链接, 如:http://www.baidu.com 搜索。 issue: #3129
126+
// 目前也支持粘贴文案:粘贴http://www.baidu.com粘贴http://www.baidu.com,连个链接。
127+
const resultText = pasteText.replace(urlRegex, function (link: string) {
128+
return `<a href="${link}" target="_blank">${link}</a>`
129+
})
130+
const range = editor.selection.getRange()
127131

128-
return editor.cmd.do(
129-
'insertHTML',
130-
`<a href="${insertUrl}" target="_blank">${insertUrl}</a>${otherText}`
131-
) // html
132+
// 文本转义问题,如果直接使用innerHTML插入html结构,地址中的特殊字符会被转义
133+
// 先生成元素,替换里面的文本,利用insertElem插入到页面
134+
const div = document.createElement('div')
135+
const fragment = document.createDocumentFragment()
136+
137+
div.innerHTML = resultText
138+
139+
if (range == null) return
140+
141+
// 将div里的dom结构,搬到fragment里
142+
while (div.childNodes.length) {
143+
fragment.append(div.childNodes[0])
144+
}
145+
146+
// 修改a 链接文案,使用innerText插入文本,这样就避免了使用innerHTML时把特殊符号转义
147+
const linkEle = fragment.querySelectorAll('a')
148+
linkEle.forEach(ele => {
149+
ele.innerText = ele.href
150+
})
151+
152+
if (range.insertNode) {
153+
range.deleteContents()
154+
range.insertNode(fragment)
155+
}
156+
157+
editor.selection.clearWindowSelectionRange()
158+
159+
return
132160
}
133161
// table 中(td、th),待开发。。。
134162
if (!pasteHtml) {

src/utils/const.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
export function EMPTY_FN() {}
77

88
//用于校验是否为url格式字符串
9-
export const urlRegex = /^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-.,@?^=%&amp;:/~+#]*[\w\-@?^=%&amp;/~+#])?/
9+
export const urlRegex = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-.,@?^=%&amp;:/~+#]*[\w\-@?^=%&amp;/~+#])?/g
1010

1111
// 编辑器为了方便继续输入/换行等原因 主动生成的空标签
1212
export const EMPTY_P = '<p data-we-empty-p=""><br></p>'

test/unit/text/paste-text-html.test.ts

+34-10
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('text utils getPasteImgs test', () => {
6363
expect(document.execCommand).toBeCalledWith('insertHTML', false, 'mock123\n')
6464
})
6565

66-
test('如果复制的文本内容是 url,则插入链接', () => {
66+
test('如果复制的文本内容是包含单个 url,则插入链接', () => {
6767
mockCommand(document)
6868

6969
jest.spyOn(document, 'queryCommandSupported').mockImplementation(() => true)
@@ -77,20 +77,44 @@ describe('text utils getPasteImgs test', () => {
7777
const pasteText = 'http://www.wangeditor.com'
7878

7979
jest.spyOn(pasteEvents, 'getPasteText').mockImplementation(() => pasteText)
80-
jest.spyOn(pasteEvents, 'getPasteHtml').mockImplementation(() => '<p>1234</p>')
81-
jest.spyOn(editor.selection, 'getSelectionContainerElem').mockImplementation(() =>
82-
$('<p></p>')
83-
)
8480

8581
pasteEventList.forEach(fn => {
8682
fn(new Event(''))
8783
})
8884

89-
expect(document.execCommand).toBeCalledWith(
90-
'insertHTML',
91-
false,
92-
`<a href="${pasteText}" target="_blank">${pasteText}</a>`
93-
)
85+
expect(
86+
editor.$textElem
87+
.html()
88+
.indexOf(`<a href="${pasteText}" target="_blank">${pasteText}</a>`)
89+
).toBeGreaterThan(0)
90+
})
91+
92+
test('如果复制的文本内容是包含多个 url,则插入多个链接', () => {
93+
mockCommand(document)
94+
95+
jest.spyOn(document, 'queryCommandSupported').mockImplementation(() => true)
96+
97+
const editor = createEditor(document, selector())
98+
99+
const pasteEventList: Function[] = []
100+
101+
pasteTextHtml(editor, pasteEventList)
102+
103+
const pasteText = 'http://www.wangeditor.com文案文案http://www.wangeditor.com文案'
104+
105+
jest.spyOn(pasteEvents, 'getPasteText').mockImplementation(() => pasteText)
106+
107+
pasteEventList.forEach(fn => {
108+
fn(new Event(''))
109+
})
110+
111+
expect(
112+
editor.$textElem
113+
.html()
114+
.indexOf(
115+
'<a href="http://www.wangeditor.com" target="_blank">http://www.wangeditor.com</a>文案文案<a href="http://www.wangeditor.com" target="_blank">http://www.wangeditor.com</a>文案'
116+
)
117+
).toBeGreaterThan(0)
94118
})
95119

96120
test('如果复制的内容没有 html 内容,直接返回', () => {

0 commit comments

Comments
 (0)