devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
190 lines (189 loc) • 11 kB
JavaScript
import { CommandBase } from '../../common/commands/command-base';
import { SimpleCommandState } from '../../common/commands/command-states';
import { MergeMode } from '../../common/commands/dialogs/dialog-finish-and-merge-command';
import { ClientModelManager } from '../../common/model-manager';
import { FieldCodeParserMailMerge } from '../../common/model/fields/parsers/field-code-parser-merge-field';
import { FieldsWaitingForUpdate } from '../../common/model/fields/tree-creator';
import { InsertParagraphManipulatorParams } from '../../common/model/manipulators/paragraph-manipulator/insert-paragraph-manipulator-params';
import { RangeCopy } from '../../common/model/manipulators/range/create-range-copy-operation';
import { InsertTextManipulatorParams } from '../../common/model/manipulators/text-manipulator/insert-text-manipulator-params';
import { ControlOptions, DocumentCapability } from '../../common/model/options/control';
import { RichUtils } from '../../common/model/rich-utils';
import { RunType } from '../../common/model/runs/run-type';
import { SectionStartType } from '../../common/model/section/enums';
import { SubDocumentIntervals, SubDocumentPosition } from '../../common/model/sub-document';
import { MaskedCharacterPropertiesBundle } from '../../common/rich-utils/properties-bundle';
import { EmptyBatchUpdatableObject } from '@devexpress/utils/lib/class/batch-updatable';
import { Errors } from '@devexpress/utils/lib/errors';
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { ListUtils } from '@devexpress/utils/lib/utils/list';
import { NumberMapUtils } from '@devexpress/utils/lib/utils/map/number';
import { StringUtils } from '@devexpress/utils/lib/utils/string';
import { DocxExportOptions } from '../formats/docx/export/docx-export-options';
import { DocxExporter } from '../formats/docx/export/exporter';
import { Importer } from '../formats/docx/import/importer';
import { ImporterOptions } from '../formats/docx/import/importer-options';
import { exportModelToBlob } from '../model-api/formats/exporter';
import { FieldCodeParserIf } from '../../common/model/fields/parsers/field-code-parser-if';
export class MailMergeCommand extends CommandBase {
getState() {
var state = new SimpleCommandState(this.isEnabled());
state.visible = this.control.modelManager.richOptions.control.fields !== DocumentCapability.Hidden;
return state;
}
isEnabledInReadOnlyMode() {
return true;
}
isEnabled() {
return (super.isEnabled() &&
ControlOptions.isEnabled(this.control.modelManager.richOptions.control.fields) &&
this.control.modelManager.model.mainSubDocument.getDocumentEndPosition() > 1 &&
!!this.getDataSource());
}
canModify() {
return true;
}
executeCore(_state, options) {
const docxExporter = new DocxExporter(this.control.modelManager.modelManipulator, new DocxExportOptions());
docxExporter.exportToBlob((blob) => {
const docxImporter = new Importer(new ImporterOptions());
docxImporter.importFromFile(blob, this.control.modelManager.richOptions, (documentModel, formatImagesImporter) => {
const exportModelOptions = this.control.getExportModelOptions({
documentFormat: options.param.documentFormat,
modelManager: this.createModelManager(documentModel),
});
this.control.commandManager.formatImagesImporters.push(formatImagesImporter);
formatImagesImporter.whenAllPicturesLoaded((successLoadedAllPictures) => {
const index = this.control.commandManager.formatImagesImporters.indexOf(formatImagesImporter);
if (index >= 0)
this.control.commandManager.formatImagesImporters.splice(index, 1);
if (!successLoadedAllPictures)
throw new Error(Errors.InternalException);
const param = options.param;
this.prepareMergedDocument(exportModelOptions.modelManager, param);
exportModelToBlob(exportModelOptions, (blob) => param.callback(blob));
}, 3000);
formatImagesImporter.import(exportModelOptions.modelManager.modelManipulator);
}, () => { });
});
return true;
}
prepareMergedDocument(modelManager, param) {
const subDoc = modelManager.model.mainSubDocument;
const subDocumentIntervals = new SubDocumentIntervals(subDoc, [new FixedInterval(0, subDoc.getDocumentEndPosition() - 1)]);
const rangeCopy = RangeCopy.create(subDocumentIntervals);
const dataSource = this.getDataSource();
const exportToIndex = Math.min(param.exportFrom + param.exportRecordsCount, dataSource.totalCount()) - 1;
if (param.exportFrom > exportToIndex)
modelManager.modelManipulator.range.removeIntervalWithoutHistory(subDoc, subDocumentIntervals.intervals[0], false);
let lastProcessedPositionInMainSubDocument = 0;
const processedSubDocIds = [];
for (let index = param.exportFrom; index <= exportToIndex; index++) {
if (index > param.exportFrom)
rangeCopy.insertTo(modelManager.modelManipulator, new SubDocumentPosition(subDoc, subDoc.getDocumentEndPosition() - 1));
const record = dataSource.items()[index];
this.replaceFieldsInModel(modelManager, record, lastProcessedPositionInMainSubDocument, processedSubDocIds);
if (index < exportToIndex)
this.insertSeparator(modelManager, param.mergeMode);
lastProcessedPositionInMainSubDocument = subDoc.getDocumentEndPosition() - 1;
}
}
getDataSource() {
return this.control.owner.dataSource;
}
replaceFieldsInModel(modelManager, record, lastProcessedPositionInMainSubDocument, processedSubDocIds) {
NumberMapUtils.forEach(modelManager.model.subDocuments, (subDoc) => {
if (!ListUtils.anyOf(processedSubDocIds, (id) => id === subDoc.id)) {
this.replaceMergeFieldsInSubDocument(modelManager, record, lastProcessedPositionInMainSubDocument, subDoc);
this.replaceIfFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc);
if (!subDoc.isMain())
processedSubDocIds.push(subDoc.id);
}
});
}
processFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc, processField) {
for (let i = subDoc.fields.length - 1; i >= 0; i--) {
const field = subDoc.fields[i];
if (subDoc.isMain() && field.getFieldStartPosition() < lastProcessedPositionInMainSubDocument) {
return;
}
const parser = FieldsWaitingForUpdate.getParser(modelManager, null, null, subDoc, field);
if (parser) {
try {
const text = processField(parser);
if (text !== null) {
this.replaceFieldWithText(modelManager, subDoc, field, text);
}
}
finally {
parser.destructor();
}
}
}
}
replaceMergeFieldsInSubDocument(modelManager, record, lastProcessedPositionInMainSubDocument, subDoc) {
const processField = (parser) => {
if (parser instanceof FieldCodeParserMailMerge) {
const fieldName = parser.getMergeFieldName();
return RichUtils.replaceParagraphEndCharsWithLineBreak(this.getResultByFieldName(record, fieldName));
}
return null;
};
this.processFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc, processField);
}
replaceIfFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc) {
const processField = (parser) => {
if (parser instanceof FieldCodeParserIf) {
parser.parseSwitchesAndArgs();
return parser.getResult();
}
return null;
};
this.processFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc, processField);
}
replaceFieldWithText(modelManager, subDoc, field, text) {
if (!StringUtils.isNullOrEmpty(text)) {
const pos = field.getFieldStartPosition();
const runProps = subDoc.getRunByPosition(pos).getCharPropsBundle(modelManager.model);
const insertParams = new InsertTextManipulatorParams(new SubDocumentPosition(subDoc, pos), runProps, RunType.TextRun, text);
modelManager.modelManipulator.text.insertTextInner(insertParams);
}
modelManager.modelManipulator.range.removeIntervalWithoutHistory(subDoc, field.getAllFieldInterval(), false);
}
getResultByFieldName(record, fieldName) {
const keys = Object.keys(record);
for (let i = 0, key; (key = keys[i]); i++) {
if (key.toLowerCase() == fieldName.toLowerCase())
return record[key] + '';
}
return '';
}
insertSeparator(modelManager, mergeMode) {
const subDoc = modelManager.model.mainSubDocument;
const position = subDoc.getDocumentEndPosition() - 1;
const firstRun = subDoc.getRunByPosition(0);
const characterStyle = firstRun.characterStyle;
const maskedCharacterProperties = firstRun.maskedCharacterProperties.clone();
const maskedCharacterPropertiesBundle = new MaskedCharacterPropertiesBundle(maskedCharacterProperties, characterStyle);
switch (mergeMode) {
case MergeMode.NewParagraph:
modelManager.modelManipulator.paragraph.insertParagraphViaHistory(new InsertParagraphManipulatorParams(new SubDocumentPosition(subDoc, position), maskedCharacterPropertiesBundle));
break;
case MergeMode.NewSection:
modelManager.modelManipulator.section.insertSectionAndSetStartType(position, SectionStartType.NextPage, maskedCharacterPropertiesBundle);
break;
}
}
createModelManager(model) {
return new ClientModelManager(model, this.control.modelManager.richOptions, new EmptyBatchUpdatableObject());
}
}
export class MailMergeCommandParameters {
constructor(callback, mergeMode, documentFormat, exportFrom, exportRecordsCount) {
this.callback = callback;
this.mergeMode = mergeMode;
this.documentFormat = documentFormat;
this.exportFrom = exportFrom ? exportFrom : 0;
this.exportRecordsCount = exportRecordsCount ? exportRecordsCount : Infinity;
}
}