suneditor
Version:
Vanilla JavaScript based WYSIWYG web editor
812 lines (778 loc) • 40.5 kB
JavaScript
/**
* ================================================================================================================================
* === DEFAULTS : Constants
* =================================================================================================================================
*/
export const DEFAULTS = {
BUTTON_LIST: [
['undo', 'redo'],
'|',
['bold', 'underline', 'italic', 'strike', '|', 'subscript', 'superscript'],
'|',
['removeFormat'],
'|',
['outdent', 'indent'],
'|',
['fullScreen', 'showBlocks', 'codeView'],
'|',
['preview', 'print'],
],
REQUIRED_FORMAT_LINE: 'div',
REQUIRED_ELEMENT_WHITELIST: 'br|div',
ELEMENT_WHITELIST:
'p|pre|blockquote|h1|h2|h3|h4|h5|h6|ol|ul|li|hr|figure|figcaption|img|iframe|audio|video|source|table|colgroup|col|thead|tbody|tr|th|td|caption|a|b|strong|var|i|em|u|ins|s|span|strike|del|sub|sup|code|svg|path|details|summary',
TEXT_STYLE_TAGS: 'strong|span|font|b|var|i|em|u|ins|s|strike|del|sub|sup|mark|a|label|code|summary',
SCOPE_SELECTION_TAGS: ['td', 'table', 'li', 'ol', 'ul', 'pre', 'figcaption', 'blockquote', 'dl', 'dt', 'dd'],
ATTRIBUTE_WHITELIST:
'contenteditable|target|href|title|download|rel|src|alt|class|type|colspan|rowspan' +
'|width|height|controls|autoplay|loop|muted|poster|preload|playsinline|volume|crossorigin|disableRemotePlayback|controlsList' +
'|allowfullscreen|sandbox|loading|allow|referrerpolicy|frameborder|scrolling',
FORMAT_LINE: 'P|H[1-6]|LI|TH|TD|DETAILS',
FORMAT_BR_LINE: 'PRE',
FORMAT_CLOSURE_BR_LINE: '',
FORMAT_BLOCK: 'BLOCKQUOTE|OL|UL|FIGCAPTION|TABLE|THEAD|TBODY|TR|CAPTION|DETAILS',
FORMAT_CLOSURE_BLOCK: 'TH|TD',
ALLOWED_EMPTY_NODE_LIST: '.se-component, pre, blockquote, hr, li, table, img, iframe, video, audio, canvas, details',
SIZE_UNITS: ['px', 'pt', 'em', 'rem'],
CLASS_NAME: '^__se__|^se-|^katex|^MathJax|^language-',
CLASS_MJX: 'mjx-container|mjx-math|mjx-mrow|mjx-mi|mjx-mo|mjx-mn|mjx-msup|mjx-mfrac|mjx-munderover',
EXTRA_TAG_MAP: { script: false, style: false, meta: false, link: false, '[a-z]+:[a-z]+': false },
CONTENT_STYLES:
'background|background-clip|background-color|' +
'border|border-bottom|border-collapse|border-color|border-image|border-left-width|border-radius|border-right-width|border-spacing|border-style|border-top|border-width|' +
'box-shadow|box-sizing|' +
'caption-side|color|content|' +
'direction|display|position|' +
'float|font|font-family|font-size|font-style|font-weight|' +
'height|min-height|max-height|' +
'width|min-width|max-width|' +
'left|letter-spacing|line-height|list-style-position|list-style-type|' +
'margin|margin-block-end|margin-block-start|margin-bottom|margin-inline-end|margin-inline-start|margin-left|margin-right|margin-top|' +
'padding|padding-bottom|padding-inline-start|padding-left|padding-right|padding-top|' +
'outline|overflow|' +
'page-break-before|page-break-after|page-break-inside|' +
'rotate|rotateX|rotateY|' +
'table-layout|text-align|text-decoration|text-shadow|text-transform|top|' +
'text-indent|text-rendering|' +
'vertical-align|visibility|' +
'white-space|word-break|word-wrap',
TAG_STYLES: {
'table|th|td': 'border|border-[a-z]+|color|background-color|text-align|float|font-weight|text-decoration|font-style|vertical-align',
'table|td': 'width',
tr: 'height',
col: 'width',
caption: 'text-align|caption-side',
'ol|ul': 'list-style-type',
figure: 'display|width|height|padding|padding-bottom',
figcaption: 'margin|margin-top|margin-bottom|text-align',
'img|video|iframe': 'transform|transform-origin|width|min-width|max-width|height|min-height|max-height|float|margin|margin-top',
hr: '',
},
SPAN_STYLES: 'font-family|font-size|color|background-color|width|height',
LINE_STYLES: 'text-align|margin|margin-left|margin-right|line-height',
RETAIN_STYLE_MODE: ['repeat', 'always', 'none'],
};
/**
* ================================================================================================================================
* === OPTIONS TYPES : Frame
* =================================================================================================================================
*/
/**
* ================================================================================================================================
* @typedef {Object} EditorFrameOptions
*
* **Frame-level editable area options**
* -----------------
*
* === Content & Editing ===
* @property {string} [value=""] - Initial value for the editor.
* @property {string} [placeholder=""] - Placeholder text.
* @property {Object<string, string>} [editableFrameAttributes={spellcheck: "false"}] - Attributes for the editable frame[.sun-editor-editable].
* ```js
* { editableFrameAttributes: { spellcheck: 'true', autocomplete: 'on' } }
* ```
* ///
*
* === Layout & Sizing ===
* @property {string|number} [width="100%"] - Width for the editor.
* @property {string|number} [minWidth=""] - Min width for the editor.
* @property {string|number} [maxWidth=""] - Max width for the editor.
* @property {string|number} [height="auto"] - Height for the editor.
* @property {string|number} [minHeight=""] - Min height for the editor.
* @property {string|number} [maxHeight=""] - Max height for the editor.
* @property {string} [editorStyle=""] - Style string of the top frame of the editor.
* ```js
* { editorStyle: 'border: 1px solid #ccc; border-radius: 4px;' }
* ```
* ///
*
* === Iframe Mode ===
* @property {boolean} [iframe=false] - Content will be placed in an iframe and isolated from the rest of the page.
* @property {boolean} [iframe_fullPage=false] - Allows the usage of HTML, HEAD, BODY tags and DOCTYPE declaration on the `iframe`.
* @property {Object<string, string>} [iframe_attributes={}] - Attributes of the `iframe`.
* ```js
* { iframe_attributes: { 'allow-scripts': 'true', sandbox: 'allow-same-origin' } }
* ```
* @property {Array<string>} [iframe_cssFileName=["suneditor"]] - CSS files to apply inside the iframe.
* - String: Filename pattern to search in document `<link>` tags.
* - `"*"`: Wildcard to include ALL stylesheets from the page.
* - Absolute URLs and data URLs (`data:text/css,`) are also supported.
* ```js
* { iframe_cssFileName: ['suneditor', 'custom', 'https://example.com/style.css'] }
* ```
* ///
*
* === Statusbar & Character Counter ===
* @property {boolean} [statusbar=true] - Enables the status bar.
* @property {boolean} [statusbar_showPathLabel=true] - Displays the current node structure in the status bar.
* @property {boolean} [statusbar_resizeEnable=true] - Enables resize function of the bottom status bar.
* @property {boolean} [charCounter=false] - Shows the number of characters in the editor.
* - If the `maxCharCount` option has a value, it becomes `true`.
* @property {?number} [charCounter_max=null] - The maximum number of characters allowed to be inserted into the editor.
* @property {?string} [charCounter_label=null] - Text to be displayed in the `charCounter` area of the bottom bar.
* ```js
* { charCounter_label: 'Characters :' }
* ```
* @property {"char"|"byte"|"byte-html"} [charCounter_type="char"] - Defines the calculation method of the `charCounter` option.
* - `char`: Characters length.
* - `byte`: Binary data size of characters.
* - `byte-html`: Binary data size of the full HTML string.
*
* === Word Counter ===
* @property {boolean} [wordCounter=false] - Shows the number of words in the editor.
* @property {?string} [wordCounter_label=null] - Text to be displayed in the `wordCounter` area of the bottom bar.
* ```js
* { wordCounter_label: 'Words :' }
* ```
*/
/** ================================================================================================================================ */
/**
* @typedef {Object} OptionStyleResult
* @property {string} top - Styles applied to the top container (e.g. width, z-index, etc).
* @property {string} frame - Styles applied to the iframe container (e.g. height, min-height).
* @property {string} editor - Styles applied to the editable content area.
*/
/**
* @typedef {Object} InternalFrameOptions
* **Runtime-only frame options (computed internally, cannot be set by users)**
* @property {Object} [__statusbarEvent] - Status bar event configuration.
* @property {SunEditor.InitFrameOptions} _origin - origin frame options
* @property {OptionStyleResult} [_defaultStyles] - Enables fixed positioning for the editor frame.
* @property {*} [codeMirrorEditor] - CodeMirror editor instance (frame-level). Set by `_checkCodeMirror` after initialization.
*/
/**
* @typedef {EditorFrameOptions & InternalFrameOptions} AllFrameOptions
*/
/**
* @typedef {'width'|'minWidth'|'maxWidth'|'height'|'minHeight'|'maxHeight'} TransformedFrameOptionKeys
*/
/**
* @typedef {Object} TransformedFrameOptions
* @property {string} width
* @property {string} minWidth
* @property {string} maxWidth
* @property {string} height
* @property {string} minHeight
* @property {string} maxHeight
*/
/**
* @typedef {Omit<AllFrameOptions, TransformedFrameOptionKeys> & TransformedFrameOptions} ProcessedFrameOptions
*/
/**
* ================================================================================================================================
* === OPTIONS TYPES : Base
* =================================================================================================================================
*/
/**
* ================================================================================================================================
* @typedef {Object} PrivateBaseOptions
*
* **Advanced internal options (user-configurable, prefixed with `__`)**
* -----------------
*
* === Defaults & Whitelists ===
* @property {string} [__textStyleTags=CONSTANTS.TEXT_STYLE_TAGS] - The basic tags that serves as the base for `textStyleTags`
* - The default follows {@link DEFAULTS.TEXT_STYLE_TAGS}
* @property {Object<string, string>} [__tagStyles=CONSTANTS.TAG_STYLES] - The basic tags that serves as the base for `tagStyles`
* - The default follows {@link DEFAULTS.TAG_STYLES}
* @property {string} [__defaultElementWhitelist] - A custom string used to construct a list of HTML elements to allow.
* - The final list (regex pattern) is dynamically generated according to the following rules:
* - A list of required elements, {@link DEFAULTS.REQUIRED_ELEMENT_WHITELIST}, is always included.
* - If this option (`__defaultElementWhitelist`) is provided as a string: That value is used.
* - If not provided or not a string: The default {@link DEFAULTS.ELEMENT_WHITELIST} is used.
* - 1. If no options are given, the final pattern is:
* - 'a|img|p|div|...' (REQUIRED + DEFAULT)
* - 2. If options are given directly, the final pattern is:
* - 'a|img|custom|tags' (REQUIRED + options.__defaultElementWhitelist)
* @property {string} [__defaultAttributeWhitelist=CONSTANTS.ATTRIBUTE_WHITELIST] - A complete list of attributes that are allowed by default on all tags. Delimiter: `"|"`.
* - The default follows {@link DEFAULTS.ATTRIBUTE_WHITELIST}
* ```js
* { __defaultAttributeWhitelist: 'href|target|src|alt|class' }
* ```
* ///
*
* === Formatting ===
* @property {string} [__defaultFormatLine=CONSTANTS.FORMAT_LINE] - Specifies the tag to be used as the editor's default `line` element.
* - The default follows {@link DEFAULTS.FORMAT_LINE}
* @property {string} [__defaultFormatBrLine=CONSTANTS.FORMAT_BR_LINE] - Specifies the tag to be used as the editor's default `brLine` element.
* - The default follows {@link DEFAULTS.FORMAT_BR_LINE}
* @property {string} [__defaultFormatClosureBrLine=CONSTANTS.FORMAT_CLOSURE_BR_LINE] - Specifies the tag to be used as the editor's default `closureBrLine` element.
* - The default follows {@link DEFAULTS.FORMAT_CLOSURE_BR_LINE}
* @property {string} [__defaultFormatBlock=CONSTANTS.FORMAT_BLOCK] - Specifies the tag to be used as the editor's default `block` element.
* - The default follows {@link DEFAULTS.FORMAT_BLOCK}
* @property {string} [__defaultFormatClosureBlock=CONSTANTS.FORMAT_CLOSURE_BLOCK] - Specifies the tag to be used as the editor's default `closureBlock` element.
* - The default follows {@link DEFAULTS.FORMAT_CLOSURE_BLOCK}
* ///
*
* === Filters & Behavior ===
* @property {boolean} [__lineFormatFilter=true] - Line format filter configuration.
* @property {Array<string>} [__listCommonStyle=["fontSize", "color", "fontFamily", "fontWeight", "fontStyle"]] - Defines the styles applied directly to `<li>` when a text style is set on the entire list item.
* - For example, when changing the font size or color of a list item (`<li>`),
* - these styles apply to the `<li>` tag instead of wrapping content in additional tags.
* @property {{pluginName: string, we: boolean}|boolean} [__pluginRetainFilter=true] - Plugin retain filter configuration. (Internal use primarily)
* - You can turn it off/on globally with `true`/`false` or set it per plugin.
* ```js
* // disable filter for table plugin only
* {
* __pluginRetainFilter: { table: false }
* }
* ```
*/
/**
* ================================================================================================================================
* @typedef {Object} EditorBaseOptions
*
* **Top-level editor configuration**
* -----------------
*
* === Plugins & Toolbar ===
* @property {Object<string, *>|Array<Object<string, *>>} [plugins] - Plugin configuration. Pass an array of plugin classes or an object keyed by plugin name.
* ```js
* import plugins, { image, link, table } from 'suneditor/src/plugins';
* { plugins: plugins,
* plugins: [image, link, table],
* plugins: { image, link, table }
* }
* ```
* @property {Array<string>} [excludedPlugins=[]] - List of plugin names to exclude.
* ```js
* { excludedPlugins: ['image', 'video'] }
* ```
* @property {SunEditor.UI.ButtonList} [buttonList=CONSTANTS.BUTTON_LIST] - List of toolbar buttons, grouped by sub-arrays.
* - The default follows {@link DEFAULTS.BUTTON_LIST}
* ///
*
* === Modes & Themes ===
* @property {boolean} [v2Migration=false] - Enables migration mode for SunEditor v2.
* @property {"classic"|"inline"|"balloon"|"balloon-always"|"classic:bottom"|"inline:bottom"} [mode="classic"] - Toolbar mode: `classic`, `inline`, `balloon`, `balloon-always`. Append `:bottom` to place toolbar at the bottom (e.g. `classic:bottom`, `inline:bottom`).
* @property {string} [type=""] - Editor type. Use `"document"` for a document-style layout, with optional sub-types after `:`.
* ```js
* // type
* 'document:header,page'
* ```
* @property {string} [theme=""] - Editor theme.
* @property {Object<string, string>} [lang] - Language configuration. default : EN
* @property {Object<string, string>} [icons] - Overrides the default icons.
* @property {"ltr"|"rtl"} [textDirection="ltr"] - Text direction: `ltr` or `rtl`.
* @property {Array<string>} [reverseButtons=['indent-outdent']] - An array of command pairs whose shortcut icons should be opposite each other.
* - Depends on the `textDirection` mode.
* ///
*
* === Strict & Advanced Filtering ===
* @property {true | {
* tagFilter: boolean,
* formatFilter: boolean,
* classFilter: boolean,
* textStyleTagFilter: boolean,
* attrFilter: boolean,
* styleFilter: boolean
* }} [strictMode=true] - Enables strict filtering of tags, attributes, and styles.
* - Use `true` to enable all filters (default), or an object to control individual filters.
* - Setting `false` is not supported; use the object form to disable specific filters instead.
* - :filter description
* - `tagFilter`: Filters disallowed HTML tags (`elementWhitelist`/`elementBlacklist`)
* - `formatFilter`: Filters format elements (`formatLine`/`formatBlock`)
* - `classFilter`: Filters disallowed CSS class names (`allowedClassName`)
* - `textStyleTagFilter`: Filters text style tags (b, i, u, span, etc.)
* - `attrFilter`: Filters disallowed HTML attributes (`attributeWhitelist`/`attributeBlacklist`)
* - `styleFilter`: Filters disallowed inline styles (`spanStyles`/`lineStyles`/`allUsedStyles`)
* ```js
* // disable only attribute and style filters
* {
* strictMode: {
* tagFilter: true,
* formatFilter: true,
* classFilter: true,
* textStyleTagFilter: true,
* attrFilter: false,
* styleFilter: false
* }
* }
* ```
* @property {Array<string>} [scopeSelectionTags=CONSTANTS.SCOPE_SELECTION_TAGS] - Tags treated as whole units when selecting all content.
* - The default follows {@link DEFAULTS.SCOPE_SELECTION_TAGS}
* ///
*
* === Content Filtering & Formatting ===
* ==
* #### 1) Tag & Element Control
* @property {string} [elementWhitelist=""] - Specifies HTML elements to additionally allow beyond the default allow list. Delimiter: `"|"`.
* - Added to the default list determined by {@link PrivateBaseOptions.__defaultElementWhitelist}.
* ```js
* { elementWhitelist: 'mark|details|summary' }
* ```
* @property {string} [elementBlacklist=""] - Specifies HTML elements that should not be used. Delimiter: `"|"`.
* - Tags specified here will eventually be removed, even if they are included in other whitelists.
* ```js
* { elementBlacklist: 'script|style|iframe' }
* ```
* @property {string} [allowedEmptyTags=CONSTANTS.ALLOWED_EMPTY_NODE_LIST] - A list of tags that are allowed to be kept even if their values are empty.
* - The default follows {@link DEFAULTS.ALLOWED_EMPTY_NODE_LIST}
* - Concatenated with `ALLOWED_EMPTY_NODE_LIST` to form the final `allowedEmptyTags` list.
* @property {string} [allowedClassName=""] - Allowed class names.
* - Added the default value {@link DEFAULTS.CLASS_NAME}
* ///
*
* #### 2) Attribute Control
* @property {{[key: string]: string|undefined}} [attributeWhitelist=null] - Specifies additional attributes to allow for each tag. `"*"` applies to all tags.
* - Rules specified here will be merged into {@link PrivateBaseOptions.__defaultAttributeWhitelist}.
* ```js
* {
* attributeWhitelist: {
* a: 'href|target',
* img: 'src|alt',
* '*': 'id|data-*'
* }
* }
* ```
* @property {{[key: string]: string|undefined}} [attributeBlacklist=null] - Specifies attributes to disallow by tag. `"*"` applies to all tags.
* - Attributes specified here will eventually be removed even if they are allowed by other settings.
* - A list of required elements, {@link DEFAULTS.REQUIRED_FORMAT_LINE}, is always included.
* ```js
* { attributeBlacklist: { '*': 'onclick|onerror' } }
* ```
* ///
*
* #### 3) Text & Inline Style Control
* @property {string} [textStyleTags=__textStyleTags] - Additional text style tags.
* - The default follows {@link PrivateBaseOptions.__textStyleTags}
* @property {Object<string, string>} [convertTextTags={bold: "strong", underline: "u", italic: "em", strike: "del", subscript: "sub", superscript: "sup"}] - Maps text styles to specific HTML tags.
* ```js
* {
* convertTextTags: {
* bold: 'b',
* italic: 'i',
* underline: 'u',
* strike: 's'
* }
* }
* ```
* @property {string} [allUsedStyles] - Specifies additional styles to the list of allowed styles. Delimiter: `"|"`.
* ```js
* { allUsedStyles: 'color|background-color|text-shadow' }
* ```
* @property {Object<string, string>} [tagStyles={}] - Specifies allowed styles for HTML tags. Key is tag name(s), value is pipe-delimited allowed styles.
* ```js
* {
* tagStyles: {
* 'table|td': 'border|color|background-color',
* hr: 'border-top'
* }
* }
* ```
* @property {string} [spanStyles=CONSTANTS.SPAN_STYLES] - Specifies allowed styles for the `span` tag.
* - The default follows {@link DEFAULTS.SPAN_STYLES}
* @property {string} [lineStyles=CONSTANTS.LINE_STYLES] - Specifies allowed styles for the `line` element (p..).
* - The default follows {@link DEFAULTS.LINE_STYLES}
* @property {Array<string>} [fontSizeUnits=CONSTANTS.SIZE_UNITS] - Allowed font size units.
* - The default follows {@link DEFAULTS.SIZE_UNITS}
* @property {"repeat"|"always"|"none"} [retainStyleMode="repeat"] - Determines how inline elements (e.g. `<span>`, `<strong>`) are handled when deleting text.
* - `repeat`: Styles are retained unless backspace is repeatedly pressed.
* - Continuous pressing will eventually remove the styles.
* - `none`: Styles are not retained. Inline elements are immediately removed with the text.
* - `always`: Styles persist indefinitely unless explicitly removed.
* - Even if all text inside an inline element is deleted, the element itself remains.
* ///
*
* #### 4) Line & Block Formatting
* @property {string} [defaultLine="p"] - Default `line` element when inserting new lines.
* @property {"line"|"br"} [defaultLineBreakFormat="line"] - Specifies the default line break format.
* - [Recommended] `line` : Line break is divided into general tags.
* - [Not recommended] `br` : Line breaks are treated as <br> on the same line. (like shift+enter)
* - Line breaks are handled as <br> within `line`.
* - You can create a new `line` by entering a line break twice in a row.
* - Formats that include `line`, such as "Quote", still operate on a `line` basis.
* - suneditor processes work in `line` units.
* - When set to `br`, performance may decrease when editing a lot of data.
* @property {string} [lineAttrReset=""] - Line properties that should be reset when changing lines. Delimiter: `"|"`.
* ```js
* { lineAttrReset: 'id|name' }
* ```
* @property {string} [formatLine=__defaultFormatLine] - Additionally allowed `line` elements beyond the default. Delimiter: `"|"`.
* - Concatenated with {@link PrivateBaseOptions.__defaultFormatLine} to form the final `line` list.
* - `line` element also contains `brLine` element.
* ```js
* { formatLine: 'ARTICLE|SECTION' }
* ```
* @property {string} [formatBrLine=__defaultFormatBrLine] - Additionally allowed `brLine` elements beyond the default.
* - Concatenated with {@link PrivateBaseOptions.__defaultFormatBrLine} to form the final `brLine` list.
* - `brLine` elements are included in the `line` element.
* - `brLine` elements' line break is `BR` tag.
* ```js
* { formatBrLine: 'CODE' }
* ```
* - ※ Entering the Enter key on the last line ends `brLine` and appends `line`.
* @property {string} [formatClosureBrLine=__defaultFormatClosureBrLine] - Additionally allowed `closureBrLine` elements beyond the default.
* - Concatenated with {@link PrivateBaseOptions.__defaultFormatClosureBrLine} for the final `closureBrLine` list.
* - `closureBrLine` elements are included in the `brLine`.
* - `closureBrLine` elements' line break is `BR` tag.
* - ※ You cannot exit this format with the Enter key or Backspace key.
* - ※ Use it only in special cases. ([ex] format of table cells)
* @property {string} [formatBlock=__defaultFormatBlock] - Additionally allowed `block` elements beyond the default.
* - Concatenated with {@link PrivateBaseOptions.__defaultFormatBlock} to form the final `block` list.
* - `block` wraps the `line` and `component`.
* @property {string} [formatClosureBlock=__defaultFormatClosureBlock] - Additionally allowed `closureBlock` elements beyond the default.
* - Concatenated with {@link PrivateBaseOptions.__defaultFormatClosureBlock} for the final `closureBlock` list.
* - `closureBlock` elements are included in the `block`.
* - `closureBlock` element wraps the `line` and `component`.
* - ※ You cannot exit this format with the Enter key or Backspace key.
* - ※ Use it only in special cases. (e.g. format of table cells)
* ///
*
* === UI & Interaction ===
* @property {boolean} [closeModalOutsideClick=false] - Closes modals when clicking outside.
* @property {boolean} [syncTabIndent=true] - Synchronizes tab indent with spaces.
* @property {boolean} [tabDisable=false] - Disables tab key input.
* @property {string} [toolbar_width="auto"] - Toolbar width.
* @property {?HTMLElement} [toolbar_container] - Container element for the toolbar.
* @property {number|{top: number, offset: number}} [toolbar_sticky=0] - Enables sticky toolbar.
* - `number`: Sets the sticky top position (px). Use `-1` to disable sticky.
* - `{top, offset}`: `top` is the sticky position when the page header is visible.
* - `offset` is the sticky position when a virtual keyboard shifts the viewport (e.g., on tablets, touch devices).
* - When the virtual keyboard is active, `offset` replaces `top` so the toolbar doesn't leave a gap
* - for a page header that has scrolled out of view. Default `offset` is `0`.
* ```js
* // Basic usage — sticky at top with 0px offset
* toolbar_sticky: 0
*
* // Account for a 92px fixed/sticky site header
* toolbar_sticky: 92
*
* // 92px header on desktop, but 0px when virtual keyboard pushes the viewport
* toolbar_sticky: { top: 92, offset: 0 }
* ```
* @property {boolean} [toolbar_hide=false] - Hides toolbar initially.
* @property {Object} [subToolbar={}] - Sub-toolbar configuration. A secondary toolbar that appears on text selection.
* @property {SunEditor.UI.ButtonList} [subToolbar.buttonList] - List of Sub-toolbar buttons, grouped by sub-arrays.
* @property {"balloon"|"balloon-always"} [subToolbar.mode="balloon"] - Sub-toolbar mode: `balloon`, `balloon-always`.
* @property {string} [subToolbar.width="auto"] - Sub-toolbar width.
* ```js
* { subToolbar: { buttonList: [['bold', 'italic', 'link']], mode: 'balloon' } }
* ```
* @property {?HTMLElement} [statusbar_container] - Container element for the status bar.
* @property {boolean} [shortcutsHint=true] - Displays shortcut hints in tooltips.
* @property {boolean} [shortcutsDisable=false] - Disables keyboard shortcuts.
* @property {{[key: string]: Array<string>|undefined}} [shortcuts={}] - Custom keyboard shortcuts.
* - Keys starting with `_` are user-defined custom shortcuts. Each value is an array of `[keyCombo, hintLabel]` pairs.
* - Key combos use `c` (Ctrl/Cmd), `s` (Shift), and `KeyEvent.code` values joined by `+`.
* - Use `$~pluginName.method` to call a specific plugin method.
* ```js
* {
* shortcuts: {
* bold: ['c+KeyB', 'B'],
* _h1: ['c+s+Digit1+$~blockStyle.applyHeaderByShortcut', '']
* }
* }
* ```
* ///
*
* === Advanced Features ===
* @property {boolean} [copyFormatKeepOn=false] - Keeps the format of the copied content.
* @property {boolean} [autoLinkify] - Automatically converts URLs into hyperlinks. (`Link` plugin required)
* - Default: `Boolean(plugins.link)` — determined by whether the `link` plugin is enabled.
* @property {Array<string>} [autoStyleify=["bold", "underline", "italic", "strike"]] - Styles applied automatically on text input.
* @property {number} [historyStackDelayTime=400] - Delay time for history stack updates (ms).
* @property {number} [historyStackSize=100] - Maximum number of history entries per root frame. Oldest entries are discarded when exceeded.
* @property {string} [printClass=""] - Class name for printing.
* @property {number} [fullScreenOffset=0] - Offset applied when entering fullscreen mode.
* @property {?string} [previewTemplate=null] - Custom HTML template for preview mode. Use `{{ contents }}` as a placeholder for editor content.
* @property {?string} [printTemplate=null] - Custom HTML template for print mode. Use `{{ contents }}` as a placeholder for editor content.
* ```js
* { previewTemplate: '<div class="my-preview"><h1>Preview</h1>{{ contents }}</div>' }
* ```
* @property {SunEditor.ComponentInsertType} [componentInsertBehavior="auto"] - Enables automatic selection of inserted components.
* - For inline components: places cursor near the component, or selects if no nearby range.
* - For block components: executes behavior based on `selectMode`:
* - `auto`: Move cursor to the next line if possible, otherwise select the component.
* - `select`: Always select the inserted component.
* - `line`: Move cursor to the next line if possible, or create a new line and move there.
* - `none`: Do nothing.
* @property {?string} [defaultUrlProtocol=null] - Default URL protocol for links.
* @property {Object<"copy", number>} [toastMessageTime={copy: 1500}] - Duration for displaying toast messages (ms).
* @property {boolean} [freeCodeViewMode=false] - Enables free code view mode.
* @property {boolean} [finder_panel=true] - Shows the built-in Find/Replace panel UI.
* - The finder is always created internally; this option controls whether the panel is visible to users (Ctrl+F / Ctrl+H).
* @property {boolean} [finder_liveSearch=true] - Searches instantly as you type (debounced). When false, search runs only on Enter.
*
* ///
*
* === Dynamic Options ===
* @property {Object<string, *>} [externalLibs] - External libraries like CodeMirror, KaTeX, or MathJax.
* - See {@link https://github.com/ARA-developer/suneditor/blob/develop/guide/external-libraries.md External Libraries Guide}
* ```js
* {
* externalLibs: {
* katex: window.katex,
* codeMirror: { src: CodeMirror }
* }
* }
* ```
* @property {Object<string, boolean>} [allowedExtraTags=CONSTANTS.EXTRA_TAG_MAP] - Specifies extra allowed or disallowed tags. `true` to allow, `false` to disallow.
* - The default follows {@link DEFAULTS.EXTRA_TAG_MAP}
* ```js
* {
* allowedExtraTags: {
* script: false,
* style: false,
* mark: true
* }
* }
* ```
* ///
*
* === User Events ===
* @property {SunEditor.Event.Handlers} [events] - User event handlers configuration.
* ```js
* {
* events: {
* onChange: (content) => console.log(content),
* onImageUploadBefore: (files, info) => true
* }
* }
* ```
* ///
*
* === [ Plugin-Specific Options ] ===
* ---[ Auto-generated by scripts/check/inject-plugin-jsdoc.cjs - DO NOT EDIT MANUALLY ]---
* @property {import('../../plugins/dropdown/align.js').AlignPluginOptions} [align]
* @property {import('../../plugins/modal/audio.js').AudioPluginOptions} [audio]
* @property {import('../../plugins/browser/audioGallery.js').AudioGalleryPluginOptions} [audioGallery]
* @property {import('../../plugins/dropdown/backgroundColor.js').BackgroundColorPluginOptions} [backgroundColor]
* @property {import('../../plugins/dropdown/blockStyle.js').BlockStylePluginOptions} [blockStyle]
* @property {import('../../plugins/command/codeBlock.js').CodeBlockPluginOptions} [codeBlock]
* @property {import('../../plugins/modal/drawing.js').DrawingPluginOptions} [drawing]
* @property {import('../../plugins/modal/embed.js').EmbedPluginOptions} [embed]
* @property {import('../../plugins/command/exportPDF.js').ExportPDFPluginOptions} [exportPDF]
* @property {import('../../plugins/browser/fileBrowser.js').FileBrowserPluginOptions} [fileBrowser]
* @property {import('../../plugins/browser/fileGallery.js').FileGalleryPluginOptions} [fileGallery]
* @property {import('../../plugins/command/fileUpload.js').FileUploadPluginOptions} [fileUpload]
* @property {import('../../plugins/dropdown/font.js').FontPluginOptions} [font]
* @property {import('../../plugins/dropdown/fontColor.js').FontColorPluginOptions} [fontColor]
* @property {import('../../plugins/input/fontSize.js').FontSizePluginOptions} [fontSize]
* @property {import('../../plugins/dropdown/hr.js').HRPluginOptions} [hr]
* @property {import('../../plugins/modal/image/index.js').ImagePluginOptions} [image]
* @property {import('../../plugins/browser/imageGallery.js').ImageGalleryPluginOptions} [imageGallery]
* @property {import('../../plugins/dropdown/layout.js').LayoutPluginOptions} [layout]
* @property {import('../../plugins/dropdown/lineHeight.js').LineHeightPluginOptions} [lineHeight]
* @property {import('../../plugins/modal/link.js').LinkPluginOptions} [link]
* @property {import('../../plugins/modal/math.js').MathPluginOptions} [math]
* @property {import('../../plugins/field/mention.js').MentionPluginOptions} [mention]
* @property {import('../../plugins/dropdown/paragraphStyle.js').ParagraphStylePluginOptions} [paragraphStyle]
* @property {import('../../plugins/dropdown/table/index.js').TablePluginOptions} [table]
* @property {import('../../plugins/dropdown/template.js').TemplatePluginOptions} [template]
* @property {import('../../plugins/dropdown/textStyle.js').TextStylePluginOptions} [textStyle]
* @property {import('../../plugins/modal/video/index.js').VideoPluginOptions} [video]
* @property {import('../../plugins/browser/videoGallery.js').VideoGalleryPluginOptions} [videoGallery]
* ///
* ---[ End of auto-generated plugin options ]---
* ================================================================================================================================
*/
/**
* ================================================================================================================================
* @typedef {Object} InternalBaseOptions
*
* **Runtime-only base options (computed internally, cannot be set by users)**
*
* @property {string} [_themeClass] - Computed className for the selected theme (e.g., `se-theme-default`).
* @property {string} [_type_options] - Additional sub-type string from the `type` option (after `:`).
* @property {string} [_allowedExtraTag] - Preprocessed allowed tag string for RegExp (e.g., "mark|figure").
* @property {string} [_disallowedExtraTag] - Preprocessed disallowed tag string.
* @property {string} [_editableClass] - Final computed editable class (used in editor wrapper).
* @property {boolean} [_rtl] - Whether text direction is RTL.
* @property {string[]} [_reverseCommandArray] - Internal key shortcut matcher for reverse commands.
* @property {string} [_subMode] - Sub toolbar mode (e.g., `balloon`).
* @property {string[]} [_textStyleTags] - Tag names used for text styling, plus span/li.
* @property {RegExp} [_textStylesRegExp] - Regex to match inline styles (e.g., fontSize, color).
* @property {RegExp} [_lineStylesRegExp] - Regex to match line styles (e.g., text-align, padding).
* @property {Object<string, string>} [_defaultStyleTagMap] - Mapping HTML tag => standard tag.
* @property {Object<string, string>} [_styleCommandMap] - Mapping HTML tag => command (e.g., bold, underline).
* @property {Object<string, string>} [_defaultTagCommand] - Mapping command => preferred tag.
* @property {string} [_editorElementWhitelist] - Element whitelist regex pattern for the editor.
*
* @property {Set} [buttons] - List of currently used toolbar buttons
* @property {Set} [buttons_sub] - List of currently used sub-toolbar buttons
* @property {string} [toolbar_sub_width] - Sub-toolbar width.
* @property {string[]} [reverseCommands] - Merged reverse command pairs array.
* - Includes default `indent-outdent` + user's `reverseButtons`.
*
* @property {*} [codeMirror] - CodeMirror configuration object from `externalLibs.codeMirror`.
* @property {boolean} [codeMirrorEditor] - Whether CodeMirror is available (base-level flag). Frame-level stores the actual CM instance.
* @property {boolean} [hasCodeMirror] - Uses CodeMirror for code view.
* @property {Set<string>} [allUsedStyles] - Processed set of all allowed CSS styles.
* - Converted from user's `string` input ("|" delimited) to `Set<string>` in constructor.
*/
// ================================================================================================================================
/**
* @typedef {EditorBaseOptions & PrivateBaseOptions & EditorFrameOptions} EditorInitOptions
*/
/**
* @typedef {EditorBaseOptions & PrivateBaseOptions & InternalBaseOptions} AllBaseOptions
*/
/**
* ================================================================================================================================
* === OPTION FLAGS : Fixed / Resettable
* =================================================================================================================================
*/
/**
* @description For all EditorInitOptions keys, only `boolean` | `null` values are allowed.
* - `fixed` → Immutable / `null` → Resettable.
* @type {Partial<Object<keyof EditorInitOptions, "fixed" | true>>}
*/
export const OPTION_FRAME_FIXED_FLAG = {
value: 'fixed',
placeholder: true,
editableFrameAttributes: true,
width: true,
minWidth: true,
maxWidth: true,
height: true,
minHeight: true,
maxHeight: true,
editorStyle: true,
iframe: 'fixed',
iframe_fullPage: 'fixed',
iframe_attributes: true,
iframe_cssFileName: true,
statusbar: true,
statusbar_showPathLabel: true,
statusbar_resizeEnable: 'fixed',
charCounter: true,
charCounter_max: true,
charCounter_label: true,
charCounter_type: true,
wordCounter: true,
wordCounter_label: true,
};
/**
* @description For all EditorInitOptions keys, only `boolean` | `null` values are allowed.
* - `fixed` → Immutable / `null` → Resettable.
* @type {Partial<Object<keyof EditorInitOptions, "fixed" | true>>}
*/
export const OPTION_FIXED_FLAG = {
plugins: 'fixed',
excludedPlugins: 'fixed',
buttonList: 'fixed',
v2Migration: 'fixed',
strictMode: 'fixed',
mode: 'fixed',
type: 'fixed',
theme: true,
lang: 'fixed',
fontSizeUnits: 'fixed',
allowedClassName: 'fixed',
closeModalOutsideClick: 'fixed',
copyFormatKeepOn: true,
syncTabIndent: true,
tabDisable: true,
autoLinkify: true,
autoStyleify: true,
retainStyleMode: true,
allowedExtraTags: 'fixed',
events: true,
__textStyleTags: 'fixed',
textStyleTags: 'fixed',
convertTextTags: 'fixed',
__tagStyles: 'fixed',
tagStyles: 'fixed',
spanStyles: 'fixed',
lineStyles: 'fixed',
textDirection: true,
reverseButtons: 'fixed',
historyStackDelayTime: true,
historyStackSize: 'fixed',
lineAttrReset: true,
printClass: true,
defaultLine: 'fixed',
defaultLineBreakFormat: true,
scopeSelectionTags: true,
__defaultElementWhitelist: 'fixed',
elementWhitelist: 'fixed',
elementBlacklist: 'fixed',
__defaultAttributeWhitelist: 'fixed',
attributeWhitelist: 'fixed',
attributeBlacklist: 'fixed',
__defaultFormatLine: 'fixed',
formatLine: 'fixed',
__defaultFormatBrLine: 'fixed',
formatBrLine: 'fixed',
__defaultFormatClosureBrLine: 'fixed',
formatClosureBrLine: 'fixed',
__defaultFormatBlock: 'fixed',
formatBlock: 'fixed',
__defaultFormatClosureBlock: 'fixed',
formatClosureBlock: 'fixed',
allowedEmptyTags: true,
toolbar_width: true,
toolbar_container: 'fixed',
toolbar_sticky: true,
toolbar_hide: true,
subToolbar: 'fixed',
statusbar_container: 'fixed',
shortcutsHint: true,
shortcutsDisable: 'fixed',
shortcuts: 'fixed',
fullScreenOffset: true,
previewTemplate: true,
printTemplate: true,
componentInsertBehavior: true,
defaultUrlProtocol: true,
allUsedStyles: 'fixed',
toastMessageTime: true,
icons: 'fixed',
freeCodeViewMode: true,
finder_panel: 'fixed',
finder_liveSearch: true,
__lineFormatFilter: true,
__pluginRetainFilter: true,
__listCommonStyle: 'fixed',
externalLibs: 'fixed',
};
/**
* @typedef {'formatClosureBrLine' | 'formatBrLine' | 'formatLine' | 'formatClosureBlock' | 'formatBlock' | 'toolbar_width' | 'toolbar_container' | '_toolbar_sticky' | '_toolbar_sticky_offset' | 'strictMode' | 'lineAttrReset'} TransformedOptionKeys
*/
/**
* @typedef {Object} StrictModeOptions
* @property {boolean} tagFilter - Filters disallowed HTML tags (`elementWhitelist`/`elementBlacklist`)
* @property {boolean} formatFilter - Filters format elements (`formatLine`/`formatBlock`)
* @property {boolean} classFilter - Filters disallowed CSS class names (`allowedClassName`)
* @property {boolean} textStyleTagFilter - Filters text style tags (b, i, u, span, etc.)
* @property {boolean} attrFilter - Filters disallowed HTML attributes (`attributeWhitelist`/`attributeBlacklist`)
* @property {boolean} styleFilter - Filters disallowed inline styles (`spanStyles`/`lineStyles`/`allUsedStyles`)
*/
/**
* @typedef {Object} TransformedOptions
* @property {{ reg: RegExp, str: string }} formatClosureBrLine
* @property {{ reg: RegExp, str: string }} formatBrLine
* @property {{ reg: RegExp, str: string }} formatLine
* @property {{ reg: RegExp, str: string }} formatClosureBlock
* @property {{ reg: RegExp, str: string }} formatBlock
* @property {string} toolbar_width
* @property {HTMLElement|null} toolbar_container
* @property {number} _toolbar_sticky
* @property {number} _toolbar_sticky_offset
* @property {StrictModeOptions} strictMode
* @property {string[]} lineAttrReset
*/
/**
* @typedef {Omit<AllBaseOptions, TransformedOptionKeys> & TransformedOptions} ProcessedBaseOptions
*/