jotterjs 0.1.2 → 0.2.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.
- package/README.md +144 -122
- package/dist/jotter.iife.min.js +3 -3
- package/dist/jotter.js +128 -120
- package/dist/jotter.min.js +3 -3
- package/package.json +45 -45
package/README.md
CHANGED
|
@@ -1,122 +1,144 @@
|
|
|
1
|
-
# JotterJS
|
|
2
|
-
|
|
3
|
-
A lightweight, vanilla JS rich-text editor component built on `contenteditable`. No dependencies at runtime.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- Full formatting toolbar (bold, italic, underline, strikethrough, subscript/superscript, inline code)
|
|
8
|
-
- Block format, font family, font size, and colour pickers
|
|
9
|
-
- Lists, indentation, alignment, tables, links, and image insertion
|
|
10
|
-
- Source (raw HTML) toggle
|
|
11
|
-
- Content themes: `default`, `warm`, `ink`, `forest`
|
|
12
|
-
- Pre-built toolbar presets (`minimal`, `writing`) and fully custom toolbar support
|
|
13
|
-
- Custom toolbar buttons with `onClick` callbacks
|
|
14
|
-
- Event system (`change`, `focus`, `blur`)
|
|
15
|
-
- Simple chainable API
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install jotterjs
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Or use the built files from `dist/` directly in a `<script>` tag.
|
|
24
|
-
|
|
25
|
-
## Usage
|
|
26
|
-
|
|
27
|
-
### ES Module
|
|
28
|
-
|
|
29
|
-
```js
|
|
30
|
-
import JotterJS from 'jotterjs';
|
|
31
|
-
|
|
32
|
-
const editor = new JotterJS('#my-editor', {
|
|
33
|
-
height: '320px',
|
|
34
|
-
placeholder: 'Start typing…',
|
|
35
|
-
onChange: (html) => console.log(html),
|
|
36
|
-
});
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### IIFE (script tag)
|
|
40
|
-
|
|
41
|
-
```html
|
|
42
|
-
<link rel="stylesheet" href="dist/jotter.min.css" />
|
|
43
|
-
<script src="dist/jotter.iife.min.js"></script>
|
|
44
|
-
<script>
|
|
45
|
-
const editor = new JotterJS('#my-editor');
|
|
46
|
-
</script>
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Options
|
|
50
|
-
|
|
51
|
-
| Option | Type | Default | Description |
|
|
52
|
-
|---------------|------------|------------------|--------------------------------------------------|
|
|
53
|
-
| `placeholder` | `string` | `'Start typing…'`| Placeholder text shown when the editor is empty |
|
|
54
|
-
| `height` | `string` | `'320px'` | Min-height of the editable area |
|
|
55
|
-
| `theme` | `string` | `'default'` | Content theme: `default`, `warm`, `ink`, `forest`|
|
|
56
|
-
| `toolbar` | `Array` | Full toolbar | Array of action descriptors |
|
|
57
|
-
| `onChange` | `Function` | — | Callback `(html)` fired on every content change |
|
|
58
|
-
| `onFocus` | `Function` | — | Callback fired on editor focus |
|
|
59
|
-
| `onBlur` | `Function` | — | Callback fired on editor blur |
|
|
60
|
-
|
|
61
|
-
## API
|
|
62
|
-
|
|
63
|
-
```js
|
|
64
|
-
editor.getHTML() // → string — raw innerHTML
|
|
65
|
-
editor.getText() // → string — plain text
|
|
66
|
-
editor.setHTML(html) // replace content
|
|
67
|
-
editor.insertHTML(html) // insert HTML at caret
|
|
68
|
-
editor.insertText(text) // insert plain text at caret
|
|
69
|
-
editor.clear() // empty the editor
|
|
70
|
-
editor.focus() // focus the editable area
|
|
71
|
-
editor.setTheme(name) // change theme at runtime
|
|
72
|
-
editor.setEnabled(bool) // toggle contenteditable
|
|
73
|
-
editor.toggleSource() // switch rich-text ↔ HTML source view
|
|
74
|
-
editor.isSourceMode() // → boolean
|
|
75
|
-
editor.on(event, fn) // subscribe to 'change' | 'focus' | 'blur'
|
|
76
|
-
editor.off(event, fn) // unsubscribe
|
|
77
|
-
editor.destroy() // unmount and return final HTML
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Methods return `this` for chaining (except `getHTML`, `getText`, `isSourceMode`, and `destroy`).
|
|
81
|
-
|
|
82
|
-
## Toolbar Presets
|
|
83
|
-
|
|
84
|
-
```js
|
|
85
|
-
new JotterJS('#el', { toolbar: JotterJS.presets.minimal });
|
|
86
|
-
new JotterJS('#el', { toolbar: JotterJS.presets.writing });
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
new JotterJS('#el', {
|
|
95
|
-
toolbar: [
|
|
96
|
-
|
|
97
|
-
actions.
|
|
98
|
-
actions.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
1
|
+
# JotterJS
|
|
2
|
+
|
|
3
|
+
A lightweight, vanilla JS rich-text editor component built on `contenteditable`. No dependencies at runtime.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Full formatting toolbar (bold, italic, underline, strikethrough, subscript/superscript, inline code)
|
|
8
|
+
- Block format, font family, font size, and colour pickers
|
|
9
|
+
- Lists, indentation, alignment, tables, links, and image insertion
|
|
10
|
+
- Source (raw HTML) toggle
|
|
11
|
+
- Content themes: `default`, `warm`, `ink`, `forest`
|
|
12
|
+
- Pre-built toolbar presets (`minimal`, `writing`, `full`) and fully custom toolbar support
|
|
13
|
+
- Custom toolbar buttons with `onClick` callbacks
|
|
14
|
+
- Event system (`change`, `focus`, `blur`)
|
|
15
|
+
- Simple chainable API
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install jotterjs
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or use the built files from `dist/` directly in a `<script>` tag.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### ES Module
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import JotterJS from 'jotterjs';
|
|
31
|
+
|
|
32
|
+
const editor = new JotterJS('#my-editor', {
|
|
33
|
+
height: '320px',
|
|
34
|
+
placeholder: 'Start typing…',
|
|
35
|
+
onChange: (html) => console.log(html),
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### IIFE (script tag)
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<link rel="stylesheet" href="dist/jotter.min.css" />
|
|
43
|
+
<script src="dist/jotter.iife.min.js"></script>
|
|
44
|
+
<script>
|
|
45
|
+
const editor = new JotterJS('#my-editor');
|
|
46
|
+
</script>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Options
|
|
50
|
+
|
|
51
|
+
| Option | Type | Default | Description |
|
|
52
|
+
|---------------|------------|------------------|--------------------------------------------------|
|
|
53
|
+
| `placeholder` | `string` | `'Start typing…'`| Placeholder text shown when the editor is empty |
|
|
54
|
+
| `height` | `string` | `'320px'` | Min-height of the editable area |
|
|
55
|
+
| `theme` | `string` | `'default'` | Content theme: `default`, `warm`, `ink`, `forest`|
|
|
56
|
+
| `toolbar` | `Array` | Full toolbar | Array of action descriptors |
|
|
57
|
+
| `onChange` | `Function` | — | Callback `(html)` fired on every content change |
|
|
58
|
+
| `onFocus` | `Function` | — | Callback fired on editor focus |
|
|
59
|
+
| `onBlur` | `Function` | — | Callback fired on editor blur |
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
editor.getHTML() // → string — raw innerHTML
|
|
65
|
+
editor.getText() // → string — plain text
|
|
66
|
+
editor.setHTML(html) // replace content
|
|
67
|
+
editor.insertHTML(html) // insert HTML at caret
|
|
68
|
+
editor.insertText(text) // insert plain text at caret
|
|
69
|
+
editor.clear() // empty the editor
|
|
70
|
+
editor.focus() // focus the editable area
|
|
71
|
+
editor.setTheme(name) // change theme at runtime
|
|
72
|
+
editor.setEnabled(bool) // toggle contenteditable
|
|
73
|
+
editor.toggleSource() // switch rich-text ↔ HTML source view
|
|
74
|
+
editor.isSourceMode() // → boolean
|
|
75
|
+
editor.on(event, fn) // subscribe to 'change' | 'focus' | 'blur'
|
|
76
|
+
editor.off(event, fn) // unsubscribe
|
|
77
|
+
editor.destroy() // unmount and return final HTML
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Methods return `this` for chaining (except `getHTML`, `getText`, `isSourceMode`, and `destroy`).
|
|
81
|
+
|
|
82
|
+
## Toolbar Presets
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
new JotterJS('#el', { toolbar: JotterJS.presets.minimal });
|
|
86
|
+
new JotterJS('#el', { toolbar: JotterJS.presets.writing });
|
|
87
|
+
new JotterJS('#el', { toolbar: JotterJS.presets.full }); // the default toolbar
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Every preset is composed from `JotterJS.actions`, so a given command has the
|
|
91
|
+
same icon and tooltip whichever toolbar it appears in. Extend one by spreading:
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
new JotterJS('#el', {
|
|
95
|
+
toolbar: [
|
|
96
|
+
...JotterJS.presets.minimal,
|
|
97
|
+
JotterJS.actions.sep,
|
|
98
|
+
JotterJS.actions.image,
|
|
99
|
+
],
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Custom Toolbar
|
|
104
|
+
|
|
105
|
+
Action descriptors and preset arrays are frozen and shared between presets, so
|
|
106
|
+
customise by copying rather than mutating in place:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
{ ...JotterJS.actions.image, onClick: fn } // ✓
|
|
110
|
+
JotterJS.actions.image.onClick = fn // ✗ throws — would leak everywhere
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
const { actions } = JotterJS;
|
|
115
|
+
|
|
116
|
+
new JotterJS('#el', {
|
|
117
|
+
toolbar: [
|
|
118
|
+
actions.bold,
|
|
119
|
+
actions.italic,
|
|
120
|
+
actions.sep,
|
|
121
|
+
{
|
|
122
|
+
icon: 'star',
|
|
123
|
+
title: 'Insert signature',
|
|
124
|
+
onClick: (editor) => editor.insertHTML('<p><em>— Sent with JotterJS</em></p>'),
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
label: 'Clear',
|
|
128
|
+
title: 'Clear content',
|
|
129
|
+
onClick: (editor) => editor.clear(),
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm run dev # start dev server
|
|
139
|
+
npm run build # build to dist/
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
package/dist/jotter.iife.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var JotterJS=(()=>{var _=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var v=(p,t)=>{for(var e in t)_(p,e,{get:t[e],enumerable:!0})},E=(p,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of L(t))!y.call(p,o)&&o!==e&&_(p,o,{get:()=>t[o],enumerable:!(i=C(t,o))||i.enumerable});return p};var T=p=>E(_({},"__esModule",{value:!0}),p);var H={};v(H,{JotterJS:()=>u,default:()=>j});var b=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],g=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],S=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],x=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],k=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],M=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],f=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],w=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||b).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",g.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),S.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),x.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",f.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),h=r.value,m=`href="${this._esc(c)}"`;h&&(m+=` target="${this._esc(h)}"`),d&&(m+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,h?e.target=h:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${m}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(k)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(M)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),w.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),i=null;if(e&&e.rangeCount){let s=e.getRangeAt(0);s.startContainer===t&&(i=s.startOffset)}let o=document.createElement("p");if(t.replaceWith(o),o.appendChild(t),i!==null){let s=document.createRange();s.setStart(t,i),s.collapse(!0),e.removeAllRanges(),e.addRange(s)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
1
|
+
var JotterJS=(()=>{var b=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var y=(u,t)=>{for(var e in t)b(u,e,{get:t[e],enumerable:!0})},S=(u,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of E(t))!T.call(u,i)&&i!==e&&b(u,i,{get:()=>t[i],enumerable:!(o=L(t,i))||o.enumerable});return u};var x=u=>S(b({},"__esModule",{value:!0}),u);var I={};y(I,{JotterJS:()=>h,default:()=>R});var g={source:{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},sep:{type:"sep"},blockformat:{type:"blockformat"},fontfamily:{type:"fontfamily"},fontsize:{type:"fontsize"},theme:{type:"theme"},undo:{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},redo:{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},bold:{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},italic:{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},underline:{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},strike:{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},subscript:{cmd:"subscript",icon:"subscript",title:"Subscript"},superscript:{cmd:"superscript",icon:"superscript",title:"Superscript"},code:{custom:"code",icon:"code",title:"Inline Code"},copy:{cmd:"copy",icon:"content_copy",title:"Copy"},cut:{cmd:"cut",icon:"content_cut",title:"Cut"},paste:{cmd:"paste",icon:"content_paste",title:"Paste"},clearFormat:{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},alignLeft:{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},alignCenter:{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},alignRight:{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},bullets:{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},numbered:{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},link:{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},unlink:{cmd:"unlink",icon:"link_off",title:"Remove Link"},foreColor:{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},hiliteColor:{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},image:{type:"popup",id:"image",icon:"image",title:"Insert Image"},video:{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},table:{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},embed:{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},symbol:{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},specialChar:{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},lorem:{type:"popup",id:"lorem",icon:"history_edu",title:"Insert Lorem Ipsum"}};Object.values(g).forEach(Object.freeze);Object.freeze(g);var s=g,_={minimal:[s.source,s.sep,s.bold,s.italic,s.underline,s.sep,s.link,s.unlink],writing:[s.source,s.sep,s.undo,s.redo,s.sep,s.blockformat,s.sep,s.bold,s.italic,s.underline,s.strike,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image],full:[s.source,s.sep,s.undo,s.redo,s.sep,s.copy,s.cut,s.paste,s.sep,s.clearFormat,s.sep,s.blockformat,s.fontfamily,s.fontsize,s.sep,s.bold,s.italic,s.underline,s.strike,s.subscript,s.superscript,s.code,s.sep,s.foreColor,s.hiliteColor,s.sep,s.alignLeft,s.alignCenter,s.alignRight,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image,s.video,s.table,s.embed,s.symbol,s.specialChar,s.lorem,s.sep,s.theme]};Object.values(_).forEach(Object.freeze);Object.freeze(_);var M=_.full,C=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],w=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],k=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],j=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],H=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],v=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],N=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],h=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||M).forEach(e=>{let o=this._buildAction(e);o&&t.appendChild(o)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,e.appendChild(o)}return e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let i=window.prompt(t.prompt);i&&document.execCommand(t.cmd,!1,i)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",C.forEach(({label:e,tag:o})=>{let i=document.createElement("option");i.value=o,i.textContent=e,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),w.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=o,i.style.fontFamily=o,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),k.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=`${o}px`,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",v.forEach(({id:e,label:o})=>{let i=document.createElement("option");i.value=e,i.textContent=o,t.appendChild(i)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let o=document.createElement("button");o.type="button",o.className="jotter-btn jotter-color-btn",o.dataset.cmd=t.cmd,o.title=t.title,o.setAttribute("aria-label",t.title);let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,o.appendChild(i);let n=document.createElement("span");n.className="jotter-color-swatch";let a=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;n.style.background=a,o.appendChild(n);let l=document.createElement("input");return l.type="color",l.className="jotter-color-input",l.value=a,l.tabIndex=-1,l.addEventListener("change",()=>{let r=l.value;n.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),o.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),l.click()}),e.appendChild(o),e.appendChild(l),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let o=document.createElement("span");return o.className="material-icons",o.textContent=t.icon,e.appendChild(o),e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,o){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=o,this._popup.classList.add("jotter-popup--visible");let i=t.getBoundingClientRect();this._popup.style.top=i.bottom+6+"px",this._popup.style.left=i.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let n=this._popup.getBoundingClientRect();n.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,i.left-(n.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let o=10,i=8,n=document.createElement("div");n.className="jotter-table-grid",n.style.gridTemplateColumns=`repeat(${o}, 1fr)`;let a=document.createElement("div");a.className="jotter-popup-hint",a.textContent="Hover to select size";let l=[];for(let r=0;r<i;r++)for(let d=0;d<o;d++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=r,c.dataset.c=d,c.addEventListener("mouseenter",()=>{a.textContent=`${r+1} \xD7 ${d+1} table`,l.forEach(p=>{p.classList.toggle("jotter-table-cell--active",+p.dataset.r<=r&&+p.dataset.c<=d)})}),c.addEventListener("click",()=>{this._insertTable(r+1,d+1),this._hidePopup()}),l.push(c),n.appendChild(c)}return t.appendChild(n),t.appendChild(a),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let o="<table><tbody>";for(let i=0;i<t;i++){o+="<tr>";for(let n=0;n<e;n++)o+=i===0?"<th><br></th>":"<td><br></td>";o+="</tr>"}o+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,o),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,o="";if(this._savedRange){let d=window.getSelection();if(d&&d.rangeCount){o=d.toString();let c=d.anchorNode;for(;c&&c!==this._editor;){if(c.nodeName==="A"){e=c;break}c=c.parentNode}}}let i=this._makeField(t,"URL","url","https://"),n=this._makeField(t,"Link text (leave blank to keep selection)","text",""),a=this._makeField(t,"Title / tooltip","text",""),l=document.createElement("label");l.className="jotter-popup-label",l.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([d,c])=>{let p=document.createElement("option");p.value=c,p.textContent=d,r.appendChild(p)}),t.appendChild(l),t.appendChild(r),e?(i.value=e.getAttribute("href")||"",n.value=e.textContent||"",a.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):o&&(n.value=o),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let d=i.value.trim();if(!d)return;let c=n.value.trim()||o||d,p=a.value.trim(),m=r.value,f=`href="${this._esc(d)}"`;m&&(f+=` target="${this._esc(m)}"`),p&&(f+=` title="${this._esc(p)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=d,m?e.target=m:e.removeAttribute("target"),p?e.title=p:e.removeAttribute("title"),e.textContent=c):document.execCommand("insertHTML",!1,`<a ${f}>${this._esc(c)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),o=this._makeField(t,"Alt text","text","Descriptive text"),i=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let n=e.value.trim();if(!n)return;let a=o.value.trim(),l=i.value.trim(),r=l?`max-width:${l}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(n)}" alt="${this._esc(a)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let o=this._ytId(e.value.trim());if(!o){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let i=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${o}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let o=t.match(e);if(o)return o[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let o=document.createElement("textarea");return o.className="jotter-popup-textarea",o.placeholder='<iframe src="..." ...></iframe>',o.rows=4,t.appendChild(e),t.appendChild(o),t.appendChild(this._makeSubmitBtn("Insert",()=>{let i=o.value.trim();i&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(j)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(H)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(o=>{let i=document.createElement("button");i.type="button",i.className="jotter-char-btn",i.textContent=o,i.title=`U+${o.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,i.addEventListener("mousedown",n=>{n.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(i)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),N.forEach(e=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=e.label,o.addEventListener("mousedown",i=>{i.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(o)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,o,i){let n=document.createElement("label");n.className="jotter-popup-label",n.textContent=e;let a=document.createElement("input");return a.type=o,a.className="jotter-popup-input",a.placeholder=i,t.appendChild(n),t.appendChild(a),a}_makeSubmitBtn(t,e){let o=document.createElement("button");return o.type="button",o.className="jotter-popup-submit",o.textContent=t,o.addEventListener("click",e),o}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{if(t.attributes.length>0)return;let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),o=null;if(e&&e.rangeCount){let n=e.getRangeAt(0);n.startContainer===t&&(o=n.startOffset)}let i=document.createElement("p");if(t.replaceWith(i),i.appendChild(t),o!==null){let n=document.createRange();n.setStart(t,o),n.collapse(!0),e.removeAllRanges(),e.addRange(n)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,o=" ",i=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),n=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
2
2
|
$1
|
|
3
3
|
`).split(`
|
|
4
|
-
`).map(
|
|
5
|
-
`)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(
|
|
4
|
+
`).map(a=>a.trim()).filter(a=>a.length>0).map(a=>{let l=a.match(/^<\/(\w+)/),r=a.match(/^<(\w+)/),d=a.endsWith("/>"),c=r?r[1].toLowerCase():null,p=l?l[1].toLowerCase():null;p&&!n.has(p)&&(e=Math.max(0,e-1));let m=o.repeat(e)+a;return c&&!d&&!i.has(c)&&!p&&!n.has(c)&&e++,m}).join(`
|
|
5
|
+
`)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(o=>o.remove()),e.querySelectorAll("*").forEach(o=>{Array.from(o.attributes).forEach(i=>{(i.name.startsWith("on")||["href","src","action","formaction","data"].includes(i.name)&&/^\s*javascript:/i.test(i.value))&&o.removeAttribute(i.name)})}),e.body.innerHTML}_toggleInlineCode(){let t=window.getSelection();if(!t||t.rangeCount===0)return;let e=t.getRangeAt(0),o=e.commonAncestorContainer;o.nodeType===3&&(o=o.parentNode);let i=o.closest?o.closest("code"):null;if(i){let n=i.parentNode;for(;i.firstChild;)n.insertBefore(i.firstChild,i);n.removeChild(i)}else if(e.collapsed){let n=document.createElement("code");n.innerHTML="​",e.insertNode(n);let a=document.createRange();a.setStart(n,0),a.setEnd(n,n.childNodes.length),t.removeAllRanges(),t.addRange(a)}else{let n=document.createElement("code");try{e.surroundContents(n)}catch{let l=e.extractContents();n.appendChild(l),e.insertNode(n)}}}_applyFontSize(t){let e=window.getSelection();if(!e||e.rangeCount===0)return;let o=e.getRangeAt(0);if(o.collapsed)return;let i=document.createElement("span");i.style.fontSize=t+"px";try{o.surroundContents(i)}catch{let a=o.extractContents();i.appendChild(a),o.insertNode(i)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let t=await navigator.clipboard.readText();document.execCommand("insertText",!1,t)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(e=>{try{e.classList.toggle("jotter-btn--active",document.queryCommandState(e.dataset.cmd))}catch{}});let t=this._toolbarEl.querySelector('[data-id="blockformat"]');if(t){let e=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),o=C.find(i=>i.tag===e);o&&(t.value=o.tag)}}_updateStatus(){let t=this._editor.innerText||"",e=t.trim()===""?0:t.trim().split(/\s+/).length,o=t.replace(/\n/g,"").length;this._wordCountEl.textContent=`${e} word${e!==1?"s":""}`,this._charCountEl.textContent=`${o} char${o!==1?"s":""}`}_saveRange(){let t=window.getSelection();return t&&t.rangeCount>0?t.getRangeAt(0).cloneRange():null}_restoreRange(t){if(!t)return;let e=window.getSelection();e.removeAllRanges(),e.addRange(t)}_emit(t,e){(this._listeners[t]||[]).forEach(o=>o(e))}getHTML(){return this._sourceMode?this._source.value:this._editor.innerHTML}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t){return this._editor.focus(),document.execCommand("insertHTML",!1,this._sanitize(t)),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}insertText(t){return this._editor.focus(),document.execCommand("insertText",!1,t),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}setHTML(t){return this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let o=v.find(i=>i.id===t)?t:"default";return this._editor.dataset.theme=o,this._options.theme=o,this._themeSelect&&(this._themeSelect.value=o),this}setEnabled(t){return this._editor.contentEditable=String(t),this._root.classList.toggle("jotter--disabled",!t),this}on(t,e){return this._listeners[t]||(this._listeners[t]=[]),this._listeners[t].push(e),this}off(t,e){return this._listeners[t]&&(this._listeners[t]=this._listeners[t].filter(o=>o!==e)),this}destroy(){let t=this.getHTML();return this._hidePopup(),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=t,this._listeners={},t}};h.toolbar=_.full;h.actions=g;h.presets=_;var R=h;return x(I);})();
|
package/dist/jotter.js
CHANGED
|
@@ -1,53 +1,127 @@
|
|
|
1
1
|
// src/jotter.js
|
|
2
|
-
var
|
|
3
|
-
{ custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
|
|
4
|
-
{ type: "sep" },
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
{ type: "
|
|
8
|
-
|
|
9
|
-
{ cmd: "
|
|
10
|
-
{ cmd: "
|
|
11
|
-
{
|
|
12
|
-
{ cmd: "
|
|
13
|
-
{
|
|
14
|
-
{
|
|
15
|
-
{
|
|
16
|
-
{
|
|
17
|
-
{
|
|
18
|
-
{ cmd: "
|
|
19
|
-
{ cmd: "
|
|
20
|
-
{ cmd: "
|
|
21
|
-
{ cmd: "
|
|
22
|
-
{ cmd: "
|
|
23
|
-
{ cmd: "
|
|
24
|
-
{
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
{ type: "
|
|
28
|
-
{
|
|
29
|
-
{ cmd: "
|
|
30
|
-
{ cmd: "
|
|
31
|
-
{
|
|
32
|
-
{ type: "
|
|
33
|
-
{
|
|
34
|
-
{
|
|
35
|
-
{ type: "
|
|
36
|
-
{ type: "popup", id: "
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
2
|
+
var ACTIONS = {
|
|
3
|
+
source: { custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
|
|
4
|
+
sep: { type: "sep" },
|
|
5
|
+
blockformat: { type: "blockformat" },
|
|
6
|
+
fontfamily: { type: "fontfamily" },
|
|
7
|
+
fontsize: { type: "fontsize" },
|
|
8
|
+
theme: { type: "theme" },
|
|
9
|
+
undo: { cmd: "undo", icon: "undo", title: "Undo (Ctrl+Z)" },
|
|
10
|
+
redo: { cmd: "redo", icon: "redo", title: "Redo (Ctrl+Y)" },
|
|
11
|
+
bold: { cmd: "bold", icon: "format_bold", title: "Bold (Ctrl+B)" },
|
|
12
|
+
italic: { cmd: "italic", icon: "format_italic", title: "Italic (Ctrl+I)" },
|
|
13
|
+
underline: { cmd: "underline", icon: "format_underlined", title: "Underline (Ctrl+U)" },
|
|
14
|
+
strike: { cmd: "strikeThrough", icon: "strikethrough_s", title: "Strikethrough" },
|
|
15
|
+
subscript: { cmd: "subscript", icon: "subscript", title: "Subscript" },
|
|
16
|
+
superscript: { cmd: "superscript", icon: "superscript", title: "Superscript" },
|
|
17
|
+
code: { custom: "code", icon: "code", title: "Inline Code" },
|
|
18
|
+
copy: { cmd: "copy", icon: "content_copy", title: "Copy" },
|
|
19
|
+
cut: { cmd: "cut", icon: "content_cut", title: "Cut" },
|
|
20
|
+
paste: { cmd: "paste", icon: "content_paste", title: "Paste" },
|
|
21
|
+
clearFormat: { cmd: "removeFormat", icon: "format_clear", title: "Clear Formatting" },
|
|
22
|
+
alignLeft: { cmd: "justifyLeft", icon: "format_align_left", title: "Align Left" },
|
|
23
|
+
alignCenter: { cmd: "justifyCenter", icon: "format_align_center", title: "Align Center" },
|
|
24
|
+
alignRight: { cmd: "justifyRight", icon: "format_align_right", title: "Align Right" },
|
|
25
|
+
bullets: { cmd: "insertUnorderedList", icon: "format_list_bulleted", title: "Bullet List" },
|
|
26
|
+
numbered: { cmd: "insertOrderedList", icon: "format_list_numbered", title: "Numbered List" },
|
|
27
|
+
link: { type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
|
|
28
|
+
unlink: { cmd: "unlink", icon: "link_off", title: "Remove Link" },
|
|
29
|
+
foreColor: { type: "color", cmd: "foreColor", icon: "format_color_text", title: "Text Color" },
|
|
30
|
+
hiliteColor: { type: "color", cmd: "hiliteColor", icon: "format_color_fill", title: "Background Color" },
|
|
31
|
+
image: { type: "popup", id: "image", icon: "image", title: "Insert Image" },
|
|
32
|
+
video: { type: "popup", id: "video", icon: "smart_display", title: "Insert YouTube Video" },
|
|
33
|
+
table: { type: "popup", id: "table", icon: "table_chart", title: "Insert Table" },
|
|
34
|
+
embed: { type: "popup", id: "embed", icon: "html", title: "Insert Embed" },
|
|
35
|
+
symbol: { type: "popup", id: "symbol", icon: "emoji_symbols", title: "Insert Symbol" },
|
|
36
|
+
specialChar: { type: "popup", id: "specialchar", icon: "format_shapes", title: "Special Characters" },
|
|
37
|
+
lorem: { type: "popup", id: "lorem", icon: "history_edu", title: "Insert Lorem Ipsum" }
|
|
38
|
+
};
|
|
39
|
+
Object.values(ACTIONS).forEach(Object.freeze);
|
|
40
|
+
Object.freeze(ACTIONS);
|
|
41
|
+
var A = ACTIONS;
|
|
42
|
+
var PRESETS = {
|
|
43
|
+
minimal: [
|
|
44
|
+
A.source,
|
|
45
|
+
A.sep,
|
|
46
|
+
A.bold,
|
|
47
|
+
A.italic,
|
|
48
|
+
A.underline,
|
|
49
|
+
A.sep,
|
|
50
|
+
A.link,
|
|
51
|
+
A.unlink
|
|
52
|
+
],
|
|
53
|
+
writing: [
|
|
54
|
+
A.source,
|
|
55
|
+
A.sep,
|
|
56
|
+
A.undo,
|
|
57
|
+
A.redo,
|
|
58
|
+
A.sep,
|
|
59
|
+
A.blockformat,
|
|
60
|
+
A.sep,
|
|
61
|
+
A.bold,
|
|
62
|
+
A.italic,
|
|
63
|
+
A.underline,
|
|
64
|
+
A.strike,
|
|
65
|
+
A.sep,
|
|
66
|
+
A.bullets,
|
|
67
|
+
A.numbered,
|
|
68
|
+
A.sep,
|
|
69
|
+
A.link,
|
|
70
|
+
A.unlink,
|
|
71
|
+
A.sep,
|
|
72
|
+
A.image
|
|
73
|
+
],
|
|
74
|
+
full: [
|
|
75
|
+
A.source,
|
|
76
|
+
A.sep,
|
|
77
|
+
A.undo,
|
|
78
|
+
A.redo,
|
|
79
|
+
A.sep,
|
|
80
|
+
A.copy,
|
|
81
|
+
A.cut,
|
|
82
|
+
A.paste,
|
|
83
|
+
A.sep,
|
|
84
|
+
A.clearFormat,
|
|
85
|
+
A.sep,
|
|
86
|
+
A.blockformat,
|
|
87
|
+
A.fontfamily,
|
|
88
|
+
A.fontsize,
|
|
89
|
+
A.sep,
|
|
90
|
+
A.bold,
|
|
91
|
+
A.italic,
|
|
92
|
+
A.underline,
|
|
93
|
+
A.strike,
|
|
94
|
+
A.subscript,
|
|
95
|
+
A.superscript,
|
|
96
|
+
A.code,
|
|
97
|
+
A.sep,
|
|
98
|
+
A.foreColor,
|
|
99
|
+
A.hiliteColor,
|
|
100
|
+
A.sep,
|
|
101
|
+
A.alignLeft,
|
|
102
|
+
A.alignCenter,
|
|
103
|
+
A.alignRight,
|
|
104
|
+
A.sep,
|
|
105
|
+
A.bullets,
|
|
106
|
+
A.numbered,
|
|
107
|
+
A.sep,
|
|
108
|
+
A.link,
|
|
109
|
+
A.unlink,
|
|
110
|
+
A.sep,
|
|
111
|
+
A.image,
|
|
112
|
+
A.video,
|
|
113
|
+
A.table,
|
|
114
|
+
A.embed,
|
|
115
|
+
A.symbol,
|
|
116
|
+
A.specialChar,
|
|
117
|
+
A.lorem,
|
|
118
|
+
A.sep,
|
|
119
|
+
A.theme
|
|
120
|
+
]
|
|
121
|
+
};
|
|
122
|
+
Object.values(PRESETS).forEach(Object.freeze);
|
|
123
|
+
Object.freeze(PRESETS);
|
|
124
|
+
var TOOLBAR_ACTIONS = PRESETS.full;
|
|
51
125
|
var HEADING_OPTIONS = [
|
|
52
126
|
{ label: "Paragraph", tag: "p" },
|
|
53
127
|
{ label: "Heading 1", tag: "h1" },
|
|
@@ -983,6 +1057,8 @@ var JotterJS = class {
|
|
|
983
1057
|
_bindEvents() {
|
|
984
1058
|
this._editor.addEventListener("input", () => {
|
|
985
1059
|
this._editor.querySelectorAll(":scope > div").forEach((d) => {
|
|
1060
|
+
if (d.attributes.length > 0)
|
|
1061
|
+
return;
|
|
986
1062
|
const p = document.createElement("p");
|
|
987
1063
|
p.innerHTML = d.innerHTML;
|
|
988
1064
|
d.replaceWith(p);
|
|
@@ -1319,77 +1395,9 @@ var JotterJS = class {
|
|
|
1319
1395
|
return html;
|
|
1320
1396
|
}
|
|
1321
1397
|
};
|
|
1322
|
-
JotterJS.toolbar =
|
|
1323
|
-
JotterJS.actions =
|
|
1324
|
-
|
|
1325
|
-
sep: { type: "sep" },
|
|
1326
|
-
blockformat: { type: "blockformat" },
|
|
1327
|
-
fontfamily: { type: "fontfamily" },
|
|
1328
|
-
fontsize: { type: "fontsize" },
|
|
1329
|
-
theme: { type: "theme" },
|
|
1330
|
-
undo: { cmd: "undo", icon: "undo", title: "Undo (Ctrl+Z)" },
|
|
1331
|
-
redo: { cmd: "redo", icon: "redo", title: "Redo (Ctrl+Y)" },
|
|
1332
|
-
bold: { cmd: "bold", icon: "format_bold", title: "Bold (Ctrl+B)" },
|
|
1333
|
-
italic: { cmd: "italic", icon: "format_italic", title: "Italic (Ctrl+I)" },
|
|
1334
|
-
underline: { cmd: "underline", icon: "format_underlined", title: "Underline (Ctrl+U)" },
|
|
1335
|
-
strike: { cmd: "strikeThrough", icon: "strikethrough_s", title: "Strikethrough" },
|
|
1336
|
-
subscript: { cmd: "subscript", icon: "subscript", title: "Subscript" },
|
|
1337
|
-
superscript: { cmd: "superscript", icon: "superscript", title: "Superscript" },
|
|
1338
|
-
code: { custom: "code", icon: "code", title: "Inline Code" },
|
|
1339
|
-
copy: { cmd: "copy", icon: "content_copy", title: "Copy" },
|
|
1340
|
-
cut: { cmd: "cut", icon: "content_cut", title: "Cut" },
|
|
1341
|
-
paste: { cmd: "paste", icon: "content_paste", title: "Paste" },
|
|
1342
|
-
clearFormat: { cmd: "removeFormat", icon: "format_clear", title: "Clear Formatting" },
|
|
1343
|
-
alignLeft: { cmd: "justifyLeft", icon: "format_align_left", title: "Align Left" },
|
|
1344
|
-
alignCenter: { cmd: "justifyCenter", icon: "format_align_center", title: "Align Center" },
|
|
1345
|
-
alignRight: { cmd: "justifyRight", icon: "format_align_right", title: "Align Right" },
|
|
1346
|
-
bullets: { cmd: "insertUnorderedList", icon: "format_list_bulleted", title: "Bullet List" },
|
|
1347
|
-
numbered: { cmd: "insertOrderedList", icon: "format_list_numbered", title: "Numbered List" },
|
|
1348
|
-
link: { type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
|
|
1349
|
-
unlink: { cmd: "unlink", icon: "link_off", title: "Remove Link" },
|
|
1350
|
-
foreColor: { type: "color", cmd: "foreColor", icon: "format_color_text", title: "Text Color" },
|
|
1351
|
-
hiliteColor: { type: "color", cmd: "hiliteColor", icon: "format_color_fill", title: "Background Color" },
|
|
1352
|
-
image: { type: "popup", id: "image", icon: "image", title: "Insert Image" },
|
|
1353
|
-
video: { type: "popup", id: "video", icon: "smart_display", title: "Insert YouTube Video" },
|
|
1354
|
-
table: { type: "popup", id: "table", icon: "table_chart", title: "Insert Table" },
|
|
1355
|
-
embed: { type: "popup", id: "embed", icon: "html", title: "Insert Embed" },
|
|
1356
|
-
symbol: { type: "popup", id: "symbol", icon: "emoji_symbols", title: "Insert Symbol" },
|
|
1357
|
-
specialChar: { type: "popup", id: "specialchar", icon: "format_shapes", title: "Special Characters" },
|
|
1358
|
-
lorem: { type: "popup", id: "lorem", icon: "article", title: "Insert Lorem Ipsum" }
|
|
1359
|
-
};
|
|
1360
|
-
JotterJS.presets = {
|
|
1361
|
-
minimal: [
|
|
1362
|
-
{ custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
|
|
1363
|
-
{ type: "sep" },
|
|
1364
|
-
{ cmd: "bold", icon: "format_bold", title: "Bold" },
|
|
1365
|
-
{ cmd: "italic", icon: "format_italic", title: "Italic" },
|
|
1366
|
-
{ cmd: "underline", icon: "format_underlined", title: "Underline" },
|
|
1367
|
-
{ type: "sep" },
|
|
1368
|
-
{ type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
|
|
1369
|
-
{ cmd: "unlink", icon: "link_off", title: "Remove Link" }
|
|
1370
|
-
],
|
|
1371
|
-
writing: [
|
|
1372
|
-
{ custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
|
|
1373
|
-
{ type: "sep" },
|
|
1374
|
-
{ cmd: "undo", icon: "undo", title: "Undo" },
|
|
1375
|
-
{ cmd: "redo", icon: "redo", title: "Redo" },
|
|
1376
|
-
{ type: "sep" },
|
|
1377
|
-
{ type: "blockformat" },
|
|
1378
|
-
{ type: "sep" },
|
|
1379
|
-
{ cmd: "bold", icon: "format_bold", title: "Bold" },
|
|
1380
|
-
{ cmd: "italic", icon: "format_italic", title: "Italic" },
|
|
1381
|
-
{ cmd: "underline", icon: "format_underlined", title: "Underline" },
|
|
1382
|
-
{ cmd: "strikeThrough", icon: "strikethrough_s", title: "Strikethrough" },
|
|
1383
|
-
{ type: "sep" },
|
|
1384
|
-
{ cmd: "insertUnorderedList", icon: "format_list_bulleted", title: "Bullet List" },
|
|
1385
|
-
{ cmd: "insertOrderedList", icon: "format_list_numbered", title: "Ordered List" },
|
|
1386
|
-
{ type: "sep" },
|
|
1387
|
-
{ type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
|
|
1388
|
-
{ cmd: "unlink", icon: "link_off", title: "Remove Link" },
|
|
1389
|
-
{ type: "sep" },
|
|
1390
|
-
{ type: "popup", id: "image", icon: "image", title: "Insert Image" }
|
|
1391
|
-
]
|
|
1392
|
-
};
|
|
1398
|
+
JotterJS.toolbar = PRESETS.full;
|
|
1399
|
+
JotterJS.actions = ACTIONS;
|
|
1400
|
+
JotterJS.presets = PRESETS;
|
|
1393
1401
|
var jotter_default = JotterJS;
|
|
1394
1402
|
export {
|
|
1395
1403
|
JotterJS,
|
package/dist/jotter.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var g=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],m=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],f=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],b=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],C=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],L=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],_=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],y=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||g).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",m.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),f.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),b.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",_.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),p=r.value,h=`href="${this._esc(c)}"`;p&&(h+=` target="${this._esc(p)}"`),d&&(h+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,p?e.target=p:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${h}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(C)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(L)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),y.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),i=null;if(e&&e.rangeCount){let s=e.getRangeAt(0);s.startContainer===t&&(i=s.startOffset)}let o=document.createElement("p");if(t.replaceWith(o),o.appendChild(t),i!==null){let s=document.createRange();s.setStart(t,i),s.collapse(!0),e.removeAllRanges(),e.addRange(s)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
1
|
+
var _={source:{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},sep:{type:"sep"},blockformat:{type:"blockformat"},fontfamily:{type:"fontfamily"},fontsize:{type:"fontsize"},theme:{type:"theme"},undo:{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},redo:{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},bold:{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},italic:{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},underline:{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},strike:{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},subscript:{cmd:"subscript",icon:"subscript",title:"Subscript"},superscript:{cmd:"superscript",icon:"superscript",title:"Superscript"},code:{custom:"code",icon:"code",title:"Inline Code"},copy:{cmd:"copy",icon:"content_copy",title:"Copy"},cut:{cmd:"cut",icon:"content_cut",title:"Cut"},paste:{cmd:"paste",icon:"content_paste",title:"Paste"},clearFormat:{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},alignLeft:{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},alignCenter:{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},alignRight:{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},bullets:{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},numbered:{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},link:{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},unlink:{cmd:"unlink",icon:"link_off",title:"Remove Link"},foreColor:{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},hiliteColor:{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},image:{type:"popup",id:"image",icon:"image",title:"Insert Image"},video:{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},table:{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},embed:{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},symbol:{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},specialChar:{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},lorem:{type:"popup",id:"lorem",icon:"history_edu",title:"Insert Lorem Ipsum"}};Object.values(_).forEach(Object.freeze);Object.freeze(_);var s=_,m={minimal:[s.source,s.sep,s.bold,s.italic,s.underline,s.sep,s.link,s.unlink],writing:[s.source,s.sep,s.undo,s.redo,s.sep,s.blockformat,s.sep,s.bold,s.italic,s.underline,s.strike,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image],full:[s.source,s.sep,s.undo,s.redo,s.sep,s.copy,s.cut,s.paste,s.sep,s.clearFormat,s.sep,s.blockformat,s.fontfamily,s.fontsize,s.sep,s.bold,s.italic,s.underline,s.strike,s.subscript,s.superscript,s.code,s.sep,s.foreColor,s.hiliteColor,s.sep,s.alignLeft,s.alignCenter,s.alignRight,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image,s.video,s.table,s.embed,s.symbol,s.specialChar,s.lorem,s.sep,s.theme]};Object.values(m).forEach(Object.freeze);Object.freeze(m);var C=m.full,f=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],v=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],L=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],E=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],T=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],b=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],y=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],h=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||C).forEach(e=>{let o=this._buildAction(e);o&&t.appendChild(o)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,e.appendChild(o)}return e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let i=window.prompt(t.prompt);i&&document.execCommand(t.cmd,!1,i)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",f.forEach(({label:e,tag:o})=>{let i=document.createElement("option");i.value=o,i.textContent=e,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),v.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=o,i.style.fontFamily=o,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),L.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=`${o}px`,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",b.forEach(({id:e,label:o})=>{let i=document.createElement("option");i.value=e,i.textContent=o,t.appendChild(i)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let o=document.createElement("button");o.type="button",o.className="jotter-btn jotter-color-btn",o.dataset.cmd=t.cmd,o.title=t.title,o.setAttribute("aria-label",t.title);let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,o.appendChild(i);let n=document.createElement("span");n.className="jotter-color-swatch";let a=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;n.style.background=a,o.appendChild(n);let l=document.createElement("input");return l.type="color",l.className="jotter-color-input",l.value=a,l.tabIndex=-1,l.addEventListener("change",()=>{let r=l.value;n.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),o.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),l.click()}),e.appendChild(o),e.appendChild(l),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let o=document.createElement("span");return o.className="material-icons",o.textContent=t.icon,e.appendChild(o),e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,o){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=o,this._popup.classList.add("jotter-popup--visible");let i=t.getBoundingClientRect();this._popup.style.top=i.bottom+6+"px",this._popup.style.left=i.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let n=this._popup.getBoundingClientRect();n.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,i.left-(n.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let o=10,i=8,n=document.createElement("div");n.className="jotter-table-grid",n.style.gridTemplateColumns=`repeat(${o}, 1fr)`;let a=document.createElement("div");a.className="jotter-popup-hint",a.textContent="Hover to select size";let l=[];for(let r=0;r<i;r++)for(let d=0;d<o;d++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=r,c.dataset.c=d,c.addEventListener("mouseenter",()=>{a.textContent=`${r+1} \xD7 ${d+1} table`,l.forEach(p=>{p.classList.toggle("jotter-table-cell--active",+p.dataset.r<=r&&+p.dataset.c<=d)})}),c.addEventListener("click",()=>{this._insertTable(r+1,d+1),this._hidePopup()}),l.push(c),n.appendChild(c)}return t.appendChild(n),t.appendChild(a),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let o="<table><tbody>";for(let i=0;i<t;i++){o+="<tr>";for(let n=0;n<e;n++)o+=i===0?"<th><br></th>":"<td><br></td>";o+="</tr>"}o+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,o),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,o="";if(this._savedRange){let d=window.getSelection();if(d&&d.rangeCount){o=d.toString();let c=d.anchorNode;for(;c&&c!==this._editor;){if(c.nodeName==="A"){e=c;break}c=c.parentNode}}}let i=this._makeField(t,"URL","url","https://"),n=this._makeField(t,"Link text (leave blank to keep selection)","text",""),a=this._makeField(t,"Title / tooltip","text",""),l=document.createElement("label");l.className="jotter-popup-label",l.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([d,c])=>{let p=document.createElement("option");p.value=c,p.textContent=d,r.appendChild(p)}),t.appendChild(l),t.appendChild(r),e?(i.value=e.getAttribute("href")||"",n.value=e.textContent||"",a.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):o&&(n.value=o),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let d=i.value.trim();if(!d)return;let c=n.value.trim()||o||d,p=a.value.trim(),u=r.value,g=`href="${this._esc(d)}"`;u&&(g+=` target="${this._esc(u)}"`),p&&(g+=` title="${this._esc(p)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=d,u?e.target=u:e.removeAttribute("target"),p?e.title=p:e.removeAttribute("title"),e.textContent=c):document.execCommand("insertHTML",!1,`<a ${g}>${this._esc(c)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),o=this._makeField(t,"Alt text","text","Descriptive text"),i=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let n=e.value.trim();if(!n)return;let a=o.value.trim(),l=i.value.trim(),r=l?`max-width:${l}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(n)}" alt="${this._esc(a)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let o=this._ytId(e.value.trim());if(!o){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let i=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${o}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let o=t.match(e);if(o)return o[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let o=document.createElement("textarea");return o.className="jotter-popup-textarea",o.placeholder='<iframe src="..." ...></iframe>',o.rows=4,t.appendChild(e),t.appendChild(o),t.appendChild(this._makeSubmitBtn("Insert",()=>{let i=o.value.trim();i&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(E)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(T)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(o=>{let i=document.createElement("button");i.type="button",i.className="jotter-char-btn",i.textContent=o,i.title=`U+${o.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,i.addEventListener("mousedown",n=>{n.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(i)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),y.forEach(e=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=e.label,o.addEventListener("mousedown",i=>{i.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(o)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,o,i){let n=document.createElement("label");n.className="jotter-popup-label",n.textContent=e;let a=document.createElement("input");return a.type=o,a.className="jotter-popup-input",a.placeholder=i,t.appendChild(n),t.appendChild(a),a}_makeSubmitBtn(t,e){let o=document.createElement("button");return o.type="button",o.className="jotter-popup-submit",o.textContent=t,o.addEventListener("click",e),o}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{if(t.attributes.length>0)return;let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),o=null;if(e&&e.rangeCount){let n=e.getRangeAt(0);n.startContainer===t&&(o=n.startOffset)}let i=document.createElement("p");if(t.replaceWith(i),i.appendChild(t),o!==null){let n=document.createRange();n.setStart(t,o),n.collapse(!0),e.removeAllRanges(),e.addRange(n)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,o=" ",i=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),n=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
2
2
|
$1
|
|
3
3
|
`).split(`
|
|
4
|
-
`).map(
|
|
5
|
-
`)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(
|
|
4
|
+
`).map(a=>a.trim()).filter(a=>a.length>0).map(a=>{let l=a.match(/^<\/(\w+)/),r=a.match(/^<(\w+)/),d=a.endsWith("/>"),c=r?r[1].toLowerCase():null,p=l?l[1].toLowerCase():null;p&&!n.has(p)&&(e=Math.max(0,e-1));let u=o.repeat(e)+a;return c&&!d&&!i.has(c)&&!p&&!n.has(c)&&e++,u}).join(`
|
|
5
|
+
`)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(o=>o.remove()),e.querySelectorAll("*").forEach(o=>{Array.from(o.attributes).forEach(i=>{(i.name.startsWith("on")||["href","src","action","formaction","data"].includes(i.name)&&/^\s*javascript:/i.test(i.value))&&o.removeAttribute(i.name)})}),e.body.innerHTML}_toggleInlineCode(){let t=window.getSelection();if(!t||t.rangeCount===0)return;let e=t.getRangeAt(0),o=e.commonAncestorContainer;o.nodeType===3&&(o=o.parentNode);let i=o.closest?o.closest("code"):null;if(i){let n=i.parentNode;for(;i.firstChild;)n.insertBefore(i.firstChild,i);n.removeChild(i)}else if(e.collapsed){let n=document.createElement("code");n.innerHTML="​",e.insertNode(n);let a=document.createRange();a.setStart(n,0),a.setEnd(n,n.childNodes.length),t.removeAllRanges(),t.addRange(a)}else{let n=document.createElement("code");try{e.surroundContents(n)}catch{let l=e.extractContents();n.appendChild(l),e.insertNode(n)}}}_applyFontSize(t){let e=window.getSelection();if(!e||e.rangeCount===0)return;let o=e.getRangeAt(0);if(o.collapsed)return;let i=document.createElement("span");i.style.fontSize=t+"px";try{o.surroundContents(i)}catch{let a=o.extractContents();i.appendChild(a),o.insertNode(i)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let t=await navigator.clipboard.readText();document.execCommand("insertText",!1,t)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(e=>{try{e.classList.toggle("jotter-btn--active",document.queryCommandState(e.dataset.cmd))}catch{}});let t=this._toolbarEl.querySelector('[data-id="blockformat"]');if(t){let e=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),o=f.find(i=>i.tag===e);o&&(t.value=o.tag)}}_updateStatus(){let t=this._editor.innerText||"",e=t.trim()===""?0:t.trim().split(/\s+/).length,o=t.replace(/\n/g,"").length;this._wordCountEl.textContent=`${e} word${e!==1?"s":""}`,this._charCountEl.textContent=`${o} char${o!==1?"s":""}`}_saveRange(){let t=window.getSelection();return t&&t.rangeCount>0?t.getRangeAt(0).cloneRange():null}_restoreRange(t){if(!t)return;let e=window.getSelection();e.removeAllRanges(),e.addRange(t)}_emit(t,e){(this._listeners[t]||[]).forEach(o=>o(e))}getHTML(){return this._sourceMode?this._source.value:this._editor.innerHTML}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t){return this._editor.focus(),document.execCommand("insertHTML",!1,this._sanitize(t)),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}insertText(t){return this._editor.focus(),document.execCommand("insertText",!1,t),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}setHTML(t){return this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let o=b.find(i=>i.id===t)?t:"default";return this._editor.dataset.theme=o,this._options.theme=o,this._themeSelect&&(this._themeSelect.value=o),this}setEnabled(t){return this._editor.contentEditable=String(t),this._root.classList.toggle("jotter--disabled",!t),this}on(t,e){return this._listeners[t]||(this._listeners[t]=[]),this._listeners[t].push(e),this}off(t,e){return this._listeners[t]&&(this._listeners[t]=this._listeners[t].filter(o=>o!==e)),this}destroy(){let t=this.getHTML();return this._hidePopup(),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=t,this._listeners={},t}};h.toolbar=m.full;h.actions=_;h.presets=m;var x=h;export{h as JotterJS,x as default};
|
package/package.json
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "jotterjs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A lightweight, vanilla JS rich-text editor component. No dependencies.",
|
|
5
|
-
"main": "dist/jotter.min.js",
|
|
6
|
-
"module": "dist/jotter.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"import": "./dist/jotter.js",
|
|
10
|
-
"default": "./dist/jotter.min.js"
|
|
11
|
-
},
|
|
12
|
-
"./dist/jotter.min.css": "./dist/jotter.min.css"
|
|
13
|
-
},
|
|
14
|
-
"scripts": {
|
|
15
|
-
"start": "node scripts/serve.js",
|
|
16
|
-
"build": "node scripts/build.js",
|
|
17
|
-
"dev": "node scripts/serve.js",
|
|
18
|
-
"prepublishOnly": "npm run build"
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist"
|
|
22
|
-
],
|
|
23
|
-
"keywords": [
|
|
24
|
-
"rich-text",
|
|
25
|
-
"editor",
|
|
26
|
-
"wysiwyg",
|
|
27
|
-
"contenteditable",
|
|
28
|
-
"vanilla-js",
|
|
29
|
-
"html-editor",
|
|
30
|
-
"text-editor"
|
|
31
|
-
],
|
|
32
|
-
"repository": {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "https://github.com/xavier-follet/JotterJS.git"
|
|
35
|
-
},
|
|
36
|
-
"homepage": "https://github.com/xavier-follet/JotterJS",
|
|
37
|
-
"bugs": {
|
|
38
|
-
"url": "https://github.com/xavier-follet/JotterJS/issues"
|
|
39
|
-
},
|
|
40
|
-
"author": "Xavier Follet",
|
|
41
|
-
"license": "MIT",
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"esbuild": "^0.20.0"
|
|
44
|
-
}
|
|
45
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "jotterjs",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A lightweight, vanilla JS rich-text editor component. No dependencies.",
|
|
5
|
+
"main": "dist/jotter.min.js",
|
|
6
|
+
"module": "dist/jotter.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/jotter.js",
|
|
10
|
+
"default": "./dist/jotter.min.js"
|
|
11
|
+
},
|
|
12
|
+
"./dist/jotter.min.css": "./dist/jotter.min.css"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node scripts/serve.js",
|
|
16
|
+
"build": "node scripts/build.js",
|
|
17
|
+
"dev": "node scripts/serve.js",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"rich-text",
|
|
25
|
+
"editor",
|
|
26
|
+
"wysiwyg",
|
|
27
|
+
"contenteditable",
|
|
28
|
+
"vanilla-js",
|
|
29
|
+
"html-editor",
|
|
30
|
+
"text-editor"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/xavier-follet/JotterJS.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/xavier-follet/JotterJS",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/xavier-follet/JotterJS/issues"
|
|
39
|
+
},
|
|
40
|
+
"author": "Xavier Follet",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"esbuild": "^0.20.0"
|
|
44
|
+
}
|
|
45
|
+
}
|