mirror of
https://github.com/ONLYOFFICE/sdkjs.git
synced 2026-02-10 18:15:19 +08:00
fix/bug-46893 (#1159)
[se] Fix bug 46893 Co-authored-by: GoshaZotov <Igor.Zotov@onlyoffice.com> Co-committed-by: GoshaZotov <Igor.Zotov@onlyoffice.com>
This commit is contained in:
@ -1865,6 +1865,114 @@
|
||||
});
|
||||
};
|
||||
|
||||
MultiplyRange.prototype.getUnionRanges = function() {
|
||||
let ranges = this.ranges;
|
||||
if (!ranges || ranges.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (ranges.length === 1) {
|
||||
return [ranges[0].clone()];
|
||||
}
|
||||
|
||||
const rangesByRow = new Array(ranges.length);
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
rangesByRow[i] = ranges[i].clone(true);
|
||||
}
|
||||
|
||||
// First sorting - by row starting position
|
||||
rangesByRow.sort(function(a, b) { return a.r1 - b.r1; });
|
||||
|
||||
// First merging pass - combine vertically aligned ranges
|
||||
const result = [rangesByRow[0]];
|
||||
let currentRange = result[0];
|
||||
|
||||
for (let i = 1; i < rangesByRow.length; i++) {
|
||||
const range = rangesByRow[i];
|
||||
|
||||
// Check if ranges have same columns and are adjacent or overlapping rows
|
||||
if (range.c1 === currentRange.c1 && range.c2 === currentRange.c2 &&
|
||||
range.r1 <= currentRange.r2 + 1) {
|
||||
// Extend current range down
|
||||
currentRange.r2 = Math.max(currentRange.r2, range.r2);
|
||||
} else {
|
||||
// Start new range if can't merge
|
||||
result.push(range);
|
||||
currentRange = range;
|
||||
}
|
||||
}
|
||||
|
||||
// Second sorting - by column starting position
|
||||
result.sort(function(a, b) { return a.c1 - b.c1; });
|
||||
|
||||
// Second merging pass - combine horizontally aligned ranges
|
||||
const finalResult = [result[0]];
|
||||
let currentHorRange = finalResult[0];
|
||||
|
||||
for (let i = 1; i < result.length; i++) {
|
||||
const range = result[i];
|
||||
|
||||
// Check if ranges have same rows and are adjacent or overlapping columns
|
||||
if (range.r1 === currentHorRange.r1 && range.r2 === currentHorRange.r2 &&
|
||||
range.c1 <= currentHorRange.c2 + 1) {
|
||||
// Extend current range horizontally
|
||||
currentHorRange.c2 = Math.max(currentHorRange.c2, range.c2);
|
||||
} else {
|
||||
// Start new range if can't merge
|
||||
finalResult.push(range);
|
||||
currentHorRange = range;
|
||||
}
|
||||
}
|
||||
|
||||
// Final merging pass - handle more complex overlapping cases
|
||||
let changed = true;
|
||||
let iterationRanges = finalResult.slice();
|
||||
|
||||
// Limit iterations to avoid excessive processing
|
||||
for (let iteration = 0; iteration < 3 && changed && iterationRanges.length > 1; iteration++) {
|
||||
changed = false;
|
||||
const tempRanges = [];
|
||||
|
||||
for (let i = 0; i < iterationRanges.length; i++) {
|
||||
const currentRange = iterationRanges[i];
|
||||
let merged = false;
|
||||
|
||||
// Try to merge with ranges already in result
|
||||
for (let j = 0; j < tempRanges.length; j++) {
|
||||
const tempRange = tempRanges[j];
|
||||
|
||||
// Check for possible merging cases (same columns or same rows with adjacency)
|
||||
if ((tempRange.c1 === currentRange.c1 && tempRange.c2 === currentRange.c2 &&
|
||||
(tempRange.r2 + 1 === currentRange.r1 || currentRange.r2 + 1 === tempRange.r1)) ||
|
||||
(tempRange.r1 === currentRange.r1 && tempRange.r2 === currentRange.r2 &&
|
||||
(tempRange.c2 + 1 === currentRange.c1 || currentRange.c2 + 1 === tempRange.c1))) {
|
||||
|
||||
// Merge ranges by taking the outer boundaries
|
||||
tempRange.c1 = Math.min(tempRange.c1, currentRange.c1);
|
||||
tempRange.r1 = Math.min(tempRange.r1, currentRange.r1);
|
||||
tempRange.c2 = Math.max(tempRange.c2, currentRange.c2);
|
||||
tempRange.r2 = Math.max(tempRange.r2, currentRange.r2);
|
||||
|
||||
merged = true;
|
||||
changed = true;
|
||||
break; // Exit inner loop once merged
|
||||
}
|
||||
}
|
||||
|
||||
// Add to result if couldn't merge
|
||||
if (!merged) {
|
||||
tempRanges.push(currentRange);
|
||||
}
|
||||
}
|
||||
|
||||
// Update working set for next iteration
|
||||
iterationRanges = tempRanges;
|
||||
}
|
||||
|
||||
return iterationRanges;
|
||||
};
|
||||
|
||||
MultiplyRange.prototype.isNull = function () {
|
||||
if (!this.ranges || 0 === this.ranges.length || (1 === this.ranges.length && this.ranges[0] == null)) {
|
||||
return true;
|
||||
|
||||
@ -7320,6 +7320,9 @@
|
||||
return this.props && this.props.specificRange;
|
||||
};
|
||||
|
||||
CDocumentSearchExcel.prototype.checkMaxReplacedCells = function () {
|
||||
return this.Count > 1000;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@ -553,6 +553,8 @@ function isAllowPasteLink(pastedWb) {
|
||||
this.vScrollPxStep = null;
|
||||
this.hScrollPxStep = null;
|
||||
|
||||
this._replaceCellTextManager = null;
|
||||
|
||||
this._init();
|
||||
|
||||
return this;
|
||||
@ -18425,15 +18427,33 @@ function isAllowPasteLink(pastedWb) {
|
||||
}
|
||||
|
||||
options.countReplace = 0;
|
||||
if (options.isReplaceAll && false === this.collaborativeEditing.getCollaborativeEditing()) {
|
||||
this._isLockedCells(aReplaceCells, /*subType*/null, function () {
|
||||
t._replaceCellText(aReplaceCells, options, lockDraw, callback, true);
|
||||
|
||||
//lock all if large replaced range
|
||||
let bCollaborativeEditing = this.collaborativeEditing.getCollaborativeEditing();
|
||||
if (options.isReplaceAll && (false === bCollaborativeEditing || (this.workbook.SearchEngine && this.workbook.SearchEngine.checkMaxReplacedCells()))) {
|
||||
let aReplaceCellsUnion = false === bCollaborativeEditing ? aReplaceCells : new AscCommonExcel.MultiplyRange(aReplaceCells).getUnionRanges();
|
||||
this._isLockedCells(aReplaceCellsUnion, /*subType*/null, function (success) {
|
||||
if (!success) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
t._replaceCellTextFast(aReplaceCells, options, lockDraw, callback, true);
|
||||
});
|
||||
} else {
|
||||
this._replaceCellText(aReplaceCells, options, lockDraw, callback, false);
|
||||
}
|
||||
};
|
||||
|
||||
WorksheetView.prototype._replaceCellTextFast = function (aReplaceCells, options, lockDraw, callback, oneUser) {
|
||||
// Use CReplaceCellTextManager for asynchronous text replacement processing
|
||||
if (!this._replaceCellTextManager) {
|
||||
this._replaceCellTextManager = new CReplaceCellTextManager();
|
||||
}
|
||||
|
||||
// Start asynchronous processing using timer
|
||||
this._replaceCellTextManager.Begin(this, aReplaceCells, options, lockDraw, callback, oneUser);
|
||||
};
|
||||
|
||||
WorksheetView.prototype._replaceCellText = function (aReplaceCells, options, lockDraw, callback, oneUser) {
|
||||
var t = this;
|
||||
var needLockCell = !oneUser;
|
||||
@ -30061,6 +30081,114 @@ function isAllowPasteLink(pastedWb) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class for asynchronous cell text replacement using timer
|
||||
* @constructor
|
||||
*/
|
||||
function CReplaceCellTextManager() {
|
||||
AscCommon.CActionOnTimerBase.call(this);
|
||||
|
||||
this.ws = null;
|
||||
this.replaceCells = [];
|
||||
this.options = null;
|
||||
this.lockDraw = false;
|
||||
this.callback = null;
|
||||
this.oneUser = false;
|
||||
this.needLockCell = false;
|
||||
this.isSC = false;
|
||||
|
||||
this.FirstActionOnTimer = true;
|
||||
this.Index = 0;
|
||||
}
|
||||
|
||||
CReplaceCellTextManager.prototype = Object.create(AscCommon.CActionOnTimerBase.prototype);
|
||||
CReplaceCellTextManager.prototype.constructor = CReplaceCellTextManager;
|
||||
|
||||
CReplaceCellTextManager.prototype.OnBegin = function(ws, aReplaceCells, options, lockDraw, callback, oneUser) {
|
||||
this.ws = ws;
|
||||
this.replaceCells = aReplaceCells;
|
||||
this.options = options;
|
||||
this.lockDraw = lockDraw;
|
||||
this.callback = callback;
|
||||
this.oneUser = oneUser;
|
||||
this.needLockCell = !oneUser;
|
||||
this.isSC = options.isSpellCheck;
|
||||
|
||||
this.Index = options.indexInArray || 0;
|
||||
};
|
||||
|
||||
CReplaceCellTextManager.prototype.OnEnd = function() {
|
||||
// After processing all cells, unlock calculation and draw
|
||||
this.ws.model.workbook.dependencyFormulas.unlockRecal();
|
||||
this.ws.draw(this.lockDraw);
|
||||
|
||||
if (this.callback) {
|
||||
this.callback(this.options);
|
||||
}
|
||||
};
|
||||
|
||||
CReplaceCellTextManager.prototype.IsContinue = function() {
|
||||
return (this.Index < this.replaceCells.length);
|
||||
};
|
||||
|
||||
CReplaceCellTextManager.prototype.DoAction = function() {
|
||||
const cell = this.replaceCells[this.Index];
|
||||
const t = this.ws;
|
||||
|
||||
this.Index++;
|
||||
this.options.indexInArray = this.Index;
|
||||
|
||||
// Check for protected ranges
|
||||
if (cell && t.model.isUserProtectedRangesIntersection(cell)) {
|
||||
t.model.workbook.handlers.trigger("asc_onError", c_oAscError.ID.ProtectedRangeByOtherUser, c_oAscError.Level.NoCritical);
|
||||
this.options.error = true;
|
||||
this.End();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check cell locking or perform replacement
|
||||
const isSuccess = !this.needLockCell || t._isLockedCells(cell, /*subType*/null, function(){});
|
||||
if (isSuccess) {
|
||||
const c = t._getVisibleCell(cell.c1, cell.r1);
|
||||
let cellValue = c.getValueForEdit();
|
||||
let v, newValue;
|
||||
const oldCellValue = cellValue;
|
||||
|
||||
// Replace text depending on the mode
|
||||
if (!this.isSC) {
|
||||
cellValue = cellValue.replace(this.options.findRegExp, () => {
|
||||
++this.options.countReplace;
|
||||
return this.options.replaceWith;
|
||||
});
|
||||
} else {
|
||||
cellValue = AscCommonExcel.replaceSpellCheckWords(cellValue, this.options);
|
||||
}
|
||||
|
||||
const isNeedToSave = oldCellValue !== cellValue;
|
||||
if (isNeedToSave) {
|
||||
// Create new fragments with replaced text
|
||||
v = c.getValueForEdit2().slice(0, 1);
|
||||
newValue = [];
|
||||
newValue[0] = new AscCommonExcel.Fragment({text: cellValue, format: v[0].format.clone()});
|
||||
|
||||
// Save new value
|
||||
if (!t._saveCellValueAfterEdit(c, newValue, /*flags*/undefined, /*isNotHistory*/true, /*lockDraw*/true)) {
|
||||
this.options.error = true;
|
||||
this.End();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update search elements
|
||||
if (t.workbook.SearchEngine) {
|
||||
t.workbook.SearchEngine.removeFromSearchElems(cell.c1, cell.r1, t.model);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If cell locking failed, stop processing
|
||||
this.options.error = true;
|
||||
this.End();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------export---------------------------------------------------
|
||||
window['AscCommonExcel'] = window['AscCommonExcel'] || {};
|
||||
|
||||
Reference in New Issue
Block a user