Mercurial
annotate rich_editor/rich_editor.js @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 9c2eec61a152 |
| children |
| rev | line source |
|---|---|
| 201 | 1 /** |
| 2 * Rich Editor | |
| 3 * ----------- | |
| 4 * A vanilla JavaScript rich text editor with: | |
| 5 * - Text formatting (h1-h6, paragraphs, lists) | |
| 6 * - Image/file upload via paste, drop, click, or /upload command | |
| 7 * - Debounced auto-save | |
| 8 * - Generic callback functions for uploads and saves | |
| 9 * | |
| 10 * Usage: | |
| 11 * const editor = RichEditor.init('editor-container', { | |
| 12 * uploadCallback: async (file) => { return { url: '...', key: '...' }; }, | |
| 13 * saveCallback: async (content) => { console.log('Saved:', content); }, | |
| 14 * debounceMs: 1000, | |
| 15 * placeholder: 'Start writing...' | |
| 16 * }); | |
| 17 */ | |
| 18 | |
| 19 const RichEditor = (function() { | |
| 20 'use strict'; | |
| 21 | |
| 22 const DEFAULT_OPTIONS = { | |
| 23 uploadCallback: null, | |
| 24 saveCallback: null, | |
| 25 debounceMs: 1000, | |
| 26 placeholder: 'Start writing... (Use /upload to insert files)', | |
| 27 cloudFrontUrl: '' | |
| 28 }; | |
| 29 | |
| 30 function debounce(func, wait) { | |
| 31 let timeout; | |
| 32 return function executedFunction(...args) { | |
| 33 const later = () => { | |
| 34 clearTimeout(timeout); | |
| 35 func(...args); | |
| 36 }; | |
| 37 clearTimeout(timeout); | |
| 38 timeout = setTimeout(later, wait); | |
| 39 }; | |
| 40 } | |
| 41 | |
| 42 function createToolbar(editor) { | |
| 43 const toolbar = document.createElement('div'); | |
| 44 toolbar.className = 'rich-editor-toolbar'; | |
| 45 | |
| 46 const buttons = [ | |
|
241
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
47 { cmd: 'h1', label: '', title: 'Heading 1', icons: "/public/icons/h1.webp" }, |
|
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
48 { cmd: 'h2', label: '', title: 'Heading 2', icons: "/public/icons/h2.webp" }, |
|
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
49 { cmd: 'h3', label: '', title: 'Heading 3', icons: "/public/icons/h3.webp" }, |
| 201 | 50 { cmd: 'p', label: '¶', title: 'Paragraph' }, |
| 51 { cmd: 'ul', label: '•', title: 'Bullet List' }, | |
| 52 { cmd: 'ol', label: '1.', title: 'Numbered List' }, | |
|
241
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
53 { cmd: 'bold', label: '', title: 'Bold', icons: "/public/icons/bold.webp" }, |
| 201 | 54 { cmd: 'separator' }, |
|
241
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
55 { cmd: 'link', label: '', title: 'Insert Link', icons: "/public/icons/link.webp" }, |
|
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
56 { cmd: 'notelink', label: '', title: 'Link to Note', icons: "/public/icons/binder.webp" }, |
|
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
57 { cmd: 'upload', label: '', title: 'Upload File', icons: "/public/icons/clipy.webp" }, |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
58 { cmd: 'separator' }, |
|
241
9c2eec61a152
[assets] Generate site images as WebP with Bazel
MrJuneJune <me@mrjunejune.com>
parents:
208
diff
changeset
|
59 { cmd: 'readonly', label: '', title: 'readonly', icons: "/public/icons/book.webp" }, |
| 201 | 60 ]; |
| 61 | |
| 62 buttons.forEach(btn => { | |
| 63 if (btn.cmd === 'separator') { | |
| 64 const sep = document.createElement('span'); | |
| 65 sep.className = 'rich-editor-separator'; | |
| 66 toolbar.appendChild(sep); | |
| 67 return; | |
| 68 } | |
| 69 | |
| 70 const button = document.createElement('button'); | |
| 71 button.type = 'button'; | |
| 72 button.className = 'rich-editor-btn'; | |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
73 if (btn.icons) |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
74 button.style.backgroundImage = `url("${btn.icons}")`; |
| 201 | 75 button.textContent = btn.label; |
| 76 button.title = btn.title; | |
| 77 button.dataset.cmd = btn.cmd; | |
| 78 | |
| 79 button.addEventListener('click', (e) => { | |
| 80 e.preventDefault(); | |
| 81 editor.execCommand(btn.cmd); | |
| 82 }); | |
| 83 | |
| 84 toolbar.appendChild(button); | |
| 85 }); | |
| 86 | |
| 87 return toolbar; | |
| 88 } | |
| 89 | |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
90 function ensureViewportMeta() { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
91 // Check if viewport meta tag exists |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
92 let viewport = document.querySelector('meta[name="viewport"]'); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
93 if (!viewport) { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
94 viewport = document.createElement('meta'); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
95 viewport.name = 'viewport'; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
96 viewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=5.0'; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
97 document.head.appendChild(viewport); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
98 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
99 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
100 |
| 201 | 101 function createStyles() { |
| 102 if (document.getElementById('rich-editor-styles')) return; | |
| 103 | |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
104 ensureViewportMeta(); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
105 |
| 201 | 106 const style = document.createElement('style'); |
| 107 style.id = 'rich-editor-styles'; | |
| 108 style.textContent = ` | |
| 109 .rich-editor-container { | |
| 110 border: 1px solid #ccc; | |
| 111 border-radius: 4px; | |
| 112 overflow: hidden; | |
| 113 } | |
| 114 | |
| 115 .rich-editor-toolbar { | |
| 116 display: flex; | |
| 117 flex-wrap: wrap; | |
| 118 gap: 4px; | |
| 119 padding: 8px; | |
| 120 background: #f5f5f5; | |
| 121 border-bottom: 1px solid #ccc; | |
| 122 } | |
| 123 | |
| 124 .rich-editor-btn { | |
| 125 padding: 6px 12px; | |
| 126 border: 1px solid #ccc; | |
| 127 border-radius: 4px; | |
| 128 background: #fff; | |
| 129 cursor: pointer; | |
| 130 font-size: 14px; | |
| 131 min-width: 32px; | |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
132 background-repeat: no-repeat; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
133 background-position: center; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
134 background-size: 24px 24px; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
135 border: none; |
| 201 | 136 } |
| 137 | |
| 138 .rich-editor-btn:hover { | |
| 139 background: #e9e9e9; | |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
140 background-repeat: no-repeat; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
141 background-position: center; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
142 background-size: 24px 24px; |
| 201 | 143 } |
| 144 | |
| 145 .rich-editor-btn:active { | |
| 146 background: #ddd; | |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
147 background-repeat: no-repeat; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
148 background-position: center; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
149 background-size: 24px 24px; |
| 201 | 150 } |
| 151 | |
| 152 .rich-editor-separator { | |
| 153 width: 1px; | |
| 154 background: #ccc; | |
| 155 margin: 0 4px; | |
| 156 } | |
| 157 | |
| 158 .rich-editor-content { | |
| 159 min-height: 300px; | |
| 160 padding: 16px; | |
| 161 outline: none; | |
| 162 overflow-y: auto; | |
| 163 } | |
| 164 | |
| 165 .rich-editor-content:empty:before { | |
| 166 content: attr(data-placeholder); | |
| 167 color: #999; | |
| 168 pointer-events: none; | |
| 169 } | |
| 170 | |
| 171 .rich-editor-content img { | |
| 172 max-width: 100%; | |
| 173 height: auto; | |
| 174 border-radius: 4px; | |
| 175 margin: 8px 0; | |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
176 cursor: pointer; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
177 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
178 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
179 .rich-editor-content img.selected { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
180 outline: 2px solid #0078ff; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
181 outline-offset: 2px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
182 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
183 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
184 .rich-editor-resize-wrapper { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
185 position: relative; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
186 display: inline-block; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
187 margin: 8px 0; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
188 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
189 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
190 .rich-editor-resize-handle { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
191 position: absolute; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
192 width: 12px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
193 height: 12px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
194 background: #0078ff; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
195 border: 2px solid white; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
196 border-radius: 50%; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
197 cursor: nwse-resize; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
198 z-index: 10; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
199 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
200 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
201 .rich-editor-resize-handle.nw { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
202 top: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
203 left: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
204 cursor: nwse-resize; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
205 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
206 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
207 .rich-editor-resize-handle.ne { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
208 top: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
209 right: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
210 cursor: nesw-resize; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
211 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
212 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
213 .rich-editor-resize-handle.sw { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
214 bottom: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
215 left: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
216 cursor: nesw-resize; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
217 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
218 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
219 .rich-editor-resize-handle.se { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
220 bottom: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
221 right: -6px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
222 cursor: nwse-resize; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
223 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
224 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
225 .rich-editor-size-input { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
226 position: absolute; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
227 top: -35px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
228 left: 50%; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
229 transform: translateX(-50%); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
230 background: white; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
231 border: 1px solid #ccc; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
232 padding: 4px 8px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
233 border-radius: 4px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
234 font-size: 12px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
235 display: flex; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
236 gap: 8px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
237 align-items: center; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
238 box-shadow: 0 2px 8px rgba(0,0,0,0.1); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
239 z-index: 11; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
240 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
241 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
242 .rich-editor-size-input input { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
243 width: 60px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
244 padding: 2px 4px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
245 border: 1px solid #ccc; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
246 border-radius: 2px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
247 font-size: 12px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
248 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
249 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
250 .rich-editor-size-input label { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
251 font-size: 11px; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
252 color: #666; |
| 201 | 253 } |
| 254 | |
| 255 .rich-editor-content a { | |
| 256 color: #0078ff; | |
| 257 text-decoration: none; | |
| 258 } | |
| 259 | |
| 260 .rich-editor-content a:hover { | |
| 261 text-decoration: underline; | |
| 262 } | |
| 263 | |
| 264 .rich-editor-content a.note-link { | |
| 265 background: #e8f4ff; | |
| 266 padding: 2px 6px; | |
| 267 border-radius: 3px; | |
| 268 } | |
| 269 | |
| 270 .rich-editor-content a.note-link:hover { | |
| 271 background: #d0e8ff; | |
| 272 } | |
| 273 | |
| 274 .rich-editor-content .upload-placeholder { | |
| 275 display: inline-block; | |
| 276 padding: 8px 16px; | |
| 277 background: #f0f0f0; | |
| 278 border-radius: 4px; | |
| 279 color: #666; | |
| 280 font-style: italic; | |
| 281 } | |
| 282 | |
| 283 .rich-editor-content .upload-placeholder.loading { | |
| 284 padding: 8px 16px; | |
| 285 background: #fffacd; | |
| 286 border-radius: 4px; | |
| 287 animation: pulse 1.5s ease-in-out infinite; | |
| 288 } | |
| 289 | |
| 290 @keyframes pulse { | |
| 291 0%, 100% { opacity: 1; } | |
| 292 50% { opacity: 0.6; } | |
| 293 } | |
| 294 | |
| 295 .rich-editor-upload-overlay { | |
| 296 position: absolute; | |
| 297 top: 0; | |
| 298 left: 0; | |
| 299 right: 0; | |
| 300 bottom: 0; | |
| 301 background: rgba(0, 120, 255, 0.1); | |
| 302 border: 2px dashed #0078ff; | |
| 303 display: flex; | |
| 304 align-items: center; | |
| 305 justify-content: center; | |
| 306 font-size: 18px; | |
| 307 color: #0078ff; | |
| 308 pointer-events: none; | |
| 309 z-index: 10; | |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
310 padding: 20px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
311 text-align: center; |
| 201 | 312 } |
| 313 | |
| 314 .rich-editor-status { | |
| 315 padding: 4px 8px; | |
| 316 font-size: 12px; | |
| 317 color: #666; | |
| 318 background: #f9f9f9; | |
| 319 border-top: 1px solid #eee; | |
| 320 } | |
| 321 | |
| 322 .rich-editor-file-input { | |
| 323 display: none; | |
| 324 } | |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
325 |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
326 ol,ul { |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
327 padding: 16px; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
328 } |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
329 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
330 /* Mobile optimizations */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
331 @media (max-width: 720px) { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
332 .rich-editor-toolbar { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
333 padding: 6px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
334 gap: 3px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
335 overflow-x: auto; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
336 -webkit-overflow-scrolling: touch; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
337 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
338 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
339 .rich-editor-btn { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
340 min-width: 44px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
341 min-height: 44px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
342 padding: 8px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
343 flex-shrink: 0; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
344 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
345 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
346 .rich-editor-content { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
347 min-height: 200px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
348 padding: 12px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
349 font-size: 16px; /* Prevents iOS zoom */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
350 line-height: 1.6; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
351 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
352 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
353 .rich-editor-content h1 { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
354 font-size: 1.75em; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
355 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
356 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
357 .rich-editor-content h2 { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
358 font-size: 1.5em; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
359 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
360 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
361 .rich-editor-content h3 { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
362 font-size: 1.25em; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
363 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
364 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
365 .rich-editor-status { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
366 font-size: 11px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
367 padding: 6px 8px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
368 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
369 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
370 /* Mobile-friendly image resizing */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
371 .rich-editor-resize-handle { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
372 width: 20px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
373 height: 20px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
374 border-width: 3px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
375 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
376 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
377 .rich-editor-resize-handle.nw { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
378 top: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
379 left: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
380 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
381 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
382 .rich-editor-resize-handle.ne { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
383 top: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
384 right: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
385 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
386 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
387 .rich-editor-resize-handle.sw { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
388 bottom: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
389 left: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
390 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
391 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
392 .rich-editor-resize-handle.se { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
393 bottom: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
394 right: -10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
395 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
396 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
397 .rich-editor-size-input { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
398 top: -40px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
399 padding: 6px 10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
400 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
401 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
402 .rich-editor-size-input input { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
403 width: 70px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
404 padding: 4px 6px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
405 font-size: 16px; /* Prevents iOS zoom */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
406 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
407 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
408 /* Better touch targets for images */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
409 .rich-editor-content img { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
410 margin: 12px 0; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
411 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
412 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
413 /* Hide drop overlay text on small screens */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
414 .rich-editor-upload-overlay { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
415 font-size: 16px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
416 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
417 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
418 /* Make link/note dialogs mobile-friendly */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
419 .rich-editor-content a { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
420 padding: 2px 0; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
421 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
422 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
423 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
424 /* Extra small devices */ |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
425 @media (max-width: 480px) { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
426 .rich-editor-toolbar { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
427 padding: 4px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
428 gap: 2px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
429 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
430 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
431 .rich-editor-btn { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
432 min-width: 40px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
433 min-height: 40px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
434 padding: 6px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
435 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
436 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
437 .rich-editor-content { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
438 padding: 10px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
439 font-size: 16px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
440 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
441 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
442 .rich-editor-size-input { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
443 font-size: 11px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
444 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
445 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
446 .rich-editor-size-input input { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
447 width: 60px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
448 font-size: 14px; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
449 } |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
450 } |
| 201 | 451 `; |
| 452 document.head.appendChild(style); | |
| 453 } | |
| 454 | |
| 455 class Editor { | |
| 456 constructor(elementId, options) { | |
| 457 this.options = { ...DEFAULT_OPTIONS, ...options }; | |
| 458 this.container = document.getElementById(elementId); | |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
459 this.state = { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
460 readOnly: false, |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
461 selectedImage: null, |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
462 resizing: false |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
463 }; |
| 201 | 464 |
| 465 if (!this.container) { | |
| 466 throw new Error(`Element with id "${elementId}" not found`); | |
| 467 } | |
| 468 | |
| 469 this.init(); | |
| 470 } | |
| 471 | |
| 472 init() { | |
| 473 createStyles(); | |
| 474 | |
| 475 // Create wrapper | |
| 476 this.wrapper = document.createElement('div'); | |
| 477 this.wrapper.className = 'rich-editor-container'; | |
| 478 this.wrapper.style.position = 'relative'; | |
| 479 | |
| 480 // Create toolbar | |
| 481 this.toolbar = createToolbar(this); | |
| 482 this.wrapper.appendChild(this.toolbar); | |
| 483 | |
| 484 // Create content area | |
| 485 this.content = document.createElement('div'); | |
| 486 this.content.className = 'rich-editor-content'; | |
| 487 this.content.contentEditable = true; | |
| 488 this.content.dataset.placeholder = this.options.placeholder; | |
| 489 this.wrapper.appendChild(this.content); | |
| 490 | |
| 491 // Create status bar | |
| 492 this.status = document.createElement('div'); | |
| 493 this.status.className = 'rich-editor-status'; | |
| 494 this.status.textContent = 'Ready'; | |
| 495 this.wrapper.appendChild(this.status); | |
| 496 | |
| 497 // Create hidden file input | |
| 498 this.fileInput = document.createElement('input'); | |
| 499 this.fileInput.type = 'file'; | |
| 500 this.fileInput.className = 'rich-editor-file-input'; | |
| 501 this.fileInput.multiple = true; | |
| 502 this.fileInput.accept = 'image/*,application/pdf,.doc,.docx,.txt'; | |
| 503 this.wrapper.appendChild(this.fileInput); | |
| 504 | |
| 505 // Add to container | |
| 506 this.container.appendChild(this.wrapper); | |
| 507 | |
| 508 // Setup event listeners | |
| 509 this.setupEvents(); | |
| 510 | |
| 511 // Setup debounced save | |
| 512 if (this.options.saveCallback) { | |
| 513 this.debouncedSave = debounce(() => { | |
| 514 this.save(); | |
| 515 }, this.options.debounceMs); | |
| 516 } | |
| 517 } | |
| 518 | |
| 519 setupEvents() { | |
| 520 // Input events for auto-save | |
| 521 this.content.addEventListener('input', () => { | |
| 522 if (this.debouncedSave) { | |
| 523 this.setStatus('Editing...'); | |
| 524 this.debouncedSave(); | |
| 525 } | |
| 526 }); | |
| 527 | |
| 528 // Handle paste | |
| 529 this.content.addEventListener('paste', (e) => { | |
| 530 const items = e.clipboardData?.items; | |
| 531 if (!items) return; | |
| 532 | |
| 533 for (const item of items) { | |
| 534 if (item.type.startsWith('image/') || item.kind === 'file') { | |
| 535 e.preventDefault(); | |
| 536 const file = item.getAsFile(); | |
| 537 if (file) this.uploadFile(file); | |
| 538 return; | |
| 539 } | |
| 540 } | |
| 541 }); | |
| 542 | |
| 543 // Handle drop | |
| 544 this.content.addEventListener('dragover', (e) => { | |
| 545 e.preventDefault(); | |
| 546 this.showDropOverlay(); | |
| 547 }); | |
| 548 | |
| 549 this.content.addEventListener('dragleave', (e) => { | |
| 550 e.preventDefault(); | |
| 551 this.hideDropOverlay(); | |
| 552 }); | |
| 553 | |
| 554 this.content.addEventListener('drop', (e) => { | |
| 555 e.preventDefault(); | |
| 556 this.hideDropOverlay(); | |
| 557 | |
| 558 const files = e.dataTransfer?.files; | |
| 559 if (files && files.length > 0) { | |
| 560 for (const file of files) { | |
| 561 this.uploadFile(file); | |
| 562 } | |
| 563 } | |
| 564 }); | |
| 565 | |
| 566 // Handle /upload command | |
| 567 this.content.addEventListener('keydown', (e) => { | |
| 568 if (e.key === 'Enter') { | |
| 569 const selection = window.getSelection(); | |
| 570 const node = selection.anchorNode; | |
| 571 if (node && node.textContent) { | |
| 572 const text = node.textContent; | |
| 573 if (text.trim() === '/upload') { | |
| 574 e.preventDefault(); | |
| 575 node.textContent = ''; | |
| 576 this.triggerFileUpload(); | |
| 577 } | |
| 578 } | |
| 579 } | |
| 580 }); | |
| 581 | |
| 582 // File input change | |
| 583 this.fileInput.addEventListener('change', (e) => { | |
| 584 const files = e.target.files; | |
| 585 if (files && files.length > 0) { | |
| 586 for (const file of files) { | |
| 587 this.uploadFile(file); | |
| 588 } | |
| 589 } | |
| 590 this.fileInput.value = ''; | |
| 591 }); | |
| 592 | |
| 593 // Handle keyboard shortcuts | |
| 594 this.content.addEventListener('keydown', (e) => { | |
| 595 if (e.ctrlKey || e.metaKey) { | |
| 596 switch (e.key.toLowerCase()) { | |
| 597 case 'b': | |
| 598 e.preventDefault(); | |
| 599 this.execCommand('bold'); | |
| 600 break; | |
| 601 case 'i': | |
| 602 e.preventDefault(); | |
| 603 this.execCommand('italic'); | |
| 604 break; | |
| 605 case 's': | |
| 606 e.preventDefault(); | |
| 607 this.save(); | |
| 608 break; | |
| 609 } | |
| 610 } | |
| 611 }); | |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
612 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
613 // Handle image clicks for resizing |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
614 this.content.addEventListener('click', (e) => { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
615 if (e.target.tagName === 'IMG') { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
616 e.preventDefault(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
617 this.selectImage(e.target); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
618 } else if (!e.target.closest('.rich-editor-resize-wrapper')) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
619 this.deselectImage(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
620 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
621 }); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
622 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
623 // Deselect image when clicking outside |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
624 document.addEventListener('click', (e) => { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
625 if (!this.content.contains(e.target)) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
626 this.deselectImage(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
627 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
628 }); |
| 201 | 629 } |
| 630 | |
| 631 showDropOverlay() { | |
| 632 if (this.dropOverlay) return; | |
| 633 | |
| 634 this.dropOverlay = document.createElement('div'); | |
| 635 this.dropOverlay.className = 'rich-editor-upload-overlay'; | |
| 636 this.dropOverlay.textContent = 'Drop files here to upload'; | |
| 637 this.wrapper.appendChild(this.dropOverlay); | |
| 638 } | |
| 639 | |
| 640 hideDropOverlay() { | |
| 641 if (this.dropOverlay) { | |
| 642 this.dropOverlay.remove(); | |
| 643 this.dropOverlay = null; | |
| 644 } | |
| 645 } | |
| 646 | |
| 647 triggerFileUpload() { | |
| 648 this.fileInput.click(); | |
| 649 } | |
| 650 | |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
651 readOnly() { |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
652 this.state.readOnly = !this.state.readOnly; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
653 |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
654 if (this.state.readOnly) |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
655 { |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
656 this.content.contentEditable = false; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
657 this.setStatus('Read-only mode'); |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
658 const buttons = this.toolbar.querySelectorAll('.rich-editor-btn'); |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
659 buttons.forEach(btn => { |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
660 if (btn.dataset.cmd !== 'readonly') { |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
661 btn.disabled = true; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
662 } |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
663 }); |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
664 } |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
665 else |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
666 { |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
667 // Enable editing |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
668 this.content.contentEditable = true; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
669 this.content.style.backgroundColor = ''; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
670 this.setStatus('Ready'); |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
671 |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
672 // Enable toolbar buttons |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
673 const buttons = this.toolbar.querySelectorAll('.rich-editor-btn'); |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
674 buttons.forEach(btn => { |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
675 btn.disabled = false; |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
676 }); |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
677 } |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
678 } |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
679 |
| 201 | 680 async uploadFile(file) { |
| 681 if (!this.options.uploadCallback) { | |
| 682 console.warn('No upload callback configured'); | |
| 683 return; | |
| 684 } | |
| 685 | |
| 686 // Insert loading placeholder | |
| 687 const placeholder = document.createElement('div'); | |
| 688 placeholder.className = 'upload-placeholder loading'; | |
| 689 placeholder.textContent = file.type.startsWith('image/') | |
| 690 ? `Processing ${file.name}... ⏳` | |
| 691 : `Uploading ${file.name}...`; | |
| 692 | |
| 693 this.insertAtCursor(placeholder); | |
| 694 | |
| 695 this.setStatus(`Uploading ${file.name}...`); | |
| 696 | |
| 697 try { | |
| 698 const result = await this.options.uploadCallback(file); | |
| 699 | |
| 700 if (result && result.url) { | |
| 701 // Replace placeholder with actual content | |
| 702 if (file.type.startsWith('image/')) { | |
| 703 const img = document.createElement('img'); | |
| 704 img.src = result.url; | |
| 705 img.alt = file.name; | |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
706 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
707 // Store original dimensions when image loads |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
708 img.onload = () => { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
709 img.dataset.originalWidth = img.naturalWidth; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
710 img.dataset.originalHeight = img.naturalHeight; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
711 }; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
712 |
| 201 | 713 placeholder.replaceWith(img); |
| 714 } else { | |
| 715 const link = document.createElement('a'); | |
| 716 link.href = result.url; | |
| 717 link.textContent = file.name; | |
| 718 link.target = '_blank'; | |
| 719 placeholder.replaceWith(link); | |
| 720 } | |
| 721 | |
| 722 this.setStatus(`Uploaded ${file.name}`); | |
| 723 if (this.debouncedSave) this.debouncedSave(); | |
| 724 } else { | |
| 725 placeholder.textContent = `Failed to upload ${file.name}`; | |
| 726 placeholder.className = 'upload-placeholder'; | |
| 727 this.setStatus('Upload failed'); | |
| 728 } | |
| 729 } catch (error) { | |
| 730 console.error('Upload error:', error); | |
| 731 placeholder.textContent = `Error: ${error.message}`; | |
| 732 placeholder.className = 'upload-placeholder'; | |
| 733 this.setStatus('Upload error'); | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 insertAtCursor(element) { | |
| 738 const selection = window.getSelection(); | |
| 739 if (selection.rangeCount > 0) { | |
| 740 const range = selection.getRangeAt(0); | |
| 741 range.deleteContents(); | |
| 742 range.insertNode(element); | |
| 743 range.setStartAfter(element); | |
| 744 range.collapse(true); | |
| 745 selection.removeAllRanges(); | |
| 746 selection.addRange(range); | |
| 747 } else { | |
| 748 this.content.appendChild(element); | |
| 749 } | |
| 750 this.content.focus(); | |
| 751 } | |
| 752 | |
| 753 execCommand(cmd) { | |
| 754 this.content.focus(); | |
| 755 | |
| 756 switch (cmd) { | |
| 757 case 'h1': | |
| 758 case 'h2': | |
| 759 case 'h3': | |
| 760 case 'h4': | |
| 761 case 'h5': | |
| 762 case 'h6': | |
| 763 document.execCommand('formatBlock', false, cmd); | |
| 764 break; | |
| 765 case 'p': | |
| 766 document.execCommand('formatBlock', false, 'p'); | |
| 767 break; | |
| 768 case 'ul': | |
| 769 document.execCommand('insertUnorderedList', false, null); | |
| 770 break; | |
| 771 case 'ol': | |
| 772 document.execCommand('insertOrderedList', false, null); | |
| 773 break; | |
| 774 case 'bold': | |
| 775 document.execCommand('bold', false, null); | |
| 776 break; | |
| 777 case 'italic': | |
| 778 document.execCommand('italic', false, null); | |
| 779 break; | |
| 780 case 'link': | |
| 781 this.insertLink(); | |
| 782 break; | |
| 783 case 'notelink': | |
| 784 this.insertNoteLink(); | |
| 785 break; | |
| 786 case 'upload': | |
| 787 this.triggerFileUpload(); | |
| 788 break; | |
|
204
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
789 case 'readonly': |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
790 this.readOnly(); |
|
e5aed6c36672
[Notes] Added icons and updated styling a bit. Probalby usable now.
MrJuneJune <me@mrjunejune.com>
parents:
201
diff
changeset
|
791 break; |
| 201 | 792 } |
| 793 | |
| 794 if (this.debouncedSave) this.debouncedSave(); | |
| 795 } | |
| 796 | |
| 797 insertLink() { | |
| 798 const url = prompt('Enter URL:'); | |
| 799 if (!url) return; | |
| 800 | |
| 801 const selection = window.getSelection(); | |
| 802 const selectedText = selection.toString() || url; | |
| 803 | |
| 804 const link = document.createElement('a'); | |
| 805 link.href = url; | |
| 806 link.textContent = selectedText; | |
| 807 link.target = '_blank'; | |
| 808 | |
| 809 if (selection.rangeCount > 0) { | |
| 810 const range = selection.getRangeAt(0); | |
| 811 range.deleteContents(); | |
| 812 range.insertNode(link); | |
| 813 range.setStartAfter(link); | |
| 814 range.collapse(true); | |
| 815 selection.removeAllRanges(); | |
| 816 selection.addRange(range); | |
| 817 } | |
| 818 } | |
| 819 | |
| 820 insertNoteLink() { | |
| 821 const noteId = prompt('Enter note ID (e.g., my-ideas):'); | |
| 822 if (!noteId) return; | |
| 823 | |
| 824 const sanitizedId = noteId.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-'); | |
| 825 const selection = window.getSelection(); | |
| 826 const selectedText = selection.toString() || sanitizedId; | |
| 827 | |
| 828 const link = document.createElement('a'); | |
| 829 link.href = '/notes/' + encodeURIComponent(sanitizedId); | |
| 830 link.textContent = selectedText; | |
| 831 link.className = 'note-link'; | |
| 832 | |
| 833 if (selection.rangeCount > 0) { | |
| 834 const range = selection.getRangeAt(0); | |
| 835 range.deleteContents(); | |
| 836 range.insertNode(link); | |
| 837 range.setStartAfter(link); | |
| 838 range.collapse(true); | |
| 839 selection.removeAllRanges(); | |
| 840 selection.addRange(range); | |
| 841 } | |
| 842 } | |
| 843 | |
| 844 setStatus(text) { | |
| 845 this.status.textContent = text; | |
| 846 } | |
| 847 | |
| 848 async save() { | |
| 849 if (!this.options.saveCallback) return; | |
| 850 | |
| 851 this.setStatus('Saving...'); | |
| 852 | |
| 853 try { | |
| 854 await this.options.saveCallback(this.getContent()); | |
| 855 this.setStatus('Saved'); | |
| 856 } catch (error) { | |
| 857 console.error('Save error:', error); | |
| 858 this.setStatus('Save failed'); | |
| 859 } | |
| 860 } | |
| 861 | |
| 862 getContent() { | |
| 863 return this.content.innerHTML; | |
| 864 } | |
| 865 | |
| 866 setContent(html) { | |
| 867 this.content.innerHTML = html; | |
| 868 } | |
| 869 | |
| 870 getText() { | |
| 871 return this.content.textContent; | |
| 872 } | |
| 873 | |
| 874 clear() { | |
| 875 this.content.innerHTML = ''; | |
| 876 } | |
| 877 | |
| 878 focus() { | |
| 879 this.content.focus(); | |
| 880 } | |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
881 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
882 selectImage(img) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
883 if (this.state.readOnly) return; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
884 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
885 // Deselect previous image |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
886 this.deselectImage(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
887 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
888 this.state.selectedImage = img; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
889 img.classList.add('selected'); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
890 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
891 // Wrap image in resize container if not already wrapped |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
892 if (!img.parentElement.classList.contains('rich-editor-resize-wrapper')) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
893 const wrapper = document.createElement('div'); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
894 wrapper.className = 'rich-editor-resize-wrapper'; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
895 wrapper.contentEditable = false; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
896 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
897 // Store original dimensions if not set |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
898 if (!img.dataset.originalWidth) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
899 img.dataset.originalWidth = img.naturalWidth; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
900 img.dataset.originalHeight = img.naturalHeight; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
901 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
902 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
903 img.parentNode.insertBefore(wrapper, img); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
904 wrapper.appendChild(img); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
905 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
906 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
907 const wrapper = img.parentElement; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
908 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
909 // Add resize handles |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
910 const corners = ['nw', 'ne', 'sw', 'se']; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
911 corners.forEach(corner => { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
912 const handle = document.createElement('div'); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
913 handle.className = `rich-editor-resize-handle ${corner}`; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
914 handle.dataset.corner = corner; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
915 |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
916 // Mouse events |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
917 handle.addEventListener('mousedown', (e) => { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
918 e.preventDefault(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
919 e.stopPropagation(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
920 this.startResize(e, img, corner); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
921 }); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
922 |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
923 // Touch events for mobile |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
924 handle.addEventListener('touchstart', (e) => { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
925 e.preventDefault(); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
926 e.stopPropagation(); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
927 const touch = e.touches[0]; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
928 this.startResize(touch, img, corner, true); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
929 }); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
930 |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
931 wrapper.appendChild(handle); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
932 }); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
933 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
934 // Add size input |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
935 const sizeInput = document.createElement('div'); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
936 sizeInput.className = 'rich-editor-size-input'; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
937 sizeInput.innerHTML = ` |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
938 <label>Width:</label> |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
939 <input type="number" class="width-input" value="${img.width}" min="50" max="2000"> |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
940 <label>px</label> |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
941 `; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
942 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
943 const widthInput = sizeInput.querySelector('.width-input'); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
944 widthInput.addEventListener('input', (e) => { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
945 const newWidth = parseInt(e.target.value); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
946 if (newWidth && newWidth > 0) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
947 this.resizeImage(img, newWidth); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
948 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
949 }); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
950 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
951 wrapper.appendChild(sizeInput); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
952 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
953 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
954 deselectImage() { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
955 if (!this.state.selectedImage) return; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
956 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
957 const img = this.state.selectedImage; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
958 img.classList.remove('selected'); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
959 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
960 const wrapper = img.parentElement; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
961 if (wrapper && wrapper.classList.contains('rich-editor-resize-wrapper')) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
962 // Remove resize handles and size input |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
963 wrapper.querySelectorAll('.rich-editor-resize-handle, .rich-editor-size-input').forEach(el => el.remove()); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
964 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
965 // Unwrap image |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
966 wrapper.parentNode.insertBefore(img, wrapper); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
967 wrapper.remove(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
968 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
969 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
970 this.state.selectedImage = null; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
971 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
972 |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
973 startResize(e, img, corner, isTouch = false) { |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
974 this.state.resizing = true; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
975 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
976 const startX = e.clientX; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
977 const startY = e.clientY; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
978 const startWidth = img.width; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
979 const startHeight = img.height; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
980 const aspectRatio = startWidth / startHeight; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
981 |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
982 const onMove = (e) => { |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
983 if (!this.state.resizing) return; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
984 |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
985 const clientX = isTouch ? e.touches[0].clientX : e.clientX; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
986 const clientY = isTouch ? e.touches[0].clientY : e.clientY; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
987 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
988 let deltaX = clientX - startX; |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
989 let deltaY = clientY - startY; |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
990 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
991 // Adjust delta based on corner |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
992 if (corner === 'nw' || corner === 'sw') { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
993 deltaX = -deltaX; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
994 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
995 if (corner === 'nw' || corner === 'ne') { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
996 deltaY = -deltaY; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
997 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
998 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
999 // Use the larger delta to maintain aspect ratio |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1000 const delta = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1001 const newWidth = Math.max(50, startWidth + delta); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1002 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1003 this.resizeImage(img, newWidth, aspectRatio); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1004 }; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1005 |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1006 const onEnd = () => { |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1007 this.state.resizing = false; |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1008 |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1009 if (isTouch) { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1010 document.removeEventListener('touchmove', onMove); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1011 document.removeEventListener('touchend', onEnd); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1012 } else { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1013 document.removeEventListener('mousemove', onMove); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1014 document.removeEventListener('mouseup', onEnd); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1015 } |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1016 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1017 // Update width input |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1018 const wrapper = img.parentElement; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1019 const widthInput = wrapper.querySelector('.width-input'); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1020 if (widthInput) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1021 widthInput.value = img.width; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1022 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1023 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1024 if (this.debouncedSave) this.debouncedSave(); |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1025 }; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1026 |
|
208
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1027 if (isTouch) { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1028 document.addEventListener('touchmove', onMove, { passive: false }); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1029 document.addEventListener('touchend', onEnd); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1030 } else { |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1031 document.addEventListener('mousemove', onMove); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1032 document.addEventListener('mouseup', onEnd); |
|
5d3e116dd745
[MrJuneJune] made it more mobile friendly.
MrJuneJune <me@mrjunejune.com>
parents:
206
diff
changeset
|
1033 } |
|
206
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1034 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1035 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1036 resizeImage(img, newWidth, aspectRatio = null) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1037 if (!aspectRatio) { |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1038 const originalWidth = parseInt(img.dataset.originalWidth) || img.naturalWidth; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1039 const originalHeight = parseInt(img.dataset.originalHeight) || img.naturalHeight; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1040 aspectRatio = originalWidth / originalHeight; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1041 } |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1042 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1043 const newHeight = newWidth / aspectRatio; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1044 |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1045 img.width = newWidth; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1046 img.height = newHeight; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1047 img.style.width = newWidth + 'px'; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1048 img.style.height = newHeight + 'px'; |
|
240337164a80
[Seobeo] SSL should be used for large file as well lol.
MrJuneJune <me@mrjunejune.com>
parents:
204
diff
changeset
|
1049 } |
| 201 | 1050 } |
| 1051 | |
| 1052 return { | |
| 1053 init: function(elementId, options) { | |
| 1054 return new Editor(elementId, options); | |
| 1055 } | |
| 1056 }; | |
| 1057 })(); | |
| 1058 | |
| 1059 // Export for module systems | |
| 1060 if (typeof module !== 'undefined' && module.exports) { | |
| 1061 module.exports = RichEditor; | |
| 1062 } |