Merge pull request #54 from Hufe921/feature/word-count
Feature/word count
This commit is contained in:
@@ -208,6 +208,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<span>可见页码:<span class="page-no-list">1</span></span>
|
<span>可见页码:<span class="page-no-list">1</span></span>
|
||||||
<span>页面:<span class="page-no">1</span>/<span class="page-size">1</span></span>
|
<span>页面:<span class="page-no">1</span>/<span class="page-size">1</span></span>
|
||||||
|
<span>字数:<span class="word-count">0</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-mode">编辑模式</div>
|
<div class="editor-mode">编辑模式</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export class Command {
|
|||||||
private static print: Function
|
private static print: Function
|
||||||
private static getImage: Function
|
private static getImage: Function
|
||||||
private static getValue: Function
|
private static getValue: Function
|
||||||
|
private static getWordCount: Function
|
||||||
private static pageMode: Function
|
private static pageMode: Function
|
||||||
private static pageScaleRecovery: Function
|
private static pageScaleRecovery: Function
|
||||||
private static pageScaleMinus: Function
|
private static pageScaleMinus: Function
|
||||||
@@ -112,6 +113,7 @@ export class Command {
|
|||||||
Command.print = adapt.print.bind(adapt)
|
Command.print = adapt.print.bind(adapt)
|
||||||
Command.getImage = adapt.getImage.bind(adapt)
|
Command.getImage = adapt.getImage.bind(adapt)
|
||||||
Command.getValue = adapt.getValue.bind(adapt)
|
Command.getValue = adapt.getValue.bind(adapt)
|
||||||
|
Command.getWordCount = adapt.getWordCount.bind(adapt)
|
||||||
Command.pageMode = adapt.pageMode.bind(adapt)
|
Command.pageMode = adapt.pageMode.bind(adapt)
|
||||||
Command.pageScaleRecovery = adapt.pageScaleRecovery.bind(adapt)
|
Command.pageScaleRecovery = adapt.pageScaleRecovery.bind(adapt)
|
||||||
Command.pageScaleMinus = adapt.pageScaleMinus.bind(adapt)
|
Command.pageScaleMinus = adapt.pageScaleMinus.bind(adapt)
|
||||||
@@ -315,6 +317,10 @@ export class Command {
|
|||||||
return Command.getValue()
|
return Command.getValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getWordCount(): Promise<number> {
|
||||||
|
return Command.getWordCount()
|
||||||
|
}
|
||||||
|
|
||||||
// 页面模式、页面缩放
|
// 页面模式、页面缩放
|
||||||
public executePageMode(payload: PageMode) {
|
public executePageMode(payload: PageMode) {
|
||||||
return Command.pageMode(payload)
|
return Command.pageMode(payload)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { CanvasEvent } from '../event/CanvasEvent'
|
|||||||
import { HistoryManager } from '../history/HistoryManager'
|
import { HistoryManager } from '../history/HistoryManager'
|
||||||
import { Position } from '../position/Position'
|
import { Position } from '../position/Position'
|
||||||
import { RangeManager } from '../range/RangeManager'
|
import { RangeManager } from '../range/RangeManager'
|
||||||
|
import { WorkerManager } from '../worker/WorkerManager'
|
||||||
|
|
||||||
|
|
||||||
export class CommandAdapt {
|
export class CommandAdapt {
|
||||||
@@ -37,6 +38,7 @@ export class CommandAdapt {
|
|||||||
private tableTool: TableTool
|
private tableTool: TableTool
|
||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
private control: Control
|
private control: Control
|
||||||
|
private workerManager: WorkerManager
|
||||||
|
|
||||||
constructor(draw: Draw) {
|
constructor(draw: Draw) {
|
||||||
this.draw = draw
|
this.draw = draw
|
||||||
@@ -47,6 +49,7 @@ export class CommandAdapt {
|
|||||||
this.tableTool = draw.getTableTool()
|
this.tableTool = draw.getTableTool()
|
||||||
this.options = draw.getOptions()
|
this.options = draw.getOptions()
|
||||||
this.control = draw.getControl()
|
this.control = draw.getControl()
|
||||||
|
this.workerManager = draw.getWorkerManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
public mode(payload: EditorMode) {
|
public mode(payload: EditorMode) {
|
||||||
@@ -1243,6 +1246,10 @@ export class CommandAdapt {
|
|||||||
return this.draw.getValue()
|
return this.draw.getValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getWordCount(): Promise<number> {
|
||||||
|
return this.workerManager.getWordCount()
|
||||||
|
}
|
||||||
|
|
||||||
public pageMode(payload: PageMode) {
|
public pageMode(payload: PageMode) {
|
||||||
this.draw.setPageMode(payload)
|
this.draw.setPageMode(payload)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import { CheckboxParticle } from './particle/CheckboxParticle'
|
|||||||
import { DeepRequired } from '../../interface/Common'
|
import { DeepRequired } from '../../interface/Common'
|
||||||
import { ControlComponent } from '../../dataset/enum/Control'
|
import { ControlComponent } from '../../dataset/enum/Control'
|
||||||
import { formatElementList } from '../../utils/element'
|
import { formatElementList } from '../../utils/element'
|
||||||
|
import { WorkerManager } from '../worker/WorkerManager'
|
||||||
|
|
||||||
export class Draw {
|
export class Draw {
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ export class Draw {
|
|||||||
private subscriptParticle: SubscriptParticle
|
private subscriptParticle: SubscriptParticle
|
||||||
private checkboxParticle: CheckboxParticle
|
private checkboxParticle: CheckboxParticle
|
||||||
private control: Control
|
private control: Control
|
||||||
|
private workerManager: WorkerManager
|
||||||
|
|
||||||
private rowList: IRow[]
|
private rowList: IRow[]
|
||||||
private painterStyle: IElementStyle | null
|
private painterStyle: IElementStyle | null
|
||||||
@@ -138,6 +140,8 @@ export class Draw {
|
|||||||
const globalEvent = new GlobalEvent(this, this.canvasEvent)
|
const globalEvent = new GlobalEvent(this, this.canvasEvent)
|
||||||
globalEvent.register()
|
globalEvent.register()
|
||||||
|
|
||||||
|
this.workerManager = new WorkerManager(this)
|
||||||
|
|
||||||
this.rowList = []
|
this.rowList = []
|
||||||
this.painterStyle = null
|
this.painterStyle = null
|
||||||
this.painterOptions = null
|
this.painterOptions = null
|
||||||
@@ -363,6 +367,10 @@ export class Draw {
|
|||||||
return this.control
|
return this.control
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getWorkerManager(): WorkerManager {
|
||||||
|
return this.workerManager
|
||||||
|
}
|
||||||
|
|
||||||
public getRowCount(): number {
|
public getRowCount(): number {
|
||||||
return this.rowList.length
|
return this.rowList.length
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Draw } from '../draw/Draw'
|
||||||
|
import WordCountWorker from './works/wordCount?worker'
|
||||||
|
|
||||||
|
export class WorkerManager {
|
||||||
|
|
||||||
|
private draw: Draw
|
||||||
|
private wordCountWorker: Worker
|
||||||
|
|
||||||
|
constructor(draw: Draw) {
|
||||||
|
this.draw = draw
|
||||||
|
this.wordCountWorker = new WordCountWorker()
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWordCount(): Promise<number> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.wordCountWorker.onmessage = (evt) => {
|
||||||
|
resolve(evt.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wordCountWorker.onerror = (evt) => {
|
||||||
|
reject(evt)
|
||||||
|
}
|
||||||
|
|
||||||
|
const elementList = this.draw.getOriginalElementList()
|
||||||
|
this.wordCountWorker.postMessage(elementList)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import { IElement } from '../../../interface/Element'
|
||||||
|
|
||||||
|
enum ElementType {
|
||||||
|
TEXT = 'text',
|
||||||
|
TABLE = 'table',
|
||||||
|
HYPERLINK = 'hyperlink',
|
||||||
|
CONTROL = 'control'
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ControlComponent {
|
||||||
|
VALUE = 'value'
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZERO = '\u200B'
|
||||||
|
const WRAP = '\n'
|
||||||
|
|
||||||
|
function pickText(elementList: IElement[]): string {
|
||||||
|
let text = ''
|
||||||
|
let e = 0
|
||||||
|
while (e < elementList.length) {
|
||||||
|
const element = elementList[e]
|
||||||
|
// 表格、超链接递归处理
|
||||||
|
if (element.type === ElementType.TABLE) {
|
||||||
|
if (element.trList) {
|
||||||
|
for (let t = 0; t < element.trList.length; t++) {
|
||||||
|
const tr = element.trList[t]
|
||||||
|
for (let d = 0; d < tr.tdList.length; d++) {
|
||||||
|
const td = tr.tdList[d]
|
||||||
|
text += pickText(td.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (element.type === ElementType.HYPERLINK) {
|
||||||
|
const hyperlinkId = element.hyperlinkId
|
||||||
|
const valueList: IElement[] = []
|
||||||
|
while (e < elementList.length) {
|
||||||
|
const hyperlinkE = elementList[e]
|
||||||
|
if (hyperlinkId !== hyperlinkE.hyperlinkId) {
|
||||||
|
e--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
delete hyperlinkE.type
|
||||||
|
valueList.push(hyperlinkE)
|
||||||
|
e++
|
||||||
|
}
|
||||||
|
text += pickText(valueList)
|
||||||
|
} else if (element.type === ElementType.CONTROL) {
|
||||||
|
const controlId = element.controlId
|
||||||
|
const valueList: IElement[] = []
|
||||||
|
while (e < elementList.length) {
|
||||||
|
const controlE = elementList[e]
|
||||||
|
if (controlId !== controlE.controlId) {
|
||||||
|
e--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (controlE.controlComponent === ControlComponent.VALUE) {
|
||||||
|
delete controlE.type
|
||||||
|
valueList.push(controlE)
|
||||||
|
}
|
||||||
|
e++
|
||||||
|
}
|
||||||
|
text += pickText(valueList)
|
||||||
|
}
|
||||||
|
// 文本追加
|
||||||
|
if (!element.type || element.type === ElementType.TEXT) {
|
||||||
|
text += element.value
|
||||||
|
}
|
||||||
|
e++
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupText(text: string): string[] {
|
||||||
|
const characterList: string[] = []
|
||||||
|
// 英文或数字整体分隔为一个字数
|
||||||
|
const numberReg = /[0-9]/
|
||||||
|
const letterReg = /[A-Za-z]/
|
||||||
|
const blankReg = /\s/
|
||||||
|
// for of 循环字符
|
||||||
|
let isPreLetter = false
|
||||||
|
let isPreNumber = false
|
||||||
|
let compositionText = ''
|
||||||
|
// 处理组合文本
|
||||||
|
function pushCompositionText() {
|
||||||
|
if (compositionText) {
|
||||||
|
characterList.push(compositionText)
|
||||||
|
compositionText = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const t of text) {
|
||||||
|
if (letterReg.test(t)) {
|
||||||
|
if (!isPreLetter) {
|
||||||
|
pushCompositionText()
|
||||||
|
}
|
||||||
|
compositionText += t
|
||||||
|
isPreLetter = true
|
||||||
|
isPreNumber = false
|
||||||
|
} else if (numberReg.test(t)) {
|
||||||
|
if (!isPreNumber) {
|
||||||
|
pushCompositionText()
|
||||||
|
}
|
||||||
|
compositionText += t
|
||||||
|
isPreLetter = false
|
||||||
|
isPreNumber = true
|
||||||
|
} else {
|
||||||
|
pushCompositionText()
|
||||||
|
isPreLetter = false
|
||||||
|
isPreNumber = false
|
||||||
|
if (!blankReg.test(t)) {
|
||||||
|
characterList.push(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushCompositionText()
|
||||||
|
return characterList
|
||||||
|
}
|
||||||
|
|
||||||
|
onmessage = (evt) => {
|
||||||
|
const elementList = <IElement[]>evt.data
|
||||||
|
// 提取文本
|
||||||
|
const originText = pickText(elementList)
|
||||||
|
// 过滤文本
|
||||||
|
const filterText = originText
|
||||||
|
.replace(new RegExp(`^${ZERO}`), '')
|
||||||
|
.replace(new RegExp(ZERO, 'g'), WRAP)
|
||||||
|
// 文本分组
|
||||||
|
const textGroup = groupText(filterText)
|
||||||
|
postMessage(textGroup.length)
|
||||||
|
}
|
||||||
@@ -754,6 +754,11 @@ window.onload = function () {
|
|||||||
activeMode.classList.add('active')
|
activeMode.classList.add('active')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance.listener.contentChange = async function () {
|
||||||
|
const wordCount = await instance.command.getWordCount()
|
||||||
|
document.querySelector<HTMLSpanElement>('.word-count')!.innerText = `${wordCount || 0}`
|
||||||
|
}
|
||||||
|
|
||||||
instance.listener.saved = function (payload) {
|
instance.listener.saved = function (payload) {
|
||||||
console.log('elementList: ', payload)
|
console.log('elementList: ', payload)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user