feat: add getHTML api #218

pr675
Hufe921 3 years ago
parent 0bacc113cd
commit b12c6cc428

@ -82,3 +82,15 @@ const {
```javascript
const catalog = await instance.command.getCatalog()
```
## getHTML
功能获取HTML
用法:
```javascript
const {
header: string
main: string
footer: string
} = await instance.command.getHTML()
```

@ -77,6 +77,7 @@ export class Command {
public getCatalog: CommandAdapt['getCatalog']
public getImage: CommandAdapt['getImage']
public getValue: CommandAdapt['getValue']
public getHTML: CommandAdapt['getHTML']
public getWordCount: CommandAdapt['getWordCount']
public getRangeText: CommandAdapt['getRangeText']
public getRangeContext: CommandAdapt['getRangeContext']
@ -167,6 +168,7 @@ export class Command {
// 获取
this.getImage = adapt.getImage.bind(adapt)
this.getValue = adapt.getValue.bind(adapt)
this.getHTML = adapt.getHTML.bind(adapt)
this.getWordCount = adapt.getWordCount.bind(adapt)
this.getRangeText = adapt.getRangeText.bind(adapt)
this.getRangeContext = adapt.getRangeContext.bind(adapt)

@ -17,6 +17,7 @@ import { TableBorder } from '../../dataset/enum/table/Table'
import { TitleLevel } from '../../dataset/enum/Title'
import { VerticalAlign } from '../../dataset/enum/VerticalAlign'
import { ICatalog } from '../../interface/Catalog'
import { DeepRequired } from '../../interface/Common'
import {
IAppendElementListOption,
IDrawImagePayload,
@ -25,6 +26,7 @@ import {
} from '../../interface/Draw'
import {
IEditorData,
IEditorHTML,
IEditorOption,
IEditorResult
} from '../../interface/Editor'
@ -37,6 +39,7 @@ import { ITr } from '../../interface/table/Tr'
import { IWatermark } from '../../interface/Watermark'
import { deepClone, downloadFile, getUUID } from '../../utils'
import {
createDomFromElementList,
formatElementContext,
formatElementList,
isTextLikeElement,
@ -61,7 +64,7 @@ export class CommandAdapt {
private historyManager: HistoryManager
private canvasEvent: CanvasEvent
private tableTool: TableTool
private options: Required<IEditorOption>
private options: DeepRequired<IEditorOption>
private control: Control
private workerManager: WorkerManager
private searchManager: Search
@ -1680,6 +1683,18 @@ export class CommandAdapt {
return this.draw.getValue(options)
}
public getHTML(): IEditorHTML {
const options = this.options
const headerElementList = this.draw.getHeaderElementList()
const mainElementList = this.draw.getOriginalMainElementList()
const footerElementList = this.draw.getFooterElementList()
return {
header: createDomFromElementList(headerElementList, options).innerHTML,
main: createDomFromElementList(mainElementList, options).innerHTML,
footer: createDomFromElementList(footerElementList, options).innerHTML
}
}
public getWordCount(): Promise<number> {
return this.workerManager.getWordCount()
}

@ -4,8 +4,7 @@ import { VIRTUAL_ELEMENT_TYPE } from '../../dataset/constant/Element'
import { ElementType } from '../../dataset/enum/Element'
import { IElement } from '../../interface/Element'
import { debounce } from '../../utils'
import { getElementListByHTML } from '../../utils/clipboard'
import { formatElementContext } from '../../utils/element'
import { formatElementContext, getElementListByHTML } from '../../utils/element'
import { Draw } from '../draw/Draw'
import { CanvasEvent } from '../event/CanvasEvent'

@ -79,3 +79,9 @@ export interface IEditorResult {
watermark?: IWatermark
data: IEditorData
}
export interface IEditorHTML {
header: string
main: string
footer: string
}

@ -1,23 +1,6 @@
import { IEditorOption, IElement, ListStyle, ListType, RowFlex } from '..'
import { ZERO } from '../dataset/constant/Common'
import {
INLINE_NODE_NAME,
TEXTLIKE_ELEMENT_TYPE
} from '../dataset/constant/Element'
import {
listStyleCSSMapping,
listTypeElementMapping
} from '../dataset/constant/List'
import {
titleNodeNameMapping,
titleOrderNumberMapping
} from '../dataset/constant/Title'
import { ControlComponent } from '../dataset/enum/Control'
import { ElementType } from '../dataset/enum/Element'
import { IEditorOption, IElement } from '..'
import { DeepRequired } from '../interface/Common'
import { ITd } from '../interface/table/Td'
import { ITr } from '../interface/table/Tr'
import { getElementRowFlex, zipElementList } from './element'
import { createDomFromElementList } from './element'
export function writeClipboardItem(text: string, html: string) {
if (!text || !html) return
@ -46,175 +29,11 @@ export function writeClipboardItem(text: string, html: string) {
}
}
export function convertElementToDom(
element: IElement,
options: DeepRequired<IEditorOption>
): HTMLElement {
let tagName: keyof HTMLElementTagNameMap = 'span'
if (element.type === ElementType.SUPERSCRIPT) {
tagName = 'sup'
} else if (element.type === ElementType.SUBSCRIPT) {
tagName = 'sub'
} else if (
element.rowFlex === RowFlex.CENTER ||
element.rowFlex === RowFlex.RIGHT
) {
tagName = 'p'
}
const dom = document.createElement(tagName)
dom.style.fontFamily = element.font || options.defaultFont
if (element.rowFlex) {
const isAlignment = element.rowFlex === RowFlex.ALIGNMENT
dom.style.textAlign = isAlignment ? 'justify' : element.rowFlex
}
if (element.color) {
dom.style.color = element.color
}
if (element.bold) {
dom.style.fontWeight = '600'
}
if (element.italic) {
dom.style.fontStyle = 'italic'
}
dom.style.fontSize = `${element.size || options.defaultSize}px`
if (element.highlight) {
dom.style.backgroundColor = element.highlight
}
dom.innerText = element.value.replace(new RegExp(`${ZERO}`, 'g'), '\n')
return dom
}
export function writeElementList(
elementList: IElement[],
options: DeepRequired<IEditorOption>
) {
function buildDomFromElementList(payload: IElement[]): HTMLDivElement {
const clipboardDom = document.createElement('div')
for (let e = 0; e < payload.length; e++) {
const element = payload[e]
// 构造表格
if (element.type === ElementType.TABLE) {
const tableDom: HTMLTableElement = document.createElement('table')
const trList = element.trList!
for (let t = 0; t < trList.length; t++) {
const trDom = document.createElement('tr')
const tr = trList[t]
for (let d = 0; d < tr.tdList.length; d++) {
const tdDom = document.createElement('td')
tdDom.style.border = '1px solid'
const td = tr.tdList[d]
tdDom.colSpan = td.colspan
tdDom.rowSpan = td.rowspan
const childDom = buildDomFromElementList(zipElementList(td.value!))
tdDom.innerHTML = childDom.innerHTML
if (td.backgroundColor) {
tdDom.style.backgroundColor = td.backgroundColor
}
trDom.append(tdDom)
}
tableDom.append(trDom)
}
clipboardDom.append(tableDom)
} else if (element.type === ElementType.HYPERLINK) {
const a = document.createElement('a')
a.innerText = element.valueList!.map(v => v.value).join('')
if (element.url) {
a.href = element.url
}
clipboardDom.append(a)
} else if (element.type === ElementType.TITLE) {
const h = document.createElement(
`h${titleOrderNumberMapping[element.level!]}`
)
const childDom = buildDomFromElementList(
zipElementList(element.valueList!)
)
h.innerHTML = childDom.innerHTML
clipboardDom.append(h)
} else if (element.type === ElementType.LIST) {
const list = document.createElement(
listTypeElementMapping[element.listType!]
)
if (element.listStyle) {
list.style.listStyleType = listStyleCSSMapping[element.listStyle]
}
// 按照换行符拆分
let curListIndex = 0
const listElementListMap: Map<number, IElement[]> = new Map()
const zipList = zipElementList(element.valueList!)
for (let z = 0; z < zipList.length; z++) {
const zipElement = zipList[z]
if (zipElement.listWrap) {
const listElementList = listElementListMap.get(curListIndex) || []
listElementList.push(zipElement)
listElementListMap.set(curListIndex, listElementList)
} else {
const zipValueList = zipElement.value.split('\n')
for (let c = 0; c < zipValueList.length; c++) {
if (c > 0) {
curListIndex += 1
}
const value = zipValueList[c]
const listElementList = listElementListMap.get(curListIndex) || []
listElementList.push({
...zipElement,
value
})
listElementListMap.set(curListIndex, listElementList)
}
}
}
listElementListMap.forEach(listElementList => {
const li = document.createElement('li')
const childDom = buildDomFromElementList(listElementList)
li.innerHTML = childDom.innerHTML
list.append(li)
})
clipboardDom.append(list)
} else if (element.type === ElementType.IMAGE) {
const img = document.createElement('img')
if (element.value) {
img.src = element.value
img.width = element.width!
img.height = element.height!
}
clipboardDom.append(img)
} else if (element.type === ElementType.SEPARATOR) {
const hr = document.createElement('hr')
clipboardDom.append(hr)
} else if (element.type === ElementType.CHECKBOX) {
const checkbox = document.createElement('input')
checkbox.type = 'checkbox'
if (element.checkbox?.value) {
checkbox.setAttribute('checked', 'true')
}
clipboardDom.append(checkbox)
} else if (
!element.type ||
element.type === ElementType.LATEX ||
TEXTLIKE_ELEMENT_TYPE.includes(element.type)
) {
let text = ''
if (element.type === ElementType.CONTROL) {
text = element.control!.value?.[0]?.value || ''
} else if (element.type === ElementType.DATE) {
text = element.valueList?.map(v => v.value).join('') || ''
} else {
text = element.value
}
if (!text) continue
// 前一个元素是标题,移除首行换行符
if (payload[e - 1]?.type === ElementType.TITLE) {
text = text.replace(/^\n/, '')
}
const dom = convertElementToDom(element, options)
dom.innerText = text.replace(new RegExp(`${ZERO}`, 'g'), '\n')
clipboardDom.append(dom)
}
}
return clipboardDom
}
const clipboardDom = buildDomFromElementList(zipElementList(elementList))
const clipboardDom = createDomFromElementList(elementList, options)
// 写入剪贴板
document.body.append(clipboardDom)
const text = clipboardDom.innerText
@ -224,233 +43,3 @@ export function writeElementList(
if (!text || !html) return
writeClipboardItem(text, html)
}
export function convertTextNodeToElement(
textNode: Element | Node
): IElement | null {
if (!textNode || textNode.nodeType !== 3) return null
const parentNode = <HTMLElement>textNode.parentNode
const anchorNode =
parentNode.nodeName === 'FONT'
? <HTMLElement>parentNode.parentNode
: parentNode
const rowFlex = getElementRowFlex(anchorNode)
const value = textNode.textContent
const style = window.getComputedStyle(anchorNode)
if (!value || anchorNode.nodeName === 'STYLE') return null
const element: IElement = {
value,
color: style.color,
bold: Number(style.fontWeight) > 500,
italic: style.fontStyle.includes('italic'),
size: Math.floor(parseFloat(style.fontSize))
}
// 元素类型-默认文本
if (anchorNode.nodeName === 'SUB' || style.verticalAlign === 'sub') {
element.type = ElementType.SUBSCRIPT
} else if (anchorNode.nodeName === 'SUP' || style.verticalAlign === 'super') {
element.type = ElementType.SUPERSCRIPT
}
// 行对齐
if (rowFlex !== RowFlex.LEFT) {
element.rowFlex = rowFlex
}
// 高亮色
if (style.backgroundColor !== 'rgba(0, 0, 0, 0)') {
element.highlight = style.backgroundColor
}
return element
}
interface IGetElementListByHTMLOption {
innerWidth: number
}
export function getElementListByHTML(
htmlText: string,
options: IGetElementListByHTMLOption
): IElement[] {
const elementList: IElement[] = []
function findTextNode(dom: Element | Node) {
if (dom.nodeType === 3) {
const element = convertTextNodeToElement(dom)
if (element) {
elementList.push(element)
}
} else if (dom.nodeType === 1) {
const childNodes = dom.childNodes
for (let n = 0; n < childNodes.length; n++) {
const node = childNodes[n]
// br元素与display:block元素需换行
if (node.nodeName === 'BR') {
elementList.push({
value: '\n'
})
} else if (node.nodeName === 'A') {
const aElement = node as HTMLLinkElement
const value = aElement.innerText
if (value) {
elementList.push({
type: ElementType.HYPERLINK,
value: '',
valueList: [
{
value
}
],
url: aElement.href
})
}
} else if (/H[1-6]/.test(node.nodeName)) {
const hElement = node as HTMLTitleElement
const valueList = getElementListByHTML(hElement.innerHTML, options)
elementList.push({
value: '',
type: ElementType.TITLE,
level: titleNodeNameMapping[node.nodeName],
valueList
})
if (
node.nextSibling &&
!INLINE_NODE_NAME.includes(node.nextSibling.nodeName)
) {
elementList.push({
value: '\n'
})
}
} else if (node.nodeName === 'UL' || node.nodeName === 'OL') {
const listNode = node as HTMLOListElement | HTMLUListElement
const listElement: IElement = {
value: '',
type: ElementType.LIST,
valueList: []
}
if (node.nodeName === 'OL') {
listElement.listType = ListType.OL
} else {
listElement.listType = ListType.UL
listElement.listStyle = <ListStyle>(
(<unknown>listNode.style.listStyleType)
)
}
listNode.querySelectorAll('li').forEach(li => {
const liValueList = getElementListByHTML(li.innerHTML, options)
liValueList.forEach(list => {
if (list.value === '\n') {
list.listWrap = true
}
})
liValueList.unshift({
value: '\n'
})
listElement.valueList!.push(...liValueList)
})
elementList.push(listElement)
} else if (node.nodeName === 'HR') {
elementList.push({
value: '\n',
type: ElementType.SEPARATOR
})
} else if (node.nodeName === 'IMG') {
const { src, width, height } = node as HTMLImageElement
if (src && width && height) {
elementList.push({
width,
height,
value: src,
type: ElementType.IMAGE
})
}
} else if (node.nodeName === 'TABLE') {
const tableElement = node as HTMLTableElement
const element: IElement = {
type: ElementType.TABLE,
value: '\n',
colgroup: [],
trList: []
}
// 基础数据
tableElement.querySelectorAll('tr').forEach(trElement => {
const trHeightStr = window
.getComputedStyle(trElement)
.height.replace('px', '')
const tr: ITr = {
height: Number(trHeightStr),
tdList: []
}
trElement.querySelectorAll('th,td').forEach(tdElement => {
const tableCell = <HTMLTableCellElement>tdElement
const valueList = getElementListByHTML(
tableCell.innerHTML,
options
)
const td: ITd = {
colspan: tableCell.colSpan,
rowspan: tableCell.rowSpan,
value: valueList
}
if (tableCell.style.backgroundColor) {
td.backgroundColor = tableCell.style.backgroundColor
}
tr.tdList.push(td)
})
if (tr.tdList.length) {
element.trList!.push(tr)
}
})
if (element.trList!.length) {
// 列选项数据
const tdCount = element.trList![0].tdList.reduce(
(pre, cur) => pre + cur.colspan,
0
)
const width = Math.ceil(options.innerWidth / tdCount)
for (let i = 0; i < tdCount; i++) {
element.colgroup!.push({
width
})
}
elementList.push(element)
}
} else if (
node.nodeName === 'INPUT' &&
(<HTMLInputElement>node).type === ControlComponent.CHECKBOX
) {
elementList.push({
type: ElementType.CHECKBOX,
value: '',
checkbox: {
value: (<HTMLInputElement>node).checked
}
})
} else {
findTextNode(node)
if (node.nodeType === 1 && n !== childNodes.length - 1) {
const display = window.getComputedStyle(node as Element).display
if (display === 'block') {
elementList.push({
value: '\n'
})
}
}
}
}
}
}
// 追加dom
const clipboardDom = document.createElement('div')
clipboardDom.innerHTML = htmlText
document.body.appendChild(clipboardDom)
const deleteNodes: ChildNode[] = []
clipboardDom.childNodes.forEach(child => {
if (child.nodeType !== 1 && !child.textContent?.trim()) {
deleteNodes.push(child)
}
})
deleteNodes.forEach(node => node.remove())
// 搜索文本节点
findTextNode(clipboardDom)
// 移除dom
clipboardDom.remove()
return elementList
}

@ -1,5 +1,12 @@
import { cloneProperty, deepClone, getUUID, splitText } from '.'
import { ElementType, IEditorOption, IElement, RowFlex } from '..'
import {
ElementType,
IEditorOption,
IElement,
ListStyle,
ListType,
RowFlex
} from '..'
import { LaTexParticle } from '../core/draw/particle/latex/LaTexParticle'
import { defaultCheckboxOption } from '../dataset/constant/Checkbox'
import { ZERO } from '../dataset/constant/Common'
@ -7,12 +14,23 @@ import { defaultControlOption } from '../dataset/constant/Control'
import {
EDITOR_ELEMENT_CONTEXT_ATTR,
EDITOR_ELEMENT_ZIP_ATTR,
INLINE_NODE_NAME,
TABLE_CONTEXT_ATTR,
TEXTLIKE_ELEMENT_TYPE
} from '../dataset/constant/Element'
import { titleSizeMapping } from '../dataset/constant/Title'
import {
listStyleCSSMapping,
listTypeElementMapping
} from '../dataset/constant/List'
import {
titleNodeNameMapping,
titleOrderNumberMapping,
titleSizeMapping
} from '../dataset/constant/Title'
import { ControlComponent, ControlType } from '../dataset/enum/Control'
import { DeepRequired } from '../interface/Common'
import { ITd } from '../interface/table/Td'
import { ITr } from '../interface/table/Tr'
export function unzipElementList(elementList: IElement[]): IElement[] {
const result: IElement[] = []
@ -644,3 +662,402 @@ export function formatElementContext(
)
}
}
export function convertElementToDom(
element: IElement,
options: DeepRequired<IEditorOption>
): HTMLElement {
let tagName: keyof HTMLElementTagNameMap = 'span'
if (element.type === ElementType.SUPERSCRIPT) {
tagName = 'sup'
} else if (element.type === ElementType.SUBSCRIPT) {
tagName = 'sub'
} else if (
element.rowFlex === RowFlex.CENTER ||
element.rowFlex === RowFlex.RIGHT
) {
tagName = 'p'
}
const dom = document.createElement(tagName)
dom.style.fontFamily = element.font || options.defaultFont
if (element.rowFlex) {
const isAlignment = element.rowFlex === RowFlex.ALIGNMENT
dom.style.textAlign = isAlignment ? 'justify' : element.rowFlex
}
if (element.color) {
dom.style.color = element.color
}
if (element.bold) {
dom.style.fontWeight = '600'
}
if (element.italic) {
dom.style.fontStyle = 'italic'
}
dom.style.fontSize = `${element.size || options.defaultSize}px`
if (element.highlight) {
dom.style.backgroundColor = element.highlight
}
dom.innerText = element.value.replace(new RegExp(`${ZERO}`, 'g'), '\n')
return dom
}
export function createDomFromElementList(
elementList: IElement[],
options: DeepRequired<IEditorOption>
) {
function buildDom(payload: IElement[]): HTMLDivElement {
const clipboardDom = document.createElement('div')
for (let e = 0; e < payload.length; e++) {
const element = payload[e]
// 构造表格
if (element.type === ElementType.TABLE) {
const tableDom: HTMLTableElement = document.createElement('table')
const trList = element.trList!
for (let t = 0; t < trList.length; t++) {
const trDom = document.createElement('tr')
const tr = trList[t]
for (let d = 0; d < tr.tdList.length; d++) {
const tdDom = document.createElement('td')
tdDom.style.border = '1px solid'
const td = tr.tdList[d]
tdDom.colSpan = td.colspan
tdDom.rowSpan = td.rowspan
const childDom = buildDom(zipElementList(td.value!))
tdDom.innerHTML = childDom.innerHTML
if (td.backgroundColor) {
tdDom.style.backgroundColor = td.backgroundColor
}
trDom.append(tdDom)
}
tableDom.append(trDom)
}
clipboardDom.append(tableDom)
} else if (element.type === ElementType.HYPERLINK) {
const a = document.createElement('a')
a.innerText = element.valueList!.map(v => v.value).join('')
if (element.url) {
a.href = element.url
}
clipboardDom.append(a)
} else if (element.type === ElementType.TITLE) {
const h = document.createElement(
`h${titleOrderNumberMapping[element.level!]}`
)
const childDom = buildDom(zipElementList(element.valueList!))
h.innerHTML = childDom.innerHTML
clipboardDom.append(h)
} else if (element.type === ElementType.LIST) {
const list = document.createElement(
listTypeElementMapping[element.listType!]
)
if (element.listStyle) {
list.style.listStyleType = listStyleCSSMapping[element.listStyle]
}
// 按照换行符拆分
let curListIndex = 0
const listElementListMap: Map<number, IElement[]> = new Map()
const zipList = zipElementList(element.valueList!)
for (let z = 0; z < zipList.length; z++) {
const zipElement = zipList[z]
if (zipElement.listWrap) {
const listElementList = listElementListMap.get(curListIndex) || []
listElementList.push(zipElement)
listElementListMap.set(curListIndex, listElementList)
} else {
const zipValueList = zipElement.value.split('\n')
for (let c = 0; c < zipValueList.length; c++) {
if (c > 0) {
curListIndex += 1
}
const value = zipValueList[c]
const listElementList = listElementListMap.get(curListIndex) || []
listElementList.push({
...zipElement,
value
})
listElementListMap.set(curListIndex, listElementList)
}
}
}
listElementListMap.forEach(listElementList => {
const li = document.createElement('li')
const childDom = buildDom(listElementList)
li.innerHTML = childDom.innerHTML
list.append(li)
})
clipboardDom.append(list)
} else if (element.type === ElementType.IMAGE) {
const img = document.createElement('img')
if (element.value) {
img.src = element.value
img.width = element.width!
img.height = element.height!
}
clipboardDom.append(img)
} else if (element.type === ElementType.SEPARATOR) {
const hr = document.createElement('hr')
clipboardDom.append(hr)
} else if (element.type === ElementType.CHECKBOX) {
const checkbox = document.createElement('input')
checkbox.type = 'checkbox'
if (element.checkbox?.value) {
checkbox.setAttribute('checked', 'true')
}
clipboardDom.append(checkbox)
} else if (
!element.type ||
element.type === ElementType.LATEX ||
TEXTLIKE_ELEMENT_TYPE.includes(element.type)
) {
let text = ''
if (element.type === ElementType.CONTROL) {
text = element.control!.value?.[0]?.value || ''
} else if (element.type === ElementType.DATE) {
text = element.valueList?.map(v => v.value).join('') || ''
} else {
text = element.value
}
if (!text) continue
// 前一个元素是标题,移除首行换行符
if (payload[e - 1]?.type === ElementType.TITLE) {
text = text.replace(/^\n/, '')
}
const dom = convertElementToDom(element, options)
dom.innerText = text.replace(new RegExp(`${ZERO}`, 'g'), '\n')
clipboardDom.append(dom)
}
}
return clipboardDom
}
return buildDom(zipElementList(elementList))
}
export function convertTextNodeToElement(
textNode: Element | Node
): IElement | null {
if (!textNode || textNode.nodeType !== 3) return null
const parentNode = <HTMLElement>textNode.parentNode
const anchorNode =
parentNode.nodeName === 'FONT'
? <HTMLElement>parentNode.parentNode
: parentNode
const rowFlex = getElementRowFlex(anchorNode)
const value = textNode.textContent
const style = window.getComputedStyle(anchorNode)
if (!value || anchorNode.nodeName === 'STYLE') return null
const element: IElement = {
value,
color: style.color,
bold: Number(style.fontWeight) > 500,
italic: style.fontStyle.includes('italic'),
size: Math.floor(parseFloat(style.fontSize))
}
// 元素类型-默认文本
if (anchorNode.nodeName === 'SUB' || style.verticalAlign === 'sub') {
element.type = ElementType.SUBSCRIPT
} else if (anchorNode.nodeName === 'SUP' || style.verticalAlign === 'super') {
element.type = ElementType.SUPERSCRIPT
}
// 行对齐
if (rowFlex !== RowFlex.LEFT) {
element.rowFlex = rowFlex
}
// 高亮色
if (style.backgroundColor !== 'rgba(0, 0, 0, 0)') {
element.highlight = style.backgroundColor
}
return element
}
interface IGetElementListByHTMLOption {
innerWidth: number
}
export function getElementListByHTML(
htmlText: string,
options: IGetElementListByHTMLOption
): IElement[] {
const elementList: IElement[] = []
function findTextNode(dom: Element | Node) {
if (dom.nodeType === 3) {
const element = convertTextNodeToElement(dom)
if (element) {
elementList.push(element)
}
} else if (dom.nodeType === 1) {
const childNodes = dom.childNodes
for (let n = 0; n < childNodes.length; n++) {
const node = childNodes[n]
// br元素与display:block元素需换行
if (node.nodeName === 'BR') {
elementList.push({
value: '\n'
})
} else if (node.nodeName === 'A') {
const aElement = node as HTMLLinkElement
const value = aElement.innerText
if (value) {
elementList.push({
type: ElementType.HYPERLINK,
value: '',
valueList: [
{
value
}
],
url: aElement.href
})
}
} else if (/H[1-6]/.test(node.nodeName)) {
const hElement = node as HTMLTitleElement
const valueList = getElementListByHTML(hElement.innerHTML, options)
elementList.push({
value: '',
type: ElementType.TITLE,
level: titleNodeNameMapping[node.nodeName],
valueList
})
if (
node.nextSibling &&
!INLINE_NODE_NAME.includes(node.nextSibling.nodeName)
) {
elementList.push({
value: '\n'
})
}
} else if (node.nodeName === 'UL' || node.nodeName === 'OL') {
const listNode = node as HTMLOListElement | HTMLUListElement
const listElement: IElement = {
value: '',
type: ElementType.LIST,
valueList: []
}
if (node.nodeName === 'OL') {
listElement.listType = ListType.OL
} else {
listElement.listType = ListType.UL
listElement.listStyle = <ListStyle>(
(<unknown>listNode.style.listStyleType)
)
}
listNode.querySelectorAll('li').forEach(li => {
const liValueList = getElementListByHTML(li.innerHTML, options)
liValueList.forEach(list => {
if (list.value === '\n') {
list.listWrap = true
}
})
liValueList.unshift({
value: '\n'
})
listElement.valueList!.push(...liValueList)
})
elementList.push(listElement)
} else if (node.nodeName === 'HR') {
elementList.push({
value: '\n',
type: ElementType.SEPARATOR
})
} else if (node.nodeName === 'IMG') {
const { src, width, height } = node as HTMLImageElement
if (src && width && height) {
elementList.push({
width,
height,
value: src,
type: ElementType.IMAGE
})
}
} else if (node.nodeName === 'TABLE') {
const tableElement = node as HTMLTableElement
const element: IElement = {
type: ElementType.TABLE,
value: '\n',
colgroup: [],
trList: []
}
// 基础数据
tableElement.querySelectorAll('tr').forEach(trElement => {
const trHeightStr = window
.getComputedStyle(trElement)
.height.replace('px', '')
const tr: ITr = {
height: Number(trHeightStr),
tdList: []
}
trElement.querySelectorAll('th,td').forEach(tdElement => {
const tableCell = <HTMLTableCellElement>tdElement
const valueList = getElementListByHTML(
tableCell.innerHTML,
options
)
const td: ITd = {
colspan: tableCell.colSpan,
rowspan: tableCell.rowSpan,
value: valueList
}
if (tableCell.style.backgroundColor) {
td.backgroundColor = tableCell.style.backgroundColor
}
tr.tdList.push(td)
})
if (tr.tdList.length) {
element.trList!.push(tr)
}
})
if (element.trList!.length) {
// 列选项数据
const tdCount = element.trList![0].tdList.reduce(
(pre, cur) => pre + cur.colspan,
0
)
const width = Math.ceil(options.innerWidth / tdCount)
for (let i = 0; i < tdCount; i++) {
element.colgroup!.push({
width
})
}
elementList.push(element)
}
} else if (
node.nodeName === 'INPUT' &&
(<HTMLInputElement>node).type === ControlComponent.CHECKBOX
) {
elementList.push({
type: ElementType.CHECKBOX,
value: '',
checkbox: {
value: (<HTMLInputElement>node).checked
}
})
} else {
findTextNode(node)
if (node.nodeType === 1 && n !== childNodes.length - 1) {
const display = window.getComputedStyle(node as Element).display
if (display === 'block') {
elementList.push({
value: '\n'
})
}
}
}
}
}
}
// 追加dom
const clipboardDom = document.createElement('div')
clipboardDom.innerHTML = htmlText
document.body.appendChild(clipboardDom)
const deleteNodes: ChildNode[] = []
clipboardDom.childNodes.forEach(child => {
if (child.nodeType !== 1 && !child.textContent?.trim()) {
deleteNodes.push(child)
}
})
deleteNodes.forEach(node => node.remove())
// 搜索文本节点
findTextNode(clipboardDom)
// 移除dom
clipboardDom.remove()
return elementList
}

Loading…
Cancel
Save