autumnnote 1.3.0 → 1.4.1
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 +68 -12
- package/dist/autumnnote.es.js +126 -4
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +125 -3
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/js/Context.js +64 -0
- package/src/js/index.js +42 -2
- package/src/js/module/Buttons.js +37 -0
- package/src/js/module/Toolbar.js +33 -2
- package/types/index.d.ts +54 -1
package/README.md
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
[](#)
|
|
13
13
|
[](types/index.d.ts)
|
|
14
14
|
|
|
15
|
-
A modern
|
|
15
|
+
A modern WYSIWYG rich-text editor built with vanilla JavaScript (ES2022+) — no jQuery, no heavy dependencies.
|
|
16
16
|
|
|
17
|
-
>
|
|
17
|
+
> **Fast. Lightweight. Reliable. Efficient.**
|
|
18
18
|
|
|
19
|
-
[Live Demo](https://cmm-cmm.github.io/Autumn-Note/)
|
|
19
|
+
[Live Demo](https://cmm-cmm.github.io/Autumn-Note/) · [Docs](https://cmm-cmm.github.io/Autumn-Note/docs.html) · [Playground](https://cmm-cmm.github.io/Autumn-Note/playground.html)
|
|
20
20
|
|
|
21
21
|
<p align="center"><img src="demo/Screenshot.png" alt="AutumnNote Screenshot"/></p>
|
|
22
22
|
|
|
@@ -27,14 +27,15 @@ A modern, lightweight WYSIWYG rich-text editor built with vanilla JavaScript (ES
|
|
|
27
27
|
1. [Features](#features)
|
|
28
28
|
2. [Installation](#installation)
|
|
29
29
|
3. [Quick Start](#quick-start)
|
|
30
|
-
4. [API](#api)
|
|
31
|
-
5. [
|
|
32
|
-
6. [
|
|
33
|
-
7. [
|
|
34
|
-
8. [
|
|
35
|
-
9. [
|
|
36
|
-
10. [
|
|
37
|
-
11. [
|
|
30
|
+
4. [Plugin API](#plugin-api)
|
|
31
|
+
5. [API](#api)
|
|
32
|
+
6. [Options](#options)
|
|
33
|
+
7. [Toolbar Customisation](#toolbar-customisation)
|
|
34
|
+
8. [Keyboard Shortcuts](#keyboard-shortcuts)
|
|
35
|
+
9. [Mentions](#mentions)
|
|
36
|
+
10. [Project Structure](#project-structure)
|
|
37
|
+
11. [Comparison](#comparison)
|
|
38
|
+
12. [License](#license)
|
|
38
39
|
|
|
39
40
|
---
|
|
40
41
|
|
|
@@ -109,7 +110,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
|
|
|
109
110
|
- **No jQuery** — pure vanilla ES2022, zero runtime dependencies
|
|
110
111
|
- **Bootstrap friendly** — optional Bootstrap 4/5 styling (`useBootstrap: true`)
|
|
111
112
|
- **FontAwesome ready** — auto-detects FA on the page; falls back to built-in SVG icons
|
|
112
|
-
- **Plugin
|
|
113
|
+
- **Plugin API** — first-class plugin system: `AutumnNote.use(plugin)`, `context.getPlugin(name)`, global button registry (`registerButton`), per-instance installation, `AsnPlugin<T>` TypeScript interface
|
|
113
114
|
- **Tree-shakeable** — ES module build; all core utilities individually exported
|
|
114
115
|
- **TypeScript definitions** — bundled `types/index.d.ts` with full JSDoc coverage
|
|
115
116
|
- **@mention autocomplete** — type `@` (or any custom trigger) to open a floating dropdown backed by a user-supplied `onSearch` function; inserts a non-editable mention chip; customisable chip HTML via `onInsert`
|
|
@@ -269,6 +270,61 @@ const editor = AutumnNote.create('#my-editor', {
|
|
|
269
270
|
|
|
270
271
|
---
|
|
271
272
|
|
|
273
|
+
## Plugin API
|
|
274
|
+
|
|
275
|
+
Plugins package editor extensions — custom modules, toolbar buttons, and event handlers — into a reusable, distributable object.
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
import AutumnNote from 'autumnnote';
|
|
279
|
+
|
|
280
|
+
const WordCountPlugin = {
|
|
281
|
+
name: 'word-count',
|
|
282
|
+
version: '1.0.0',
|
|
283
|
+
// Buttons registered BEFORE create() — usable by name in toolbar config
|
|
284
|
+
buttons: [{
|
|
285
|
+
name: 'wordCountBtn',
|
|
286
|
+
icon: 'hashtag',
|
|
287
|
+
tooltip: 'Word count',
|
|
288
|
+
action: (ctx) => alert(`${ctx.getWordCount()} words`),
|
|
289
|
+
}],
|
|
290
|
+
// Called after all built-in modules initialise
|
|
291
|
+
install(ctx, options) {
|
|
292
|
+
ctx.on('change', () => console.log('words:', ctx.getWordCount()));
|
|
293
|
+
return { getMax: () => options.maxWords };
|
|
294
|
+
},
|
|
295
|
+
uninstall(ctx) { /* cleanup */ },
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// Global — applied to every future editor instance
|
|
299
|
+
AutumnNote.use(WordCountPlugin, { maxWords: 500 });
|
|
300
|
+
|
|
301
|
+
const editor = AutumnNote.create('#editor', {
|
|
302
|
+
toolbar: [['bold', 'italic', 'wordCountBtn']], // 'wordCountBtn' resolved from registry
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
editor.getPlugin('word-count').getMax(); // → 500
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Per-instance installation:**
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
const editor = AutumnNote.create('#editor');
|
|
312
|
+
editor.use(WordCountPlugin, { maxWords: 200 });
|
|
313
|
+
editor.invoke('toolbar.rebuild'); // re-render toolbar with new buttons
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
| Method | Description |
|
|
317
|
+
|---|---|
|
|
318
|
+
| `AutumnNote.use(plugin, opts?)` | Install globally. Buttons registered immediately; `install()` called after modules init. |
|
|
319
|
+
| `AutumnNote.hasPlugin(name)` | Returns `true` if plugin registered globally. |
|
|
320
|
+
| `AutumnNote.registerButton(def)` | Register a single button globally by name. |
|
|
321
|
+
| `context.use(plugin, opts?)` | Install on this instance only. |
|
|
322
|
+
| `context.getPlugin<T>(name)` | Returns the public API from `plugin.install()`. |
|
|
323
|
+
|
|
324
|
+
See the [full Plugin API docs →](https://cmm-cmm.github.io/Autumn-Note/docs.html#plugin-api)
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
272
328
|
## API
|
|
273
329
|
|
|
274
330
|
### Factory
|
package/dist/autumnnote.es.js
CHANGED
|
@@ -1089,6 +1089,34 @@ function btn(name, icon, tooltip, action, isActive, isDisabled) {
|
|
|
1089
1089
|
isDisabled
|
|
1090
1090
|
};
|
|
1091
1091
|
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Global registry for custom buttons registered via AutumnNote.registerButton()
|
|
1094
|
+
* or via a plugin's `buttons` array. Toolbar resolves string names from here.
|
|
1095
|
+
* @type {Map<string, object>}
|
|
1096
|
+
*/
|
|
1097
|
+
var _buttonRegistry = /* @__PURE__ */ new Map();
|
|
1098
|
+
/**
|
|
1099
|
+
* Registers a button definition in the global registry so it can be referenced
|
|
1100
|
+
* by string name in toolbar configuration: `toolbar: [['myBtn', boldBtn]]`.
|
|
1101
|
+
* @param {object} btnDef - Any ToolbarItemDef-compatible object with a `name` string.
|
|
1102
|
+
*/
|
|
1103
|
+
function registerButton(btnDef) {
|
|
1104
|
+
if (!btnDef || typeof btnDef.name !== "string") {
|
|
1105
|
+
console.warn("[AutumnNote] registerButton: btnDef must have a string `name` property.");
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (_buttonRegistry.has(btnDef.name)) console.warn(`[AutumnNote] registerButton: overwriting existing button "${btnDef.name}".`);
|
|
1109
|
+
_buttonRegistry.set(btnDef.name, btnDef);
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Looks up a button definition by name from the global registry.
|
|
1113
|
+
* Returns undefined when not found.
|
|
1114
|
+
* @param {string} name
|
|
1115
|
+
* @returns {object|undefined}
|
|
1116
|
+
*/
|
|
1117
|
+
function getButton(name) {
|
|
1118
|
+
return _buttonRegistry.get(name);
|
|
1119
|
+
}
|
|
1092
1120
|
var boldBtn = btn("bold", "bold", "Bold (Ctrl+B)", () => bold(), () => document.queryCommandState("bold"));
|
|
1093
1121
|
var italicBtn = btn("italic", "italic", "Italic (Ctrl+I)", () => italic(), () => document.queryCommandState("italic"));
|
|
1094
1122
|
var underlineBtn = btn("underline", "underline", "Underline (Ctrl+U)", () => underline(), () => {
|
|
@@ -5797,6 +5825,8 @@ var Editor = class {
|
|
|
5797
5825
|
* Toolbar.js - Builds and manages the editor toolbar UI
|
|
5798
5826
|
* Inspired by Summernote's Toolbar module — rewritten without jQuery
|
|
5799
5827
|
*/
|
|
5828
|
+
/** Resolve a toolbar item: string → registry lookup, object → pass-through. */
|
|
5829
|
+
var _resolveBtn = (item) => typeof item === "string" ? getButton(item) : item;
|
|
5800
5830
|
var _faPageLevelReady = null;
|
|
5801
5831
|
var _S$1 = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
|
|
5802
5832
|
var _svgWrap = (paths) => `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" ${_S$1} style="display:block">${paths}</svg>`;
|
|
@@ -5893,7 +5923,7 @@ var Toolbar = class {
|
|
|
5893
5923
|
this.el = createElement("div", { class: "an-toolbar" });
|
|
5894
5924
|
this._faReady = this._detectFontAwesome();
|
|
5895
5925
|
this._buildButtons();
|
|
5896
|
-
this._btnMap = new Map((this.options.toolbar || []).flat().map((b) => [b.name, b]));
|
|
5926
|
+
this._btnMap = new Map((this.options.toolbar || []).flat().map(_resolveBtn).filter(Boolean).map((b) => [b.name, b]));
|
|
5897
5927
|
return this;
|
|
5898
5928
|
}
|
|
5899
5929
|
destroy() {
|
|
@@ -5909,7 +5939,12 @@ var Toolbar = class {
|
|
|
5909
5939
|
const fragment = document.createDocumentFragment();
|
|
5910
5940
|
toolbar.forEach((group) => {
|
|
5911
5941
|
const groupEl = createElement("div", { class: "an-btn-group" });
|
|
5912
|
-
group.forEach((
|
|
5942
|
+
group.forEach((item) => {
|
|
5943
|
+
const btnDef = _resolveBtn(item);
|
|
5944
|
+
if (!btnDef) {
|
|
5945
|
+
console.warn(`[AutumnNote] Toolbar: button "${item}" not found in registry. Skipped.`);
|
|
5946
|
+
return;
|
|
5947
|
+
}
|
|
5913
5948
|
let el;
|
|
5914
5949
|
if (btnDef.type === "select") el = this._createSelect(btnDef);
|
|
5915
5950
|
else if (btnDef.type === "grid") el = this._createGridPicker(btnDef);
|
|
@@ -6329,6 +6364,24 @@ var Toolbar = class {
|
|
|
6329
6364
|
hide() {
|
|
6330
6365
|
if (this.el) this.el.style.display = "none";
|
|
6331
6366
|
}
|
|
6367
|
+
/**
|
|
6368
|
+
* Tears down and re-renders the toolbar in-place.
|
|
6369
|
+
* Call after registering new buttons post-create via context.use(plugin)
|
|
6370
|
+
* or AutumnNote.registerButton() to make them appear in the toolbar.
|
|
6371
|
+
*/
|
|
6372
|
+
rebuild() {
|
|
6373
|
+
if (this._refreshRaf) {
|
|
6374
|
+
cancelAnimationFrame(this._refreshRaf);
|
|
6375
|
+
this._refreshRaf = null;
|
|
6376
|
+
}
|
|
6377
|
+
this._disposers.forEach((d) => d());
|
|
6378
|
+
this._disposers = [];
|
|
6379
|
+
if (this.el) this.el.innerHTML = "";
|
|
6380
|
+
this._faReady = this._detectFontAwesome();
|
|
6381
|
+
this._buildButtons();
|
|
6382
|
+
this._btnMap = new Map((this.options.toolbar || []).flat().map(_resolveBtn).filter(Boolean).map((b) => [b.name, b]));
|
|
6383
|
+
this.refresh();
|
|
6384
|
+
}
|
|
6332
6385
|
};
|
|
6333
6386
|
//#endregion
|
|
6334
6387
|
//#region src/js/module/Statusbar.js
|
|
@@ -15776,6 +15829,8 @@ var Mention = class {
|
|
|
15776
15829
|
*/
|
|
15777
15830
|
/** Module registry shared across all Context instances (populated via AutumnNote.registerModule). */
|
|
15778
15831
|
var _customModules = /* @__PURE__ */ new Map();
|
|
15832
|
+
/** Global plugin registry (populated via AutumnNote.use()). Applied to every new Context. */
|
|
15833
|
+
var _globalPlugins = /* @__PURE__ */ new Map();
|
|
15779
15834
|
var Context = class {
|
|
15780
15835
|
/**
|
|
15781
15836
|
* @param {HTMLElement} targetEl - The element to replace with the editor
|
|
@@ -15792,6 +15847,8 @@ var Context = class {
|
|
|
15792
15847
|
this._listeners = /* @__PURE__ */ new Map();
|
|
15793
15848
|
/** @type {Map<string, object>} */
|
|
15794
15849
|
this._modules = /* @__PURE__ */ new Map();
|
|
15850
|
+
/** @type {Map<string, { plugin: object, publicApi: * }>} */
|
|
15851
|
+
this._plugins = /* @__PURE__ */ new Map();
|
|
15795
15852
|
this._disposers = [];
|
|
15796
15853
|
this._alive = false;
|
|
15797
15854
|
}
|
|
@@ -15814,6 +15871,7 @@ var Context = class {
|
|
|
15814
15871
|
if (this.options.focus) editable.focus();
|
|
15815
15872
|
this._alive = true;
|
|
15816
15873
|
this.invoke("toolbar.refresh");
|
|
15874
|
+
this._applyGlobalPlugins();
|
|
15817
15875
|
if (typeof this.options.onInit === "function") this.options.onInit(this);
|
|
15818
15876
|
return this;
|
|
15819
15877
|
}
|
|
@@ -15865,6 +15923,46 @@ var Context = class {
|
|
|
15865
15923
|
this._modules.set(name, instance);
|
|
15866
15924
|
return this;
|
|
15867
15925
|
}
|
|
15926
|
+
/**
|
|
15927
|
+
* Installs a plugin on this editor instance.
|
|
15928
|
+
* If called after create(), buttons are registered immediately but the toolbar
|
|
15929
|
+
* must be rebuilt via ctx.invoke('toolbar.rebuild') to render new buttons.
|
|
15930
|
+
* @param {object} plugin - { name, version?, buttons?, install?, uninstall? }
|
|
15931
|
+
* @param {object} [options] - Forwarded to plugin.install(context, options)
|
|
15932
|
+
* @returns {this}
|
|
15933
|
+
*/
|
|
15934
|
+
use(plugin, options = {}) {
|
|
15935
|
+
if (Array.isArray(plugin.buttons)) plugin.buttons.forEach((b) => registerButton(b));
|
|
15936
|
+
this._installPlugin(plugin, options);
|
|
15937
|
+
return this;
|
|
15938
|
+
}
|
|
15939
|
+
/**
|
|
15940
|
+
* Returns the public API returned by plugin.install(), or null.
|
|
15941
|
+
* @param {string} name
|
|
15942
|
+
* @returns {*}
|
|
15943
|
+
*/
|
|
15944
|
+
getPlugin(name) {
|
|
15945
|
+
return this._plugins.get(name)?.publicApi ?? null;
|
|
15946
|
+
}
|
|
15947
|
+
_installPlugin(plugin, pluginOptions = {}) {
|
|
15948
|
+
const { name } = plugin;
|
|
15949
|
+
if (!name || typeof name !== "string") {
|
|
15950
|
+
console.warn("[AutumnNote] Plugin must have a string `name` property.");
|
|
15951
|
+
return;
|
|
15952
|
+
}
|
|
15953
|
+
if (this._plugins.has(name)) {
|
|
15954
|
+
console.warn(`[AutumnNote] Plugin "${name}" already installed on this instance. Skipping.`);
|
|
15955
|
+
return;
|
|
15956
|
+
}
|
|
15957
|
+
const publicApi = typeof plugin.install === "function" ? plugin.install(this, pluginOptions) ?? null : null;
|
|
15958
|
+
this._plugins.set(name, {
|
|
15959
|
+
plugin,
|
|
15960
|
+
publicApi
|
|
15961
|
+
});
|
|
15962
|
+
}
|
|
15963
|
+
_applyGlobalPlugins() {
|
|
15964
|
+
for (const { plugin, options } of _globalPlugins.values()) this._installPlugin(plugin, options);
|
|
15965
|
+
}
|
|
15868
15966
|
_bindEditorEvents(editable) {
|
|
15869
15967
|
const d0 = on(editable, "input", () => this._syncToTarget());
|
|
15870
15968
|
const d1 = on(editable, "focus", () => {
|
|
@@ -16115,6 +16213,10 @@ var Context = class {
|
|
|
16115
16213
|
if (typeof module.destroy === "function") module.destroy();
|
|
16116
16214
|
});
|
|
16117
16215
|
this._modules.clear();
|
|
16216
|
+
for (const { plugin } of this._plugins.values()) if (typeof plugin.uninstall === "function") try {
|
|
16217
|
+
plugin.uninstall(this);
|
|
16218
|
+
} catch (_) {}
|
|
16219
|
+
this._plugins.clear();
|
|
16118
16220
|
this._disposers.forEach((d) => d());
|
|
16119
16221
|
this._disposers = [];
|
|
16120
16222
|
const container = this.layoutInfo.container;
|
|
@@ -16301,7 +16403,27 @@ var AutumnNote = {
|
|
|
16301
16403
|
registerModule(name, ModuleClass) {
|
|
16302
16404
|
_customModules.set(name, ModuleClass);
|
|
16303
16405
|
},
|
|
16304
|
-
|
|
16406
|
+
use(plugin, options = {}) {
|
|
16407
|
+
if (!plugin || typeof plugin.name !== "string") throw new TypeError("[AutumnNote] AutumnNote.use: plugin must have a string `name` property.");
|
|
16408
|
+
if (_globalPlugins.has(plugin.name)) {
|
|
16409
|
+
console.warn(`[AutumnNote] Plugin "${plugin.name}" already registered globally. Skipping.`);
|
|
16410
|
+
return this;
|
|
16411
|
+
}
|
|
16412
|
+
if (Array.isArray(plugin.buttons)) plugin.buttons.forEach((b) => registerButton(b));
|
|
16413
|
+
_globalPlugins.set(plugin.name, {
|
|
16414
|
+
plugin,
|
|
16415
|
+
options
|
|
16416
|
+
});
|
|
16417
|
+
return this;
|
|
16418
|
+
},
|
|
16419
|
+
hasPlugin(name) {
|
|
16420
|
+
return _globalPlugins.has(name);
|
|
16421
|
+
},
|
|
16422
|
+
registerButton(btnDef) {
|
|
16423
|
+
registerButton(btnDef);
|
|
16424
|
+
return this;
|
|
16425
|
+
},
|
|
16426
|
+
version: "1.4.1"
|
|
16305
16427
|
};
|
|
16306
16428
|
/**
|
|
16307
16429
|
* @param {string|Element|NodeList|Element[]} selector
|
|
@@ -16314,6 +16436,6 @@ function resolveElements(selector) {
|
|
|
16314
16436
|
return [];
|
|
16315
16437
|
}
|
|
16316
16438
|
//#endregion
|
|
16317
|
-
export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, locales, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, remove, removeFormatBtn, resolveLocale, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
|
|
16439
|
+
export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, _buttonRegistry, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, getButton, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, locales, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, registerButton, remove, removeFormatBtn, resolveLocale, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
|
|
16318
16440
|
|
|
16319
16441
|
//# sourceMappingURL=autumnnote.es.js.map
|