autumnnote 1.8.0 → 1.8.2
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/README.md +17 -5
- package/dist/autumnnote.css +18 -1
- package/dist/autumnnote.es.js +350 -120
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +350 -120
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/js/core/markdown.js +32 -6
- package/src/js/editing/Style.js +288 -159
- package/src/js/editing/Table.js +10 -2
- package/src/js/editing/Typing.js +21 -4
- package/src/js/i18n/de.js +10 -0
- package/src/js/i18n/es.js +10 -0
- package/src/js/i18n/fr.js +10 -0
- package/src/js/i18n/ja.js +10 -0
- package/src/js/i18n/ko.js +10 -0
- package/src/js/i18n/vi.js +8 -0
- package/src/js/i18n/zh.js +10 -0
- package/src/js/index.js +1 -1
- package/src/js/module/Clipboard.js +1 -1
- package/src/js/module/ImageDialog.js +1 -0
- package/src/js/module/Mention.js +10 -1
- package/src/js/module/TableTooltip.js +21 -0
- package/src/styles/autumnnote.scss +20 -3
- package/types/index.d.ts +3 -3
package/dist/autumnnote.es.js
CHANGED
|
@@ -807,13 +807,74 @@ function _checklistItemToP(checkLi) {
|
|
|
807
807
|
} catch {}
|
|
808
808
|
}
|
|
809
809
|
/**
|
|
810
|
-
* Inserts an unordered list or converts
|
|
810
|
+
* Inserts an unordered (bulleted) list, or converts the current list to `<ul>`.
|
|
811
|
+
*
|
|
812
|
+
* When the cursor is already inside a list, direct DOM manipulation is used to
|
|
813
|
+
* transition between list types — `execCommand` alone cannot handle checklist →
|
|
814
|
+
* UL/OL conversions because it has no awareness of the `an-checklist` class or
|
|
815
|
+
* the checkbox `<input>` elements.
|
|
816
|
+
*
|
|
817
|
+
* Transition paths:
|
|
818
|
+
* - **Checklist → UL**: strips `an-checklist` class and all checkbox inputs;
|
|
819
|
+
* converts `<ol>` container to `<ul>` via `changeTagName()` if needed.
|
|
820
|
+
* - **OL → UL**: swaps the container tag via `changeTagName()`.
|
|
821
|
+
* - **UL → paragraphs**: falls back to `execCommand('insertUnorderedList')`
|
|
822
|
+
* which toggles the list off (browser-native behaviour).
|
|
823
|
+
* - **No list → UL**: falls back to `execCommand('insertUnorderedList')`.
|
|
811
824
|
*/
|
|
812
|
-
var insertUnorderedList = () => execCommand("insertUnorderedList");
|
|
813
825
|
/**
|
|
814
|
-
*
|
|
826
|
+
* Helper to get the closest ul/ol element containing the current selection.
|
|
827
|
+
* @returns {Element|null}
|
|
815
828
|
*/
|
|
816
|
-
|
|
829
|
+
function getSelectedList() {
|
|
830
|
+
const sel = globalThis.getSelection();
|
|
831
|
+
if (!sel?.rangeCount) return null;
|
|
832
|
+
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
833
|
+
if (container.nodeType === 3) container = container.parentElement;
|
|
834
|
+
return container?.closest("ul, ol") || null;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Strips the checklist class and checkbox inputs from a list element.
|
|
838
|
+
* @param {Element} listEl
|
|
839
|
+
*/
|
|
840
|
+
function stripChecklist(listEl) {
|
|
841
|
+
listEl.classList.remove("an-checklist");
|
|
842
|
+
listEl.querySelectorAll("input[type=\"checkbox\"]").forEach((cb) => cb.remove());
|
|
843
|
+
}
|
|
844
|
+
function insertUnorderedList() {
|
|
845
|
+
const listEl = getSelectedList();
|
|
846
|
+
if (listEl) if (listEl.classList.contains("an-checklist")) {
|
|
847
|
+
stripChecklist(listEl);
|
|
848
|
+
if (listEl.tagName === "OL") changeTagName(listEl, "ul");
|
|
849
|
+
} else if (listEl.tagName === "OL") changeTagName(listEl, "ul");
|
|
850
|
+
else execCommand("insertUnorderedList");
|
|
851
|
+
else execCommand("insertUnorderedList");
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Inserts an ordered (numbered) list, or converts the current list to `<ol>`.
|
|
855
|
+
*
|
|
856
|
+
* When the cursor is already inside a list, direct DOM manipulation is used to
|
|
857
|
+
* transition between list types — `execCommand` alone cannot handle checklist →
|
|
858
|
+
* UL/OL conversions because it has no awareness of the `an-checklist` class or
|
|
859
|
+
* the checkbox `<input>` elements.
|
|
860
|
+
*
|
|
861
|
+
* Transition paths:
|
|
862
|
+
* - **Checklist → OL**: strips `an-checklist` class and all checkbox inputs;
|
|
863
|
+
* converts container to `<ol>` via `changeTagName()`.
|
|
864
|
+
* - **UL → OL**: swaps the container tag via `changeTagName()`.
|
|
865
|
+
* - **OL → paragraphs**: falls back to `execCommand('insertOrderedList')`
|
|
866
|
+
* which toggles the list off (browser-native behaviour).
|
|
867
|
+
* - **No list → OL**: falls back to `execCommand('insertOrderedList')`.
|
|
868
|
+
*/
|
|
869
|
+
function insertOrderedList() {
|
|
870
|
+
const listEl = getSelectedList();
|
|
871
|
+
if (listEl) if (listEl.classList.contains("an-checklist")) {
|
|
872
|
+
stripChecklist(listEl);
|
|
873
|
+
changeTagName(listEl, "ol");
|
|
874
|
+
} else if (listEl.tagName === "UL") changeTagName(listEl, "ol");
|
|
875
|
+
else execCommand("insertOrderedList");
|
|
876
|
+
else execCommand("insertOrderedList");
|
|
877
|
+
}
|
|
817
878
|
/**
|
|
818
879
|
* Set the line-height on every block-level element that intersects the current selection.
|
|
819
880
|
*
|
|
@@ -939,18 +1000,34 @@ function isInlineCode() {
|
|
|
939
1000
|
return !!(code && !code.closest("pre"));
|
|
940
1001
|
}
|
|
941
1002
|
/**
|
|
1003
|
+
* Changes the tag name of an element in the DOM while preserving attributes and children.
|
|
1004
|
+
* @param {Element} el
|
|
1005
|
+
* @param {string} newTagName
|
|
1006
|
+
* @returns {HTMLElement}
|
|
1007
|
+
*/
|
|
1008
|
+
function changeTagName(el, newTagName) {
|
|
1009
|
+
const newEl = document.createElement(newTagName);
|
|
1010
|
+
for (const attr of el.attributes) newEl.setAttribute(attr.name, attr.value);
|
|
1011
|
+
while (el.firstChild) newEl.appendChild(el.firstChild);
|
|
1012
|
+
el.parentNode.replaceChild(newEl, el);
|
|
1013
|
+
return newEl;
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Ensures all list items under the list element have a checkbox.
|
|
1017
|
+
* @param {Element} listEl
|
|
1018
|
+
*/
|
|
1019
|
+
function ensureCheckboxes(listEl) {
|
|
1020
|
+
listEl.querySelectorAll("li").forEach((li) => {
|
|
1021
|
+
if (!li.querySelector("input[type=\"checkbox\"]")) {
|
|
1022
|
+
const cb = document.createElement("input");
|
|
1023
|
+
cb.type = "checkbox";
|
|
1024
|
+
cb.contentEditable = "false";
|
|
1025
|
+
li.insertBefore(cb, li.firstChild);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
942
1030
|
* Toggle a checklist at the current selection or caret.
|
|
943
|
-
*
|
|
944
|
-
* When the selection is inside an existing checklist `<ul class="an-checklist">`,
|
|
945
|
-
* converts the selected `<li>` items back into `<p>` paragraphs and places the caret
|
|
946
|
-
* at the start of the first converted paragraph. Otherwise creates a checklist:
|
|
947
|
-
* - If the selection is collapsed, converts the nearest block-level ancestor (or inserts
|
|
948
|
-
* a single checklist item at the editable root) into a checklist with one item containing
|
|
949
|
-
* that block's text and places the caret inside the new item.
|
|
950
|
-
* - If the selection is a range, converts each intersecting block element into one checklist
|
|
951
|
-
* item (preserving textual content) and places the caret at the end of the last item.
|
|
952
|
-
*
|
|
953
|
-
* Empty or whitespace-only selections do not create a checklist.
|
|
954
1031
|
*/
|
|
955
1032
|
function toggleChecklist() {
|
|
956
1033
|
const sel = globalThis.getSelection();
|
|
@@ -958,27 +1035,26 @@ function toggleChecklist() {
|
|
|
958
1035
|
const range = sel.getRangeAt(0);
|
|
959
1036
|
let container = range.commonAncestorContainer;
|
|
960
1037
|
if (container.nodeType === 3) container = container.parentElement;
|
|
961
|
-
const
|
|
962
|
-
if (
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1038
|
+
const listEl = container?.closest("ul, ol");
|
|
1039
|
+
if (listEl) if (listEl.classList.contains("an-checklist")) {
|
|
1040
|
+
if (listEl.parentNode) {
|
|
1041
|
+
const lis = Array.from(listEl.children);
|
|
1042
|
+
let firstP = null;
|
|
1043
|
+
lis.forEach((li) => {
|
|
967
1044
|
const p = document.createElement("p");
|
|
968
1045
|
for (const child of li.childNodes) {
|
|
969
1046
|
if (child.nodeType === 1 && child.tagName === "INPUT") continue;
|
|
970
1047
|
p.appendChild(child.cloneNode(true));
|
|
971
1048
|
}
|
|
972
|
-
p.innerHTML = p.innerHTML.replaceAll("", "");
|
|
1049
|
+
p.innerHTML = p.innerHTML.replaceAll("", "").replaceAll("", "");
|
|
973
1050
|
if (!p.hasChildNodes() || !p.textContent.trim()) {
|
|
974
1051
|
p.innerHTML = "";
|
|
975
1052
|
p.appendChild(document.createTextNode("\xA0"));
|
|
976
1053
|
}
|
|
977
|
-
|
|
1054
|
+
listEl.before(p);
|
|
978
1055
|
if (!firstP) firstP = p;
|
|
979
|
-
li.remove();
|
|
980
1056
|
});
|
|
981
|
-
|
|
1057
|
+
listEl.remove();
|
|
982
1058
|
if (firstP) {
|
|
983
1059
|
const nr = document.createRange();
|
|
984
1060
|
nr.setStart(firstP.firstChild || firstP, 0);
|
|
@@ -986,11 +1062,65 @@ function toggleChecklist() {
|
|
|
986
1062
|
sel.removeAllRanges();
|
|
987
1063
|
sel.addRange(nr);
|
|
988
1064
|
}
|
|
989
|
-
|
|
1065
|
+
}
|
|
1066
|
+
} else {
|
|
1067
|
+
const targetUl = changeTagName(listEl, "ul");
|
|
1068
|
+
targetUl.classList.add("an-checklist");
|
|
1069
|
+
ensureCheckboxes(targetUl);
|
|
1070
|
+
const firstLi = targetUl.querySelector("li");
|
|
1071
|
+
if (firstLi) {
|
|
1072
|
+
const nr = document.createRange();
|
|
1073
|
+
nr.selectNodeContents(firstLi);
|
|
1074
|
+
nr.collapse(false);
|
|
1075
|
+
sel.removeAllRanges();
|
|
1076
|
+
sel.addRange(nr);
|
|
990
1077
|
}
|
|
991
1078
|
}
|
|
992
|
-
|
|
993
|
-
const
|
|
1079
|
+
else {
|
|
1080
|
+
const editableRoot = container?.closest("[contenteditable=\"true\"]");
|
|
1081
|
+
if (range.collapsed) {
|
|
1082
|
+
const BLOCK_TAGS = new Set([
|
|
1083
|
+
"P",
|
|
1084
|
+
"DIV",
|
|
1085
|
+
"H1",
|
|
1086
|
+
"H2",
|
|
1087
|
+
"H3",
|
|
1088
|
+
"H4",
|
|
1089
|
+
"H5",
|
|
1090
|
+
"H6",
|
|
1091
|
+
"BLOCKQUOTE",
|
|
1092
|
+
"LI"
|
|
1093
|
+
]);
|
|
1094
|
+
let block = container;
|
|
1095
|
+
while (block?.parentNode && block !== editableRoot && !BLOCK_TAGS.has(block.tagName)) block = block.parentNode;
|
|
1096
|
+
if (block === editableRoot) block = null;
|
|
1097
|
+
const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replaceAll("\xA0", " ") : "";
|
|
1098
|
+
const newUl = document.createElement("ul");
|
|
1099
|
+
newUl.className = "an-checklist";
|
|
1100
|
+
const li = document.createElement("li");
|
|
1101
|
+
const checkbox = document.createElement("input");
|
|
1102
|
+
checkbox.type = "checkbox";
|
|
1103
|
+
checkbox.contentEditable = "false";
|
|
1104
|
+
li.appendChild(checkbox);
|
|
1105
|
+
li.appendChild(document.createTextNode(itemText || ""));
|
|
1106
|
+
newUl.appendChild(li);
|
|
1107
|
+
if (block && BLOCK_TAGS.has(block.tagName)) block.parentNode.replaceChild(newUl, block);
|
|
1108
|
+
else {
|
|
1109
|
+
const nativeRange = sel.getRangeAt(0);
|
|
1110
|
+
nativeRange.deleteContents();
|
|
1111
|
+
nativeRange.insertNode(newUl);
|
|
1112
|
+
}
|
|
1113
|
+
const textNode = li.lastChild;
|
|
1114
|
+
const nr = document.createRange();
|
|
1115
|
+
const offset = textNode.nodeType === Node.TEXT_NODE ? textNode.textContent.length : 0;
|
|
1116
|
+
nr.setStart(textNode, offset);
|
|
1117
|
+
nr.collapse(true);
|
|
1118
|
+
sel.removeAllRanges();
|
|
1119
|
+
sel.addRange(nr);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (!sel.toString().replace(/[\u00a0\u200B]/g, " ").trim()) return;
|
|
1123
|
+
const BLOCK_TAGS_MULTI = new Set([
|
|
994
1124
|
"P",
|
|
995
1125
|
"DIV",
|
|
996
1126
|
"H1",
|
|
@@ -1000,89 +1130,51 @@ function toggleChecklist() {
|
|
|
1000
1130
|
"H5",
|
|
1001
1131
|
"H6",
|
|
1002
1132
|
"BLOCKQUOTE",
|
|
1133
|
+
"PRE",
|
|
1003
1134
|
"LI"
|
|
1004
1135
|
]);
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
const nativeRange = sel.getRangeAt(0);
|
|
1020
|
-
nativeRange.deleteContents();
|
|
1021
|
-
nativeRange.insertNode(ul);
|
|
1136
|
+
const blocks = [];
|
|
1137
|
+
const seenBlocks = /* @__PURE__ */ new Set();
|
|
1138
|
+
const commonAncestor = range.commonAncestorContainer;
|
|
1139
|
+
const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
|
|
1140
|
+
let node;
|
|
1141
|
+
while (node = iter.nextNode()) {
|
|
1142
|
+
if (!range.intersectsNode(node)) continue;
|
|
1143
|
+
let blockEl = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
1144
|
+
while (blockEl && blockEl !== editableRoot && !BLOCK_TAGS_MULTI.has(blockEl.tagName)) blockEl = blockEl.parentElement;
|
|
1145
|
+
if (blockEl === editableRoot) blockEl = null;
|
|
1146
|
+
if (blockEl && !seenBlocks.has(blockEl)) {
|
|
1147
|
+
seenBlocks.add(blockEl);
|
|
1148
|
+
blocks.push(blockEl);
|
|
1149
|
+
}
|
|
1022
1150
|
}
|
|
1023
|
-
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
const commonAncestor = range.commonAncestorContainer;
|
|
1049
|
-
const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
|
|
1050
|
-
let node;
|
|
1051
|
-
while (node = iter.nextNode()) {
|
|
1052
|
-
if (!range.intersectsNode(node)) continue;
|
|
1053
|
-
let block = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
1054
|
-
while (block && !BLOCK_TAGS_MULTI.has(block.tagName)) block = block.parentElement;
|
|
1055
|
-
if (block && !seenBlocks.has(block)) {
|
|
1056
|
-
seenBlocks.add(block);
|
|
1057
|
-
blocks.push(block);
|
|
1151
|
+
if (blocks.length === 0) return;
|
|
1152
|
+
const newUl = document.createElement("ul");
|
|
1153
|
+
newUl.className = "an-checklist";
|
|
1154
|
+
/** @type {Text|null} */ let lastTextNode = null;
|
|
1155
|
+
blocks.forEach((block) => {
|
|
1156
|
+
const li = document.createElement("li");
|
|
1157
|
+
const cb = document.createElement("input");
|
|
1158
|
+
cb.type = "checkbox";
|
|
1159
|
+
cb.contentEditable = "false";
|
|
1160
|
+
li.appendChild(cb);
|
|
1161
|
+
const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
|
|
1162
|
+
const tn = document.createTextNode(blockText || "");
|
|
1163
|
+
li.appendChild(tn);
|
|
1164
|
+
newUl.appendChild(li);
|
|
1165
|
+
lastTextNode = tn;
|
|
1166
|
+
});
|
|
1167
|
+
const firstBlock = blocks[0];
|
|
1168
|
+
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
1169
|
+
blocks.forEach((block) => block.remove());
|
|
1170
|
+
if (lastTextNode) {
|
|
1171
|
+
const nr = document.createRange();
|
|
1172
|
+
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
1173
|
+
nr.collapse(true);
|
|
1174
|
+
sel.removeAllRanges();
|
|
1175
|
+
sel.addRange(nr);
|
|
1058
1176
|
}
|
|
1059
1177
|
}
|
|
1060
|
-
if (blocks.length === 0) return;
|
|
1061
|
-
const newUl = document.createElement("ul");
|
|
1062
|
-
newUl.className = "an-checklist";
|
|
1063
|
-
/** @type {Text|null} */ let lastTextNode = null;
|
|
1064
|
-
blocks.forEach((block) => {
|
|
1065
|
-
const li = document.createElement("li");
|
|
1066
|
-
const cb = document.createElement("input");
|
|
1067
|
-
cb.type = "checkbox";
|
|
1068
|
-
cb.setAttribute("contenteditable", "false");
|
|
1069
|
-
li.appendChild(cb);
|
|
1070
|
-
const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
|
|
1071
|
-
const tn = document.createTextNode(blockText || "");
|
|
1072
|
-
li.appendChild(tn);
|
|
1073
|
-
newUl.appendChild(li);
|
|
1074
|
-
lastTextNode = tn;
|
|
1075
|
-
});
|
|
1076
|
-
const firstBlock = blocks[0];
|
|
1077
|
-
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
1078
|
-
blocks.forEach((block) => block.remove());
|
|
1079
|
-
if (lastTextNode) {
|
|
1080
|
-
const nr = document.createRange();
|
|
1081
|
-
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
1082
|
-
nr.collapse(true);
|
|
1083
|
-
sel.removeAllRanges();
|
|
1084
|
-
sel.addRange(nr);
|
|
1085
|
-
}
|
|
1086
1178
|
}
|
|
1087
1179
|
/**
|
|
1088
1180
|
* Returns true when the cursor is inside a checklist item.
|
|
@@ -2176,6 +2268,7 @@ var locales = {
|
|
|
2176
2268
|
findPlaceholder: "Tìm…",
|
|
2177
2269
|
searchAriaLabel: "Văn bản tìm kiếm",
|
|
2178
2270
|
caseSensitive: "\xA0Phân biệt hoa thường",
|
|
2271
|
+
wholeWord: "Toàn bộ từ",
|
|
2179
2272
|
prevBtn: "← Trước",
|
|
2180
2273
|
nextBtn: "Tiếp →",
|
|
2181
2274
|
replacePlaceholder: "Thay thế bằng…",
|
|
@@ -2343,9 +2436,16 @@ var locales = {
|
|
|
2343
2436
|
columnWidth: "Chiều rộng cột",
|
|
2344
2437
|
rowHeight: "Chiều cao hàng",
|
|
2345
2438
|
tableBorderWidth: "Độ rộng viền bảng",
|
|
2439
|
+
tableBorderColor: "Màu viền bảng",
|
|
2346
2440
|
deleteTable: "Xóa bảng",
|
|
2441
|
+
cellAlignLeft: "Căn trái",
|
|
2442
|
+
cellAlignCenter: "Căn giữa",
|
|
2443
|
+
cellAlignRight: "Căn phải",
|
|
2444
|
+
cellAlignJustify: "Căn đều",
|
|
2445
|
+
toggleHeaderRow: "Bật/tắt hàng tiêu đề",
|
|
2347
2446
|
cellBackground: "Màu Nền Ô",
|
|
2348
2447
|
noShading: "Xóa Màu Nền",
|
|
2448
|
+
noBorderColor: "Xóa màu viền",
|
|
2349
2449
|
columnWidthPx: "Chiều rộng cột (px)",
|
|
2350
2450
|
rowHeightPx: "Chiều cao hàng (px)",
|
|
2351
2451
|
tableBorderWidthPx: "Độ rộng viền bảng (px)",
|
|
@@ -2522,6 +2622,7 @@ var locales = {
|
|
|
2522
2622
|
findPlaceholder: "検索…",
|
|
2523
2623
|
searchAriaLabel: "検索テキスト",
|
|
2524
2624
|
caseSensitive: "\xA0大文字/小文字を区別",
|
|
2625
|
+
wholeWord: "単語単位",
|
|
2525
2626
|
prevBtn: "← 前へ",
|
|
2526
2627
|
nextBtn: "次へ →",
|
|
2527
2628
|
replacePlaceholder: "置換後…",
|
|
@@ -2689,7 +2790,16 @@ var locales = {
|
|
|
2689
2790
|
columnWidth: "列幅",
|
|
2690
2791
|
rowHeight: "行の高さ",
|
|
2691
2792
|
tableBorderWidth: "テーブルの枠幅",
|
|
2793
|
+
tableBorderColor: "テーブルの枠線色",
|
|
2692
2794
|
deleteTable: "テーブルを削除",
|
|
2795
|
+
cellAlignLeft: "左揃え",
|
|
2796
|
+
cellAlignCenter: "中央揃え",
|
|
2797
|
+
cellAlignRight: "右揃え",
|
|
2798
|
+
cellAlignJustify: "両端揃え",
|
|
2799
|
+
toggleHeaderRow: "ヘッダー行の切り替え",
|
|
2800
|
+
cellBackground: "セル背景色",
|
|
2801
|
+
noShading: "背景色なし",
|
|
2802
|
+
noBorderColor: "枠線色なし",
|
|
2693
2803
|
columnWidthPx: "列幅 (px)",
|
|
2694
2804
|
rowHeightPx: "行の高さ (px)",
|
|
2695
2805
|
tableBorderWidthPx: "テーブルの枠幅 (px)",
|
|
@@ -2866,6 +2976,7 @@ var locales = {
|
|
|
2866
2976
|
findPlaceholder: "查找…",
|
|
2867
2977
|
searchAriaLabel: "搜索文字",
|
|
2868
2978
|
caseSensitive: "\xA0区分大小写",
|
|
2979
|
+
wholeWord: "全字匹配",
|
|
2869
2980
|
prevBtn: "← 上一个",
|
|
2870
2981
|
nextBtn: "下一个 →",
|
|
2871
2982
|
replacePlaceholder: "替换为…",
|
|
@@ -3033,7 +3144,16 @@ var locales = {
|
|
|
3033
3144
|
columnWidth: "列宽",
|
|
3034
3145
|
rowHeight: "行高",
|
|
3035
3146
|
tableBorderWidth: "表格边框宽度",
|
|
3147
|
+
tableBorderColor: "表格边框颜色",
|
|
3036
3148
|
deleteTable: "删除表格",
|
|
3149
|
+
cellAlignLeft: "左对齐",
|
|
3150
|
+
cellAlignCenter: "居中对齐",
|
|
3151
|
+
cellAlignRight: "右对齐",
|
|
3152
|
+
cellAlignJustify: "两端对齐",
|
|
3153
|
+
toggleHeaderRow: "切换标题行",
|
|
3154
|
+
cellBackground: "单元格背景",
|
|
3155
|
+
noShading: "无底纹",
|
|
3156
|
+
noBorderColor: "无边框颜色",
|
|
3037
3157
|
columnWidthPx: "列宽 (px)",
|
|
3038
3158
|
rowHeightPx: "行高 (px)",
|
|
3039
3159
|
tableBorderWidthPx: "表格边框宽度 (px)",
|
|
@@ -3210,6 +3330,7 @@ var locales = {
|
|
|
3210
3330
|
findPlaceholder: "Rechercher…",
|
|
3211
3331
|
searchAriaLabel: "Texte à rechercher",
|
|
3212
3332
|
caseSensitive: "\xA0Respecter la casse",
|
|
3333
|
+
wholeWord: "Mot entier",
|
|
3213
3334
|
prevBtn: "← Préc.",
|
|
3214
3335
|
nextBtn: "Suiv. →",
|
|
3215
3336
|
replacePlaceholder: "Remplacer par…",
|
|
@@ -3377,7 +3498,16 @@ var locales = {
|
|
|
3377
3498
|
columnWidth: "Largeur de colonne",
|
|
3378
3499
|
rowHeight: "Hauteur de ligne",
|
|
3379
3500
|
tableBorderWidth: "Épaisseur des bordures",
|
|
3501
|
+
tableBorderColor: "Couleur de bordure",
|
|
3380
3502
|
deleteTable: "Supprimer le tableau",
|
|
3503
|
+
cellAlignLeft: "Aligner à gauche",
|
|
3504
|
+
cellAlignCenter: "Centrer",
|
|
3505
|
+
cellAlignRight: "Aligner à droite",
|
|
3506
|
+
cellAlignJustify: "Justifier",
|
|
3507
|
+
toggleHeaderRow: "Ligne d'en-tête",
|
|
3508
|
+
cellBackground: "Couleur de fond",
|
|
3509
|
+
noShading: "Aucun fond",
|
|
3510
|
+
noBorderColor: "Aucune bordure",
|
|
3381
3511
|
columnWidthPx: "Largeur de colonne (px)",
|
|
3382
3512
|
rowHeightPx: "Hauteur de ligne (px)",
|
|
3383
3513
|
tableBorderWidthPx: "Épaisseur des bordures (px)",
|
|
@@ -3554,6 +3684,7 @@ var locales = {
|
|
|
3554
3684
|
findPlaceholder: "Suchen…",
|
|
3555
3685
|
searchAriaLabel: "Suchtext",
|
|
3556
3686
|
caseSensitive: "\xA0Groß-/Kleinschreibung",
|
|
3687
|
+
wholeWord: "Ganzes Wort",
|
|
3557
3688
|
prevBtn: "← Zurück",
|
|
3558
3689
|
nextBtn: "Weiter →",
|
|
3559
3690
|
replacePlaceholder: "Ersetzen durch…",
|
|
@@ -3721,7 +3852,16 @@ var locales = {
|
|
|
3721
3852
|
columnWidth: "Spaltenbreite",
|
|
3722
3853
|
rowHeight: "Zeilenhöhe",
|
|
3723
3854
|
tableBorderWidth: "Tabellenrahmenbreite",
|
|
3855
|
+
tableBorderColor: "Tabellenrahmenfarbe",
|
|
3724
3856
|
deleteTable: "Tabelle löschen",
|
|
3857
|
+
cellAlignLeft: "Linksbündig",
|
|
3858
|
+
cellAlignCenter: "Zentrieren",
|
|
3859
|
+
cellAlignRight: "Rechtsbündig",
|
|
3860
|
+
cellAlignJustify: "Blocksatz",
|
|
3861
|
+
toggleHeaderRow: "Kopfzeile umschalten",
|
|
3862
|
+
cellBackground: "Zellhintergrund",
|
|
3863
|
+
noShading: "Kein Hintergrund",
|
|
3864
|
+
noBorderColor: "Keine Rahmenfarbe",
|
|
3725
3865
|
columnWidthPx: "Spaltenbreite (px)",
|
|
3726
3866
|
rowHeightPx: "Zeilenhöhe (px)",
|
|
3727
3867
|
tableBorderWidthPx: "Tabellenrahmenbreite (px)",
|
|
@@ -3898,6 +4038,7 @@ var locales = {
|
|
|
3898
4038
|
findPlaceholder: "Buscar…",
|
|
3899
4039
|
searchAriaLabel: "Texto de búsqueda",
|
|
3900
4040
|
caseSensitive: "\xA0Distinguir mayúsculas",
|
|
4041
|
+
wholeWord: "Palabra completa",
|
|
3901
4042
|
prevBtn: "← Anterior",
|
|
3902
4043
|
nextBtn: "Siguiente →",
|
|
3903
4044
|
replacePlaceholder: "Reemplazar con…",
|
|
@@ -4065,7 +4206,16 @@ var locales = {
|
|
|
4065
4206
|
columnWidth: "Ancho de columna",
|
|
4066
4207
|
rowHeight: "Alto de fila",
|
|
4067
4208
|
tableBorderWidth: "Grosor del borde de la tabla",
|
|
4209
|
+
tableBorderColor: "Color del borde de la tabla",
|
|
4068
4210
|
deleteTable: "Eliminar tabla",
|
|
4211
|
+
cellAlignLeft: "Alinear a la izquierda",
|
|
4212
|
+
cellAlignCenter: "Centrar",
|
|
4213
|
+
cellAlignRight: "Alinear a la derecha",
|
|
4214
|
+
cellAlignJustify: "Justificar",
|
|
4215
|
+
toggleHeaderRow: "Activar fila de encabezado",
|
|
4216
|
+
cellBackground: "Fondo de celda",
|
|
4217
|
+
noShading: "Sin fondo",
|
|
4218
|
+
noBorderColor: "Sin color de borde",
|
|
4069
4219
|
columnWidthPx: "Ancho de columna (px)",
|
|
4070
4220
|
rowHeightPx: "Alto de fila (px)",
|
|
4071
4221
|
tableBorderWidthPx: "Grosor del borde (px)",
|
|
@@ -4242,6 +4392,7 @@ var locales = {
|
|
|
4242
4392
|
findPlaceholder: "찾기…",
|
|
4243
4393
|
searchAriaLabel: "검색 텍스트",
|
|
4244
4394
|
caseSensitive: "\xA0대소문자 구분",
|
|
4395
|
+
wholeWord: "전체 단어",
|
|
4245
4396
|
prevBtn: "← 이전",
|
|
4246
4397
|
nextBtn: "다음 →",
|
|
4247
4398
|
replacePlaceholder: "바꿀 내용…",
|
|
@@ -4409,7 +4560,16 @@ var locales = {
|
|
|
4409
4560
|
columnWidth: "열 너비",
|
|
4410
4561
|
rowHeight: "행 높이",
|
|
4411
4562
|
tableBorderWidth: "표 테두리 너비",
|
|
4563
|
+
tableBorderColor: "표 테두리 색상",
|
|
4412
4564
|
deleteTable: "표 삭제",
|
|
4565
|
+
cellAlignLeft: "왼쪽 정렬",
|
|
4566
|
+
cellAlignCenter: "가운데 정렬",
|
|
4567
|
+
cellAlignRight: "오른쪽 정렬",
|
|
4568
|
+
cellAlignJustify: "양쪽 정렬",
|
|
4569
|
+
toggleHeaderRow: "머리글 행 전환",
|
|
4570
|
+
cellBackground: "셀 배경색",
|
|
4571
|
+
noShading: "배경 없음",
|
|
4572
|
+
noBorderColor: "테두리 색 없음",
|
|
4413
4573
|
columnWidthPx: "열 너비 (px)",
|
|
4414
4574
|
rowHeightPx: "행 높이 (px)",
|
|
4415
4575
|
tableBorderWidthPx: "표 테두리 너비 (px)",
|
|
@@ -4927,7 +5087,11 @@ function insertTable(cols, rows, opts = {}) {
|
|
|
4927
5087
|
const sel = globalThis.getSelection();
|
|
4928
5088
|
if (!sel || sel.rangeCount === 0) return;
|
|
4929
5089
|
const range = sel.getRangeAt(0);
|
|
4930
|
-
|
|
5090
|
+
try {
|
|
5091
|
+
range.deleteContents();
|
|
5092
|
+
} catch (_) {
|
|
5093
|
+
return;
|
|
5094
|
+
}
|
|
4931
5095
|
const BLOCK = new Set([
|
|
4932
5096
|
"P",
|
|
4933
5097
|
"DIV",
|
|
@@ -4952,7 +5116,11 @@ function insertTable(cols, rows, opts = {}) {
|
|
|
4952
5116
|
table.after(p);
|
|
4953
5117
|
}
|
|
4954
5118
|
if (!anchor.textContent.trim() && !anchor.querySelector("img, video, table")) anchor.remove();
|
|
4955
|
-
} else
|
|
5119
|
+
} else try {
|
|
5120
|
+
range.insertNode(table);
|
|
5121
|
+
} catch (_) {
|
|
5122
|
+
return;
|
|
5123
|
+
}
|
|
4956
5124
|
const firstCell = table.querySelector("td, th");
|
|
4957
5125
|
if (firstCell) {
|
|
4958
5126
|
const nr = document.createRange();
|
|
@@ -5035,6 +5203,23 @@ var _FA_PATTERN = /\bfa-/;
|
|
|
5035
5203
|
var isFAIcon = (n) => !!(n?.nodeName === "I" && _FA_PATTERN.test(n.className || ""));
|
|
5036
5204
|
var isZwsAnchor = (n) => !!(n?.nodeType === Node.TEXT_NODE && (n.textContent === "" || n.textContent === ""));
|
|
5037
5205
|
/**
|
|
5206
|
+
* Extracts the content from `startContainer:startOffset` to the end of `li`.
|
|
5207
|
+
* Returns an empty fragment if the range is invalid (e.g. detached node).
|
|
5208
|
+
* @param {Range} nativeRange
|
|
5209
|
+
* @param {Element} li
|
|
5210
|
+
* @returns {DocumentFragment}
|
|
5211
|
+
*/
|
|
5212
|
+
function extractAfterContent(nativeRange, li) {
|
|
5213
|
+
try {
|
|
5214
|
+
const r = document.createRange();
|
|
5215
|
+
r.setStart(nativeRange.startContainer, nativeRange.startOffset);
|
|
5216
|
+
r.setEnd(li, li.childNodes.length);
|
|
5217
|
+
return r.extractContents();
|
|
5218
|
+
} catch (_) {
|
|
5219
|
+
return document.createDocumentFragment();
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
/**
|
|
5038
5223
|
* Handles special keydown behaviour inside the editor.
|
|
5039
5224
|
* @param {KeyboardEvent} event
|
|
5040
5225
|
* @param {HTMLElement} editable
|
|
@@ -5242,12 +5427,10 @@ function handleKeydown(event, editable, options = {}) {
|
|
|
5242
5427
|
}
|
|
5243
5428
|
if (!nativeRange.collapsed) {
|
|
5244
5429
|
nativeRange.deleteContents();
|
|
5430
|
+
if (sel.rangeCount === 0 || !checkLi.isConnected) return true;
|
|
5245
5431
|
nativeRange = sel.getRangeAt(0);
|
|
5246
5432
|
}
|
|
5247
|
-
const
|
|
5248
|
-
afterRange.setStart(nativeRange.startContainer, nativeRange.startOffset);
|
|
5249
|
-
afterRange.setEnd(checkLi, checkLi.childNodes.length);
|
|
5250
|
-
const afterFrag = afterRange.extractContents();
|
|
5433
|
+
const afterFrag = extractAfterContent(nativeRange, checkLi);
|
|
5251
5434
|
const newLi = document.createElement("li");
|
|
5252
5435
|
const cb = document.createElement("input");
|
|
5253
5436
|
cb.type = "checkbox";
|
|
@@ -5363,7 +5546,15 @@ function _domToMd(node, depth = 0) {
|
|
|
5363
5546
|
const items = Array.from(el.querySelectorAll(":scope > li"));
|
|
5364
5547
|
if (!items.length) return inner();
|
|
5365
5548
|
const indent = " ".repeat(depth);
|
|
5366
|
-
const
|
|
5549
|
+
const isChecklist = el.classList.contains("an-checklist");
|
|
5550
|
+
const lines = items.map((li) => {
|
|
5551
|
+
let prefix = "- ";
|
|
5552
|
+
if (isChecklist) {
|
|
5553
|
+
const cb = li.querySelector("input[type=\"checkbox\"]");
|
|
5554
|
+
prefix = (cb ? cb.checked : false) ? "- [x] " : "- [ ] ";
|
|
5555
|
+
}
|
|
5556
|
+
return `${indent}${prefix}${_domToMd(li, depth + 1).trim()}`;
|
|
5557
|
+
}).join("\n");
|
|
5367
5558
|
return depth === 0 ? `\n\n${lines}\n\n` : `\n${lines}`;
|
|
5368
5559
|
}
|
|
5369
5560
|
case "ol": {
|
|
@@ -5403,7 +5594,7 @@ function _domToMd(node, depth = 0) {
|
|
|
5403
5594
|
* @returns {boolean} `true` if any Markdown-like pattern is present, `false` otherwise.
|
|
5404
5595
|
*/
|
|
5405
5596
|
function isMarkdown(text) {
|
|
5406
|
-
return /^#{1,6} \
|
|
5597
|
+
return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text);
|
|
5407
5598
|
}
|
|
5408
5599
|
/**
|
|
5409
5600
|
* Converts a Markdown string to an HTML string.
|
|
@@ -5453,11 +5644,20 @@ function markdownToHTML(text) {
|
|
|
5453
5644
|
}
|
|
5454
5645
|
if (/^[-*+] /.test(line)) {
|
|
5455
5646
|
const items = [];
|
|
5647
|
+
const isChecklist = /^[-*+]\s+\[[ xX]\]\s+/.test(line);
|
|
5648
|
+
const listTag = isChecklist ? "ul class=\"an-checklist\"" : "ul";
|
|
5456
5649
|
while (i < lines.length && /^[-*+] /.test(lines[i])) {
|
|
5457
|
-
|
|
5650
|
+
if (/^[-*+]\s+\[[ xX]\]\s+/.test(lines[i]) !== isChecklist) break;
|
|
5651
|
+
const content = lines[i].slice(2);
|
|
5652
|
+
if (isChecklist) {
|
|
5653
|
+
const cbMatch = /^\[([ xX])\][ \t]+/.exec(content);
|
|
5654
|
+
const cbHtml = `<input type="checkbox" contenteditable="false"${cbMatch?.[1]?.toLowerCase() === "x" ? " checked" : ""}>`;
|
|
5655
|
+
const textContent = cbMatch ? content.slice(cbMatch[0].length) : content;
|
|
5656
|
+
items.push(`<li>${cbHtml}${_inline(textContent)}</li>`);
|
|
5657
|
+
} else items.push(`<li>${_inline(content)}</li>`);
|
|
5458
5658
|
i++;
|
|
5459
5659
|
}
|
|
5460
|
-
out.push(
|
|
5660
|
+
out.push(`<${listTag}>${items.join("")}</${listTag.split(" ")[0]}>`);
|
|
5461
5661
|
continue;
|
|
5462
5662
|
}
|
|
5463
5663
|
if (/^\d+\. /.test(line)) {
|
|
@@ -5514,7 +5714,7 @@ function _inline(text) {
|
|
|
5514
5714
|
text = text.replace(/_{2}([^_\n]+?)_{2}/g, (_, c) => `<strong>${_esc(c)}</strong>`);
|
|
5515
5715
|
text = text.replace(/\*([^*\n]+?)\*/g, (_, c) => `<em>${_esc(c)}</em>`);
|
|
5516
5716
|
text = text.replace(/_([^_\n]+?)_/g, (_, c) => `<em>${_esc(c)}</em>`);
|
|
5517
|
-
text = text.replace(/~~([
|
|
5717
|
+
text = text.replace(/~~([^~\n]+?)~~/g, (_, c) => `<del>${_esc(c)}</del>`);
|
|
5518
5718
|
text = text.replace(/`([^`]+)`/g, (_, c) => `<code>${_esc(c)}</code>`);
|
|
5519
5719
|
return text;
|
|
5520
5720
|
}
|
|
@@ -7213,7 +7413,7 @@ var Clipboard = class {
|
|
|
7213
7413
|
const mime = /:(.*?);/.exec(header)?.[1] ?? "image/png";
|
|
7214
7414
|
const binary = atob(b64);
|
|
7215
7415
|
const arr = new Uint8Array(binary.length);
|
|
7216
|
-
for (let i = 0; i < binary.length; i++) arr[i] = binary.
|
|
7416
|
+
for (let i = 0; i < binary.length; i++) arr[i] = binary.charCodeAt(i);
|
|
7217
7417
|
return new Blob([arr], { type: mime });
|
|
7218
7418
|
}
|
|
7219
7419
|
/**
|
|
@@ -7819,6 +8019,7 @@ var ImageDialog = class extends BaseDialog {
|
|
|
7819
8019
|
return overlay;
|
|
7820
8020
|
}
|
|
7821
8021
|
_onFileChange() {
|
|
8022
|
+
if (this.context._alive === false) return;
|
|
7822
8023
|
const file = this._fileInput?.files?.[0];
|
|
7823
8024
|
if (!file?.type?.startsWith("image/")) return;
|
|
7824
8025
|
if (!new Set([
|
|
@@ -10384,8 +10585,28 @@ var TableTooltip = class {
|
|
|
10384
10585
|
return direction === "asc" ? aText.localeCompare(bText) : bText.localeCompare(aText);
|
|
10385
10586
|
});
|
|
10386
10587
|
rows.forEach((row) => tbody.appendChild(row));
|
|
10588
|
+
this._markSortIndicator(table, colIdx, direction);
|
|
10387
10589
|
this.context.invoke("editor.afterCommand");
|
|
10388
10590
|
}
|
|
10591
|
+
/**
|
|
10592
|
+
* Marks the header cell of the sorted column with `an-sort-asc`/`an-sort-desc`
|
|
10593
|
+
* so the active sort column and direction are visible, and clears any previous
|
|
10594
|
+
* indicator. No-op for tables without a `<thead>` (no header row to mark).
|
|
10595
|
+
* @param {HTMLTableElement} table
|
|
10596
|
+
* @param {number} colIdx
|
|
10597
|
+
* @param {'asc'|'desc'} direction
|
|
10598
|
+
*/
|
|
10599
|
+
_markSortIndicator(table, colIdx, direction) {
|
|
10600
|
+
const thead = table.querySelector("thead");
|
|
10601
|
+
if (!thead) return;
|
|
10602
|
+
thead.querySelectorAll(".an-sort-asc, .an-sort-desc").forEach((el) => {
|
|
10603
|
+
el.classList.remove("an-sort-asc", "an-sort-desc");
|
|
10604
|
+
});
|
|
10605
|
+
const headerRow = thead.querySelector("tr");
|
|
10606
|
+
if (!headerRow) return;
|
|
10607
|
+
const headerCell = getCellAtVisualCol(headerRow, colIdx);
|
|
10608
|
+
if (headerCell) headerCell.classList.add(direction === "asc" ? "an-sort-asc" : "an-sort-desc");
|
|
10609
|
+
}
|
|
10389
10610
|
_exportTableCSV() {
|
|
10390
10611
|
const table = this._activeTable;
|
|
10391
10612
|
if (!table) return;
|
|
@@ -16304,6 +16525,7 @@ var BubbleToolbar = class {
|
|
|
16304
16525
|
* debounce: 200,
|
|
16305
16526
|
* onSearch: (query, callback) => void,
|
|
16306
16527
|
* onInsert: (item) => string | null,
|
|
16528
|
+
* onError: (err: Error) => void,
|
|
16307
16529
|
* mentionClass: 'an-mention',
|
|
16308
16530
|
* allowSpaces: false,
|
|
16309
16531
|
* }
|
|
@@ -16341,6 +16563,7 @@ var Mention = class {
|
|
|
16341
16563
|
debounce: cfg.debounce ?? 200,
|
|
16342
16564
|
onSearch: cfg.onSearch,
|
|
16343
16565
|
onInsert: cfg.onInsert || null,
|
|
16566
|
+
onError: cfg.onError || null,
|
|
16344
16567
|
mentionClass: cfg.mentionClass || "an-mention",
|
|
16345
16568
|
allowSpaces: cfg.allowSpaces || false
|
|
16346
16569
|
};
|
|
@@ -16496,7 +16719,14 @@ var Mention = class {
|
|
|
16496
16719
|
this._renderItems(items);
|
|
16497
16720
|
this._showDropdown();
|
|
16498
16721
|
};
|
|
16499
|
-
|
|
16722
|
+
let result;
|
|
16723
|
+
try {
|
|
16724
|
+
result = this._cfg.onSearch(this._query, cb);
|
|
16725
|
+
} catch (err) {
|
|
16726
|
+
this._hideDropdown();
|
|
16727
|
+
if (typeof this._cfg.onError === "function") this._cfg.onError(err);
|
|
16728
|
+
return;
|
|
16729
|
+
}
|
|
16500
16730
|
if (result && typeof result.then === "function") result.then(cb).catch((err) => {
|
|
16501
16731
|
this._hideDropdown();
|
|
16502
16732
|
if (typeof this._cfg.onError === "function") this._cfg.onError(err);
|
|
@@ -17262,7 +17492,7 @@ var AutumnNote = {
|
|
|
17262
17492
|
/** All pre-built button definitions — accessible in every module format including UMD/CJS. */
|
|
17263
17493
|
buttons,
|
|
17264
17494
|
/** Library version */
|
|
17265
|
-
version: "1.8.
|
|
17495
|
+
version: "1.8.2"
|
|
17266
17496
|
};
|
|
17267
17497
|
/**
|
|
17268
17498
|
* @param {string|Element|NodeList|Element[]} selector
|