Editor

The editor is TinyMCE

TinyMCE 8.10.0 is vendored in this repository as one file, dist/tinymce.min.js (core, the silver theme, the DOM model, the icons, the plugins autoresize, code, fullscreen, image, link, lists and media, and the oxide skins, light and dark). It is under the GPL (license_key: "gpl"), see vendor/tinymce/LICENSE.md. It is not part of tsunami.min.js: a page with an editor loads it with its own script first. Pages without an editor do not load it. Nothing else is fetched at run time.

<script src="https://cdn.jsdelivr.net/gh/Djirasak/tsunami-framework@v0.1.3/dist/tinymce.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/Djirasak/tsunami-framework@v0.1.3/dist/tsunami.min.js"></script>

If a textarea.editor is on a page and TinyMCE is not loaded, the script throws an error that says so.

Source code in a code editor

The code button opens TinyMCE's own dialog, a plain textarea. If the page also loads Monaco, the button opens a dialog with a code editor instead: coloured HTML, line numbers and the page's theme, and Save sets the HTML back. The editors on this page do that. Monaco costs about 40 KB when the page loads and about 6 MB the first time a code editor opens, so a page that does not need it leaves it out and keeps the plain dialog. Load Monaco's loader after TinyMCE and before the Tsunami script.

<script src="https://cdn.jsdelivr.net/gh/Djirasak/tsunami-framework@v0.1.3/dist/tinymce.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/Djirasak/tsunami-framework@v0.1.3/dist/monaco/vs/loader.js"></script>
<script src="https://cdn.jsdelivr.net/gh/Djirasak/tsunami-framework@v0.1.3/dist/tsunami.min.js"></script>

A textarea with the class editor

The textarea's text is the starting HTML. Without the script, or before TinyMCE has started, the textarea is all there is, and it already has the height of the editor.

The placeholder shows in an empty editor

A form sends the HTML

The textarea always holds the editor's HTML, so a form sent by the browser, new FormData(form) and textarea.value all carry it. No call to save is needed.

Nothing sent yet.

The save bar sees edits, Reset puts the text back

Typing, a toolbar command, undo and redo make the textarea send a bubbling input event, which is all the save bar needs. Discard (a reset button) restores the text the page started with.

A script reads and sets the HTML through the element

The element's editor property is the TinyMCE instance, null until it has started.

Nothing read yet.

Data attributes set the toolbar and the height

data-toolbar="none" hides the toolbar. data-menubar and data-statusbar turn those bars on.

Other TinyMCE options go through mount()

mount(el, options) takes any TinyMCE option. They win over the defaults and over the data attributes. The safety settings below are defaults too, so an option can loosen them: think before passing valid_elements.

Read only, disabled and invalid

readonly, disabled (also from a disabled fieldset) and required are read from the textarea when the editor starts. aria-invalid="true" gives the frame the danger colour. A required, empty editor stops the form and the browser shows its message at the editor.

A summary is required.

What the editor allows

The HTML may be shown by other apps, so the editor does not produce just anything. Pasted text, the textarea's text and the source code dialog all pass the same filter.

  • Elements: p br hr h1-h6 pre blockquote strong b em i u s strike code sub sup ul ol li span a img video audio. Anything else (script, style, iframe, object, embed, form, svg, ...) is removed.
  • Attributes: href title target rel on links, src alt title width height on images, src controls width height on video and src controls on audio. No class, no id, no on* handler.
  • Styles: text-align and padding-left / padding-right (the alignment and indent buttons), and width / height on images and video.
  • Addresses: http, https and mailto, plus relative and # addresses. A javascript:, data: or other address is emptied. A link that opens a new tab gets rel="noopener". Pasted images are not kept.
  • Media: the dialog takes the address of a video (mp4 webm ogv mov) or audio file and writes a video or audio element. Embed codes and pages such as YouTube are refused, since they need an iframe.

The editor does not upload images. The image dialog takes an address; an app that wants uploads passes TinyMCE's images_upload_handler through mount().

Look

The text being edited loads the Tsunami stylesheet and takes the typography class, in the page's theme. The skin follows the theme too: oxide in light and oxide-dark in dark, and a tsunami:themechange starts the editor again with the other skin (the text stays; undo history does not). TinyMCE's CSS is not in a layer and would beat Tsunami's, so the few rules that recolour its frame, toolbar, menus and dialogs are !important in the components layer and use only the CSS variables.

API

The functions live on window.tsunami.editor.

Function Description
tsunami.editor.mount(el, options?) Starts an editor on a textarea, with or without the class, and starts it again if it already has one. options are TinyMCE options.
tsunami.editor.get(el) The TinyMCE instance, or null before it has started.
tsunami.editor.destroy(el) Removes the editor and shows the textarea again with the text as it stands. Removing the textarea from the page does this itself.

On the element

Member Description
el.editor The TinyMCE instance, null until it has started. It has TinyMCE's own methods: getContent(), setContent(html), save(), focus().
data-editor-ready Set on the textarea once the editor has started. The textarea is then kept out of sight, but it stays in the form.
el.value The HTML, kept up to date as the text is edited. Setting it does not change the editor: call setContent().

Events

Event Detail Description
tsunami:editorready { editor } From the textarea, bubbling, when the editor has started (again after a theme change).
input From the textarea, bubbling, after typing, a command, undo or redo. The save bar listens to it.
tsunami:themechange { theme } From the theme module; the editor starts again with the matching skin.

A form's reset puts the text back to the textarea's default. On start the editor takes its own tidy version of the starting HTML as that default, so Reset and the save bar compare the same text.

Passing options

The defaults are below. The data attributes come next and the options given to mount() last.

Setting Value
Toolbar Block style, bold, italic, underline, bullet list, numbered list, link, image, media, four alignments, outdent, indent, clear formatting, source code, fullscreen. It wraps on a narrow screen.
Plugins image link fullscreen media autoresize lists code. Only these are in tinymce.min.js; a button from another plugin needs that plugin added to the file.
Height From 200px to 800px, growing with the text.
Menu bar, status bar, branding, promotion Off.

Attributes

Attribute / element Values Description
textarea.editor Starts an editor on the textarea, when the page loads or when it is added later.
name text The HTML is sent with the form under this name.
placeholder text Shown in an empty editor.
required present An empty editor stops the form.
readonly present The text cannot be changed.
disabled present The editor is dimmed and not sent. A disabled fieldset counts.
aria-invalid true Draws the frame in the danger colour.
aria-label text Names the editing area when there is no label.
data-toolbar toolbar string, or none The buttons, as TinyMCE's toolbar option.
data-min-height pixels The least height, default 200.
data-max-height pixels The greatest height, default 800.
data-menubar present or false Shows TinyMCE's menu bar.
data-statusbar present or false Shows TinyMCE's status bar.
data-editor-ready set by the script On the textarea once the editor has started.