/* * (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) { /** * Механизм поиска. Хранит параграфы с найденной строкой * @constructor */ function CDocumentSearch(oLogicDocument) { this.LogicDocument = oLogicDocument; this.Text = ""; this.MatchCase = false; this.Word = false; this.Pattern = new AscCommonWord.CSearchPatternEngine(); this.Prefix = []; this.Id = 0; this.Count = 0; this.Elements = {}; this.ReplacedId = []; this.CurId = -1; this.Direction = true; // направление true - вперед, false - назад this.ClearOnRecalc = true; // Флаг, говорящий о том, запустился ли пересчет из-за Replace this.Selection = false; this.Footnotes = []; this.Endnotes = []; this.InsertPattern = new AscCommonWord.CSearchPatternEngine(); this.TextAroundId = -1; this.TextAroundTimer = null; this.TextAroundUpdate = true; this.ReplaceEvent = true; this.TextAroundEmpty = true; // Флаг, что все очищено, чтобы не очищать повторно } CDocumentSearch.prototype.Reset = function() { this.Text = ""; this.MatchCase = false; this.Word = false; }; /** * @param {AscCommon.CSearchSettings} oProps */ CDocumentSearch.prototype.Compare = function(oProps) { return (oProps && this.Text === oProps.GetText() && this.MatchCase === oProps.IsMatchCase() && this.Word === oProps.IsWholeWords()); }; CDocumentSearch.prototype.Clear = function() { this.Reset(); // Очищаем предыдущие элементы поиска for (var Id in this.Elements) { this.Elements[Id].ClearSearchResults(); } this.Id = 0; this.Count = 0; this.Elements = {}; this.ReplacedId = []; this.CurId = -1; this.Direction = true; this.TextAroundUpdate = true; this.StopTextAround(); this.SendClearAllTextAround(); }; CDocumentSearch.prototype.Add = function(Paragraph) { this.Count++; this.Elements[this.Id++] = Paragraph; return (this.Id - 1); }; CDocumentSearch.prototype.GetElementsMap = function() { let map = {}; for (let searchId in this.Elements) { let paraId = this.Elements[searchId].GetId(); if (!map[paraId]) map[paraId] = this.Elements[searchId]; } return map; }; CDocumentSearch.prototype.Select = function(nId, bUpdateStates) { var Paragraph = this.Elements[nId]; if (Paragraph) { var SearchElement = Paragraph.SearchResults[nId]; if (SearchElement) { Paragraph.Selection.Use = true; Paragraph.Selection.Start = false; Paragraph.Set_SelectionContentPos(SearchElement.StartPos, SearchElement.EndPos); Paragraph.Set_ParaContentPos(SearchElement.StartPos, false, -1, -1); Paragraph.Document_SetThisElementCurrent(false !== bUpdateStates); } this.SetCurrent(nId); } }; CDocumentSearch.prototype.SetCurrent = function(nId) { this.CurId = undefined !== nId ? nId : -1; let nIndex = -1 !== this.CurId ? this.GetElementIndexById(this.CurId) : -1; let oApi = this.LogicDocument.GetApi() oApi.sync_setSearchCurrent(nIndex, this.Count); }; CDocumentSearch.prototype.ResetCurrent = function() { this.SetCurrent(-1); }; CDocumentSearch.prototype.GetCount = function() { return this.Count; }; CDocumentSearch.prototype.GetCurrent = function() { return this.CurId; }; CDocumentSearch.prototype.Replace = function(sReplaceString, Id, bRestorePos) { this.InsertPattern.Set(sReplaceString); var oPara = this.Elements[Id]; if (oPara) { if (oPara.IsInPlaceholder()) return false; var oLogicDocument = oPara.LogicDocument; var isTrackRevisions = oLogicDocument ? oLogicDocument.IsTrackRevisions() : false; var SearchElement = oPara.SearchResults[Id]; if (SearchElement) { if (oPara.GetPlaceHolderObject(SearchElement.StartPos) || oPara.GetPlaceHolderObject(SearchElement.EndPos)) return false; var ContentPos, StartPos, EndPos, bSelection; if (true === bRestorePos) { // Сохраняем позицию состояние параграфа, чтобы курсор остался в том же месте и после замены. bSelection = oPara.IsSelectionUse(); ContentPos = oPara.Get_ParaContentPos(false, false); StartPos = oPara.Get_ParaContentPos(true, true); EndPos = oPara.Get_ParaContentPos(true, false); oPara.Check_NearestPos({ContentPos : ContentPos}); oPara.Check_NearestPos({ContentPos : StartPos}); oPara.Check_NearestPos({ContentPos : EndPos}); } if (isTrackRevisions) { // Встанем в конечную позицию поиска и добавим новый текст var oEndContentPos = SearchElement.EndPos; var oEndRun = SearchElement.ClassesE[SearchElement.ClassesE.length - 1]; var nRunPos = oEndContentPos.Get(SearchElement.ClassesE.length - 1); if (reviewtype_Add === oEndRun.GetReviewType() && oEndRun.GetReviewInfo().IsCurrentUser()) { this.private_AddReplacedStringToRun(oEndRun, nRunPos); } else { var oRunParent = oEndRun.GetParent(); var nRunPosInParent = oEndRun.GetPosInParent(oRunParent); var oReplaceRun = oEndRun.Split2(nRunPos, oRunParent, nRunPosInParent); if (!oReplaceRun.IsEmpty()) oReplaceRun.Split2(0, oRunParent, nRunPosInParent + 1); this.private_AddReplacedStringToRun(oReplaceRun, 0); oReplaceRun.SetReviewType(reviewtype_Add); } } else { // Сначала в начальную позицию поиска добавляем новый текст var StartContentPos = SearchElement.StartPos; var StartRun = SearchElement.ClassesS[SearchElement.ClassesS.length - 1]; var RunPos = StartContentPos.Get(SearchElement.ClassesS.length - 1); this.private_AddReplacedStringToRun(StartRun, RunPos); } // Выделяем старый объект поиска и удаляем его oPara.Selection.Use = true; oPara.Set_SelectionContentPos(SearchElement.StartPos, SearchElement.EndPos); oPara.Remove(); // Перемещаем курсор в конец поиска oPara.RemoveSelection(); oPara.Set_ParaContentPos(SearchElement.StartPos, true, -1, -1); // Удаляем запись о данном элементе this.Count--; oPara.RemoveSearchResult(Id); delete this.Elements[Id]; this.private_AddReplacedId(Id); if (true === bRestorePos) { oPara.Set_SelectionContentPos(StartPos, EndPos); oPara.Set_ParaContentPos(ContentPos, true, -1, -1); oPara.Selection.Use = bSelection; oPara.Clear_NearestPosArray(); } if (this.ReplaceEvent && !this.TextAroundUpdate) this.LogicDocument.GetApi().sync_removeTextAroundSearch(Id); return true; } } return false; }; CDocumentSearch.prototype.private_AddReplacedId = function(nId) { for (let nPos = 0, nCount = this.ReplacedId.length; nPos < nCount; ++nPos) { if (this.ReplacedId[nPos] > nId) return this.ReplacedId.splice(nPos, 0, nId); } this.ReplacedId.push(nId); }; CDocumentSearch.prototype.private_AddReplacedStringToRun = function(oRun, nInRunPos) { var isMathRun = oRun.IsMathRun(); for (var nIndex = 0, nAdd = 0, nCount = this.InsertPattern.GetLength(); nIndex < nCount; ++nIndex) { var oItem = this.InsertPattern.Get(nIndex).ToRunElement(isMathRun); if (oItem) { oRun.AddToContent(nInRunPos + nAdd, oItem, false); nAdd++; } } }; CDocumentSearch.prototype.GetElementIndexById = function(nId) { for (let nPos = 0, nCount = this.ReplacedId.length; nPos < nCount; ++nPos) { if (this.ReplacedId[nPos] > nId) return (nId - nPos); else if (this.ReplacedId[nPos] === nId) return -1; } return (nId - this.ReplacedId.length); }; CDocumentSearch.prototype.ReplaceAll = function(NewStr, bUpdateStates) { this.RemoveEvent = false; for (var Id = this.Id; Id >= 0; --Id) { if (this.Elements[Id]) this.Replace(NewStr, Id, true); } this.RemoveEvent = true; this.Clear(); }; /** * @param {AscCommon.CSearchSettings} oProps */ CDocumentSearch.prototype.Set = function(oProps) { if (!oProps) return; this.Text = oProps.GetText(); this.MatchCase = oProps.IsMatchCase(); this.Word = oProps.IsWholeWords(); var _sText = this.Text; if (!this.MatchCase) _sText = this.Text.toLowerCase(); this.Pattern.Set(_sText); this.private_CalculatePrefix(); }; CDocumentSearch.prototype.IsWholeWords = function() { return this.Word; }; CDocumentSearch.prototype.IsMatchCase = function() { return this.MatchCase; }; CDocumentSearch.prototype.SetFootnotes = function(arrFootnotes) { this.Footnotes = arrFootnotes; }; CDocumentSearch.prototype.SetEndnotes = function(arrEndnotes) { this.Endnotes = arrEndnotes; }; CDocumentSearch.prototype.GetFootnotes = function() { return this.Footnotes; }; CDocumentSearch.prototype.GetEndnotes = function() { return this.Endnotes; }; CDocumentSearch.prototype.GetDirection = function() { return this.Direction; }; CDocumentSearch.prototype.SetDirection = function(bDirection) { this.Direction = bDirection; }; CDocumentSearch.prototype.private_CalculatePrefix = function() { var nLen = this.Pattern.GetLength(); this.Prefix = new Int32Array(nLen); this.Prefix[0] = 0; for (var nPos = 1, nK = 0; nPos < nLen; ++nPos) { nK = this.Prefix[nPos - 1] while (nK > 0 && !(this.Pattern.Get(nPos).IsMatch(this.Pattern.Get(nK)))) nK = this.Prefix[nK - 1]; if (this.Pattern.Get(nPos).IsMatch(this.Pattern.Get(nK))) nK++; this.Prefix[nPos] = nK; } }; CDocumentSearch.prototype.GetPrefix = function(nIndex) { return this.Prefix[nIndex]; }; CDocumentSearch.prototype.StartTextAround = function() { if (!this.TextAroundUpdate) return this.SendAllTextAround(); this.TextAroundUpdate = false; this.StopTextAround(); this.TextAroundId = 0; this.LogicDocument.GetApi().sync_startTextAroundSearch(); let oThis = this; this.TextAroundTimer = setTimeout(function() { oThis.ContinueGetTextAround() }, 20); this.TextArround = []; }; CDocumentSearch.prototype.ContinueGetTextAround = function() { let arrResult = []; let nStartTime = performance.now(); while (performance.now() - nStartTime < 20) { if (this.TextAroundId >= this.Id) break; let sId = this.TextAroundId++; if (!this.Elements[sId]) continue; let textAround = this.Elements[sId].GetTextAroundSearchResult(sId); this.TextArround[sId] = textAround; arrResult.push([sId, textAround]); } if (arrResult.length) this.TextAroundEmpty = false; this.LogicDocument.GetApi().sync_getTextAroundSearchPack(arrResult); let oThis = this; if (this.TextAroundId >= 0 && this.TextAroundId < this.Id) { this.TextAroundTimer = setTimeout(function() { oThis.ContinueGetTextAround(); }, 20); } else { this.TextAroundId = -1; this.TextAroundTimer = null; this.LogicDocument.GetApi().sync_endTextAroundSearch(); } }; CDocumentSearch.prototype.StopTextAround = function() { if (this.TextAroundTimer) { clearTimeout(this.TextAroundTimer); this.LogicDocument.GetApi().sync_endTextAroundSearch(); } this.TextAroundTimer = null; this.TextAroundId = -1; }; CDocumentSearch.prototype.SendAllTextAround = function() { if (this.TextAroundTimer) return; let arrResult = []; for (let nId = 0; nId < this.Id; ++nId) { if (!this.Elements[nId] || undefined === this.TextArround[nId]) continue; arrResult.push([nId, this.TextArround[nId]]); } let oApi = this.LogicDocument.GetApi(); oApi.sync_startTextAroundSearch(); oApi.sync_getTextAroundSearchPack(arrResult); oApi.sync_endTextAroundSearch(); }; CDocumentSearch.prototype.SendClearAllTextAround = function() { if (this.TextAroundEmpty) return; let oApi = this.LogicDocument.GetApi(); if (!oApi) return; oApi.sync_startTextAroundSearch(); oApi.sync_endTextAroundSearch(); this.TextAroundEmpty = true; }; //--------------------------------------------------------export---------------------------------------------------- window['AscCommonWord'] = window['AscCommonWord'] || {}; window['AscCommonWord'].CDocumentSearch = CDocumentSearch; })(window);