Skip to content

Commit

Permalink
Merge pull request #3867 from wangeditor-team/fix-link-text
Browse files Browse the repository at this point in the history
fix: url或者常规文本中HTML转义字符显示异常
  • Loading branch information
wangfupeng1988 authored Feb 10, 2022
2 parents 93c71c4 + 03310ee commit be6c314
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 20 deletions.
10 changes: 8 additions & 2 deletions src/menus/link/create-panel-conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,19 @@ export default function (editor: Editor, text: string, link: string): PanelConf
*
* 同上,列表无法插入链接的原因,是因为在insertLink, 处理text时有问题。
*/

const $elem: DomElement = $(`<a href="${link}" target="_blank">${text}</a>`)

// fix: 字符转义问题,https://xxx.org?bar=1&macro=2 => https://xxx.org?bar=1¯o=2
$elem.elems[0].innerText = text

if (isActive(editor)) {
// 选区处于链接中,则选中整个菜单,再执行 insertHTML
selectLinkElem()
editor.cmd.do('insertHTML', `<a href="${link}" target="_blank">${text}</a>`)
editor.cmd.do('insertElem', $elem)
} else {
// 选区未处于链接中,直接插入即可
editor.cmd.do('insertHTML', `<a href="${link}" target="_blank">${text}</a>`)
editor.cmd.do('insertElem', $elem)
}
}

Expand Down
42 changes: 35 additions & 7 deletions src/text/event-hooks/paste-text-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ function pasteTextHtml(editor: Editor, pasteEvents: Function[]) {
return
}

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

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

return editor.cmd.do(
'insertHTML',
`<a href="${insertUrl}" target="_blank">${insertUrl}</a>${otherText}`
) // html
// 文本转义问题,如果直接使用innerHTML插入html结构,地址中的特殊字符会被转义
// 先生成元素,替换里面的文本,利用insertElem插入到页面
const div = document.createElement('div')
const fragment = document.createDocumentFragment()

div.innerHTML = resultText

if (range == null) return

// 将div里的dom结构,搬到fragment里
while (div.childNodes.length) {
fragment.append(div.childNodes[0])
}

// 修改a 链接文案,使用innerText插入文本,这样就避免了使用innerHTML时把特殊符号转义
const linkEle = fragment.querySelectorAll('a')
linkEle.forEach(ele => {
ele.innerText = ele.href
})

if (range.insertNode) {
range.deleteContents()
range.insertNode(fragment)
}

editor.selection.clearWindowSelectionRange()

return
}
// table 中(td、th),待开发。。。
if (!pasteHtml) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
export function EMPTY_FN() {}

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

// 编辑器为了方便继续输入/换行等原因 主动生成的空标签
export const EMPTY_P = '<p data-we-empty-p=""><br></p>'
Expand Down
44 changes: 34 additions & 10 deletions test/unit/text/paste-text-html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('text utils getPasteImgs test', () => {
expect(document.execCommand).toBeCalledWith('insertHTML', false, 'mock123\n')
})

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

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

jest.spyOn(pasteEvents, 'getPasteText').mockImplementation(() => pasteText)
jest.spyOn(pasteEvents, 'getPasteHtml').mockImplementation(() => '<p>1234</p>')
jest.spyOn(editor.selection, 'getSelectionContainerElem').mockImplementation(() =>
$('<p></p>')
)

pasteEventList.forEach(fn => {
fn(new Event(''))
})

expect(document.execCommand).toBeCalledWith(
'insertHTML',
false,
`<a href="${pasteText}" target="_blank">${pasteText}</a>`
)
expect(
editor.$textElem
.html()
.indexOf(`<a href="${pasteText}" target="_blank">${pasteText}</a>`)
).toBeGreaterThan(0)
})

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

jest.spyOn(document, 'queryCommandSupported').mockImplementation(() => true)

const editor = createEditor(document, selector())

const pasteEventList: Function[] = []

pasteTextHtml(editor, pasteEventList)

const pasteText = 'http://www.wangeditor.com文案文案http://www.wangeditor.com文案'

jest.spyOn(pasteEvents, 'getPasteText').mockImplementation(() => pasteText)

pasteEventList.forEach(fn => {
fn(new Event(''))
})

expect(
editor.$textElem
.html()
.indexOf(
'<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>文案'
)
).toBeGreaterThan(0)
})

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

0 comments on commit be6c314

Please sign in to comment.