ep_data_tables 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/static/js/client_hooks.js +524 -548
|
@@ -17,7 +17,6 @@ const ATTR_TABLE_JSON = 'tbljson';
|
|
|
17
17
|
const ATTR_CELL = 'td';
|
|
18
18
|
const ATTR_CLASS_PREFIX = 'tbljson-'; // For finding the class in DOM
|
|
19
19
|
const log = (...m) => console.debug('[ep_data_tables:client_hooks]', ...m);
|
|
20
|
-
log('version 0.0.6');
|
|
21
20
|
const DELIMITER = '\u241F'; // Internal column delimiter (␟)
|
|
22
21
|
// Use the same rare character inside the hidden span so acePostWriteDomLineHTML can
|
|
23
22
|
// still find delimiters when it splits node.innerHTML.
|
|
@@ -67,14 +66,13 @@ let isAndroidChromeComposition = false;
|
|
|
67
66
|
let handledCurrentComposition = false;
|
|
68
67
|
// Suppress all beforeinput insertText events during an Android Chrome IME composition
|
|
69
68
|
let suppressBeforeInputInsertTextDuringComposition = false;
|
|
70
|
-
// Helper to detect Android
|
|
71
|
-
function
|
|
69
|
+
// Helper to detect any Android browser (exclude iOS/Safari)
|
|
70
|
+
function isAndroidUA() {
|
|
72
71
|
const ua = (navigator.userAgent || '').toLowerCase();
|
|
73
72
|
const isAndroid = ua.includes('android');
|
|
74
73
|
const isIOS = ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod') || ua.includes('crios');
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return isAndroid && !isIOS && !isFirefox && isChromiumFamily;
|
|
74
|
+
// Safari on Android is rare (WebKit ports), but our exclusion target is iOS Safari; we exclude all iOS above
|
|
75
|
+
return isAndroid && !isIOS;
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
// ─────────────────── Reusable Helper Functions ───────────────────
|
|
@@ -122,16 +120,16 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
|
|
|
122
120
|
try {
|
|
123
121
|
const metadata = JSON.parse(attribs);
|
|
124
122
|
if (metadata && metadata.tblId) {
|
|
125
|
-
|
|
123
|
+
// log(`${funcName}: Found metadata via attribute for line ${lineNum}`);
|
|
126
124
|
return metadata;
|
|
127
125
|
}
|
|
128
126
|
} catch (e) {
|
|
129
|
-
|
|
127
|
+
// log(`${funcName}: Invalid JSON in tbljson attribute on line ${lineNum}:`, e.message);
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
|
|
133
131
|
// Fallback for block-styled lines.
|
|
134
|
-
|
|
132
|
+
// log(`${funcName}: No valid attribute on line ${lineNum}, checking DOM.`);
|
|
135
133
|
const rep = editorInfo.ace_getRep();
|
|
136
134
|
|
|
137
135
|
// This is the fix: Get the lineNode directly from the rep. It's more reliable
|
|
@@ -140,7 +138,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
|
|
|
140
138
|
const lineNode = lineEntry?.lineNode;
|
|
141
139
|
|
|
142
140
|
if (!lineNode) {
|
|
143
|
-
|
|
141
|
+
// log(`${funcName}: Could not find line node in rep for line ${lineNum}`);
|
|
144
142
|
return null;
|
|
145
143
|
}
|
|
146
144
|
|
|
@@ -152,7 +150,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
|
|
|
152
150
|
try {
|
|
153
151
|
const decodedString = atob(encodedData);
|
|
154
152
|
const metadata = JSON.parse(decodedString);
|
|
155
|
-
|
|
153
|
+
// log(`${funcName}: Reconstructed metadata from DOM for line ${lineNum}:`, metadata);
|
|
156
154
|
return metadata;
|
|
157
155
|
} catch (e) {
|
|
158
156
|
console.error(`${funcName}: Failed to decode/parse tbljson class on line ${lineNum}:`, e);
|
|
@@ -162,7 +160,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
|
|
|
162
160
|
}
|
|
163
161
|
}
|
|
164
162
|
|
|
165
|
-
|
|
163
|
+
// log(`${funcName}: Could not find table metadata for line ${lineNum} in DOM.`);
|
|
166
164
|
return null;
|
|
167
165
|
} catch (e) {
|
|
168
166
|
console.error(`[ep_data_tables] ${funcName}: Error getting metadata for line ${lineNum}:`, e);
|
|
@@ -183,7 +181,7 @@ function getTableLineMetadata(lineNum, editorInfo, docManager) {
|
|
|
183
181
|
*/
|
|
184
182
|
function navigateToNextCell(currentLineNum, currentCellIndex, tableMetadata, shiftKey, editorInfo, docManager) {
|
|
185
183
|
const funcName = 'navigateToNextCell';
|
|
186
|
-
|
|
184
|
+
// log(`${funcName}: START - Current: Line=${currentLineNum}, Cell=${currentCellIndex}, Shift=${shiftKey}`);
|
|
187
185
|
|
|
188
186
|
try {
|
|
189
187
|
let targetRow = tableMetadata.row;
|
|
@@ -207,12 +205,12 @@ function navigateToNextCell(currentLineNum, currentCellIndex, tableMetadata, shi
|
|
|
207
205
|
}
|
|
208
206
|
}
|
|
209
207
|
|
|
210
|
-
|
|
208
|
+
// log(`${funcName}: Target coordinates - Row=${targetRow}, Col=${targetCol}`);
|
|
211
209
|
|
|
212
210
|
// Find the line number for the target row
|
|
213
211
|
const targetLineNum = findLineForTableRow(tableMetadata.tblId, targetRow, editorInfo, docManager);
|
|
214
212
|
if (targetLineNum === -1) {
|
|
215
|
-
|
|
213
|
+
// log(`${funcName}: Could not find line for target row ${targetRow}`);
|
|
216
214
|
return false;
|
|
217
215
|
}
|
|
218
216
|
|
|
@@ -236,25 +234,25 @@ function navigateToNextCell(currentLineNum, currentCellIndex, tableMetadata, shi
|
|
|
236
234
|
*/
|
|
237
235
|
function navigateToCellBelow(currentLineNum, currentCellIndex, tableMetadata, editorInfo, docManager) {
|
|
238
236
|
const funcName = 'navigateToCellBelow';
|
|
239
|
-
|
|
237
|
+
// log(`${funcName}: START - Current: Line=${currentLineNum}, Cell=${currentCellIndex}`);
|
|
240
238
|
|
|
241
239
|
try {
|
|
242
240
|
const targetRow = tableMetadata.row + 1;
|
|
243
241
|
const targetCol = currentCellIndex;
|
|
244
242
|
|
|
245
|
-
|
|
243
|
+
// log(`${funcName}: Target coordinates - Row=${targetRow}, Col=${targetCol}`);
|
|
246
244
|
|
|
247
245
|
// Find the line number for the target row
|
|
248
246
|
const targetLineNum = findLineForTableRow(tableMetadata.tblId, targetRow, editorInfo, docManager);
|
|
249
247
|
|
|
250
248
|
if (targetLineNum !== -1) {
|
|
251
249
|
// Found the row below, navigate to it.
|
|
252
|
-
|
|
250
|
+
// log(`${funcName}: Found line for target row ${targetRow}, navigating.`);
|
|
253
251
|
return navigateToCell(targetLineNum, targetCol, editorInfo, docManager);
|
|
254
252
|
} else {
|
|
255
253
|
// Could not find the row below, we must be on the last line.
|
|
256
254
|
// Create a new, empty line after the table.
|
|
257
|
-
|
|
255
|
+
// log(`${funcName}: Could not find next row. Creating new line after table.`);
|
|
258
256
|
const rep = editorInfo.ace_getRep();
|
|
259
257
|
const lineTextLength = rep.lines.atIndex(currentLineNum).text.length;
|
|
260
258
|
const endOfLinePos = [currentLineNum, lineTextLength];
|
|
@@ -272,7 +270,7 @@ function navigateToCellBelow(currentLineNum, currentCellIndex, tableMetadata, ed
|
|
|
272
270
|
// We've now exited the table, so clear the last-clicked state.
|
|
273
271
|
const editor = editorInfo.editor;
|
|
274
272
|
if (editor) editor.ep_data_tables_last_clicked = null;
|
|
275
|
-
|
|
273
|
+
// log(`${funcName}: Cleared last click info as we have exited the table.`);
|
|
276
274
|
|
|
277
275
|
return true; // We handled it.
|
|
278
276
|
}
|
|
@@ -292,12 +290,12 @@ function navigateToCellBelow(currentLineNum, currentCellIndex, tableMetadata, ed
|
|
|
292
290
|
*/
|
|
293
291
|
function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
|
|
294
292
|
const funcName = 'findLineForTableRow';
|
|
295
|
-
|
|
293
|
+
// log(`${funcName}: Searching for tblId=${tblId}, row=${targetRow}`);
|
|
296
294
|
|
|
297
295
|
try {
|
|
298
296
|
const rep = editorInfo.ace_getRep();
|
|
299
297
|
if (!rep || !rep.lines) {
|
|
300
|
-
|
|
298
|
+
// log(`${funcName}: Could not get rep or rep.lines`);
|
|
301
299
|
return -1;
|
|
302
300
|
}
|
|
303
301
|
|
|
@@ -315,7 +313,7 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
|
|
|
315
313
|
const domTblId = tableInDOM.getAttribute('data-tblId');
|
|
316
314
|
const domRow = tableInDOM.getAttribute('data-row');
|
|
317
315
|
if (domTblId === tblId && domRow !== null && parseInt(domRow, 10) === targetRow) {
|
|
318
|
-
|
|
316
|
+
// log(`${funcName}: Found target via DOM: line ${lineIndex}`);
|
|
319
317
|
return lineIndex;
|
|
320
318
|
}
|
|
321
319
|
}
|
|
@@ -325,7 +323,7 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
|
|
|
325
323
|
if (lineAttrString) {
|
|
326
324
|
const lineMetadata = JSON.parse(lineAttrString);
|
|
327
325
|
if (lineMetadata.tblId === tblId && lineMetadata.row === targetRow) {
|
|
328
|
-
|
|
326
|
+
// log(`${funcName}: Found target via attribute: line ${lineIndex}`);
|
|
329
327
|
return lineIndex;
|
|
330
328
|
}
|
|
331
329
|
}
|
|
@@ -334,7 +332,7 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
|
|
|
334
332
|
}
|
|
335
333
|
}
|
|
336
334
|
|
|
337
|
-
|
|
335
|
+
// log(`${funcName}: Target row not found`);
|
|
338
336
|
return -1;
|
|
339
337
|
} catch (e) {
|
|
340
338
|
console.error(`[ep_data_tables] ${funcName}: Error searching for line:`, e);
|
|
@@ -352,19 +350,19 @@ function findLineForTableRow(tblId, targetRow, editorInfo, docManager) {
|
|
|
352
350
|
*/
|
|
353
351
|
function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager) {
|
|
354
352
|
const funcName = 'navigateToCell';
|
|
355
|
-
|
|
353
|
+
// log(`${funcName}: START - Target: Line=${targetLineNum}, Cell=${targetCellIndex}`);
|
|
356
354
|
let targetPos;
|
|
357
355
|
|
|
358
356
|
try {
|
|
359
357
|
const rep = editorInfo.ace_getRep();
|
|
360
358
|
if (!rep || !rep.lines) {
|
|
361
|
-
|
|
359
|
+
// log(`${funcName}: Could not get rep or rep.lines`);
|
|
362
360
|
return false;
|
|
363
361
|
}
|
|
364
362
|
|
|
365
363
|
const lineEntry = rep.lines.atIndex(targetLineNum);
|
|
366
364
|
if (!lineEntry) {
|
|
367
|
-
|
|
365
|
+
// log(`${funcName}: Could not get line entry for line ${targetLineNum}`);
|
|
368
366
|
return false;
|
|
369
367
|
}
|
|
370
368
|
|
|
@@ -372,7 +370,7 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
|
|
|
372
370
|
const cells = lineText.split(DELIMITER);
|
|
373
371
|
|
|
374
372
|
if (targetCellIndex >= cells.length) {
|
|
375
|
-
|
|
373
|
+
// log(`${funcName}: Target cell ${targetCellIndex} doesn't exist (only ${cells.length} cells)`);
|
|
376
374
|
return false;
|
|
377
375
|
}
|
|
378
376
|
|
|
@@ -399,12 +397,12 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
|
|
|
399
397
|
cellIndex: targetCellIndex,
|
|
400
398
|
relativePos: targetCellContent.length,
|
|
401
399
|
};
|
|
402
|
-
|
|
400
|
+
// log(`${funcName}: Pre-emptively updated stored click info:`, editor.ep_data_tables_last_clicked);
|
|
403
401
|
} else {
|
|
404
|
-
|
|
402
|
+
// log(`${funcName}: Could not get table metadata for target line ${targetLineNum}, cannot update click info.`);
|
|
405
403
|
}
|
|
406
404
|
} catch (e) {
|
|
407
|
-
|
|
405
|
+
// log(`${funcName}: Could not update stored click info before navigation:`, e.message);
|
|
408
406
|
}
|
|
409
407
|
|
|
410
408
|
// The previous attempts involving wrappers and poking the renderer have all
|
|
@@ -414,17 +412,17 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
|
|
|
414
412
|
try {
|
|
415
413
|
// 1. Update the internal representation of the selection.
|
|
416
414
|
editorInfo.ace_performSelectionChange(targetPos, targetPos, false);
|
|
417
|
-
|
|
415
|
+
// log(`${funcName}: Updated internal selection to [${targetPos}]`);
|
|
418
416
|
|
|
419
417
|
// 2. Explicitly tell the editor to update the browser's visual selection
|
|
420
418
|
// to match the new internal representation. This is the correct way to
|
|
421
419
|
// make the caret appear in the new location without causing a race condition.
|
|
422
420
|
editorInfo.ace_updateBrowserSelectionFromRep();
|
|
423
|
-
|
|
421
|
+
// log(`${funcName}: Called updateBrowserSelectionFromRep to sync visual caret.`);
|
|
424
422
|
|
|
425
423
|
// 3. Ensure the editor has focus.
|
|
426
424
|
editorInfo.ace_focus();
|
|
427
|
-
|
|
425
|
+
// log(`${funcName}: Editor focused.`);
|
|
428
426
|
|
|
429
427
|
} catch(e) {
|
|
430
428
|
console.error(`[ep_data_tables] ${funcName}: Error during direct navigation update:`, e);
|
|
@@ -437,7 +435,7 @@ function navigateToCell(targetLineNum, targetCellIndex, editorInfo, docManager)
|
|
|
437
435
|
return false;
|
|
438
436
|
}
|
|
439
437
|
|
|
440
|
-
|
|
438
|
+
// log(`${funcName}: Navigation considered successful.`);
|
|
441
439
|
return true;
|
|
442
440
|
}
|
|
443
441
|
|
|
@@ -448,36 +446,36 @@ exports.collectContentPre = (hook, ctx) => {
|
|
|
448
446
|
const state = ctx.state;
|
|
449
447
|
const cc = ctx.cc; // ContentCollector instance
|
|
450
448
|
|
|
451
|
-
|
|
449
|
+
// log(`${funcName}: *** ENTRY POINT *** Hook: ${hook}, Node: ${node?.tagName}.${node?.className}`);
|
|
452
450
|
|
|
453
451
|
// ***** START Primary Path: Reconstruct from rendered table *****
|
|
454
452
|
if (node?.classList?.contains('ace-line')) {
|
|
455
453
|
const tableNode = node.querySelector('table.dataTable[data-tblId]');
|
|
456
454
|
if (tableNode) {
|
|
457
|
-
|
|
455
|
+
// log(`${funcName}: Found ace-line with rendered table. Attempting reconstruction from DOM.`);
|
|
458
456
|
|
|
459
457
|
const docManager = cc.documentAttributeManager;
|
|
460
458
|
const rep = cc.rep;
|
|
461
459
|
const lineNum = rep?.lines?.indexOfKey(node.id);
|
|
462
460
|
|
|
463
461
|
if (typeof lineNum === 'number' && lineNum >= 0 && docManager) {
|
|
464
|
-
|
|
462
|
+
// log(`${funcName}: Processing line ${lineNum} (NodeID: ${node.id}) for DOM reconstruction.`);
|
|
465
463
|
try {
|
|
466
464
|
const existingAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
|
|
467
|
-
|
|
465
|
+
// log(`${funcName}: Line ${lineNum} existing ${ATTR_TABLE_JSON} attribute: '${existingAttrString}'`);
|
|
468
466
|
|
|
469
467
|
if (existingAttrString) {
|
|
470
468
|
const existingMetadata = JSON.parse(existingAttrString);
|
|
471
469
|
if (existingMetadata && typeof existingMetadata.tblId !== 'undefined' &&
|
|
472
470
|
typeof existingMetadata.row !== 'undefined' && typeof existingMetadata.cols === 'number') {
|
|
473
|
-
|
|
471
|
+
// log(`${funcName}: Line ${lineNum} existing metadata is valid:`, existingMetadata);
|
|
474
472
|
|
|
475
473
|
const trNode = tableNode.querySelector('tbody > tr');
|
|
476
474
|
if (trNode) {
|
|
477
|
-
|
|
475
|
+
// log(`${funcName}: Line ${lineNum} found <tr> node for cell content extraction.`);
|
|
478
476
|
let cellHTMLSegments = Array.from(trNode.children).map((td, index) => {
|
|
479
477
|
let segmentHTML = td.innerHTML || '';
|
|
480
|
-
|
|
478
|
+
// log(`${funcName}: Line ${lineNum} TD[${index}] raw innerHTML (first 100): "${segmentHTML.substring(0,100)}"`);
|
|
481
479
|
|
|
482
480
|
const resizeHandleRegex = /<div class="ep-data_tables-resize-handle"[^>]*><\/div>/ig;
|
|
483
481
|
segmentHTML = segmentHTML.replace(resizeHandleRegex, '');
|
|
@@ -498,44 +496,44 @@ exports.collectContentPre = (hook, ctx) => {
|
|
|
498
496
|
const hidden = index === 0 ? '' :
|
|
499
497
|
/* keep the char in the DOM but make it visually disappear and non-editable */
|
|
500
498
|
`<span class="ep-data_tables-delim" contenteditable="false">${HIDDEN_DELIM}</span>`;
|
|
501
|
-
|
|
499
|
+
// log(`${funcName}: Line ${lineNum} TD[${index}] cleaned innerHTML (first 100): "${segmentHTML.substring(0,100)}"`);
|
|
502
500
|
return segmentHTML;
|
|
503
501
|
});
|
|
504
502
|
|
|
505
503
|
if (cellHTMLSegments.length !== existingMetadata.cols) {
|
|
506
|
-
|
|
504
|
+
// log(`${funcName}: WARNING Line ${lineNum}: Reconstructed cell count (${cellHTMLSegments.length}) does not match metadata cols (${existingMetadata.cols}). Padding/truncating.`);
|
|
507
505
|
while (cellHTMLSegments.length < existingMetadata.cols) cellHTMLSegments.push('');
|
|
508
506
|
if (cellHTMLSegments.length > existingMetadata.cols) cellHTMLSegments.length = existingMetadata.cols;
|
|
509
507
|
}
|
|
510
508
|
|
|
511
509
|
const canonicalLineText = cellHTMLSegments.join(DELIMITER);
|
|
512
510
|
state.line = canonicalLineText;
|
|
513
|
-
|
|
511
|
+
// log(`${funcName}: Line ${lineNum} successfully reconstructed ctx.state.line: "${canonicalLineText.substring(0, 200)}..."`);
|
|
514
512
|
|
|
515
513
|
state.lineAttributes = state.lineAttributes || [];
|
|
516
514
|
state.lineAttributes = state.lineAttributes.filter(attr => attr[0] !== ATTR_TABLE_JSON);
|
|
517
515
|
state.lineAttributes.push([ATTR_TABLE_JSON, existingAttrString]);
|
|
518
|
-
|
|
516
|
+
// log(`${funcName}: Line ${lineNum} ensured ${ATTR_TABLE_JSON} attribute is in state.lineAttributes.`);
|
|
519
517
|
|
|
520
|
-
|
|
518
|
+
// log(`${funcName}: Line ${lineNum} reconstruction complete. Returning undefined to prevent default DOM collection.`);
|
|
521
519
|
return undefined;
|
|
522
520
|
} else {
|
|
523
|
-
|
|
521
|
+
// log(`${funcName}: ERROR Line ${lineNum}: Could not find tbody > tr in rendered table for reconstruction.`);
|
|
524
522
|
}
|
|
525
523
|
} else {
|
|
526
|
-
|
|
524
|
+
// log(`${funcName}: ERROR Line ${lineNum}: Invalid or incomplete existing metadata from line attribute:`, existingMetadata);
|
|
527
525
|
}
|
|
528
526
|
} else {
|
|
529
|
-
|
|
527
|
+
// log(`${funcName}: WARNING Line ${lineNum}: No existing ${ATTR_TABLE_JSON} attribute found for reconstruction, despite table DOM presence. Table may be malformed or attribute lost.`);
|
|
530
528
|
const domTblId = tableNode.getAttribute('data-tblId');
|
|
531
529
|
const domRow = tableNode.getAttribute('data-row');
|
|
532
530
|
const trNode = tableNode.querySelector('tbody > tr');
|
|
533
531
|
if (domTblId && domRow !== null && trNode && trNode.children.length > 0) {
|
|
534
|
-
|
|
532
|
+
// log(`${funcName}: Line ${lineNum} FALLBACK: Attempting reconstruction using table DOM attributes as ${ATTR_TABLE_JSON} was missing.`);
|
|
535
533
|
const domCols = trNode.children.length;
|
|
536
534
|
const tempMetadata = {tblId: domTblId, row: parseInt(domRow, 10), cols: domCols};
|
|
537
535
|
const tempAttrString = JSON.stringify(tempMetadata);
|
|
538
|
-
|
|
536
|
+
// log(`${funcName}: Line ${lineNum} FALLBACK: Constructed temporary metadata: ${tempAttrString}`);
|
|
539
537
|
|
|
540
538
|
let cellHTMLSegments = Array.from(trNode.children).map((td, index) => {
|
|
541
539
|
let segmentHTML = td.innerHTML || '';
|
|
@@ -555,7 +553,7 @@ exports.collectContentPre = (hook, ctx) => {
|
|
|
555
553
|
});
|
|
556
554
|
|
|
557
555
|
if (cellHTMLSegments.length !== domCols) {
|
|
558
|
-
|
|
556
|
+
// log(`${funcName}: WARNING Line ${lineNum} (Fallback): Reconstructed cell count (${cellHTMLSegments.length}) does not match DOM cols (${domCols}).`);
|
|
559
557
|
while(cellHTMLSegments.length < domCols) cellHTMLSegments.push('');
|
|
560
558
|
if(cellHTMLSegments.length > domCols) cellHTMLSegments.length = domCols;
|
|
561
559
|
}
|
|
@@ -565,24 +563,24 @@ exports.collectContentPre = (hook, ctx) => {
|
|
|
565
563
|
state.lineAttributes = state.lineAttributes || [];
|
|
566
564
|
state.lineAttributes = state.lineAttributes.filter(attr => attr[0] !== ATTR_TABLE_JSON);
|
|
567
565
|
state.lineAttributes.push([ATTR_TABLE_JSON, tempAttrString]);
|
|
568
|
-
|
|
566
|
+
// log(`${funcName}: Line ${lineNum} FALLBACK: Successfully reconstructed line using DOM attributes. Returning undefined.`);
|
|
569
567
|
return undefined;
|
|
570
568
|
} else {
|
|
571
|
-
|
|
569
|
+
// log(`${funcName}: Line ${lineNum} FALLBACK: Could not reconstruct from DOM attributes due to missing info.`);
|
|
572
570
|
}
|
|
573
571
|
}
|
|
574
572
|
} catch (e) {
|
|
575
573
|
console.error(`[ep_data_tables] ${funcName}: Line ${lineNum} error during DOM reconstruction:`, e);
|
|
576
|
-
|
|
574
|
+
// log(`${funcName}: Line ${lineNum} Exception details:`, { message: e.message, stack: e.stack });
|
|
577
575
|
}
|
|
578
576
|
} else {
|
|
579
|
-
|
|
577
|
+
// log(`${funcName}: Could not get valid line number (${lineNum}), rep, or docManager for DOM reconstruction of ace-line.`);
|
|
580
578
|
}
|
|
581
579
|
} else {
|
|
582
|
-
|
|
580
|
+
// log(`${funcName}: Node is ace-line but no rendered table.dataTable[data-tblId] found. Allowing normal processing for: ${node?.className}`);
|
|
583
581
|
}
|
|
584
582
|
} else {
|
|
585
|
-
|
|
583
|
+
// log(`${funcName}: Node is not an ace-line (or node is null). Node: ${node?.tagName}.${node?.className}. Allowing normal processing.`);
|
|
586
584
|
}
|
|
587
585
|
// ***** END Primary Path *****
|
|
588
586
|
|
|
@@ -591,19 +589,19 @@ exports.collectContentPre = (hook, ctx) => {
|
|
|
591
589
|
const classes = ctx.cls ? ctx.cls.split(' ') : [];
|
|
592
590
|
let appliedAttribFromClass = false;
|
|
593
591
|
if (classes.length > 0) {
|
|
594
|
-
|
|
592
|
+
// log(`${funcName}: Secondary path - Checking classes on node ${node?.tagName}.${node?.className}: [${classes.join(', ')}]`);
|
|
595
593
|
for (const cls of classes) {
|
|
596
594
|
if (cls.startsWith('tbljson-')) {
|
|
597
|
-
|
|
595
|
+
// log(`${funcName}: Secondary path - Found tbljson class: ${cls} on node ${node?.tagName}.${node?.className}`);
|
|
598
596
|
const encodedMetadata = cls.substring(8);
|
|
599
597
|
try {
|
|
600
598
|
const decodedMetadata = dec(encodedMetadata);
|
|
601
599
|
if (decodedMetadata) {
|
|
602
600
|
cc.doAttrib(state, `${ATTR_TABLE_JSON}::${decodedMetadata}`);
|
|
603
601
|
appliedAttribFromClass = true;
|
|
604
|
-
|
|
602
|
+
// log(`${funcName}: Secondary path - Applied attribute to OP via cc.doAttrib for class ${cls.substring(0, 20)}... on ${node?.tagName}`);
|
|
605
603
|
} else {
|
|
606
|
-
|
|
604
|
+
// log(`${funcName}: Secondary path - ERROR - Decoded metadata is null or empty for class ${cls}`);
|
|
607
605
|
}
|
|
608
606
|
} catch (e) {
|
|
609
607
|
console.error(`[ep_data_tables] ${funcName}: Secondary path - Error processing tbljson class ${cls} on ${node?.tagName}:`, e);
|
|
@@ -612,49 +610,49 @@ exports.collectContentPre = (hook, ctx) => {
|
|
|
612
610
|
}
|
|
613
611
|
}
|
|
614
612
|
if (!appliedAttribFromClass && classes.some(c => c.startsWith('tbljson-'))) {
|
|
615
|
-
|
|
613
|
+
// log(`${funcName}: Secondary path - Found tbljson- class but failed to apply attribute.`);
|
|
616
614
|
} else if (!classes.some(c => c.startsWith('tbljson-'))) {
|
|
617
|
-
|
|
615
|
+
// log(`${funcName}: Secondary path - No tbljson- class found on this node.`);
|
|
618
616
|
}
|
|
619
617
|
} else {
|
|
620
|
-
|
|
618
|
+
// log(`${funcName}: Secondary path - Node ${node?.tagName}.${node?.className} has no ctx.cls or classes array is empty.`);
|
|
621
619
|
}
|
|
622
620
|
|
|
623
|
-
|
|
621
|
+
// log(`${funcName}: *** EXIT POINT *** For Node: ${node?.tagName}.${node?.className}. Applied from class: ${appliedAttribFromClass}`);
|
|
624
622
|
};
|
|
625
623
|
|
|
626
624
|
// ───────────── attribute → span‑class mapping (linestylefilter hook) ─────────
|
|
627
625
|
exports.aceAttribsToClasses = (hook, ctx) => {
|
|
628
626
|
const funcName = 'aceAttribsToClasses';
|
|
629
|
-
|
|
627
|
+
// log(`>>>> ${funcName}: Called with key: ${ctx.key}`); // log entry
|
|
630
628
|
if (ctx.key === ATTR_TABLE_JSON) {
|
|
631
|
-
|
|
629
|
+
// log(`${funcName}: Processing ATTR_TABLE_JSON.`);
|
|
632
630
|
// ctx.value is the raw JSON string from Etherpad's attribute pool
|
|
633
631
|
const rawJsonValue = ctx.value;
|
|
634
|
-
|
|
632
|
+
// log(`${funcName}: Received raw attribute value (ctx.value):`, rawJsonValue);
|
|
635
633
|
|
|
636
634
|
// Attempt to parse for logging purposes
|
|
637
635
|
let parsedMetadataForLog = '[JSON Parse Error]';
|
|
638
636
|
try {
|
|
639
637
|
parsedMetadataForLog = JSON.parse(rawJsonValue);
|
|
640
|
-
|
|
638
|
+
// log(`${funcName}: Value parsed for logging:`, parsedMetadataForLog);
|
|
641
639
|
} catch(e) {
|
|
642
|
-
|
|
640
|
+
// log(`${funcName}: Error parsing raw JSON value for logging:`, e);
|
|
643
641
|
// Continue anyway, enc() might still work if it's just a string
|
|
644
642
|
}
|
|
645
643
|
|
|
646
644
|
// Generate the class name by base64 encoding the raw JSON string.
|
|
647
645
|
// This ensures acePostWriteDomLineHTML receives the expected encoded format.
|
|
648
646
|
const className = `tbljson-${enc(rawJsonValue)}`;
|
|
649
|
-
|
|
647
|
+
// log(`${funcName}: Generated class name by encoding raw JSON: ${className}`);
|
|
650
648
|
return [className];
|
|
651
649
|
}
|
|
652
650
|
if (ctx.key === ATTR_CELL) {
|
|
653
651
|
// Keep this in case we want cell-specific styling later
|
|
654
|
-
|
|
652
|
+
//// log(`${funcName}: Processing ATTR_CELL: ${ctx.value}`); // Optional: Uncomment if needed
|
|
655
653
|
return [`tblCell-${ctx.value}`];
|
|
656
654
|
}
|
|
657
|
-
|
|
655
|
+
//// log(`${funcName}: Processing other key: ${ctx.key}`); // Optional: Uncomment if needed
|
|
658
656
|
return [];
|
|
659
657
|
};
|
|
660
658
|
|
|
@@ -677,11 +675,11 @@ function escapeHtml(text = '') {
|
|
|
677
675
|
// NEW Helper function to build table HTML from pre-rendered delimited content with resize handles
|
|
678
676
|
function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
|
|
679
677
|
const funcName = 'buildTableFromDelimitedHTML';
|
|
680
|
-
|
|
678
|
+
// log(`${funcName}: START`, { metadata, innerHTMLSegments });
|
|
681
679
|
|
|
682
680
|
if (!metadata || typeof metadata.tblId === 'undefined' || typeof metadata.row === 'undefined') {
|
|
683
681
|
console.error(`[ep_data_tables] ${funcName}: Invalid or missing metadata. Aborting.`);
|
|
684
|
-
|
|
682
|
+
// log(`${funcName}: END - Error`);
|
|
685
683
|
return '<table class="dataTable dataTable-error"><tbody><tr><td>Error: Missing table metadata</td></tr></tbody></table>'; // Return error table
|
|
686
684
|
}
|
|
687
685
|
|
|
@@ -747,17 +745,17 @@ function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
|
|
|
747
745
|
const tdContent = `<td style="${cellStyle}" data-column="${index}" draggable="false">${modifiedSegment}${resizeHandle}</td>`;
|
|
748
746
|
return tdContent;
|
|
749
747
|
}).join('');
|
|
750
|
-
|
|
748
|
+
// log(`${funcName}: Joined all cellsHtml:`, cellsHtml);
|
|
751
749
|
|
|
752
750
|
// Add 'dataTable-first-row' class if it's the logical first row (row index 0)
|
|
753
751
|
const firstRowClass = metadata.row === 0 ? ' dataTable-first-row' : '';
|
|
754
|
-
|
|
752
|
+
// log(`${funcName}: First row class applied: '${firstRowClass}'`);
|
|
755
753
|
|
|
756
754
|
// Construct the final table HTML
|
|
757
755
|
// Rely on CSS for border-collapse, width etc. Add data attributes from metadata.
|
|
758
|
-
const tableHtml = `<table class="dataTable${firstRowClass}" data-tblId="${metadata.tblId}" data-row="${metadata.row}" style="width:100%; border-collapse: collapse; table-layout: fixed;" draggable="false"><tbody><tr>${cellsHtml}</tr></tbody></table>`;
|
|
759
|
-
|
|
760
|
-
|
|
756
|
+
const tableHtml = `<table class="dataTable${firstRowClass}" writingsuggestions="false" data-tblId="${metadata.tblId}" data-row="${metadata.row}" style="width:100%; border-collapse: collapse; table-layout: fixed;" draggable="false"><tbody><tr>${cellsHtml}</tr></tbody></table>`;
|
|
757
|
+
// log(`${funcName}: Generated final table HTML:`, tableHtml);
|
|
758
|
+
// log(`${funcName}: END - Success`);
|
|
761
759
|
return tableHtml;
|
|
762
760
|
}
|
|
763
761
|
|
|
@@ -770,28 +768,28 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
770
768
|
const logPrefix = '[ep_data_tables:acePostWriteDomLineHTML]'; // Consistent prefix
|
|
771
769
|
|
|
772
770
|
// *** STARTUP LOGGING ***
|
|
773
|
-
|
|
771
|
+
// log(`${logPrefix} ----- START ----- NodeID: ${nodeId} LineNum: ${lineNum}`);
|
|
774
772
|
if (!node || !nodeId) {
|
|
775
|
-
|
|
773
|
+
// log(`${logPrefix} ERROR - Received invalid node or node without ID. Aborting.`);
|
|
776
774
|
console.error(`[ep_data_tables] ${funcName}: Received invalid node or node without ID.`);
|
|
777
775
|
return cb();
|
|
778
776
|
}
|
|
779
777
|
|
|
780
778
|
// *** ENHANCED DEBUG: Log complete DOM state ***
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
779
|
+
// log(`${logPrefix} NodeID#${nodeId}: COMPLETE DOM STRUCTURE DEBUG:`);
|
|
780
|
+
// log(`${logPrefix} NodeID#${nodeId}: Node tagName: ${node.tagName}`);
|
|
781
|
+
// log(`${logPrefix} NodeID#${nodeId}: Node className: ${node.className}`);
|
|
782
|
+
// log(`${logPrefix} NodeID#${nodeId}: Node innerHTML length: ${node.innerHTML?.length || 0}`);
|
|
783
|
+
// log(`${logPrefix} NodeID#${nodeId}: Node innerHTML (first 500 chars): "${(node.innerHTML || '').substring(0, 500)}"`);
|
|
784
|
+
// log(`${logPrefix} NodeID#${nodeId}: Node children count: ${node.children?.length || 0}`);
|
|
787
785
|
|
|
788
786
|
// log all child elements and their classes
|
|
789
787
|
if (node.children) {
|
|
790
788
|
for (let i = 0; i < Math.min(node.children.length, 10); i++) {
|
|
791
789
|
const child = node.children[i];
|
|
792
|
-
|
|
790
|
+
// log(`${logPrefix} NodeID#${nodeId}: Child[${i}] tagName: ${child.tagName}, className: "${child.className}", innerHTML length: ${child.innerHTML?.length || 0}`);
|
|
793
791
|
if (child.className && child.className.includes('tbljson-')) {
|
|
794
|
-
|
|
792
|
+
// log(`${logPrefix} NodeID#${nodeId}: *** FOUND TBLJSON CLASS ON CHILD[${i}] ***`);
|
|
795
793
|
}
|
|
796
794
|
}
|
|
797
795
|
}
|
|
@@ -800,69 +798,69 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
800
798
|
let encodedJsonString = null;
|
|
801
799
|
|
|
802
800
|
// --- 1. Find and Parse Metadata Attribute ---
|
|
803
|
-
|
|
801
|
+
// log(`${logPrefix} NodeID#${nodeId}: Searching for tbljson-* class...`);
|
|
804
802
|
|
|
805
803
|
// ENHANCED Helper function to recursively search for tbljson class in all descendants
|
|
806
804
|
function findTbljsonClass(element, depth = 0, path = '') {
|
|
807
805
|
const indent = ' '.repeat(depth);
|
|
808
|
-
|
|
806
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}Searching element: ${element.tagName || 'unknown'}, path: ${path}`);
|
|
809
807
|
|
|
810
808
|
// Check the element itself
|
|
811
809
|
if (element.classList) {
|
|
812
|
-
|
|
810
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has ${element.classList.length} classes: [${Array.from(element.classList).join(', ')}]`);
|
|
813
811
|
for (const cls of element.classList) {
|
|
814
812
|
if (cls.startsWith('tbljson-')) {
|
|
815
|
-
|
|
813
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}*** FOUND TBLJSON CLASS: ${cls.substring(8)} at depth ${depth}, path: ${path} ***`);
|
|
816
814
|
return cls.substring(8);
|
|
817
815
|
}
|
|
818
816
|
}
|
|
819
817
|
} else {
|
|
820
|
-
|
|
818
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has no classList`);
|
|
821
819
|
}
|
|
822
820
|
|
|
823
821
|
// Recursively check all descendants
|
|
824
822
|
if (element.children) {
|
|
825
|
-
|
|
823
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has ${element.children.length} children`);
|
|
826
824
|
for (let i = 0; i < element.children.length; i++) {
|
|
827
825
|
const child = element.children[i];
|
|
828
826
|
const childPath = `${path}>${child.tagName}[${i}]`;
|
|
829
827
|
const found = findTbljsonClass(child, depth + 1, childPath);
|
|
830
828
|
if (found) {
|
|
831
|
-
|
|
829
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}Returning found result from child: ${found}`);
|
|
832
830
|
return found;
|
|
833
831
|
}
|
|
834
832
|
}
|
|
835
833
|
} else {
|
|
836
|
-
|
|
834
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}Element has no children`);
|
|
837
835
|
}
|
|
838
836
|
|
|
839
|
-
|
|
837
|
+
// log(`${logPrefix} NodeID#${nodeId}: ${indent}No tbljson class found in this element or its children`);
|
|
840
838
|
return null;
|
|
841
839
|
}
|
|
842
840
|
|
|
843
841
|
// Search for tbljson class starting from the node
|
|
844
|
-
|
|
842
|
+
// log(`${logPrefix} NodeID#${nodeId}: Starting recursive search for tbljson class...`);
|
|
845
843
|
encodedJsonString = findTbljsonClass(node, 0, 'ROOT');
|
|
846
844
|
|
|
847
845
|
if (encodedJsonString) {
|
|
848
|
-
|
|
846
|
+
// log(`${logPrefix} NodeID#${nodeId}: *** SUCCESS: Found encoded tbljson class: ${encodedJsonString} ***`);
|
|
849
847
|
} else {
|
|
850
|
-
|
|
848
|
+
// log(`${logPrefix} NodeID#${nodeId}: *** NO TBLJSON CLASS FOUND ***`);
|
|
851
849
|
}
|
|
852
850
|
|
|
853
851
|
// If no attribute found, it's not a table line managed by us
|
|
854
852
|
if (!encodedJsonString) {
|
|
855
|
-
|
|
853
|
+
// log(`${logPrefix} NodeID#${nodeId}: No tbljson-* class found. Assuming not a table line. END.`);
|
|
856
854
|
|
|
857
855
|
// DEBUG: Add detailed logging to understand why tbljson class is missing
|
|
858
|
-
|
|
859
|
-
|
|
856
|
+
// log(`${logPrefix} NodeID#${nodeId}: DEBUG - Node tag: ${node.tagName}, Node classes:`, Array.from(node.classList || []));
|
|
857
|
+
// log(`${logPrefix} NodeID#${nodeId}: DEBUG - Node innerHTML (first 200 chars): "${(node.innerHTML || '').substring(0, 200)}"`);
|
|
860
858
|
|
|
861
859
|
// Check if there are any child elements with classes
|
|
862
860
|
if (node.children && node.children.length > 0) {
|
|
863
861
|
for (let i = 0; i < Math.min(node.children.length, 5); i++) {
|
|
864
862
|
const child = node.children[i];
|
|
865
|
-
|
|
863
|
+
// log(`${logPrefix} NodeID#${nodeId}: DEBUG - Child ${i} tag: ${child.tagName}, classes:`, Array.from(child.classList || []));
|
|
866
864
|
}
|
|
867
865
|
}
|
|
868
866
|
|
|
@@ -871,24 +869,24 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
871
869
|
if (existingTable) {
|
|
872
870
|
const existingTblId = existingTable.getAttribute('data-tblId');
|
|
873
871
|
const existingRow = existingTable.getAttribute('data-row');
|
|
874
|
-
|
|
872
|
+
// log(`${logPrefix} NodeID#${nodeId}: DEBUG - Found orphaned table! TblId: ${existingTblId}, Row: ${existingRow}`);
|
|
875
873
|
|
|
876
874
|
// This suggests the table exists but the tbljson class was lost
|
|
877
875
|
// Check if we're in a post-resize situation
|
|
878
876
|
if (existingTblId && existingRow !== null) {
|
|
879
|
-
|
|
877
|
+
// log(`${logPrefix} NodeID#${nodeId}: POTENTIAL ISSUE - Table exists but no tbljson class. This may be a post-resize issue.`);
|
|
880
878
|
|
|
881
879
|
// Try to look up what the metadata should be based on the table attributes
|
|
882
880
|
const tableCells = existingTable.querySelectorAll('td');
|
|
883
|
-
|
|
881
|
+
// log(`${logPrefix} NodeID#${nodeId}: Table has ${tableCells.length} cells`);
|
|
884
882
|
|
|
885
883
|
// log the current line's attribute state if we can get line number
|
|
886
884
|
if (lineNum !== undefined && args?.documentAttributeManager) {
|
|
887
885
|
try {
|
|
888
886
|
const currentLineAttr = args.documentAttributeManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
|
|
889
|
-
|
|
887
|
+
// log(`${logPrefix} NodeID#${nodeId}: Current line ${lineNum} tbljson attribute: ${currentLineAttr || 'NULL'}`);
|
|
890
888
|
} catch (e) {
|
|
891
|
-
|
|
889
|
+
// log(`${logPrefix} NodeID#${nodeId}: Error getting line attribute:`, e);
|
|
892
890
|
}
|
|
893
891
|
}
|
|
894
892
|
}
|
|
@@ -900,7 +898,7 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
900
898
|
// *** NEW CHECK: If table already rendered, skip regeneration ***
|
|
901
899
|
const existingTable = node.querySelector('table.dataTable[data-tblId]');
|
|
902
900
|
if (existingTable) {
|
|
903
|
-
|
|
901
|
+
// log(`${logPrefix} NodeID#${nodeId}: Table already exists in DOM. Skipping innerHTML replacement.`);
|
|
904
902
|
// Optionally, verify tblId matches metadata? For now, assume it's correct.
|
|
905
903
|
// const existingTblId = existingTable.getAttribute('data-tblId');
|
|
906
904
|
// try {
|
|
@@ -911,26 +909,26 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
911
909
|
return cb(); // Do nothing further
|
|
912
910
|
}
|
|
913
911
|
|
|
914
|
-
|
|
912
|
+
// log(`${logPrefix} NodeID#${nodeId}: Decoding and parsing metadata...`);
|
|
915
913
|
try {
|
|
916
914
|
const decoded = dec(encodedJsonString);
|
|
917
|
-
|
|
915
|
+
// log(`${logPrefix} NodeID#${nodeId}: Decoded string: ${decoded}`);
|
|
918
916
|
if (!decoded) throw new Error('Decoded string is null or empty.');
|
|
919
917
|
rowMetadata = JSON.parse(decoded);
|
|
920
|
-
|
|
918
|
+
// log(`${logPrefix} NodeID#${nodeId}: Parsed rowMetadata:`, rowMetadata);
|
|
921
919
|
|
|
922
920
|
// Validate essential metadata
|
|
923
921
|
if (!rowMetadata || typeof rowMetadata.tblId === 'undefined' || typeof rowMetadata.row === 'undefined' || typeof rowMetadata.cols !== 'number') {
|
|
924
922
|
throw new Error('Invalid or incomplete metadata (missing tblId, row, or cols).');
|
|
925
923
|
}
|
|
926
|
-
|
|
924
|
+
// log(`${logPrefix} NodeID#${nodeId}: Metadata validated successfully.`);
|
|
927
925
|
|
|
928
926
|
} catch(e) {
|
|
929
|
-
|
|
927
|
+
// log(`${logPrefix} NodeID#${nodeId}: FATAL ERROR - Failed to decode/parse/validate tbljson metadata. Rendering cannot proceed.`, e);
|
|
930
928
|
console.error(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Failed to decode/parse/validate tbljson.`, encodedJsonString, e);
|
|
931
929
|
// Optionally render an error state in the node?
|
|
932
930
|
node.innerHTML = '<div style="color:red; border: 1px solid red; padding: 5px;">[ep_data_tables] Error: Invalid table metadata attribute found.</div>';
|
|
933
|
-
|
|
931
|
+
// log(`${logPrefix} NodeID#${nodeId}: Rendered error message in node. END.`);
|
|
934
932
|
return cb();
|
|
935
933
|
}
|
|
936
934
|
// --- End Metadata Parsing ---
|
|
@@ -944,88 +942,66 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
944
942
|
// After an edit, aceKeyEvent updates atext, and node.innerHTML reflects that new "EditedCell1|Cell2" string.
|
|
945
943
|
// When styling is applied, it will include spans like: <span class="author-xxx bold">Cell1</span>|<span class="author-yyy italic">Cell2</span>
|
|
946
944
|
const delimitedTextFromLine = node.innerHTML;
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
945
|
+
// log(`${logPrefix} NodeID#${nodeId}: Using node.innerHTML for delimited text to preserve styling.`);
|
|
946
|
+
// log(`${logPrefix} NodeID#${nodeId}: Raw innerHTML length: ${delimitedTextFromLine?.length || 0}`);
|
|
947
|
+
// log(`${logPrefix} NodeID#${nodeId}: Raw innerHTML (first 1000 chars): "${(delimitedTextFromLine || '').substring(0, 1000)}"`);
|
|
950
948
|
|
|
951
949
|
// *** ENHANCED DEBUG: Analyze delimiter presence ***
|
|
952
950
|
const delimiterCount = (delimitedTextFromLine || '').split(DELIMITER).length - 1;
|
|
953
|
-
|
|
954
|
-
|
|
951
|
+
// log(`${logPrefix} NodeID#${nodeId}: Delimiter '${DELIMITER}' count in innerHTML: ${delimiterCount}`);
|
|
952
|
+
// log(`${logPrefix} NodeID#${nodeId}: Expected delimiters for ${rowMetadata.cols} columns: ${rowMetadata.cols - 1}`);
|
|
955
953
|
|
|
956
954
|
// log all delimiter positions
|
|
957
955
|
let pos = -1;
|
|
958
956
|
const delimiterPositions = [];
|
|
959
957
|
while ((pos = delimitedTextFromLine.indexOf(DELIMITER, pos + 1)) !== -1) {
|
|
960
958
|
delimiterPositions.push(pos);
|
|
961
|
-
|
|
959
|
+
// log(`${logPrefix} NodeID#${nodeId}: Delimiter found at position ${pos}, context: "${delimitedTextFromLine.substring(Math.max(0, pos - 20), pos + 21)}"`);
|
|
962
960
|
}
|
|
963
|
-
|
|
961
|
+
// log(`${logPrefix} NodeID#${nodeId}: All delimiter positions: [${delimiterPositions.join(', ')}]`);
|
|
964
962
|
|
|
965
963
|
// The DELIMITER const is defined at the top of this file.
|
|
966
964
|
// NEW: Remove all hidden-delimiter <span> wrappers **before** we split so
|
|
967
965
|
// the embedded delimiter character they carry doesn't inflate or shrink
|
|
968
966
|
// the segment count.
|
|
969
|
-
const spanDelimRegex = new RegExp('<span class="ep-data_tables-delim"[^>]*>' + DELIMITER + '
|
|
970
|
-
// Safari-specific normalization: it may serialize the delimiter as entities and inject Apple spans
|
|
971
|
-
const delimiterEntityHexRE = /␟/ig; // hex entity for U+241F
|
|
972
|
-
const delimiterEntityDecRE = /␟/g; // decimal entity for U+241F
|
|
973
|
-
const appleConvertedSpaceRE = /<span class="Apple-converted-space">[\s\u00A0]*<\/span>/ig;
|
|
974
|
-
const zeroWidthCharsRE = /[\u200B\u200C\u200D\uFEFF]/g;
|
|
975
|
-
|
|
976
|
-
const hexMatches = ((delimitedTextFromLine || '').match(delimiterEntityHexRE) || []).length;
|
|
977
|
-
const decMatches = ((delimitedTextFromLine || '').match(delimiterEntityDecRE) || []).length;
|
|
978
|
-
const appleSpaceMatches = ((delimitedTextFromLine || '').match(appleConvertedSpaceRE) || []).length;
|
|
979
|
-
|
|
967
|
+
const spanDelimRegex = new RegExp('<span class="ep-data_tables-delim"[^>]*>' + DELIMITER + '<\\/span>', 'ig');
|
|
980
968
|
const sanitizedHTMLForSplit = (delimitedTextFromLine || '')
|
|
981
969
|
.replace(spanDelimRegex, '')
|
|
982
970
|
// strip caret anchors from raw line html before split
|
|
983
|
-
.replace(/<span class="ep-data_tables-caret-anchor"[^>]*><\/span>/ig, '')
|
|
984
|
-
// Safari may serialize the delimiter as HTML entities – convert back to raw char
|
|
985
|
-
.replace(delimiterEntityHexRE, DELIMITER)
|
|
986
|
-
.replace(delimiterEntityDecRE, DELIMITER)
|
|
987
|
-
// Safari sometimes injects Apple-converted-space wrappers; collapse to a normal space
|
|
988
|
-
.replace(appleConvertedSpaceRE, ' ')
|
|
989
|
-
// Guard against invisible characters that can disturb splitting
|
|
990
|
-
.replace(zeroWidthCharsRE, '');
|
|
991
|
-
|
|
992
|
-
if ((hexMatches + decMatches + appleSpaceMatches) > 0) {
|
|
993
|
-
console.warn(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Normalized Safari entities/spans before splitting: hex=${hexMatches}, dec=${decMatches}, appleSpaces=${appleSpaceMatches}`);
|
|
994
|
-
}
|
|
995
|
-
|
|
971
|
+
.replace(/<span class="ep-data_tables-caret-anchor"[^>]*><\/span>/ig, '');
|
|
996
972
|
const htmlSegments = sanitizedHTMLForSplit.split(DELIMITER);
|
|
997
973
|
|
|
998
|
-
|
|
999
|
-
|
|
974
|
+
// log(`${logPrefix} NodeID#${nodeId}: *** SEGMENT ANALYSIS ***`);
|
|
975
|
+
// log(`${logPrefix} NodeID#${nodeId}: Split resulted in ${htmlSegments.length} segments`);
|
|
1000
976
|
for (let i = 0; i < htmlSegments.length; i++) {
|
|
1001
977
|
const segment = htmlSegments[i] || '';
|
|
1002
|
-
|
|
1003
|
-
|
|
978
|
+
// log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] length: ${segment.length}`);
|
|
979
|
+
// log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (first 200 chars): "${segment.substring(0, 200)}"`);
|
|
1004
980
|
if (segment.length > 200) {
|
|
1005
|
-
|
|
981
|
+
// log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (chars 200-400): "${segment.substring(200, 400)}"`);
|
|
1006
982
|
}
|
|
1007
983
|
if (segment.length > 400) {
|
|
1008
|
-
|
|
984
|
+
// log(`${logPrefix} NodeID#${nodeId}: Segment[${i}] content (chars 400-600): "${segment.substring(400, 600)}"`);
|
|
1009
985
|
}
|
|
1010
986
|
// Check if segment contains image-related content
|
|
1011
987
|
if (segment.includes('image:') || segment.includes('image-placeholder') || segment.includes('currently-selected')) {
|
|
1012
|
-
|
|
988
|
+
// log(`${logPrefix} NodeID#${nodeId}: *** SEGMENT[${i}] CONTAINS IMAGE CONTENT ***`);
|
|
1013
989
|
}
|
|
1014
990
|
}
|
|
1015
991
|
|
|
1016
|
-
|
|
992
|
+
// log(`${logPrefix} NodeID#${nodeId}: Parsed HTML segments (${htmlSegments.length}):`, htmlSegments.map(s => (s || '').substring(0,50) + (s && s.length > 50 ? '...' : '')));
|
|
1017
993
|
|
|
1018
994
|
// --- Enhanced Validation with Automatic Structure Reconstruction ---
|
|
1019
995
|
let finalHtmlSegments = htmlSegments;
|
|
1020
996
|
|
|
1021
997
|
if (htmlSegments.length !== rowMetadata.cols) {
|
|
1022
|
-
|
|
998
|
+
// log(`${logPrefix} NodeID#${nodeId}: *** MISMATCH DETECTED *** - Attempting reconstruction.`);
|
|
1023
999
|
|
|
1024
1000
|
// Check if this is an image selection issue
|
|
1025
1001
|
const hasImageSelected = delimitedTextFromLine.includes('currently-selected');
|
|
1026
1002
|
const hasImageContent = delimitedTextFromLine.includes('image:');
|
|
1027
1003
|
if (hasImageSelected) {
|
|
1028
|
-
|
|
1004
|
+
// log(`${logPrefix} NodeID#${nodeId}: *** POTENTIAL CAUSE: Image selection state may be affecting segment parsing ***`);
|
|
1029
1005
|
}
|
|
1030
1006
|
|
|
1031
1007
|
// First attempt: reconstruct using DOM spans that carry tblCell-N classes
|
|
@@ -1097,14 +1073,14 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
1097
1073
|
console.warn(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Could not reconstruct to expected ${rowMetadata.cols} segments. Got ${finalHtmlSegments.length}.`);
|
|
1098
1074
|
}
|
|
1099
1075
|
} else {
|
|
1100
|
-
|
|
1076
|
+
// log(`${logPrefix} NodeID#${nodeId}: Segment count matches metadata cols (${rowMetadata.cols}). Using original segments.`);
|
|
1101
1077
|
}
|
|
1102
1078
|
|
|
1103
1079
|
// --- 3. Build and Render Table ---
|
|
1104
|
-
|
|
1080
|
+
// log(`${logPrefix} NodeID#${nodeId}: Calling buildTableFromDelimitedHTML...`);
|
|
1105
1081
|
try {
|
|
1106
1082
|
const newTableHTML = buildTableFromDelimitedHTML(rowMetadata, finalHtmlSegments);
|
|
1107
|
-
|
|
1083
|
+
// log(`${logPrefix} NodeID#${nodeId}: Received new table HTML from helper. Replacing content.`);
|
|
1108
1084
|
|
|
1109
1085
|
// The old local findTbljsonElement is removed from here. We use the global one now.
|
|
1110
1086
|
const tbljsonElement = findTbljsonElement(node);
|
|
@@ -1117,24 +1093,24 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
1117
1093
|
const blockElements = ['center', 'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'right', 'left', 'ul', 'ol', 'li', 'code'];
|
|
1118
1094
|
|
|
1119
1095
|
if (blockElements.includes(parentTag)) {
|
|
1120
|
-
|
|
1096
|
+
// log(`${logPrefix} NodeID#${nodeId}: Preserving block element ${parentTag} and replacing its content with table.`);
|
|
1121
1097
|
tbljsonElement.parentElement.innerHTML = newTableHTML;
|
|
1122
1098
|
} else {
|
|
1123
|
-
|
|
1099
|
+
// log(`${logPrefix} NodeID#${nodeId}: Parent element ${parentTag} is not a block element, replacing entire node content.`);
|
|
1124
1100
|
node.innerHTML = newTableHTML;
|
|
1125
1101
|
}
|
|
1126
1102
|
} else {
|
|
1127
1103
|
// Replace the node's content entirely with the generated table
|
|
1128
|
-
|
|
1104
|
+
// log(`${logPrefix} NodeID#${nodeId}: No nested block element found, replacing entire node content.`);
|
|
1129
1105
|
node.innerHTML = newTableHTML;
|
|
1130
1106
|
}
|
|
1131
1107
|
|
|
1132
|
-
|
|
1108
|
+
// log(`${logPrefix} NodeID#${nodeId}: Successfully replaced content with new table structure.`);
|
|
1133
1109
|
} catch (renderError) {
|
|
1134
|
-
|
|
1110
|
+
// log(`${logPrefix} NodeID#${nodeId}: ERROR during table building or rendering.`, renderError);
|
|
1135
1111
|
console.error(`[ep_data_tables] ${funcName} NodeID#${nodeId}: Error building/rendering table.`, renderError);
|
|
1136
1112
|
node.innerHTML = '<div style="color:red; border: 1px solid red; padding: 5px;">[ep_data_tables] Error: Failed to render table structure.</div>';
|
|
1137
|
-
|
|
1113
|
+
// log(`${logPrefix} NodeID#${nodeId}: Rendered build/render error message in node. END.`);
|
|
1138
1114
|
return cb();
|
|
1139
1115
|
}
|
|
1140
1116
|
// --- End Table Building ---
|
|
@@ -1142,7 +1118,7 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
1142
1118
|
// *** REMOVED CACHING LOGIC ***
|
|
1143
1119
|
// The old logic based on tableRowNodes cache is completely removed.
|
|
1144
1120
|
|
|
1145
|
-
|
|
1121
|
+
// log(`${logPrefix}: ----- END ----- NodeID: ${nodeId}`);
|
|
1146
1122
|
return cb();
|
|
1147
1123
|
};
|
|
1148
1124
|
|
|
@@ -1168,34 +1144,34 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1168
1144
|
|
|
1169
1145
|
const startLogTime = Date.now();
|
|
1170
1146
|
const logPrefix = '[ep_data_tables:aceKeyEvent]';
|
|
1171
|
-
|
|
1147
|
+
// log(`${logPrefix} START Key='${evt?.key}' Code=${evt?.keyCode} Type=${evt?.type} Modifiers={ctrl:${evt?.ctrlKey},alt:${evt?.altKey},meta:${evt?.metaKey},shift:${evt?.shiftKey}}`, { selStart: rep?.selStart, selEnd: rep?.selEnd });
|
|
1172
1148
|
|
|
1173
1149
|
if (!rep || !rep.selStart || !editorInfo || !evt || !docManager) {
|
|
1174
|
-
|
|
1150
|
+
// log(`${logPrefix} Skipping - Missing critical context.`);
|
|
1175
1151
|
return false;
|
|
1176
1152
|
}
|
|
1177
1153
|
|
|
1178
1154
|
// Get caret info from event context - may be stale
|
|
1179
1155
|
const reportedLineNum = rep.selStart[0];
|
|
1180
1156
|
const reportedCol = rep.selStart[1];
|
|
1181
|
-
|
|
1157
|
+
// log(`${logPrefix} Reported caret from rep: Line=${reportedLineNum}, Col=${reportedCol}`);
|
|
1182
1158
|
|
|
1183
1159
|
// --- Get Table Metadata for the reported line ---
|
|
1184
1160
|
let tableMetadata = null;
|
|
1185
1161
|
let lineAttrString = null; // Store for potential use later
|
|
1186
1162
|
try {
|
|
1187
1163
|
// Add debugging to see what's happening with attribute retrieval
|
|
1188
|
-
|
|
1164
|
+
// log(`${logPrefix} DEBUG: Attempting to get ${ATTR_TABLE_JSON} attribute from line ${reportedLineNum}`);
|
|
1189
1165
|
lineAttrString = docManager.getAttributeOnLine(reportedLineNum, ATTR_TABLE_JSON);
|
|
1190
|
-
|
|
1166
|
+
// log(`${logPrefix} DEBUG: getAttributeOnLine returned: ${lineAttrString ? `"${lineAttrString}"` : 'null/undefined'}`);
|
|
1191
1167
|
|
|
1192
1168
|
// Also check if there are any attributes on this line at all
|
|
1193
1169
|
if (typeof docManager.getAttributesOnLine === 'function') {
|
|
1194
1170
|
try {
|
|
1195
1171
|
const allAttribs = docManager.getAttributesOnLine(reportedLineNum);
|
|
1196
|
-
|
|
1172
|
+
// log(`${logPrefix} DEBUG: All attributes on line ${reportedLineNum}:`, allAttribs);
|
|
1197
1173
|
} catch(e) {
|
|
1198
|
-
|
|
1174
|
+
// log(`${logPrefix} DEBUG: Error getting all attributes:`, e);
|
|
1199
1175
|
}
|
|
1200
1176
|
}
|
|
1201
1177
|
|
|
@@ -1209,34 +1185,34 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1209
1185
|
if (tableInDOM) {
|
|
1210
1186
|
const domTblId = tableInDOM.getAttribute('data-tblId');
|
|
1211
1187
|
const domRow = tableInDOM.getAttribute('data-row');
|
|
1212
|
-
|
|
1188
|
+
// log(`${logPrefix} DEBUG: Found table in DOM without attribute! TblId=${domTblId}, Row=${domRow}`);
|
|
1213
1189
|
// Try to reconstruct the metadata from DOM
|
|
1214
1190
|
const domCells = tableInDOM.querySelectorAll('td');
|
|
1215
1191
|
if (domTblId && domRow !== null && domCells.length > 0) {
|
|
1216
|
-
|
|
1192
|
+
// log(`${logPrefix} DEBUG: Attempting to reconstruct metadata from DOM...`);
|
|
1217
1193
|
const reconstructedMetadata = {
|
|
1218
1194
|
tblId: domTblId,
|
|
1219
1195
|
row: parseInt(domRow, 10),
|
|
1220
1196
|
cols: domCells.length
|
|
1221
1197
|
};
|
|
1222
1198
|
lineAttrString = JSON.stringify(reconstructedMetadata);
|
|
1223
|
-
|
|
1199
|
+
// log(`${logPrefix} DEBUG: Reconstructed metadata: ${lineAttrString}`);
|
|
1224
1200
|
}
|
|
1225
1201
|
}
|
|
1226
1202
|
}
|
|
1227
1203
|
} catch(e) {
|
|
1228
|
-
|
|
1204
|
+
// log(`${logPrefix} DEBUG: Error checking DOM for table:`, e);
|
|
1229
1205
|
}
|
|
1230
1206
|
}
|
|
1231
1207
|
|
|
1232
1208
|
if (lineAttrString) {
|
|
1233
1209
|
tableMetadata = JSON.parse(lineAttrString);
|
|
1234
1210
|
if (!tableMetadata || typeof tableMetadata.cols !== 'number') {
|
|
1235
|
-
|
|
1211
|
+
// log(`${logPrefix} Line ${reportedLineNum} has attribute, but metadata invalid/missing cols.`);
|
|
1236
1212
|
tableMetadata = null; // Ensure it's null if invalid
|
|
1237
1213
|
}
|
|
1238
1214
|
} else {
|
|
1239
|
-
|
|
1215
|
+
// log(`${logPrefix} DEBUG: No ${ATTR_TABLE_JSON} attribute found on line ${reportedLineNum}`);
|
|
1240
1216
|
// Not a table line based on reported caret line
|
|
1241
1217
|
}
|
|
1242
1218
|
} catch(e) {
|
|
@@ -1247,7 +1223,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1247
1223
|
// Get last known good state
|
|
1248
1224
|
const editor = editorInfo.editor; // Get editor instance
|
|
1249
1225
|
const lastClick = editor?.ep_data_tables_last_clicked; // Read shared state
|
|
1250
|
-
|
|
1226
|
+
// log(`${logPrefix} Reading stored click/caret info:`, lastClick);
|
|
1251
1227
|
|
|
1252
1228
|
// --- Determine the TRUE target line, cell, and caret position ---
|
|
1253
1229
|
let currentLineNum = -1;
|
|
@@ -1262,22 +1238,22 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1262
1238
|
|
|
1263
1239
|
// ** Scenario 1: Try to trust lastClick info **
|
|
1264
1240
|
if (lastClick) {
|
|
1265
|
-
|
|
1241
|
+
// log(`${logPrefix} Attempting to validate stored click info for Line=${lastClick.lineNum}...`);
|
|
1266
1242
|
let storedLineAttrString = null;
|
|
1267
1243
|
let storedLineMetadata = null;
|
|
1268
1244
|
try {
|
|
1269
|
-
|
|
1245
|
+
// log(`${logPrefix} DEBUG: Getting ${ATTR_TABLE_JSON} attribute from stored line ${lastClick.lineNum}`);
|
|
1270
1246
|
storedLineAttrString = docManager.getAttributeOnLine(lastClick.lineNum, ATTR_TABLE_JSON);
|
|
1271
|
-
|
|
1247
|
+
// log(`${logPrefix} DEBUG: Stored line attribute result: ${storedLineAttrString ? `"${storedLineAttrString}"` : 'null/undefined'}`);
|
|
1272
1248
|
|
|
1273
1249
|
if (storedLineAttrString) {
|
|
1274
1250
|
storedLineMetadata = JSON.parse(storedLineAttrString);
|
|
1275
|
-
|
|
1251
|
+
// log(`${logPrefix} DEBUG: Parsed stored metadata:`, storedLineMetadata);
|
|
1276
1252
|
}
|
|
1277
1253
|
|
|
1278
1254
|
// Check if metadata is valid and tblId matches
|
|
1279
1255
|
if (storedLineMetadata && typeof storedLineMetadata.cols === 'number' && storedLineMetadata.tblId === lastClick.tblId) {
|
|
1280
|
-
|
|
1256
|
+
// log(`${logPrefix} Stored click info VALIDATED (Metadata OK and tblId matches). Trusting stored state.`);
|
|
1281
1257
|
trustedLastClick = true;
|
|
1282
1258
|
currentLineNum = lastClick.lineNum;
|
|
1283
1259
|
targetCellIndex = lastClick.cellIndex;
|
|
@@ -1286,10 +1262,10 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1286
1262
|
|
|
1287
1263
|
lineText = rep.lines.atIndex(currentLineNum)?.text || '';
|
|
1288
1264
|
cellTexts = lineText.split(DELIMITER);
|
|
1289
|
-
|
|
1265
|
+
// log(`${logPrefix} Using Line=${currentLineNum}, CellIndex=${targetCellIndex}. Text: "${lineText}"`);
|
|
1290
1266
|
|
|
1291
1267
|
if (cellTexts.length !== metadataForTargetLine.cols) {
|
|
1292
|
-
|
|
1268
|
+
// log(`${logPrefix} WARNING: Stored cell count mismatch for trusted line ${currentLineNum}.`);
|
|
1293
1269
|
}
|
|
1294
1270
|
|
|
1295
1271
|
cellStartCol = 0;
|
|
@@ -1297,20 +1273,20 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1297
1273
|
cellStartCol += (cellTexts[i]?.length ?? 0) + DELIMITER.length;
|
|
1298
1274
|
}
|
|
1299
1275
|
precedingCellsOffset = cellStartCol;
|
|
1300
|
-
|
|
1276
|
+
// log(`${logPrefix} Calculated cellStartCol=${cellStartCol} from trusted cellIndex=${targetCellIndex}.`);
|
|
1301
1277
|
|
|
1302
1278
|
if (typeof lastClick.relativePos === 'number' && lastClick.relativePos >= 0) {
|
|
1303
1279
|
const currentCellTextLength = cellTexts[targetCellIndex]?.length ?? 0;
|
|
1304
1280
|
relativeCaretPos = Math.max(0, Math.min(lastClick.relativePos, currentCellTextLength));
|
|
1305
|
-
|
|
1281
|
+
// log(`${logPrefix} Using and validated stored relative position: ${relativeCaretPos}.`);
|
|
1306
1282
|
} else {
|
|
1307
1283
|
relativeCaretPos = reportedCol - cellStartCol; // Use reportedCol for initial calc if relative is missing
|
|
1308
1284
|
const currentCellTextLength = cellTexts[targetCellIndex]?.length ?? 0;
|
|
1309
1285
|
relativeCaretPos = Math.max(0, Math.min(relativeCaretPos, currentCellTextLength));
|
|
1310
|
-
|
|
1286
|
+
// log(`${logPrefix} Stored relativePos missing, calculated from reportedCol (${reportedCol}): ${relativeCaretPos}`);
|
|
1311
1287
|
}
|
|
1312
1288
|
} else {
|
|
1313
|
-
|
|
1289
|
+
// log(`${logPrefix} Stored click info INVALID (Metadata missing/invalid or tblId mismatch). Clearing stored state.`);
|
|
1314
1290
|
if (editor) editor.ep_data_tables_last_clicked = null;
|
|
1315
1291
|
}
|
|
1316
1292
|
} catch (e) {
|
|
@@ -1321,7 +1297,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1321
1297
|
|
|
1322
1298
|
// ** Scenario 2: Fallback - Use reported line/col ONLY if stored info wasn't trusted **
|
|
1323
1299
|
if (!trustedLastClick) {
|
|
1324
|
-
|
|
1300
|
+
// log(`${logPrefix} Fallback: Using reported caret position Line=${reportedLineNum}, Col=${reportedCol}.`);
|
|
1325
1301
|
// Fetch metadata for the reported line again, in case it wasn't fetched or was invalid earlier
|
|
1326
1302
|
try {
|
|
1327
1303
|
lineAttrString = docManager.getAttributeOnLine(reportedLineNum, ATTR_TABLE_JSON);
|
|
@@ -1338,11 +1314,11 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1338
1314
|
if (tableInDOM) {
|
|
1339
1315
|
const domTblId = tableInDOM.getAttribute('data-tblId');
|
|
1340
1316
|
const domRow = tableInDOM.getAttribute('data-row');
|
|
1341
|
-
|
|
1317
|
+
// log(`${logPrefix} Fallback: Found table in DOM without attribute! TblId=${domTblId}, Row=${domRow}`);
|
|
1342
1318
|
// Try to reconstruct the metadata from DOM
|
|
1343
1319
|
const domCells = tableInDOM.querySelectorAll('td');
|
|
1344
1320
|
if (domTblId && domRow !== null && domCells.length > 0) {
|
|
1345
|
-
|
|
1321
|
+
// log(`${logPrefix} Fallback: Attempting to reconstruct metadata from DOM...`);
|
|
1346
1322
|
const reconstructedMetadata = {
|
|
1347
1323
|
tblId: domTblId,
|
|
1348
1324
|
row: parseInt(domRow, 10),
|
|
@@ -1350,31 +1326,31 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1350
1326
|
};
|
|
1351
1327
|
lineAttrString = JSON.stringify(reconstructedMetadata);
|
|
1352
1328
|
tableMetadata = reconstructedMetadata;
|
|
1353
|
-
|
|
1329
|
+
// log(`${logPrefix} Fallback: Reconstructed metadata: ${lineAttrString}`);
|
|
1354
1330
|
}
|
|
1355
1331
|
}
|
|
1356
1332
|
}
|
|
1357
1333
|
} catch(e) {
|
|
1358
|
-
|
|
1334
|
+
// log(`${logPrefix} Fallback: Error checking DOM for table:`, e);
|
|
1359
1335
|
}
|
|
1360
1336
|
}
|
|
1361
1337
|
} catch(e) { tableMetadata = null; } // Ignore errors here, handled below
|
|
1362
1338
|
|
|
1363
1339
|
if (!tableMetadata) {
|
|
1364
|
-
|
|
1340
|
+
// log(`${logPrefix} Fallback: Reported line ${reportedLineNum} is not a valid table line. Allowing default.`);
|
|
1365
1341
|
return false;
|
|
1366
1342
|
}
|
|
1367
1343
|
|
|
1368
1344
|
currentLineNum = reportedLineNum;
|
|
1369
1345
|
metadataForTargetLine = tableMetadata;
|
|
1370
|
-
|
|
1346
|
+
// log(`${logPrefix} Fallback: Processing based on reported line ${currentLineNum}.`);
|
|
1371
1347
|
|
|
1372
1348
|
lineText = rep.lines.atIndex(currentLineNum)?.text || '';
|
|
1373
1349
|
cellTexts = lineText.split(DELIMITER);
|
|
1374
|
-
|
|
1350
|
+
// log(`${logPrefix} Fallback: Fetched text for reported line ${currentLineNum}: "${lineText}"`);
|
|
1375
1351
|
|
|
1376
1352
|
if (cellTexts.length !== metadataForTargetLine.cols) {
|
|
1377
|
-
|
|
1353
|
+
// log(`${logPrefix} WARNING (Fallback): Cell count mismatch for reported line ${currentLineNum}.`);
|
|
1378
1354
|
}
|
|
1379
1355
|
|
|
1380
1356
|
// Calculate target cell based on reportedCol
|
|
@@ -1388,7 +1364,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1388
1364
|
relativeCaretPos = reportedCol - currentOffset;
|
|
1389
1365
|
cellStartCol = currentOffset;
|
|
1390
1366
|
precedingCellsOffset = cellStartCol;
|
|
1391
|
-
|
|
1367
|
+
// log(`${logPrefix} --> (Fallback Calc) Found target cell ${foundIndex}. RelativePos: ${relativeCaretPos}.`);
|
|
1392
1368
|
break;
|
|
1393
1369
|
}
|
|
1394
1370
|
if (i < cellTexts.length - 1 && reportedCol === cellEndCol + DELIMITER.length) {
|
|
@@ -1396,7 +1372,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1396
1372
|
relativeCaretPos = 0;
|
|
1397
1373
|
cellStartCol = currentOffset + cellLength + DELIMITER.length;
|
|
1398
1374
|
precedingCellsOffset = cellStartCol;
|
|
1399
|
-
|
|
1375
|
+
// log(`${logPrefix} --> (Fallback Calc) Caret at delimiter AFTER cell ${i}. Treating as start of cell ${foundIndex}.`);
|
|
1400
1376
|
break;
|
|
1401
1377
|
}
|
|
1402
1378
|
currentOffset += cellLength + DELIMITER.length;
|
|
@@ -1409,9 +1385,9 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1409
1385
|
for (let i = 0; i < foundIndex; i++) { cellStartCol += (cellTexts[i]?.length ?? 0) + DELIMITER.length; }
|
|
1410
1386
|
precedingCellsOffset = cellStartCol;
|
|
1411
1387
|
relativeCaretPos = cellTexts[foundIndex]?.length ?? 0;
|
|
1412
|
-
|
|
1388
|
+
// log(`${logPrefix} --> (Fallback Calc) Caret detected at END of last cell (${foundIndex}).`);
|
|
1413
1389
|
} else {
|
|
1414
|
-
|
|
1390
|
+
// log(`${logPrefix} (Fallback Calc) FAILED to determine target cell for caret col ${reportedCol}. Allowing default handling.`);
|
|
1415
1391
|
return false;
|
|
1416
1392
|
}
|
|
1417
1393
|
}
|
|
@@ -1420,12 +1396,12 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1420
1396
|
|
|
1421
1397
|
// --- Final Validation ---
|
|
1422
1398
|
if (currentLineNum < 0 || targetCellIndex < 0 || !metadataForTargetLine || targetCellIndex >= metadataForTargetLine.cols) {
|
|
1423
|
-
|
|
1399
|
+
// log(`${logPrefix} FAILED final validation: Line=${currentLineNum}, Cell=${targetCellIndex}, Metadata=${!!metadataForTargetLine}. Allowing default.`);
|
|
1424
1400
|
if (editor) editor.ep_data_tables_last_clicked = null;
|
|
1425
1401
|
return false;
|
|
1426
1402
|
}
|
|
1427
1403
|
|
|
1428
|
-
|
|
1404
|
+
// log(`${logPrefix} --> Final Target: Line=${currentLineNum}, CellIndex=${targetCellIndex}, RelativePos=${relativeCaretPos}`);
|
|
1429
1405
|
// --- End Cell/Position Determination ---
|
|
1430
1406
|
|
|
1431
1407
|
// --- START NEW: Handle Highlight Deletion/Replacement ---
|
|
@@ -1434,11 +1410,11 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1434
1410
|
const hasSelection = selStartActual[0] !== selEndActual[0] || selStartActual[1] !== selEndActual[1];
|
|
1435
1411
|
|
|
1436
1412
|
if (hasSelection) {
|
|
1437
|
-
|
|
1438
|
-
|
|
1413
|
+
// log(`${logPrefix} [selection] Active selection detected. Start:[${selStartActual[0]},${selStartActual[1]}], End:[${selEndActual[0]},${selEndActual[1]}]`);
|
|
1414
|
+
// log(`${logPrefix} [caretTrace] [selection] Initial rep.selStart: Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
|
|
1439
1415
|
|
|
1440
1416
|
if (selStartActual[0] !== currentLineNum || selEndActual[0] !== currentLineNum) {
|
|
1441
|
-
|
|
1417
|
+
// log(`${logPrefix} [selection] Selection spans multiple lines (${selStartActual[0]}-${selEndActual[0]}) or is not on the current focused table line (${currentLineNum}). Preventing default action.`);
|
|
1442
1418
|
evt.preventDefault();
|
|
1443
1419
|
return true;
|
|
1444
1420
|
}
|
|
@@ -1486,7 +1462,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1486
1462
|
selectionEndColInLine = cellContentEndColInLine;
|
|
1487
1463
|
}
|
|
1488
1464
|
|
|
1489
|
-
|
|
1465
|
+
// log(`${logPrefix} [selection] Cell context for selection: targetCellIndex=${targetCellIndex}, cellStartColInLine=${cellContentStartColInLine}, cellEndColInLine=${cellContentEndColInLine}, currentCellFullText='${currentCellFullText}'`);
|
|
1490
1466
|
|
|
1491
1467
|
const isSelectionEntirelyWithinCell =
|
|
1492
1468
|
selectionStartColInLine >= cellContentStartColInLine &&
|
|
@@ -1518,10 +1494,10 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1518
1494
|
|
|
1519
1495
|
|
|
1520
1496
|
if (isSelectionEntirelyWithinCell && (isCurrentKeyDelete || isCurrentKeyBackspace || isCurrentKeyTyping)) {
|
|
1521
|
-
|
|
1497
|
+
// log(`${logPrefix} [selection] Handling key='${evt.key}' (Type: ${evt.type}) for valid intra-cell selection.`);
|
|
1522
1498
|
|
|
1523
1499
|
if (evt.type !== 'keydown') {
|
|
1524
|
-
|
|
1500
|
+
// log(`${logPrefix} [selection] Ignoring non-keydown event type ('${evt.type}') for selection handling. Allowing default.`);
|
|
1525
1501
|
return false;
|
|
1526
1502
|
}
|
|
1527
1503
|
evt.preventDefault();
|
|
@@ -1531,20 +1507,20 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1531
1507
|
let replacementText = '';
|
|
1532
1508
|
let newAbsoluteCaretCol = selectionStartColInLine;
|
|
1533
1509
|
const repBeforeEdit = editorInfo.ace_getRep(); // Get rep before edit for attribute helper
|
|
1534
|
-
|
|
1510
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart before ace_performDocumentReplaceRange: Line=${repBeforeEdit.selStart[0]}, Col=${repBeforeEdit.selStart[1]}`);
|
|
1535
1511
|
|
|
1536
1512
|
if (isCurrentKeyTyping) {
|
|
1537
1513
|
replacementText = evt.key;
|
|
1538
1514
|
newAbsoluteCaretCol = selectionStartColInLine + replacementText.length;
|
|
1539
|
-
|
|
1515
|
+
// log(`${logPrefix} [selection] -> Replacing selected range [[${rangeStart[0]},${rangeStart[1]}],[${rangeEnd[0]},${rangeEnd[1]}]] with text '${replacementText}'`);
|
|
1540
1516
|
} else { // Delete or Backspace
|
|
1541
|
-
|
|
1517
|
+
// log(`${logPrefix} [selection] -> Deleting selected range [[${rangeStart[0]},${rangeStart[1]}],[${rangeEnd[0]},${rangeEnd[1]}]]`);
|
|
1542
1518
|
// If whole cell is being wiped, keep a single space so cell isn't empty
|
|
1543
1519
|
const isWholeCell = selectionStartColInLine <= cellContentStartColInLine && selectionEndColInLine >= cellContentEndColInLine;
|
|
1544
1520
|
if (isWholeCell) {
|
|
1545
1521
|
replacementText = ' ';
|
|
1546
1522
|
newAbsoluteCaretCol = selectionStartColInLine + 1;
|
|
1547
|
-
|
|
1523
|
+
// log(`${logPrefix} [selection] Whole cell cleared – inserting single space to preserve caret/author span.`);
|
|
1548
1524
|
}
|
|
1549
1525
|
}
|
|
1550
1526
|
|
|
@@ -1563,44 +1539,44 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1563
1539
|
);
|
|
1564
1540
|
}
|
|
1565
1541
|
const repAfterReplace = editorInfo.ace_getRep();
|
|
1566
|
-
|
|
1542
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performDocumentReplaceRange: Line=${repAfterReplace.selStart[0]}, Col=${repAfterReplace.selStart[1]}`);
|
|
1567
1543
|
|
|
1568
1544
|
|
|
1569
|
-
|
|
1545
|
+
// log(`${logPrefix} [selection] -> Re-applying tbljson line attribute...`);
|
|
1570
1546
|
const applyHelper = editorInfo.ep_data_tables_applyMeta;
|
|
1571
1547
|
if (applyHelper && typeof applyHelper === 'function' && repBeforeEdit) {
|
|
1572
1548
|
const attrStringToApply = (trustedLastClick || reportedLineNum === currentLineNum) ? lineAttrString : null;
|
|
1573
1549
|
applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, repBeforeEdit, editorInfo, attrStringToApply, docManager);
|
|
1574
|
-
|
|
1550
|
+
// log(`${logPrefix} [selection] -> tbljson line attribute re-applied (using rep before edit).`);
|
|
1575
1551
|
} else {
|
|
1576
1552
|
console.error(`${logPrefix} [selection] -> FAILED to re-apply tbljson attribute (helper or repBeforeEdit missing).`);
|
|
1577
1553
|
const currentRepFallback = editorInfo.ace_getRep();
|
|
1578
1554
|
if (applyHelper && typeof applyHelper === 'function' && currentRepFallback) {
|
|
1579
|
-
|
|
1555
|
+
// log(`${logPrefix} [selection] -> Retrying attribute application with current rep...`);
|
|
1580
1556
|
applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, currentRepFallback, editorInfo, null, docManager);
|
|
1581
|
-
|
|
1557
|
+
// log(`${logPrefix} [selection] -> tbljson line attribute re-applied (using current rep fallback).`);
|
|
1582
1558
|
} else {
|
|
1583
1559
|
console.error(`${logPrefix} [selection] -> FAILED to re-apply tbljson attribute even with fallback rep.`);
|
|
1584
1560
|
}
|
|
1585
1561
|
}
|
|
1586
1562
|
|
|
1587
|
-
|
|
1588
|
-
|
|
1563
|
+
// log(`${logPrefix} [selection] -> Setting selection/caret to: [${currentLineNum}, ${newAbsoluteCaretCol}]`);
|
|
1564
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart before ace_performSelectionChange: Line=${editorInfo.ace_getRep().selStart[0]}, Col=${editorInfo.ace_getRep().selStart[1]}`);
|
|
1589
1565
|
editorInfo.ace_performSelectionChange([currentLineNum, newAbsoluteCaretCol], [currentLineNum, newAbsoluteCaretCol], false);
|
|
1590
1566
|
const repAfterSelectionChange = editorInfo.ace_getRep();
|
|
1591
|
-
|
|
1567
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performSelectionChange: Line=${repAfterSelectionChange.selStart[0]}, Col=${repAfterSelectionChange.selStart[1]}`);
|
|
1592
1568
|
|
|
1593
1569
|
// Add sync hint AFTER setting selection
|
|
1594
1570
|
editorInfo.ace_fastIncorp(1);
|
|
1595
1571
|
const repAfterFastIncorp = editorInfo.ace_getRep();
|
|
1596
|
-
|
|
1597
|
-
|
|
1572
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_fastIncorp: Line=${repAfterFastIncorp.selStart[0]}, Col=${repAfterFastIncorp.selStart[1]}`);
|
|
1573
|
+
// log(`${logPrefix} [selection] -> Requested sync hint (fastIncorp 1).`);
|
|
1598
1574
|
|
|
1599
1575
|
// --- Re-assert selection ---
|
|
1600
|
-
|
|
1576
|
+
// log(`${logPrefix} [caretTrace] [selection] Attempting to re-assert selection post-fastIncorp to [${currentLineNum}, ${newAbsoluteCaretCol}]`);
|
|
1601
1577
|
editorInfo.ace_performSelectionChange([currentLineNum, newAbsoluteCaretCol], [currentLineNum, newAbsoluteCaretCol], false);
|
|
1602
1578
|
const repAfterReassert = editorInfo.ace_getRep();
|
|
1603
|
-
|
|
1579
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart after re-asserting selection: Line=${repAfterReassert.selStart[0]}, Col=${repAfterReassert.selStart[1]}`);
|
|
1604
1580
|
|
|
1605
1581
|
const newRelativePos = newAbsoluteCaretCol - cellStartCol;
|
|
1606
1582
|
if (editor) {
|
|
@@ -1610,15 +1586,15 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1610
1586
|
cellIndex: targetCellIndex,
|
|
1611
1587
|
relativePos: newRelativePos < 0 ? 0 : newRelativePos
|
|
1612
1588
|
};
|
|
1613
|
-
|
|
1589
|
+
// log(`${logPrefix} [selection] -> Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
|
|
1614
1590
|
} else {
|
|
1615
|
-
|
|
1591
|
+
// log(`${logPrefix} [selection] -> Editor instance not found, cannot update ep_data_tables_last_clicked.`);
|
|
1616
1592
|
}
|
|
1617
1593
|
|
|
1618
|
-
|
|
1594
|
+
// log(`${logPrefix} END [selection] (Handled highlight modification) Key='${evt.key}' Type='${evt.type}'. Duration: ${Date.now() - startLogTime}ms`);
|
|
1619
1595
|
return true;
|
|
1620
1596
|
} catch (error) {
|
|
1621
|
-
|
|
1597
|
+
// log(`${logPrefix} [selection] ERROR during highlight modification:`, error);
|
|
1622
1598
|
console.error('[ep_data_tables] Error processing highlight modification:', error);
|
|
1623
1599
|
return true; // Still return true as we prevented default.
|
|
1624
1600
|
}
|
|
@@ -1629,12 +1605,12 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1629
1605
|
// --- Check for Ctrl+X (Cut) key combination ---
|
|
1630
1606
|
const isCutKey = (evt.ctrlKey || evt.metaKey) && (evt.key === 'x' || evt.key === 'X' || evt.keyCode === 88);
|
|
1631
1607
|
if (isCutKey && hasSelection) {
|
|
1632
|
-
|
|
1608
|
+
// log(`${logPrefix} Ctrl+X (Cut) detected with selection. Letting cut event handler manage this.`);
|
|
1633
1609
|
// Let the cut event handler handle this - we don't need to preventDefault here
|
|
1634
1610
|
// as the cut event will handle the operation and prevent default
|
|
1635
1611
|
return false; // Allow the cut event to be triggered
|
|
1636
1612
|
} else if (isCutKey && !hasSelection) {
|
|
1637
|
-
|
|
1613
|
+
// log(`${logPrefix} Ctrl+X (Cut) detected but no selection. Allowing default.`);
|
|
1638
1614
|
return false; // Allow default - nothing to cut
|
|
1639
1615
|
}
|
|
1640
1616
|
|
|
@@ -1645,7 +1621,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1645
1621
|
const isNavigationKey = [33, 34, 35, 36, 37, 38, 39, 40].includes(evt.keyCode);
|
|
1646
1622
|
const isTabKey = evt.key === 'Tab';
|
|
1647
1623
|
const isEnterKey = evt.key === 'Enter';
|
|
1648
|
-
|
|
1624
|
+
// log(`${logPrefix} Key classification: Typing=${isTypingKey}, Backspace=${isBackspaceKey}, Delete=${isDeleteKey}, Nav=${isNavigationKey}, Tab=${isTabKey}, Enter=${isEnterKey}, Cut=${isCutKey}`);
|
|
1649
1625
|
|
|
1650
1626
|
/*
|
|
1651
1627
|
* Prevent caret placement *after* the invisible caret-anchor.
|
|
@@ -1660,7 +1636,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1660
1636
|
if (evt.type === 'keydown' && !evt.ctrlKey && !evt.metaKey && !evt.altKey) {
|
|
1661
1637
|
// Right-arrow – if at the very end of a cell, move to the next cell.
|
|
1662
1638
|
if (evt.keyCode === 39 && relativeCaretPos >= currentCellTextLengthEarly && targetCellIndex < metadataForTargetLine.cols - 1) {
|
|
1663
|
-
|
|
1639
|
+
// log(`${logPrefix} ArrowRight at cell boundary – navigating to next cell to avoid anchor zone.`);
|
|
1664
1640
|
evt.preventDefault();
|
|
1665
1641
|
navigateToNextCell(currentLineNum, targetCellIndex, metadataForTargetLine, false, editorInfo, docManager);
|
|
1666
1642
|
return true;
|
|
@@ -1668,7 +1644,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1668
1644
|
|
|
1669
1645
|
// Left-arrow – if at the very start of a cell, move to the previous cell.
|
|
1670
1646
|
if (evt.keyCode === 37 && relativeCaretPos === 0 && targetCellIndex > 0) {
|
|
1671
|
-
|
|
1647
|
+
// log(`${logPrefix} ArrowLeft at cell boundary – navigating to previous cell to avoid anchor zone.`);
|
|
1672
1648
|
evt.preventDefault();
|
|
1673
1649
|
navigateToNextCell(currentLineNum, targetCellIndex, metadataForTargetLine, true, editorInfo, docManager);
|
|
1674
1650
|
return true;
|
|
@@ -1679,45 +1655,45 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1679
1655
|
|
|
1680
1656
|
// 1. Allow non-Tab navigation keys immediately
|
|
1681
1657
|
if (isNavigationKey && !isTabKey) {
|
|
1682
|
-
|
|
1658
|
+
// log(`${logPrefix} Allowing navigation key: ${evt.key}. Clearing click state.`);
|
|
1683
1659
|
if (editor) editor.ep_data_tables_last_clicked = null; // Clear state on navigation
|
|
1684
1660
|
return false;
|
|
1685
1661
|
}
|
|
1686
1662
|
|
|
1687
1663
|
// 2. Handle Tab - Navigate to next cell (only on keydown to avoid double navigation)
|
|
1688
1664
|
if (isTabKey) {
|
|
1689
|
-
|
|
1665
|
+
// log(`${logPrefix} Tab key pressed. Event type: ${evt.type}`);
|
|
1690
1666
|
evt.preventDefault();
|
|
1691
1667
|
|
|
1692
1668
|
// Only process keydown events for navigation to avoid double navigation
|
|
1693
1669
|
if (evt.type !== 'keydown') {
|
|
1694
|
-
|
|
1670
|
+
// log(`${logPrefix} Ignoring Tab ${evt.type} event to prevent double navigation.`);
|
|
1695
1671
|
return true;
|
|
1696
1672
|
}
|
|
1697
1673
|
|
|
1698
|
-
|
|
1674
|
+
// log(`${logPrefix} Processing Tab keydown - implementing cell navigation.`);
|
|
1699
1675
|
const success = navigateToNextCell(currentLineNum, targetCellIndex, metadataForTargetLine, evt.shiftKey, editorInfo, docManager);
|
|
1700
1676
|
if (!success) {
|
|
1701
|
-
|
|
1677
|
+
// log(`${logPrefix} Tab navigation failed, cell navigation not possible.`);
|
|
1702
1678
|
}
|
|
1703
1679
|
return true;
|
|
1704
1680
|
}
|
|
1705
1681
|
|
|
1706
1682
|
// 3. Handle Enter - Navigate to cell below (only on keydown to avoid double navigation)
|
|
1707
1683
|
if (isEnterKey) {
|
|
1708
|
-
|
|
1684
|
+
// log(`${logPrefix} Enter key pressed. Event type: ${evt.type}`);
|
|
1709
1685
|
evt.preventDefault();
|
|
1710
1686
|
|
|
1711
1687
|
// Only process keydown events for navigation to avoid double navigation
|
|
1712
1688
|
if (evt.type !== 'keydown') {
|
|
1713
|
-
|
|
1689
|
+
// log(`${logPrefix} Ignoring Enter ${evt.type} event to prevent double navigation.`);
|
|
1714
1690
|
return true;
|
|
1715
1691
|
}
|
|
1716
1692
|
|
|
1717
|
-
|
|
1693
|
+
// log(`${logPrefix} Processing Enter keydown - implementing cell navigation.`);
|
|
1718
1694
|
const success = navigateToCellBelow(currentLineNum, targetCellIndex, metadataForTargetLine, editorInfo, docManager);
|
|
1719
1695
|
if (!success) {
|
|
1720
|
-
|
|
1696
|
+
// log(`${logPrefix} Enter navigation failed, cell navigation not possible.`);
|
|
1721
1697
|
}
|
|
1722
1698
|
return true;
|
|
1723
1699
|
}
|
|
@@ -1726,25 +1702,25 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1726
1702
|
const currentCellTextLength = cellTexts[targetCellIndex]?.length ?? 0;
|
|
1727
1703
|
// Backspace at the very beginning of cell > 0
|
|
1728
1704
|
if (isBackspaceKey && relativeCaretPos === 0 && targetCellIndex > 0) {
|
|
1729
|
-
|
|
1705
|
+
// log(`${logPrefix} Intercepted Backspace at start of cell ${targetCellIndex}. Preventing default.`);
|
|
1730
1706
|
evt.preventDefault();
|
|
1731
1707
|
return true;
|
|
1732
1708
|
}
|
|
1733
1709
|
// NEW: Backspace at very beginning of first cell – would merge with previous line
|
|
1734
1710
|
if (isBackspaceKey && relativeCaretPos === 0 && targetCellIndex === 0) {
|
|
1735
|
-
|
|
1711
|
+
// log(`${logPrefix} Intercepted Backspace at start of first cell (line boundary). Preventing merge.`);
|
|
1736
1712
|
evt.preventDefault();
|
|
1737
1713
|
return true;
|
|
1738
1714
|
}
|
|
1739
1715
|
// Delete at the very end of cell < last cell
|
|
1740
1716
|
if (isDeleteKey && relativeCaretPos === currentCellTextLength && targetCellIndex < metadataForTargetLine.cols - 1) {
|
|
1741
|
-
|
|
1717
|
+
// log(`${logPrefix} Intercepted Delete at end of cell ${targetCellIndex}. Preventing default.`);
|
|
1742
1718
|
evt.preventDefault();
|
|
1743
1719
|
return true;
|
|
1744
1720
|
}
|
|
1745
1721
|
// NEW: Delete at very end of last cell – would merge with next line
|
|
1746
1722
|
if (isDeleteKey && relativeCaretPos === currentCellTextLength && targetCellIndex === metadataForTargetLine.cols - 1) {
|
|
1747
|
-
|
|
1723
|
+
// log(`${logPrefix} Intercepted Delete at end of last cell (line boundary). Preventing merge.`);
|
|
1748
1724
|
evt.preventDefault();
|
|
1749
1725
|
return true;
|
|
1750
1726
|
}
|
|
@@ -1756,7 +1732,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1756
1732
|
// Guard: internal Backspace at relativePos 1 (would delete delimiter) & Delete at relativePos 0
|
|
1757
1733
|
if ((isInternalBackspace && relativeCaretPos === 1 && targetCellIndex > 0) ||
|
|
1758
1734
|
(isInternalDelete && relativeCaretPos === 0 && targetCellIndex > 0)) {
|
|
1759
|
-
|
|
1735
|
+
// log(`${logPrefix} Attempt to erase protected delimiter – operation blocked.`);
|
|
1760
1736
|
evt.preventDefault();
|
|
1761
1737
|
return true;
|
|
1762
1738
|
}
|
|
@@ -1764,25 +1740,25 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1764
1740
|
if (isTypingKey || isInternalBackspace || isInternalDelete) {
|
|
1765
1741
|
// --- PREVENT TYPING DIRECTLY AFTER DELIMITER (relativeCaretPos===0) ---
|
|
1766
1742
|
if (isTypingKey && relativeCaretPos === 0 && targetCellIndex > 0) {
|
|
1767
|
-
|
|
1743
|
+
// log(`${logPrefix} Caret at forbidden position 0 (just after delimiter). Auto-advancing to position 1.`);
|
|
1768
1744
|
const safePosAbs = cellStartCol + 1;
|
|
1769
1745
|
editorInfo.ace_performSelectionChange([currentLineNum, safePosAbs], [currentLineNum, safePosAbs], false);
|
|
1770
1746
|
editorInfo.ace_updateBrowserSelectionFromRep();
|
|
1771
1747
|
relativeCaretPos = 1;
|
|
1772
|
-
|
|
1748
|
+
// log(`${logPrefix} Caret moved to safe position. New relativeCaretPos=${relativeCaretPos}`);
|
|
1773
1749
|
}
|
|
1774
1750
|
// *** Use the validated currentLineNum and currentCol derived from relativeCaretPos ***
|
|
1775
1751
|
const currentCol = cellStartCol + relativeCaretPos;
|
|
1776
|
-
|
|
1777
|
-
|
|
1752
|
+
// log(`${logPrefix} Handling INTERNAL key='${evt.key}' Type='${evt.type}' at Line=${currentLineNum}, Col=${currentCol} (CellIndex=${targetCellIndex}, RelativePos=${relativeCaretPos}).`);
|
|
1753
|
+
// log(`${logPrefix} [caretTrace] Initial rep.selStart for internal edit: Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
|
|
1778
1754
|
|
|
1779
1755
|
// Only process keydown events for modifications
|
|
1780
1756
|
if (evt.type !== 'keydown') {
|
|
1781
|
-
|
|
1757
|
+
// log(`${logPrefix} Ignoring non-keydown event type ('${evt.type}') for handled key.`);
|
|
1782
1758
|
return false;
|
|
1783
1759
|
}
|
|
1784
1760
|
|
|
1785
|
-
|
|
1761
|
+
// log(`${logPrefix} Preventing default browser action for keydown event.`);
|
|
1786
1762
|
evt.preventDefault();
|
|
1787
1763
|
|
|
1788
1764
|
let newAbsoluteCaretCol = -1;
|
|
@@ -1790,56 +1766,56 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1790
1766
|
|
|
1791
1767
|
try {
|
|
1792
1768
|
repBeforeEdit = editorInfo.ace_getRep(); // Get rep *before* making changes
|
|
1793
|
-
|
|
1769
|
+
// log(`${logPrefix} [caretTrace] rep.selStart before ace_performDocumentReplaceRange: Line=${repBeforeEdit.selStart[0]}, Col=${repBeforeEdit.selStart[1]}`);
|
|
1794
1770
|
|
|
1795
1771
|
if (isTypingKey) {
|
|
1796
1772
|
const insertPos = [currentLineNum, currentCol];
|
|
1797
|
-
|
|
1773
|
+
// log(`${logPrefix} -> Inserting text '${evt.key}' at [${insertPos}]`);
|
|
1798
1774
|
editorInfo.ace_performDocumentReplaceRange(insertPos, insertPos, evt.key);
|
|
1799
1775
|
newAbsoluteCaretCol = currentCol + 1;
|
|
1800
1776
|
|
|
1801
1777
|
} else if (isInternalBackspace) {
|
|
1802
1778
|
const delRangeStart = [currentLineNum, currentCol - 1];
|
|
1803
1779
|
const delRangeEnd = [currentLineNum, currentCol];
|
|
1804
|
-
|
|
1780
|
+
// log(`${logPrefix} -> Deleting (Backspace) range [${delRangeStart}]-[${delRangeEnd}]`);
|
|
1805
1781
|
editorInfo.ace_performDocumentReplaceRange(delRangeStart, delRangeEnd, '');
|
|
1806
1782
|
newAbsoluteCaretCol = currentCol - 1;
|
|
1807
1783
|
|
|
1808
1784
|
} else if (isInternalDelete) {
|
|
1809
1785
|
const delRangeStart = [currentLineNum, currentCol];
|
|
1810
1786
|
const delRangeEnd = [currentLineNum, currentCol + 1];
|
|
1811
|
-
|
|
1787
|
+
// log(`${logPrefix} -> Deleting (Delete) range [${delRangeStart}]-[${delRangeEnd}]`);
|
|
1812
1788
|
editorInfo.ace_performDocumentReplaceRange(delRangeStart, delRangeEnd, '');
|
|
1813
1789
|
newAbsoluteCaretCol = currentCol; // Caret stays at the same column for delete
|
|
1814
1790
|
}
|
|
1815
1791
|
const repAfterReplace = editorInfo.ace_getRep();
|
|
1816
|
-
|
|
1792
|
+
// log(`${logPrefix} [caretTrace] rep.selStart after ace_performDocumentReplaceRange: Line=${repAfterReplace.selStart[0]}, Col=${repAfterReplace.selStart[1]}`);
|
|
1817
1793
|
|
|
1818
1794
|
|
|
1819
1795
|
// *** CRITICAL: Re-apply the line attribute after ANY modification ***
|
|
1820
|
-
|
|
1796
|
+
// log(`${logPrefix} -> Re-applying tbljson line attribute...`);
|
|
1821
1797
|
|
|
1822
1798
|
// DEBUG: Log the values before calculating attrStringToApply
|
|
1823
|
-
|
|
1824
|
-
|
|
1799
|
+
// log(`${logPrefix} DEBUG: Before calculating attrStringToApply - trustedLastClick=${trustedLastClick}, reportedLineNum=${reportedLineNum}, currentLineNum=${currentLineNum}`);
|
|
1800
|
+
// log(`${logPrefix} DEBUG: lineAttrString value:`, lineAttrString ? `"${lineAttrString}"` : 'null/undefined');
|
|
1825
1801
|
|
|
1826
1802
|
const applyHelper = editorInfo.ep_data_tables_applyMeta;
|
|
1827
1803
|
if (applyHelper && typeof applyHelper === 'function' && repBeforeEdit) {
|
|
1828
1804
|
// Pass the original lineAttrString if available AND if it belongs to the currentLineNum
|
|
1829
1805
|
const attrStringToApply = (trustedLastClick || reportedLineNum === currentLineNum) ? lineAttrString : null;
|
|
1830
1806
|
|
|
1831
|
-
|
|
1832
|
-
|
|
1807
|
+
// log(`${logPrefix} DEBUG: Calculated attrStringToApply:`, attrStringToApply ? `"${attrStringToApply}"` : 'null/undefined');
|
|
1808
|
+
// log(`${logPrefix} DEBUG: Condition result: (${trustedLastClick} || ${reportedLineNum} === ${currentLineNum}) = ${trustedLastClick || reportedLineNum === currentLineNum}`);
|
|
1833
1809
|
|
|
1834
1810
|
applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, repBeforeEdit, editorInfo, attrStringToApply, docManager);
|
|
1835
|
-
|
|
1811
|
+
// log(`${logPrefix} -> tbljson line attribute re-applied (using rep before edit).`);
|
|
1836
1812
|
} else {
|
|
1837
1813
|
console.error(`${logPrefix} -> FAILED to re-apply tbljson attribute (helper or repBeforeEdit missing).`);
|
|
1838
1814
|
const currentRepFallback = editorInfo.ace_getRep();
|
|
1839
1815
|
if (applyHelper && typeof applyHelper === 'function' && currentRepFallback) {
|
|
1840
|
-
|
|
1816
|
+
// log(`${logPrefix} -> Retrying attribute application with current rep...`);
|
|
1841
1817
|
applyHelper(currentLineNum, metadataForTargetLine.tblId, metadataForTargetLine.row, metadataForTargetLine.cols, currentRepFallback, editorInfo, null, docManager); // Cannot guarantee old attr string is valid here
|
|
1842
|
-
|
|
1818
|
+
// log(`${logPrefix} -> tbljson line attribute re-applied (using current rep fallback).`);
|
|
1843
1819
|
} else {
|
|
1844
1820
|
console.error(`${logPrefix} -> FAILED to re-apply tbljson attribute even with fallback rep.`);
|
|
1845
1821
|
}
|
|
@@ -1848,26 +1824,26 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1848
1824
|
// Set caret position immediately
|
|
1849
1825
|
if (newAbsoluteCaretCol >= 0) {
|
|
1850
1826
|
const newCaretPos = [currentLineNum, newAbsoluteCaretCol]; // Use the trusted currentLineNum
|
|
1851
|
-
|
|
1852
|
-
|
|
1827
|
+
// log(`${logPrefix} -> Setting selection immediately to:`, newCaretPos);
|
|
1828
|
+
// log(`${logPrefix} [caretTrace] rep.selStart before ace_performSelectionChange: Line=${editorInfo.ace_getRep().selStart[0]}, Col=${editorInfo.ace_getRep().selStart[1]}`);
|
|
1853
1829
|
try {
|
|
1854
1830
|
editorInfo.ace_performSelectionChange(newCaretPos, newCaretPos, false);
|
|
1855
1831
|
const repAfterSelectionChange = editorInfo.ace_getRep();
|
|
1856
|
-
|
|
1857
|
-
|
|
1832
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_performSelectionChange: Line=${repAfterSelectionChange.selStart[0]}, Col=${repAfterSelectionChange.selStart[1]}`);
|
|
1833
|
+
// log(`${logPrefix} -> Selection set immediately.`);
|
|
1858
1834
|
|
|
1859
1835
|
// Add sync hint AFTER setting selection
|
|
1860
1836
|
editorInfo.ace_fastIncorp(1);
|
|
1861
1837
|
const repAfterFastIncorp = editorInfo.ace_getRep();
|
|
1862
|
-
|
|
1863
|
-
|
|
1838
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart after ace_fastIncorp: Line=${repAfterFastIncorp.selStart[0]}, Col=${repAfterFastIncorp.selStart[1]}`);
|
|
1839
|
+
// log(`${logPrefix} -> Requested sync hint (fastIncorp 1).`);
|
|
1864
1840
|
|
|
1865
1841
|
// --- Re-assert selection ---
|
|
1866
1842
|
const targetCaretPosForReassert = [currentLineNum, newAbsoluteCaretCol];
|
|
1867
|
-
|
|
1843
|
+
// log(`${logPrefix} [caretTrace] Attempting to re-assert selection post-fastIncorp to [${targetCaretPosForReassert[0]}, ${targetCaretPosForReassert[1]}]`);
|
|
1868
1844
|
editorInfo.ace_performSelectionChange(targetCaretPosForReassert, targetCaretPosForReassert, false);
|
|
1869
1845
|
const repAfterReassert = editorInfo.ace_getRep();
|
|
1870
|
-
|
|
1846
|
+
// log(`${logPrefix} [caretTrace] [selection] rep.selStart after re-asserting selection: Line=${repAfterReassert.selStart[0]}, Col=${repAfterReassert.selStart[1]}`);
|
|
1871
1847
|
|
|
1872
1848
|
// Store the updated caret info for the next event
|
|
1873
1849
|
const newRelativePos = newAbsoluteCaretCol - cellStartCol;
|
|
@@ -1877,19 +1853,19 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1877
1853
|
cellIndex: targetCellIndex,
|
|
1878
1854
|
relativePos: newRelativePos
|
|
1879
1855
|
};
|
|
1880
|
-
|
|
1881
|
-
|
|
1856
|
+
// log(`${logPrefix} -> Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
|
|
1857
|
+
// log(`${logPrefix} [caretTrace] Updated ep_data_tables_last_clicked. Line=${editor.ep_data_tables_last_clicked.lineNum}, Cell=${editor.ep_data_tables_last_clicked.cellIndex}, RelPos=${editor.ep_data_tables_last_clicked.relativePos}`);
|
|
1882
1858
|
|
|
1883
1859
|
|
|
1884
1860
|
} catch (selError) {
|
|
1885
1861
|
console.error(`${logPrefix} -> ERROR setting selection immediately:`, selError);
|
|
1886
1862
|
}
|
|
1887
1863
|
} else {
|
|
1888
|
-
|
|
1864
|
+
// log(`${logPrefix} -> Warning: newAbsoluteCaretCol not set, skipping selection update.`);
|
|
1889
1865
|
}
|
|
1890
1866
|
|
|
1891
1867
|
} catch (error) {
|
|
1892
|
-
|
|
1868
|
+
// log(`${logPrefix} ERROR during manual key handling:`, error);
|
|
1893
1869
|
console.error('[ep_data_tables] Error processing key event update:', error);
|
|
1894
1870
|
// Maybe return false to allow default as a fallback on error?
|
|
1895
1871
|
// For now, return true as we prevented default.
|
|
@@ -1897,7 +1873,7 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1897
1873
|
}
|
|
1898
1874
|
|
|
1899
1875
|
const endLogTime = Date.now();
|
|
1900
|
-
|
|
1876
|
+
// log(`${logPrefix} END (Handled Internal Edit Manually) Key='${evt.key}' Type='${evt.type}' -> Returned true. Duration: ${endLogTime - startLogTime}ms`);
|
|
1901
1877
|
return true; // We handled the key event
|
|
1902
1878
|
|
|
1903
1879
|
} // End if(isTypingKey || isInternalBackspace || isInternalDelete)
|
|
@@ -1905,90 +1881,90 @@ exports.aceKeyEvent = (h, ctx) => {
|
|
|
1905
1881
|
|
|
1906
1882
|
// Fallback for any other keys or edge cases not handled above
|
|
1907
1883
|
const endLogTimeFinal = Date.now();
|
|
1908
|
-
|
|
1884
|
+
// log(`${logPrefix} END (Fell Through / Unhandled Case) Key='${evt.key}' Type='${evt.type}'. Allowing default. Duration: ${endLogTimeFinal - startLogTime}ms`);
|
|
1909
1885
|
// Clear click state if it wasn't handled?
|
|
1910
1886
|
// if (editor?.ep_data_tables_last_clicked) editor.ep_data_tables_last_clicked = null;
|
|
1911
|
-
|
|
1887
|
+
// log(`${logPrefix} [caretTrace] Final rep.selStart at end of aceKeyEvent (if unhandled): Line=${rep.selStart[0]}, Col=${rep.selStart[1]}`);
|
|
1912
1888
|
return false; // Allow default browser/ACE handling
|
|
1913
1889
|
};
|
|
1914
1890
|
|
|
1915
1891
|
// ───────────────────── ace init + public helpers ─────────────────────
|
|
1916
1892
|
exports.aceInitialized = (h, ctx) => {
|
|
1917
1893
|
const logPrefix = '[ep_data_tables:aceInitialized]';
|
|
1918
|
-
|
|
1894
|
+
// log(`${logPrefix} START`, { hook_name: h, context: ctx });
|
|
1919
1895
|
const ed = ctx.editorInfo;
|
|
1920
1896
|
const docManager = ctx.documentAttributeManager;
|
|
1921
1897
|
|
|
1922
|
-
|
|
1898
|
+
// log(`${logPrefix} Attaching ep_data_tables_applyMeta helper to editorInfo.`);
|
|
1923
1899
|
ed.ep_data_tables_applyMeta = applyTableLineMetadataAttribute;
|
|
1924
|
-
|
|
1900
|
+
// log(`${logPrefix}: Attached applyTableLineMetadataAttribute helper to ed.ep_data_tables_applyMeta successfully.`);
|
|
1925
1901
|
|
|
1926
1902
|
// Store the documentAttributeManager reference for later use
|
|
1927
|
-
|
|
1903
|
+
// log(`${logPrefix} Storing documentAttributeManager reference on editorInfo.`);
|
|
1928
1904
|
ed.ep_data_tables_docManager = docManager;
|
|
1929
|
-
|
|
1905
|
+
// log(`${logPrefix}: Stored documentAttributeManager reference as ed.ep_data_tables_docManager.`);
|
|
1930
1906
|
|
|
1931
1907
|
// *** ENHANCED: Paste event listener + Column resize listeners ***
|
|
1932
|
-
|
|
1908
|
+
// log(`${logPrefix} Preparing to attach paste and resize listeners via ace_callWithAce.`);
|
|
1933
1909
|
ed.ace_callWithAce((ace) => {
|
|
1934
1910
|
const callWithAceLogPrefix = '[ep_data_tables:aceInitialized:callWithAceForListeners]';
|
|
1935
|
-
|
|
1911
|
+
// log(`${callWithAceLogPrefix} Entered ace_callWithAce callback for listeners.`);
|
|
1936
1912
|
|
|
1937
1913
|
if (!ace || !ace.editor) {
|
|
1938
1914
|
console.error(`${callWithAceLogPrefix} ERROR: ace or ace.editor is not available. Cannot attach listeners.`);
|
|
1939
|
-
|
|
1915
|
+
// log(`${callWithAceLogPrefix} Aborting listener attachment due to missing ace.editor.`);
|
|
1940
1916
|
return;
|
|
1941
1917
|
}
|
|
1942
1918
|
const editor = ace.editor;
|
|
1943
|
-
|
|
1919
|
+
// log(`${callWithAceLogPrefix} ace.editor obtained successfully.`);
|
|
1944
1920
|
|
|
1945
1921
|
// Store editor reference for later use in table operations
|
|
1946
|
-
|
|
1922
|
+
// log(`${logPrefix} Storing editor reference on editorInfo.`);
|
|
1947
1923
|
ed.ep_data_tables_editor = editor;
|
|
1948
|
-
|
|
1924
|
+
// log(`${logPrefix}: Stored editor reference as ed.ep_data_tables_editor.`);
|
|
1949
1925
|
|
|
1950
1926
|
// Attempt to find the inner iframe body, similar to ep_image_insert
|
|
1951
1927
|
let $inner;
|
|
1952
1928
|
try {
|
|
1953
|
-
|
|
1929
|
+
// log(`${callWithAceLogPrefix} Attempting to find inner iframe body for listener attachment.`);
|
|
1954
1930
|
const $iframeOuter = $('iframe[name="ace_outer"]');
|
|
1955
1931
|
if ($iframeOuter.length === 0) {
|
|
1956
1932
|
console.error(`${callWithAceLogPrefix} ERROR: Could not find outer iframe (ace_outer).`);
|
|
1957
|
-
|
|
1933
|
+
// log(`${callWithAceLogPrefix} Failed to find ace_outer.`);
|
|
1958
1934
|
return;
|
|
1959
1935
|
}
|
|
1960
|
-
|
|
1936
|
+
// log(`${callWithAceLogPrefix} Found ace_outer:`, $iframeOuter);
|
|
1961
1937
|
|
|
1962
1938
|
const $iframeInner = $iframeOuter.contents().find('iframe[name="ace_inner"]');
|
|
1963
1939
|
if ($iframeInner.length === 0) {
|
|
1964
1940
|
console.error(`${callWithAceLogPrefix} ERROR: Could not find inner iframe (ace_inner).`);
|
|
1965
|
-
|
|
1941
|
+
// log(`${callWithAceLogPrefix} Failed to find ace_inner within ace_outer.`);
|
|
1966
1942
|
return;
|
|
1967
1943
|
}
|
|
1968
|
-
|
|
1944
|
+
// log(`${callWithAceLogPrefix} Found ace_inner:`, $iframeInner);
|
|
1969
1945
|
|
|
1970
1946
|
const innerDocBody = $iframeInner.contents().find('body');
|
|
1971
1947
|
if (innerDocBody.length === 0) {
|
|
1972
1948
|
console.error(`${callWithAceLogPrefix} ERROR: Could not find body element in inner iframe.`);
|
|
1973
|
-
|
|
1949
|
+
// log(`${callWithAceLogPrefix} Failed to find body in ace_inner.`);
|
|
1974
1950
|
return;
|
|
1975
1951
|
}
|
|
1976
1952
|
$inner = $(innerDocBody[0]); // Ensure it's a jQuery object of the body itself
|
|
1977
|
-
|
|
1953
|
+
// log(`${callWithAceLogPrefix} Successfully found inner iframe body:`, $inner);
|
|
1978
1954
|
} catch (e) {
|
|
1979
1955
|
console.error(`${callWithAceLogPrefix} ERROR: Exception while trying to find inner iframe body:`, e);
|
|
1980
|
-
|
|
1956
|
+
// log(`${callWithAceLogPrefix} Exception details:`, { message: e.message, stack: e.stack });
|
|
1981
1957
|
return;
|
|
1982
1958
|
}
|
|
1983
1959
|
|
|
1984
1960
|
if (!$inner || $inner.length === 0) {
|
|
1985
1961
|
console.error(`${callWithAceLogPrefix} ERROR: $inner is not valid after attempting to find iframe body. Cannot attach listeners.`);
|
|
1986
|
-
|
|
1962
|
+
// log(`${callWithAceLogPrefix} $inner is invalid. Aborting.`);
|
|
1987
1963
|
return;
|
|
1988
1964
|
}
|
|
1989
1965
|
|
|
1990
1966
|
// *** CUT EVENT LISTENER ***
|
|
1991
|
-
|
|
1967
|
+
// log(`${callWithAceLogPrefix} Attaching cut event listener to $inner (inner iframe body).`);
|
|
1992
1968
|
$inner.on('cut', (evt) => {
|
|
1993
1969
|
const cutLogPrefix = '[ep_data_tables:cutHandler]';
|
|
1994
1970
|
console.log(`${cutLogPrefix} CUT EVENT TRIGGERED. Event object:`, evt);
|
|
@@ -2194,32 +2170,32 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2194
2170
|
});
|
|
2195
2171
|
|
|
2196
2172
|
// *** BEFOREINPUT EVENT LISTENER FOR CONTEXT-MENU DELETE ***
|
|
2197
|
-
|
|
2173
|
+
// log(`${callWithAceLogPrefix} Attaching beforeinput event listener to $inner (inner iframe body).`);
|
|
2198
2174
|
$inner.on('beforeinput', (evt) => {
|
|
2199
2175
|
const deleteLogPrefix = '[ep_data_tables:beforeinputDeleteHandler]';
|
|
2200
|
-
|
|
2176
|
+
// log(`${deleteLogPrefix} BEFOREINPUT EVENT TRIGGERED. inputType: "${evt.originalEvent.inputType}", event object:`, evt);
|
|
2201
2177
|
|
|
2202
2178
|
// Only intercept deletion-related input events
|
|
2203
2179
|
if (!evt.originalEvent.inputType || !evt.originalEvent.inputType.startsWith('delete')) {
|
|
2204
|
-
|
|
2180
|
+
// log(`${deleteLogPrefix} Not a deletion event (inputType: "${evt.originalEvent.inputType}"). Allowing default.`);
|
|
2205
2181
|
return; // Allow default for non-delete events
|
|
2206
2182
|
}
|
|
2207
2183
|
|
|
2208
|
-
|
|
2184
|
+
// log(`${deleteLogPrefix} Getting current editor representation (rep).`);
|
|
2209
2185
|
const rep = ed.ace_getRep();
|
|
2210
2186
|
if (!rep || !rep.selStart) {
|
|
2211
|
-
|
|
2187
|
+
// log(`${deleteLogPrefix} WARNING: Could not get representation or selection. Allowing default delete.`);
|
|
2212
2188
|
console.warn(`${deleteLogPrefix} Could not get rep or selStart.`);
|
|
2213
2189
|
return; // Allow default
|
|
2214
2190
|
}
|
|
2215
|
-
|
|
2191
|
+
// log(`${deleteLogPrefix} Rep obtained. selStart:`, rep.selStart, `selEnd:`, rep.selEnd);
|
|
2216
2192
|
const selStart = rep.selStart;
|
|
2217
2193
|
const selEnd = rep.selEnd;
|
|
2218
2194
|
const lineNum = selStart[0];
|
|
2219
|
-
|
|
2195
|
+
// log(`${deleteLogPrefix} Current line number: ${lineNum}. Column start: ${selStart[1]}, Column end: ${selEnd[1]}.`);
|
|
2220
2196
|
|
|
2221
2197
|
// Android Chrome IME: collapsed backspace/forward-delete often comes via beforeinput
|
|
2222
|
-
const isAndroidChrome =
|
|
2198
|
+
const isAndroidChrome = isAndroidUA();
|
|
2223
2199
|
const inputType = (evt.originalEvent && evt.originalEvent.inputType) || '';
|
|
2224
2200
|
|
|
2225
2201
|
// Handle collapsed deletes on Android Chrome inside a table line to protect delimiters
|
|
@@ -2315,12 +2291,12 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2315
2291
|
|
|
2316
2292
|
// Check if selection spans multiple lines
|
|
2317
2293
|
if (selStart[0] !== selEnd[0]) {
|
|
2318
|
-
|
|
2294
|
+
// log(`${deleteLogPrefix} WARNING: Selection spans multiple lines. Preventing delete to protect table structure.`);
|
|
2319
2295
|
evt.preventDefault();
|
|
2320
2296
|
return;
|
|
2321
2297
|
}
|
|
2322
2298
|
|
|
2323
|
-
|
|
2299
|
+
// log(`${deleteLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
|
|
2324
2300
|
let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
|
|
2325
2301
|
let tableMetadata = null;
|
|
2326
2302
|
|
|
@@ -2337,11 +2313,11 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2337
2313
|
}
|
|
2338
2314
|
|
|
2339
2315
|
if (!tableMetadata || typeof tableMetadata.cols !== 'number' || typeof tableMetadata.tblId === 'undefined' || typeof tableMetadata.row === 'undefined') {
|
|
2340
|
-
|
|
2316
|
+
// log(`${deleteLogPrefix} Line ${lineNum} is NOT a recognised table line. Allowing default delete.`);
|
|
2341
2317
|
return; // Not a table line
|
|
2342
2318
|
}
|
|
2343
2319
|
|
|
2344
|
-
|
|
2320
|
+
// log(`${deleteLogPrefix} Line ${lineNum} IS a table line. Metadata:`, tableMetadata);
|
|
2345
2321
|
|
|
2346
2322
|
// Validate selection is within cell boundaries
|
|
2347
2323
|
const lineText = rep.lines.atIndex(lineNum)?.text || '';
|
|
@@ -2392,25 +2368,25 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2392
2368
|
}
|
|
2393
2369
|
|
|
2394
2370
|
if (targetCellIndex === -1 || selEnd[1] > cellEndCol) {
|
|
2395
|
-
|
|
2371
|
+
// log(`${deleteLogPrefix} WARNING: Selection spans cell boundaries or is outside cells. Preventing delete to protect table structure.`);
|
|
2396
2372
|
evt.preventDefault();
|
|
2397
2373
|
return;
|
|
2398
2374
|
}
|
|
2399
2375
|
|
|
2400
2376
|
// If we reach here, the selection is entirely within a single cell - intercept delete and preserve table structure
|
|
2401
|
-
|
|
2377
|
+
// log(`${deleteLogPrefix} Selection is entirely within cell ${targetCellIndex}. Intercepting delete to preserve table structure.`);
|
|
2402
2378
|
evt.preventDefault();
|
|
2403
2379
|
|
|
2404
2380
|
try {
|
|
2405
2381
|
// No clipboard operations needed for delete - just perform the deletion within the cell using ace operations
|
|
2406
|
-
|
|
2382
|
+
// log(`${deleteLogPrefix} Performing deletion via ed.ace_callWithAce.`);
|
|
2407
2383
|
ed.ace_callWithAce((aceInstance) => {
|
|
2408
2384
|
const callAceLogPrefix = `${deleteLogPrefix}[ace_callWithAceOps]`;
|
|
2409
|
-
|
|
2385
|
+
// log(`${callAceLogPrefix} Entered ace_callWithAce for delete operations. selStart:`, selStart, `selEnd:`, selEnd);
|
|
2410
2386
|
|
|
2411
|
-
|
|
2387
|
+
// log(`${callAceLogPrefix} Calling aceInstance.ace_performDocumentReplaceRange to delete selected text.`);
|
|
2412
2388
|
aceInstance.ace_performDocumentReplaceRange(selStart, selEnd, '');
|
|
2413
|
-
|
|
2389
|
+
// log(`${callAceLogPrefix} ace_performDocumentReplaceRange successful.`);
|
|
2414
2390
|
|
|
2415
2391
|
// --- Ensure cell is not left empty (zero-length) ---
|
|
2416
2392
|
const repAfterDeletion = aceInstance.ace_getRep();
|
|
@@ -2419,7 +2395,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2419
2395
|
const cellTextAfterDeletion = cellsAfterDeletion[targetCellIndex] || '';
|
|
2420
2396
|
|
|
2421
2397
|
if (cellTextAfterDeletion.length === 0) {
|
|
2422
|
-
|
|
2398
|
+
// log(`${callAceLogPrefix} Cell ${targetCellIndex} became empty after delete – inserting single space to preserve structure.`);
|
|
2423
2399
|
const insertPos = [lineNum, selStart[1]]; // Start of the now-empty cell
|
|
2424
2400
|
aceInstance.ace_performDocumentReplaceRange(insertPos, insertPos, ' ');
|
|
2425
2401
|
|
|
@@ -2431,9 +2407,9 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2431
2407
|
);
|
|
2432
2408
|
}
|
|
2433
2409
|
|
|
2434
|
-
|
|
2410
|
+
// log(`${callAceLogPrefix} Preparing to re-apply tbljson attribute to line ${lineNum}.`);
|
|
2435
2411
|
const repAfterDelete = aceInstance.ace_getRep();
|
|
2436
|
-
|
|
2412
|
+
// log(`${callAceLogPrefix} Fetched rep after delete for applyMeta. Line ${lineNum} text now: "${repAfterDelete.lines.atIndex(lineNum).text}"`);
|
|
2437
2413
|
|
|
2438
2414
|
ed.ep_data_tables_applyMeta(
|
|
2439
2415
|
lineNum,
|
|
@@ -2445,22 +2421,22 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2445
2421
|
null,
|
|
2446
2422
|
docManager
|
|
2447
2423
|
);
|
|
2448
|
-
|
|
2424
|
+
// log(`${callAceLogPrefix} tbljson attribute re-applied successfully via ep_data_tables_applyMeta.`);
|
|
2449
2425
|
|
|
2450
2426
|
// Determine new caret position – one char forward if we inserted a space
|
|
2451
2427
|
const newCaretAbsoluteCol = (cellTextAfterDeletion.length === 0) ? selStart[1] + 1 : selStart[1];
|
|
2452
2428
|
const newCaretPos = [lineNum, newCaretAbsoluteCol];
|
|
2453
|
-
|
|
2429
|
+
// log(`${callAceLogPrefix} Setting caret position to: [${newCaretPos}].`);
|
|
2454
2430
|
aceInstance.ace_performSelectionChange(newCaretPos, newCaretPos, false);
|
|
2455
|
-
|
|
2431
|
+
// log(`${callAceLogPrefix} Selection change successful.`);
|
|
2456
2432
|
|
|
2457
|
-
|
|
2433
|
+
// log(`${callAceLogPrefix} Delete operations within ace_callWithAce completed successfully.`);
|
|
2458
2434
|
}, 'tableDeleteTextOperations', true);
|
|
2459
2435
|
|
|
2460
|
-
|
|
2436
|
+
// log(`${deleteLogPrefix} Delete operation completed successfully.`);
|
|
2461
2437
|
} catch (error) {
|
|
2462
2438
|
console.error(`${deleteLogPrefix} ERROR during delete operation:`, error);
|
|
2463
|
-
|
|
2439
|
+
// log(`${deleteLogPrefix} Delete operation failed. Error details:`, { message: error.message, stack: error.stack });
|
|
2464
2440
|
}
|
|
2465
2441
|
});
|
|
2466
2442
|
|
|
@@ -2472,8 +2448,8 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2472
2448
|
// Only intercept insert types
|
|
2473
2449
|
if (!inputType || !inputType.startsWith('insert')) return;
|
|
2474
2450
|
|
|
2475
|
-
// Target only Android
|
|
2476
|
-
if (!
|
|
2451
|
+
// Target only Android browsers (exclude iOS)
|
|
2452
|
+
if (!isAndroidUA()) return;
|
|
2477
2453
|
|
|
2478
2454
|
// Get current selection and ensure we are inside a table line
|
|
2479
2455
|
const rep = ed.ace_getRep();
|
|
@@ -2638,7 +2614,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2638
2614
|
|
|
2639
2615
|
// Composition start marker (Android Chrome/table only)
|
|
2640
2616
|
$inner.on('compositionstart', (evt) => {
|
|
2641
|
-
if (!
|
|
2617
|
+
if (!isAndroidUA()) return;
|
|
2642
2618
|
const rep = ed.ace_getRep();
|
|
2643
2619
|
if (!rep || !rep.selStart) return;
|
|
2644
2620
|
const lineNum = rep.selStart[0];
|
|
@@ -2655,7 +2631,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2655
2631
|
$inner.on('compositionupdate', (evt) => {
|
|
2656
2632
|
const compLogPrefix = '[ep_data_tables:compositionHandler]';
|
|
2657
2633
|
|
|
2658
|
-
if (!
|
|
2634
|
+
if (!isAndroidUA()) return;
|
|
2659
2635
|
|
|
2660
2636
|
const rep = ed.ace_getRep();
|
|
2661
2637
|
if (!rep || !rep.selStart) return;
|
|
@@ -2778,12 +2754,12 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2778
2754
|
});
|
|
2779
2755
|
|
|
2780
2756
|
// *** DRAG AND DROP EVENT LISTENERS ***
|
|
2781
|
-
|
|
2757
|
+
// log(`${callWithAceLogPrefix} Attaching drag and drop event listeners to $inner (inner iframe body).`);
|
|
2782
2758
|
|
|
2783
2759
|
// Prevent drops that could damage table structure
|
|
2784
2760
|
$inner.on('drop', (evt) => {
|
|
2785
2761
|
const dropLogPrefix = '[ep_data_tables:dropHandler]';
|
|
2786
|
-
|
|
2762
|
+
// log(`${dropLogPrefix} DROP EVENT TRIGGERED. Event object:`, evt);
|
|
2787
2763
|
|
|
2788
2764
|
// Block drops directly targeted at a table element regardless of selection state
|
|
2789
2765
|
const targetEl = evt.target;
|
|
@@ -2797,19 +2773,19 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2797
2773
|
return;
|
|
2798
2774
|
}
|
|
2799
2775
|
|
|
2800
|
-
|
|
2776
|
+
// log(`${dropLogPrefix} Getting current editor representation (rep).`);
|
|
2801
2777
|
const rep = ed.ace_getRep();
|
|
2802
2778
|
if (!rep || !rep.selStart) {
|
|
2803
|
-
|
|
2779
|
+
// log(`${dropLogPrefix} WARNING: Could not get representation or selection. Allowing default drop.`);
|
|
2804
2780
|
return; // Allow default
|
|
2805
2781
|
}
|
|
2806
2782
|
|
|
2807
2783
|
const selStart = rep.selStart;
|
|
2808
2784
|
const lineNum = selStart[0];
|
|
2809
|
-
|
|
2785
|
+
// log(`${dropLogPrefix} Current line number: ${lineNum}.`);
|
|
2810
2786
|
|
|
2811
2787
|
// Check if we're dropping onto a table line
|
|
2812
|
-
|
|
2788
|
+
// log(`${dropLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
|
|
2813
2789
|
let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
|
|
2814
2790
|
let isTableLine = !!lineAttrString;
|
|
2815
2791
|
|
|
@@ -2819,7 +2795,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2819
2795
|
}
|
|
2820
2796
|
|
|
2821
2797
|
if (isTableLine) {
|
|
2822
|
-
|
|
2798
|
+
// log(`${dropLogPrefix} Line ${lineNum} IS a table line. Preventing drop to protect table structure.`);
|
|
2823
2799
|
evt.preventDefault();
|
|
2824
2800
|
evt.stopPropagation();
|
|
2825
2801
|
console.warn('[ep_data_tables] Drop operation prevented to protect table structure. Please use copy/paste within table cells.');
|
|
@@ -2850,7 +2826,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2850
2826
|
const lineNum = selStart[0];
|
|
2851
2827
|
|
|
2852
2828
|
// Check if we're dragging over a table line
|
|
2853
|
-
|
|
2829
|
+
// log(`${dragLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
|
|
2854
2830
|
let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
|
|
2855
2831
|
let isTableLine = !!lineAttrString;
|
|
2856
2832
|
|
|
@@ -2859,7 +2835,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2859
2835
|
}
|
|
2860
2836
|
|
|
2861
2837
|
if (isTableLine) {
|
|
2862
|
-
|
|
2838
|
+
// log(`${dragLogPrefix} Preventing dragover on table line ${lineNum} to control drop handling.`);
|
|
2863
2839
|
evt.preventDefault();
|
|
2864
2840
|
}
|
|
2865
2841
|
});
|
|
@@ -2877,67 +2853,67 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2877
2853
|
});
|
|
2878
2854
|
|
|
2879
2855
|
// *** EXISTING PASTE LISTENER ***
|
|
2880
|
-
|
|
2856
|
+
// log(`${callWithAceLogPrefix} Attaching paste event listener to $inner (inner iframe body).`);
|
|
2881
2857
|
$inner.on('paste', (evt) => {
|
|
2882
2858
|
const pasteLogPrefix = '[ep_data_tables:pasteHandler]';
|
|
2883
|
-
|
|
2859
|
+
// log(`${pasteLogPrefix} PASTE EVENT TRIGGERED. Event object:`, evt);
|
|
2884
2860
|
|
|
2885
|
-
|
|
2861
|
+
// log(`${pasteLogPrefix} Getting current editor representation (rep).`);
|
|
2886
2862
|
const rep = ed.ace_getRep();
|
|
2887
2863
|
if (!rep || !rep.selStart) {
|
|
2888
|
-
|
|
2864
|
+
// log(`${pasteLogPrefix} WARNING: Could not get representation or selection. Allowing default paste.`);
|
|
2889
2865
|
console.warn(`${pasteLogPrefix} Could not get rep or selStart.`);
|
|
2890
2866
|
return; // Allow default
|
|
2891
2867
|
}
|
|
2892
|
-
|
|
2868
|
+
// log(`${pasteLogPrefix} Rep obtained. selStart:`, rep.selStart, `selEnd:`, rep.selEnd);
|
|
2893
2869
|
const selStart = rep.selStart;
|
|
2894
2870
|
const selEnd = rep.selEnd;
|
|
2895
2871
|
const lineNum = selStart[0];
|
|
2896
|
-
|
|
2872
|
+
// log(`${pasteLogPrefix} Current line number: ${lineNum}. Column start: ${selStart[1]}, Column end: ${selEnd[1]}.`);
|
|
2897
2873
|
|
|
2898
2874
|
// NEW: Check if selection spans multiple lines
|
|
2899
2875
|
if (selStart[0] !== selEnd[0]) {
|
|
2900
|
-
|
|
2876
|
+
// log(`${pasteLogPrefix} WARNING: Selection spans multiple lines. Preventing paste to protect table structure.`);
|
|
2901
2877
|
evt.preventDefault();
|
|
2902
2878
|
return;
|
|
2903
2879
|
}
|
|
2904
2880
|
|
|
2905
|
-
|
|
2881
|
+
// log(`${pasteLogPrefix} Checking if line ${lineNum} is a table line by fetching '${ATTR_TABLE_JSON}' attribute.`);
|
|
2906
2882
|
let lineAttrString = docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON);
|
|
2907
2883
|
let tableMetadata = null;
|
|
2908
2884
|
|
|
2909
2885
|
if (!lineAttrString) {
|
|
2910
2886
|
// Block-styled row? Reconstruct metadata from the DOM.
|
|
2911
|
-
|
|
2887
|
+
// log(`${pasteLogPrefix} No '${ATTR_TABLE_JSON}' attribute found. Checking if this is a block-styled table row via DOM reconstruction.`);
|
|
2912
2888
|
const fallbackMeta = getTableLineMetadata(lineNum, ed, docManager);
|
|
2913
2889
|
if (fallbackMeta) {
|
|
2914
2890
|
tableMetadata = fallbackMeta;
|
|
2915
2891
|
lineAttrString = JSON.stringify(fallbackMeta);
|
|
2916
|
-
|
|
2892
|
+
// log(`${pasteLogPrefix} Block-styled table row detected. Reconstructed metadata:`, fallbackMeta);
|
|
2917
2893
|
}
|
|
2918
2894
|
}
|
|
2919
2895
|
|
|
2920
2896
|
if (!lineAttrString) {
|
|
2921
|
-
|
|
2897
|
+
// log(`${pasteLogPrefix} Line ${lineNum} is NOT a table line (no '${ATTR_TABLE_JSON}' attribute found and no DOM reconstruction possible). Allowing default paste.`);
|
|
2922
2898
|
return; // Not a table line
|
|
2923
2899
|
}
|
|
2924
|
-
|
|
2900
|
+
// log(`${pasteLogPrefix} Line ${lineNum} IS a table line. Attribute string: "${lineAttrString}".`);
|
|
2925
2901
|
|
|
2926
2902
|
try {
|
|
2927
|
-
|
|
2903
|
+
// log(`${pasteLogPrefix} Parsing table metadata from attribute string.`);
|
|
2928
2904
|
if (!tableMetadata) {
|
|
2929
2905
|
tableMetadata = JSON.parse(lineAttrString);
|
|
2930
2906
|
}
|
|
2931
|
-
|
|
2907
|
+
// log(`${pasteLogPrefix} Parsed table metadata:`, tableMetadata);
|
|
2932
2908
|
if (!tableMetadata || typeof tableMetadata.cols !== 'number' || typeof tableMetadata.tblId === 'undefined' || typeof tableMetadata.row === 'undefined') {
|
|
2933
|
-
|
|
2909
|
+
// log(`${pasteLogPrefix} WARNING: Invalid or incomplete table metadata on line ${lineNum}. Allowing default paste. Metadata:`, tableMetadata);
|
|
2934
2910
|
console.warn(`${pasteLogPrefix} Invalid table metadata for line ${lineNum}.`);
|
|
2935
2911
|
return; // Allow default
|
|
2936
2912
|
}
|
|
2937
|
-
|
|
2913
|
+
// log(`${pasteLogPrefix} Table metadata validated successfully: tblId=${tableMetadata.tblId}, row=${tableMetadata.row}, cols=${tableMetadata.cols}.`);
|
|
2938
2914
|
} catch(e) {
|
|
2939
2915
|
console.error(`${pasteLogPrefix} ERROR parsing table metadata for line ${lineNum}:`, e);
|
|
2940
|
-
|
|
2916
|
+
// log(`${pasteLogPrefix} Metadata parse error. Allowing default paste. Error details:`, { message: e.message, stack: e.stack });
|
|
2941
2917
|
return; // Allow default
|
|
2942
2918
|
}
|
|
2943
2919
|
|
|
@@ -2968,29 +2944,29 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2968
2944
|
selEnd[1] = cellEndCol; // clamp
|
|
2969
2945
|
}
|
|
2970
2946
|
if (targetCellIndex === -1 || selEnd[1] > cellEndCol) {
|
|
2971
|
-
|
|
2947
|
+
// log(`${pasteLogPrefix} WARNING: Selection spans cell boundaries or is outside cells. Preventing paste to protect table structure.`);
|
|
2972
2948
|
evt.preventDefault();
|
|
2973
2949
|
return;
|
|
2974
2950
|
}
|
|
2975
2951
|
|
|
2976
|
-
|
|
2952
|
+
// log(`${pasteLogPrefix} Accessing clipboard data.`);
|
|
2977
2953
|
const clipboardData = evt.originalEvent.clipboardData || window.clipboardData;
|
|
2978
2954
|
if (!clipboardData) {
|
|
2979
|
-
|
|
2955
|
+
// log(`${pasteLogPrefix} WARNING: No clipboard data found. Allowing default paste.`);
|
|
2980
2956
|
return; // Allow default
|
|
2981
2957
|
}
|
|
2982
|
-
|
|
2958
|
+
// log(`${pasteLogPrefix} Clipboard data object obtained:`, clipboardData);
|
|
2983
2959
|
|
|
2984
2960
|
// Allow default handling (so ep_hyperlinked_text plugin can process) if rich HTML is present
|
|
2985
2961
|
const types = clipboardData.types || [];
|
|
2986
2962
|
if (types.includes('text/html') && clipboardData.getData('text/html')) {
|
|
2987
|
-
|
|
2963
|
+
// log(`${pasteLogPrefix} Detected text/html in clipboard – deferring to other plugins and default paste.`);
|
|
2988
2964
|
return; // Do not intercept
|
|
2989
2965
|
}
|
|
2990
2966
|
|
|
2991
|
-
|
|
2967
|
+
// log(`${pasteLogPrefix} Getting 'text/plain' from clipboard.`);
|
|
2992
2968
|
const pastedTextRaw = clipboardData.getData('text/plain');
|
|
2993
|
-
|
|
2969
|
+
// log(`${pasteLogPrefix} Pasted text raw: "${pastedTextRaw}" (Type: ${typeof pastedTextRaw})`);
|
|
2994
2970
|
|
|
2995
2971
|
// ENHANCED: More thorough sanitization of pasted content
|
|
2996
2972
|
let pastedText = pastedTextRaw
|
|
@@ -3000,18 +2976,18 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3000
2976
|
.replace(/\s+/g, " ") // Normalize whitespace
|
|
3001
2977
|
.trim(); // Trim leading/trailing whitespace
|
|
3002
2978
|
|
|
3003
|
-
|
|
2979
|
+
// log(`${pasteLogPrefix} Pasted text after sanitization: "${pastedText}"`);
|
|
3004
2980
|
|
|
3005
2981
|
if (typeof pastedText !== 'string' || pastedText.length === 0) {
|
|
3006
|
-
|
|
2982
|
+
// log(`${pasteLogPrefix} No plain text in clipboard or text is empty (after sanitization). Allowing default paste.`);
|
|
3007
2983
|
const types = clipboardData.types;
|
|
3008
|
-
|
|
2984
|
+
// log(`${pasteLogPrefix} Clipboard types available:`, types);
|
|
3009
2985
|
if (types && types.includes('text/html')) {
|
|
3010
|
-
|
|
2986
|
+
// log(`${pasteLogPrefix} Clipboard also contains HTML:`, clipboardData.getData('text/html'));
|
|
3011
2987
|
}
|
|
3012
2988
|
return; // Allow default if no plain text
|
|
3013
2989
|
}
|
|
3014
|
-
|
|
2990
|
+
// log(`${pasteLogPrefix} Plain text obtained from clipboard: "${pastedText}". Length: ${pastedText.length}.`);
|
|
3015
2991
|
|
|
3016
2992
|
// NEW: Check if paste would exceed cell boundaries
|
|
3017
2993
|
const currentCellText = cells[targetCellIndex] || '';
|
|
@@ -3025,18 +3001,18 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3025
3001
|
// see fit or set to `Infinity` to remove the cap entirely.
|
|
3026
3002
|
const MAX_CELL_LENGTH = 8000;
|
|
3027
3003
|
if (newCellLength > MAX_CELL_LENGTH) {
|
|
3028
|
-
|
|
3004
|
+
// log(`${pasteLogPrefix} WARNING: Paste would exceed maximum cell length (${newCellLength} > ${MAX_CELL_LENGTH}). Truncating paste.`);
|
|
3029
3005
|
const truncatedPaste = pastedText.substring(0, MAX_CELL_LENGTH - (currentCellText.length - selectionLength));
|
|
3030
3006
|
if (truncatedPaste.length === 0) {
|
|
3031
|
-
|
|
3007
|
+
// log(`${pasteLogPrefix} Paste would be completely truncated. Preventing paste.`);
|
|
3032
3008
|
evt.preventDefault();
|
|
3033
3009
|
return;
|
|
3034
3010
|
}
|
|
3035
|
-
|
|
3011
|
+
// log(`${pasteLogPrefix} Using truncated paste: "${truncatedPaste}"`);
|
|
3036
3012
|
pastedText = truncatedPaste;
|
|
3037
3013
|
}
|
|
3038
3014
|
|
|
3039
|
-
|
|
3015
|
+
// log(`${pasteLogPrefix} INTERCEPTING paste of plain text into table line ${lineNum}. PREVENTING DEFAULT browser action.`);
|
|
3040
3016
|
evt.preventDefault();
|
|
3041
3017
|
// Prevent other plugins from handling the same paste event once we
|
|
3042
3018
|
// have intercepted it inside a table cell.
|
|
@@ -3044,20 +3020,20 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3044
3020
|
if (typeof evt.stopImmediatePropagation === 'function') evt.stopImmediatePropagation();
|
|
3045
3021
|
|
|
3046
3022
|
try {
|
|
3047
|
-
|
|
3023
|
+
// log(`${pasteLogPrefix} Preparing to perform paste operations via ed.ace_callWithAce.`);
|
|
3048
3024
|
ed.ace_callWithAce((aceInstance) => {
|
|
3049
3025
|
const callAceLogPrefix = `${pasteLogPrefix}[ace_callWithAceOps]`;
|
|
3050
|
-
|
|
3026
|
+
// log(`${callAceLogPrefix} Entered ace_callWithAce for paste operations. selStart:`, selStart, `selEnd:`, selEnd);
|
|
3051
3027
|
|
|
3052
|
-
|
|
3028
|
+
// log(`${callAceLogPrefix} Original line text from initial rep: "${rep.lines.atIndex(lineNum).text}". SelStartCol: ${selStart[1]}, SelEndCol: ${selEnd[1]}.`);
|
|
3053
3029
|
|
|
3054
|
-
|
|
3030
|
+
// log(`${callAceLogPrefix} Calling aceInstance.ace_performDocumentReplaceRange to insert text: "${pastedText}".`);
|
|
3055
3031
|
aceInstance.ace_performDocumentReplaceRange(selStart, selEnd, pastedText);
|
|
3056
|
-
|
|
3032
|
+
// log(`${callAceLogPrefix} ace_performDocumentReplaceRange successful.`);
|
|
3057
3033
|
|
|
3058
|
-
|
|
3034
|
+
// log(`${callAceLogPrefix} Preparing to re-apply tbljson attribute to line ${lineNum}.`);
|
|
3059
3035
|
const repAfterReplace = aceInstance.ace_getRep();
|
|
3060
|
-
|
|
3036
|
+
// log(`${callAceLogPrefix} Fetched rep after replace for applyMeta. Line ${lineNum} text now: "${repAfterReplace.lines.atIndex(lineNum).text}"`);
|
|
3061
3037
|
|
|
3062
3038
|
ed.ep_data_tables_applyMeta(
|
|
3063
3039
|
lineNum,
|
|
@@ -3069,17 +3045,17 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3069
3045
|
null,
|
|
3070
3046
|
docManager
|
|
3071
3047
|
);
|
|
3072
|
-
|
|
3048
|
+
// log(`${callAceLogPrefix} tbljson attribute re-applied successfully via ep_data_tables_applyMeta.`);
|
|
3073
3049
|
|
|
3074
3050
|
const newCaretCol = selStart[1] + pastedText.length;
|
|
3075
3051
|
const newCaretPos = [lineNum, newCaretCol];
|
|
3076
|
-
|
|
3052
|
+
// log(`${callAceLogPrefix} New calculated caret position: [${newCaretPos}]. Setting selection.`);
|
|
3077
3053
|
aceInstance.ace_performSelectionChange(newCaretPos, newCaretPos, false);
|
|
3078
|
-
|
|
3054
|
+
// log(`${callAceLogPrefix} Selection change successful.`);
|
|
3079
3055
|
|
|
3080
|
-
|
|
3056
|
+
// log(`${callAceLogPrefix} Requesting fastIncorp(10) for sync.`);
|
|
3081
3057
|
aceInstance.ace_fastIncorp(10);
|
|
3082
|
-
|
|
3058
|
+
// log(`${callAceLogPrefix} fastIncorp requested.`);
|
|
3083
3059
|
|
|
3084
3060
|
// Update stored click/caret info
|
|
3085
3061
|
if (editor && editor.ep_data_tables_last_clicked && editor.ep_data_tables_last_clicked.tblId === tableMetadata.tblId) {
|
|
@@ -3090,23 +3066,23 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3090
3066
|
cellIndex: targetCellIndex,
|
|
3091
3067
|
relativePos: newRelativePos < 0 ? 0 : newRelativePos,
|
|
3092
3068
|
};
|
|
3093
|
-
|
|
3069
|
+
// log(`${callAceLogPrefix} Updated stored click/caret info:`, editor.ep_data_tables_last_clicked);
|
|
3094
3070
|
}
|
|
3095
3071
|
|
|
3096
|
-
|
|
3072
|
+
// log(`${callAceLogPrefix} Paste operations within ace_callWithAce completed successfully.`);
|
|
3097
3073
|
}, 'tablePasteTextOperations', true);
|
|
3098
|
-
|
|
3074
|
+
// log(`${pasteLogPrefix} ed.ace_callWithAce for paste operations was called.`);
|
|
3099
3075
|
|
|
3100
3076
|
} catch (error) {
|
|
3101
3077
|
console.error(`${pasteLogPrefix} CRITICAL ERROR during paste handling operation:`, error);
|
|
3102
|
-
|
|
3103
|
-
|
|
3078
|
+
// log(`${pasteLogPrefix} Error details:`, { message: error.message, stack: error.stack });
|
|
3079
|
+
// log(`${pasteLogPrefix} Paste handling FAILED. END OF HANDLER.`);
|
|
3104
3080
|
}
|
|
3105
3081
|
});
|
|
3106
|
-
|
|
3082
|
+
// log(`${callWithAceLogPrefix} Paste event listener attached.`);
|
|
3107
3083
|
|
|
3108
3084
|
// *** NEW: Column resize listeners ***
|
|
3109
|
-
|
|
3085
|
+
// log(`${callWithAceLogPrefix} Attaching column resize listeners...`);
|
|
3110
3086
|
|
|
3111
3087
|
// Get the iframe documents for proper event delegation
|
|
3112
3088
|
const $iframeOuter = $('iframe[name="ace_outer"]');
|
|
@@ -3114,16 +3090,16 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3114
3090
|
const innerDoc = $iframeInner.contents();
|
|
3115
3091
|
const outerDoc = $iframeOuter.contents();
|
|
3116
3092
|
|
|
3117
|
-
|
|
3093
|
+
// log(`${callWithAceLogPrefix} Found iframe documents: outer=${outerDoc.length}, inner=${innerDoc.length}`);
|
|
3118
3094
|
|
|
3119
3095
|
// Mousedown on resize handles
|
|
3120
3096
|
$inner.on('mousedown', '.ep-data_tables-resize-handle', (evt) => {
|
|
3121
3097
|
const resizeLogPrefix = '[ep_data_tables:resizeMousedown]';
|
|
3122
|
-
|
|
3098
|
+
// log(`${resizeLogPrefix} Resize handle mousedown detected`);
|
|
3123
3099
|
|
|
3124
3100
|
// Only handle left mouse button clicks
|
|
3125
3101
|
if (evt.button !== 0) {
|
|
3126
|
-
|
|
3102
|
+
// log(`${resizeLogPrefix} Ignoring non-left mouse button: ${evt.button}`);
|
|
3127
3103
|
return;
|
|
3128
3104
|
}
|
|
3129
3105
|
|
|
@@ -3134,7 +3110,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3134
3110
|
const isImageResizeHandle = $target.hasClass('image-resize-handle') || $target.closest('.image-resize-handle').length > 0;
|
|
3135
3111
|
|
|
3136
3112
|
if (isImageRelated || isImageResizeHandle) {
|
|
3137
|
-
|
|
3113
|
+
// log(`${resizeLogPrefix} Click detected on image-related element or image resize handle, ignoring for table resize`);
|
|
3138
3114
|
return;
|
|
3139
3115
|
}
|
|
3140
3116
|
|
|
@@ -3146,7 +3122,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3146
3122
|
const table = handle.closest('table.dataTable');
|
|
3147
3123
|
const lineNode = table.closest('div.ace-line');
|
|
3148
3124
|
|
|
3149
|
-
|
|
3125
|
+
// log(`${resizeLogPrefix} Parsed resize target: columnIndex=${columnIndex}, table=${!!table}, lineNode=${!!lineNode}`);
|
|
3150
3126
|
|
|
3151
3127
|
if (table && lineNode && !isNaN(columnIndex)) {
|
|
3152
3128
|
// Get table metadata
|
|
@@ -3160,7 +3136,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3160
3136
|
|
|
3161
3137
|
const lineNum = rep.lines.indexOfKey(lineNode.id);
|
|
3162
3138
|
|
|
3163
|
-
|
|
3139
|
+
// log(`${resizeLogPrefix} Table info: tblId=${tblId}, lineNum=${lineNum}`);
|
|
3164
3140
|
|
|
3165
3141
|
if (tblId && lineNum !== -1) {
|
|
3166
3142
|
try {
|
|
@@ -3168,17 +3144,17 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3168
3144
|
if (lineAttrString) {
|
|
3169
3145
|
const metadata = JSON.parse(lineAttrString);
|
|
3170
3146
|
if (metadata.tblId === tblId) {
|
|
3171
|
-
|
|
3147
|
+
// log(`${resizeLogPrefix} Starting resize with metadata:`, metadata);
|
|
3172
3148
|
startColumnResize(table, columnIndex, evt.clientX, metadata, lineNum);
|
|
3173
|
-
|
|
3149
|
+
// log(`${resizeLogPrefix} Started resize for column ${columnIndex}`);
|
|
3174
3150
|
|
|
3175
3151
|
// DEBUG: Verify global state is set
|
|
3176
|
-
|
|
3152
|
+
// log(`${resizeLogPrefix} Global resize state: isResizing=${isResizing}, targetTable=${!!resizeTargetTable}, targetColumn=${resizeTargetColumn}`);
|
|
3177
3153
|
} else {
|
|
3178
|
-
|
|
3154
|
+
// log(`${resizeLogPrefix} Table ID mismatch: ${metadata.tblId} vs ${tblId}`);
|
|
3179
3155
|
}
|
|
3180
3156
|
} else {
|
|
3181
|
-
|
|
3157
|
+
// log(`${resizeLogPrefix} No table metadata found for line ${lineNum}, trying DOM reconstruction...`);
|
|
3182
3158
|
|
|
3183
3159
|
// Fallback: Reconstruct metadata from DOM (same logic as ace_doDatatableOptions)
|
|
3184
3160
|
const rep = ed.ace_getRep();
|
|
@@ -3212,37 +3188,37 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3212
3188
|
cols: domCells.length,
|
|
3213
3189
|
columnWidths: columnWidths
|
|
3214
3190
|
};
|
|
3215
|
-
|
|
3191
|
+
// log(`${resizeLogPrefix} Reconstructed metadata from DOM:`, reconstructedMetadata);
|
|
3216
3192
|
|
|
3217
3193
|
startColumnResize(table, columnIndex, evt.clientX, reconstructedMetadata, lineNum);
|
|
3218
|
-
|
|
3194
|
+
// log(`${resizeLogPrefix} Started resize for column ${columnIndex} using reconstructed metadata`);
|
|
3219
3195
|
|
|
3220
3196
|
// DEBUG: Verify global state is set
|
|
3221
|
-
|
|
3197
|
+
// log(`${resizeLogPrefix} Global resize state: isResizing=${isResizing}, targetTable=${!!resizeTargetTable}, targetColumn=${resizeTargetColumn}`);
|
|
3222
3198
|
} else {
|
|
3223
|
-
|
|
3199
|
+
// log(`${resizeLogPrefix} DOM table found but no cells detected`);
|
|
3224
3200
|
}
|
|
3225
3201
|
} else {
|
|
3226
|
-
|
|
3202
|
+
// log(`${resizeLogPrefix} DOM table found but tblId mismatch or missing row: domTblId=${domTblId}, domRow=${domRow}`);
|
|
3227
3203
|
}
|
|
3228
3204
|
} else {
|
|
3229
|
-
|
|
3205
|
+
// log(`${resizeLogPrefix} No table found in DOM for line ${lineNum}`);
|
|
3230
3206
|
}
|
|
3231
3207
|
} else {
|
|
3232
|
-
|
|
3208
|
+
// log(`${resizeLogPrefix} Could not get line entry or lineNode for line ${lineNum}`);
|
|
3233
3209
|
}
|
|
3234
3210
|
} else {
|
|
3235
|
-
|
|
3211
|
+
// log(`${resizeLogPrefix} Could not get rep or rep.lines for DOM reconstruction`);
|
|
3236
3212
|
}
|
|
3237
3213
|
}
|
|
3238
3214
|
} catch (e) {
|
|
3239
3215
|
console.error(`${resizeLogPrefix} Error getting table metadata:`, e);
|
|
3240
3216
|
}
|
|
3241
3217
|
} else {
|
|
3242
|
-
|
|
3218
|
+
// log(`${resizeLogPrefix} Invalid line number (${lineNum}) or table ID (${tblId})`);
|
|
3243
3219
|
}
|
|
3244
3220
|
} else {
|
|
3245
|
-
|
|
3221
|
+
// log(`${resizeLogPrefix} Invalid resize target:`, { table: !!table, lineNode: !!lineNode, columnIndex });
|
|
3246
3222
|
}
|
|
3247
3223
|
});
|
|
3248
3224
|
|
|
@@ -3261,57 +3237,57 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3261
3237
|
|
|
3262
3238
|
// Mouseup handler with enhanced debugging
|
|
3263
3239
|
const handleMouseup = (evt) => {
|
|
3264
|
-
|
|
3240
|
+
// log(`${mouseupLogPrefix} Mouseup detected on ${evt.target.tagName || 'unknown'}. isResizing: ${isResizing}`);
|
|
3265
3241
|
|
|
3266
3242
|
if (isResizing) {
|
|
3267
|
-
|
|
3243
|
+
// log(`${mouseupLogPrefix} Processing resize completion...`);
|
|
3268
3244
|
evt.preventDefault();
|
|
3269
3245
|
evt.stopPropagation();
|
|
3270
3246
|
|
|
3271
3247
|
// Add a small delay to ensure all DOM updates are complete
|
|
3272
3248
|
setTimeout(() => {
|
|
3273
|
-
|
|
3249
|
+
// log(`${mouseupLogPrefix} Executing finishColumnResize after delay...`);
|
|
3274
3250
|
finishColumnResize(ed, docManager);
|
|
3275
|
-
|
|
3251
|
+
// log(`${mouseupLogPrefix} Resize completion finished.`);
|
|
3276
3252
|
}, 50);
|
|
3277
3253
|
} else {
|
|
3278
|
-
|
|
3254
|
+
// log(`${mouseupLogPrefix} Not in resize mode, ignoring mouseup.`);
|
|
3279
3255
|
}
|
|
3280
3256
|
};
|
|
3281
3257
|
|
|
3282
3258
|
// Attach to multiple contexts to ensure we catch the event
|
|
3283
|
-
|
|
3259
|
+
// log(`${callWithAceLogPrefix} Attaching global mousemove/mouseup handlers to multiple contexts...`);
|
|
3284
3260
|
|
|
3285
3261
|
// 1. Main document (outside iframes)
|
|
3286
3262
|
$(document).on('mousemove', handleMousemove);
|
|
3287
3263
|
$(document).on('mouseup', handleMouseup);
|
|
3288
|
-
|
|
3264
|
+
// log(`${callWithAceLogPrefix} Attached to main document`);
|
|
3289
3265
|
|
|
3290
3266
|
// 2. Outer iframe document
|
|
3291
3267
|
if (outerDoc.length > 0) {
|
|
3292
3268
|
outerDoc.on('mousemove', handleMousemove);
|
|
3293
3269
|
outerDoc.on('mouseup', handleMouseup);
|
|
3294
|
-
|
|
3270
|
+
// log(`${callWithAceLogPrefix} Attached to outer iframe document`);
|
|
3295
3271
|
}
|
|
3296
3272
|
|
|
3297
3273
|
// 3. Inner iframe document
|
|
3298
3274
|
if (innerDoc.length > 0) {
|
|
3299
3275
|
innerDoc.on('mousemove', handleMousemove);
|
|
3300
3276
|
innerDoc.on('mouseup', handleMouseup);
|
|
3301
|
-
|
|
3277
|
+
// log(`${callWithAceLogPrefix} Attached to inner iframe document`);
|
|
3302
3278
|
}
|
|
3303
3279
|
|
|
3304
3280
|
// 4. Inner iframe body (the editing area)
|
|
3305
3281
|
$inner.on('mousemove', handleMousemove);
|
|
3306
3282
|
$inner.on('mouseup', handleMouseup);
|
|
3307
|
-
|
|
3283
|
+
// log(`${callWithAceLogPrefix} Attached to inner iframe body`);
|
|
3308
3284
|
|
|
3309
3285
|
// 5. Add a failsafe - listen for any mouse events during resize
|
|
3310
3286
|
const failsafeMouseup = (evt) => {
|
|
3311
3287
|
if (isResizing) {
|
|
3312
|
-
|
|
3288
|
+
// log(`${mouseupLogPrefix} FAILSAFE: Detected mouse event during resize: ${evt.type}`);
|
|
3313
3289
|
if (evt.type === 'mouseup' || evt.type === 'mousedown' || evt.type === 'click') {
|
|
3314
|
-
|
|
3290
|
+
// log(`${mouseupLogPrefix} FAILSAFE: Triggering resize completion due to ${evt.type}`);
|
|
3315
3291
|
setTimeout(() => {
|
|
3316
3292
|
if (isResizing) { // Double-check we're still resizing
|
|
3317
3293
|
finishColumnResize(ed, docManager);
|
|
@@ -3325,7 +3301,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3325
3301
|
document.addEventListener('mouseup', failsafeMouseup, true);
|
|
3326
3302
|
document.addEventListener('mousedown', failsafeMouseup, true);
|
|
3327
3303
|
document.addEventListener('click', failsafeMouseup, true);
|
|
3328
|
-
|
|
3304
|
+
// log(`${callWithAceLogPrefix} Attached failsafe event handlers`);
|
|
3329
3305
|
|
|
3330
3306
|
// *** DRAG PREVENTION FOR TABLE ELEMENTS ***
|
|
3331
3307
|
const preventTableDrag = (evt) => {
|
|
@@ -3333,7 +3309,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3333
3309
|
// Block ANY drag gesture that originates within a table (including spans/text inside cells)
|
|
3334
3310
|
const inTable = target && typeof target.closest === 'function' && target.closest('table.dataTable');
|
|
3335
3311
|
if (inTable) {
|
|
3336
|
-
|
|
3312
|
+
// log('[ep_data_tables:dragPrevention] Preventing drag operation originating from inside table');
|
|
3337
3313
|
evt.preventDefault();
|
|
3338
3314
|
evt.stopPropagation();
|
|
3339
3315
|
if (evt.originalEvent && evt.originalEvent.dataTransfer) {
|
|
@@ -3347,7 +3323,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3347
3323
|
$inner.on('dragstart', preventTableDrag);
|
|
3348
3324
|
$inner.on('drag', preventTableDrag);
|
|
3349
3325
|
$inner.on('dragend', preventTableDrag);
|
|
3350
|
-
|
|
3326
|
+
// log(`${callWithAceLogPrefix} Attached drag prevention handlers to inner body`);
|
|
3351
3327
|
|
|
3352
3328
|
// Attach drag prevention broadly to cover iframe boundaries and the host document
|
|
3353
3329
|
if (innerDoc.length > 0) {
|
|
@@ -3368,15 +3344,15 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3368
3344
|
// Setup the global handlers
|
|
3369
3345
|
setupGlobalHandlers();
|
|
3370
3346
|
|
|
3371
|
-
|
|
3347
|
+
// log(`${callWithAceLogPrefix} Column resize listeners attached successfully.`);
|
|
3372
3348
|
|
|
3373
3349
|
}, 'tablePasteAndResizeListeners', true);
|
|
3374
|
-
|
|
3350
|
+
// log(`${logPrefix} ace_callWithAce for listeners setup completed.`);
|
|
3375
3351
|
|
|
3376
3352
|
// Helper function to apply the metadata attribute to a line
|
|
3377
3353
|
function applyTableLineMetadataAttribute (lineNum, tblId, rowIndex, numCols, rep, editorInfo, attributeString = null, documentAttributeManager = null) {
|
|
3378
3354
|
const funcName = 'applyTableLineMetadataAttribute';
|
|
3379
|
-
|
|
3355
|
+
// log(`${logPrefix}:${funcName}: START - Applying METADATA attribute to line ${lineNum}`, {tblId, rowIndex, numCols});
|
|
3380
3356
|
|
|
3381
3357
|
let finalMetadata;
|
|
3382
3358
|
|
|
@@ -3387,14 +3363,14 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3387
3363
|
if (providedMetadata.columnWidths && Array.isArray(providedMetadata.columnWidths) && providedMetadata.columnWidths.length === numCols) {
|
|
3388
3364
|
// Already has valid columnWidths, use as-is
|
|
3389
3365
|
finalMetadata = providedMetadata;
|
|
3390
|
-
|
|
3366
|
+
// log(`${logPrefix}:${funcName}: Using provided metadata with existing columnWidths`);
|
|
3391
3367
|
} else {
|
|
3392
3368
|
// Has metadata but missing/invalid columnWidths, extract from DOM
|
|
3393
3369
|
finalMetadata = providedMetadata;
|
|
3394
|
-
|
|
3370
|
+
// log(`${logPrefix}:${funcName}: Provided metadata missing columnWidths, attempting DOM extraction`);
|
|
3395
3371
|
}
|
|
3396
3372
|
} catch (e) {
|
|
3397
|
-
|
|
3373
|
+
// log(`${logPrefix}:${funcName}: Error parsing provided attributeString, will reconstruct:`, e);
|
|
3398
3374
|
finalMetadata = null;
|
|
3399
3375
|
}
|
|
3400
3376
|
}
|
|
@@ -3425,13 +3401,13 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3425
3401
|
columnWidths.push(100 / numCols);
|
|
3426
3402
|
}
|
|
3427
3403
|
});
|
|
3428
|
-
|
|
3404
|
+
// log(`${logPrefix}:${funcName}: Extracted column widths from DOM: ${columnWidths.map(w => w.toFixed(1) + '%').join(', ')}`);
|
|
3429
3405
|
}
|
|
3430
3406
|
}
|
|
3431
3407
|
}
|
|
3432
3408
|
}
|
|
3433
3409
|
} catch (e) {
|
|
3434
|
-
|
|
3410
|
+
// log(`${logPrefix}:${funcName}: Error extracting column widths from DOM:`, e);
|
|
3435
3411
|
}
|
|
3436
3412
|
|
|
3437
3413
|
// Build final metadata
|
|
@@ -3448,26 +3424,26 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3448
3424
|
}
|
|
3449
3425
|
|
|
3450
3426
|
const finalAttributeString = JSON.stringify(finalMetadata);
|
|
3451
|
-
|
|
3427
|
+
// log(`${logPrefix}:${funcName}: Final metadata attribute string: ${finalAttributeString}`);
|
|
3452
3428
|
|
|
3453
3429
|
try {
|
|
3454
3430
|
// Get current line info
|
|
3455
3431
|
const lineEntry = rep.lines.atIndex(lineNum);
|
|
3456
3432
|
if (!lineEntry) {
|
|
3457
|
-
|
|
3433
|
+
// log(`${logPrefix}:${funcName}: ERROR - Could not find line entry for line number ${lineNum}`);
|
|
3458
3434
|
return;
|
|
3459
3435
|
}
|
|
3460
3436
|
const lineLength = Math.max(1, lineEntry.text.length);
|
|
3461
|
-
|
|
3437
|
+
// log(`${logPrefix}:${funcName}: Line ${lineNum} text length: ${lineLength}`);
|
|
3462
3438
|
|
|
3463
3439
|
// Simple attribute application - just add the tbljson attribute
|
|
3464
3440
|
const attributes = [[ATTR_TABLE_JSON, finalAttributeString]];
|
|
3465
3441
|
const start = [lineNum, 0];
|
|
3466
3442
|
const end = [lineNum, lineLength];
|
|
3467
3443
|
|
|
3468
|
-
|
|
3444
|
+
// log(`${logPrefix}:${funcName}: Applying tbljson attribute to range [${start}]-[${end}]`);
|
|
3469
3445
|
editorInfo.ace_performDocumentApplyAttributesToRange(start, end, attributes);
|
|
3470
|
-
|
|
3446
|
+
// log(`${logPrefix}:${funcName}: Successfully applied tbljson attribute to line ${lineNum}`);
|
|
3471
3447
|
|
|
3472
3448
|
} catch(e) {
|
|
3473
3449
|
console.error(`[ep_data_tables] ${logPrefix}:${funcName}: Error applying metadata attribute on line ${lineNum}:`, e);
|
|
@@ -3477,59 +3453,59 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3477
3453
|
/** Insert a fresh rows×cols blank table at the caret */
|
|
3478
3454
|
ed.ace_createTableViaAttributes = (rows = 2, cols = 2) => {
|
|
3479
3455
|
const funcName = 'ace_createTableViaAttributes';
|
|
3480
|
-
|
|
3456
|
+
// log(`${funcName}: START - Refactored Phase 4 (Get Selection Fix)`, { rows, cols });
|
|
3481
3457
|
rows = Math.max(1, rows); cols = Math.max(1, cols);
|
|
3482
|
-
|
|
3458
|
+
// log(`${funcName}: Ensuring minimum 1 row, 1 col.`);
|
|
3483
3459
|
|
|
3484
3460
|
// --- Phase 1: Prepare Data ---
|
|
3485
3461
|
const tblId = rand();
|
|
3486
|
-
|
|
3462
|
+
// log(`${funcName}: Generated table ID: ${tblId}`);
|
|
3487
3463
|
const initialCellContent = ' '; // Start with a single space per cell
|
|
3488
3464
|
const lineTxt = Array.from({ length: cols }).fill(initialCellContent).join(DELIMITER);
|
|
3489
|
-
|
|
3465
|
+
// log(`${funcName}: Constructed initial line text for ${cols} cols: "${lineTxt}"`);
|
|
3490
3466
|
const block = Array.from({ length: rows }).fill(lineTxt).join('\n') + '\n';
|
|
3491
|
-
|
|
3467
|
+
// log(`${funcName}: Constructed block for ${rows} rows:\n${block}`);
|
|
3492
3468
|
|
|
3493
3469
|
// Get current selection BEFORE making changes using ace_getRep()
|
|
3494
|
-
|
|
3470
|
+
// log(`${funcName}: Getting current representation and selection...`);
|
|
3495
3471
|
const currentRepInitial = ed.ace_getRep();
|
|
3496
3472
|
if (!currentRepInitial || !currentRepInitial.selStart || !currentRepInitial.selEnd) {
|
|
3497
3473
|
console.error(`[ep_data_tables] ${funcName}: Could not get current representation or selection via ace_getRep(). Aborting.`);
|
|
3498
|
-
|
|
3474
|
+
// log(`${funcName}: END - Error getting initial rep/selection`);
|
|
3499
3475
|
return;
|
|
3500
3476
|
}
|
|
3501
3477
|
const start = currentRepInitial.selStart;
|
|
3502
3478
|
const end = currentRepInitial.selEnd;
|
|
3503
3479
|
const initialStartLine = start[0]; // Store the starting line number
|
|
3504
|
-
|
|
3480
|
+
// log(`${funcName}: Current selection from initial rep:`, { start, end });
|
|
3505
3481
|
|
|
3506
3482
|
// --- Phase 2: Insert Text Block ---
|
|
3507
|
-
|
|
3483
|
+
// log(`${funcName}: Phase 2 - Inserting text block...`);
|
|
3508
3484
|
ed.ace_performDocumentReplaceRange(start, end, block);
|
|
3509
|
-
|
|
3510
|
-
|
|
3485
|
+
// log(`${funcName}: Inserted block of delimited text lines.`);
|
|
3486
|
+
// log(`${funcName}: Requesting text sync (ace_fastIncorp 20)...`);
|
|
3511
3487
|
ed.ace_fastIncorp(20); // Sync text insertion
|
|
3512
|
-
|
|
3488
|
+
// log(`${funcName}: Text sync requested.`);
|
|
3513
3489
|
|
|
3514
3490
|
// --- Phase 3: Apply Metadata Attributes ---
|
|
3515
|
-
|
|
3491
|
+
// log(`${funcName}: Phase 3 - Applying metadata attributes to ${rows} inserted lines...`);
|
|
3516
3492
|
// Need rep to be updated after text insertion to apply attributes correctly
|
|
3517
3493
|
const currentRep = ed.ace_getRep(); // Get potentially updated rep
|
|
3518
3494
|
if (!currentRep || !currentRep.lines) {
|
|
3519
3495
|
console.error(`[ep_data_tables] ${funcName}: Could not get updated rep after text insertion. Cannot apply attributes reliably.`);
|
|
3520
|
-
|
|
3496
|
+
// log(`${funcName}: END - Error getting updated rep`);
|
|
3521
3497
|
// Maybe attempt to continue without rep? Risky.
|
|
3522
3498
|
return;
|
|
3523
3499
|
}
|
|
3524
|
-
|
|
3500
|
+
// log(`${funcName}: Fetched updated rep for attribute application.`);
|
|
3525
3501
|
|
|
3526
3502
|
for (let r = 0; r < rows; r++) {
|
|
3527
3503
|
const lineNumToApply = initialStartLine + r;
|
|
3528
|
-
|
|
3504
|
+
// log(`${funcName}: -> Processing row ${r} on line ${lineNumToApply}`);
|
|
3529
3505
|
|
|
3530
3506
|
const lineEntry = currentRep.lines.atIndex(lineNumToApply);
|
|
3531
3507
|
if (!lineEntry) {
|
|
3532
|
-
|
|
3508
|
+
// log(`${funcName}: Could not find line entry for ${lineNumToApply}, skipping attribute application.`);
|
|
3533
3509
|
continue;
|
|
3534
3510
|
}
|
|
3535
3511
|
const lineText = lineEntry.text || '';
|
|
@@ -3542,7 +3518,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3542
3518
|
if (cellContent.length > 0) { // Only apply to non-empty cells
|
|
3543
3519
|
const cellStart = [lineNumToApply, offset];
|
|
3544
3520
|
const cellEnd = [lineNumToApply, offset + cellContent.length];
|
|
3545
|
-
|
|
3521
|
+
// log(`${funcName}: Applying ${ATTR_CELL} attribute to Line ${lineNumToApply} Col ${c} Range ${offset}-${offset + cellContent.length}`);
|
|
3546
3522
|
ed.ace_performDocumentApplyAttributesToRange(cellStart, cellEnd, [[ATTR_CELL, String(c)]]);
|
|
3547
3523
|
}
|
|
3548
3524
|
offset += cellContent.length;
|
|
@@ -3555,30 +3531,30 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3555
3531
|
// Note: documentAttributeManager not available in this context for new table creation
|
|
3556
3532
|
applyTableLineMetadataAttribute(lineNumToApply, tblId, r, cols, currentRep, ed, null, null);
|
|
3557
3533
|
}
|
|
3558
|
-
|
|
3559
|
-
|
|
3534
|
+
// log(`${funcName}: Finished applying metadata attributes.`);
|
|
3535
|
+
// log(`${funcName}: Requesting attribute sync (ace_fastIncorp 20)...`);
|
|
3560
3536
|
ed.ace_fastIncorp(20); // Final sync after attributes
|
|
3561
|
-
|
|
3537
|
+
// log(`${funcName}: Attribute sync requested.`);
|
|
3562
3538
|
|
|
3563
3539
|
// --- Phase 4: Set Caret Position ---
|
|
3564
|
-
|
|
3540
|
+
// log(`${funcName}: Phase 4 - Setting final caret position...`);
|
|
3565
3541
|
const finalCaretLine = initialStartLine + rows; // Line number after the last inserted row
|
|
3566
3542
|
const finalCaretPos = [finalCaretLine, 0];
|
|
3567
|
-
|
|
3543
|
+
// log(`${funcName}: Target caret position:`, finalCaretPos);
|
|
3568
3544
|
try {
|
|
3569
3545
|
ed.ace_performSelectionChange(finalCaretPos, finalCaretPos, false);
|
|
3570
|
-
|
|
3546
|
+
// log(`${funcName}: Successfully set caret position.`);
|
|
3571
3547
|
} catch(e) {
|
|
3572
3548
|
console.error(`[ep_data_tables] ${funcName}: Error setting caret position after table creation:`, e);
|
|
3573
|
-
|
|
3549
|
+
// log(`[ep_data_tables] ${funcName}: Error details:`, { message: e.message, stack: e.stack });
|
|
3574
3550
|
}
|
|
3575
3551
|
|
|
3576
|
-
|
|
3552
|
+
// log(`${funcName}: END - Refactored Phase 4`);
|
|
3577
3553
|
};
|
|
3578
3554
|
|
|
3579
3555
|
ed.ace_doDatatableOptions = (action) => {
|
|
3580
3556
|
const funcName = 'ace_doDatatableOptions';
|
|
3581
|
-
|
|
3557
|
+
// log(`${funcName}: START - Processing action: ${action}`);
|
|
3582
3558
|
|
|
3583
3559
|
// Get the last clicked cell info to determine which table to operate on
|
|
3584
3560
|
const editor = ed.ep_data_tables_editor;
|
|
@@ -3589,12 +3565,12 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3589
3565
|
|
|
3590
3566
|
const lastClick = editor.ep_data_tables_last_clicked;
|
|
3591
3567
|
if (!lastClick || !lastClick.tblId) {
|
|
3592
|
-
|
|
3568
|
+
// log(`${funcName}: No table selected. Please click on a table cell first.`);
|
|
3593
3569
|
console.warn('[ep_data_tables] No table selected. Please click on a table cell first.');
|
|
3594
3570
|
return;
|
|
3595
3571
|
}
|
|
3596
3572
|
|
|
3597
|
-
|
|
3573
|
+
// log(`${funcName}: Operating on table ${lastClick.tblId}, clicked line ${lastClick.lineNum}, cell ${lastClick.cellIndex}`);
|
|
3598
3574
|
|
|
3599
3575
|
try {
|
|
3600
3576
|
// Get current representation and document manager
|
|
@@ -3611,7 +3587,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3611
3587
|
return;
|
|
3612
3588
|
}
|
|
3613
3589
|
|
|
3614
|
-
|
|
3590
|
+
// log(`${funcName}: Successfully obtained documentAttributeManager from stored reference.`);
|
|
3615
3591
|
|
|
3616
3592
|
// Find all lines that belong to this table
|
|
3617
3593
|
const tableLines = [];
|
|
@@ -3640,7 +3616,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3640
3616
|
cols: domCells.length
|
|
3641
3617
|
};
|
|
3642
3618
|
lineAttrString = JSON.stringify(reconstructedMetadata);
|
|
3643
|
-
|
|
3619
|
+
// log(`${funcName}: Reconstructed metadata from DOM for line ${lineIndex}: ${lineAttrString}`);
|
|
3644
3620
|
}
|
|
3645
3621
|
}
|
|
3646
3622
|
}
|
|
@@ -3668,13 +3644,13 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3668
3644
|
}
|
|
3669
3645
|
|
|
3670
3646
|
if (tableLines.length === 0) {
|
|
3671
|
-
|
|
3647
|
+
// log(`${funcName}: No table lines found for table ${lastClick.tblId}`);
|
|
3672
3648
|
return;
|
|
3673
3649
|
}
|
|
3674
3650
|
|
|
3675
3651
|
// Sort by row number to ensure correct order
|
|
3676
3652
|
tableLines.sort((a, b) => a.row - b.row);
|
|
3677
|
-
|
|
3653
|
+
// log(`${funcName}: Found ${tableLines.length} table lines`);
|
|
3678
3654
|
|
|
3679
3655
|
// Determine table dimensions and target indices with robust matching
|
|
3680
3656
|
const numRows = tableLines.length;
|
|
@@ -3688,7 +3664,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3688
3664
|
|
|
3689
3665
|
// If that fails, try to match by finding the row that contains the clicked table
|
|
3690
3666
|
if (targetRowIndex === -1) {
|
|
3691
|
-
|
|
3667
|
+
// log(`${funcName}: Direct line number match failed, searching by DOM structure...`);
|
|
3692
3668
|
const clickedLineEntry = currentRep.lines.atIndex(lastClick.lineNum);
|
|
3693
3669
|
if (clickedLineEntry && clickedLineEntry.lineNode) {
|
|
3694
3670
|
const clickedTable = clickedLineEntry.lineNode.querySelector('table.dataTable[data-tblId="' + lastClick.tblId + '"]');
|
|
@@ -3697,7 +3673,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3697
3673
|
if (clickedRowAttr !== null) {
|
|
3698
3674
|
const clickedRowNum = parseInt(clickedRowAttr, 10);
|
|
3699
3675
|
targetRowIndex = tableLines.findIndex(line => line.row === clickedRowNum);
|
|
3700
|
-
|
|
3676
|
+
// log(`${funcName}: Found target row by DOM attribute matching: row ${clickedRowNum}, index ${targetRowIndex}`);
|
|
3701
3677
|
}
|
|
3702
3678
|
}
|
|
3703
3679
|
}
|
|
@@ -3705,13 +3681,13 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3705
3681
|
|
|
3706
3682
|
// If still not found, default to first row but log the issue
|
|
3707
3683
|
if (targetRowIndex === -1) {
|
|
3708
|
-
|
|
3684
|
+
// log(`${funcName}: Warning: Could not find target row, defaulting to row 0`);
|
|
3709
3685
|
targetRowIndex = 0;
|
|
3710
3686
|
}
|
|
3711
3687
|
|
|
3712
3688
|
const targetColIndex = lastClick.cellIndex || 0;
|
|
3713
3689
|
|
|
3714
|
-
|
|
3690
|
+
// log(`${funcName}: Table dimensions: ${numRows} rows x ${numCols} cols. Target: row ${targetRowIndex}, col ${targetColIndex}`);
|
|
3715
3691
|
|
|
3716
3692
|
// Perform table operations with both text and metadata updates
|
|
3717
3693
|
let newNumCols = numCols;
|
|
@@ -3719,23 +3695,23 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3719
3695
|
|
|
3720
3696
|
switch (action) {
|
|
3721
3697
|
case 'addTblRowA': // Insert row above
|
|
3722
|
-
|
|
3698
|
+
// log(`${funcName}: Inserting row above row ${targetRowIndex}`);
|
|
3723
3699
|
success = addTableRowAboveWithText(tableLines, targetRowIndex, numCols, lastClick.tblId, ed, docManager);
|
|
3724
3700
|
break;
|
|
3725
3701
|
|
|
3726
3702
|
case 'addTblRowB': // Insert row below
|
|
3727
|
-
|
|
3703
|
+
// log(`${funcName}: Inserting row below row ${targetRowIndex}`);
|
|
3728
3704
|
success = addTableRowBelowWithText(tableLines, targetRowIndex, numCols, lastClick.tblId, ed, docManager);
|
|
3729
3705
|
break;
|
|
3730
3706
|
|
|
3731
3707
|
case 'addTblColL': // Insert column left
|
|
3732
|
-
|
|
3708
|
+
// log(`${funcName}: Inserting column left of column ${targetColIndex}`);
|
|
3733
3709
|
newNumCols = numCols + 1;
|
|
3734
3710
|
success = addTableColumnLeftWithText(tableLines, targetColIndex, ed, docManager);
|
|
3735
3711
|
break;
|
|
3736
3712
|
|
|
3737
3713
|
case 'addTblColR': // Insert column right
|
|
3738
|
-
|
|
3714
|
+
// log(`${funcName}: Inserting column right of column ${targetColIndex}`);
|
|
3739
3715
|
newNumCols = numCols + 1;
|
|
3740
3716
|
success = addTableColumnRightWithText(tableLines, targetColIndex, ed, docManager);
|
|
3741
3717
|
break;
|
|
@@ -3744,10 +3720,10 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3744
3720
|
// Show confirmation prompt for row deletion
|
|
3745
3721
|
const rowConfirmMessage = `Are you sure you want to delete Row ${targetRowIndex + 1} and all content within?`;
|
|
3746
3722
|
if (!confirm(rowConfirmMessage)) {
|
|
3747
|
-
|
|
3723
|
+
// log(`${funcName}: Row deletion cancelled by user`);
|
|
3748
3724
|
return;
|
|
3749
3725
|
}
|
|
3750
|
-
|
|
3726
|
+
// log(`${funcName}: Deleting row ${targetRowIndex}`);
|
|
3751
3727
|
success = deleteTableRowWithText(tableLines, targetRowIndex, ed, docManager);
|
|
3752
3728
|
break;
|
|
3753
3729
|
|
|
@@ -3755,16 +3731,16 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3755
3731
|
// Show confirmation prompt for column deletion
|
|
3756
3732
|
const colConfirmMessage = `Are you sure you want to delete Column ${targetColIndex + 1} and all content within?`;
|
|
3757
3733
|
if (!confirm(colConfirmMessage)) {
|
|
3758
|
-
|
|
3734
|
+
// log(`${funcName}: Column deletion cancelled by user`);
|
|
3759
3735
|
return;
|
|
3760
3736
|
}
|
|
3761
|
-
|
|
3737
|
+
// log(`${funcName}: Deleting column ${targetColIndex}`);
|
|
3762
3738
|
newNumCols = numCols - 1;
|
|
3763
3739
|
success = deleteTableColumnWithText(tableLines, targetColIndex, ed, docManager);
|
|
3764
3740
|
break;
|
|
3765
3741
|
|
|
3766
3742
|
default:
|
|
3767
|
-
|
|
3743
|
+
// log(`${funcName}: Unknown action: ${action}`);
|
|
3768
3744
|
return;
|
|
3769
3745
|
}
|
|
3770
3746
|
|
|
@@ -3773,11 +3749,11 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3773
3749
|
return;
|
|
3774
3750
|
}
|
|
3775
3751
|
|
|
3776
|
-
|
|
3752
|
+
// log(`${funcName}: Table operation completed successfully with text and metadata synchronization`);
|
|
3777
3753
|
|
|
3778
3754
|
} catch (error) {
|
|
3779
3755
|
console.error(`[ep_data_tables] ${funcName}: Error during table operation:`, error);
|
|
3780
|
-
|
|
3756
|
+
// log(`${funcName}: Error details:`, { message: error.message, stack: error.stack });
|
|
3781
3757
|
}
|
|
3782
3758
|
};
|
|
3783
3759
|
|
|
@@ -3830,7 +3806,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3830
3806
|
columnWidths.push(100 / numCols);
|
|
3831
3807
|
}
|
|
3832
3808
|
});
|
|
3833
|
-
|
|
3809
|
+
// log('[ep_data_tables] addTableRowAbove: Extracted column widths from DOM:', columnWidths);
|
|
3834
3810
|
}
|
|
3835
3811
|
}
|
|
3836
3812
|
}
|
|
@@ -3908,7 +3884,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3908
3884
|
columnWidths.push(100 / numCols);
|
|
3909
3885
|
}
|
|
3910
3886
|
});
|
|
3911
|
-
|
|
3887
|
+
// log('[ep_data_tables] addTableRowBelow: Extracted column widths from DOM:', columnWidths);
|
|
3912
3888
|
}
|
|
3913
3889
|
}
|
|
3914
3890
|
}
|
|
@@ -3973,7 +3949,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3973
3949
|
if (cellContent.length > 0) { // Only apply to non-empty cells
|
|
3974
3950
|
const cellStart = [tableLine.lineIndex, offset];
|
|
3975
3951
|
const cellEnd = [tableLine.lineIndex, offset + cellContent.length];
|
|
3976
|
-
|
|
3952
|
+
// log(`[ep_data_tables] ${funcName}: Applying ${ATTR_CELL} attribute to Line ${tableLine.lineIndex} Col ${c} Range ${offset}-${offset + cellContent.length}`);
|
|
3977
3953
|
editorInfo.ace_performDocumentApplyAttributesToRange(cellStart, cellEnd, [[ATTR_CELL, String(c)]]);
|
|
3978
3954
|
}
|
|
3979
3955
|
offset += cellContent.length;
|
|
@@ -3988,7 +3964,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3988
3964
|
const newColCount = tableLine.cols + 1;
|
|
3989
3965
|
const equalWidth = 100 / newColCount;
|
|
3990
3966
|
const normalizedWidths = Array(newColCount).fill(equalWidth);
|
|
3991
|
-
|
|
3967
|
+
// log(`[ep_data_tables] addTableColumnLeft: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
|
|
3992
3968
|
|
|
3993
3969
|
// Apply updated metadata
|
|
3994
3970
|
const newMetadata = { ...tableLine.metadata, cols: tableLine.cols + 1, columnWidths: normalizedWidths };
|
|
@@ -4040,7 +4016,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4040
4016
|
if (cellContent.length > 0) { // Only apply to non-empty cells
|
|
4041
4017
|
const cellStart = [tableLine.lineIndex, offset];
|
|
4042
4018
|
const cellEnd = [tableLine.lineIndex, offset + cellContent.length];
|
|
4043
|
-
|
|
4019
|
+
// log(`[ep_data_tables] ${funcName}: Applying ${ATTR_CELL} attribute to Line ${tableLine.lineIndex} Col ${c} Range ${offset}-${offset + cellContent.length}`);
|
|
4044
4020
|
editorInfo.ace_performDocumentApplyAttributesToRange(cellStart, cellEnd, [[ATTR_CELL, String(c)]]);
|
|
4045
4021
|
}
|
|
4046
4022
|
offset += cellContent.length;
|
|
@@ -4055,7 +4031,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4055
4031
|
const newColCount = tableLine.cols + 1;
|
|
4056
4032
|
const equalWidth = 100 / newColCount;
|
|
4057
4033
|
const normalizedWidths = Array(newColCount).fill(equalWidth);
|
|
4058
|
-
|
|
4034
|
+
// log(`[ep_data_tables] addTableColumnRight: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
|
|
4059
4035
|
|
|
4060
4036
|
// Apply updated metadata
|
|
4061
4037
|
const newMetadata = { ...tableLine.metadata, cols: tableLine.cols + 1, columnWidths: normalizedWidths };
|
|
@@ -4078,7 +4054,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4078
4054
|
// Special handling for deleting the first row (row index 0)
|
|
4079
4055
|
// Insert a blank line to prevent the table from getting stuck at line 1
|
|
4080
4056
|
if (targetRowIndex === 0) {
|
|
4081
|
-
|
|
4057
|
+
// log('[ep_data_tables] Deleting first row (row 0) - inserting blank line to prevent table from getting stuck');
|
|
4082
4058
|
const insertStart = [targetLine.lineIndex, 0];
|
|
4083
4059
|
editorInfo.ace_performDocumentReplaceRange(insertStart, insertStart, '\n');
|
|
4084
4060
|
|
|
@@ -4118,7 +4094,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4118
4094
|
columnWidths.push(100 / targetLine.metadata.cols);
|
|
4119
4095
|
}
|
|
4120
4096
|
});
|
|
4121
|
-
|
|
4097
|
+
// log('[ep_data_tables] deleteTableRow: Extracted column widths from DOM:', columnWidths);
|
|
4122
4098
|
break;
|
|
4123
4099
|
}
|
|
4124
4100
|
}
|
|
@@ -4155,7 +4131,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4155
4131
|
const cells = lineText.split(DELIMITER);
|
|
4156
4132
|
|
|
4157
4133
|
if (targetColIndex >= cells.length) {
|
|
4158
|
-
|
|
4134
|
+
// log(`[ep_data_tables] Warning: Target column ${targetColIndex} doesn't exist in line with ${cells.length} columns`);
|
|
4159
4135
|
continue;
|
|
4160
4136
|
}
|
|
4161
4137
|
|
|
@@ -4180,7 +4156,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4180
4156
|
deleteStart -= DELIMITER.length;
|
|
4181
4157
|
}
|
|
4182
4158
|
|
|
4183
|
-
|
|
4159
|
+
// log(`[ep_data_tables] Deleting column ${targetColIndex} from line ${tableLine.lineIndex}: chars ${deleteStart}-${deleteEnd} from "${lineText}"`);
|
|
4184
4160
|
|
|
4185
4161
|
// Perform the precise deletion
|
|
4186
4162
|
const rangeStart = [tableLine.lineIndex, deleteStart];
|
|
@@ -4194,7 +4170,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4194
4170
|
if (newColCount > 0) {
|
|
4195
4171
|
const equalWidth = 100 / newColCount;
|
|
4196
4172
|
const normalizedWidths = Array(newColCount).fill(equalWidth);
|
|
4197
|
-
|
|
4173
|
+
// log(`[ep_data_tables] deleteTableColumn: Reset all column widths to equal distribution: ${newColCount} columns at ${equalWidth.toFixed(1)}% each`);
|
|
4198
4174
|
|
|
4199
4175
|
// Update metadata
|
|
4200
4176
|
const newMetadata = { ...tableLine.metadata, cols: newColCount, columnWidths: normalizedWidths };
|
|
@@ -4212,7 +4188,7 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
4212
4188
|
|
|
4213
4189
|
// ... existing code ...
|
|
4214
4190
|
|
|
4215
|
-
|
|
4191
|
+
// log('aceInitialized: END - helpers defined.');
|
|
4216
4192
|
};
|
|
4217
4193
|
|
|
4218
4194
|
// ───────────────────── required no‑op stubs ─────────────────────
|
|
@@ -4222,11 +4198,11 @@ exports.aceEndLineAndCharForPoint = () => { return undefined; };
|
|
|
4222
4198
|
// NEW: Style protection for table cells
|
|
4223
4199
|
exports.aceSetAuthorStyle = (hook, ctx) => {
|
|
4224
4200
|
const logPrefix = '[ep_data_tables:aceSetAuthorStyle]';
|
|
4225
|
-
|
|
4201
|
+
// log(`${logPrefix} START`, { hook, ctx });
|
|
4226
4202
|
|
|
4227
4203
|
// If no selection or no style to apply, allow default
|
|
4228
4204
|
if (!ctx || !ctx.rep || !ctx.rep.selStart || !ctx.rep.selEnd || !ctx.key) {
|
|
4229
|
-
|
|
4205
|
+
// log(`${logPrefix} No selection or style key. Allowing default.`);
|
|
4230
4206
|
return;
|
|
4231
4207
|
}
|
|
4232
4208
|
|
|
@@ -4236,14 +4212,14 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
|
|
|
4236
4212
|
|
|
4237
4213
|
// If selection spans multiple lines, prevent style application
|
|
4238
4214
|
if (startLine !== endLine) {
|
|
4239
|
-
|
|
4215
|
+
// log(`${logPrefix} Selection spans multiple lines. Preventing style application to protect table structure.`);
|
|
4240
4216
|
return false;
|
|
4241
4217
|
}
|
|
4242
4218
|
|
|
4243
4219
|
// Check if the line is a table line
|
|
4244
4220
|
const lineAttrString = ctx.documentAttributeManager?.getAttributeOnLine(startLine, ATTR_TABLE_JSON);
|
|
4245
4221
|
if (!lineAttrString) {
|
|
4246
|
-
|
|
4222
|
+
// log(`${logPrefix} Line ${startLine} is not a table line. Allowing default style application.`);
|
|
4247
4223
|
return;
|
|
4248
4224
|
}
|
|
4249
4225
|
|
|
@@ -4254,7 +4230,7 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
|
|
|
4254
4230
|
];
|
|
4255
4231
|
|
|
4256
4232
|
if (BLOCKED_STYLES.includes(ctx.key)) {
|
|
4257
|
-
|
|
4233
|
+
// log(`${logPrefix} Blocked potentially harmful style '${ctx.key}' from being applied to table cell.`);
|
|
4258
4234
|
return false;
|
|
4259
4235
|
}
|
|
4260
4236
|
|
|
@@ -4262,7 +4238,7 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
|
|
|
4262
4238
|
try {
|
|
4263
4239
|
const tableMetadata = JSON.parse(lineAttrString);
|
|
4264
4240
|
if (!tableMetadata || typeof tableMetadata.cols !== 'number') {
|
|
4265
|
-
|
|
4241
|
+
// log(`${logPrefix} Invalid table metadata. Preventing style application.`);
|
|
4266
4242
|
return false;
|
|
4267
4243
|
}
|
|
4268
4244
|
|
|
@@ -4288,7 +4264,7 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
|
|
|
4288
4264
|
|
|
4289
4265
|
// If selection spans multiple cells, prevent style application
|
|
4290
4266
|
if (selectionStartCell !== selectionEndCell) {
|
|
4291
|
-
|
|
4267
|
+
// log(`${logPrefix} Selection spans multiple cells. Preventing style application to protect table structure.`);
|
|
4292
4268
|
return false;
|
|
4293
4269
|
}
|
|
4294
4270
|
|
|
@@ -4297,15 +4273,15 @@ exports.aceSetAuthorStyle = (hook, ctx) => {
|
|
|
4297
4273
|
const cellEndCol = cellStartCol + cells[selectionStartCell].length;
|
|
4298
4274
|
|
|
4299
4275
|
if (ctx.rep.selStart[1] <= cellStartCol || ctx.rep.selEnd[1] >= cellEndCol) {
|
|
4300
|
-
|
|
4276
|
+
// log(`${logPrefix} Selection includes cell delimiters. Preventing style application to protect table structure.`);
|
|
4301
4277
|
return false;
|
|
4302
4278
|
}
|
|
4303
4279
|
|
|
4304
|
-
|
|
4280
|
+
// log(`${logPrefix} Style '${ctx.key}' allowed within cell boundaries.`);
|
|
4305
4281
|
return; // Allow the style to be applied
|
|
4306
4282
|
} catch (e) {
|
|
4307
4283
|
console.error(`${logPrefix} Error processing style application:`, e);
|
|
4308
|
-
|
|
4284
|
+
// log(`${logPrefix} Error details:`, { message: e.message, stack: e.stack });
|
|
4309
4285
|
return false; // Prevent style application on error
|
|
4310
4286
|
}
|
|
4311
4287
|
};
|
|
@@ -4322,7 +4298,7 @@ exports.aceRegisterBlockElements = () => ['table'];
|
|
|
4322
4298
|
// NEW: Column resize helper functions (adapted from images plugin)
|
|
4323
4299
|
const startColumnResize = (table, columnIndex, startX, metadata, lineNum) => {
|
|
4324
4300
|
const funcName = 'startColumnResize';
|
|
4325
|
-
|
|
4301
|
+
// log(`${funcName}: Starting resize for column ${columnIndex}`);
|
|
4326
4302
|
|
|
4327
4303
|
isResizing = true;
|
|
4328
4304
|
resizeStartX = startX;
|
|
@@ -4336,7 +4312,7 @@ const startColumnResize = (table, columnIndex, startX, metadata, lineNum) => {
|
|
|
4336
4312
|
const numCols = metadata.cols;
|
|
4337
4313
|
resizeOriginalWidths = metadata.columnWidths ? [...metadata.columnWidths] : Array(numCols).fill(100 / numCols);
|
|
4338
4314
|
|
|
4339
|
-
|
|
4315
|
+
// log(`${funcName}: Original widths:`, resizeOriginalWidths);
|
|
4340
4316
|
|
|
4341
4317
|
// Create visual overlay instead of modifying table directly
|
|
4342
4318
|
createResizeOverlay(table, columnIndex);
|
|
@@ -4402,7 +4378,7 @@ const createResizeOverlay = (table, columnIndex) => {
|
|
|
4402
4378
|
|
|
4403
4379
|
const totalTableHeight = maxBottom - minTop;
|
|
4404
4380
|
|
|
4405
|
-
|
|
4381
|
+
// log(`createResizeOverlay: Found ${allTableRows.length} table rows, total height: ${totalTableHeight}px`);
|
|
4406
4382
|
|
|
4407
4383
|
// Calculate positioning using the same method as image plugin
|
|
4408
4384
|
let innerBodyRect, innerIframeRect, outerBodyRect;
|
|
@@ -4489,7 +4465,7 @@ const createResizeOverlay = (table, columnIndex) => {
|
|
|
4489
4465
|
// Append to outer body like image plugin does with its outline
|
|
4490
4466
|
padOuter.append(resizeOverlay);
|
|
4491
4467
|
|
|
4492
|
-
|
|
4468
|
+
// log('createResizeOverlay: Created Google Docs style blue line overlay spanning entire table height');
|
|
4493
4469
|
};
|
|
4494
4470
|
|
|
4495
4471
|
const updateColumnResize = (currentX) => {
|
|
@@ -4549,19 +4525,19 @@ const updateColumnResize = (currentX) => {
|
|
|
4549
4525
|
|
|
4550
4526
|
const finishColumnResize = (editorInfo, docManager) => {
|
|
4551
4527
|
if (!isResizing || !resizeTargetTable) {
|
|
4552
|
-
|
|
4528
|
+
// log('finishColumnResize: Not in resize mode');
|
|
4553
4529
|
return;
|
|
4554
4530
|
}
|
|
4555
4531
|
|
|
4556
4532
|
const funcName = 'finishColumnResize';
|
|
4557
|
-
|
|
4533
|
+
// log(`${funcName}: Finishing resize`);
|
|
4558
4534
|
|
|
4559
4535
|
// Calculate final widths from actual mouse movement
|
|
4560
4536
|
const tableRect = resizeTargetTable.getBoundingClientRect();
|
|
4561
4537
|
const deltaX = resizeCurrentX - resizeStartX;
|
|
4562
4538
|
const deltaPercent = (deltaX / tableRect.width) * 100;
|
|
4563
4539
|
|
|
4564
|
-
|
|
4540
|
+
// log(`${funcName}: Mouse moved ${deltaX}px (${deltaPercent.toFixed(1)}%)`);
|
|
4565
4541
|
|
|
4566
4542
|
const finalWidths = [...resizeOriginalWidths];
|
|
4567
4543
|
const currentColumn = resizeTargetColumn;
|
|
@@ -4575,7 +4551,7 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4575
4551
|
finalWidths[currentColumn] += actualTransfer;
|
|
4576
4552
|
finalWidths[nextColumn] -= actualTransfer;
|
|
4577
4553
|
|
|
4578
|
-
|
|
4554
|
+
// log(`${funcName}: Transferred ${actualTransfer.toFixed(1)}% from column ${nextColumn} to column ${currentColumn}`);
|
|
4579
4555
|
}
|
|
4580
4556
|
|
|
4581
4557
|
// Normalize widths
|
|
@@ -4586,7 +4562,7 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4586
4562
|
});
|
|
4587
4563
|
}
|
|
4588
4564
|
|
|
4589
|
-
|
|
4565
|
+
// log(`${funcName}: Final normalized widths:`, finalWidths.map(w => w.toFixed(1) + '%'));
|
|
4590
4566
|
|
|
4591
4567
|
// Clean up overlay
|
|
4592
4568
|
if (resizeOverlay) {
|
|
@@ -4606,7 +4582,7 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4606
4582
|
// Apply updated metadata to ALL rows in the table (not just the resized row)
|
|
4607
4583
|
editorInfo.ace_callWithAce((ace) => {
|
|
4608
4584
|
const callWithAceLogPrefix = `${funcName}[ace_callWithAce]`;
|
|
4609
|
-
|
|
4585
|
+
// log(`${callWithAceLogPrefix}: Finding and updating all table rows with tblId: ${resizeTableMetadata.tblId}`);
|
|
4610
4586
|
|
|
4611
4587
|
try {
|
|
4612
4588
|
const rep = ace.ace_getRep();
|
|
@@ -4663,7 +4639,7 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4663
4639
|
cols: domCells.length,
|
|
4664
4640
|
columnWidths: columnWidths
|
|
4665
4641
|
};
|
|
4666
|
-
|
|
4642
|
+
// log(`${callWithAceLogPrefix}: Reconstructed metadata from DOM for line ${lineIndex}:`, reconstructedMetadata);
|
|
4667
4643
|
tableLines.push({
|
|
4668
4644
|
lineIndex,
|
|
4669
4645
|
metadata: reconstructedMetadata
|
|
@@ -4678,7 +4654,7 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4678
4654
|
}
|
|
4679
4655
|
}
|
|
4680
4656
|
|
|
4681
|
-
|
|
4657
|
+
// log(`${callWithAceLogPrefix}: Found ${tableLines.length} table lines to update`);
|
|
4682
4658
|
|
|
4683
4659
|
// Update all table lines with new column widths
|
|
4684
4660
|
for (const tableLine of tableLines) {
|
|
@@ -4696,7 +4672,7 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4696
4672
|
const rangeStart = [tableLine.lineIndex, 0];
|
|
4697
4673
|
const rangeEnd = [tableLine.lineIndex, lineLength];
|
|
4698
4674
|
|
|
4699
|
-
|
|
4675
|
+
// log(`${callWithAceLogPrefix}: Updating line ${tableLine.lineIndex} (row ${tableLine.metadata.row}) with new column widths`);
|
|
4700
4676
|
|
|
4701
4677
|
// Apply the updated metadata attribute directly
|
|
4702
4678
|
ace.ace_performDocumentApplyAttributesToRange(rangeStart, rangeEnd, [
|
|
@@ -4704,15 +4680,15 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4704
4680
|
]);
|
|
4705
4681
|
}
|
|
4706
4682
|
|
|
4707
|
-
|
|
4683
|
+
// log(`${callWithAceLogPrefix}: Successfully applied updated column widths to all ${tableLines.length} table rows`);
|
|
4708
4684
|
|
|
4709
4685
|
} catch (error) {
|
|
4710
4686
|
console.error(`${callWithAceLogPrefix}: Error applying updated metadata:`, error);
|
|
4711
|
-
|
|
4687
|
+
// log(`${callWithAceLogPrefix}: Error details:`, { message: error.message, stack: error.stack });
|
|
4712
4688
|
}
|
|
4713
4689
|
}, 'applyTableResizeToAllRows', true);
|
|
4714
4690
|
|
|
4715
|
-
|
|
4691
|
+
// log(`${funcName}: Column width update initiated for all table rows via ace_callWithAce`);
|
|
4716
4692
|
|
|
4717
4693
|
// Reset state
|
|
4718
4694
|
resizeStartX = 0;
|
|
@@ -4723,16 +4699,16 @@ const finishColumnResize = (editorInfo, docManager) => {
|
|
|
4723
4699
|
resizeTableMetadata = null;
|
|
4724
4700
|
resizeLineNum = -1;
|
|
4725
4701
|
|
|
4726
|
-
|
|
4702
|
+
// log(`${funcName}: Resize complete - state reset`);
|
|
4727
4703
|
};
|
|
4728
4704
|
|
|
4729
4705
|
// NEW: Undo/Redo protection
|
|
4730
4706
|
exports.aceUndoRedo = (hook, ctx) => {
|
|
4731
4707
|
const logPrefix = '[ep_data_tables:aceUndoRedo]';
|
|
4732
|
-
|
|
4708
|
+
// log(`${logPrefix} START`, { hook, ctx });
|
|
4733
4709
|
|
|
4734
4710
|
if (!ctx || !ctx.rep || !ctx.rep.selStart || !ctx.rep.selEnd) {
|
|
4735
|
-
|
|
4711
|
+
// log(`${logPrefix} No selection or context. Allowing default.`);
|
|
4736
4712
|
return;
|
|
4737
4713
|
}
|
|
4738
4714
|
|
|
@@ -4753,11 +4729,11 @@ exports.aceUndoRedo = (hook, ctx) => {
|
|
|
4753
4729
|
}
|
|
4754
4730
|
|
|
4755
4731
|
if (!hasTableLines) {
|
|
4756
|
-
|
|
4732
|
+
// log(`${logPrefix} No table lines affected. Allowing default undo/redo.`);
|
|
4757
4733
|
return;
|
|
4758
4734
|
}
|
|
4759
4735
|
|
|
4760
|
-
|
|
4736
|
+
// log(`${logPrefix} Table lines affected:`, { tableLines });
|
|
4761
4737
|
|
|
4762
4738
|
// Validate table structure after undo/redo
|
|
4763
4739
|
try {
|
|
@@ -4767,7 +4743,7 @@ exports.aceUndoRedo = (hook, ctx) => {
|
|
|
4767
4743
|
|
|
4768
4744
|
const tableMetadata = JSON.parse(lineAttrString);
|
|
4769
4745
|
if (!tableMetadata || typeof tableMetadata.cols !== 'number') {
|
|
4770
|
-
|
|
4746
|
+
// log(`${logPrefix} Invalid table metadata after undo/redo. Attempting recovery.`);
|
|
4771
4747
|
// Attempt to recover table structure
|
|
4772
4748
|
const lineText = ctx.rep.lines.atIndex(line)?.text || '';
|
|
4773
4749
|
const cells = lineText.split(DELIMITER);
|
|
@@ -4782,16 +4758,16 @@ exports.aceUndoRedo = (hook, ctx) => {
|
|
|
4782
4758
|
|
|
4783
4759
|
// Apply the recovered metadata
|
|
4784
4760
|
ctx.documentAttributeManager.setAttributeOnLine(line, ATTR_TABLE_JSON, JSON.stringify(newMetadata));
|
|
4785
|
-
|
|
4761
|
+
// log(`${logPrefix} Recovered table structure for line ${line}`);
|
|
4786
4762
|
} else {
|
|
4787
4763
|
// If we can't recover, remove the table attribute
|
|
4788
4764
|
ctx.documentAttributeManager.removeAttributeOnLine(line, ATTR_TABLE_JSON);
|
|
4789
|
-
|
|
4765
|
+
// log(`${logPrefix} Removed invalid table attribute from line ${line}`);
|
|
4790
4766
|
}
|
|
4791
4767
|
}
|
|
4792
4768
|
}
|
|
4793
4769
|
} catch (e) {
|
|
4794
4770
|
console.error(`${logPrefix} Error during undo/redo validation:`, e);
|
|
4795
|
-
|
|
4771
|
+
// log(`${logPrefix} Error details:`, { message: e.message, stack: e.stack });
|
|
4796
4772
|
}
|
|
4797
4773
|
};
|