UNPKG

@syncfusion/ej2-documenteditor

Version:

Feature-rich document editor control with built-in support for context menu, options pane and dialogs.

1,086 lines 1.03 MB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; import { TextPosition, ImageInfo } from '../selection/selection-helper'; import { ParagraphWidget, LineWidget, ElementBox, TextElementBox, Margin, ImageElementBox, BlockWidget, BlockContainer, BodyWidget, TableWidget, TableCellWidget, TableRowWidget, Widget, ListTextElementBox, BookmarkElementBox, HeaderFooterWidget, FieldTextElementBox, TabElementBox, EditRangeStartElementBox, EditRangeEndElementBox, CommentElementBox, CommentCharacterElementBox, CheckBoxFormField, DropDownFormField, TextFormField, ShapeElementBox, TextFrame, ContentControl, FootnoteElementBox, FootNoteWidget } from '../viewer/page'; import { WCharacterFormat } from '../format/character-format'; import { HelperMethods, Base64 } from './editor-helper'; import { isNullOrUndefined, Browser, classList, L10n } from '@syncfusion/ej2-base'; import { WParagraphFormat, WSectionFormat, WListFormat, WTableFormat, WRowFormat, WCellFormat, WBorder, WBorders, WTabStop } from '../index'; import { WList } from '../list/list'; import { WAbstractList } from '../list/abstract-list'; import { WListLevel } from '../list/list-level'; import { WLevelOverride } from '../list/level-override'; import { FieldElementBox } from '../viewer/page'; import { protectionTypeChangeEvent, imagesProperty, abstractListIdProperty } from '../../base/index'; import { SelectionCharacterFormat } from '../index'; import { PageLayoutViewer } from '../index'; import { WCharacterStyle } from '../format/style'; import { HistoryInfo } from '../editor-history/index'; import { TableResizer } from './table-resizer'; import { Dictionary } from '../../base/dictionary'; import { WParagraphStyle } from '../format/style'; import { CONTROL_CHARACTERS } from '../../base/types'; import { showSpinner, hideSpinner } from '@syncfusion/ej2-popups'; import { DialogUtility } from '@syncfusion/ej2-popups'; import { Revision } from '../track-changes/track-changes'; import { XmlHttpRequestHandler } from '../../base/ajax-helper'; import { beforeCommentActionEvent, trackChangeEvent, beforeXmlHttpRequestSend, internalStyleCollectionChange } from '../../base/index'; import { SectionBreakType } from '../../base/types'; import { sectionsProperty, commentsProperty, bidiProperty, revisionsProperty, lastParagraphMarkCopiedProperty, sectionFormatProperty, revisionIdProperty, contextualSpacingProperty, keepWithNextProperty, keepLinesTogetherProperty, widowControlProperty, outlineLevelProperty, numberFormatProperty, startAtProperty, paragraphFormatProperty, listsProperty, abstractListsProperty, listIdProperty, listLevelNumberProperty, leftIndentProperty, rightIndentProperty, firstLineIndentProperty, textAlignmentProperty, afterSpacingProperty, beforeSpacingProperty, lineSpacingProperty, lineSpacingTypeProperty, listFormatProperty, cellsProperty, rowsProperty, blocksProperty, listLevelPatternProperty, levelsProperty, stylesProperty, nameProperty } from '../../index'; import { SanitizeHtmlHelper } from '@syncfusion/ej2-base'; import { ChangesSingleView } from '../track-changes/track-changes-pane'; /** * Editor module */ var Editor = /** @class */ (function () { /** * Initialize the editor module * * @param {DocumentHelper} documentHelper - Document helper * @private */ function Editor(documentHelper) { var _this = this; this.nodes = []; this.editHyperlinkInternal = false; this.startParagraph = undefined; this.endParagraph = undefined; this.formFieldCounter = 1; this.skipFieldDeleteTracking = false; this.skipFootNoteDeleteTracking = false; this.isForHyperlinkFormat = false; this.isTrackingFormField = false; this.isInsertText = false; this.keywordIndex = 0; /** * @private */ this.isFootnoteElementRemoved = false; /** * @private */ this.isEndnoteElementRemoved = false; /** * @private */ this.handledEnter = false; /** * @private */ this.removeEditRange = false; /** * @private */ this.isRemoveRevision = false; /** * @private */ this.isFootNoteInsert = false; /** * @private */ this.isTableInsert = false; /** * @private */ this.isFootNote = false; /** * @private */ this.isHandledComplex = false; /** * @private */ this.isUserInsert = false; /** * @private */ this.tableResize = undefined; /** * @private */ this.tocStyles = {}; /** * @private */ this.triggerPageSpellCheck = true; /** * @private */ this.chartType = false; /** * @private */ this.removedBookmarkElements = []; /** * @private */ this.removedEditRangeStartElements = []; /** * @private */ this.removedEditRangeEndElements = []; /** * @private */ this.tocBookmarkId = 0; /** * @private */ this.copiedData = undefined; /** * @private */ this.isPasteContentCheck = false; this.pageRefFields = {}; this.delBlockContinue = false; this.delBlock = undefined; this.delSection = undefined; /** * @private */ this.isInsertingTOC = false; this.editStartRangeCollection = []; this.skipReplace = false; this.skipTableElements = false; this.editRangeID = []; /** * @private */ this.isImageInsert = false; /** * @private */ this.isSkipOperationsBuild = false; /** * @private */ this.revisionData = []; /** * @private */ this.splittedRevisions = []; /** * @private */ this.isSkipComments = false; /** * @private */ this.isRemoteAction = false; /** * @private */ this.listNumberFormat = ''; /** * @private */ this.listLevelNumber = 0; /** * @private */ this.isXmlMapped = false; this.combineLastBlock = false; /* eslint-disable @typescript-eslint/no-explicit-any */ /** * @private */ this.copiedContent = ''; /** * @private */ this.copiedTextContent = ''; /** * @private */ this.previousParaFormat = undefined; this.previousCharFormat = undefined; this.previousSectionFormat = undefined; this.pasteTextPosition = undefined; //public isSkipHistory: boolean = false; /** * @private */ this.isPaste = false; /** * @private */ this.isPasteListUpdated = false; /** * @private */ this.isHtmlPaste = false; /** * @private */ this.isInsertField = false; /** * @private */ this.isBordersAndShadingDialog = false; /** * @private */ this.pasteImageIndex = undefined; /** * @private * @returns {void} */ this.onTextInputInternal = function () { if (Browser.isDevice) { var documentHelper = _this.documentHelper; var nbsp = new RegExp(String.fromCharCode(160), 'g'); var lineFeed = new RegExp(String.fromCharCode(10), 'g'); documentHelper.prefix = documentHelper.prefix.replace(nbsp, ' ').replace(lineFeed, ' '); var text = documentHelper.editableDiv.textContent.replace(nbsp, ' ').replace(lineFeed, ' '); var textBoxText = text.substring(2); if (documentHelper.isCompositionStart && documentHelper.isCompositionUpdated) { documentHelper.isCompositionUpdated = false; if (!documentHelper.owner.isReadOnlyMode && documentHelper.owner.isDocumentLoaded && _this.canEditContentControl) { if (documentHelper.prefix.substring(2) !== textBoxText) { if (_this.selection.isEmpty) { /* eslint-disable-next-line max-len */ _this.selection.start.setPositionForLineWidget(documentHelper.selection.start.currentWidget, _this.selection.start.offset - (documentHelper.prefix.length - 2)); _this.handleTextInput(textBoxText); documentHelper.prefix = '@' + String.fromCharCode(160) + textBoxText; } else { _this.handleTextInput(textBoxText); documentHelper.prefix = '@' + String.fromCharCode(160) + textBoxText; } } } return; } else if (documentHelper.isCompositionStart && documentHelper.isCompositionEnd && documentHelper.suffix === '') { if (documentHelper.prefix.substring(2) !== textBoxText) { if (_this.selection.isEmpty && documentHelper.isCompositionStart) { documentHelper.isCompositionStart = false; /* eslint-disable-next-line max-len */ _this.selection.start.setPositionForLineWidget(documentHelper.selection.start.currentWidget, _this.selection.start.offset - documentHelper.prefix.substring(2).length); _this.selection.retrieveCurrentFormatProperties(); if (documentHelper.suffix === '' || textBoxText === '') { _this.handleTextInput(textBoxText); } } else if (!_this.selection.isEmpty) { documentHelper.isCompositionStart = false; _this.handleTextInput(textBoxText); } } else if (textBoxText === '') { documentHelper.isCompositionStart = false; _this.handleBackKey(); } else if (documentHelper.prefix.substring(2) === textBoxText && documentHelper.suffix === '') { documentHelper.isCompositionStart = false; _this.handleTextInput(' '); } documentHelper.isCompositionEnd = false; return; } else if (documentHelper.isCompositionEnd || documentHelper.isCompositionStart && !documentHelper.isCompositionUpdated) { if (textBoxText.length < documentHelper.prefix.length && /* eslint-disable-next-line max-len */ textBoxText === documentHelper.prefix.substring(2, documentHelper.prefix.length - 1) || documentHelper.editableDiv.innerText.length < 2) { _this.handleBackKey(); return; } else if (documentHelper.suffix !== '' && documentHelper.editableDiv.innerText[documentHelper.editableDiv.innerText.length - 1] !== String.fromCharCode(160)) { documentHelper.isCompositionStart = false; //When cursor is placed in between a word and chosen a word from predicted words. /* eslint-disable-next-line max-len */ _this.selection.start.setPositionForLineWidget(documentHelper.selection.start.currentWidget, _this.selection.start.offset - (documentHelper.prefix.length - 2)); /* eslint-disable-next-line max-len */ _this.selection.end.setPositionForLineWidget(documentHelper.selection.end.currentWidget, _this.selection.end.offset + documentHelper.suffix.length); //Retrieve the character format properties. Since the selection was changed manually. _this.selection.retrieveCurrentFormatProperties(); _this.handleTextInput(textBoxText); return; } } if (text !== '\r' && text !== '\b' && text !== String.fromCharCode(27) && !documentHelper.owner.isReadOnlyMode && documentHelper.isControlPressed === false && _this.canEditContentControl) { if (text === '@' || text[0] !== '@' || text === '' || text.length < documentHelper.prefix.length && textBoxText === documentHelper.prefix.substring(2, documentHelper.prefix.length - 1)) { _this.handleBackKey(); if (documentHelper.editableDiv.innerText.length < 2) { _this.predictText(); } } else if (text.indexOf(documentHelper.prefix) === 0 && text.length > documentHelper.prefix.length) { _this.handleTextInput(text.substring(documentHelper.prefix.length)); } else if (text.indexOf(documentHelper.prefix) === -1 && text[text.length - 1] !== String.fromCharCode(160) && text[text.length - 1] !== ' ') { if ((textBoxText.charAt(0).toLowerCase() + textBoxText.slice(1)) === documentHelper.prefix.substring(2)) { /* eslint-disable-next-line max-len */ _this.selection.start.setPositionParagraph(documentHelper.selection.start.currentWidget, _this.selection.start.offset - (documentHelper.prefix.length - 2)); } _this.handleTextInput(textBoxText); } else if (text.length !== 2) { _this.handleTextInput(' '); } } } else { var text = _this.documentHelper.editableDiv.innerText; if (text !== String.fromCharCode(160)) { if (text !== '\r' && text !== '\b' && text !== String.fromCharCode(27) && !_this.owner.isReadOnlyMode && _this.documentHelper.isControlPressed === false && _this.canEditContentControl) { _this.handleTextInput(text); } } else { _this.handleTextInput(' '); } _this.documentHelper.editableDiv.innerText = ''; } }; /** * Fired on paste. * * @param {ClipboardEvent} event - Specfies clipboard event * @private * @returns {void} */ this.onPaste = function (event) { if (!_this.owner.isReadOnlyMode && _this.canEditContentControl) { _this.pasteInternal(event); } event.preventDefault(); }; this.documentHelper = documentHelper; if (!isNullOrUndefined(this.documentHelper)) { this.tableResize = new TableResizer(this.documentHelper.owner); } this.base64 = new Base64(); } Object.defineProperty(Editor.prototype, "restrictFormatting", { /** * @private * @returns {boolean} - Returns the restrict formatting */ get: function () { return this.documentHelper.isDocumentProtected && (this.documentHelper.restrictFormatting || (!this.documentHelper.restrictFormatting && !this.selection.isSelectionInEditRegion())) && this.documentHelper.protectionType !== 'RevisionsOnly'; }, enumerable: true, configurable: true }); Object.defineProperty(Editor.prototype, "restrictEditing", { /** * @private * @returns {boolean} - Returns the restrict editing */ get: function () { return this.documentHelper.isDocumentProtected && ((this.documentHelper.protectionType === 'ReadOnly' || this.documentHelper.isCommentOnlyMode) && !this.selection.isSelectionInEditRegion() || this.documentHelper.protectionType === 'FormFieldsOnly'); }, enumerable: true, configurable: true }); Object.defineProperty(Editor.prototype, "canEditContentControl", { /** * @private * @returns {boolean} - Returns the can edit content control. */ get: function () { if (this.owner.isReadOnlyMode) { return false; } if (this.selection.checkContentControlLocked()) { return false; } return true; }, enumerable: true, configurable: true }); Object.defineProperty(Editor.prototype, "viewer", { get: function () { if (!isNullOrUndefined(this.owner)) { return this.owner.viewer; } return undefined; }, enumerable: true, configurable: true }); Object.defineProperty(Editor.prototype, "editorHistory", { get: function () { return this.documentHelper.owner.editorHistory; }, enumerable: true, configurable: true }); Object.defineProperty(Editor.prototype, "selection", { get: function () { if (this.documentHelper) { return this.documentHelper.selection; } return undefined; }, enumerable: true, configurable: true }); Object.defineProperty(Editor.prototype, "owner", { get: function () { return this.documentHelper.owner; }, enumerable: true, configurable: true }); Editor.prototype.getModuleName = function () { return 'Editor'; }; /** * Sets the field information for the selected field. * * @param { FieldInfo } fieldInfo – Specifies the field information. * @returns {void} * > Nested field gets replaced completely with the specified field information. */ Editor.prototype.setFieldInfo = function (fieldInfo) { var field = this.selection.getHyperlinkField(true); if (!isNullOrUndefined(field)) { this.selection.selectField(); this.insertField(fieldInfo.code, fieldInfo.result); } }; /** * Inserts the specified field at cursor position. * * @param {string} code Specify the field code. * @param {string} result Specify the field result. * @returns {void} */ Editor.prototype.insertField = function (code, result) { code = HelperMethods.sanitizeString(code); if (!isNullOrUndefined(result)) { result = HelperMethods.sanitizeString(result); } this.isInsertField = true; var fieldCode = code; fieldCode = HelperMethods.trimStart(fieldCode); if (fieldCode.substring(0, 8) === 'NUMPAGES') { this.insertPageCount(result); } else if (fieldCode.substring(0, 4) === 'PAGE') { this.insertPageNumber(result); } else { if (isNullOrUndefined(result)) { if (fieldCode.substring(0, 10) === 'MERGEFIELD') { fieldCode = fieldCode.substring(10).trim(); var index = fieldCode.indexOf('\\*'); result = '«' + fieldCode.substring(0, index).trim() + '»'; } } var paragraph = new ParagraphWidget(); var insertFormat = new WCharacterFormat(); var selectionFormat = this.copyInsertFormat(insertFormat, false); var line = new LineWidget(paragraph); var fieldBegin = new FieldElementBox(0); fieldBegin.characterFormat.mergeFormat(selectionFormat); line.children.push(fieldBegin); var fieldCodeSpan = new TextElementBox(); fieldCodeSpan.characterFormat.mergeFormat(selectionFormat); fieldCodeSpan.text = code; line.children.push(fieldCodeSpan); var fieldSeparator = new FieldElementBox(2); fieldSeparator.characterFormat.mergeFormat(selectionFormat); fieldSeparator.fieldBegin = fieldBegin; fieldBegin.fieldSeparator = fieldSeparator; line.children.push(fieldSeparator); var fieldResultSpan = new TextElementBox(); fieldResultSpan.text = result; fieldResultSpan.characterFormat.mergeFormat(selectionFormat); line.children.push(fieldResultSpan); var fieldEnd = new FieldElementBox(1); fieldEnd.characterFormat.mergeFormat(selectionFormat); fieldEnd.fieldSeparator = fieldSeparator; fieldEnd.fieldBegin = fieldBegin; fieldBegin.fieldEnd = fieldEnd; fieldSeparator.fieldEnd = fieldEnd; line.children.push(fieldEnd); fieldBegin.line = line; paragraph.childWidgets.push(line); this.documentHelper.fields.push(fieldBegin); var section = new BodyWidget(); section.sectionFormat = new WSectionFormat(section); section.childWidgets.push(paragraph); this.pasteContentsInternal([section], false); } this.isInsertField = false; }; /** * @private */ Editor.prototype.isLinkedStyle = function (styleName) { var styleObj = this.documentHelper.styles.findByName(styleName); return !isNullOrUndefined(styleObj.link); }; /** * Applies the specified style for paragraph. * * @param {string} style Specify the style name to apply. * @param {boolean} clearDirectFormatting - Removes manual formatting (formatting not applied using a style) * from the selected text, to match the formatting of the applied style. Default value is false. * @returns {void} */ Editor.prototype.applyStyle = function (style, clearDirectFormatting) { clearDirectFormatting = isNullOrUndefined(clearDirectFormatting) ? false : clearDirectFormatting; var startPosition = undefined; var endPosition = undefined; if (clearDirectFormatting) { this.initComplexHistory('ApplyStyle'); this.setOffsetValue(this.selection); startPosition = this.startOffset; endPosition = this.endOffset; var isSelectionEmpty = this.selection.isEmpty; this.clearFormattingInternal(!this.isLinkedStyle(style)); if (isSelectionEmpty && !this.selection.isEmpty) { this.selection.end.setPositionInternal(this.selection.start); } } var styleObj = this.documentHelper.styles.findByName(style); if (styleObj !== undefined) { if (styleObj instanceof WCharacterStyle && styleObj.type === 'Character') { if (this.selection.isEmpty) { var offset = this.selection.start.offset; var preservedStartPosition = this.selection.start.clone(); var preservedEndPosition = this.selection.end.clone(); this.selection.selectCurrentWord(); if (offset === this.selection.start.offset || offset === this.selection.end.offset - 1) { this.selection.start = preservedStartPosition; this.selection.end = preservedEndPosition; this.selection.characterFormat.copyFormat(styleObj.characterFormat); } else { this.onApplyCharacterFormat('styleName', styleObj, false, true); } } else { this.onApplyCharacterFormat('styleName', styleObj, false, true); } } else { this.onApplyParagraphFormat('styleName', styleObj, false, true); } } else { /* eslint-disable-next-line max-len */ this.documentHelper.owner.parser.parseStyle(JSON.parse(this.getCompleteStyles()), JSON.parse(this.documentHelper.preDefinedStyles.get(style)), this.documentHelper.styles); this.applyStyle(style); } if (this.editorHistory && this.editorHistory.currentHistoryInfo && this.editorHistory.currentHistoryInfo.action === 'ApplyStyle') { this.startOffset = startPosition; this.endOffset = endPosition; this.editorHistory.updateComplexHistory(); } this.startParagraph = undefined; this.endParagraph = undefined; }; // Public Implementation Starts /** * Moves the selected content in the document editor control to clipboard. * * @returns {void} */ Editor.prototype.cut = function () { if (this.owner.isReadOnlyMode || this.selection.isEmpty || !this.canEditContentControl) { return; } this.selection.copySelectedContent(true); this.documentHelper.owner.parser.isCutPerformed = true; }; /** * Inserts the editing region in the current selection range for the specified user. * * @param {string} user Specifies the native rendering * @returns {void} */ Editor.prototype.insertEditingRegion = function (user) { this.insertEditRangeElement(user && user !== '' ? user : 'Everyone'); }; Editor.prototype.enforceProtection = function (credential, restrictFormatType, isReadOnly) { var typeOfProtection; var limitToFormatting; if (typeof (restrictFormatType) === 'boolean') { typeOfProtection = isReadOnly ? 'ReadOnly' : this.documentHelper.protectionType; limitToFormatting = restrictFormatType; } else { limitToFormatting = true; typeOfProtection = restrictFormatType; } this.documentHelper.restrictFormatting = limitToFormatting; this.documentHelper.protectionType = typeOfProtection; this.selection.isHighlightEditRegion = true; this.addProtection(credential, this.documentHelper.protectionType, false); }; Editor.prototype.enforceProtectionAsync = function (credential, restrictFormatType, isReadOnly) { return __awaiter(this, void 0, void 0, function () { var typeOfProtection, limitToFormatting; return __generator(this, function (_a) { switch (_a.label) { case 0: if (typeof (restrictFormatType) === 'boolean') { typeOfProtection = isReadOnly ? 'ReadOnly' : this.documentHelper.protectionType; limitToFormatting = restrictFormatType; } else { limitToFormatting = true; typeOfProtection = restrictFormatType; } this.documentHelper.restrictFormatting = limitToFormatting; this.documentHelper.protectionType = typeOfProtection; this.selection.isHighlightEditRegion = true; return [4 /*yield*/, this.addProtection(credential, this.documentHelper.protectionType, true)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; Editor.prototype.getCommentHierarchicalIndex = function (comment) { var index = ''; while (comment.ownerComment) { if (!isNullOrUndefined(comment.ownerComment)) { index = comment.ownerComment.replyComments.indexOf(comment) + ';' + index; comment = comment.ownerComment; } else { index = comment.replyComments.indexOf(comment) + ';' + index; comment = comment; } } index = 'C;' + this.documentHelper.comments.indexOf(comment) + ';' + index; return index; }; Editor.prototype.alertBox = function () { var localObj = new L10n('documenteditor', this.owner.defaultLocale); localObj.setLocale(this.owner.locale); DialogUtility.alert({ title: localObj.getConstant('Information'), content: localObj.getConstant('Multiple Comment') }); }; /** * Inserts the comment. * * @param {string} text Specify the comment text to be inserted. * @returns {void} */ // Comment implementation starts Editor.prototype.insertComment = function (text) { if (isNullOrUndefined(this.selection.start) || (this.owner.isReadOnlyMode && !this.documentHelper.isCommentOnlyMode) || this.viewer.owner.enableHeaderAndFooter || !this.viewer.owner.enableComment) { return; } if (this.viewer.owner.commentReviewPane.commentPane.isEditMode) { return this.alertBox(); } if (isNullOrUndefined(text)) { text = ''; } this.insertCommentInternal(text); }; Editor.prototype.insertCommentInternal = function (text) { this.documentHelper.layout.allowLayout = false; if (this.selection.isEmpty) { // If selection is at paragraph end, move selection to previous word similar to MS Word if (this.selection.start.isAtSamePosition(this.selection.end) && this.selection.start.isAtParagraphEnd) { var startOffset = this.selection.start.offset; this.selection.start.offset = startOffset - 1 !== -1 ? startOffset - 1 : startOffset; } this.selection.selectCurrentWord(); } // If paragraph mark selected, remove paragraph mark selection if (this.selection.isParagraphLastLine(this.selection.end.currentWidget) && this.selection.end.offset === this.selection.getLineLength(this.selection.end.currentWidget) + 1) { this.selection.end.offset -= 1; } var paragraphInfo = this.selection.getParagraphInfo(this.selection.start); var startIndex = this.selection.getHierarchicalIndex(paragraphInfo.paragraph, paragraphInfo.offset.toString()); var endParagraphInfo = this.selection.getParagraphInfo(this.selection.end); var endIndex = this.selection.getHierarchicalIndex(endParagraphInfo.paragraph, endParagraphInfo.offset.toString()); this.initComplexHistory('InsertComment'); var startPosition = this.selection.start; var endPosition = this.selection.end; var position = new TextPosition(this.owner); if (!this.selection.isForward) { startPosition = this.selection.end; endPosition = this.selection.start; } // Clones the end position. position.setPositionInternal(endPosition); var commentRangeStart = new CommentCharacterElementBox(0); var commentRangeEnd = new CommentCharacterElementBox(1); var isSameLine = startPosition.currentWidget === endPosition.currentWidget; // Adds comment start at selection start position. endPosition.setPositionInternal(startPosition); this.initInsertInline(commentRangeStart); if (isNullOrUndefined(position.paragraph) || (position.currentWidget && position.currentWidget.children.length === 0 && position.currentWidget.indexInOwner === -1)) { var endPos = this.selection.getTextPosBasedOnLogicalIndex(endIndex); position.setPositionInternal(endPos); } // Updates the cloned position, since comment start is added in the same line. if (isSameLine) { position.setPositionParagraph(position.currentWidget, position.offset + commentRangeStart.length); } // Adds comment end and comment at selection end position. startPosition.setPositionInternal(position); endPosition.setPositionInternal(position); this.initInsertInline(commentRangeEnd); var commentAdv = new CommentElementBox(HelperMethods.getUtcDate()); if (this.owner.editorHistory) { this.initHistory('InsertCommentWidget'); this.owner.editorHistory.currentBaseHistoryInfo.insertedText = CONTROL_CHARACTERS.Marker_Start + CONTROL_CHARACTERS.Marker_End; this.owner.editorHistory.currentBaseHistoryInfo.removedNodes.push(commentAdv); } var markerData = { author: this.owner.currentUser ? this.owner.currentUser : 'Guest user', initial: this.constructCommentInitial(commentAdv.author), text: SanitizeHtmlHelper.sanitize(text), commentId: Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15), }; this.updateCommentElement(commentAdv, commentRangeStart, commentRangeEnd, markerData); this.addCommentWidget(commentAdv, true, true, true); if (this.editorHistory) { this.editorHistory.currentBaseHistoryInfo.insertPosition = this.getCommentHierarchicalIndex(commentAdv); this.editorHistory.updateHistory(); } // this.selection.selectPosition(this.selection.getTextPosBasedOnLogicalIndex(startIndex), this.selection.getTextPosBasedOnLogicalIndex(endIndex)); if (this.editorHistory) { this.editorHistory.updateComplexHistory(); } this.reLayout(this.selection, false); this.documentHelper.layout.allowLayout = true; if (!this.isUserInsert) { var comment = this.owner.commentReviewPane.commentPane.comments.get(commentAdv); comment.postComment(); } }; /** * @private */ Editor.prototype.updateCommentElement = function (commentAdv, commentRangeStart, commentRangeEnd, markerData) { commentAdv.author = markerData.author; commentAdv.initial = markerData.initial; commentAdv.text = markerData.text; commentAdv.commentId = markerData.commentId; if (!isNullOrUndefined(markerData.done)) { commentAdv.isResolved = markerData.done; } if (!isNullOrUndefined(markerData.isReply)) { commentAdv.isReply = markerData.isReply; } commentRangeStart.comment = commentAdv; commentRangeStart.commentId = commentAdv.commentId; commentRangeEnd.comment = commentAdv; commentRangeEnd.commentId = commentAdv.commentId; commentAdv.commentStart = commentRangeStart; commentAdv.commentEnd = commentRangeEnd; return commentAdv; }; /** * Deletes all the comments in the current document. * * @returns {void} */ Editor.prototype.deleteAllComments = function () { if (this.documentHelper.comments.length === 0) { return; } // this.documentHelper.clearSearchHighlight(); this.initComplexHistory('DeleteAllComments'); this.owner.isLayoutEnabled = false; var historyInfo; if (this.editorHistory && this.editorHistory.currentHistoryInfo) { historyInfo = this.editorHistory.currentHistoryInfo; } while (this.documentHelper.comments.length > 0) { var comment = this.documentHelper.comments[0]; this.initComplexHistory('DeleteComment'); this.deleteCommentInternal(comment); if (this.editorHistory && this.editorHistory.currentHistoryInfo) { historyInfo.addModifiedAction(this.editorHistory.currentHistoryInfo); } } this.selection.selectContent(this.owner.documentStart, true); if (this.editorHistory) { this.editorHistory.currentHistoryInfo = historyInfo; this.editorHistory.updateComplexHistory(); } }; /** * Deletes the current selected comment. * * @returns {void} */ Editor.prototype.deleteComment = function () { if ((this.owner.isReadOnlyMode && !this.documentHelper.isCommentOnlyMode) || isNullOrUndefined(this.owner) || isNullOrUndefined(this.owner.viewer) || isNullOrUndefined(this.owner.documentHelper.currentSelectedComment) || this.owner.enableHeaderAndFooter || !this.viewer.owner.enableComment) { return; } this.deleteCommentInternal(this.owner.documentHelper.currentSelectedComment); }; /** * @param {CommentElementBox} comment - Specified the comment element box * @private * @returns {void} */ Editor.prototype.deleteCommentInternal = function (comment) { this.initComplexHistory('DeleteComment'); if (comment) { if (comment.replyComments.length > 0) { for (var i = comment.replyComments.length - 1; i >= 0; i--) { this.deleteCommentInternal(comment.replyComments[i]); } } this.deleteCommentWidgetInternal(comment); var commentStart = comment.commentStart; var commentEnd = comment.commentEnd; if (commentEnd.indexInOwner !== -1) { this.removeInline(commentEnd); } if (commentStart.indexInOwner !== -1) { this.removeInline(commentStart); } commentStart.removeCommentMark(); } if (this.editorHistory) { this.editorHistory.updateComplexHistory(); } }; Editor.prototype.deleteCommentWidgetInternal = function (comment) { if (this.owner.editorHistory) { this.initHistory('DeleteCommentWidget'); this.owner.editorHistory.currentBaseHistoryInfo.insertPosition = this.getCommentHierarchicalIndex(comment); this.owner.editorHistory.currentBaseHistoryInfo.removedNodes.push(comment); } this.deleteCommentWidget(comment); if (this.editorHistory) { this.editorHistory.updateHistory(); } }; /** * @param {CommentElementBox} comment - Specified the comment element box * @private * @returns {void} */ Editor.prototype.deleteCommentWidget = function (comment) { var commentIndex = this.documentHelper.comments.indexOf(comment); if (commentIndex !== -1) { this.documentHelper.comments.splice(commentIndex, 1); } else if (comment.isReply && comment.ownerComment) { commentIndex = comment.ownerComment.replyComments.indexOf(comment); comment.ownerComment.replyComments.splice(commentIndex, 1); } if (this.owner.commentReviewPane) { this.owner.commentReviewPane.deleteComment(comment); if (this.documentHelper.currentSelectedComment === comment) { this.documentHelper.currentSelectedComment = undefined; } } }; /** * @param {CommentElementBox} comment - Specified the comment element box * @private * @returns {void} */ Editor.prototype.resolveComment = function (comment) { if (this.owner.isReadOnlyMode && !this.documentHelper.isCommentOnlyMode) { return; } var eventArgs = { author: comment.author, cancel: false, type: 'Resolve' }; this.owner.trigger(beforeCommentActionEvent, eventArgs); if (eventArgs.cancel && eventArgs.type === 'Resolve') { return; } this.initHistory('ResolveComment'); if (this.editorHistory && this.editorHistory.currentBaseHistoryInfo) { this.editorHistory.currentBaseHistoryInfo.removedNodes.push(comment); } this.resolveOrReopenComment(comment, true); }; /** * @param {CommentElementBox} comment - Specified the comment element box * @private * @returns {void} */ Editor.prototype.reopenComment = function (comment) { if (this.owner.isReadOnlyMode && !this.documentHelper.isCommentOnlyMode) { return; } var eventArgs = { author: comment.author, cancel: false, type: 'Reopen' }; this.owner.trigger(beforeCommentActionEvent, eventArgs); if (eventArgs.cancel && eventArgs.type === 'Reopen') { return; } this.initHistory('ResolveComment'); if (this.editorHistory && this.editorHistory.currentBaseHistoryInfo) { this.editorHistory.currentBaseHistoryInfo.removedNodes.push(comment); } this.resolveOrReopenComment(comment, false); }; /** * @private */ Editor.prototype.resolveOrReopenComment = function (comment, resolve) { comment.isResolved = resolve; for (var i = 0; i < comment.replyComments.length; i++) { comment.replyComments[i].isResolved = resolve; } if (this.owner.commentReviewPane) { if (resolve) { this.owner.commentReviewPane.resolveComment(comment); } else { this.owner.commentReviewPane.reopenComment(comment); } } this.reLayout(this.selection, false, false); }; /** * @param {CommentElementBox} parentComment - Specified the parent comment * @param {string} text - Specified the text. * @private * @returns {void} */ Editor.prototype.replyComment = function (parentComment, text) { if (this.owner.isReadOnlyMode && !this.documentHelper.isCommentOnlyMode) { return; } var commentWidget = parentComment; if (parentComment) { this.initComplexHistory('InsertComment'); var currentCmtStart = commentWidget.commentStart; var currentCmtEnd = commentWidget.commentEnd; var offset = currentCmtStart.line.getOffset(currentCmtStart, 1); var startPosition = new TextPosition(this.documentHelper.owner); startPosition.setPositionParagraph(currentCmtStart.line, offset); var endOffset = currentCmtEnd.line.getOffset(currentCmtEnd, 1); var endPosition = new TextPosition(this.documentHelper.owner); endPosition.setPositionParagraph(currentCmtEnd.line, endOffset); this.selection.start.setPositionInternal(startPosition); this.selection.end.setPositionInternal(endPosition); startPosition = this.selection.start; endPosition = this.selection.end; var position = new TextPosition(this.owner); // Clones the end position. position.setPositionInternal(endPosition); var commentRangeStart = new CommentCharacterElementBox(0); var commentRangeEnd = new CommentCharacterElementBox(1); var isAtSameLine = startPosition.currentWidget === endPosition.currentWidget; // Adds comment start at selection start position. endPosition.setPositionInternal(startPosition); var lineIndex = position.currentWidget.indexInOwner; this.initInsertInline(commentRangeStart); if (position.currentWidget.indexInOwner === -1) { position.currentWidget = position.currentWidget.paragraph.childWidgets[lineIndex]; } // Updates the cloned position, since comment start is added in the same paragraph. if (isAtSameLine) { position.setPositionParagraph(position.currentWidget, position.offset + commentRangeStart.length); } // Adds comment end and comment at selection end position. startPosition.setPositionInternal(position); endPosition.setPositionInternal(position); this.initInsertInline(commentRangeEnd); var replyComment = new CommentElementBox(HelperMethods.getUtcDate()); replyComment.author = this.owner.currentUser ? this.owner.currentUser : 'Guest user'; replyComment.text = text ? text : ''; replyComment.commentId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); replyComment.isReply = true; commentWidget.replyComments.push(replyComment); replyComment.ownerComment = commentWidget; if (this.owner.editorHistory) { this.initHistory('InsertCommentWidget'); this.owner.editorHistory.currentBaseHistoryInfo.removedNodes.push(replyComment); this.owner.editorHistory.currentBaseHistoryInfo.insertedText = CONTROL_CHARACTERS.Marker_Start + CONTROL_CHARACTERS.Marker_End; } commentRangeStart.comment = replyComment; commentRangeStart.commentId = replyComment.commentId; commentRangeEnd.comment = replyComment; commentRangeEnd.commentId = replyComment.commentId; replyComment.commentStart = commentRangeStart; replyComment.commentEnd = commentRangeEnd; if (this.owner.commentReviewPane) { this.owner.commentReviewPane.addReply(replyComment, false, true); } if (this.editorHistory) { this.editorHistory.currentBaseHistoryInfo.insertPosition = this.getCommentHierarchicalIndex(replyComment); this.editorHistory.updateHistory(); } if (this.editorHistory) { this.editorHistory.updateComplexHistory(); } this.isSkipOperationsBuild = true; this.reLayout(this.selection); } }; Editor.prototype.removeInline = function (element) { this.selection.start.setPositionParagraph(element.line, element.line.getOffset(element, 0)); this.selection.end.setPositionParagraph(this.selection.start.currentWidget, this.selection.start.offset + element.length); this.initHistory('RemoveInline'); if (this.editorHistory && this.editorHistory.currentBaseHistoryInfo) { this.updateHistoryPosition(this.selection.start, true); } this.removeSelectedContents(this.documentHelper.selection); if (this.editorHistory) { this.editorHistory.updateHistory(); } this.fireContentChange(); }; /** * @param {CommentElementBox} commentWidget - Specifies the comment * @param {boolean} isNewComment - Specifies is new comment * @param {boolean} showComments - Specifies show comments * @param {boolean} selectComment - Specified select comment * @private * @returns {void} */ Editor.prototype.addCommentWidget = function (commentWidget, isNewComment, showComments, selectComment) { if (this.documentHelper.comments.indexOf(commentWidget) === -1) { var isInserted = fals