/* * (c) Copyright Ascensio System SIA 2010-2024 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ "use strict"; (function (window, undefined) { function RecalcData() { this.Inline = { Pos: -1, PageNum: 0 }; this.Flow = []; this.HdrFtr = []; this.Drawings = { All: false, Map: {}, ThemeInfo: null, SlideMinIdx: null }; this.Tables = []; this.NumPr = []; this.NotesEnd = false; this.NotesEndPage = 0; this.Update = true; this.ChangedStyles = {}; this.ChangedNums = {}; this.LineNumbers = false; this.ResetCache = false; this.AllParagraphs = null; } /** * Класс локальной истории изменений * @param {AscWord.CDocument} Document * @constructor */ function CHistory(Document) { this.Index = -1; this.SavedIndex = null; // Номер точки отката, на которой произошло последнее сохранение this.ForceSave = false; // Нужно сохранение, случается, когда у нас точка SavedIndex смещается из-за объединения точек, и мы делаем Undo this.RecIndex = -1; // Номер точки, на которой произошел последний пересчет this.Points = []; // Точки истории, в каждой хранится массив с изменениями после текущей точки this.Document = Document; this.Api = null; this.CollaborativeEditing = null; this.GroupChanges = []; this.CanNotAddChanges = false; // флаг для отслеживания ошибок добавления изменений без точки:Create_NewPoint->Add->Save_Changes->Add this.CollectChanges = false; this.UndoRedoInProgress = false; // this.RecalculateData = new RecalcData(); this.TurnOffHistory = 0; this.RegisterClasses = 0; this.MinorChanges = false; // Данный параметр нужен, чтобы определить влияют ли добавленные изменения на пересчет this.BinaryWriter = new AscCommon.CMemory(true); this.BinaryWriter.Init(1024*1024*1); this.FileCheckSum = 0; this.FileSize = 0; // Параметры для специального сохранения для локальной версии редактора this.UserSaveMode = false; this.UserSavedIndex = null; // Номер точки, на которой произошло последнее сохранение пользователем (не автосохранение) this.StoredData = []; this.PosInCurPoint = null; } CHistory.prototype = { Set_LogicDocument : function(LogicDocument) { if (!LogicDocument) return; this.Document = LogicDocument; this.Api = LogicDocument.Get_Api(); this.CollaborativeEditing = LogicDocument.Get_CollaborativeEditing(); }, Is_UserSaveMode : function() { return this.UserSaveMode; }, Update_FileDescription : function(pData, nSize) { this.FileCheckSum = AscCommon.g_oCRC32.Calculate_ByByteArray(pData, nSize); this.FileSize = nSize; }, Update_PointInfoItem : function(PointIndex, StartPoint, LastPoint, SumIndex, DeletedIndex) { var Point = this.Points[PointIndex]; if (Point) { // Проверяем первое изменение. Если оно уже нужного типа, тогда мы его удаляем. Добавляем специфическое // первое изменение с описанием. var Class = AscCommon.g_oTableId; if (Point.Items.length > 0) { var FirstItem = Point.Items[0]; if (FirstItem.Class === Class && AscDFH.historyitem_TableId_Description === FirstItem.Data.Type) Point.Items.splice(0, 1); } var Data = new AscCommon.CChangesTableIdDescription(Class, this.FileCheckSum, this.FileSize, Point.Description, Point.Items.length, PointIndex, StartPoint, LastPoint, SumIndex, DeletedIndex ); var Binary_Pos = this.BinaryWriter.GetCurPosition(); if ((Asc.editor || editor).binaryChanges) { this.BinaryWriter.WriteWithLen(this, function(){ this.BinaryWriter.WriteString2(Class.Get_Id()); this.BinaryWriter.WriteLong(Data.Type); Data.WriteToBinary(this.BinaryWriter); }); } else { this.BinaryWriter.WriteString2(Class.Get_Id()); this.BinaryWriter.WriteLong(Data.Type); Data.WriteToBinary(this.BinaryWriter); } var Binary_Len = this.BinaryWriter.GetCurPosition() - Binary_Pos; var Item = { Class : Class, Data : Data, Binary : { Pos : Binary_Pos, Len : Binary_Len }, NeedRecalc : false }; Point.Items.splice(0, 0, Item); } }, Is_Clear : function() { if ( this.Points.length <= 0 ) return true; return false; }, Clear : function() { this.Index = -1; this.SavedIndex = null; this.ForceSave = false; this.UserSavedIndex = null; this.Points.length = 0; this.TurnOffHistory = 0; this.private_ClearRecalcData(); }, Can_Undo : function() { return (!this.UndoRedoInProgress && this.Index >= 0); }, Can_Redo : function() { return (!this.UndoRedoInProgress && this.Points.length > 0 && this.Index < this.Points.length - 1); }, UndoLastPoint : function(nBottomIndex) { // TODO: Данная функция почему-то НЕ меняет индекс, надо проверить на корректность те места, где она используется let arrChanges = []; var oPoint = this.Points[this.Index]; if(oPoint) { var aItems = oPoint.Items; var _bottomIndex; if(AscFormat.isRealNumber(nBottomIndex)) { _bottomIndex = nBottomIndex - 1; } else { _bottomIndex = -1; } for (var i = aItems.length - 1; i > _bottomIndex; i--) { var oItem = aItems[i]; oItem.Data.Undo(); arrChanges.push(oItem.Data); } this.Document.RecalculateByChanges(arrChanges); oPoint.Items.length = _bottomIndex + 1; this.Document.SetSelectionState( oPoint.State ); } return arrChanges; }, Undo : function(Options) { var arrChanges = []; this.CheckUnionLastPoints(); if (!this.Can_Undo()) return null; this.UndoRedoInProgress = true; // Запоминаем самое последнее состояние документа для Redo if ( this.Index === this.Points.length - 1 && this.Document) this.LastState = this.Document.GetSelectionState(); if (this.Document) this.Document.RemoveSelection(true); var Point = null; if (undefined !== Options && null !== Options && true === Options.All) { while (this.Index >= 0) { Point = this.Points[this.Index--]; this.private_UndoPoint(Point, arrChanges); } } else { Point = this.Points[this.Index--]; this.private_UndoPoint(Point, arrChanges); } if (null != Point && this.Document) this.Document.SetSelectionState( Point.State ); if(!window['AscCommon'].g_specialPasteHelper.specialPasteStart) { window['AscCommon'].g_specialPasteHelper.SpecialPasteButton_Hide(true); } this.UndoRedoInProgress = false; return arrChanges; }, Redo : function() { var arrChanges = []; if (!this.Can_Redo()) return null; this.UndoRedoInProgress = true; if (this.Document) this.Document.RemoveSelection(true); var Point = this.Points[++this.Index]; this.private_RedoPoint(Point, arrChanges); // Восстанавливаем состояние на следующую точку var State = null; if (this.Document) { if ( this.Index === this.Points.length - 1) State = this.LastState; else State = this.Points[this.Index + 1].State; this.Document.SetSelectionState( State ); } if(!window['AscCommon'].g_specialPasteHelper.pasteStart) { window['AscCommon'].g_specialPasteHelper.SpecialPasteButton_Hide(); } this.UndoRedoInProgress = false; return arrChanges; }, /** * Создаем новую точку в истории * @param {number} nDescription - идентификатор производимого действия * @param {object} [oSelectionState=undefined] - сохраненное состояние редактора до начала действия (если не задано используем состояние на текущий момент) * @returns {boolean} */ Create_NewPoint : function(nDescription, oSelectionState) { if ( 0 !== this.TurnOffHistory ) return false; this.RemoveLastTemporaryPoint(); if (this.Document && this.Document.ClearListsCache) this.Document.ClearListsCache(); this.CanNotAddChanges = false; this.CollectChanges = false; if (null !== this.SavedIndex && this.Index < this.SavedIndex) this.Set_SavedIndex(this.Index); this.ClearAdditional(); this.CheckUnionLastPoints(); var State = oSelectionState ? oSelectionState : this.Document ? this.Document.GetSelectionState() : undefined; var Items = []; var Time = new Date().getTime(); // Создаем новую точку this.Points[++this.Index] = { State : State, // Текущее состояние документа (курсор, селект) Items : Items, // Массив изменений, начиная с текущего момента Time : Time, // Текущее время Additional : {}, // Дополнительная информация Temporary : false, Description: nDescription }; // Удаляем ненужные точки this.Points.length = this.Index + 1; if(!window['AscCommon'].g_specialPasteHelper.pasteStart) { window['AscCommon'].g_specialPasteHelper.SpecialPasteButton_Hide(); } return true; }, /** * Специальная функция, для создания точки, чтобы отловить все изменения, которые происходят. После использования * данная точка ДОЛЖНА быть удалена через функцию Remove_LastPoint. * @param {number} description - идентификатор действия */ CreateNewPointToCollectChanges : function(description) { this.Points[++this.Index] = { State : null, Items : [], Time : null, Additional : {}, Description : description ? description : -1 }; this.Points.length = this.Index + 1; this.CollectChanges = true; return this.Index; }, Remove_LastPoint : function() { if (this.Index > -1) { this.CollectChanges = false; this.Index--; this.Points.length = this.Index + 1; } }, Is_LastPointEmpty : function() { if (!this.Points[this.Index] || this.Points[this.Index].Items.length <= 0) return true; return false; }, Is_LastPointNeedRecalc : function() { if (!this.Points[this.Index]) return false; var RecalcData = this.Get_RecalcData(); if (RecalcData.Flow.length > 0 || RecalcData.HdrFtr.length > 0 || -1 !== RecalcData.Inline.Pos || true === RecalcData.Drawings.All) return true; for(var Key in RecalcData.Drawings.Map) { if(null != AscCommon.g_oTableId.Get_ById(Key)) { return true; } } return false; }, Clear_Redo : function() { // Удаляем ненужные точки this.Points.length = this.Index + 1; }, ClearRedo : function() { return this.Clear_Redo(); }, // Регистрируем новое изменение: // Class - объект, в котором оно произошло // Data - сами изменения Add : function(_Class, Data) { if (_Class && _Class.CheckNeedRecalculate) _Class.CheckNeedRecalculate(); if (!this.CanAddChanges()) return; this._CheckCanNotAddChanges(); // Заглушка на случай, если у нас во время создания одной точки в истории, после нескольких изменений идет // пересчет, потом снова добавляются изменения и снова запускается пересчет и т.д. if (this.RecIndex >= this.Index) this.RecIndex = this.Index - 1; var Binary_Pos = this.BinaryWriter.GetCurPosition(); let Class = _Class ? _Class.GetClass() : undefined; if (_Class) { Data = _Class; this.BinaryWriter.WriteString2(Class.Get_Id()); this.BinaryWriter.WriteLong(_Class.Type); _Class.WriteToBinary(this.BinaryWriter); } var Binary_Len = this.BinaryWriter.GetCurPosition() - Binary_Pos; var Item = { Class : Class, Data : Data, Binary : { Pos : Binary_Pos, Len : Binary_Len }, NeedRecalc : !this.MinorChanges && (!_Class || _Class.IsNeedRecalculate() || _Class.IsNeedRecalculateLineNumbers()) }; this.Points[this.Index].Items.push(Item); if (!this.CollaborativeEditing || !_Class) return; if (_Class.IsContentChange() && this.CollaborativeEditing.IsTrackingPositions()) { var bAdd = _Class.IsAdd(); var Count = _Class.GetItemsCount(); var ContentChanges = new AscCommon.CContentChangesElement(bAdd == true ? AscCommon.contentchanges_Add : AscCommon.contentchanges_Remove, Data.Pos, Count, Item); Class.Add_ContentChanges(ContentChanges); this.CollaborativeEditing.Add_NewDC(Class); if (true === bAdd) this.CollaborativeEditing.Update_DocumentPositionsOnAdd(Class, Data.Pos); else this.CollaborativeEditing.Update_DocumentPositionsOnRemove(Class, Data.Pos, Count); } if (_Class.IsPosExtChange()) { this.CollaborativeEditing.AddPosExtChanges(Item, _Class); } }, RecalcData_Add : function(Data) { if (true !== this.RecalculateData.Update) return; if ( "undefined" === typeof(Data) || null === Data ) return; switch ( Data.Type ) { case AscDFH.historyitem_recalctype_Flow: { var bNew = true; for ( var Index = 0; Index < this.RecalculateData.Flow.length; Index++ ) { if ( this.RecalculateData.Flow[Index] === Data.Data ) { bNew = false; break; } } if ( true === bNew ) this.RecalculateData.Flow.push( Data.Data ); break; } case AscDFH.historyitem_recalctype_HdrFtr: { if ( null === Data.Data ) break; var bNew = true; for ( var Index = 0; Index < this.RecalculateData.HdrFtr.length; Index++ ) { if ( this.RecalculateData.HdrFtr[Index] === Data.Data ) { bNew = false; break; } } if ( true === bNew ) this.RecalculateData.HdrFtr.push( Data.Data ); break } case AscDFH.historyitem_recalctype_Inline: { if ( (Data.Data.Pos < this.RecalculateData.Inline.Pos) || (Data.Data.Pos === this.RecalculateData.Inline.Pos && Data.Data.PageNum < this.RecalculateData.Inline.PageNum) || this.RecalculateData.Inline.Pos < 0 ) { this.RecalculateData.Inline.Pos = Data.Data.Pos; this.RecalculateData.Inline.PageNum = Data.Data.PageNum; } break; } case AscDFH.historyitem_recalctype_Drawing: { let oDrawings = this.RecalculateData.Drawings; if(!oDrawings.All) { if(Data.All) { oDrawings.All = true; } else { if(Data.Theme) { oDrawings.ThemeInfo = { Theme: true, ThemeObj: Data.ThemeObj } } else if(AscFormat.isRealNumber(Data.SlideMinIdx)) { if(AscFormat.isRealNumber(oDrawings.SlideMinIdx)) { oDrawings.SlideMinIdx = Math.min(Data.SlideMinIdx, oDrawings.SlideMinIdx); } else { oDrawings.SlideMinIdx = Data.SlideMinIdx; } } else { this.RecalculateData.Drawings.Map[Data.Object.Get_Id()] = Data.Object; } } } break; } case AscDFH.historyitem_recalctype_NotesEnd: { this.RecalculateData.NotesEnd = true; this.RecalculateData.NotesEndPage = Data.PageNum; break; } case AscDFH.historyitem_recalctype_FromStart: { this.RecalculateData.Inline.Pos = 0; this.RecalculateData.Inline.PageNum = 0; this.RecalculateData.Flow = []; this.RecalculateData.HdrFtr = []; this.RecalculateData.Drawings.All = true; this.RecalculateData.Drawings.Map = {}; this.RecalculateData.Tables = []; this.RecalculateData.NumPr = []; this.RecalculateData.NotesEnd = false; this.RecalculateData.NotesEndPage = 0; this.RecalculateData.ResetCache = true; } } }, AddChangedStyleToRecalculateData : function(sId, oStyle) { if (!this.RecalculateData.ChangedStyles) this.RecalculateData.ChangedStyles = {}; if (this.RecalculateData.ChangedStyles[sId] === oStyle) return false; this.RecalculateData.ChangedStyles[sId] = oStyle; return true; }, AddChangedNumberingToRecalculateData : function(NumId, Lvl, oNum) { if(this.Document && this.Document.IsDocumentEditor()) { if (!this.RecalculateData.ChangedNums) this.RecalculateData.ChangedNums = {}; if (!this.RecalculateData.ChangedNums[NumId]) this.RecalculateData.ChangedNums[NumId] = {}; if (this.RecalculateData.ChangedNums[NumId][Lvl] === oNum) return false; this.RecalculateData.ChangedNums[NumId][Lvl] = oNum; return true; } return false; }, Add_RecalcNumPr : function(NumPr) { if(this.Document && this.Document.IsDocumentEditor()) { if (undefined !== NumPr && null !== NumPr && undefined !== NumPr.NumId) this.RecalculateData.NumPr[NumPr.NumId] = true; } }, Add_RecalcTableGrid : function(TableId) { this.RecalculateData.Tables[TableId] = true; }, AddLineNumbersToRecalculateData : function() { this.RecalculateData.LineNumbers = true; }, OnEnd_GetRecalcData : function() { // Пересчитываем таблицы for (let tableId in this.RecalculateData.Tables) { let table = AscCommon.g_oTableId.Get_ById(tableId); if (table && table.IsUseInDocument() && table.IsChangedTableGrid()) { table.Refresh_RecalcData2(0, 0); } } // Делаем это, чтобы пересчитались ячейки таблиц, в которых есть заданная нумерация. Но нам не нужно менять // начальную точку пересчета здесь, т.к. начальная точка уже рассчитана правильно. this.RecalculateData.Update = false; for (var NumId in this.RecalculateData.NumPr) { var NumPr = new AscWord.NumPr(); NumPr.NumId = NumId; for (var Lvl = 0; Lvl < 9; ++Lvl) { NumPr.Lvl = Lvl; var AllParagraphs = this.Document.GetAllParagraphsByNumbering(NumPr); var Count = AllParagraphs.length; for (var Index = 0; Index < Count; ++Index) { var Para = AllParagraphs[Index]; Para.Refresh_RecalcData2(0); } } } this.RecalculateData.NumPr = []; this.RecalculateData.Update = true; }, CheckUnionLastPoints : function() { // Не объединяем точки в истории, когда отключается пересчет. // TODO: Неправильно изменяется RecalcIndex if (this.Document && (!this.Document.Is_OnRecalculate() || this.Document.IsActionStarted())) return false; // Не объединяем точки во время Undo/Redo if (this.Index < this.Points.length - 1) return false; // Не объединяем точки истории, если на предыдущей точке произошло сохранение if (this.Points.length < 2 || (true !== this.Is_UserSaveMode() && null !== this.SavedIndex && this.SavedIndex >= this.Points.length - 2) || (true === this.Is_UserSaveMode() && null !== this.UserSavedIndex && this.UserSavedIndex >= this.Points.length - 2)) return false; var Point1 = this.Points[this.Points.length - 2]; var Point2 = this.Points[this.Points.length - 1]; if (Point1.Additional.FormFilling !== Point2.Additional.FormFilling) return false; // Не объединяем слова больше 63 элементов if (Point1.Items.length > 63 && AscDFH.historydescription_Document_AddLetterUnion === Point1.Description) return false; var StartIndex1 = 0; var StartIndex2 = 0; if (Point1.Items.length > 0 && Point1.Items[0].Data && AscDFH.historyitem_TableId_Description === Point1.Items[0].Data.Type) StartIndex1 = 1; if (Point2.Items.length > 0 && Point2.Items[0].Data && AscDFH.historyitem_TableId_Description === Point2.Items[0].Data.Type) StartIndex2 = 1; var NewDescription; if ((AscDFH.historydescription_Document_CompositeInput === Point1.Description || AscDFH.historydescription_Document_CompositeInputReplace === Point1.Description) && AscDFH.historydescription_Document_CompositeInputReplace === Point2.Description) { // Ничего не делаем. Эта ветка означает, что эти две точки можно объединить NewDescription = Point1.Description; } else if (AscDFH.historydescription_Document_CompositeInput === Point1.Description || AscDFH.historydescription_Document_CompositeInputReplace === Point1.Description || AscDFH.historydescription_Document_CompositeInput === Point2.Description || AscDFH.historydescription_Document_CompositeInputReplace === Point2.Description) { // Композитный ввод не разрешаем объединять ни с чем, кроме композитного ввода return false; } else if ((AscDFH.historydescription_Document_AddLetter === Point1.Description || AscDFH.historydescription_Document_AddLetterUnion === Point1.Description) && (AscDFH.historydescription_Document_AddLetter === Point2.Description || AscDFH.historydescription_Document_AddLetterUnion === Point2.Description)) { var PrevItem = null; var Class = null; for (var Index = StartIndex1; Index < Point1.Items.length; Index++) { var Item = Point1.Items[Index]; if (null === Class) Class = Item.Class; else if (Class != Item.Class || "undefined" === typeof(Class.Check_HistoryUninon) || false === Class.Check_HistoryUninon(PrevItem.Data, Item.Data)) return; PrevItem = Item; } for (var Index = StartIndex2; Index < Point2.Items.length; Index++) { var Item = Point2.Items[Index]; if (Class != Item.Class || "undefined" === typeof(Class.Check_HistoryUninon) || false === Class.Check_HistoryUninon(PrevItem.Data, Item.Data)) return; PrevItem = Item; } NewDescription = AscDFH.historydescription_Document_AddLetterUnion; } else { return false; } if (0 !== StartIndex1) Point1.Items.splice(0, 1); if (0 !== StartIndex2) Point2.Items.splice(0, 1); var NewPoint = { State : Point1.State, Items : Point1.Items.concat(Point2.Items), Time : Point1.Time, Additional : {}, Description: NewDescription }; let oForm = Point1.Additional.FormFilling; if (oForm) NewPoint.Additional.FormFilling = oForm; if (null !== this.SavedIndex && this.SavedIndex >= this.Points.length - 2) this.Set_SavedIndex(this.Points.length - 3); this.Points.splice( this.Points.length - 2, 2, NewPoint ); if ( this.Index >= this.Points.length ) { var DiffIndex = -this.Index + (this.Points.length - 1); this.Index += DiffIndex; this.RecIndex = Math.max( -1, this.RecIndex + DiffIndex); } return true; }, CanRemoveLastPoint : function(pointsCount) { if (undefined === pointsCount || null === pointsCount) pointsCount = 1; if (this.Points.length < pointsCount || (true !== this.Is_UserSaveMode() && null !== this.SavedIndex && this.SavedIndex >= this.Points.length - pointsCount) || (true === this.Is_UserSaveMode() && null !== this.UserSavedIndex && this.UserSavedIndex >= this.Points.length - pointsCount)) return false; return true; }, /** @returns {boolean} */ Is_On : function() { return (0 === this.TurnOffHistory); }, /** @returns {boolean} */ IsOn : function() { return (0 === this.TurnOffHistory); }, Reset_SavedIndex : function(IsUserSave) { this.SavedIndex = (null === this.SavedIndex && -1 === this.Index ? null : this.Index); if (true === this.Is_UserSaveMode()) { if (true === IsUserSave) { this.UserSavedIndex = this.Index; this.ForceSave = false; } } else { this.ForceSave = false; } }, Set_SavedIndex : function(Index) { this.SavedIndex = Index; if (true === this.Is_UserSaveMode()) { if (null !== this.UserSavedIndex && this.UserSavedIndex > this.SavedIndex) { this.UserSavedIndex = Index; this.ForceSave = true; } } else { this.ForceSave = true; } }, Have_Changes : function(IsNotUserSave) { if (!this.Document || this.Document.IsViewModeInReview()) return false; var checkIndex = (this.Is_UserSaveMode() && !IsNotUserSave) ? this.UserSavedIndex : this.SavedIndex; if (-1 === this.Index && null === checkIndex && false === this.ForceSave) return false; if (this.Index != checkIndex || true === this.ForceSave) return true; return false; }, Get_RecalcData : function(oRecalcData, arrChanges, nChangeStart, nChangeEnd) { if (oRecalcData) { this.RecalculateData = oRecalcData; } else if (arrChanges) { this.private_ClearRecalcData(); var nStart = undefined !== nChangeStart ? nChangeStart : 0; var nEnd = undefined !== nChangeEnd ? nChangeEnd : arrChanges.length - 1; for (var nIndex = nStart; nIndex <= nEnd; ++nIndex) { var oChange = arrChanges[nIndex]; oChange.RefreshRecalcData(); } this.private_PostProcessingRecalcData(); } else { if (this.Index >= 0 || this.GroupChanges.length) { this.private_ClearRecalcData(); for (let i = 0; i < this.GroupChanges.length; ++i) { this.GroupChanges[i].RefreshRecalcData(); } for (var Pos = this.RecIndex + 1; Pos <= this.Index; Pos++) { // Считываем изменения, начиная с последней точки, и смотрим что надо пересчитать. var Point = this.Points[Pos]; // Выполняем все действия в прямом порядке for (var Index = 0; Index < Point.Items.length; Index++) { var Item = Point.Items[Index]; if (true === Item.NeedRecalc) Item.Class.Refresh_RecalcData(Item.Data); } } this.private_PostProcessingRecalcData(); } } this.OnEnd_GetRecalcData(); return this.RecalculateData; }, Reset_RecalcIndex : function() { this.RecIndex = this.Index; this.GroupChanges = []; }, Set_Additional_ExtendDocumentToPos : function() { if ( this.Index >= 0 ) { this.Points[this.Index].Additional.ExtendDocumentToPos = true; } }, Is_ExtendDocumentToPos : function() { if ( undefined === this.Points[this.Index] || undefined === this.Points[this.Index].Additional || undefined === this.Points[this.Index].Additional.ExtendDocumentToPos ) return false; return true; }, Get_EditingTime : function(dTime) { var Count = this.Points.length; var TimeLine = []; for ( var Index = 0; Index < Count; Index++ ) { var PointTime = this.Points[Index].Time; TimeLine.push( { t0 : PointTime - dTime, t1 : PointTime } ); } Count = TimeLine.length; for ( var Index = 1; Index < Count; Index++ ) { var CurrEl = TimeLine[Index]; var PrevEl = TimeLine[Index - 1]; if ( CurrEl.t0 <= PrevEl.t1 ) { PrevEl.t1 = CurrEl.t1; TimeLine.splice( Index, 1 ); Index--; Count--; } } Count = TimeLine.length; var OverallTime = 0; for ( var Index = 0; Index < Count; Index++ ) { OverallTime += TimeLine[Index].t1 - TimeLine[Index].t0; } return OverallTime; }, Get_DocumentPositionBinary : function() { var PosInfo = this.Document.Get_DocumentPositionInfoForCollaborative(); return AscCommon.CollaborativeEditing.GetDocumentPositionBinary(this.BinaryWriter, PosInfo); }, _CheckCanNotAddChanges : function() { try { if (this.CanNotAddChanges && this.Api && !this.CollectChanges) { this.CanNotAddChanges = false; var tmpErr = new Error(); if (tmpErr.stack) { AscCommon.sendClientLog("error", "changesError: " + tmpErr.stack, this.Api); } } } catch (e) { } } }; CHistory.prototype.GetLocalChangesSize = function() { function GetDescriptionSize() { let change = new AscCommon.CChangesTableIdDescription(AscCommon.g_oTableId, 0, 0, 0, 0, 0, 0, 0, 0, 0); let id = "" + AscCommon.g_oTableId.GetId(); return (4 + ((id.length & 0x7FFFFFFF) * 2) + 4 + change.GetBinarySize()); } function GetBase64Size(binarySize) { // Бинарник пишется Binary.Len + ";" + base64Encode(Binary.Data) return ((binarySize + ";").length + (((4 * binarySize / 3) + 3) & ~3)); } let descriptionSize = GetBase64Size(GetDescriptionSize()); let size = 0; let startIndex = null !== this.SavedIndex ? this.SavedIndex + 1 : 0; for (let pointIndex = startIndex; pointIndex <= this.Index; ++pointIndex) { let point = this.Points[pointIndex]; let firstItem = point.Items[0]; if (!firstItem || !firstItem.Data.IsDescriptionChange()) size += descriptionSize; for (let itemIndex = 0, itemsCount = point.Items.length; itemIndex < itemsCount; ++itemIndex) { let item = point.Items[itemIndex]; size += GetBase64Size(item.Binary.Len); } } return size; }; /** * Проверяем, можно ли добавить изменение * @returns {boolean} */ CHistory.prototype.CanAddChanges = function() { return (0 === this.TurnOffHistory && this.Index >= 0); }; CHistory.prototype.CanRegisterClasses = function() { return (0 === this.TurnOffHistory || this.RegisterClasses >= this.TurnOffHistory); }; CHistory.prototype.TurnOff = function() { this.TurnOffHistory++; }; CHistory.prototype.TurnOn = function() { this.TurnOffHistory--; if(this.TurnOffHistory < 0) this.TurnOffHistory = 0; }; CHistory.prototype.TurnOffChanges = function() { this.TurnOffHistory++; this.RegisterClasses++; }; CHistory.prototype.TurnOnChanges = function() { this.TurnOffHistory--; if(this.TurnOffHistory < 0) this.TurnOffHistory = 0; this.RegisterClasses--; if (this.RegisterClasses < 0) this.RegisterClasses = 0; }; CHistory.prototype.SetAdditionalFormFilling = function(oForm, nCount) { if (undefined === nCount) nCount = 1; for (let nIndex = this.Index; nIndex > this.Index - nCount && nIndex >= 0; --nIndex) { this.Points[nIndex].Additional.FormFilling = oForm; } }; CHistory.prototype.GetLastPointFormFilling = function() { let additional = this.Index >= 0 ? this.Points[this.Index].Additional : null; return (additional && additional.FormFilling ? additional.FormFilling : null); }; CHistory.prototype.ClearFormFillingInfo = function() { if (this.Points[this.Index] && this.Points[this.Index].Additional.FormFilling) delete this.Points[this.Index].Additional.FormFilling; }; CHistory.prototype.SetLastPointTemporary = function() { if (this.Index < 0) return; this.Points[this.Index].Temporary = true; }; CHistory.prototype.RemoveLastTemporaryPoint = function() { if (!this.Document || this.Index < 0 || !this.Document.IsDocumentEditor() || !this.Points[this.Index].Temporary) return; let needOn = this.Document.TurnOff_InterfaceEvents(); let changes = this.Undo(); this.Document.UpdateAfterUndoRedo(changes); if (needOn) this.Document.TurnOn_InterfaceEvents(); }; CHistory.prototype.ClearAdditional = function() { if (this.Index >= 0) { // TODO: На создании новой точки не удаляем информацию о заполнении формы // надо переназвать функции по-нормальному let form = this.GetLastPointFormFilling(); this.Points[this.Index].Additional = {}; if (form) this.SetAdditionalFormFilling(form); } if(this.Api) { if (true === this.Api.isMarkerFormat) this.Api.sync_MarkerFormatCallback(false); if (true === this.Api.isDrawTablePen) this.Api.sync_TableDrawModeCallback(false); if (true === this.Api.isDrawTableErase) this.Api.sync_TableEraseModeCallback(false); if(this.Api.isEyedropperStarted()) this.Api.cancelEyedropper(); if(this.Api.isInkDrawerOn()) this.Api.stopInkDrawer(); } }; CHistory.prototype.private_UpdateContentChangesOnUndo = function(Item) { if (this.private_IsContentChange(Item.Class, Item.Data)) { Item.Class.m_oContentChanges.RemoveByHistoryItem(Item); } }; CHistory.prototype.private_UpdateContentChangesOnRedo = function(Item) { if (this.private_IsContentChange(Item.Class, Item.Data)) { var bAdd = this.private_IsAddContentChange(Item.Class, Item.Data); var Count = this.private_GetItemsCountInContentChange(Item.Class, Item.Data); var ContentChanges = new AscCommon.CContentChangesElement(( bAdd == true ? AscCommon.contentchanges_Add : AscCommon.contentchanges_Remove ), Item.Data.Pos, Count, Item); Item.Class.Add_ContentChanges(ContentChanges); if (this.CollaborativeEditing) this.CollaborativeEditing.Add_NewDC(Item.Class); } }; CHistory.prototype.private_IsContentChange = function(Class, Data) { // TODO: Заменить на проверку через change.IsContentChange var bPresentation = !(typeof CPresentation === "undefined"); var bSlide = !(typeof Slide === "undefined"); if ( ( Class instanceof CDocument && ( AscDFH.historyitem_Document_AddItem === Data.Type || AscDFH.historyitem_Document_RemoveItem === Data.Type ) ) || (((Class instanceof CDocumentContent || Class instanceof AscFormat.CDrawingDocContent)) && ( AscDFH.historyitem_DocumentContent_AddItem === Data.Type || AscDFH.historyitem_DocumentContent_RemoveItem === Data.Type ) ) || ( Class instanceof CTable && ( AscDFH.historyitem_Table_AddRow === Data.Type || AscDFH.historyitem_Table_RemoveRow === Data.Type ) ) || ( Class instanceof CTableRow && ( AscDFH.historyitem_TableRow_AddCell === Data.Type || AscDFH.historyitem_TableRow_RemoveCell === Data.Type ) ) || ( Class instanceof Paragraph && ( AscDFH.historyitem_Paragraph_AddItem === Data.Type || AscDFH.historyitem_Paragraph_RemoveItem === Data.Type ) ) || ( Class instanceof ParaHyperlink && ( AscDFH.historyitem_Hyperlink_AddItem === Data.Type || AscDFH.historyitem_Hyperlink_RemoveItem === Data.Type ) ) || ( Class instanceof ParaRun && ( AscDFH.historyitem_ParaRun_AddItem === Data.Type || AscDFH.historyitem_ParaRun_RemoveItem === Data.Type ) ) || ( bPresentation && Class instanceof CPresentation && (AscDFH.historyitem_Presentation_AddSlide === Data.Type || AscDFH.historyitem_Presentation_RemoveSlide === Data.Type)) || ( bSlide && Class instanceof Slide && (AscDFH.historyitem_SlideAddToSpTree === Data.Type || AscDFH.historyitem_SlideRemoveFromSpTree === Data.Type)) ) return true; return false; }; CHistory.prototype.private_IsAddContentChange = function(Class, Data) { var bPresentation = !(typeof CPresentation === "undefined"); var bSlide = !(typeof Slide === "undefined"); return ( ( Class instanceof CDocument && AscDFH.historyitem_Document_AddItem === Data.Type ) || ( ((Class instanceof CDocumentContent || Class instanceof AscFormat.CDrawingDocContent)) && AscDFH.historyitem_DocumentContent_AddItem === Data.Type ) || ( Class instanceof CTable && AscDFH.historyitem_Table_AddRow === Data.Type ) || ( Class instanceof CTableRow && AscDFH.historyitem_TableRow_AddCell === Data.Type ) || ( Class instanceof Paragraph && AscDFH.historyitem_Paragraph_AddItem === Data.Type ) || ( Class instanceof ParaHyperlink && AscDFH.historyitem_Hyperlink_AddItem === Data.Type ) || ( Class instanceof ParaRun && AscDFH.historyitem_ParaRun_AddItem === Data.Type ) || ( bPresentation && Class instanceof CPresentation && (AscDFH.historyitem_Presentation_AddSlide === Data.Type )) || ( bSlide && Class instanceof Slide && (AscDFH.historyitem_SlideAddToSpTree === Data.Type)) ) ? true : false; }; CHistory.prototype.private_GetItemsCountInContentChange = function(Class, Data) { if ( ( Class instanceof Paragraph ) || ( Class instanceof ParaHyperlink) || ( Class instanceof ParaRun ) || ( Class instanceof CDocument && AscDFH.historyitem_Document_RemoveItem === Data.Type ) || ( ((Class instanceof CDocumentContent || Class instanceof AscFormat.CDrawingDocContent)) && AscDFH.historyitem_DocumentContent_RemoveItem === Data.Type ) ) return Data.Items.length; return 1; }; CHistory.prototype.GetAllParagraphsForRecalcData = function(Props) { if (!this.RecalculateData.AllParagraphs) { if (this.Document) this.RecalculateData.AllParagraphs = this.Document.GetAllParagraphs({All : true}); else this.RecalculateData.AllParagraphs = []; } var arrParagraphs = []; if (!Props || true === Props.All) { return this.RecalculateData.AllParagraphs; } else if (true === Props.Style) { var arrStylesId = Props.StylesId; for (var nParaIndex = 0, nParasCount = this.RecalculateData.AllParagraphs.length; nParaIndex < nParasCount; ++nParaIndex) { var oPara = this.RecalculateData.AllParagraphs[nParaIndex]; for (var nStyleIndex = 0, nStylesCount = arrStylesId.length; nStyleIndex < nStylesCount; ++nStyleIndex) { if (oPara.Pr.PStyle === arrStylesId[nStyleIndex]) { arrParagraphs.push(oPara); break; } } } } else if (true === Props.Numbering) { for (var nParaIndex = 0, nParasCount = this.RecalculateData.AllParagraphs.length; nParaIndex < nParasCount; ++nParaIndex) { var oPara = this.RecalculateData.AllParagraphs[nParaIndex]; var NumPr = Props.NumPr; var _NumPr = oPara.GetNumPr(); if (undefined != _NumPr && _NumPr.NumId === NumPr.NumId && (_NumPr.Lvl === NumPr.Lvl || undefined === NumPr.Lvl)) arrParagraphs.push(oPara); } } return arrParagraphs; }; CHistory.prototype.GetRecalculateIndex = function() { return this.RecIndex; }; CHistory.prototype.SetRecalculateIndex = function(nIndex) { this.RecIndex = Math.min(this.Index, nIndex); }; CHistory.prototype.SaveRedoPoints = function() { var arrData = []; this.StoredData.push(arrData); for (var nIndex = this.Index + 1, nCount = this.Points.length; nIndex < nCount; ++nIndex) { arrData.push(this.Points[nIndex]); } }; CHistory.prototype.PopRedoPoints = function() { if (this.StoredData.length <= 0) return; var arrPoints = this.StoredData[this.StoredData.length - 1]; this.Points.length = this.Index + 1; for (var nIndex = 0, nCount = arrPoints.length; nIndex < nCount; ++nIndex) { this.Points[this.Index + nIndex + 1] = arrPoints[nIndex]; } this.StoredData.length = this.StoredData.length - 1; }; CHistory.prototype.RemoveLastPoint = function() { this.Remove_LastPoint(); }; CHistory.prototype.private_ClearRecalcData = function() { // NumPr здесь не обнуляем let numPr = this.RecalculateData.NumPr; this.RecalculateData = new RecalcData(); this.RecalculateData.numPr = numPr; }; CHistory.prototype.getRecalcDataByElements = function(elements) { let storedRecalcData = this.RecalculateData; this.RecalculateData = new RecalcData(); for (let i = 0, count = elements.length; i < count; ++i) { elements[i].Refresh_RecalcData2(); } let result = this.RecalculateData; this.RecalculateData = storedRecalcData; return result; }; /** * Обработка изменений после Undo/Redo всех изменений */ CHistory.prototype.private_PostProcessingRecalcData = function() { for (var sId in this.RecalculateData.ChangedStyles) { var oStyle = this.RecalculateData.ChangedStyles[sId]; oStyle.RecalculateRelatedParagraphs(); } }; /** * Получаем сколько изменений в истории уже сохранено на сервере на данный момент с учетом текущей точки в истории * @returns {number} */ CHistory.prototype.GetDeleteIndex = function() { if (null === this.SavedIndex) return null; var nSum = 0; for (var nPointIndex = 0, nLastPoint = Math.min(this.SavedIndex, this.Index); nPointIndex <= nLastPoint; ++nPointIndex) { nSum += this.Points[nPointIndex].Items.length; } return nSum; }; /** * Удаляем изменения из истории, которые сохранены на сервере. Это происходит при подключении второго пользователя */ CHistory.prototype.RemovePointsByDeleteIndex = function() { var nDeleteIndex = this.GetDeleteIndex(); if (null === nDeleteIndex) return; while (nDeleteIndex > 0 && this.Points.length > 0) { nDeleteIndex -= this.Points[0].Items.length; this.Points.splice(0, 1); if (this.Index >= 0) this.Index--; if (this.RecIndex >= 0) this.RecIndex--; } this.SavedIndex = null; }; /** * Удаляем все точки */ CHistory.prototype.RemoveAllPoints = function() { this.Index = -1; this.SavedIndex = null; this.ForceSave = false; this.RecIndex = -1; this.Points.length = 0; }; /** * Пустая ли история * @returns {boolean} */ CHistory.prototype.isEmpty = function() { return !this.Points.length; }; /** * Получаем массив изменений, которые еще не были пересчитаны * @returns {[]} */ CHistory.prototype.GetNonRecalculatedChanges = function() { var arrChanges = []; if (this.Index - this.RecIndex !== 1 && this.RecIndex >= -1) { for (var nPointIndex = this.RecIndex + 1; nPointIndex <= this.Index; ++nPointIndex) { this.GetChangesFromPoint(nPointIndex, arrChanges); } } else if (this.Index >= 0) { this.GetChangesFromPoint(this.Index, arrChanges); } return arrChanges; }; /** * Получем массив изменений из заданной точки * @param nPointIndex {number} * @param [arrChanges=undefined] {[CChangesBase]} * @returns {[CChangesBase]} */ CHistory.prototype.GetChangesFromPoint = function(nPointIndex, arrChanges) { if (!arrChanges) arrChanges = []; var oHPoint = this.Points[nPointIndex]; if (oHPoint) { for (var nIndex = 0, nItemsCount = oHPoint.Items.length; nIndex < nItemsCount; ++nIndex) { arrChanges.push(oHPoint.Items[nIndex].Data); } } return arrChanges; }; /** * Проверяем перед автозаменой, что действие совершается во время набора * @param oLastElement - последний элемент, добавленный перед автозаменой * @param nHistoryActions - количество точек предществующих автозамене * @param [nMaxTimeDelay=0] - если задано, то максимальное значение времени, которое прошло с момента последнего действия * @returns {boolean} */ CHistory.prototype.CheckAsYouTypeAutoCorrect = function(oLastElement, nHistoryActions, nMaxTimeDelay) { // В nHistoryActions задано количество точек, которые предществовали автозамене, т.е. // выполнялись действия, которые и вызывали автозамену в итоге. Нам надо проверить предыдущую точку до заданных // Если в там происходило добавление заданного элемента, значит у нас был набор текста if (this.Index < nHistoryActions) return false; var nCurIndex = this.Index - nHistoryActions; while (this.private_IsPointDoAutoCorrect(nCurIndex) && nCurIndex > 0) nCurIndex--; if (nCurIndex < 0) return false; var oPoint = this.Points[nCurIndex]; if (nMaxTimeDelay && (new Date().getTime() - oPoint.Time) > nMaxTimeDelay) return false; var nItemsCount = oPoint.Items.length; if ((AscDFH.historydescription_Document_AddLetter === oPoint.Description || AscDFH.historydescription_Document_AddLetterUnion === oPoint.Description || AscDFH.historydescription_Document_SpaceButton === oPoint.Description || AscDFH.historydescription_Presentation_ParagraphAdd === oPoint.Description || AscDFH.historydescription_Document_PasteHotKey === oPoint.Description) && nItemsCount > 0) { if (AscDFH.historydescription_Document_PasteHotKey === oPoint.Description) { // Последний добавленный элемент во время вставки был до AscDFH.historyitem_ParaRun_OnStartSplit let splitCounter = 0; for (let changeIndex = oPoint.Items.length - 1; changeIndex >= 0; --changeIndex) { let change = oPoint.Items[changeIndex].Data; if (!change) continue; if (AscDFH.historyitem_ParaRun_OnEndSplit === change.GetType()) { ++splitCounter; continue; } else if (AscDFH.historyitem_ParaRun_OnStartSplit === change.GetType()) { --splitCounter; continue; } if (splitCounter > 0 || !change.IsContentChange() || AscDFH.historyitem_ParaRun_AddItem !== change.GetType()) continue; let nChangeItemsCount = change.GetItemsCount(); return (nChangeItemsCount > 0 && change.GetItem(nChangeItemsCount - 1) === oLastElement); } } else { var oChange = oPoint.Items[nItemsCount - 1].Data; if (!oChange || !oChange.IsContentChange()) return false; let nChangeItemsCount = oChange.GetItemsCount(); return (nChangeItemsCount > 0 && AscDFH.historyitem_ParaRun_AddItem === oChange.GetType() && oChange.GetItem(nChangeItemsCount - 1) === oLastElement); } } return false; }; CHistory.prototype.private_IsPointDoAutoCorrect = function(nPointIndex) { if (nPointIndex < 0 || nPointIndex >= this.Points.length) return false; var nDescription = this.Points[nPointIndex].Description; return (AscDFH.historydescription_Document_AutoCorrectCommon === nDescription || AscDFH.historydescription_Document_AutoCorrectFirstLetterOfSentence === nDescription || AscDFH.historydescription_Document_AutoCorrectHyphensWithDash === nDescription || AscDFH.historydescription_Document_AutoCorrectSmartQuotes === nDescription); }; /** * Специальная функция для отмены последнего ввода через композитный ввод */ CHistory.prototype.UndoCompositeInput = function(documentState) { if (this.UndoRedoInProgress) return []; let lastIndex = this.Index; while (lastIndex >= 0) { let description = this.Points[lastIndex].Description; if (AscDFH.historydescription_Document_CompositeInput === description) break; if (AscDFH.historydescription_Document_CompositeInputReplace !== description) return null; lastIndex--; } if (lastIndex < 0) return null; this.UndoRedoInProgress = true; let changes = []; for (; this.Index >= lastIndex; --this.Index) { this.private_UndoPoint(this.Points[this.Index], changes); } this.UndoRedoInProgress = false; if (documentState) this.Document.SetSelectionState(documentState); return changes; }; /** * Проверяем что последнее действие ввод заданного символа, в предшествующей заданной позиции * @param run {AscWord.CRun} * @param inRunPos {number} * @param codePoint {?number} * @returns {boolean} */ CHistory.prototype.checkAsYouTypeEnterText = function(run, inRunPos, codePoint) { this.CheckUnionLastPoints(); if (this.Points.length <= 0 || this.Index !== this.Points.length - 1) return false; let point = this.Points[this.Index]; let description = point.Description; if (AscDFH.historydescription_Document_AddLetter !== description && AscDFH.historydescription_Document_AddLetterUnion !== description && AscDFH.historydescription_Document_SpaceButton !== description && AscDFH.historydescription_Document_CorrectEnterText !== description && AscDFH.historydescription_Document_CompositeInput !== description && AscDFH.historydescription_Document_CompositeInputReplace !== description && AscDFH.historydescription_Presentation_ParagraphAdd !== description) return false; let changes = point.Items; let lastChange = null; for (let i = changes.length - 1; i >= 0; --i) { lastChange = changes[i].Data; if (lastChange.IsContentChange()) break; } return (lastChange && AscDFH.historyitem_ParaRun_AddItem === lastChange.Type && lastChange.Class === run && lastChange.Pos === inRunPos - 1 && lastChange.Items.length && (undefined === codePoint || lastChange.Items[0].GetCodePoint() === codePoint)); }; /** * Специальная функция отмена ввода в форму * @returns {CChangesBase[]} */ CHistory.prototype.UndoFormFilling = function(form) { if (!form || !this.Can_Undo()) return []; this.UndoRedoInProgress = true; let changes = []; if (this.Index === this.Points.length - 1) this.LastState = this.Document.GetSelectionState(); this.Document.RemoveSelection(true); let point = this.Points[this.Index]; let newState = null; while (this.private_IsFormFillingPoint(point, form)) { newState = point.State; this.private_UndoPoint(point, changes) this.Index--; if (this.Index < 0) break; point = this.Points[this.Index]; } if (newState) this.Document.SetSelectionState(newState); if (!window['AscCommon'].g_specialPasteHelper.specialPasteStart) window['AscCommon'].g_specialPasteHelper.SpecialPasteButton_Hide(true); this.UndoRedoInProgress = false; return changes; }; /** * Конвертируем все действия внутри заданной точки из составных в простые */ CHistory.prototype.ConvertPointItemsToSimpleChanges = function(pointIndex) { let point = this.Points[pointIndex]; if (!point) return; let writer = this.BinaryWriter; let items = point.Items.slice(); point.Items = []; for (let index = 0, count = items.length; index < count; ++index) { let change = items[index].Data; if (change.IsContentChange()) { let needRecalc = items[index].NeedRecalc; let object = change.GetClass(); let simpleChanges = change.ConvertToSimpleChanges(); if (simpleChanges.length <= 1) { point.Items.push(items[index]); } else { for (let simpleIndex = 0, simpleChangesCount = simpleChanges.length; simpleIndex < simpleChangesCount; ++simpleIndex) { let simpleChange = simpleChanges[simpleIndex]; let dataPos = writer.GetCurPosition(); writer.WriteString2(object.GetId()); writer.WriteLong(simpleChange.Type); simpleChange.WriteToBinary(writer); let dataLen = writer.GetCurPosition() - dataPos; point.Items.push({ Class : object, Data : simpleChange, Binary : {Pos : dataPos, Len : dataLen}, NeedRecalc : needRecalc }); } } } else { point.Items.push(items[index]); } } }; /** * Проверяем лок для последних нескольких точек * @param pointCount */ CHistory.prototype.checkLock = function(pointCount) { if (!pointCount || pointCount - 1 > this.Index) return; let checkFormLockCounter = 1; let lockData = { document : this.Document, locked : false, isFillingForm : function() { return this.document.IsFillingFormMode(); }, lock : function() { AscCommon.CollaborativeEditing.Add_CheckLock(true); this.locked = true; }, isLocked : function() { return this.locked; }, isSkipFormCheck : function() { return checkFormLockCounter <= 0; } }; for (let pointIndex = 0; pointIndex < pointCount; ++pointIndex) { let point = this.Points[this.Index - pointIndex]; let checkFormLockArray = point.Additional && point.Additional.FormFillingLockCheck ? point.Additional.FormFillingLockCheck : []; let checkFormLockPos = 0; checkFormLockCounter = 1; for (let changeIndex = 0; changeIndex < point.Items.length; ++changeIndex) { while (checkFormLockPos < checkFormLockArray.length && checkFormLockArray[checkFormLockPos][0] <= changeIndex) { checkFormLockCounter += checkFormLockArray[checkFormLockPos][1]; ++checkFormLockPos; } point.Items[changeIndex].Data.CheckLock(lockData); if (lockData.isLocked()) return; } } }; /** * Помечаем изменения, в которых не нужно проверять возможность заполнения форм в проверке на лок * @param isSkip */ CHistory.prototype.skipFormFillingLockCheck = function(isSkip) { if (this.Index < 0) return; let point = this.Points[this.Index]; if (!point.Additional) point.Additional = {}; if (!point.Additional.FormFillingLockCheck) point.Additional.FormFillingLockCheck = []; if (isSkip) point.Additional.FormFillingLockCheck.push([point.Items.length, -1]); else point.Additional.FormFillingLockCheck.push([point.Items.length, 1]); }; CHistory.prototype.startGroupPoints = function() { this.Create_NewPoint(AscDFH.historydescription_GroupPointsOpen); }; CHistory.prototype.cancelGroupPoints = function() { let startIndex = this._getLongPointIndex(); if (-1 === startIndex || this.UndoRedoInProgress) return []; let changes = []; this.UndoRedoInProgress = true; let point; for (let i = this.Index; i >= startIndex; --i) { point = this.Points[i]; this.private_UndoPoint(point, changes); } if (point && this.Document) this.Document.SetSelectionState(point.State); if (!window['AscCommon'].g_specialPasteHelper.specialPasteStart) window['AscCommon'].g_specialPasteHelper.SpecialPasteButton_Hide(true); this.Index = startIndex - 1; this.ClearRedo() this.UndoRedoInProgress = false; this.GroupChanges = this.GroupChanges.length ? this.GroupChanges.concat(changes) : changes; return changes; }; CHistory.prototype.endGroupPoints = function() { this.ClearRedo(); let startIndex = this._getLongPointIndex(); if (-1 === startIndex || this.UndoRedoInProgress) return; let point = this.Points[startIndex]; point.Description = AscDFH.historydescription_GroupPoints; for (let i = startIndex + 1; i < this.Points.length; ++i) { point.Items = point.Items.concat(this.Points[i].Items); } this.Points.length = startIndex + 1; this.Index = startIndex; }; CHistory.prototype.getGroupChanges = function() { return this.GroupChanges; }; CHistory.prototype.resetGroupChanges = function() { this.GroupChanges = []; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Private area //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// CHistory.prototype.private_UndoPoint = function(point, changes) { for (let changeIndex = point.Items.length - 1; changeIndex >= 0; --changeIndex) { let item = point.Items[changeIndex]; if (item.Data) { item.Data.Undo(); item.Data.CheckNeedRecalculate(); changes.push(item.Data); } this.private_UpdateContentChangesOnUndo(item); } }; CHistory.prototype.private_RedoPoint = function(point, changes) { for (var changeIndex = 0; changeIndex < point.Items.length; ++changeIndex) { let item = point.Items[changeIndex]; if (item.Data) { item.Data.Redo(); item.Data.CheckNeedRecalculate(); changes.push(item.Data); } this.private_UpdateContentChangesOnRedo(item); } }; CHistory.prototype.private_IsFormFillingPoint = function(point, form) { return (point.Additional && form === point.Additional.FormFilling); }; CHistory.prototype._getLongPointIndex = function() { for (let i = this.Index; i >= 0; --i) { if (AscDFH.historydescription_GroupPointsOpen === this.Points[i].Description) return i; } return -1; }; CHistory.prototype.SavePointIndex = function() { var oPoint = this.Points[this.Index]; if(oPoint) { this.PosInCurPoint = oPoint.Items.length; } else { this.PosInCurPoint = null; } }; CHistory.prototype.UndoToPointIndex = function() { var oPoint = this.Points[this.Index]; if(oPoint) { if(this.PosInCurPoint !== null) { for (let Index = oPoint.Items.length - 1; Index >= this.PosInCurPoint; --Index) { let item = oPoint.Items[Index]; if (item.Data) { item.Data.Undo(); item.Data.RefreshRecalcData(); } this.private_UpdateContentChangesOnUndo(item); } oPoint.Items.splice(this.PosInCurPoint); } } this.PosInCurPoint = null; }; CHistory.prototype.ClearPointIndex = function() { this.PosInCurPoint = null; }; CHistory.prototype.StartTransaction = function () {}; CHistory.prototype.EndTransaction = function () {}; //----------------------------------------------------------export-------------------------------------------------- window['AscCommon'] = window['AscCommon'] || {}; window['AscCommon'].CHistory = CHistory; window['AscCommon'].RecalcData = RecalcData; window['AscCommon'].History = new CHistory(); })(window);