autumnnote 1.0.7 → 1.0.9
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 +332 -638
- package/dist/autumnnote.css +40 -4
- package/dist/autumnnote.es.js +3375 -262
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +3374 -261
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/Context.js +4 -0
- package/src/js/editing/Style.js +195 -44
- package/src/js/editing/Typing.js +2 -2
- package/src/js/i18n/de.js +320 -0
- package/src/js/i18n/en.js +328 -0
- package/src/js/i18n/es.js +320 -0
- package/src/js/i18n/fr.js +321 -0
- package/src/js/i18n/index.js +59 -0
- package/src/js/i18n/ja.js +321 -0
- package/src/js/i18n/ko.js +320 -0
- package/src/js/i18n/vi.js +321 -0
- package/src/js/i18n/zh.js +321 -0
- package/src/js/index.js +2 -1
- package/src/js/module/CodeTooltip.js +12 -9
- package/src/js/module/ContextMenu.js +13 -11
- package/src/js/module/Editor.js +14 -3
- package/src/js/module/EmojiDialog.js +19 -7
- package/src/js/module/FindReplace.js +14 -13
- package/src/js/module/IconDialog.js +25 -13
- package/src/js/module/ImageDialog.js +25 -20
- package/src/js/module/ImageTooltip.js +13 -12
- package/src/js/module/LinkDialog.js +10 -9
- package/src/js/module/LinkTooltip.js +6 -5
- package/src/js/module/Placeholder.js +6 -1
- package/src/js/module/ShortcutsDialog.js +5 -4
- package/src/js/module/Statusbar.js +6 -5
- package/src/js/module/TableTooltip.js +412 -102
- package/src/js/module/Toolbar.js +21 -15
- package/src/js/module/VideoDialog.js +10 -9
- package/src/js/module/VideoTooltip.js +12 -11
- package/src/js/settings.js +4 -0
- package/src/styles/autumnnote.scss +50 -5
- package/types/index.d.ts +58 -1
|
@@ -122,7 +122,7 @@ export class FindReplace {
|
|
|
122
122
|
const isReplace = this._mode === 'replace';
|
|
123
123
|
if (replaceRow) replaceRow.style.display = isReplace ? '' : 'none';
|
|
124
124
|
if (replaceActions) replaceActions.style.display = isReplace ? '' : 'none';
|
|
125
|
-
if (title) title.textContent = isReplace ?
|
|
125
|
+
if (title) title.textContent = isReplace ? this.context.locale.findReplace.findReplaceTitle : this.context.locale.findReplace.findTitle;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// ---------------------------------------------------------------------------
|
|
@@ -130,24 +130,25 @@ export class FindReplace {
|
|
|
130
130
|
// ---------------------------------------------------------------------------
|
|
131
131
|
|
|
132
132
|
_buildDialog() {
|
|
133
|
+
const L = this.context.locale.findReplace;
|
|
133
134
|
const overlay = createElement('div', {
|
|
134
135
|
class: 'an-dialog-overlay an-fr-dialog',
|
|
135
136
|
role: 'dialog',
|
|
136
137
|
'aria-modal': 'true',
|
|
137
|
-
'aria-label':
|
|
138
|
+
'aria-label': L.findReplaceTitle,
|
|
138
139
|
});
|
|
139
140
|
const box = createElement('div', { class: 'an-dialog-box' });
|
|
140
141
|
|
|
141
142
|
// ---- Title row ----
|
|
142
143
|
const titleRow = createElement('div', { class: 'an-icon-title-row' });
|
|
143
144
|
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
144
|
-
title.textContent =
|
|
145
|
+
title.textContent = L.findTitle;
|
|
145
146
|
const closeBtn = createElement('button', {
|
|
146
147
|
type: 'button',
|
|
147
148
|
class: 'an-icon-close',
|
|
148
149
|
'aria-label': 'Close',
|
|
149
150
|
});
|
|
150
|
-
closeBtn.textContent =
|
|
151
|
+
closeBtn.textContent = L.close;
|
|
151
152
|
this._closeBtn = closeBtn;
|
|
152
153
|
titleRow.append(title, closeBtn);
|
|
153
154
|
box.appendChild(titleRow);
|
|
@@ -157,8 +158,8 @@ export class FindReplace {
|
|
|
157
158
|
const findInput = createElement('input', {
|
|
158
159
|
type: 'text',
|
|
159
160
|
class: 'an-input',
|
|
160
|
-
placeholder:
|
|
161
|
-
'aria-label':
|
|
161
|
+
placeholder: L.findPlaceholder,
|
|
162
|
+
'aria-label': L.searchAriaLabel,
|
|
162
163
|
});
|
|
163
164
|
this._findInput = findInput;
|
|
164
165
|
findRow.appendChild(findInput);
|
|
@@ -172,7 +173,7 @@ export class FindReplace {
|
|
|
172
173
|
'aria-label': 'Case sensitive',
|
|
173
174
|
});
|
|
174
175
|
this._caseCheckbox = caseCheckbox;
|
|
175
|
-
caseLabel.append(caseCheckbox, document.createTextNode(
|
|
176
|
+
caseLabel.append(caseCheckbox, document.createTextNode(L.caseSensitive));
|
|
176
177
|
const counter = createElement('span', { class: 'an-fr-counter' });
|
|
177
178
|
this._counterEl = counter;
|
|
178
179
|
optRow.append(caseLabel, counter);
|
|
@@ -181,9 +182,9 @@ export class FindReplace {
|
|
|
181
182
|
// ---- Find actions ----
|
|
182
183
|
const findActions = createElement('div', { class: 'an-dialog-actions an-fr-find-actions' });
|
|
183
184
|
const prevBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
184
|
-
prevBtn.textContent =
|
|
185
|
+
prevBtn.textContent = L.prevBtn;
|
|
185
186
|
const nextBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
|
|
186
|
-
nextBtn.textContent =
|
|
187
|
+
nextBtn.textContent = L.nextBtn;
|
|
187
188
|
findActions.append(prevBtn, nextBtn);
|
|
188
189
|
box.appendChild(findActions);
|
|
189
190
|
|
|
@@ -193,8 +194,8 @@ export class FindReplace {
|
|
|
193
194
|
const replaceInput = createElement('input', {
|
|
194
195
|
type: 'text',
|
|
195
196
|
class: 'an-input',
|
|
196
|
-
placeholder:
|
|
197
|
-
'aria-label':
|
|
197
|
+
placeholder: L.replacePlaceholder,
|
|
198
|
+
'aria-label': L.replaceAriaLabel,
|
|
198
199
|
});
|
|
199
200
|
this._replaceInput = replaceInput;
|
|
200
201
|
replaceRow.appendChild(replaceInput);
|
|
@@ -204,9 +205,9 @@ export class FindReplace {
|
|
|
204
205
|
const replaceActions = createElement('div', { class: 'an-dialog-actions an-fr-replace-actions' });
|
|
205
206
|
replaceActions.style.display = 'none';
|
|
206
207
|
const replaceBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
207
|
-
replaceBtn.textContent =
|
|
208
|
+
replaceBtn.textContent = L.replaceBtn;
|
|
208
209
|
const replaceAllBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
|
|
209
|
-
replaceAllBtn.textContent =
|
|
210
|
+
replaceAllBtn.textContent = L.replaceAllBtn;
|
|
210
211
|
replaceActions.append(replaceBtn, replaceAllBtn);
|
|
211
212
|
box.appendChild(replaceActions);
|
|
212
213
|
|
|
@@ -332,11 +332,12 @@ export class IconDialog {
|
|
|
332
332
|
// ---------------------------------------------------------------------------
|
|
333
333
|
|
|
334
334
|
_buildDialog() {
|
|
335
|
+
const L = this.context.locale.iconDialog;
|
|
335
336
|
const overlay = createElement('div', {
|
|
336
337
|
class: 'an-dialog-overlay',
|
|
337
338
|
role: 'dialog',
|
|
338
339
|
'aria-modal': 'true',
|
|
339
|
-
'aria-label':
|
|
340
|
+
'aria-label': L.ariaLabel,
|
|
340
341
|
});
|
|
341
342
|
|
|
342
343
|
const box = createElement('div', { class: 'an-dialog-box an-icon-box' });
|
|
@@ -344,8 +345,8 @@ export class IconDialog {
|
|
|
344
345
|
// Title row
|
|
345
346
|
const titleRow = createElement('div', { class: 'an-icon-title-row' });
|
|
346
347
|
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
347
|
-
title.textContent =
|
|
348
|
-
const closeBtn = createElement('button', { type: 'button', class: 'an-icon-close', 'aria-label':
|
|
348
|
+
title.textContent = L.title;
|
|
349
|
+
const closeBtn = createElement('button', { type: 'button', class: 'an-icon-close', 'aria-label': L.close });
|
|
349
350
|
closeBtn.innerHTML = '×';
|
|
350
351
|
titleRow.append(title, closeBtn);
|
|
351
352
|
|
|
@@ -353,7 +354,7 @@ export class IconDialog {
|
|
|
353
354
|
const searchInput = createElement('input', {
|
|
354
355
|
type: 'search',
|
|
355
356
|
class: 'an-input an-icon-search',
|
|
356
|
-
placeholder:
|
|
357
|
+
placeholder: L.searchPlaceholder,
|
|
357
358
|
autocomplete: 'off',
|
|
358
359
|
});
|
|
359
360
|
this._searchInput = searchInput;
|
|
@@ -361,11 +362,11 @@ export class IconDialog {
|
|
|
361
362
|
// Category tabs
|
|
362
363
|
const catBar = createElement('div', { class: 'an-icon-cats' });
|
|
363
364
|
const allTab = createElement('button', { type: 'button', class: 'an-icon-cat active', 'data-cat': 'all' });
|
|
364
|
-
allTab.textContent =
|
|
365
|
+
allTab.textContent = L.all;
|
|
365
366
|
catBar.appendChild(allTab);
|
|
366
367
|
ICON_CATEGORIES.forEach(({ id, label }) => {
|
|
367
368
|
const tab = createElement('button', { type: 'button', class: 'an-icon-cat', 'data-cat': id });
|
|
368
|
-
tab.textContent = label;
|
|
369
|
+
tab.textContent = (L.categories && L.categories[id]) || label;
|
|
369
370
|
catBar.appendChild(tab);
|
|
370
371
|
});
|
|
371
372
|
this._catBar = catBar;
|
|
@@ -386,7 +387,7 @@ export class IconDialog {
|
|
|
386
387
|
const optRow = createElement('div', { class: 'an-icon-options' });
|
|
387
388
|
|
|
388
389
|
const styleLabel = createElement('label', { class: 'an-label' });
|
|
389
|
-
styleLabel.textContent =
|
|
390
|
+
styleLabel.textContent = L.style;
|
|
390
391
|
const styleSelect = createElement('select', { class: 'an-input an-icon-option-select' });
|
|
391
392
|
[['fa-solid', 'Solid'], ['fa-regular', 'Regular'], ['fa-light', 'Light (Pro)']].forEach(([v, t]) => {
|
|
392
393
|
const opt = createElement('option', { value: v });
|
|
@@ -397,7 +398,7 @@ export class IconDialog {
|
|
|
397
398
|
this._styleSelect = styleSelect;
|
|
398
399
|
|
|
399
400
|
const sizeLabel = createElement('label', { class: 'an-label' });
|
|
400
|
-
sizeLabel.textContent =
|
|
401
|
+
sizeLabel.textContent = L.size;
|
|
401
402
|
const sizeSelect = createElement('select', { class: 'an-input an-icon-option-select' });
|
|
402
403
|
[['', 'Inherit'], ['0.75em', '0.75em'], ['1em', '1em'], ['1.25em', '1.25em'], ['1.5em', '1.5em'], ['2em', '2em'], ['3em', '3em']].forEach(([v, t]) => {
|
|
403
404
|
const opt = createElement('option', { value: v });
|
|
@@ -408,30 +409,30 @@ export class IconDialog {
|
|
|
408
409
|
this._sizeSelect = sizeSelect;
|
|
409
410
|
|
|
410
411
|
const colorLabel = createElement('label', { class: 'an-label' });
|
|
411
|
-
colorLabel.textContent =
|
|
412
|
+
colorLabel.textContent = L.color;
|
|
412
413
|
const colorInput = createElement('input', { type: 'color', class: 'an-icon-color', value: '#000000' });
|
|
413
414
|
this._colorInput = colorInput;
|
|
414
415
|
|
|
415
416
|
const useColorLabel = createElement('label', { class: 'an-label an-label-inline an-icon-use-color' });
|
|
416
417
|
const useColorCb = createElement('input', { type: 'checkbox', checked: '' });
|
|
417
418
|
this._useColorCb = useColorCb;
|
|
418
|
-
useColorLabel.append(useColorCb, document.createTextNode(
|
|
419
|
+
useColorLabel.append(useColorCb, document.createTextNode(L.useColor));
|
|
419
420
|
|
|
420
421
|
optRow.append(styleLabel, styleSelect, sizeLabel, sizeSelect, colorLabel, colorInput, useColorLabel);
|
|
421
422
|
|
|
422
423
|
// Preview
|
|
423
424
|
const preview = createElement('div', { class: 'an-icon-preview' });
|
|
424
425
|
const previewHint = createElement('span', { class: 'an-icon-preview-hint' });
|
|
425
|
-
previewHint.textContent =
|
|
426
|
+
previewHint.textContent = L.selectHint;
|
|
426
427
|
preview.appendChild(previewHint);
|
|
427
428
|
this._preview = preview;
|
|
428
429
|
|
|
429
430
|
// Actions
|
|
430
431
|
const btnRow = createElement('div', { class: 'an-dialog-actions' });
|
|
431
432
|
const insertBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary', disabled: '' });
|
|
432
|
-
insertBtn.textContent =
|
|
433
|
+
insertBtn.textContent = L.insertBtn;
|
|
433
434
|
const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
434
|
-
cancelBtn.textContent =
|
|
435
|
+
cancelBtn.textContent = L.cancelBtn;
|
|
435
436
|
btnRow.append(insertBtn, cancelBtn);
|
|
436
437
|
this._insertBtn = insertBtn;
|
|
437
438
|
|
|
@@ -575,7 +576,18 @@ export class IconDialog {
|
|
|
575
576
|
|
|
576
577
|
// 3. Insert the <i> node directly — never via execCommand/insertHTML
|
|
577
578
|
// (execCommand leaves the caret inside the inserted element)
|
|
579
|
+
//
|
|
580
|
+
// E-1: When the range covers the entire content of a <td>/<th>,
|
|
581
|
+
// deleteContents() can drift the range endpoint outside the cell in some
|
|
582
|
+
// browsers. Save the cell reference first and re-anchor after deletion.
|
|
583
|
+
const _sc = range.startContainer;
|
|
584
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)
|
|
585
|
+
?.closest?.('td, th');
|
|
578
586
|
range.deleteContents();
|
|
587
|
+
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
588
|
+
range.setStart(_tdAnchor, 0);
|
|
589
|
+
range.collapse(true);
|
|
590
|
+
}
|
|
579
591
|
range.insertNode(iconEl);
|
|
580
592
|
|
|
581
593
|
// 4. Place cursor after the icon.
|
|
@@ -57,35 +57,36 @@ export class ImageDialog {
|
|
|
57
57
|
// ---------------------------------------------------------------------------
|
|
58
58
|
|
|
59
59
|
_buildDialog() {
|
|
60
|
+
const L = this.context.locale.imageDialog;
|
|
60
61
|
const overlay = createElement('div', {
|
|
61
62
|
class: 'an-dialog-overlay',
|
|
62
63
|
role: 'dialog',
|
|
63
64
|
'aria-modal': 'true',
|
|
64
|
-
'aria-label':
|
|
65
|
+
'aria-label': L.ariaLabel,
|
|
65
66
|
});
|
|
66
67
|
const box = createElement('div', { class: 'an-dialog-box' });
|
|
67
68
|
|
|
68
69
|
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
69
|
-
title.textContent =
|
|
70
|
+
title.textContent = L.title;
|
|
70
71
|
|
|
71
72
|
// URL tab
|
|
72
73
|
const urlLabel = createElement('label', { class: 'an-label' });
|
|
73
|
-
urlLabel.textContent =
|
|
74
|
+
urlLabel.textContent = L.imageUrl;
|
|
74
75
|
const urlInput = createElement('input', {
|
|
75
76
|
type: 'url',
|
|
76
77
|
class: 'an-input',
|
|
77
|
-
placeholder:
|
|
78
|
+
placeholder: L.urlPlaceholder,
|
|
78
79
|
autocomplete: 'off',
|
|
79
80
|
});
|
|
80
81
|
this._urlInput = urlInput;
|
|
81
82
|
|
|
82
83
|
// Alt text
|
|
83
84
|
const altLabel = createElement('label', { class: 'an-label' });
|
|
84
|
-
altLabel.textContent =
|
|
85
|
+
altLabel.textContent = L.altText;
|
|
85
86
|
const altInput = createElement('input', {
|
|
86
87
|
type: 'text',
|
|
87
88
|
class: 'an-input',
|
|
88
|
-
placeholder:
|
|
89
|
+
placeholder: L.altPlaceholder,
|
|
89
90
|
autocomplete: 'off',
|
|
90
91
|
});
|
|
91
92
|
this._altInput = altInput;
|
|
@@ -94,13 +95,13 @@ export class ImageDialog {
|
|
|
94
95
|
|
|
95
96
|
// Alignment
|
|
96
97
|
const alignLabel = createElement('label', { class: 'an-label' });
|
|
97
|
-
alignLabel.textContent =
|
|
98
|
+
alignLabel.textContent = L.alignment;
|
|
98
99
|
const alignRow = createElement('div', { class: 'an-align-row' });
|
|
99
100
|
const alignOptions = [
|
|
100
|
-
{ value: '', label:
|
|
101
|
-
{ value: 'left', label:
|
|
102
|
-
{ value: 'center', label:
|
|
103
|
-
{ value: 'right', label:
|
|
101
|
+
{ value: '', label: L.alignNone },
|
|
102
|
+
{ value: 'left', label: L.alignLeft },
|
|
103
|
+
{ value: 'center', label: L.alignCenter },
|
|
104
|
+
{ value: 'right', label: L.alignRight },
|
|
104
105
|
];
|
|
105
106
|
alignOptions.forEach(({ value, label }) => {
|
|
106
107
|
const radioId = `an-align-${value || 'none'}`;
|
|
@@ -114,11 +115,11 @@ export class ImageDialog {
|
|
|
114
115
|
box.append(alignLabel, alignRow);
|
|
115
116
|
if (this.options.allowImageUpload !== false) {
|
|
116
117
|
const fileLabel = createElement('label', { class: 'an-label' });
|
|
117
|
-
fileLabel.textContent =
|
|
118
|
+
fileLabel.textContent = L.uploadLabel;
|
|
118
119
|
const fileInput = createElement('input', {
|
|
119
120
|
type: 'file',
|
|
120
121
|
class: 'an-input',
|
|
121
|
-
accept: 'image
|
|
122
|
+
accept: 'image/jpeg,image/png,image/gif,image/webp,image/svg+xml,image/avif',
|
|
122
123
|
});
|
|
123
124
|
this._fileInput = fileInput;
|
|
124
125
|
// Hint line shown below the file input for format errors
|
|
@@ -132,9 +133,9 @@ export class ImageDialog {
|
|
|
132
133
|
// Buttons
|
|
133
134
|
const btnRow = createElement('div', { class: 'an-dialog-actions' });
|
|
134
135
|
const insertBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
|
|
135
|
-
insertBtn.textContent =
|
|
136
|
+
insertBtn.textContent = L.insertBtn;
|
|
136
137
|
const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
137
|
-
cancelBtn.textContent =
|
|
138
|
+
cancelBtn.textContent = L.cancelBtn;
|
|
138
139
|
btnRow.appendChild(insertBtn);
|
|
139
140
|
btnRow.appendChild(cancelBtn);
|
|
140
141
|
|
|
@@ -159,10 +160,14 @@ export class ImageDialog {
|
|
|
159
160
|
const file = this._fileInput && this._fileInput.files && this._fileInput.files[0];
|
|
160
161
|
if (!file || !file.type.startsWith('image/')) return;
|
|
161
162
|
|
|
162
|
-
// C2:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
// C2: Only allow web-displayable formats. TIFF, BMP, RAW and similar
|
|
164
|
+
// formats are not rendered by browsers — reject them with a clear message.
|
|
165
|
+
const SUPPORTED = new Set([
|
|
166
|
+
'image/jpeg', 'image/png', 'image/gif',
|
|
167
|
+
'image/webp', 'image/svg+xml', 'image/avif',
|
|
168
|
+
]);
|
|
169
|
+
if (!SUPPORTED.has(file.type)) {
|
|
170
|
+
const message = this.context.locale.errors.imageFormat(file.type);
|
|
166
171
|
if (this._fileHint) this._fileHint.textContent = message;
|
|
167
172
|
this.context.triggerEvent('imageError', { file, message });
|
|
168
173
|
this._fileInput.value = '';
|
|
@@ -173,7 +178,7 @@ export class ImageDialog {
|
|
|
173
178
|
// Validate file size (max 5 MB by default)
|
|
174
179
|
const maxSize = (this.options.maxImageSize || 5) * 1024 * 1024;
|
|
175
180
|
if (file.size > maxSize) {
|
|
176
|
-
const message =
|
|
181
|
+
const message = this.context.locale.errors.imageSize(this.options.maxImageSize || 5);
|
|
177
182
|
if (this._fileHint) this._fileHint.textContent = message;
|
|
178
183
|
console.warn('[AutumnNote] ImageDialog:', message);
|
|
179
184
|
this.context.triggerEvent('imageError', { file, message });
|
|
@@ -75,24 +75,25 @@ export class ImageTooltip {
|
|
|
75
75
|
// ---------------------------------------------------------------------------
|
|
76
76
|
|
|
77
77
|
_buildTooltip() {
|
|
78
|
+
const L = this.context.locale.tooltips.image;
|
|
78
79
|
const el = createElement('div', {
|
|
79
80
|
class: 'an-link-tooltip an-image-tooltip',
|
|
80
81
|
role: 'toolbar',
|
|
81
|
-
'aria-label':
|
|
82
|
+
'aria-label': L.ariaLabel,
|
|
82
83
|
});
|
|
83
84
|
el.style.display = 'none';
|
|
84
85
|
|
|
85
86
|
// Label showing "Image"
|
|
86
87
|
this._label = createElement('span', { class: 'an-link-tooltip-url' });
|
|
87
|
-
this._label.textContent =
|
|
88
|
+
this._label.textContent = L.label;
|
|
88
89
|
el.appendChild(this._label);
|
|
89
90
|
|
|
90
91
|
el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
|
|
91
92
|
|
|
92
|
-
this._floatLeftBtn = this._makeBtn(ICONS.floatLeft,
|
|
93
|
-
this._floatNoneBtn = this._makeBtn(ICONS.floatNone,
|
|
94
|
-
this._alignCenterBtn = this._makeBtn(ICONS.alignCenter,
|
|
95
|
-
this._floatRightBtn = this._makeBtn(ICONS.floatRight,
|
|
93
|
+
this._floatLeftBtn = this._makeBtn(ICONS.floatLeft, L.floatLeft, () => this._setFloat('left'));
|
|
94
|
+
this._floatNoneBtn = this._makeBtn(ICONS.floatNone, L.noFloat, () => this._setFloat(''));
|
|
95
|
+
this._alignCenterBtn = this._makeBtn(ICONS.alignCenter, L.alignCenter, () => this._setCenter());
|
|
96
|
+
this._floatRightBtn = this._makeBtn(ICONS.floatRight, L.floatRight, () => this._setFloat('right'));
|
|
96
97
|
|
|
97
98
|
el.appendChild(this._floatLeftBtn);
|
|
98
99
|
el.appendChild(this._floatNoneBtn);
|
|
@@ -101,28 +102,28 @@ export class ImageTooltip {
|
|
|
101
102
|
|
|
102
103
|
el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
|
|
103
104
|
|
|
104
|
-
this._originalBtn = this._makeBtn(ICONS.originalSize,
|
|
105
|
+
this._originalBtn = this._makeBtn(ICONS.originalSize, L.originalSize, () => this._resetSize());
|
|
105
106
|
|
|
106
107
|
el.appendChild(this._originalBtn);
|
|
107
108
|
|
|
108
109
|
el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
|
|
109
110
|
|
|
110
|
-
el.appendChild(this._makeBtn(ICONS.rotateLeft,
|
|
111
|
-
el.appendChild(this._makeBtn(ICONS.rotateRight,
|
|
111
|
+
el.appendChild(this._makeBtn(ICONS.rotateLeft, L.rotateLeft, () => this._rotate(-90)));
|
|
112
|
+
el.appendChild(this._makeBtn(ICONS.rotateRight, L.rotateRight, () => this._rotate(90)));
|
|
112
113
|
|
|
113
114
|
el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
|
|
114
115
|
|
|
115
|
-
this._cropBtn = this._makeBtn(ICONS.crop,
|
|
116
|
+
this._cropBtn = this._makeBtn(ICONS.crop, L.cropImage, () => this._crop());
|
|
116
117
|
el.appendChild(this._cropBtn);
|
|
117
118
|
|
|
118
119
|
el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
|
|
119
120
|
|
|
120
|
-
this._captionBtn = this._makeBtn(ICONS.caption,
|
|
121
|
+
this._captionBtn = this._makeBtn(ICONS.caption, L.addCaption, () => this._toggleCaption());
|
|
121
122
|
el.appendChild(this._captionBtn);
|
|
122
123
|
|
|
123
124
|
el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
|
|
124
125
|
|
|
125
|
-
this._deleteBtn = this._makeBtn(ICONS.deleteImg,
|
|
126
|
+
this._deleteBtn = this._makeBtn(ICONS.deleteImg, L.deleteImage, () => this._delete(), true);
|
|
126
127
|
|
|
127
128
|
el.appendChild(this._deleteBtn);
|
|
128
129
|
|
|
@@ -59,19 +59,20 @@ export class LinkDialog {
|
|
|
59
59
|
// ---------------------------------------------------------------------------
|
|
60
60
|
|
|
61
61
|
_buildDialog() {
|
|
62
|
-
const
|
|
62
|
+
const L = this.context.locale.linkDialog;
|
|
63
|
+
const overlay = createElement('div', { class: 'an-dialog-overlay', role: 'dialog', 'aria-modal': 'true', 'aria-label': L.ariaLabel });
|
|
63
64
|
const box = createElement('div', { class: 'an-dialog-box' });
|
|
64
65
|
|
|
65
66
|
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
66
|
-
title.textContent =
|
|
67
|
+
title.textContent = L.title;
|
|
67
68
|
|
|
68
69
|
// URL field
|
|
69
70
|
const urlLabel = createElement('label', { class: 'an-label' });
|
|
70
|
-
urlLabel.textContent =
|
|
71
|
+
urlLabel.textContent = L.url;
|
|
71
72
|
const urlInput = createElement('input', {
|
|
72
73
|
type: 'url',
|
|
73
74
|
class: 'an-input',
|
|
74
|
-
placeholder:
|
|
75
|
+
placeholder: L.urlPlaceholder,
|
|
75
76
|
id: 'an-link-url',
|
|
76
77
|
name: 'url',
|
|
77
78
|
autocomplete: 'off',
|
|
@@ -80,11 +81,11 @@ export class LinkDialog {
|
|
|
80
81
|
|
|
81
82
|
// Text field
|
|
82
83
|
const textLabel = createElement('label', { class: 'an-label' });
|
|
83
|
-
textLabel.textContent =
|
|
84
|
+
textLabel.textContent = L.displayText;
|
|
84
85
|
const textInput = createElement('input', {
|
|
85
86
|
type: 'text',
|
|
86
87
|
class: 'an-input',
|
|
87
|
-
placeholder:
|
|
88
|
+
placeholder: L.textPlaceholder,
|
|
88
89
|
id: 'an-link-text',
|
|
89
90
|
name: 'linkText',
|
|
90
91
|
autocomplete: 'off',
|
|
@@ -100,14 +101,14 @@ export class LinkDialog {
|
|
|
100
101
|
});
|
|
101
102
|
this._tabCheckbox = tabCheckbox;
|
|
102
103
|
tabLabel.appendChild(tabCheckbox);
|
|
103
|
-
tabLabel.appendChild(document.createTextNode('
|
|
104
|
+
tabLabel.appendChild(document.createTextNode(' ' + L.openInNewTab));
|
|
104
105
|
|
|
105
106
|
// Buttons
|
|
106
107
|
const btnRow = createElement('div', { class: 'an-dialog-actions' });
|
|
107
108
|
const insertBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
|
|
108
|
-
insertBtn.textContent =
|
|
109
|
+
insertBtn.textContent = L.insertBtn;
|
|
109
110
|
const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
110
|
-
cancelBtn.textContent =
|
|
111
|
+
cancelBtn.textContent = L.cancelBtn;
|
|
111
112
|
btnRow.appendChild(insertBtn);
|
|
112
113
|
btnRow.appendChild(cancelBtn);
|
|
113
114
|
|
|
@@ -63,7 +63,8 @@ export class LinkTooltip {
|
|
|
63
63
|
// ---------------------------------------------------------------------------
|
|
64
64
|
|
|
65
65
|
_buildTooltip() {
|
|
66
|
-
const
|
|
66
|
+
const L = this.context.locale.tooltips.link;
|
|
67
|
+
const el = createElement('div', { class: 'an-link-tooltip', role: 'toolbar', 'aria-label': L.ariaLabel });
|
|
67
68
|
el.style.display = 'none';
|
|
68
69
|
|
|
69
70
|
// URL preview text (truncated)
|
|
@@ -74,10 +75,10 @@ export class LinkTooltip {
|
|
|
74
75
|
el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
|
|
75
76
|
|
|
76
77
|
// Action buttons
|
|
77
|
-
this._openBtn = this._makeBtn(ICONS.open,
|
|
78
|
-
this._copyBtn = this._makeBtn(ICONS.copy,
|
|
79
|
-
this._editBtn = this._makeBtn(ICONS.edit,
|
|
80
|
-
this._unlinkBtn = this._makeBtn(ICONS.unlink,
|
|
78
|
+
this._openBtn = this._makeBtn(ICONS.open, L.openLink, () => this._openLink());
|
|
79
|
+
this._copyBtn = this._makeBtn(ICONS.copy, L.copyUrl, () => this._copyLink());
|
|
80
|
+
this._editBtn = this._makeBtn(ICONS.edit, L.editLink, () => this._editLink());
|
|
81
|
+
this._unlinkBtn = this._makeBtn(ICONS.unlink, L.removeLink, () => this._unlink());
|
|
81
82
|
|
|
82
83
|
el.appendChild(this._openBtn);
|
|
83
84
|
el.appendChild(this._copyBtn);
|
|
@@ -39,7 +39,12 @@ export class Placeholder {
|
|
|
39
39
|
_update() {
|
|
40
40
|
const editable = this.context.layoutInfo.editable;
|
|
41
41
|
const isFocused = document.activeElement === editable;
|
|
42
|
-
|
|
42
|
+
// Strip ZWS (\u200B) cursor anchors used by checklist/icon insertion in
|
|
43
|
+
// addition to regular whitespace before deciding if the editor is empty.
|
|
44
|
+
// Without this, a freshly-created checklist item or icon leaves a ZWS in
|
|
45
|
+
// the DOM that causes the placeholder to overlap real content (A-1).
|
|
46
|
+
const hasText = editable.textContent.replace(/\u200B/g, '').trim().length > 0;
|
|
47
|
+
const isEmpty = !hasText &&
|
|
43
48
|
!editable.querySelector('img, table, hr, .an-video-wrapper');
|
|
44
49
|
editable.classList.toggle('an-placeholder', isEmpty && !isFocused);
|
|
45
50
|
}
|
|
@@ -93,7 +93,7 @@ export class ShortcutsDialog {
|
|
|
93
93
|
class: 'an-dialog-overlay',
|
|
94
94
|
role: 'dialog',
|
|
95
95
|
'aria-modal': 'true',
|
|
96
|
-
'aria-label':
|
|
96
|
+
'aria-label': this.context.locale.shortcutsDialog.ariaLabel,
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
const box = createElement('div', { class: 'an-dialog-box an-shortcuts-box' });
|
|
@@ -101,18 +101,19 @@ export class ShortcutsDialog {
|
|
|
101
101
|
// Title row with close button
|
|
102
102
|
const titleRow = createElement('div', { class: 'an-icon-title-row' });
|
|
103
103
|
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
104
|
-
title.textContent =
|
|
104
|
+
title.textContent = this.context.locale.shortcutsDialog.title;
|
|
105
105
|
const closeBtn = createElement('button', {
|
|
106
106
|
type: 'button',
|
|
107
107
|
class: 'an-icon-close',
|
|
108
|
-
'aria-label':
|
|
108
|
+
'aria-label': this.context.locale.shortcutsDialog.close,
|
|
109
109
|
});
|
|
110
110
|
closeBtn.textContent = '×';
|
|
111
111
|
this._closeBtn = closeBtn;
|
|
112
112
|
titleRow.append(title, closeBtn);
|
|
113
113
|
box.appendChild(titleRow);
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
const shortcuts = this.context.locale.shortcutsDialog.shortcuts || SHORTCUTS;
|
|
116
|
+
shortcuts.forEach(({ category, items }) => {
|
|
116
117
|
const catEl = createElement('div', { class: 'an-shortcuts-cat' });
|
|
117
118
|
catEl.textContent = category;
|
|
118
119
|
box.appendChild(catEl);
|
|
@@ -82,7 +82,7 @@ export class Statusbar {
|
|
|
82
82
|
if (this.options.resizable !== false) {
|
|
83
83
|
const handle = createElement('div', {
|
|
84
84
|
class: 'an-resize-handle',
|
|
85
|
-
title:
|
|
85
|
+
title: this.context.locale.statusbar.resizeHandle,
|
|
86
86
|
'aria-hidden': 'true',
|
|
87
87
|
});
|
|
88
88
|
this._bindResize(handle);
|
|
@@ -220,12 +220,13 @@ export class Statusbar {
|
|
|
220
220
|
const maxWords = this.options.maxWords || 0;
|
|
221
221
|
const maxChars = this.options.maxChars || 0;
|
|
222
222
|
|
|
223
|
+
const LS = this.context.locale.statusbar;
|
|
223
224
|
this._wordCountEl.textContent = maxWords
|
|
224
|
-
?
|
|
225
|
-
:
|
|
225
|
+
? LS.wordsLimit(words, maxWords)
|
|
226
|
+
: LS.words(words);
|
|
226
227
|
this._charCountEl.textContent = maxChars
|
|
227
|
-
?
|
|
228
|
-
:
|
|
228
|
+
? LS.charsLimit(chars, maxChars)
|
|
229
|
+
: LS.chars(chars);
|
|
229
230
|
|
|
230
231
|
// Apply warning / exceeded styles
|
|
231
232
|
_applyLimitClass(this._wordCountEl, words, maxWords);
|