/**
* @description link 菜单 panel tab 配置
* @author wangfupeng
*/
import Editor from '../../editor/index'
import { PanelConf } from '../menu-constructors/Panel'
import { getRandom } from '../../utils/util'
import $, { DomElement } from '../../utils/dom-core'
import isActive, { getParentNodeA, EXTRA_TAG } from './is-active'
import { insertHtml } from './util'
export default function (editor: Editor, text: string, link: string): PanelConf {
// panel 中需要用到的id
const inputLinkId = getRandom('input-link')
const inputTextId = getRandom('input-text')
const btnOkId = getRandom('btn-ok')
const btnDelId = getRandom('btn-del')
// 是否显示“取消链接”
const delBtnDisplay = isActive(editor) ? 'inline-block' : 'none'
let $selectedLink: DomElement
/**
* 选中整个链接元素
*/
function selectLinkElem(): void {
if (!isActive(editor)) return
const $linkElem = editor.selection.getSelectionContainerElem()
if (!$linkElem) return
editor.selection.createRangeByElem($linkElem)
editor.selection.restoreSelection()
$selectedLink = $linkElem // 赋值给函数内全局变量
}
/**
* 插入链接
* @param text 文字
* @param link 链接
*/
function insertLink(text: string, link: string): void {
// fix: 修复列表下无法设置超链接的问题(替换选中文字中的标签)
// const TagRegExp = new RegExp(/(<\/?ul>)|(<\/?li>)|(<\/?ol>)/g)
// const resultText = text.replace(TagRegExp, '')
/**
* fix: 插入链接后再修改链接地址问题,会导致页面链接有问题
*
* 同上,列表无法插入链接的原因,是因为在insertLink, 处理text时有问题。
*/
const resultText = text.replace(//g, '>') // Link xss
const $elem: DomElement = $(`${resultText}`)
const linkDom = $elem.elems[0] as HTMLAnchorElement
// fix: 字符转义问题,https://xxx.org?bar=1¯o=2 => https://xxx.org?bar=1¯o=2
linkDom.innerText = text
// 避免拼接字符串,带来的字符串嵌套问题:如: "> 造成xss攻击
linkDom.href = link
if (isActive(editor)) {
// 选区处于链接中,则选中整个菜单,再执行 insertHTML
selectLinkElem()
editor.cmd.do('insertElem', $elem)
} else {
// 选区未处于链接中,直接插入即可
editor.cmd.do('insertElem', $elem)
}
}
/**
* 取消链接
*/
function delLink(): void {
if (!isActive(editor)) {
return
}
// 选中整个链接
selectLinkElem()
/**
* 替换链接
*
* 两种情况
* 1. 特殊标签里嵌套a,也要保留特殊标签: 先加粗后添加链接
* 2. a标签里面可能会含有其他元素如:b, i等,要保留: 先添加链接后加粗
*/
if ($selectedLink.getNodeName() === 'A') {
const linkElem = $selectedLink.elems[0]
const linkParentNode = linkElem.parentElement
// 判断父级元素是不是特殊元素
if (linkParentNode && EXTRA_TAG.includes(linkParentNode.nodeName)) {
// 将特殊元素的内容设置为a标签的内容
linkParentNode.innerHTML = linkElem.innerHTML
} else {
// 如果父级不是特殊元素,直接设置内容
editor.cmd.do('insertHTML', '' + linkElem.innerHTML + '')
}
} else {
// 如果链接上选区是特殊元素,需要获取最近的a标签,获取html结果,以保留特殊元素
const parentNodeA = getParentNodeA($selectedLink)!
const selectionContent = parentNodeA.innerHTML
editor.cmd.do('insertHTML', '' + selectionContent + '')
}
}
/**
* 校验链接是否合法
* @param link 链接
*/
function checkLink(text: string, link: string): boolean {
//查看开发者自定义配置的返回值
const check = editor.config.linkCheck(text, link)
if (check === undefined) {
//用户未能通过开发者的校验,且开发者不希望编辑器提示用户
} else if (check === true) {
//用户通过了开发者的校验
return true
} else {
//用户未能通过开发者的校验,开发者希望我们提示这一字符串
editor.config.customAlert(check, 'warning')
}
return false
}
const conf = {
width: 300,
height: 0,
// 拼接字符串的:xss 攻击:
// 如值为:">
, 插入后:value="">
", 插入一个img元素
// panel 中可包含多个 tab
tabs: [
{
// tab 的标题
title: editor.i18next.t('menus.panelMenus.link.链接'),
// 模板
tpl: `