Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

1967 lines
62 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (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);