UNPKG

devexpress-richedit

Version:

DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.

206 lines (205 loc) 10.6 kB
import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { ModelIterator } from '../model-iterator'; import { LinkedInterval } from '../position/linked-interval'; import { RichUtils } from '../rich-utils'; import { RunType } from '../runs/run-type'; import { SubDocumentInterval, SubDocumentIntervals } from '../sub-document'; import { Field } from './field'; import { FieldCodeParser } from './parsers/field-code-parser'; import { FieldCodeParserDate } from './parsers/field-code-parser-date'; import { FieldCodeParserDocVariable } from './parsers/field-code-parser-doc-variable'; import { FieldCodeParserHyperlink } from './parsers/field-code-parser-hyperlink'; import { FieldCodeParserMailMerge } from './parsers/field-code-parser-merge-field'; import { FieldCodeParserNumPages } from './parsers/field-code-parser-num-pages'; import { FieldCodeParserPage } from './parsers/field-code-parser-page'; import { FieldCodeParserPageRef } from './parsers/field-code-parser-page-ref'; import { FieldCodeParserSeq } from './parsers/field-code-parser-seq'; import { FieldCodeParserTc } from './parsers/field-code-parser-tc'; import { FieldCodeParserTime } from './parsers/field-code-parser-time'; import { FieldCodeParserToc } from './parsers/field-code-parser-toc'; import { FieldCodeParserFillIn } from './parsers/fill-in'; import { FieldCodeParserIf } from './parsers/field-code-parser-if'; class FieldParsersAndIntervals { constructor(interval) { this.interval = interval; this.parsers = []; this.updated = false; } destructor(manager) { this.interval.destructor(manager); } } export class FieldUpdateResult { constructor(subDocIntervals) { this.subDocIntervals = subDocIntervals; } } export class UpdateFieldsOptions { constructor(updateToc = true, updateFillIn = true) { this.updateToc = updateToc; this.updateFillIn = updateFillIn; } } export class FieldsWaitingForUpdate { get fields() { return this.subDocument.fields; } constructor(modelManager, layoutFormatterManager, requestManager, subDocumentIntervals, options, callback) { this.infoForFutureUpdate = []; this.savedSelectionIntervals = []; this.modelManager = modelManager; this.layoutFormatterManager = layoutFormatterManager; this.requestManager = requestManager; this.callback = callback; this.subDocument = subDocumentIntervals.subDocument; this.options = options; this.infoForFutureUpdate = ListUtils.map(subDocumentIntervals.intervals, (interval) => new FieldParsersAndIntervals(new LinkedInterval(this.subDocument.positionManager, interval))); this.savedSelectionIntervals = ListUtils.map(subDocumentIntervals.intervals, (interval) => new LinkedInterval(this.subDocument.positionManager, interval)); } update(response, immediateSendRequest = true) { if (this.fields.length == 0) { this.endAction(); return; } this.requestManager.checkResponse(this.subDocument, response); this.requestManager.clear(this.subDocument); var countUpdatedInfos = 0; for (var infoIndex = 0, info; info = this.infoForFutureUpdate[infoIndex]; infoIndex++) { if (info.updated) { countUpdatedInfos++; continue; } if (info.parsers.length > 0) { if (this.continueUpdateCurrentInterval(info.parsers, response)) { info.updated = true; countUpdatedInfos++; } continue; } var fieldIndex = Math.max(0, Field.normedBinaryIndexOf(this.fields, info.interval.start + 1)); var field = this.fields[fieldIndex]; while (!field.getAllFieldInterval().containsWithIntervalEnd(info.interval.start) && field.parent) field = field.parent; var startParent = field.parent; var someFieldInCurrentInfoNotUpdated = false; for (fieldIndex = field.index; field = this.fields[fieldIndex]; fieldIndex++) { const startPos = field.getFieldStartPosition(); if (startPos > info.interval.end || startPos == info.interval.end && info.interval.length > 0) break; if (field.getFieldEndPosition() <= info.interval.start || (field.parent != null && field.parent != startParent) || !this.subDocument.isEditable([field.getAllFieldInterval()])) continue; const fieldParser = FieldsWaitingForUpdate.getParser(this.modelManager, this.layoutFormatterManager, this.requestManager, this.subDocument, field); if (fieldParser) { if (!this.options.updateToc && fieldParser instanceof FieldCodeParserToc || !this.options.updateFillIn && fieldParser instanceof FieldCodeParserFillIn) { const skipAllBefore = field.getFieldEndPosition(); fieldParser.destructor(); for (let ind = fieldIndex + 1; field = this.fields[ind]; ind++) { if (field.getFieldStartPosition() >= skipAllBefore) { fieldIndex = ind - 1; break; } } continue; } if (!fieldParser.update(response)) { someFieldInCurrentInfoNotUpdated = true; info.parsers.push(fieldParser); } else fieldParser.destructor(); } else { this.modelManager.modelManipulator.range.removeInterval(new SubDocumentInterval(this.subDocument, field.getResultInterval()), true, false); FieldCodeParser.finalAction(this.layoutFormatterManager, field, this.subDocument); } } if (!someFieldInCurrentInfoNotUpdated) { info.updated = true; countUpdatedInfos++; } } if (this.infoForFutureUpdate.length != countUpdatedInfos) this.requestManager.sendRequest(this.subDocument, this.modelManager.richOptions.mailMerge.activeRecordIndex, immediateSendRequest); else { this.requestManager.forceSendDelayedRequests(); this.endAction(); } } endAction() { const selectionIntervals = ListUtils.map(this.savedSelectionIntervals, (interval) => { const fixed = interval.getFixedInterval(); interval.destructor(this.subDocument.positionManager); return fixed; }); for (let info of this.infoForFutureUpdate) info.destructor(this.subDocument.positionManager); this.infoForFutureUpdate = []; this.callback(new FieldUpdateResult(new SubDocumentIntervals(this.subDocument, selectionIntervals))); } continueUpdateCurrentInterval(fieldParsers, response) { var allFieldUpdated = true; for (var parserIndex = 0, parser; parser = fieldParsers[parserIndex]; parserIndex++) { if (parser.update(response)) { parser.destructor(); fieldParsers.splice(parserIndex, 1); parserIndex--; } else allFieldUpdated = false; } return allFieldUpdated; } static getParser(modelManager, layoutFormatterManager, requestManager, subDocument, field) { const modelIterator = new ModelIterator(subDocument, false); modelIterator.setPosition(field.getCodeStartPosition()); const result = FieldsWaitingForUpdate.findName(modelIterator); const parserConstructor = FieldsWaitingForUpdate.parsersMap[result.fieldName]; return parserConstructor ? parserConstructor({ modelManager: modelManager, layoutFormatterManager: layoutFormatterManager, requestManager: requestManager, subDocument: subDocument, field: field, modelIterator: modelIterator, fieldNameFirstLetterPosition: result.fieldNameFirstLetterPosition }) : null; } static findName(modelIterator) { while (modelIterator.run.getType() == RunType.TextRun && RichUtils.isWhitespace.test(modelIterator.getCurrentChar()) || modelIterator.run.getType() == RunType.ParagraphRun || modelIterator.run.getType() == RunType.SectionRun) modelIterator.moveToNextChar(); const fieldNameFirstLetterPosition = modelIterator.getAbsolutePosition(); let fieldName = ""; do { fieldName += modelIterator.getCurrentChar(); modelIterator.moveToNextChar(); } while (modelIterator.run.getType() == RunType.TextRun && !RichUtils.isWhitespace.test(modelIterator.getCurrentChar()) && modelIterator.run.getType() != RunType.ParagraphRun && modelIterator.run.getType() != RunType.SectionRun); return new FindFieldNameResult(fieldName.toUpperCase(), fieldNameFirstLetterPosition); } } FieldsWaitingForUpdate.TOC_NAME = "TOC"; FieldsWaitingForUpdate.parsersMap = { ["IF"]: (args) => new FieldCodeParserIf(args), ["DATE"]: (args) => new FieldCodeParserDate(args), ["TIME"]: (args) => new FieldCodeParserTime(args), ["DOCVARIABLE"]: (args) => new FieldCodeParserDocVariable(args), ["HYPERLINK"]: (args) => new FieldCodeParserHyperlink(args), ["MERGEFIELD"]: (args) => new FieldCodeParserMailMerge(args), ["NUMPAGES"]: (args) => new FieldCodeParserNumPages(args), ["PAGE"]: (args) => new FieldCodeParserPage(args), ["SEQ"]: (args) => new FieldCodeParserSeq(args), ["TC"]: (args) => new FieldCodeParserTc(args), [FieldsWaitingForUpdate.TOC_NAME]: (args) => new FieldCodeParserToc(args), ["PAGEREF"]: (args) => new FieldCodeParserPageRef(args), ["FILLIN"]: (args) => new FieldCodeParserFillIn(args), }; export class FindFieldNameResult { constructor(fieldName, fieldNameFirstLetterPosition) { this.fieldName = fieldName; this.fieldNameFirstLetterPosition = fieldNameFirstLetterPosition; } }