391 lines
12 KiB
JavaScript
391 lines
12 KiB
JavaScript
/**
|
|
* TinyMCE version 7.5.1 (TBD)
|
|
*/
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
|
|
|
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 isString = isType$1('string');
|
|
const isBoolean = isSimpleType('boolean');
|
|
const isNullable = a => a === null || a === undefined;
|
|
const isNonNullable = a => !isNullable(a);
|
|
const isFunction = isSimpleType('function');
|
|
const isNumber = isSimpleType('number');
|
|
|
|
const compose1 = (fbc, fab) => a => fbc(fab(a));
|
|
const constant = value => {
|
|
return () => {
|
|
return value;
|
|
};
|
|
};
|
|
const never = constant(false);
|
|
|
|
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 = (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 DOCUMENT_FRAGMENT = 11;
|
|
const ELEMENT = 1;
|
|
const TEXT = 3;
|
|
|
|
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 is = (element, selector) => {
|
|
const dom = element.dom;
|
|
if (dom.nodeType !== ELEMENT) {
|
|
return false;
|
|
} else {
|
|
const elem = dom;
|
|
if (elem.matches !== undefined) {
|
|
return elem.matches(selector);
|
|
} else if (elem.msMatchesSelector !== undefined) {
|
|
return elem.msMatchesSelector(selector);
|
|
} else if (elem.webkitMatchesSelector !== undefined) {
|
|
return elem.webkitMatchesSelector(selector);
|
|
} else if (elem.mozMatchesSelector !== undefined) {
|
|
return elem.mozMatchesSelector(selector);
|
|
} else {
|
|
throw new Error('Browser lacks native selectors');
|
|
}
|
|
}
|
|
};
|
|
|
|
typeof window !== 'undefined' ? window : Function('return this;')();
|
|
|
|
const name = element => {
|
|
const r = element.dom.nodeName;
|
|
return r.toLowerCase();
|
|
};
|
|
const type = element => element.dom.nodeType;
|
|
const isType = t => element => type(element) === t;
|
|
const isElement = isType(ELEMENT);
|
|
const isText = isType(TEXT);
|
|
const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
|
|
const isTag = tag => e => isElement(e) && name(e) === tag;
|
|
|
|
const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
|
|
const children$2 = element => map(element.dom.childNodes, SugarElement.fromDom);
|
|
|
|
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 remove = (element, key) => {
|
|
element.dom.removeAttribute(key);
|
|
};
|
|
|
|
const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
|
|
const getRootNode = e => SugarElement.fromDom(e.dom.getRootNode());
|
|
const getShadowRoot = e => {
|
|
const r = getRootNode(e);
|
|
return isShadowRoot(r) ? Optional.some(r) : Optional.none();
|
|
};
|
|
const getShadowHost = e => SugarElement.fromDom(e.dom.host);
|
|
|
|
const inBody = element => {
|
|
const dom = isText(element) ? element.dom.parentNode : element.dom;
|
|
if (dom === undefined || dom === null || dom.ownerDocument === null) {
|
|
return false;
|
|
}
|
|
const doc = dom.ownerDocument;
|
|
return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
|
|
};
|
|
|
|
const ancestor$1 = (scope, predicate, isRoot) => {
|
|
let element = scope.dom;
|
|
const stop = isFunction(isRoot) ? isRoot : never;
|
|
while (element.parentNode) {
|
|
element = element.parentNode;
|
|
const el = SugarElement.fromDom(element);
|
|
if (predicate(el)) {
|
|
return Optional.some(el);
|
|
} else if (stop(el)) {
|
|
break;
|
|
}
|
|
}
|
|
return Optional.none();
|
|
};
|
|
|
|
const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);
|
|
|
|
const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);
|
|
|
|
const get = (element, property) => {
|
|
const dom = element.dom;
|
|
const styles = window.getComputedStyle(dom);
|
|
const r = styles.getPropertyValue(property);
|
|
return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
|
|
};
|
|
const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';
|
|
|
|
const getDirection = element => get(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';
|
|
|
|
const children$1 = (scope, predicate) => filter(children$2(scope), predicate);
|
|
|
|
const children = (scope, selector) => children$1(scope, e => is(e, selector));
|
|
|
|
const getParentElement = element => parent(element).filter(isElement);
|
|
const getNormalizedBlock = (element, isListItem) => {
|
|
const normalizedElement = isListItem ? ancestor(element, 'ol,ul') : Optional.some(element);
|
|
return normalizedElement.getOr(element);
|
|
};
|
|
const isListItem = isTag('li');
|
|
const setDirOnElements = (dom, blocks, dir) => {
|
|
each(blocks, block => {
|
|
const blockElement = SugarElement.fromDom(block);
|
|
const isBlockElementListItem = isListItem(blockElement);
|
|
const normalizedBlock = getNormalizedBlock(blockElement, isBlockElementListItem);
|
|
const normalizedBlockParent = getParentElement(normalizedBlock);
|
|
normalizedBlockParent.each(parent => {
|
|
dom.setStyle(normalizedBlock.dom, 'direction', null);
|
|
const parentDirection = getDirection(parent);
|
|
if (parentDirection === dir) {
|
|
remove(normalizedBlock, 'dir');
|
|
} else {
|
|
set(normalizedBlock, 'dir', dir);
|
|
}
|
|
if (getDirection(normalizedBlock) !== dir) {
|
|
dom.setStyle(normalizedBlock.dom, 'direction', dir);
|
|
}
|
|
if (isBlockElementListItem) {
|
|
const listItems = children(normalizedBlock, 'li[dir],li[style]');
|
|
each(listItems, listItem => {
|
|
remove(listItem, 'dir');
|
|
dom.setStyle(listItem.dom, 'direction', null);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
};
|
|
const setDir = (editor, dir) => {
|
|
if (editor.selection.isEditable()) {
|
|
setDirOnElements(editor.dom, editor.selection.getSelectedBlocks(), dir);
|
|
editor.nodeChanged();
|
|
}
|
|
};
|
|
|
|
const register$1 = editor => {
|
|
editor.addCommand('mceDirectionLTR', () => {
|
|
setDir(editor, 'ltr');
|
|
});
|
|
editor.addCommand('mceDirectionRTL', () => {
|
|
setDir(editor, 'rtl');
|
|
});
|
|
};
|
|
|
|
const getNodeChangeHandler = (editor, dir) => api => {
|
|
const nodeChangeHandler = e => {
|
|
const element = SugarElement.fromDom(e.element);
|
|
api.setActive(getDirection(element) === dir);
|
|
api.setEnabled(editor.selection.isEditable());
|
|
};
|
|
editor.on('NodeChange', nodeChangeHandler);
|
|
api.setEnabled(editor.selection.isEditable());
|
|
return () => editor.off('NodeChange', nodeChangeHandler);
|
|
};
|
|
const register = editor => {
|
|
editor.ui.registry.addToggleButton('ltr', {
|
|
tooltip: 'Left to right',
|
|
icon: 'ltr',
|
|
onAction: () => editor.execCommand('mceDirectionLTR'),
|
|
onSetup: getNodeChangeHandler(editor, 'ltr')
|
|
});
|
|
editor.ui.registry.addToggleButton('rtl', {
|
|
tooltip: 'Right to left',
|
|
icon: 'rtl',
|
|
onAction: () => editor.execCommand('mceDirectionRTL'),
|
|
onSetup: getNodeChangeHandler(editor, 'rtl')
|
|
});
|
|
};
|
|
|
|
var Plugin = () => {
|
|
global.add('directionality', editor => {
|
|
register$1(editor);
|
|
register(editor);
|
|
});
|
|
};
|
|
|
|
Plugin();
|
|
|
|
})();
|