ep_data_tables 0.0.9 → 0.0.96
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 +111 -64
package/package.json
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
const ATTR_TABLE_JSON = 'tbljson';
|
|
2
|
+
// Global guard: Chrome/macOS sometimes loads the same plugin script twice due to a
|
|
3
|
+
// subtle preload/import timing quirk, causing duplicate aceInitialized hooks that
|
|
4
|
+
// explode into large duplicate-row changesets. Bail early on re-entry.
|
|
5
|
+
if (typeof window !== 'undefined') {
|
|
6
|
+
if (window.__epDataTablesLoaded) {
|
|
7
|
+
console.debug('[ep_data_tables] Duplicate client_hooks.js load suppressed');
|
|
8
|
+
// eslint-disable-next-line no-useless-return
|
|
9
|
+
return; // Abort evaluation of the rest of the module
|
|
10
|
+
}
|
|
11
|
+
window.__epDataTablesLoaded = true;
|
|
12
|
+
}
|
|
2
13
|
const ATTR_CELL = 'td';
|
|
3
14
|
const ATTR_CLASS_PREFIX = 'tbljson-';
|
|
4
15
|
const log = (...m) => console.debug('[ep_data_tables:client_hooks]', ...m);
|
|
@@ -600,7 +611,7 @@ function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
|
|
|
600
611
|
if (!metadata || typeof metadata.tblId === 'undefined' || typeof metadata.row === 'undefined') {
|
|
601
612
|
console.error(`[ep_data_tables] ${funcName}: Invalid or missing metadata. Aborting.`);
|
|
602
613
|
// log(`${funcName}: END - Error`);
|
|
603
|
-
return '<table class="dataTable dataTable-error"><tbody><tr><td>Error: Missing table metadata</td></tr></tbody></table>';
|
|
614
|
+
return '<table class="dataTable dataTable-error" writingsuggestions="false" autocorrect="off" autocapitalize="off" spellcheck="false"><tbody><tr><td>Error: Missing table metadata</td></tr></tbody></table>';
|
|
604
615
|
}
|
|
605
616
|
|
|
606
617
|
const numCols = innerHTMLSegments.length;
|
|
@@ -623,7 +634,8 @@ function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
|
|
|
623
634
|
const cellsHtml = innerHTMLSegments.map((segment, index) => {
|
|
624
635
|
const textOnly = (segment || '').replace(/<[^>]*>/g, '').replace(/ /ig, ' ').trim();
|
|
625
636
|
let modifiedSegment = segment || '';
|
|
626
|
-
const
|
|
637
|
+
const containsImage = /\bimage-placeholder\b/.test(modifiedSegment);
|
|
638
|
+
const isEmpty = (!segment || textOnly === '') && !containsImage;
|
|
627
639
|
if (isEmpty) {
|
|
628
640
|
const cellClass = encodedTbljsonClass ? `${encodedTbljsonClass} tblCell-${index}` : `tblCell-${index}`;
|
|
629
641
|
modifiedSegment = `<span class="${cellClass}"> </span>`;
|
|
@@ -679,7 +691,7 @@ function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
|
|
|
679
691
|
const resizeHandle = !isLastColumn ?
|
|
680
692
|
`<div class="ep-data_tables-resize-handle" data-column="${index}" style="position: absolute; top: 0; right: -2px; width: 4px; height: 100%; cursor: col-resize; background: transparent; z-index: 10;"></div>` : '';
|
|
681
693
|
|
|
682
|
-
const tdContent = `<td style="${cellStyle}" data-column="${index}" draggable="false">${modifiedSegment}${resizeHandle}</td>`;
|
|
694
|
+
const tdContent = `<td style="${cellStyle}" data-column="${index}" draggable="false" autocorrect="off" autocapitalize="off" spellcheck="false">${modifiedSegment}${resizeHandle}</td>`;
|
|
683
695
|
return tdContent;
|
|
684
696
|
}).join('');
|
|
685
697
|
// log(`${funcName}: Joined all cellsHtml:`, cellsHtml);
|
|
@@ -687,7 +699,7 @@ function buildTableFromDelimitedHTML(metadata, innerHTMLSegments) {
|
|
|
687
699
|
const firstRowClass = metadata.row === 0 ? ' dataTable-first-row' : '';
|
|
688
700
|
// log(`${funcName}: First row class applied: '${firstRowClass}'`);
|
|
689
701
|
|
|
690
|
-
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>`;
|
|
702
|
+
const tableHtml = `<table class="dataTable${firstRowClass}" writingsuggestions="false" autocorrect="off" autocapitalize="off" spellcheck="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>`;
|
|
691
703
|
// log(`${funcName}: Generated final table HTML:`, tableHtml);
|
|
692
704
|
// log(`${funcName}: END - Success`);
|
|
693
705
|
return tableHtml;
|
|
@@ -862,8 +874,8 @@ exports.acePostWriteDomLineHTML = function (hook_name, args, cb) {
|
|
|
862
874
|
.replace(/<span class="ep-data_tables-caret-anchor"[^>]*><\/span>/ig, '')
|
|
863
875
|
.replace(/\r?\n/g, ' ')
|
|
864
876
|
.replace(/<br\s*\/?>/gi, ' ')
|
|
865
|
-
|
|
866
|
-
|
|
877
|
+
// .replace(/\u00A0/gu, ' ')
|
|
878
|
+
// .replace(/[\u200B\u200C\u200D\uFEFF]/g, '')
|
|
867
879
|
.replace(/\s+/g, ' ');
|
|
868
880
|
const htmlSegments = sanitizedHTMLForSplit.split(DELIMITER);
|
|
869
881
|
|
|
@@ -1692,33 +1704,53 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
1692
1704
|
ed.ep_data_tables_editor = editor;
|
|
1693
1705
|
// log(`${logPrefix}: Stored editor reference as ed.ep_data_tables_editor.`);
|
|
1694
1706
|
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1707
|
+
// Retry logic for iframe access to handle timing/race conditions
|
|
1708
|
+
const tryGetIframeBody = (attempt = 0) => {
|
|
1709
|
+
if (attempt > 0) {
|
|
1710
|
+
console.log(`${callWithAceLogPrefix} Retry attempt ${attempt}/5 to access iframe body`);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1698
1713
|
const $iframeOuter = $('iframe[name="ace_outer"]');
|
|
1699
1714
|
if ($iframeOuter.length === 0) {
|
|
1700
|
-
|
|
1701
|
-
|
|
1715
|
+
if (attempt < 5) {
|
|
1716
|
+
setTimeout(() => tryGetIframeBody(attempt + 1), 100);
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
console.error(`${callWithAceLogPrefix} ERROR: Could not find outer iframe (ace_outer) after ${attempt} attempts.`);
|
|
1702
1720
|
return;
|
|
1703
1721
|
}
|
|
1704
|
-
// log(`${callWithAceLogPrefix} Found ace_outer:`, $iframeOuter);
|
|
1705
1722
|
|
|
1706
1723
|
const $iframeInner = $iframeOuter.contents().find('iframe[name="ace_inner"]');
|
|
1707
1724
|
if ($iframeInner.length === 0) {
|
|
1708
|
-
|
|
1709
|
-
|
|
1725
|
+
if (attempt < 5) {
|
|
1726
|
+
setTimeout(() => tryGetIframeBody(attempt + 1), 100);
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
console.error(`${callWithAceLogPrefix} ERROR: Could not find inner iframe (ace_inner) after ${attempt} attempts.`);
|
|
1710
1730
|
return;
|
|
1711
1731
|
}
|
|
1712
|
-
// log(`${callWithAceLogPrefix} Found ace_inner:`, $iframeInner);
|
|
1713
1732
|
|
|
1714
1733
|
const innerDocBody = $iframeInner.contents().find('body');
|
|
1715
1734
|
if (innerDocBody.length === 0) {
|
|
1716
|
-
|
|
1717
|
-
|
|
1735
|
+
if (attempt < 5) {
|
|
1736
|
+
setTimeout(() => tryGetIframeBody(attempt + 1), 100);
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
console.error(`${callWithAceLogPrefix} ERROR: Could not find body element in inner iframe after ${attempt} attempts.`);
|
|
1718
1740
|
return;
|
|
1719
1741
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1742
|
+
|
|
1743
|
+
const $inner = $(innerDocBody[0]);
|
|
1744
|
+
if (attempt > 0) {
|
|
1745
|
+
console.log(`${callWithAceLogPrefix} Successfully found iframe body on attempt ${attempt + 1}`);
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// SUCCESS - Now attach all listeners and set attributes
|
|
1749
|
+
attachListeners($inner, $iframeOuter, $iframeInner, innerDocBody);
|
|
1750
|
+
};
|
|
1751
|
+
|
|
1752
|
+
const attachListeners = ($inner, $iframeOuter, $iframeInner, innerDocBody) => {
|
|
1753
|
+
try {
|
|
1722
1754
|
|
|
1723
1755
|
const mobileSuggestionBlocker = (evt) => {
|
|
1724
1756
|
const t = evt && evt.inputType || '';
|
|
@@ -1899,37 +1931,38 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
1899
1931
|
};
|
|
1900
1932
|
|
|
1901
1933
|
// IME/autocorrect diagnostics: capture-phase logging and newline soft-normalization for table lines
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
}
|
|
1934
|
+
// COMMENTED OUT FOR PRODUCTION - Uncomment for debugging IME/composition issues
|
|
1935
|
+
// const logIMEEvent = (rawEvt, tag) => {
|
|
1936
|
+
// try {
|
|
1937
|
+
// const e = rawEvt && (rawEvt.originalEvent || rawEvt);
|
|
1938
|
+
// const rep = ed.ace_getRep && ed.ace_getRep();
|
|
1939
|
+
// const selStart = rep && rep.selStart;
|
|
1940
|
+
// const lineNum = selStart ? selStart[0] : -1;
|
|
1941
|
+
// let isTableLine = false;
|
|
1942
|
+
// if (lineNum >= 0) {
|
|
1943
|
+
// let s = docManager && docManager.getAttributeOnLine ? docManager.getAttributeOnLine(lineNum, ATTR_TABLE_JSON) : null;
|
|
1944
|
+
// if (!s) {
|
|
1945
|
+
// const meta = getTableLineMetadata(lineNum, ed, docManager);
|
|
1946
|
+
// isTableLine = !!meta && typeof meta.cols === 'number';
|
|
1947
|
+
// } else {
|
|
1948
|
+
// isTableLine = true;
|
|
1949
|
+
// }
|
|
1950
|
+
// }
|
|
1951
|
+
// if (!isTableLine) return;
|
|
1952
|
+
// const payload = {
|
|
1953
|
+
// tag,
|
|
1954
|
+
// type: e && e.type,
|
|
1955
|
+
// inputType: e && e.inputType,
|
|
1956
|
+
// data: typeof (e && e.data) === 'string' ? e.data : null,
|
|
1957
|
+
// isComposing: !!(e && e.isComposing),
|
|
1958
|
+
// key: e && e.key,
|
|
1959
|
+
// code: e && e.code,
|
|
1960
|
+
// which: e && e.which,
|
|
1961
|
+
// keyCode: e && e.keyCode,
|
|
1962
|
+
// };
|
|
1963
|
+
// console.debug('[ep_data_tables:ime-diag]', payload);
|
|
1964
|
+
// } catch (_) {}
|
|
1965
|
+
// };
|
|
1933
1966
|
|
|
1934
1967
|
const softBreakNormalizer = (rawEvt) => {
|
|
1935
1968
|
try {
|
|
@@ -1937,6 +1970,15 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
1937
1970
|
if (!e || e._epDataTablesNormalized) return;
|
|
1938
1971
|
const t = e.inputType || '';
|
|
1939
1972
|
const dataStr = typeof e.data === 'string' ? e.data : '';
|
|
1973
|
+
|
|
1974
|
+
// If this NBSP is flanked by ZWSPs we are inside an image placeholder.
|
|
1975
|
+
// In that case leave it untouched so caret math stays correct.
|
|
1976
|
+
if (dataStr === '\u00A0' && rep && rep.selStart) {
|
|
1977
|
+
const lineText = rep.lines.atIndex(rep.selStart[0])?.text || '';
|
|
1978
|
+
const pos = rep.selStart[1]; // caret is before the NBSP
|
|
1979
|
+
if (lineText.slice(pos - 1, pos + 2) === '\u200B\u00A0\u200B') return;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1940
1982
|
const hasSoftWs = /[\r\n\u00A0]/.test(dataStr); // include NBSP (U+00A0)
|
|
1941
1983
|
const isSoftBreak = t === 'insertParagraph' || t === 'insertLineBreak' || hasSoftWs;
|
|
1942
1984
|
if (!isSoftBreak) return;
|
|
@@ -1970,9 +2012,10 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
1970
2012
|
|
|
1971
2013
|
if ($inner && $inner.length > 0 && $inner[0].addEventListener) {
|
|
1972
2014
|
const el = $inner[0];
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
2015
|
+
// COMMENTED OUT FOR PRODUCTION - Uncomment for debugging IME/composition issues
|
|
2016
|
+
// ['beforeinput','input','textInput','compositionstart','compositionupdate','compositionend','keydown','keyup'].forEach((t) => {
|
|
2017
|
+
// el.addEventListener(t, (ev) => logIMEEvent(ev, 'capture'), true);
|
|
2018
|
+
// });
|
|
1976
2019
|
el.addEventListener('beforeinput', softBreakNormalizer, true);
|
|
1977
2020
|
}
|
|
1978
2021
|
|
|
@@ -2002,20 +2045,16 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
2002
2045
|
el.setAttribute('autocomplete', 'off');
|
|
2003
2046
|
el.setAttribute('autocapitalize', 'off');
|
|
2004
2047
|
el.setAttribute('spellcheck', 'false');
|
|
2048
|
+
el.setAttribute('data-gramm', 'false');
|
|
2049
|
+
el.setAttribute('data-enable-grammarly', 'false');
|
|
2005
2050
|
};
|
|
2006
2051
|
disableAuto(innerDocBody[0] || innerDocBody);
|
|
2007
2052
|
} catch (_) {}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
if (!$inner || $inner.length === 0) {
|
|
2015
|
-
console.error(`${callWithAceLogPrefix} ERROR: $inner is not valid after attempting to find iframe body. Cannot attach listeners.`);
|
|
2016
|
-
// log(`${callWithAceLogPrefix} $inner is invalid. Aborting.`);
|
|
2017
|
-
return;
|
|
2018
|
-
}
|
|
2053
|
+
|
|
2054
|
+
if (!$inner || $inner.length === 0) {
|
|
2055
|
+
console.error(`${callWithAceLogPrefix} ERROR: $inner is not valid. Cannot attach listeners.`);
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2019
2058
|
|
|
2020
2059
|
// log(`${callWithAceLogPrefix} Attaching cut event listener to $inner (inner iframe body).`);
|
|
2021
2060
|
$inner.on('cut', (evt) => {
|
|
@@ -3618,6 +3657,13 @@ exports.aceInitialized = (h, ctx) => {
|
|
|
3618
3657
|
setupGlobalHandlers();
|
|
3619
3658
|
|
|
3620
3659
|
// log(`${callWithAceLogPrefix} Column resize listeners attached successfully.`);
|
|
3660
|
+
} catch (e) {
|
|
3661
|
+
console.error(`${callWithAceLogPrefix} ERROR: Exception while attaching listeners:`, e);
|
|
3662
|
+
}
|
|
3663
|
+
}; // End of attachListeners function
|
|
3664
|
+
|
|
3665
|
+
// Start the retry process to access iframes and attach all listeners
|
|
3666
|
+
tryGetIframeBody(0);
|
|
3621
3667
|
|
|
3622
3668
|
}, 'tablePasteAndResizeListeners', true);
|
|
3623
3669
|
// log(`${logPrefix} ace_callWithAce for listeners setup completed.`);
|
|
@@ -4890,3 +4936,4 @@ exports.aceUndoRedo = (hook, ctx) => {
|
|
|
4890
4936
|
// log(`${logPrefix} Error details:`, { message: e.message, stack: e.stack });
|
|
4891
4937
|
}
|
|
4892
4938
|
};
|
|
4939
|
+
|