Merge pull request #8 from wangeditor-team/feature-text-API

getJSON 和处理 tab
This commit is contained in:
王福朋 2020-06-19 10:36:08 +08:00 committed by GitHub
commit 6923608c8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 223 additions and 27 deletions

2
.gitignore vendored
View File

@ -106,3 +106,5 @@ dist
examples/test-page/
.DS_Store
ISSUE1.md

30
examples/getJSON.html Normal file
View File

@ -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>

View File

@ -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

View File

@ -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', '&nbsp;&nbsp;&nbsp;&nbsp;')
}
}
// 保留函数
tabDownEvents.push(fn)
}
export default tabHandler

View File

@ -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

View File

@ -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 === 9Down 时
enterUpEvents: Function[] // enter 键keyCode === 13up 时
deleteUpEvents: Function[] // 删除键keyCode === 8up 时
deleteDownEvents: Function[] // 删除键keyCode === 8down 时
@ -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 {
// 阻止默认行为,使用 execCommand 的粘贴命令
e.preventDefault()
}
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))
})
}
}

View File

@ -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()