Files
DocumentServer-v-9.2.0/sdkjs/common/collaboration/deleted-text-recovery.js
T
Yajbir Singh f1b860b25c
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

677 lines
19 KiB
JavaScript

/*
* (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 ()
{
/**
* Класс, восстанавливающий удаленные части документа
* @param {AscWord.Document} logicDocument
* @constructor
*/
function DeletedTextRecovery(logicDocument)
{
this.document = logicDocument;
/**
* Список всех изменений связанных с удалением текста
* @type {*[]}
*/
this.m_RewiewDelPoints = [];
this.arrColor = [];
this.userId = undefined;
this.userName = undefined;
this.userTime = undefined;
}
/**
* Восстанавливаем удаленный текст в текущей точке истории версий
* @return {boolean}
*/
DeletedTextRecovery.prototype.RecoverDeletedText = function()
{
this.UndoRecoveredText();
return this.ShowDelText();
};
/**
* Отменяем восстановление удаленного текста, если оно было
*/
DeletedTextRecovery.prototype.UndoRecoveredText = function ()
{
if (!this.HaveRecoveredText())
return;
let localHistory = AscCommon.History;
let changes = localHistory.Undo();
this.document.UpdateAfterUndoRedo(changes);
localHistory.ClearRedo();
};
/**
* Запрашивем,есть ли восстановленный удаленный текст
* @return {boolean}
*/
DeletedTextRecovery.prototype.HaveRecoveredText = function()
{
let localHistory = AscCommon.History;
let lastPoint = localHistory.Points[localHistory.Points.length - 1];
return !!(lastPoint && lastPoint.Description === AscDFH.historydescription_Collaborative_DeletedTextRecovery);
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Private area
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Инициализация и создание промежуточных данных для отображения удаленного текста в текущей ревизии
*/
DeletedTextRecovery.prototype.HandleChanges = function()
{
AscCommon.CollaborativeEditing.CoHistory.SplitChangesByPoints();
let oCoHistory = AscCommon.CollaborativeEditing.CoHistory;
let arrChangesList = oCoHistory.Changes;
let arrPointsList = AscCommon.CollaborativeEditing.CoHistory.ChangesSplitByPoints;
let nIndex = AscCommon.CollaborativeEditing.CoHistory.curChangeIndex;
let arrChanges = arrChangesList.slice(0, arrPointsList[nIndex]);
if (!arrChanges || !arrChanges.length)
return;
this.m_RewiewDelPoints = arrChanges;
};
/**
* Получаем подготовленные данные, разбитые по точкам
* @return {*[]}
*/
DeletedTextRecovery.prototype.GetChanges = function()
{
let arr = this.m_RewiewDelPoints.slice();
let arrOutput = []
for (let i = 0; i < arr.length; i++)
{
let oChange = arr[i];
if (oChange.ConvertToSimpleChanges)
{
let arrSplitChange = oChange.ConvertToSimpleChanges();
if (arrSplitChange && arrSplitChange.length)
{
for (let j = 0; j < arrSplitChange.length; j++)
{
arrOutput.push(arrSplitChange[j])
}
} else
{
arrOutput.push(oChange);
}
}
else
arrOutput.push(oChange);
}
return arrOutput;
};
DeletedTextRecovery.prototype.GetRemoveTextChanges = function (arrInputChanges, oRemoveText)
{
let oAddText = new AddTextPositions();
// отбираем удаленный текст связанный с текущей ревизией
for (let i = 0; i < arrInputChanges.length; i++)
{
let oCurChange = arrInputChanges[i];
// пропускаем все изменения связанные со сплитом
if (oCurChange instanceof CChangesRunOnStartSplit)
{
while (oCurChange && !(oCurChange instanceof CChangesRunOnEndSplit))
{
i++;
oCurChange = arrInputChanges[i];
}
}
if (oCurChange === undefined)
break;
if (oCurChange instanceof AscCommon.CChangesTableIdDescription || !oCurChange.Copy)
continue;
let oNewCurChange = oCurChange.Copy();
oRemoveText.ProceedChange(oNewCurChange);
if (oNewCurChange instanceof CChangesRunAddItem || oNewCurChange instanceof CChangesParagraphAddItem || oNewCurChange instanceof CChangesDocumentAddItem)
oAddText.Add(oCurChange.Class, oCurChange, oCurChange.UseArray ? oCurChange.PosArray[0] : oCurChange.Pos, i);
else if (oNewCurChange instanceof CChangesRunRemoveItem || oNewCurChange instanceof CChangesParagraphRemoveItem || oNewCurChange instanceof CChangesDocumentRemoveItem)
oRemoveText.Check(oAddText, oCurChange, i);
}
oRemoveText.DelDuplicate();
}
DeletedTextRecovery.prototype.CommuteChanges = function (arrInputChanges, arrSaveData, oRemoveText)
{
// коммутируем изменения
let arrRevInput = arrInputChanges;
let arrDelChangesForCommute = oRemoveText.GetArrayChanges();
for (let j = 0; j < arrDelChangesForCommute.length; j++)
{
let oCurItem = arrDelChangesForCommute[j];
let nPos = oCurItem.nIndex;
let oChange = oCurItem.item;
if (oChange.IsContentChange())
{
let _oChange = oChange.Copy();
if (AscCommon.CollaborativeEditing.CoHistory.CommuteContentChange(_oChange, nPos, arrRevInput))
arrSaveData.push(_oChange);
}
else
{
arrSaveData.push(oChange);
}
}
oRemoveText.ResetData();
}
/**
* Отменяем заданные изменения
* @param arrInputChanges
* @return {*[]}
*/
DeletedTextRecovery.prototype.RedoUndoChanges = function (arrInputChanges)
{
let oRemoveText = new RemoveTextPositions();
let arrChanges = [];
let arrDelChanges = [];
this.GetRemoveTextChanges(arrInputChanges, oRemoveText);
this.CommuteChanges(arrInputChanges, arrDelChanges, oRemoveText);
for (let i = 0; i < arrDelChanges.length; i++)
{
this.RedoUndoChange(arrDelChanges[i], false, arrChanges);
let nPos = arrDelChanges[i].UseArray
? arrDelChanges[i].PosArray[0]
: arrDelChanges[i].Pos;
oRemoveText.Add(
arrDelChanges[i].Class,
arrDelChanges[i],
nPos,
i
);
}
let result = {
data: oRemoveText.ProceedPositions(this),
classes: oRemoveText.oClasses
};
return [arrChanges, result];
};
DeletedTextRecovery.prototype.ShowDelText = function ()
{
let versionHistory = this.document.GetApi().getVersionHistory();
if (!versionHistory)
return false;
this.HandleChanges();
let arrInput = this.GetChanges();
if (arrInput.length === 0)
return false;
let localHistory = AscCommon.History;
localHistory.Create_NewPoint(AscDFH.historydescription_Collaborative_DeletedTextRecovery);
this.userId = versionHistory.userId;
this.userName = versionHistory.userName;
this.userTime = new Date(versionHistory.dateOfRevision).getTime();
// отменяем изменения до нужного места (необходимо для перемещения по истории)
let arrCurrentPoint = this.RedoUndoChanges(arrInput);
let delChanges = arrCurrentPoint[0];
let arrResult = arrCurrentPoint[1];
for (let i = 0; i < delChanges.length; i++)
{
this.document.History.Add(delChanges[i]);
}
this.Split(arrResult);
this.document.RecalculateByChanges(delChanges);
this.m_RewiewDelPoints = [];
return true;
};
DeletedTextRecovery.prototype.Split = function (arrInput)
{
let data = arrInput.data;
let classes = arrInput.classes;
let arrKeys = Object.keys(data);
for (let nKey = 0; nKey < arrKeys.length; nKey++)
{
let strCurrentKey = arrKeys[nKey];
let arrCurrentRunData = data[strCurrentKey];
let oCurrentRun = classes[strCurrentKey];
arrCurrentRunData.sort(function (a, b) { return a.nStart - b.nStart });
for (let j = arrCurrentRunData.length - 1; j >= 0; j--)
{
let oCurrentRule = arrCurrentRunData[j];
let nStart = oCurrentRule.nStart;
let nEnd = oCurrentRule.nEnd;
if (oCurrentRun instanceof CDocument)
{
let arrContent = oCurrentRun.Content;
for (let j = nStart; j <= nEnd; j++)
{
let oCurrentParagraph = arrContent[j];
if (oCurrentParagraph)
this.SetReviewInfo(oCurrentParagraph);
}
}
else if (oCurrentRun instanceof Paragraph)
{
let arrContent = oCurrentRun.Content;
if (nStart === 0 && arrContent.length === nEnd)
{
this.SetReviewInfo(oCurrentRun);
}
else
{
for (let i = nStart; i <= nEnd; i++)
{
this.SetReviewInfo(arrContent[i]);
}
}
}
else if (oCurrentRun instanceof ParaRun)
{
let newCollab = [];
if (oCurrentRun.Content.length === 0 || (nEnd + 1 - nStart) === oCurrentRun.Content.length)
{
this.SetReviewInfo(oCurrentRun);
continue;
}
for (let i = 0; i < oCurrentRun.CollaborativeMarks.Ranges.length; i++)
{
let oCollab = oCurrentRun.CollaborativeMarks.Ranges[i];
newCollab.push({PosS: oCollab.PosS, PosE: oCollab.PosE, Color: oCollab.Color, oCurrentRun: oCurrentRun});
}
let oParent = oCurrentRun.GetParent();
let RunPos = this.FindPosInParent(oCurrentRun);
let RightRun = oCurrentRun.SplitForSpreadCollaborativeMark(nStart);
oParent.Add_ToContent(RunPos + 1, RightRun);
let oNewer = RightRun.SplitForSpreadCollaborativeMark(nEnd - nStart + 1);
oParent.Add_ToContent(RunPos + 2, oNewer);
this.SetReviewInfo(RightRun);
for (let i = 0; i < newCollab.length; i++)
{
let oCurCollaborativeMark = newCollab[i];
this.arrColor.push(oCurCollaborativeMark);
}
}
}
}
}
DeletedTextRecovery.prototype.SetReviewInfo = function (oReviewInfoParent)
{
if (!oReviewInfoParent === undefined)
return;
// TODO: Проверку по каким классам нужно проходить лучше переделать
if (!oReviewInfoParent || !oReviewInfoParent.GetReviewInfo)
{
if (oReviewInfoParent instanceof ParaMath)
{
let oRootContent = oReviewInfoParent.Root.Content;
for (let i = 0; i < oRootContent.length; i++)
{
let oCurrentContent = oRootContent[i];
this.SetReviewInfo(oCurrentContent);
}
}
else if (oReviewInfoParent.Content.length > 0)
{
for (let i = 0; i < oReviewInfoParent.Content.length; i++)
{
let oCurrentContent = oReviewInfoParent.Content[i];
this.SetReviewInfo(oCurrentContent);
}
}
return;
}
if (oReviewInfoParent.GetReviewType() !== reviewtype_Remove)
{
let reviewInfo = oReviewInfoParent.GetReviewInfo();
if (!reviewInfo)
reviewInfo = new AscWord.ReviewInfo();
else
reviewInfo = reviewInfo.Copy();
reviewInfo.UserId = this.userId;
reviewInfo.UserName = this.userName;
reviewInfo.DateTime = this.userTime;
oReviewInfoParent.SetReviewTypeWithInfo(reviewtype_Remove, reviewInfo, false);
}
};
DeletedTextRecovery.prototype.FindPosInParent = function(oClass)
{
let oParent = oClass.GetParent();
let arrParentContent = oParent.Content;
for (let i = 0; i < arrParentContent.length; i++)
{
if (arrParentContent[i] === oClass)
return i;
}
};
DeletedTextRecovery.prototype.RedoUndoChange = function (oChange, isRedo, arrToSave)
{
if (!oChange)
return;
if (oChange.IsContentChange())
{
let arrSimpleChanges = oChange.ConvertToSimpleChanges();
for (let simpleIndex = arrSimpleChanges.length - 1; simpleIndex >= 0; simpleIndex--)
{
if (isRedo)
arrSimpleChanges[simpleIndex].Redo();
else
arrSimpleChanges[simpleIndex].Undo();
let oRevChange = arrSimpleChanges[simpleIndex];
if (!isRedo)
oRevChange = oRevChange.CreateReverseChange();
if (oRevChange)
arrToSave.push(oRevChange);
}
}
else
{
if (isRedo)
oChange.Redo();
else
oChange.Undo();
let oRevChange = oChange;
if (!isRedo)
oRevChange = oRevChange.CreateReverseChange();
if (oRevChange)
arrToSave.push(oRevChange);
}
};
function AddTextPositions()
{
this.data = {};
this.Add = function (oClass, oItem, Pos, nIndex)
{
if (!this.data[oClass.Id])
this.data[oClass.Id] = [];
this.data[oClass.Id].push({item: oItem, pos: Pos, nIndex: nIndex});
}
}
function RemoveTextPositions()
{
this.data = {};
this.oClasses = {};
this.arrClasses = [];
this.Check = function (oAddText, oCurChange, i)
{
if (oCurChange.Class)
{
let strCurrentId = oCurChange.Class.Id;
let arrRemData = oAddText.data[strCurrentId];
if (!arrRemData)
{
this.Add(oCurChange.Class, oCurChange, oCurChange.UseArray ? oCurChange.PosArray[0] : oCurChange.Pos, i);
return;
}
let addItem = oCurChange.Items[0];
for (let i = 0; i < arrRemData.length; i++)
{
let oCurrentRemItem = arrRemData[i];
if (oCurChange.UseArray && oCurChange.PosArray[0] === oCurrentRemItem.pos && addItem.Value === oCurrentRemItem.item.Items[0].Value)
{
return false;
}
else if (!oCurChange.UseArray && oCurChange.Pos === oCurrentRemItem.pos && addItem.Value === oCurrentRemItem.item.Items[0].Value)
{
return false;
}
}
}
this.Add(oCurChange.Class, oCurChange, oCurChange.UseArray ? oCurChange.PosArray[0] : oCurChange.Pos, i);
}
this.DelDuplicate = function ()
{
let arrKeys = Object.keys(this.data);
for (let nKey = 0; nKey < arrKeys.length; nKey++)
{
let strCurrentKey = arrKeys[nKey];
let arrCurrentRunData = this.data[strCurrentKey];
for (let i = 0; i < arrCurrentRunData.length; i++)
{
for (let j = i + 1; j < arrCurrentRunData.length; j++)
{
if (arrCurrentRunData[i].item === arrCurrentRunData[j].item)
{
arrCurrentRunData.splice(j, 1);
break
}
}
}
}
}
this.Add = function (oClass, oItem, Pos, nIndex)
{
if (!this.data[oClass.Id])
this.data[oClass.Id] = [];
this.data[oClass.Id].push({item: oItem, pos: Pos, nIndex: nIndex});
}
this.GetArrayChanges = function ()
{
let arrOutput = []
let arrKeys = Object.keys(this.data);
for (let nKey = 0; nKey < arrKeys.length; nKey++)
{
let strCurrentKey = arrKeys[nKey];
let arrCurrentRunData = this.data[strCurrentKey];
for (let i = 0; i < arrCurrentRunData.length; i++)
{
arrOutput.push(arrCurrentRunData[i]);
}
}
return arrOutput;
}
this.ProceedChange = function (oChange)
{
if (oChange.Class && !this.oClasses[oChange.Class.Id])
{
this.oClasses[oChange.Class.Id] = oChange.Class;
if (-1 === this.arrClasses.indexOf(oChange.Class.Id))
{
this.arrClasses.push(oChange.Class.Id);
}
}
}
this.FindInParent = function (oClass, oItem)
{
let arrParentContent = oClass.Content;
for (let nPos = 0; nPos < arrParentContent.length; nPos++)
{
if (arrParentContent[nPos] === oItem)
return nPos;
}
}
this.ProceedPositions = function ()
{
for (let nKey = 0; nKey < this.arrClasses.length; nKey++)
{
let strCurrentKey = this.arrClasses[nKey];
let arrCurrentRunData = this.data[strCurrentKey];
if (!arrCurrentRunData)
continue;
let oClass = this.oClasses[strCurrentKey];
let newArrCurrentRunData = [];
for (let i = 0; i < arrCurrentRunData.length; i++)
{
let oItem = arrCurrentRunData[i];
let nPos = this.FindInParent(oClass, oItem.item.Items[0]);
newArrCurrentRunData.push(nPos);
}
let nCurrentPos;
let nPrevPos;
let nTempPrevPos;
for (let nPos = 1; nPos < newArrCurrentRunData.length; nPos++)
{
nPrevPos = newArrCurrentRunData[nPos - 1];
nCurrentPos = newArrCurrentRunData[nPos];
if (nTempPrevPos === nCurrentPos || nCurrentPos === nPrevPos)
{
if (nTempPrevPos)
{
nTempPrevPos = undefined;
newArrCurrentRunData[nPos] = nPrevPos + 1;
nTempPrevPos = nCurrentPos;
}
else
{
newArrCurrentRunData[nPos] = nCurrentPos + 1;
nTempPrevPos = nCurrentPos;
}
}
}
this.data[strCurrentKey] = newArrCurrentRunData;
}
let transformedObject = CollapsePositions(this.data);
return transformedObject
}
this.ResetData = function ()
{
this.data = {};
}
}
function CollapsePositions (oInput)
{
let transformedObject = {};
for (let key in oInput)
{
if (oInput.hasOwnProperty(key))
{
let values = oInput[key];
let pairs = [];
let nStart = null;
let nEnd = null;
let decreasingSequence = false;
for (let i = 0; i < values.length; i++)
{
let value = values[i];
if (nStart === null)
{
nStart = value;
nEnd = value;
}
else if (value === nEnd + 1)
{
nEnd = value;
decreasingSequence = false;
}
else if (value === nEnd - 1)
{
nStart = value;
decreasingSequence = true;
}
else
{
pairs.push({ nStart : nStart, nEnd: nEnd });
nStart = value;
nEnd = value;
decreasingSequence = false;
}
}
if (nStart !== null && nEnd !== null)
pairs.push({ nStart : nStart, nEnd: nEnd });
transformedObject[key] = pairs;
}
}
return transformedObject;
}
//--------------------------------------------------------export----------------------------------------------------
AscCommon.DeletedTextRecovery = DeletedTextRecovery;
})();