autumnnote 1.0.0

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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +874 -0
  3. package/dist/autumnnote.css +1 -0
  4. package/dist/autumnnote.es.js +5888 -0
  5. package/dist/autumnnote.es.js.map +1 -0
  6. package/dist/autumnnote.umd.js +74 -0
  7. package/dist/autumnnote.umd.js.map +1 -0
  8. package/package.json +55 -0
  9. package/src/js/Context.js +497 -0
  10. package/src/js/core/dom.js +315 -0
  11. package/src/js/core/env.js +25 -0
  12. package/src/js/core/func.js +153 -0
  13. package/src/js/core/key.js +66 -0
  14. package/src/js/core/lists.js +121 -0
  15. package/src/js/core/markdown.js +294 -0
  16. package/src/js/core/range.js +194 -0
  17. package/src/js/core/sanitise.js +78 -0
  18. package/src/js/editing/History.js +205 -0
  19. package/src/js/editing/Style.js +329 -0
  20. package/src/js/editing/Table.js +59 -0
  21. package/src/js/editing/Typing.js +142 -0
  22. package/src/js/index.js +126 -0
  23. package/src/js/module/Buttons.js +300 -0
  24. package/src/js/module/Clipboard.js +460 -0
  25. package/src/js/module/CodeTooltip.js +428 -0
  26. package/src/js/module/Codeview.js +122 -0
  27. package/src/js/module/ContextMenu.js +470 -0
  28. package/src/js/module/Editor.js +528 -0
  29. package/src/js/module/EmojiDialog.js +726 -0
  30. package/src/js/module/FindReplace.js +440 -0
  31. package/src/js/module/Fullscreen.js +80 -0
  32. package/src/js/module/IconDialog.js +620 -0
  33. package/src/js/module/ImageDialog.js +208 -0
  34. package/src/js/module/ImageResizer.js +216 -0
  35. package/src/js/module/ImageTooltip.js +286 -0
  36. package/src/js/module/LinkDialog.js +204 -0
  37. package/src/js/module/LinkTooltip.js +242 -0
  38. package/src/js/module/Placeholder.js +44 -0
  39. package/src/js/module/ShortcutsDialog.js +141 -0
  40. package/src/js/module/Statusbar.js +238 -0
  41. package/src/js/module/TableTooltip.js +568 -0
  42. package/src/js/module/Toolbar.js +562 -0
  43. package/src/js/module/VideoDialog.js +263 -0
  44. package/src/js/module/VideoResizer.js +227 -0
  45. package/src/js/module/VideoTooltip.js +252 -0
  46. package/src/js/renderer.js +107 -0
  47. package/src/js/settings.js +134 -0
  48. package/src/styles/_variables.scss +48 -0
  49. package/src/styles/autumnnote.scss +1740 -0
  50. package/types/index.d.ts +324 -0
