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