| |
| |
| |
|
|
| (function () { |
| 'use strict'; |
|
|
| const Cell = initial => { |
| let value = initial; |
| const get = () => { |
| return value; |
| }; |
| const set = v => { |
| value = v; |
| }; |
| return { |
| get, |
| set |
| }; |
| }; |
|
|
| var global = tinymce.util.Tools.resolve('tinymce.PluginManager'); |
|
|
| const get$2 = toggleState => { |
| const isEnabled = () => { |
| return toggleState.get(); |
| }; |
| return { isEnabled }; |
| }; |
|
|
| const fireVisualChars = (editor, state) => { |
| return editor.dispatch('VisualChars', { state }); |
| }; |
|
|
| const hasProto = (v, constructor, predicate) => { |
| var _a; |
| if (predicate(v, constructor.prototype)) { |
| return true; |
| } else { |
| return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name; |
| } |
| }; |
| const typeOf = x => { |
| const t = typeof x; |
| if (x === null) { |
| return 'null'; |
| } else if (t === 'object' && Array.isArray(x)) { |
| return 'array'; |
| } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) { |
| return 'string'; |
| } else { |
| return t; |
| } |
| }; |
| const isType$1 = type => value => typeOf(value) === type; |
| const isSimpleType = type => value => typeof value === type; |
| const eq = t => a => t === a; |
| const isString = isType$1('string'); |
| const isObject = isType$1('object'); |
| const isNull = eq(null); |
| const isBoolean = isSimpleType('boolean'); |
| const isNullable = a => a === null || a === undefined; |
| const isNonNullable = a => !isNullable(a); |
| const isNumber = isSimpleType('number'); |
|
|
| class Optional { |
| constructor(tag, value) { |
| this.tag = tag; |
| this.value = value; |
| } |
| static some(value) { |
| return new Optional(true, value); |
| } |
| static none() { |
| return Optional.singletonNone; |
| } |
| fold(onNone, onSome) { |
| if (this.tag) { |
| return onSome(this.value); |
| } else { |
| return onNone(); |
| } |
| } |
| isSome() { |
| return this.tag; |
| } |
| isNone() { |
| return !this.tag; |
| } |
| map(mapper) { |
| if (this.tag) { |
| return Optional.some(mapper(this.value)); |
| } else { |
| return Optional.none(); |
| } |
| } |
| bind(binder) { |
| if (this.tag) { |
| return binder(this.value); |
| } else { |
| return Optional.none(); |
| } |
| } |
| exists(predicate) { |
| return this.tag && predicate(this.value); |
| } |
| forall(predicate) { |
| return !this.tag || predicate(this.value); |
| } |
| filter(predicate) { |
| if (!this.tag || predicate(this.value)) { |
| return this; |
| } else { |
| return Optional.none(); |
| } |
| } |
| getOr(replacement) { |
| return this.tag ? this.value : replacement; |
| } |
| or(replacement) { |
| return this.tag ? this : replacement; |
| } |
| getOrThunk(thunk) { |
| return this.tag ? this.value : thunk(); |
| } |
| orThunk(thunk) { |
| return this.tag ? this : thunk(); |
| } |
| getOrDie(message) { |
| if (!this.tag) { |
| throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None'); |
| } else { |
| return this.value; |
| } |
| } |
| static from(value) { |
| return isNonNullable(value) ? Optional.some(value) : Optional.none(); |
| } |
| getOrNull() { |
| return this.tag ? this.value : null; |
| } |
| getOrUndefined() { |
| return this.value; |
| } |
| each(worker) { |
| if (this.tag) { |
| worker(this.value); |
| } |
| } |
| toArray() { |
| return this.tag ? [this.value] : []; |
| } |
| toString() { |
| return this.tag ? `some(${ this.value })` : 'none()'; |
| } |
| } |
| Optional.singletonNone = new Optional(false); |
|
|
| const map = (xs, f) => { |
| const len = xs.length; |
| const r = new Array(len); |
| for (let i = 0; i < len; i++) { |
| const x = xs[i]; |
| r[i] = f(x, i); |
| } |
| return r; |
| }; |
| const each$1 = (xs, f) => { |
| for (let i = 0, len = xs.length; i < len; i++) { |
| const x = xs[i]; |
| f(x, i); |
| } |
| }; |
| const filter = (xs, pred) => { |
| const r = []; |
| for (let i = 0, len = xs.length; i < len; i++) { |
| const x = xs[i]; |
| if (pred(x, i)) { |
| r.push(x); |
| } |
| } |
| return r; |
| }; |
|
|
| const keys = Object.keys; |
| const each = (obj, f) => { |
| const props = keys(obj); |
| for (let k = 0, len = props.length; k < len; k++) { |
| const i = props[k]; |
| const x = obj[i]; |
| f(x, i); |
| } |
| }; |
|
|
| const Global = typeof window !== 'undefined' ? window : Function('return this;')(); |
|
|
| const path = (parts, scope) => { |
| let o = scope !== undefined && scope !== null ? scope : Global; |
| for (let i = 0; i < parts.length && o !== undefined && o !== null; ++i) { |
| o = o[parts[i]]; |
| } |
| return o; |
| }; |
| const resolve = (p, scope) => { |
| const parts = p.split('.'); |
| return path(parts, scope); |
| }; |
|
|
| const unsafe = (name, scope) => { |
| return resolve(name, scope); |
| }; |
| const getOrDie = (name, scope) => { |
| const actual = unsafe(name, scope); |
| if (actual === undefined || actual === null) { |
| throw new Error(name + ' not available on this browser'); |
| } |
| return actual; |
| }; |
|
|
| const getPrototypeOf = Object.getPrototypeOf; |
| const sandHTMLElement = scope => { |
| return getOrDie('HTMLElement', scope); |
| }; |
| const isPrototypeOf = x => { |
| const scope = resolve('ownerDocument.defaultView', x); |
| return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\w*Element$/.test(getPrototypeOf(x).constructor.name)); |
| }; |
|
|
| const ELEMENT = 1; |
| const TEXT = 3; |
|
|
| const type = element => element.dom.nodeType; |
| const value = element => element.dom.nodeValue; |
| const isType = t => element => type(element) === t; |
| const isHTMLElement = element => isElement(element) && isPrototypeOf(element.dom); |
| const isElement = isType(ELEMENT); |
| const isText = isType(TEXT); |
|
|
| const rawSet = (dom, key, value) => { |
| if (isString(value) || isBoolean(value) || isNumber(value)) { |
| dom.setAttribute(key, value + ''); |
| } else { |
| console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom); |
| throw new Error('Attribute value was not simple'); |
| } |
| }; |
| const set = (element, key, value) => { |
| rawSet(element.dom, key, value); |
| }; |
| const get$1 = (element, key) => { |
| const v = element.dom.getAttribute(key); |
| return v === null ? undefined : v; |
| }; |
| const remove$3 = (element, key) => { |
| element.dom.removeAttribute(key); |
| }; |
|
|
| const read = (element, attr) => { |
| const value = get$1(element, attr); |
| return value === undefined || value === '' ? [] : value.split(' '); |
| }; |
| const add$2 = (element, attr, id) => { |
| const old = read(element, attr); |
| const nu = old.concat([id]); |
| set(element, attr, nu.join(' ')); |
| return true; |
| }; |
| const remove$2 = (element, attr, id) => { |
| const nu = filter(read(element, attr), v => v !== id); |
| if (nu.length > 0) { |
| set(element, attr, nu.join(' ')); |
| } else { |
| remove$3(element, attr); |
| } |
| return false; |
| }; |
|
|
| const supports = element => element.dom.classList !== undefined; |
| const get = element => read(element, 'class'); |
| const add$1 = (element, clazz) => add$2(element, 'class', clazz); |
| const remove$1 = (element, clazz) => remove$2(element, 'class', clazz); |
|
|
| const add = (element, clazz) => { |
| if (supports(element)) { |
| element.dom.classList.add(clazz); |
| } else { |
| add$1(element, clazz); |
| } |
| }; |
| const cleanClass = element => { |
| const classList = supports(element) ? element.dom.classList : get(element); |
| if (classList.length === 0) { |
| remove$3(element, 'class'); |
| } |
| }; |
| const remove = (element, clazz) => { |
| if (supports(element)) { |
| const classList = element.dom.classList; |
| classList.remove(clazz); |
| } else { |
| remove$1(element, clazz); |
| } |
| cleanClass(element); |
| }; |
|
|
| const fromHtml = (html, scope) => { |
| const doc = scope || document; |
| const div = doc.createElement('div'); |
| div.innerHTML = html; |
| if (!div.hasChildNodes() || div.childNodes.length > 1) { |
| const message = 'HTML does not have a single root node'; |
| console.error(message, html); |
| throw new Error(message); |
| } |
| return fromDom(div.childNodes[0]); |
| }; |
| const fromTag = (tag, scope) => { |
| const doc = scope || document; |
| const node = doc.createElement(tag); |
| return fromDom(node); |
| }; |
| const fromText = (text, scope) => { |
| const doc = scope || document; |
| const node = doc.createTextNode(text); |
| return fromDom(node); |
| }; |
| const fromDom = node => { |
| if (node === null || node === undefined) { |
| throw new Error('Node cannot be null or undefined'); |
| } |
| return { dom: node }; |
| }; |
| const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom); |
| const SugarElement = { |
| fromHtml, |
| fromTag, |
| fromText, |
| fromDom, |
| fromPoint |
| }; |
|
|
| const charMap = { |
| '\xA0': 'nbsp', |
| '\xAD': 'shy' |
| }; |
| const charMapToRegExp = (charMap, global) => { |
| let regExp = ''; |
| each(charMap, (_value, key) => { |
| regExp += key; |
| }); |
| return new RegExp('[' + regExp + ']', global ? 'g' : ''); |
| }; |
| const charMapToSelector = charMap => { |
| let selector = ''; |
| each(charMap, value => { |
| if (selector) { |
| selector += ','; |
| } |
| selector += 'span.mce-' + value; |
| }); |
| return selector; |
| }; |
| const regExp = charMapToRegExp(charMap); |
| const regExpGlobal = charMapToRegExp(charMap, true); |
| const selector = charMapToSelector(charMap); |
| const nbspClass = 'mce-nbsp'; |
|
|
| const getRaw = element => element.dom.contentEditable; |
|
|
| const wrapCharWithSpan = value => '<span data-mce-bogus="1" class="mce-' + charMap[value] + '">' + value + '</span>'; |
|
|
| const isWrappedNbsp = node => node.nodeName.toLowerCase() === 'span' && node.classList.contains('mce-nbsp-wrap'); |
| const isMatch = n => { |
| const value$1 = value(n); |
| return isText(n) && isString(value$1) && regExp.test(value$1); |
| }; |
| const isContentEditableFalse = node => isHTMLElement(node) && getRaw(node) === 'false'; |
| const isChildEditable = (node, currentState) => { |
| if (isHTMLElement(node) && !isWrappedNbsp(node.dom)) { |
| const value = getRaw(node); |
| if (value === 'true') { |
| return true; |
| } else if (value === 'false') { |
| return false; |
| } |
| } |
| return currentState; |
| }; |
| const filterEditableDescendants = (scope, predicate, editable) => { |
| let result = []; |
| const dom = scope.dom; |
| const children = map(dom.childNodes, SugarElement.fromDom); |
| const isEditable = node => isWrappedNbsp(node.dom) || !isContentEditableFalse(node); |
| each$1(children, x => { |
| if (editable && isEditable(x) && predicate(x)) { |
| result = result.concat([x]); |
| } |
| result = result.concat(filterEditableDescendants(x, predicate, isChildEditable(x, editable))); |
| }); |
| return result; |
| }; |
| const findParentElm = (elm, rootElm) => { |
| while (elm.parentNode) { |
| if (elm.parentNode === rootElm) { |
| return rootElm; |
| } |
| elm = elm.parentNode; |
| } |
| return undefined; |
| }; |
| const replaceWithSpans = text => text.replace(regExpGlobal, wrapCharWithSpan); |
|
|
| const show = (editor, rootElm) => { |
| const dom = editor.dom; |
| const nodeList = filterEditableDescendants(SugarElement.fromDom(rootElm), isMatch, editor.dom.isEditable(rootElm)); |
| each$1(nodeList, n => { |
| var _a; |
| const parent = n.dom.parentNode; |
| if (isWrappedNbsp(parent)) { |
| add(SugarElement.fromDom(parent), nbspClass); |
| } else { |
| const withSpans = replaceWithSpans(dom.encode((_a = value(n)) !== null && _a !== void 0 ? _a : '')); |
| const div = dom.create('div', {}, withSpans); |
| let node; |
| while (node = div.lastChild) { |
| dom.insertAfter(node, n.dom); |
| } |
| editor.dom.remove(n.dom); |
| } |
| }); |
| }; |
| const hide = (editor, rootElm) => { |
| const nodeList = editor.dom.select(selector, rootElm); |
| each$1(nodeList, node => { |
| if (isWrappedNbsp(node)) { |
| remove(SugarElement.fromDom(node), nbspClass); |
| } else { |
| editor.dom.remove(node, true); |
| } |
| }); |
| }; |
| const toggle = editor => { |
| const body = editor.getBody(); |
| const bookmark = editor.selection.getBookmark(); |
| let parentNode = findParentElm(editor.selection.getNode(), body); |
| parentNode = parentNode !== undefined ? parentNode : body; |
| hide(editor, parentNode); |
| show(editor, parentNode); |
| editor.selection.moveToBookmark(bookmark); |
| }; |
|
|
| const applyVisualChars = (editor, toggleState) => { |
| fireVisualChars(editor, toggleState.get()); |
| const body = editor.getBody(); |
| if (toggleState.get() === true) { |
| show(editor, body); |
| } else { |
| hide(editor, body); |
| } |
| }; |
| const toggleVisualChars = (editor, toggleState) => { |
| toggleState.set(!toggleState.get()); |
| const bookmark = editor.selection.getBookmark(); |
| applyVisualChars(editor, toggleState); |
| editor.selection.moveToBookmark(bookmark); |
| }; |
|
|
| const register$2 = (editor, toggleState) => { |
| editor.addCommand('mceVisualChars', () => { |
| toggleVisualChars(editor, toggleState); |
| }); |
| }; |
|
|
| const option = name => editor => editor.options.get(name); |
| const register$1 = editor => { |
| const registerOption = editor.options.register; |
| registerOption('visualchars_default_state', { |
| processor: 'boolean', |
| default: false |
| }); |
| }; |
| const isEnabledByDefault = option('visualchars_default_state'); |
|
|
| const setup$1 = (editor, toggleState) => { |
| editor.on('init', () => { |
| applyVisualChars(editor, toggleState); |
| }); |
| }; |
|
|
| const first = (fn, rate) => { |
| let timer = null; |
| const cancel = () => { |
| if (!isNull(timer)) { |
| clearTimeout(timer); |
| timer = null; |
| } |
| }; |
| const throttle = (...args) => { |
| if (isNull(timer)) { |
| timer = setTimeout(() => { |
| timer = null; |
| fn.apply(null, args); |
| }, rate); |
| } |
| }; |
| return { |
| cancel, |
| throttle |
| }; |
| }; |
|
|
| const setup = (editor, toggleState) => { |
| const debouncedToggle = first(() => { |
| toggle(editor); |
| }, 300); |
| editor.on('keydown', e => { |
| if (toggleState.get() === true) { |
| e.keyCode === 13 ? toggle(editor) : debouncedToggle.throttle(); |
| } |
| }); |
| editor.on('remove', debouncedToggle.cancel); |
| }; |
|
|
| const toggleActiveState = (editor, enabledStated) => api => { |
| api.setActive(enabledStated.get()); |
| const editorEventCallback = e => api.setActive(e.state); |
| editor.on('VisualChars', editorEventCallback); |
| return () => editor.off('VisualChars', editorEventCallback); |
| }; |
| const register = (editor, toggleState) => { |
| const onAction = () => editor.execCommand('mceVisualChars'); |
| editor.ui.registry.addToggleButton('visualchars', { |
| tooltip: 'Show invisible characters', |
| icon: 'visualchars', |
| onAction, |
| onSetup: toggleActiveState(editor, toggleState) |
| }); |
| editor.ui.registry.addToggleMenuItem('visualchars', { |
| text: 'Show invisible characters', |
| icon: 'visualchars', |
| onAction, |
| onSetup: toggleActiveState(editor, toggleState) |
| }); |
| }; |
|
|
| var Plugin = () => { |
| global.add('visualchars', editor => { |
| register$1(editor); |
| const toggleState = Cell(isEnabledByDefault(editor)); |
| register$2(editor, toggleState); |
| register(editor, toggleState); |
| setup(editor, toggleState); |
| setup$1(editor, toggleState); |
| return get$2(toggleState); |
| }); |
| }; |
|
|
| Plugin(); |
|
|
| })(); |
|
|