package/README.md ADDED
@@ -0,0 +1,874 @@
1
+ # AutumnNote
2
+
3
+ [![Version](https://img.shields.io/badge/version-1.0.0-blue)](#)
4
+ [![CI](https://github.com/cmm-cmm/Autumn-Note/actions/workflows/pages.yml/badge.svg)](https://github.com/cmm-cmm/Autumn-Note/actions/workflows/pages.yml)
5
+ [![JavaScript](https://img.shields.io/badge/JavaScript-ES2022-F7DF1E?logo=javascript&logoColor=black)](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
6
+ [![Build](https://img.shields.io/badge/Build-Vite-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/)
7
+ [![Test](https://img.shields.io/badge/Test-Vitest-6E9F18?logo=vitest&logoColor=white)](https://vitest.dev/)
8
+ [![License](https://img.shields.io/badge/License-MIT-brightgreen)](https://opensource.org/licenses/MIT)
9
+ [![jQuery](https://img.shields.io/badge/jQuery-free-lightgrey)](#)
10
+ [![FontAwesome](https://img.shields.io/badge/FontAwesome-6.5.2-528DD7?logo=fontawesome&logoColor=white)](https://fontawesome.com/)
11
+
12
+ A modern, lightweight WYSIWYG rich-text editor with vanilla JavaScript (ES2022+), no jQuery dependency.
13
+
14
+ > ✍️ *Write rich text. No dependencies. No drama.*
15
+
16
+ 🔗 **[Live Demo](https://cmm-cmm.github.io/Autumn-Note/)**
17
+
18
+ ![AutumnNote Screenshot](demo/Screenshot.png)
19
+
20
+ ---
21
+
22
+ ## Features
23
+
24
+ ### Editing
25
+ - **Text formatting** — bold, italic, underline, strikethrough, superscript, subscript
26
+ - **Paragraph styles** — Normal, H1–H6, Blockquote, Code block
27
+ - **Font family** — customisable dropdown (10 families by default)
28
+ - **Line height** — dropdown from 1.0 to 3.0
29
+ - **Text & highlight colour** — native colour picker with last-used colour memory
30
+ - **Alignment** — left, center, right, justify
31
+ - **Lists** — unordered and ordered, with indent / outdent; Tab/Shift+Tab in list context
32
+ - **Undo / redo** — built-in history stack (100 levels, `Ctrl+Z` / `Ctrl+Y`)
33
+ - **Tab key** — configurable spaces-per-tab; smart list indentation inside `<li>`
34
+
35
+ ### Insert
36
+ - **Horizontal rule** — inserts an `<hr>` at the current caret position
37
+ - **Link dialog** — URL, display text (auto-filled from selection), "Open in new tab" checkbox; edits existing links when caret is inside an `<a>`
38
+ - **Image dialog** — insert by URL with alt text, or file upload (base64 embed); enforces `maxImageSize`; supports custom `onImageUpload` handler for server-side upload
39
+ - **Video dialog** — paste a YouTube watch/short URL, Vimeo URL, or direct `.mp4 / .webm / .ogg` URL; configurable width; renders as responsive `<iframe>` or `<video>`
40
+ - **Table** — interactive grid picker (up to 10×10); floating tooltip on table click for full row/column/cell management (see [Table tooltip](#table-tooltip))
41
+ - **Emoji picker** — ~380 Unicode emoji across 7 categories (Smileys, People, Animals, Food, Travel, Objects, Symbols); keyword search; click to insert instantly as a plain text character (UTF-8 / utf8mb4 safe)
42
+ - **FA Icon picker** — FontAwesome 6 Free Solid icons across 8 categories (Popular, Interface, Navigation, Media, Communication, Files, People, Objects); keyword search; configurable style (Solid / Regular / Light), size, and colour; inserts as `<i>` element; auto-injects FA CDN if not detected on the page
43
+
44
+ ### Inline tooltips
45
+ Floating toolbars appear automatically when the user clicks on an editable element:
46
+
47
+ | Element | Actions |
48
+ |---|---|
49
+ | **Link** | Open in new tab, Edit (reopens dialog), Unlink |
50
+ | **Image** | Edit alt/URL (reopens dialog), Delete |
51
+ | **Video** | Edit (reopens dialog), Delete |
52
+ | **Table cell** | Row above/below, Delete row, Column left/right, Delete column, Merge cells, Column width, Row height, Delete table |
53
+ | **Code block** (`<pre>`) | Copy code, Delete block |
54
+
55
+ ### Context menu
56
+ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **Cut**, **Copy**, **Paste**, **Bold**, **Italic**, **Underline**, **Copy Format**, **Paste Format**, **Remove Format**.
57
+
58
+ ### UI
59
+ - **Toolbar** — fully configurable button groups; renders with FontAwesome icons when detected, falls back to inline SVG icons
60
+ - **Image resizer** — drag handle on selected image to resize proportionally
61
+ - **Video resizer** — drag handle on selected video embed to resize
62
+ - **Statusbar** — live word and character count; drag handle to resize editor height
63
+ - **Code view** — toggle raw HTML; sanitised before applying back to the editor
64
+ - **Fullscreen** — expands the editor to fill the viewport
65
+ - **Placeholder** — CSS `::before` pseudo-element, zero DOM node cost
66
+
67
+ ### Integration
68
+ - **No jQuery** — pure vanilla ES2022, zero runtime dependencies
69
+ - **Bootstrap friendly** — optional Bootstrap 4/5 styling for toolbar buttons (`useBootstrap: true`)
70
+ - **FontAwesome ready** — auto-detects FA on the page; falls back to built-in SVG icons
71
+ - **Plugin-ready** — register custom modules via `AutumnNote.defaults`
72
+ - **Tree-shakeable** — ES module build; all core utilities are individually exported
73
+
74
+ ### Security
75
+ - All HTML (pasted content, `setHTML()`, or code-view output) is passed through a DOM-based sanitiser that strips `<script>`, `<iframe>`, `<object>`, `<embed>`, and all `on*` event handler attributes
76
+ - `javascript:` and `data:` URLs are rejected in links and images
77
+ - Clipboard paste sanitises rich content to remove XSS vectors before inserting
78
+
79
+ ---
80
+
81
+ ## Installation
82
+
83
+ ### npm / pnpm / yarn
84
+
85
+ ```bash
86
+ npm install AutumnNote
87
+ ```
88
+
89
+ ### CDN
90
+
91
+ ```html
92
+ <link rel="stylesheet" href="dist/AutumnNote.css" />
93
+ <script src="dist/AutumnNote.umd.js"></script>
94
+ ```
95
+
96
+ > **FontAwesome icons** — the editor auto-detects FontAwesome on the page and falls back to built-in SVG icons when absent. To enable FA icons, include the FA stylesheet:
97
+ >
98
+ > ```html
99
+ > <!-- FontAwesome 6 Free (recommended) -->
100
+ > <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
101
+ > ```
102
+ >
103
+ > If FA is not on the page but the **FA Icon picker** is used, the editor will automatically inject the FontAwesome CDN stylesheet on first open.
104
+
105
+ ---
106
+
107
+ ## Quick Start
108
+
109
+ ### ES Module
110
+
111
+ ```js
112
+ import AutumnNote from 'AutumnNote';
113
+
114
+ const editor = AutumnNote.create('#my-editor', {
115
+ placeholder: 'Start typing…',
116
+ height: 300,
117
+ onChange(html) {
118
+ console.log(html);
119
+ },
120
+ });
121
+ ```
122
+
123
+ ### Script tag (UMD)
124
+
125
+ ```html
126
+ <div id="my-editor"><p>Hello!</p></div>
127
+ <script src="dist/AutumnNote.umd.js"></script>
128
+ <script>
129
+ const editor = AutumnNote.create('#my-editor');
130
+ </script>
131
+ ```
132
+
133
+ ### With Bootstrap 5
134
+
135
+ ```js
136
+ const editor = AutumnNote.create('#my-editor', {
137
+ useBootstrap: true,
138
+ bootstrapVersion: 5,
139
+ toolbarButtonClass: 'btn btn-sm btn-light',
140
+ });
141
+ ```
142
+
143
+ ### With FontAwesome 6
144
+
145
+ ```js
146
+ const editor = AutumnNote.create('#my-editor', {
147
+ useFontAwesome: true,
148
+ fontAwesomeClass: 'fa-solid', // FA 6 prefix
149
+ });
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Usage
155
+
156
+ ### Read content on demand
157
+
158
+ The most common pattern — attach a button that reads back the current HTML:
159
+
160
+ ```html
161
+ <link rel="stylesheet" href="dist/autumnnote.css" />
162
+
163
+ <div id="editor"><p>Hello, <strong>AutumnNote</strong>!</p></div>
164
+
165
+ <button onclick="getHTML()">Get HTML</button>
166
+ <pre id="output"></pre>
167
+
168
+ <script src="dist/autumnnote.umd.js"></script>
169
+ <script>
170
+ const editor = AutumnNote.create('#editor', { height: 300 });
171
+
172
+ function getHTML() {
173
+ document.getElementById('output').innerText = editor.getHTML();
174
+ }
175
+ </script>
176
+ ```
177
+
178
+ ### React to every change
179
+
180
+ Use `onChange` (or `editor.on('change', fn)`) to update a live preview automatically:
181
+
182
+ ```html
183
+ <div id="editor"><p>Start typing…</p></div>
184
+ <pre id="preview"></pre>
185
+
186
+ <script src="dist/autumnnote.umd.js"></script>
187
+ <script>
188
+ const editor = AutumnNote.create('#editor', {
189
+ height: 300,
190
+ onChange(html) {
191
+ document.getElementById('preview').innerText = html;
192
+ },
193
+ });
194
+ </script>
195
+ ```
196
+
197
+ ### Set and clear content programmatically
198
+
199
+ ```js
200
+ editor.setHTML('<p>New <em>content</em></p>'); // set
201
+ editor.clear(); // clear to empty <p>
202
+ console.log(editor.getText()); // plain text, no markup
203
+ ```
204
+
205
+ ---
206
+
207
+ ## API
208
+
209
+ ### Factory
210
+
211
+ | Method | Description |
212
+ |---|---|
213
+ | `AutumnNote.create(selector, options?)` | Creates editor instance(s). `selector` can be a CSS string, `Element`, `NodeList`, or `Element[]`. Returns a `Context` for a single match, or `Context[]` for multiple. |
214
+ | `AutumnNote.destroy(selector)` | Destroys editor(s) matching the selector and restores the original element. |
215
+ | `AutumnNote.getInstance(selector)` | Returns the `Context` for a given element, or `null` if not initialised. |
216
+ | `AutumnNote.defaults` | Global default options object. Mutate before calling `create()` to apply project-wide settings. |
217
+
218
+ ### Context (editor instance)
219
+
220
+ | Method | Description |
221
+ |---|---|
222
+ | `editor.getHTML()` | Returns the current HTML content. Zero-width spaces inserted by the icon picker are stripped automatically. |
223
+ | `editor.setHTML(html)` | Sets HTML content. Input is sanitised before rendering. |
224
+ | `editor.getText()` | Returns plain text with no markup. |
225
+ | `editor.clear()` | Clears all content, resets to an empty `<p>`. |
226
+ | `editor.setDisabled(bool)` | Disables (`true`) or re-enables (`false`) the editor and toolbar. |
227
+ | `editor.destroy()` | Removes the editor, disposes all modules, and restores the original element. |
228
+ | `editor.on(event, fn)` | Subscribes to an editor event. Returns an unsubscribe function — call it to remove the listener. |
229
+ | `editor.invoke('module.method', ...args)` | Calls any registered module method by dot-separated name. Returns the method's return value. |
230
+
231
+ ### Events
232
+
233
+ | Name | Payload | Description |
234
+ |---|---|---|
235
+ | `change` | `html: string` | Fired after every content mutation. Debounced internally. |
236
+ | `focus` | — | Editor's editable area gained focus. |
237
+ | `blur` | — | Editor's editable area lost focus. |
238
+
239
+ #### Subscribing to events
240
+
241
+ ```js
242
+ const editor = AutumnNote.create('#editor');
243
+
244
+ // Subscribe
245
+ const unsub = editor.on('change', (html) => {
246
+ document.getElementById('output').innerHTML = html;
247
+ });
248
+
249
+ // Unsubscribe later
250
+ unsub();
251
+ ```
252
+
253
+ ### `invoke()` examples
254
+
255
+ ```js
256
+ // Programmatically open a dialog
257
+ editor.invoke('linkDialog.show');
258
+ editor.invoke('imageDialog.show');
259
+ editor.invoke('emojiDialog.show');
260
+ editor.invoke('iconDialog.show');
261
+
262
+ // Undo / redo
263
+ editor.invoke('editor.undo');
264
+ editor.invoke('editor.redo');
265
+
266
+ // Toggle views
267
+ editor.invoke('codeview.toggle');
268
+ editor.invoke('fullscreen.toggle');
269
+
270
+ // Query state
271
+ const isCodeview = editor.invoke('codeview.isActive'); // boolean
272
+ const isFullscreen = editor.invoke('fullscreen.isActive'); // boolean
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Options
278
+
279
+ | Option | Type | Default | Description |
280
+ |---|---|---|---|
281
+ | `placeholder` | `string` | `''` | Placeholder text shown when the editor is empty. |
282
+ | `height` | `number` | `200` | Initial / minimum editor height in px. |
283
+ | `minHeight` | `number` | `100` | Hard minimum height in px (enforced during resize). |
284
+ | `maxHeight` | `number` | `0` | Maximum height in px. `0` = unlimited. |
285
+ | `focus` | `boolean` | `false` | Auto-focus the editor on initialisation. |
286
+ | `resizeable` | `boolean` | `true` | Show the drag-to-resize handle in the statusbar. |
287
+ | `toolbar` | `Array` | default | Array of button group arrays. See [Toolbar customisation](#toolbar-customisation). |
288
+ | `useBootstrap` | `boolean` | `false` | Apply Bootstrap button classes to toolbar buttons. |
289
+ | `bootstrapVersion` | `number` | `5` | Bootstrap major version to target (`4` or `5`). |
290
+ | `toolbarButtonClass` | `string` | `'btn btn-sm btn-light'` | CSS classes applied to toolbar buttons when `useBootstrap` is `true`. |
291
+ | `useFontAwesome` | `boolean` | `true` | Render toolbar icons via FontAwesome when FA is detected on the page. |
292
+ | `fontAwesomeClass` | `string` | `'fas'` | FontAwesome prefix class. Use `'fas'` for FA 5, `'fa-solid'` for FA 6. |
293
+ | `pasteAsPlainText` | `boolean` | `false` | Force all pasted content to plain text, stripping all formatting. |
294
+ | `pasteCleanHTML` | `boolean` | `true` | Sanitise HTML on paste — strips scripts and dangerous attributes. |
295
+ | `allowImageUpload` | `boolean` | `true` | Show the file upload input in the image dialog. |
296
+ | `maxImageSize` | `number` | `5` | Maximum image upload file size in MB. Files exceeding this are rejected with an alert. |
297
+ | `onImageUpload` | `Function` | `null` | `(files: FileList) => void` — custom upload handler. When provided, overrides the default base64 embed behaviour. Insert the resulting URL yourself via `editor.invoke('editor.insertImage', url, alt)`. |
298
+ | `tabSize` | `number` | `0` | Number of spaces inserted per Tab key press outside of lists. `0` = browser default Tab behaviour. |
299
+ | `defaultFontFamily` | `string` | `'Arial'` | Font family applied as the default style for the editable area. |
300
+ | `fontFamilies` | `string[]` | (10 fonts) | Font families listed in the Font Family dropdown. Default: Arial, Arial Black, Comic Sans MS, Courier New, Georgia, Impact, Tahoma, Times New Roman, Trebuchet MS, Verdana. |
301
+ | `onChange` | `Function` | `null` | `(html: string) => void` — shorthand for `editor.on('change', fn)`. |
302
+ | `onFocus` | `Function` | `null` | `(context: Context) => void` — shorthand for `editor.on('focus', fn)`. |
303
+ | `onBlur` | `Function` | `null` | `(context: Context) => void` — shorthand for `editor.on('blur', fn)`. |
304
+ | `stickyToolbar` | `boolean` | `false` | Stick the toolbar to the viewport top when the page is scrolled. |
305
+ | `stickyToolbarOffset` | `number` | `0` | Top offset in px for the sticky toolbar (e.g. height of a fixed navigation bar). |
306
+ | `theme` | `string` | `'light'` | Colour theme: `'light'` or `'dark'`. |
307
+ | `codeHighlight` | `boolean` | `false` | Auto-load Prism.js for syntax highlighting inside `<pre><code>` blocks. |
308
+ | `codeHighlightCDN` | `string` | cdnjs Prism 1.29.0 | Base CDN URL used when auto-loading Prism assets. |
309
+ | `markdownPaste` | `boolean` | `true` | Convert pasted Markdown text to HTML when no HTML is present in the clipboard. |
310
+
311
+ ---
312
+
313
+ ## Dialogs
314
+
315
+ ### Link dialog
316
+ Fields: **URL** (type=url, required), **Display text** (auto-populated from the current selection), **Open in new tab** checkbox.
317
+ When the caret is inside an existing `<a>` element, the dialog pre-fills with the current link's values and updates in-place on confirm.
318
+
319
+ ### Image dialog
320
+ Fields: **Image URL** (type=url), **Alt text**, and — when `allowImageUpload` is `true` — a **file picker** (accepts `image/*`). Selecting a file embeds it as a base64 data URI unless `onImageUpload` is provided.
321
+
322
+ ### Video dialog
323
+ Fields: **Video URL** (YouTube / Vimeo / direct file), **Width** (px, default 560). Supported URL formats:
324
+ - `https://www.youtube.com/watch?v=…` → `<iframe>` embed
325
+ - `https://youtu.be/…` → `<iframe>` embed
326
+ - `https://vimeo.com/…` → `<iframe>` embed
327
+ - Direct `.mp4`, `.webm`, `.ogg` URL → `<video controls>` element
328
+
329
+ ### Emoji picker
330
+ Displays ~380 Unicode emoji in a scrollable grid grouped into 7 categories. Filter by category tab or keyword search. Clicking an emoji inserts it immediately as a plain Unicode character — no extra "Insert" step. All characters are UTF-8 / utf8mb4 compatible.
331
+
332
+ ### FA Icon picker
333
+ Displays FontAwesome 6 Free Solid icons grouped into 8 categories: **Popular**, **Interface**, **Navigation**, **Media**, **Communication**, **Files**, **People**, **Objects**. Filter by category or keyword search. Before inserting, configure:
334
+ - **Style** — Solid, Regular, or Light (Pro)
335
+ - **Size** — Inherit, 0.75em, 1em, 1.25em, 1.5em, 2em, 3em
336
+ - **Colour** — colour picker + "Use colour" toggle
337
+
338
+ The icon is inserted as `<i class="fa-solid fa-{name}" style="…">`. If FontAwesome is not loaded on the page, the dialog automatically injects the FA 6 CDN stylesheet on first open.
339
+
340
+ ---
341
+
342
+ ## Table tooltip
343
+
344
+ Clicking inside any table opens a floating tooltip with the following actions:
345
+
346
+ | Group | Actions |
347
+ |---|---|
348
+ | Rows | Add Row Above, Add Row Below, Delete Row |
349
+ | Columns | Add Column Left, Add Column Right, Delete Column |
350
+ | Cells | Merge Cells |
351
+ | Resize | Column Width (px / %), Row Height (px) |
352
+ | Danger | Delete Table |
353
+
354
+ ---
355
+
356
+ ## Toolbar Customisation
357
+
358
+ The `toolbar` option accepts an array of button groups. Each group is a sub-array of button definition objects:
359
+
360
+ ```js
361
+ import AutumnNote from 'AutumnNote';
362
+ import {
363
+ boldBtn, italicBtn, underlineBtn, strikeBtn,
364
+ foreColorBtn, backColorBtn,
365
+ linkBtn, imageBtn, videoBtn, tableBtn,
366
+ emojiBtn, iconBtn,
367
+ codeviewBtn, fullscreenBtn,
368
+ } from 'AutumnNote/src/js/module/Buttons.js';
369
+
370
+ AutumnNote.create('#editor', {
371
+ toolbar: [
372
+ [boldBtn, italicBtn, underlineBtn, strikeBtn],
373
+ [foreColorBtn, backColorBtn],
374
+ [linkBtn, imageBtn, videoBtn, tableBtn],
375
+ [emojiBtn, iconBtn],
376
+ [codeviewBtn, fullscreenBtn],
377
+ ],
378
+ });
379
+ ```
380
+
381
+ **Available buttons**
382
+
383
+ | Export | Type | Tooltip |
384
+ |---|---|---|
385
+ | `paragraphStyleBtn` | dropdown | Paragraph Style |
386
+ | `fontFamilyBtn` | dropdown | Font Family |
387
+ | `lineHeightBtn` | dropdown | Line Height |
388
+ | `undoBtn` / `redoBtn` | button | Undo / Redo |
389
+ | `boldBtn` / `italicBtn` / `underlineBtn` / `strikeBtn` | button | Text style |
390
+ | `superscriptBtn` / `subscriptBtn` | button | Super / Subscript |
391
+ | `foreColorBtn` / `backColorBtn` | color picker | Text colour / Highlight colour |
392
+ | `alignLeftBtn` / `alignCenterBtn` / `alignRightBtn` / `alignJustifyBtn` | button | Alignment |
393
+ | `ulBtn` / `olBtn` / `indentBtn` / `outdentBtn` | button | Lists & indentation |
394
+ | `hrBtn` | button | Horizontal Rule |
395
+ | `linkBtn` | button | Insert Link |
396
+ | `imageBtn` | button | Insert Image |
397
+ | `videoBtn` | button | Insert Video |
398
+ | `tableBtn` | grid picker | Insert Table |
399
+ | `emojiBtn` | button | Insert Emoji |
400
+ | `iconBtn` | button | Insert FA Icon |
401
+ | `codeviewBtn` | button | HTML Code View |
402
+ | `fullscreenBtn` | button | Fullscreen |
403
+
404
+ ### Setting global defaults
405
+
406
+ ```js
407
+ import AutumnNote from 'AutumnNote';
408
+
409
+ // Apply once before any create() calls
410
+ Object.assign(AutumnNote.defaults, {
411
+ height: 400,
412
+ placeholder: 'Write something…',
413
+ fontAwesomeClass: 'fa-solid',
414
+ fontFamilies: ['Inter', 'Roboto', 'Georgia', 'Courier New'],
415
+ });
416
+ ```
417
+
418
+ ---
419
+
420
+ ## Custom Image Upload
421
+
422
+ ```js
423
+ AutumnNote.create('#editor', {
424
+ allowImageUpload: true,
425
+ onImageUpload(files) {
426
+ const formData = new FormData();
427
+ formData.append('file', files[0]);
428
+
429
+ fetch('/api/upload', { method: 'POST', body: formData })
430
+ .then(r => r.json())
431
+ .then(({ url }) => {
432
+ // Insert the returned URL into the editor
433
+ this.invoke('editor.insertImage', url, files[0].name);
434
+ });
435
+ },
436
+ });
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Multiple Instances
442
+
443
+ ```js
444
+ const editors = AutumnNote.create('.rich-editor', { height: 250 });
445
+ // editors is Context[] when selector matches multiple elements
446
+
447
+ // Iterate all instances
448
+ document.querySelectorAll('.rich-editor').forEach((el) => {
449
+ const editor = AutumnNote.getInstance(el);
450
+ console.log(editor.getHTML());
451
+ });
452
+ ```
453
+
454
+ ---
455
+
456
+ ## Project Structure
457
+
458
+ ```
459
+ src/
460
+ ├── js/
461
+ │ ├── core/
462
+ │ │ ├── dom.js DOM utilities (createElement, on, closest, …)
463
+ │ │ ├── range.js Selection / Range API helpers (withSavedRange, …)
464
+ │ │ ├── func.js General helpers (mergeDeep, debounce, …)
465
+ │ │ ├── key.js Keyboard key constants
466
+ │ │ ├── lists.js Array helpers
467
+ │ │ ├── env.js Browser / platform detection
468
+ │ │ ├── markdown.js Lightweight Markdown → HTML converter (paste handling)
469
+ │ │ └── sanitise.js DOM-based HTML and URL sanitiser (shared by all modules)
470
+ │ ├── editing/
471
+ │ │ ├── History.js Undo / redo stack (100 levels)
472
+ │ │ ├── Style.js execCommand style wrappers
473
+ │ │ ├── Table.js Table creation and cell manipulation
474
+ │ │ └── Typing.js Tab / Enter key behaviour
475
+ │ ├── module/
476
+ │ │ ├── Editor.js Core editing commands + getHTML / setHTML + sanitiser
477
+ │ │ ├── Toolbar.js Toolbar UI, button rendering (SVG + FA), dropdowns, colour picker
478
+ │ │ ├── Buttons.js Button / dropdown / colorpicker definitions and defaultToolbar
479
+ │ │ ├── Statusbar.js Word & character count + drag-to-resize
480
+ │ │ ├── Clipboard.js Paste sanitisation (HTML clean + plain-text mode)
481
+ │ │ ├── ContextMenu.js Right-click context menu (cut, copy, paste, format tools)
482
+ │ │ ├── Placeholder.js CSS-based placeholder
483
+ │ │ ├── Codeview.js HTML source view toggle
484
+ │ │ ├── Fullscreen.js Fullscreen mode
485
+ │ │ ├── LinkDialog.js Link insert / edit dialog
486
+ │ │ ├── LinkTooltip.js Floating toolbar for links (open / edit / unlink)
487
+ │ │ ├── ImageDialog.js Image insert dialog (URL + optional file upload)
488
+ │ │ ├── ImageTooltip.js Floating toolbar for images (edit / delete)
489
+ │ │ ├── ImageResizer.js Drag handle to resize images
490
+ │ │ ├── VideoDialog.js Video embed dialog (YouTube, Vimeo, direct file)
491
+ │ │ ├── VideoTooltip.js Floating toolbar for video embeds (edit / delete)
492
+ │ │ ├── VideoResizer.js Drag handle to resize video embeds
493
+ │ │ ├── TableTooltip.js Floating toolbar for tables (row/col/cell management)
494
+ │ │ ├── CodeTooltip.js Floating toolbar for code blocks (copy / delete)
495
+ │ │ ├── EmojiDialog.js Unicode emoji picker (~380 emoji, 7 categories)
496
+ │ │ ├── IconDialog.js FontAwesome icon picker (FA 6 Free Solid, 8 categories)
497
+ │ │ └── ShortcutsDialog.js Keyboard shortcuts reference dialog (Shift+?)
498
+ │ ├── Context.js Editor instance hub — module registry and event bus
499
+ │ ├── settings.js Default options (AsnOptions)
500
+ │ ├── renderer.js DOM layout builder
501
+ │ └── index.js Public entry point + AutumnNote factory
502
+ └── styles/
503
+ ├── _variables.scss SCSS design tokens (colours, spacing, radii, transitions)
504
+ └── AutumnNote.scss Main stylesheet
505
+ ```
506
+
507
+ ---
508
+
509
+ ## Development
510
+
511
+ ```bash
512
+ # Install dependencies
513
+ npm install
514
+
515
+ # Start dev server with HMR (Vite)
516
+ npm run dev
517
+
518
+ # Build library (ES module + UMD + CSS)
519
+ npm run build
520
+
521
+ # Run unit tests (Vitest)
522
+ npm test
523
+ ```
524
+
525
+ Build output in `dist/`:
526
+ - `AutumnNote.es.js` — ES module (tree-shakeable)
527
+ - `AutumnNote.umd.js` — UMD bundle (script tag / CommonJS)
528
+ - `AutumnNote.css` — compiled stylesheet
529
+
530
+ ---
531
+
532
+ ## Comparison with Summernote
533
+
534
+ | Feature | Summernote | AutumnNote |
535
+ |---|---|---|
536
+ | jQuery required | Yes | No |
537
+ | Bootstrap required | Optional | No |
538
+ | Build system | Grunt | Vite |
539
+ | Module format | IIFE | ES module + UMD |
540
+ | Written in | ES5 / ES6 mix | ES2022 |
541
+ | HTML sanitisation | Basic | DOM-based (strips scripts, XSS vectors) |
542
+ | Emoji picker | No | Yes (~380 Unicode emoji, 7 categories) |
543
+ | FA icon picker | No | Yes (FA 6 Free Solid, 8 categories, searchable) |
544
+ | Video embeds | No | Yes (YouTube, Vimeo, direct file) |
545
+ | Image / video resize | No | Yes (drag handles) |
546
+ | Inline tooltips | No | Yes (link, image, video, table, code) |
547
+ | Context menu | No | Yes (with format copy/paste) |
548
+ | Right-click context menu | No | Yes |
549
+
550
+ ---
551
+
552
+ ## License
553
+
554
+ MIT
555
+
556
+ ---
557
+
558
+ ## Features
559
+
560
+ ### Editing
561
+ - **Text formatting** — bold, italic, underline, strikethrough, superscript, subscript
562
+ - **Paragraph styles** — Normal, H1–H6, Blockquote, Code block
563
+ - **Font family** — customisable dropdown (10 families by default)
564
+ - **Line height** — dropdown from 1.0 to 3.0
565
+ - **Text & highlight colour** — native colour picker with last-used colour
566
+ - **Alignment** — left, center, right, justify
567
+ - **Lists** — unordered and ordered, with indent / outdent
568
+ - **Undo / redo** — built-in history stack (100 levels, `Ctrl+Z` / `Ctrl+Y`)
569
+ - **Tab key** — configurable spaces-per-tab, smart list indentation
570
+
571
+ ### Insert
572
+ - **Horizontal rule**
573
+ - **Link dialog** — insert / edit hyperlinks with text and target options
574
+ - **Image dialog** — insert by URL or file upload (base64 embed); configurable max size
575
+ - **Video dialog** — insert YouTube / Vimeo / direct video URLs as responsive embeds
576
+ - **Table** — interactive grid picker (up to 10×10), context-menu actions (add/delete row/col, merge, split)
577
+ - **Emoji picker** — ~380 Unicode emoji across 7 categories (Smileys, People, Animals, Food, Travel, Objects, Symbols) with keyword search; click to insert instantly
578
+ - **FA Icon picker** — browse FontAwesome 6 Free Solid icons by category with keyword search; configurable style, size, and colour
579
+
580
+ ### UI
581
+ - **Toolbar** — fully configurable button groups; auto-renders with SVG fallback or FontAwesome icons
582
+ - **Inline tooltips** — link, image, video, table, and code-block context tooltips with edit/delete actions
583
+ - **Image resizer** — drag handle to resize inserted images
584
+ - **Video resizer** — drag handle to resize inserted video embeds
585
+ - **Statusbar** — word and character count + drag-to-resize editor height
586
+ - **Code view** — toggle raw HTML source editor with sanitisation on apply
587
+ - **Fullscreen** — expand to fill the viewport
588
+ - **Placeholder** — CSS-based, zero DOM pollution
589
+ - **Context menu** — right-click menu for common actions
590
+
591
+ ### Integration
592
+ - **No jQuery** — pure vanilla ES2022, zero runtime dependencies
593
+ - **Bootstrap friendly** — optional Bootstrap 4/5 styling for toolbar buttons (`useBootstrap: true`)
594
+ - **FontAwesome ready** — auto-detects FA on the page; falls back to inline SVG icons
595
+ - **Plugin-ready** — register custom modules via `AutumnNote.defaults`
596
+ - **Tree-shakeable** — ES module build; all core utilities are individually exported
597
+
598
+ ### Security
599
+ - All HTML (pasted, loaded via `setHTML`, or applied from code view) is sanitised through a DOM-based parser — strips `<script>`, `<iframe>`, `<object>`, all `on*` attributes
600
+ - `javascript:` URLs are rejected in links and images
601
+ - Clipboard paste sanitises and removes XSS vectors before inserting
602
+
603
+ ---
604
+
605
+ ## Installation
606
+
607
+ ### npm / pnpm / yarn
608
+
609
+ ```bash
610
+ npm install AutumnNote
611
+ ```
612
+
613
+ ### CDN
614
+
615
+ ```html
616
+ <link rel="stylesheet" href="dist/AutumnNote.css" />
617
+ <script src="dist/AutumnNote.umd.js"></script>
618
+ ```
619
+
620
+ > To use FontAwesome toolbar icons, include the FA stylesheet on your page. The editor auto-detects it and falls back to built-in SVG icons when FA is absent.
621
+ >
622
+ > ```html
623
+ > <!-- FontAwesome 6 Free -->
624
+ > <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
625
+ > ```
626
+
627
+ ---
628
+
629
+ ## Quick Start
630
+
631
+ ### ES Module
632
+
633
+ ```js
634
+ import AutumnNote from 'AutumnNote';
635
+
636
+ const editor = AutumnNote.create('#my-editor', {
637
+ placeholder: 'Start typing…',
638
+ height: 300,
639
+ onChange(html) {
640
+ console.log(html);
641
+ },
642
+ });
643
+ ```
644
+
645
+ ### Script tag (UMD)
646
+
647
+ ```html
648
+ <div id="my-editor"><p>Hello!</p></div>
649
+ <script src="dist/AutumnNote.umd.js"></script>
650
+ <script>
651
+ const editor = AutumnNote.create('#my-editor');
652
+ </script>
653
+ ```
654
+
655
+ ### With Bootstrap 5
656
+
657
+ ```js
658
+ const editor = AutumnNote.create('#my-editor', {
659
+ useBootstrap: true,
660
+ bootstrapVersion: 5,
661
+ toolbarButtonClass: 'btn btn-sm btn-light',
662
+ });
663
+ ```
664
+
665
+ ---
666
+
667
+ ## API
668
+
669
+ ### Factory
670
+
671
+ | Method | Description |
672
+ |---|---|
673
+ | `AutumnNote.create(selector, options?)` | Creates editor instance(s). Returns a `Context` (or array of `Context`). |
674
+ | `AutumnNote.destroy(selector)` | Destroys editor(s) and restores the original element. |
675
+ | `AutumnNote.getInstance(selector)` | Returns the `Context` for a given element, or `null`. |
676
+ | `AutumnNote.defaults` | Global default options — mutate before calling `create()` to set project-wide defaults. |
677
+
678
+ ### Context (editor instance)
679
+
680
+ | Method | Description |
681
+ |---|---|
682
+ | `editor.getHTML()` | Returns the current HTML content (sanitised). |
683
+ | `editor.setHTML(html)` | Sets HTML content (sanitised). |
684
+ | `editor.getText()` | Returns plain text (no markup). |
685
+ | `editor.clear()` | Clears all content. |
686
+ | `editor.setDisabled(bool)` | Enables or disables the editor. |
687
+ | `editor.destroy()` | Removes the editor and restores the original element. |
688
+ | `editor.on(event, fn)` | Subscribes to an editor event. Returns an unsubscribe function. |
689
+ | `editor.invoke('module.method', ...args)` | Calls any registered module method by name. |
690
+
691
+ ### Events
692
+
693
+ | Name | Payload | Description |
694
+ |---|---|---|
695
+ | `change` | `html: string` | Fired after every content mutation. |
696
+ | `focus` | — | Editor gained focus. |
697
+ | `blur` | — | Editor lost focus. |
698
+
699
+ ---
700
+
701
+ ## Options
702
+
703
+ | Option | Type | Default | Description |
704
+ |---|---|---|---|
705
+ | `placeholder` | `string` | `''` | Placeholder text shown when the editor is empty. |
706
+ | `height` | `number` | `200` | Initial / minimum editor height in px. |
707
+ | `minHeight` | `number` | `100` | Hard minimum height in px. |
708
+ | `maxHeight` | `number` | `0` | Maximum height in px. `0` = unlimited. |
709
+ | `focus` | `boolean` | `false` | Auto-focus the editor on initialisation. |
710
+ | `resizeable` | `boolean` | `true` | Show the drag-to-resize handle in the statusbar. |
711
+ | `toolbar` | `Array` | default | Array of button group arrays. See [Toolbar customisation](#toolbar-customisation). |
712
+ | `useBootstrap` | `boolean` | `false` | Apply Bootstrap classes to toolbar buttons. |
713
+ | `bootstrapVersion` | `number` | `5` | Bootstrap major version (`4` or `5`). |
714
+ | `toolbarButtonClass` | `string` | `'btn btn-sm btn-light'` | CSS classes for toolbar buttons when `useBootstrap` is `true`. |
715
+ | `useFontAwesome` | `boolean` | `true` | Use FA icons when FontAwesome is detected on the page. |
716
+ | `fontAwesomeClass` | `string` | `'fas'` | FA prefix — `'fas'` for FA 5, `'fa-solid'` for FA 6. |
717
+ | `pasteAsPlainText` | `boolean` | `false` | Strip all formatting on paste. |
718
+ | `pasteCleanHTML` | `boolean` | `true` | Sanitise HTML on paste. |
719
+ | `allowImageUpload` | `boolean` | `true` | Allow file upload in the image dialog. |
720
+ | `maxImageSize` | `number` | `5` | Maximum image upload size in MB. |
721
+ | `onImageUpload` | `Function` | `null` | Custom upload handler `(files: FileList) => void`. Overrides base64 embed. |
722
+ | `tabSize` | `number` | `0` | Spaces inserted per Tab key press. `0` = browser default. |
723
+ | `defaultFontFamily` | `string` | `'Arial'` | Font applied to the editable area. |
724
+ | `fontFamilies` | `string[]` | (10 fonts) | Font families listed in the font-family dropdown. |
725
+ | `onChange` | `Function` | `null` | `(html: string) => void` — called on every change. |
726
+ | `onFocus` | `Function` | `null` | `(context: Context) => void` |
727
+ | `onBlur` | `Function` | `null` | `(context: Context) => void` |
728
+ | `stickyToolbar` | `boolean` | `false` | Stick the toolbar to the viewport top when the page is scrolled. |
729
+ | `stickyToolbarOffset` | `number` | `0` | Top offset in px for the sticky toolbar (e.g. height of a fixed navigation bar). |
730
+ | `theme` | `string` | `'light'` | Colour theme: `'light'` or `'dark'`. |
731
+ | `codeHighlight` | `boolean` | `false` | Auto-load Prism.js for syntax highlighting inside `<pre><code>` blocks. |
732
+ | `codeHighlightCDN` | `string` | cdnjs Prism 1.29.0 | Base CDN URL used when auto-loading Prism assets. |
733
+ | `markdownPaste` | `boolean` | `true` | Convert pasted Markdown text to HTML when no HTML is present in the clipboard. |
734
+
735
+ ---
736
+
737
+ ## Toolbar Customisation
738
+
739
+ The `toolbar` option accepts an array of button groups. Each group is an array of button definition objects imported from `Buttons.js`:
740
+
741
+ ```js
742
+ import AutumnNote from 'AutumnNote';
743
+ import {
744
+ boldBtn, italicBtn, underlineBtn,
745
+ linkBtn, imageBtn, emojiBtn, iconBtn,
746
+ codeviewBtn, fullscreenBtn,
747
+ } from 'AutumnNote/src/js/module/Buttons.js';
748
+
749
+ AutumnNote.create('#editor', {
750
+ toolbar: [
751
+ [boldBtn, italicBtn, underlineBtn],
752
+ [linkBtn, imageBtn, emojiBtn, iconBtn],
753
+ [codeviewBtn, fullscreenBtn],
754
+ ],
755
+ });
756
+ ```
757
+
758
+ **Available buttons**
759
+
760
+ | Export | Tooltip |
761
+ |---|---|
762
+ | `paragraphStyleBtn` | Paragraph Style (dropdown) |
763
+ | `fontFamilyBtn` | Font Family (dropdown) |
764
+ | `lineHeightBtn` | Line Height (dropdown) |
765
+ | `undoBtn` / `redoBtn` | Undo / Redo |
766
+ | `boldBtn` / `italicBtn` / `underlineBtn` / `strikeBtn` | Text style |
767
+ | `superscriptBtn` / `subscriptBtn` | Super / Subscript |
768
+ | `foreColorBtn` / `backColorBtn` | Text colour / Highlight colour |
769
+ | `alignLeftBtn` / `alignCenterBtn` / `alignRightBtn` / `alignJustifyBtn` | Alignment |
770
+ | `ulBtn` / `olBtn` / `indentBtn` / `outdentBtn` | Lists & indentation |
771
+ | `hrBtn` | Horizontal Rule |
772
+ | `linkBtn` | Insert Link |
773
+ | `imageBtn` | Insert Image |
774
+ | `videoBtn` | Insert Video |
775
+ | `tableBtn` | Insert Table (grid picker) |
776
+ | `emojiBtn` | Insert Emoji |
777
+ | `iconBtn` | Insert FA Icon |
778
+ | `codeviewBtn` | HTML Code View |
779
+ | `fullscreenBtn` | Fullscreen |
780
+
781
+ ---
782
+
783
+ ## Project Structure
784
+
785
+ ```
786
+ src/
787
+ ├── js/
788
+ │ ├── core/
789
+ │ │ ├── dom.js DOM utilities
790
+ │ │ ├── range.js Selection / Range API helpers
791
+ │ │ ├── func.js General utility helpers
792
+ │ │ ├── key.js Keyboard key constants
793
+ │ │ ├── lists.js Array helpers
794
+ │ │ ├── env.js Browser / platform detection
795
+ │ │ ├── markdown.js Markdown → HTML converter
796
+ │ │ └── sanitise.js DOM-based HTML and URL sanitiser
797
+ │ ├── editing/
798
+ │ │ ├── History.js Undo / redo stack
799
+ │ │ ├── Style.js execCommand style wrappers
800
+ │ │ ├── Table.js Table creation and manipulation
801
+ │ │ └── Typing.js Tab / Enter key behaviour
802
+ │ ├── module/
803
+ │ │ ├── Editor.js Core editing commands + getHTML / setHTML
804
+ │ │ ├── Toolbar.js Toolbar UI and button rendering
805
+ │ │ ├── Buttons.js Button and dropdown definitions
806
+ │ │ ├── Statusbar.js Status bar + drag-to-resize
807
+ │ │ ├── Clipboard.js Paste sanitisation
808
+ │ │ ├── ContextMenu.js Right-click context menu
809
+ │ │ ├── Placeholder.js Placeholder text
810
+ │ │ ├── Codeview.js HTML source view
811
+ │ │ ├── Fullscreen.js Fullscreen mode
812
+ │ │ ├── LinkDialog.js Link insert / edit dialog
813
+ │ │ ├── LinkTooltip.js Inline link tooltip
814
+ │ │ ├── ImageDialog.js Image insert dialog (URL + upload)
815
+ │ │ ├── ImageTooltip.js Inline image tooltip
816
+ │ │ ├── ImageResizer.js Drag-to-resize for images
817
+ │ │ ├── VideoDialog.js Video embed dialog
818
+ │ │ ├── VideoTooltip.js Inline video tooltip
819
+ │ │ ├── VideoResizer.js Drag-to-resize for video embeds
820
+ │ │ ├── TableTooltip.js Table context tooltip
821
+ │ │ ├── CodeTooltip.js Code block context tooltip
822
+ │ │ ├── EmojiDialog.js Unicode emoji picker
823
+ │ │ ├── IconDialog.js FontAwesome icon picker
824
+ │ │ └── ShortcutsDialog.js Keyboard shortcuts dialog
825
+ │ ├── Context.js Editor instance hub
826
+ │ ├── settings.js Default options
827
+ │ ├── renderer.js DOM layout builder
828
+ │ └── index.js Public entry point
829
+ └── styles/
830
+ ├── _variables.scss SCSS design tokens
831
+ └── AutumnNote.scss Main stylesheet
832
+ ```
833
+
834
+ ---
835
+
836
+ ## Development
837
+
838
+ ```bash
839
+ # Install dependencies
840
+ npm install
841
+
842
+ # Start dev server (Vite)
843
+ npm run dev
844
+
845
+ # Build library
846
+ npm run build
847
+
848
+ # Run tests
849
+ npm test
850
+ ```
851
+
852
+ ---
853
+
854
+ ## Comparison with Summernote
855
+
856
+ | Feature | Summernote | AutumnNote |
857
+ |---|---|---|
858
+ | jQuery required | Yes | No |
859
+ | Bootstrap required | Optional | No |
860
+ | Build system | Grunt | Vite |
861
+ | Module format | IIFE | ES module + UMD |
862
+ | Written in | ES5 / ES6 mix | ES2022 |
863
+ | HTML sanitisation | Basic | DOM-based (strips scripts, XSS vectors) |
864
+ | Emoji picker | No | Yes (~380 Unicode emoji) |
865
+ | FA icon picker | No | Yes (FA 6 Free Solid, searchable) |
866
+ | Video embeds | No | Yes (YouTube, Vimeo, direct) |
867
+ | Image / video resize | No | Yes (drag handles) |
868
+ | Inline tooltips | No | Yes (link, image, video, table, code) |
869
+
870
+ ---
871
+
872
+ ## License
873
+
874
+ MIT