Merge pull request #8 from wangeditor-team/feature-text-API
getJSON 和处理 tab
This commit is contained in:
commit
6923608c8c
|
@ -106,3 +106,5 @@ dist
|
|||
examples/test-page/
|
||||
|
||||
.DS_Store
|
||||
|
||||
ISSUE1.md
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>wangEditor example</title>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
wangEditor getJSON
|
||||
</p>
|
||||
<div id="div1">
|
||||
<p>我是一行文字</p>
|
||||
<p>我是一行<b>这里加粗</b>文字</p>
|
||||
<p>我是一行<a href="123" target="_blank">链接</a>文字</p>
|
||||
<p><img src="https://www.tslang.cn/assets/images/fork_me_ribbon.svg" style="width: 200px;"/></p>
|
||||
</div>
|
||||
|
||||
<script src="../dist/wangEditor.js"></script>
|
||||
<script>
|
||||
const E = window.wangEditor
|
||||
const editor = new E('#div1')
|
||||
editor.create()
|
||||
|
||||
console.log( editor.txt.getJSON() )
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -6,6 +6,7 @@
|
|||
import Text from '../index'
|
||||
import enterToCreateP from './enter-to-create-p'
|
||||
import deleteToKeepP from './del-to-keep-p'
|
||||
import tabToSpan from './tab-to-space'
|
||||
|
||||
/**
|
||||
* 初始化 text 事件钩子函数
|
||||
|
@ -20,6 +21,9 @@ function initTextHooks(text: Text): void {
|
|||
|
||||
// 删除时,保留 <p><br></p>
|
||||
deleteToKeepP(editor, eventHooks.deleteUpEvents, eventHooks.deleteDownEvents)
|
||||
|
||||
// tab 转换为空格
|
||||
tabToSpan(editor, eventHooks.tabDownEvents)
|
||||
}
|
||||
|
||||
export default initTextHooks
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* @description 编辑区域 tab 的特殊处理
|
||||
* @author wangfupeng
|
||||
*/
|
||||
|
||||
import Editor from '../../editor/index'
|
||||
|
||||
/**
|
||||
* 编辑区域 tab 的特殊处理,转换为空格
|
||||
* @param editor 编辑器实例
|
||||
* @param tabDownEvents tab down 事件钩子
|
||||
*/
|
||||
function tabHandler(editor: Editor, tabDownEvents: Function[]) {
|
||||
// 定义函数
|
||||
function fn() {
|
||||
if (!editor.cmd.queryCommandSupported('insertHTML')) {
|
||||
// 必须原生支持 insertHTML 命令
|
||||
return
|
||||
}
|
||||
const $selectionElem = editor.selection.getSelectionContainerElem()
|
||||
if (!$selectionElem) {
|
||||
return
|
||||
}
|
||||
const $parentElem = $selectionElem.parent()
|
||||
const selectionNodeName = $selectionElem.getNodeName()
|
||||
const parentNodeName = $parentElem.getNodeName()
|
||||
|
||||
if (selectionNodeName === 'CODE' && parentNodeName === 'PRE') {
|
||||
// <pre><code> 里面
|
||||
editor.cmd.do('insertHTML', ' ')
|
||||
} else {
|
||||
// 普通文字
|
||||
editor.cmd.do('insertHTML', ' ')
|
||||
}
|
||||
}
|
||||
// 保留函数
|
||||
tabDownEvents.push(fn)
|
||||
}
|
||||
|
||||
export default tabHandler
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* @description 获取子元素的 JSON 格式数据
|
||||
* @author wangfupeng
|
||||
*/
|
||||
|
||||
import { replaceHtmlSymbol } from '../utils/util'
|
||||
import $, { DomElement } from '../utils/dom-core'
|
||||
|
||||
type AttrType = {
|
||||
name: string
|
||||
value: string
|
||||
}
|
||||
export type NodeType = {
|
||||
tag: string
|
||||
attrs: AttrType[]
|
||||
children: NodeListType
|
||||
}
|
||||
export type NodeListType = Array<string | NodeType>
|
||||
|
||||
/**
|
||||
* 获取子元素的 JSON 格式数据
|
||||
* @param $elem DOM 节点
|
||||
*/
|
||||
function getChildrenJSON($elem: DomElement): NodeListType {
|
||||
const result: NodeListType = [] // 存储结果
|
||||
|
||||
const $children = $elem.childNodes() || [] // 注意 childNodes() 可以获取文本节点
|
||||
$children.forEach((curElem: HTMLElement) => {
|
||||
let elemResult
|
||||
const nodeType = curElem.nodeType
|
||||
|
||||
// 文本节点
|
||||
if (nodeType === 3) {
|
||||
elemResult = curElem.textContent || ''
|
||||
elemResult = replaceHtmlSymbol(elemResult)
|
||||
}
|
||||
|
||||
// 普通 DOM 节点
|
||||
if (nodeType === 1) {
|
||||
elemResult = {}
|
||||
elemResult = elemResult as NodeType
|
||||
|
||||
// tag
|
||||
elemResult.tag = curElem.nodeName.toLowerCase()
|
||||
// attr
|
||||
const attrData = []
|
||||
const attrList = curElem.attributes || []
|
||||
const attrListLength = attrList.length || 0
|
||||
for (let i = 0; i < attrListLength; i++) {
|
||||
const attr = attrList[i]
|
||||
attrData.push({
|
||||
name: attr.name,
|
||||
value: attr.value,
|
||||
})
|
||||
}
|
||||
elemResult.attrs = attrData
|
||||
// children(递归)
|
||||
elemResult.children = getChildrenJSON($(curElem))
|
||||
}
|
||||
|
||||
if (elemResult) {
|
||||
result.push(elemResult)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export default getChildrenJSON
|
|
@ -7,11 +7,14 @@ import $ from '../utils/dom-core'
|
|||
import Editor from '../editor/index'
|
||||
import initEventHooks from './event-hooks/index'
|
||||
import { UA } from '../utils/util'
|
||||
import getChildrenJSON, { NodeListType } from './getChildrenJSON'
|
||||
|
||||
// 各个事件钩子函数
|
||||
type TextEventHooks = {
|
||||
dropEvents: Function[]
|
||||
keyupEvents: Function[]
|
||||
tabUpEvents: Function[] // tab 键(keyCode === )Up 时
|
||||
tabDownEvents: Function[] // tab 键(keyCode === 9)Down 时
|
||||
enterUpEvents: Function[] // enter 键(keyCode === 13)up 时
|
||||
deleteUpEvents: Function[] // 删除键(keyCode === 8)up 时
|
||||
deleteDownEvents: Function[] // 删除键(keyCode === 8)down 时
|
||||
|
@ -28,6 +31,8 @@ class Text {
|
|||
this.eventHooks = {
|
||||
dropEvents: [],
|
||||
keyupEvents: [],
|
||||
tabUpEvents: [],
|
||||
tabDownEvents: [],
|
||||
enterUpEvents: [],
|
||||
deleteUpEvents: [],
|
||||
deleteDownEvents: [],
|
||||
|
@ -42,11 +47,11 @@ class Text {
|
|||
// 实时保存选取范围
|
||||
this._saveRange()
|
||||
|
||||
// 绑定事件
|
||||
this._bindEventHooks()
|
||||
|
||||
// 初始化 text 事件钩子函数
|
||||
initEventHooks(this)
|
||||
|
||||
// 绑定事件
|
||||
this._bindEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,7 +83,14 @@ class Text {
|
|||
editor.initSelection()
|
||||
}
|
||||
|
||||
// 获取 JSON
|
||||
/**
|
||||
* 获取 json 格式的数据
|
||||
*/
|
||||
public getJSON(): NodeListType {
|
||||
const editor = this.editor
|
||||
const $textElem = editor.$textElem
|
||||
return getChildrenJSON($textElem)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取/设置 字符串内容
|
||||
|
@ -146,51 +158,57 @@ class Text {
|
|||
/**
|
||||
* 绑定事件,事件会触发钩子函数
|
||||
*/
|
||||
private _bindEvent(): void {
|
||||
private _bindEventHooks(): void {
|
||||
const $textElem = this.editor.$textElem
|
||||
const eventHooks = this.eventHooks
|
||||
|
||||
// enter 键 up 时的 hooks
|
||||
const enterUpEvents = eventHooks.enterUpEvents
|
||||
$textElem.on('keyup', (e: KeyboardEvent) => {
|
||||
if (e.keyCode !== 13) {
|
||||
// 不是回车键
|
||||
return
|
||||
}
|
||||
if (e.keyCode !== 13) return
|
||||
const enterUpEvents = eventHooks.enterUpEvents
|
||||
enterUpEvents.forEach(fn => fn(e))
|
||||
})
|
||||
|
||||
// delete 键 up 时 hooks
|
||||
const deleteUpEvents = eventHooks.deleteUpEvents
|
||||
$textElem.on('keyup', (e: KeyboardEvent) => {
|
||||
if (e.keyCode !== 8) {
|
||||
// 不是删除键
|
||||
return
|
||||
}
|
||||
if (e.keyCode !== 8) return
|
||||
const deleteUpEvents = eventHooks.deleteUpEvents
|
||||
deleteUpEvents.forEach(fn => fn(e))
|
||||
})
|
||||
|
||||
// delete 键 down 时 hooks
|
||||
const deleteDownEvents = eventHooks.deleteDownEvents
|
||||
$textElem.on('keydown', (e: KeyboardEvent) => {
|
||||
if (e.keyCode !== 8) {
|
||||
// 不是删除键
|
||||
return
|
||||
}
|
||||
if (e.keyCode !== 8) return
|
||||
const deleteDownEvents = eventHooks.deleteDownEvents
|
||||
deleteDownEvents.forEach(fn => fn(e))
|
||||
})
|
||||
|
||||
// 粘贴
|
||||
const pasteEvents = eventHooks.pasteEvents
|
||||
$textElem.on('paste', (e: Event) => {
|
||||
if (UA.isIE()) {
|
||||
return
|
||||
} else {
|
||||
if (UA.isIE()) return // IE 不支持
|
||||
|
||||
// 阻止默认行为,使用 execCommand 的粘贴命令
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const pasteEvents = eventHooks.pasteEvents
|
||||
pasteEvents.forEach(fn => fn(e))
|
||||
})
|
||||
|
||||
// tab up
|
||||
$textElem.on('keyup', (e: KeyboardEvent) => {
|
||||
if (e.keyCode !== 9) return
|
||||
e.preventDefault()
|
||||
const tabUpEvents = eventHooks.tabUpEvents
|
||||
tabUpEvents.forEach(fn => fn(e))
|
||||
})
|
||||
|
||||
// tab down
|
||||
$textElem.on('keydown', (e: KeyboardEvent) => {
|
||||
if (e.keyCode !== 9) return
|
||||
e.preventDefault()
|
||||
const tabDownEvents = eventHooks.tabDownEvents
|
||||
tabDownEvents.forEach(fn => fn(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import createEditor from '../fns/create-editor'
|
||||
import Editor from '../../src/editor'
|
||||
import { NodeType } from '../../src/text/getChildrenJSON'
|
||||
|
||||
let editor: Editor
|
||||
let TEXT = '这是用于 editor.text API 单元测试的文字'
|
||||
|
@ -32,6 +33,39 @@ test('追加内容', () => {
|
|||
expect(html.indexOf(r)).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('获取 JSON', () => {
|
||||
const html = `<p>我是一行文字</p><p>我是一行<b>这里加粗</b>文字</p><p>我是一行<a href="123" target="_blank">链接</a>文字</p>`
|
||||
|
||||
editor.txt.html(html)
|
||||
|
||||
const result = editor.txt.getJSON()
|
||||
|
||||
expect(Array.isArray(result)).toBe(true) // 是一个数组
|
||||
|
||||
// 第一个 p 标签
|
||||
const p0 = result[0] as NodeType
|
||||
expect(p0.tag).toBe('p')
|
||||
expect(p0.children[0]).toBe('我是一行文字')
|
||||
|
||||
// 第二个 p 标签
|
||||
const p1 = result[1] as NodeType
|
||||
expect(p1.children.length).toBe(3)
|
||||
const b = p1.children[1] as NodeType // b 标签
|
||||
expect(b.tag).toBe('b')
|
||||
expect(b.children[0]).toBe('这里加粗')
|
||||
|
||||
// 第三个 p 标签
|
||||
const p2 = result[2] as NodeType
|
||||
const a = p2.children[1] as NodeType // a 标签
|
||||
expect(a.attrs.length).toBe(2)
|
||||
const attr1 = a.attrs[0] // 属性
|
||||
expect(attr1.name).toBe('href')
|
||||
expect(attr1.value).toBe('123')
|
||||
const attr2 = a.attrs[1]
|
||||
expect(attr2.name).toBe('target')
|
||||
expect(attr2.value).toBe('_blank')
|
||||
})
|
||||
|
||||
// 最后测
|
||||
test('清空内容', () => {
|
||||
editor.txt.clear()
|
||||
|
|
Loading…
Reference in New Issue