UNPKG

devexpress-richedit

Version:

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

249 lines (248 loc) 13.1 kB
import { ModelChangeType } from './model/changes/enums'; import { RichUtils } from './model/rich-utils'; import { SubDocument } from './model/sub-document'; import { BatchUpdatableObject } from '@devexpress/utils/lib/class/batch-updatable'; import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { ContentInsertedSubDocumentChange } from './model/changes/change-base'; var PublicApiEventType; (function (PublicApiEventType) { PublicApiEventType[PublicApiEventType["None"] = 0] = "None"; PublicApiEventType[PublicApiEventType["DocumentLoaded"] = 1] = "DocumentLoaded"; PublicApiEventType[PublicApiEventType["ModelChanged"] = 2] = "ModelChanged"; PublicApiEventType[PublicApiEventType["SelectionChanged"] = 3] = "SelectionChanged"; PublicApiEventType[PublicApiEventType["DocumentFormatted"] = 4] = "DocumentFormatted"; })(PublicApiEventType || (PublicApiEventType = {})); class PublicEvent { constructor(type, action, change) { this.type = type; this.action = action; this.change = change; } } export class GlobalEventDispatcher extends BatchUpdatableObject { constructor(rich, onModelChanged) { super(); this.deferredEvents = []; this.locked = false; this.rich = rich; this.onModelChanged = onModelChanged; } addDeferredEvent(e, change, type = PublicApiEventType.None) { if (!this.rich.commandManager.isPublicApiCall || this.rich.modelManager.richOptions.control.raiseClientEventsOnModificationsViaAPI) { this.deferredEvents.push(new PublicEvent(type, e, change)); this.handleDeferredEvents(); } } isCharacterPropertiesChangedType(type) { return type == ModelChangeType.CharacterPropertiesChanged || type == ModelChangeType.CharacterFormattingChanged; } equals(source, target) { if (!source || !target) return false; if (this.isCharacterPropertiesChangedType(source.type) && this.isCharacterPropertiesChangedType(target.type)) { const sourceChange = source; const targetChange = target; return sourceChange.subDocumentId == targetChange.subDocumentId && sourceChange.newState.interval.equals(targetChange.newState.interval); } return false; } handleDeferredEvents() { if (!this.isLocked() && (!this.rich.commandManager.isPublicApiCall || this.rich.modelManager.richOptions.control.raiseClientEventsOnModificationsViaAPI)) { const singleEvents = []; const occuredFormattingEvents = []; let prevInsertingEvent = null; this.deferredEvents = ListUtils.reducedMap(this.deferredEvents, (e) => { if (e.type == PublicApiEventType.None) { const change = e.change; if (change) { if (this.isCharacterPropertiesChangedType(change.type)) { if (ListUtils.indexBy(occuredFormattingEvents, (oe) => this.equals(oe.change, e.change)) < 0) occuredFormattingEvents.push(e); else return null; } if (change instanceof ContentInsertedSubDocumentChange) { if (this.tryAppendInsertingEvent(prevInsertingEvent, change)) return null; else prevInsertingEvent = e; } } return e; } singleEvents.push(e); return null; }); ListUtils.addListOnTail(this.deferredEvents, ListUtils.unique(singleEvents, (a, b) => { return a.type - b.type; })); if (!this.locked) { this.locked = true; for (let action; action = this.deferredEvents.shift();) action.action(); this.locked = false; } } } tryAppendInsertingEvent(prevEvent, modelChange) { if (prevEvent) { const prevChange = prevEvent.change; if (prevChange.canContinuesWith(modelChange)) { prevChange.length += modelChange.length; prevEvent.action = () => this.rich.clientSideEvents.raiseContentInserted(prevChange.subDocumentId, new FixedInterval(prevChange.position, prevChange.length)); return true; } } return false; } onUpdateUnlocked(_occurredEvents) { this.handleDeferredEvents(); } processModelChanged() { this.addDeferredEvent(() => this.onModelChanged(), null, PublicApiEventType.ModelChanged); } NotifyDocumentLoaded() { this.addDeferredEvent(() => this.rich.clientSideEvents.raiseDocumentLoaded(), null, PublicApiEventType.DocumentLoaded); } NotifyPagesReady(_pageChanges) { } NotifyFullyFormatted(pageCount) { this.addDeferredEvent(() => this.rich.clientSideEvents.raiseDocumentFormatted(pageCount), null, PublicApiEventType.DocumentFormatted); } NotifySelectionChanged(selection) { if (selection.prevState.intervalsInfo.subDocument != selection.currState.intervalsInfo.subDocument) this.addDeferredEvent(() => this.rich.clientSideEvents.raiseActiveSubDocumentChanged(), null, PublicApiEventType.SelectionChanged); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseSelectionChanged(), null, PublicApiEventType.SelectionChanged); } NotifyScrollPositionChanged() { } modelChanged(change) { switch (change.type) { case ModelChangeType.PageColor: case ModelChangeType.DefaultTabWidth: case ModelChangeType.DifferentOddAndEvenPages: case ModelChangeType.HeaderFooterIndexChanged: case ModelChangeType.SectionFormattingChanged: case ModelChangeType.AbstractNumberingListAdded: case ModelChangeType.AbstractNumberingListDeleted: case ModelChangeType.NumberingListAdded: case ModelChangeType.NumberingListDeleted: case ModelChangeType.ListLevelPropertyChanged: case ModelChangeType.ListLevelParagraphPropertyChanged: case ModelChangeType.ListLevelCharacterPropertyChanged: case ModelChangeType.IOverrideListLevelChanged: case ModelChangeType.BookmarkCreated: case ModelChangeType.BookmarkDeleted: case ModelChangeType.TabInserted: case ModelChangeType.TabDeleted: case ModelChangeType.LoadPicturesInfo: case ModelChangeType.InlinePicturesUpdated: case ModelChangeType.AnchoredPictureSizeChanged: case ModelChangeType.InlineObjectRunPropertyChanged: case ModelChangeType.FieldInserted: case ModelChangeType.FieldDeleted: case ModelChangeType.HyperlinkInfoChanged: case ModelChangeType.FieldsShowCodeChanged: case ModelChangeType.TextBufferChanged: case ModelChangeType.ParagraphAndCharacterMergedPropertiesReset: case ModelChangeType.AnchoredTextBoxSizeChanged: case ModelChangeType.AnchoredTextBoxPropertiesChanged: case ModelChangeType.AnchorInfoPropertyChanged: case ModelChangeType.ShapeChanged: case ModelChangeType.ShapePropertyChanged: case ModelChangeType.CreateStyleLink: case ModelChangeType.DeleteStyleLink: case ModelChangeType.CharacterStyleApplied: case ModelChangeType.ParagraphStyleApplied: case ModelChangeType.TableStyleChanged: case ModelChangeType.ParagraphNumberingListChanged: case ModelChangeType.TableCreated: case ModelChangeType.TableStartPositionShifted: case ModelChangeType.TableCellPropertyChanged: case ModelChangeType.TablePropertyChanged: case ModelChangeType.TableRowPropertyChanged: case ModelChangeType.TableCellSplittedHorizontally: case ModelChangeType.TableCellMergedHorizontally: case ModelChangeType.TableRowInserted: case ModelChangeType.TableRowRemoved: case ModelChangeType.TableCellRemoved: case ModelChangeType.TableCellInserted: { this.processModelChanged(); break; } case ModelChangeType.SectionMerged: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseContentRemoved(SubDocument.MAIN_SUBDOCUMENT_ID, change.removedInterval, RichUtils.specialCharacters.SectionMark), change); break; } case ModelChangeType.ParagraphMerged: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseContentRemoved(change.subDocumentId, change.removedInterval, RichUtils.specialCharacters.ParagraphMark), change); break; } case ModelChangeType.HeaderFooterCreated: case ModelChangeType.InlinePictureInserted: case ModelChangeType.AnchoredPictureInserted: case ModelChangeType.ParagraphInserted: case ModelChangeType.SectionInserted: case ModelChangeType.AnchoredTextBoxInserted: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseContentInserted(change.subDocumentId, new FixedInterval(change.position, 1)), change); break; } case ModelChangeType.SimpleRunInserted: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseContentInserted(change.subDocumentId, new FixedInterval(change.position, change.length)), change); break; } case ModelChangeType.AnchorObjectRemoved: { break; } case ModelChangeType.IntervalRemoved: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseContentRemoved(change.subDocumentId, change.interval, change.removedText), change); break; } case ModelChangeType.CharacterFormattingChanged: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseCharacterPropertiesChanged(change.subDocumentId, change.newState.interval), change); break; } case ModelChangeType.CharacterPropertiesChanged: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseCharacterPropertiesChanged(change.subDocumentId, change.newState.interval), change); break; } case ModelChangeType.ParagraphFormattingChanged: { this.processModelChanged(); const paragraphIndexs = this.rich.modelManager.model.subDocuments[change.subDocumentId].getParagraphsIndices(change.newState.interval); for (let i = paragraphIndexs.start; i < paragraphIndexs.end; i++) { const parInd = i; this.addDeferredEvent(() => this.rich.clientSideEvents.raiseParagraphPropertiesChanged(change.subDocumentId, parInd), change); } break; } case ModelChangeType.ParagraphPropertiesChanged: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseParagraphPropertiesChanged(change.subDocumentId, change.paragraphIndex), change); break; } case ModelChangeType.TableRemoved: { this.processModelChanged(); this.addDeferredEvent(() => this.rich.clientSideEvents.raiseContentRemoved(change.subDocumentId, FixedInterval.fromPositions(change.startPosition, change.endPosition), change.removedText), change); break; } case ModelChangeType.DocumentProtectionChanged: { this.processModelChanged(); break; } case ModelChangeType.RangePermissionsChanged: { this.processModelChanged(); break; } case ModelChangeType.RangePermissionsPropertiesChanged: { this.processModelChanged(); break; } } } }