PageSourceSearch

https://www.mapcon.com/lib/tinymce/plugins/table/plugin.csgfqsu7ep.js

js mapcon.com collected 2026-09-24 11:35:25 UTC 163,314 bytes, 4,019 lines download raw bytes

vendor: 7,684 bytes, lines 1-210
1/**
2 * TinyMCE version 7.9.1 (2025-05-29)
3 */
4
5(function () {
6    'use strict';
7
8    var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');
9
10    /* eslint-disable @typescript-eslint/no-wrapper-object-types */
11    const hasProto = (v, constructor, predicate) => {
12        var _a;
13        if (predicate(v, constructor.prototype)) {
14            return true;
15        }
16        else {
17            // String-based fallback time
18            return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
19        }
20    };
21    const typeOf = (x) => {
22        const t = typeof x;
23        if (x === null) {
24            return 'null';
25        }
26        else if (t === 'object' && Array.isArray(x)) {
27            return 'array';
28        }
29        else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
30            return 'string';
31        }
32        else {
33            return t;
34        }
35    };
36    const isType$1 = (type) => (value) => typeOf(value) === type;
37    const isSimpleType = (type) => (value) => typeof value === type;
38    const eq$1 = (t) => (a) => t === a;
39    const isString = isType$1('string');
40    const isArray = isType$1('array');
41    const isBoolean = isSimpleType('boolean');
42    const isUndefined = eq$1(undefined);
43    const isNullable = (a) => a === null || a === undefined;
44    const isNonNullable = (a) => !isNullable(a);
45    const isFunction = isSimpleType('function');
46    const isNumber = isSimpleType('number');
47
48    const noop = () => { };
49    /** Compose two unary functions. Similar to compose, but avoids using Function.prototype.apply. */
50    const compose1 = (fbc, fab) => (a) => fbc(fab(a));
51    const constant = (value) => {
52        return () => {
53            return value;
54        };
55    };
56    const identity = (x) => {
57        return x;
58    };
59    const tripleEquals = (a, b) => {
60        return a === b;
61    };
62    // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
63    function curry(fn, ...initialArgs) {
64        return (...restArgs) => {
65            const all = initialArgs.concat(restArgs);
66            return fn.apply(null, all);
67        };
68    }
69    const call = (f) => {
70        f();
71    };
72    const never = constant(false);
73    const always = constant(true);
74
75    /**
76     * The `Optional` type represents a value (of any type) that potentially does
77     * not exist. Any `Optional<T>` can either be a `Some<T>` (in which case the
78     * value does exist) or a `None` (in which case the value does not exist). This
79     * module defines a whole lot of FP-inspired utility functions for dealing with
80     * `Optional` objects.
81     *
82     * Comparison with null or undefined:
83     * - We don't get fancy null coalescing operators with `Optional`
84     * - We do get fancy helper functions with `Optional`
85     * - `Optional` support nesting, and allow for the type to still be nullable (or
86     * another `Optional`)
87     * - There is no option to turn off strict-optional-checks like there is for
88     * strict-null-checks
89     */
90    class Optional {
91        // The internal representation has a `tag` and a `value`, but both are
92        // private: able to be console.logged, but not able to be accessed by code
93        constructor(tag, value) {
94            this.tag = tag;
95            this.value = value;
96        }
97        // --- Identities ---
98        /**
99         * Creates a new `Optional<T>` that **does** contain a value.
100         */
101        static some(value) {
102            return new Optional(true, value);
103        }
104        /**
105         * Create a new `Optional<T>` that **does not** contain a value. `T` can be
106         * any type because we don't actually have a `T`.
107         */
108        static none() {
109            return Optional.singletonNone;
110        }
111        /**
112         * Perform a transform on an `Optional` type. Regardless of whether this
113         * `Optional` contains a value or not, `fold` will return a value of type `U`.
114         * If this `Optional` does not contain a value, the `U` will be created by
115         * calling `onNone`. If this `Optional` does contain a value, the `U` will be
116         * created by calling `onSome`.
117         *
118         * For the FP enthusiasts in the room, this function:
119         * 1. Could be used to implement all of the functions below
120         * 2. Forms a catamorphism
121         */
122        fold(onNone, onSome) {
123            if (this.tag) {
124                return onSome(this.value);
125            }
126            else {
127                return onNone();
128            }
129        }
130        /**
131         * Determine if this `Optional` object contains a value.
132         */
133        isSome() {
134            return this.tag;
135        }
136        /**
137         * Determine if this `Optional` object **does not** contain a value.
138         */
139        isNone() {
140            return !this.tag;
141        }
142        // --- Functor (name stolen from Haskell / maths) ---
143        /**
144         * Perform a transform on an `Optional` object, **if** there is a value. If
145         * you provide a function to turn a T into a U, this is the function you use
146         * to turn an `Optional<T>` into an `Optional<U>`. If this **does** contain
147         * a value then the output will also contain a value (that value being the
148         * output of `mapper(this.value)`), and if this **does not** contain a value
149         * then neither will the output.
150         */
151        map(mapper) {
152            if (this.tag) {
153                return Optional.some(mapper(this.value));
154            }
155            else {
156                return Optional.none();
157            }
158        }
159        // --- Monad (name stolen from Haskell / maths) ---
160        /**
161         * Perform a transform on an `Optional` object, **if** there is a value.
162         * Unlike `map`, here the transform itself also returns an `Optional`.
163         */
164        bind(binder) {
165            if (this.tag) {
166                return binder(this.value);
167            }
168            else {
169                return Optional.none();
170            }
171        }
172        // --- Traversable (name stolen from Haskell / maths) ---
173        /**
174         * For a given predicate, this function finds out if there **exists** a value
175         * inside this `Optional` object that meets the predicate. In practice, this
176         * means that for `Optional`s that do not contain a value it returns false (as
177         * no predicate-meeting value exists).
178         */
179        exists(predicate) {
180            return this.tag && predicate(this.value);
181        }
182        /**
183         * For a given predicate, this function finds out if **all** the values inside
184         * this `Optional` object meet the predicate. In practice, this means that
185         * for `Optional`s that do not contain a value it returns true (as all 0
186         * objects do meet the predicate).
187         */
188        forall(predicate) {
189            return !this.tag || predicate(this.value);
190        }
191        filter(predicate) {
192            if (!this.tag || predicate(this.value)) {
193                return this;
194            }
195            else {
196                return Optional.none();
197            }
198        }
199        // --- Getters ---
200        /**
201         * Get the value out of the inside of the `Optional` object, using a default
202         * `replacement` value if the provided `Optional` object does not contain a
203         * value.
204         */
205        getOr(replacement) {
206            return this.tag ? this.value : replacement;
207        }
208        /**
209         * Get the value out of the inside of the `Optional` object, using a default
210         * `replacement` value if the provided `Optional` object does not contain a
211         * value.  Unlike `getOr`, in this method the `replacement` object is also
212         * `Optional` - meaning that this method will always return an `Optional`.
213         */
214        or(replacement) {
215            return this.tag ? this : replacement;
216        }
217        /**
218         * Get the value out of the inside of the `Optional` object, using a default
219         * `replacement` value if the provided `Optional` object does not contain a
220         * value. Unlike `getOr`, in this method the `replacement` value is
221         * "thunked" - that is to say that you don't pass a value to `getOrThunk`, you
222         * pass a function which (if called) will **return** the `value` you want to
223         * use.
224         */
225        getOrThunk(thunk) {
226            return this.tag ? this.value : thunk();
227        }
228        /**
229         * Get the value out of the inside of the `Optional` object, using a default
230         * `replacement` value if the provided Optional object does not contain a
231         * value.
232         *
233         * Unlike `or`, in this method the `replacement` value is "thunked" - that is
234         * to say that you don't pass a value to `orThunk`, you pass a function which
235         * (if called) will **return** the `value` you want to use.
236         *
237         * Unlike `getOrThunk`, in this method the `replacement` value is also
238         * `Optional`, meaning that this method will always return an `Optional`.
239         */
240        orThunk(thunk) {
241            return this.tag ? this : thunk();
242        }
243        /**
244         * Get the value out of the inside of the `Optional` object, throwing an
245         * exception if the provided `Optional` object does not contain a value.
246         *
247         * WARNING:
248         * You should only be using this function if you know that the `Optional`
249         * object **is not** empty (otherwise you're throwing exceptions in production
250         * code, which is bad).
251         *
252         * In tests this is more acceptable.
253         *
254         * Prefer other methods to this, such as `.each`.
255         */
256        getOrDie(message) {
257            if (!this.tag) {
258                throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
259            }
260            else {
261                return this.value;
262            }
263        }
264        // --- Interop with null and undefined ---
265        /**
vendor: 5,993 bytes, lines 266-434
266         * Creates an `Optional` value from a nullable (or undefined-able) input.
267         * Null, or undefined, is converted to `None`, and anything else is converted
268         * to `Some`.
269         */
270        static from(value) {
271            return isNonNullable(value) ? Optional.some(value) : Optional.none();
272        }
273        /**
274         * Converts an `Optional` to a nullable type, by getting the value if it
275         * exists, or returning `null` if it does not.
276         */
277        getOrNull() {
278            return this.tag ? this.value : null;
279        }
280        /**
281         * Converts an `Optional` to an undefined-able type, by getting the value if
282         * it exists, or returning `undefined` if it does not.
283         */
284        getOrUndefined() {
285            return this.value;
286        }
287        // --- Utilities ---
288        /**
289         * If the `Optional` contains a value, perform an action on that value.
290         * Unlike the rest of the methods on this type, `.each` has side-effects. If
291         * you want to transform an `Optional<T>` **into** something, then this is not
292         * the method for you. If you want to use an `Optional<T>` to **do**
293         * something, then this is the method for you - provided you're okay with not
294         * doing anything in the case where the `Optional` doesn't have a value inside
295         * it. If you're not sure whether your use-case fits into transforming
296         * **into** something or **doing** something, check whether it has a return
297         * value. If it does, you should be performing a transform.
298         */
299        each(worker) {
300            if (this.tag) {
301                worker(this.value);
302            }
303        }
304        /**
305         * Turn the `Optional` object into an array that contains all of the values
306         * stored inside the `Optional`. In practice, this means the output will have
307         * either 0 or 1 elements.
308         */
309        toArray() {
310            return this.tag ? [this.value] : [];
311        }
312        /**
313         * Turn the `Optional` object into a string for debugging or printing. Not
314         * recommended for production code, but good for debugging. Also note that
315         * these days an `Optional` object can be logged to the console directly, and
316         * its inner value (if it exists) will be visible.
317         */
318        toString() {
319            return this.tag ? `some(${this.value})` : 'none()';
320        }
321    }
322    // Sneaky optimisation: every instance of Optional.none is identical, so just
323    // reuse the same object
324    Optional.singletonNone = new Optional(false);
325
326    /* eslint-disable @typescript-eslint/unbound-method */
327    const nativeSlice = Array.prototype.slice;
328    const nativeIndexOf = Array.prototype.indexOf;
329    const nativePush = Array.prototype.push;
330    /* eslint-enable */
331    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
332    const contains = (xs, x) => rawIndexOf(xs, x) > -1;
333    const exists = (xs, pred) => {
334        for (let i = 0, len = xs.length; i < len; i++) {
335            const x = xs[i];
336            if (pred(x, i)) {
337                return true;
338            }
339        }
340        return false;
341    };
342    const range = (num, f) => {
343        const r = [];
344        for (let i = 0; i < num; i++) {
345            r.push(f(i));
346        }
347        return r;
348    };
349    const map = (xs, f) => {
350        // pre-allocating array size when it's guaranteed to be known
351        // http://jsperf.com/push-allocated-vs-dynamic/22
352        const len = xs.length;
353        const r = new Array(len);
354        for (let i = 0; i < len; i++) {
355            const x = xs[i];
356            r[i] = f(x, i);
357        }
358        return r;
359    };
360    // Unwound implementing other functions in terms of each.
361    // The code size is roughly the same, and it should allow for better optimisation.
362    // const each = function<T, U>(xs: T[], f: (x: T, i?: number, xs?: T[]) => void): void {
363    const each$1 = (xs, f) => {
364        for (let i = 0, len = xs.length; i < len; i++) {
365            const x = xs[i];
366            f(x, i);
367        }
368    };
369    const eachr = (xs, f) => {
370        for (let i = xs.length - 1; i >= 0; i--) {
371            const x = xs[i];
372            f(x, i);
373        }
374    };
375    const partition = (xs, pred) => {
376        const pass = [];
377        const fail = [];
378        for (let i = 0, len = xs.length; i < len; i++) {
379            const x = xs[i];
380            const arr = pred(x, i) ? pass : fail;
381            arr.push(x);
382        }
383        return { pass, fail };
384    };
385    const filter$1 = (xs, pred) => {
386        const r = [];
387        for (let i = 0, len = xs.length; i < len; i++) {
388            const x = xs[i];
389            if (pred(x, i)) {
390                r.push(x);
391            }
392        }
393        return r;
394    };
395    const foldr = (xs, f, acc) => {
396        eachr(xs, (x, i) => {
397            acc = f(acc, x, i);
398        });
399        return acc;
400    };
401    const foldl = (xs, f, acc) => {
402        each$1(xs, (x, i) => {
403            acc = f(acc, x, i);
404        });
405        return acc;
406    };
407    const findUntil = (xs, pred, until) => {
408        for (let i = 0, len = xs.length; i < len; i++) {
409            const x = xs[i];
410            if (pred(x, i)) {
411                return Optional.some(x);
412            }
413            else if (until(x, i)) {
414                break;
415            }
416        }
417        return Optional.none();
418    };
419    const find = (xs, pred) => {
420        return findUntil(xs, pred, never);
421    };
422    const findIndex = (xs, pred) => {
423        for (let i = 0, len = xs.length; i < len; i++) {
424            const x = xs[i];
425            if (pred(x, i)) {
426                return Optional.some(i);
427            }
428        }
429        return Optional.none();
430    };
431    const flatten$1 = (xs) => {
432        // Note, this is possible because push supports multiple arguments:
433        // http://jsperf.com/concat-push/6
434        // Note that in the past, concat() would silently work (very slowly) for array-like objects.
vendor: 32,234 bytes, lines 435-1275
435        // With this change it will throw an error.
436        const r = [];
437        for (let i = 0, len = xs.length; i < len; ++i) {
438            // Ensure that each value is an array itself
439            if (!isArray(xs[i])) {
440                throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
441            }
442            nativePush.apply(r, xs[i]);
443        }
444        return r;
445    };
446    const bind = (xs, f) => flatten$1(map(xs, f));
447    const forall = (xs, pred) => {
448        for (let i = 0, len = xs.length; i < len; ++i) {
449            const x = xs[i];
450            if (pred(x, i) !== true) {
451                return false;
452            }
453        }
454        return true;
455    };
456    const mapToObject = (xs, f) => {
457        const r = {};
458        for (let i = 0, len = xs.length; i < len; i++) {
459            const x = xs[i];
460            r[String(x)] = f(x, i);
461        }
462        return r;
463    };
464    const get$4 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
465    const head = (xs) => get$4(xs, 0);
466    const last = (xs) => get$4(xs, xs.length - 1);
467    isFunction(Array.from) ? Array.from : (x) => nativeSlice.call(x);
468    const findMap = (arr, f) => {
469        for (let i = 0; i < arr.length; i++) {
470            const r = f(arr[i], i);
471            if (r.isSome()) {
472                return r;
473            }
474        }
475        return Optional.none();
476    };
477
478    // There are many variations of Object iteration that are faster than the 'for-in' style:
479    // http://jsperf.com/object-keys-iteration/107
480    //
481    // Use the native keys if it is available (IE9+), otherwise fall back to manually filtering
482    const keys = Object.keys;
483    // eslint-disable-next-line @typescript-eslint/unbound-method
484    const hasOwnProperty = Object.hasOwnProperty;
485    const each = (obj, f) => {
486        const props = keys(obj);
487        for (let k = 0, len = props.length; k < len; k++) {
488            const i = props[k];
489            const x = obj[i];
490            f(x, i);
491        }
492    };
493    const objAcc = (r) => (x, i) => {
494        r[i] = x;
495    };
496    const internalFilter = (obj, pred, onTrue, onFalse) => {
497        each(obj, (x, i) => {
498            (pred(x, i) ? onTrue : onFalse)(x, i);
499        });
500    };
501    const filter = (obj, pred) => {
502        const t = {};
503        internalFilter(obj, pred, objAcc(t), noop);
504        return t;
505    };
506    const mapToArray = (obj, f) => {
507        const r = [];
508        each(obj, (value, name) => {
509            r.push(f(value, name));
510        });
511        return r;
512    };
513    const values = (obj) => {
514        return mapToArray(obj, identity);
515    };
516    const size = (obj) => {
517        return keys(obj).length;
518    };
519    const get$3 = (obj, key) => {
520        return has(obj, key) ? Optional.from(obj[key]) : Optional.none();
521    };
522    const has = (obj, key) => hasOwnProperty.call(obj, key);
523    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;
524    const isEmpty$1 = (r) => {
525        for (const x in r) {
526            if (hasOwnProperty.call(r, x)) {
527                return false;
528            }
529        }
530        return true;
531    };
532
533    const Cell = (initial) => {
534        let value = initial;
535        const get = () => {
536            return value;
537        };
538        const set = (v) => {
539            value = v;
540        };
541        return {
542            get,
543            set
544        };
545    };
546
547    /**
548     * **Is** the value stored inside this Optional object equal to `rhs`?
549     */
550    const is$2 = (lhs, rhs, comparator = tripleEquals) => lhs.exists((left) => comparator(left, rhs));
551    const cat = (arr) => {
552        const r = [];
553        const push = (x) => {
554            r.push(x);
555        };
556        for (let i = 0; i < arr.length; i++) {
557            arr[i].each(push);
558        }
559        return r;
560    };
561    /*
562    Notes on the lift functions:
563    - We used to have a generic liftN, but we were concerned about its type-safety, and the below variants were faster in microbenchmarks.
564    - The getOrDie calls are partial functions, but are checked beforehand. This is faster and more convenient (but less safe) than folds.
565    - && is used instead of a loop for simplicity and performance.
566    */
567    const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();
568    const flatten = (oot) => oot.bind(identity);
569    // This can help with type inference, by specifying the type param on the none case, so the caller doesn't have to.
570    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();
571
572    const singleton = (doRevoke) => {
573        const subject = Cell(Optional.none());
574        const revoke = () => subject.get().each(doRevoke);
575        const clear = () => {
576            revoke();
577            subject.set(Optional.none());
578        };
579        const isSet = () => subject.get().isSome();
580        const get = () => subject.get();
581        const set = (s) => {
582            revoke();
583            subject.set(Optional.some(s));
584        };
585        return {
586            clear,
587            isSet,
588            get,
589            set
590        };
591    };
592    const unbindable = () => singleton((s) => s.unbind());
593
594    const removeFromStart = (str, numChars) => {
595        return str.substring(numChars);
596    };
597
598    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
599    const removeLeading = (str, prefix) => {
600        return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
601    };
602    /** Does 'str' start with 'prefix'?
603     *  Note: all strings start with the empty string.
604     *        More formally, for all strings x, startsWith(x, "").
605     *        This is so that for all strings x and y, startsWith(y + x, y)
606     */
607    const startsWith = (str, prefix) => {
608        return checkRange(str, prefix, 0);
609    };
610    const blank = (r) => (s) => s.replace(r, '');
611    /** removes all leading and trailing spaces */
612    const trim = blank(/^\s+|\s+$/g);
613    const isNotEmpty = (s) => s.length > 0;
614    const isEmpty = (s) => !isNotEmpty(s);
615    const toInt = (value, radix = 10) => {
616        const num = parseInt(value, radix);
617        return isNaN(num) ? Optional.none() : Optional.some(num);
618    };
619    const toFloat = (value) => {
620        const num = parseFloat(value);
621        return isNaN(num) ? Optional.none() : Optional.some(num);
622    };
623
624    const cached = (f) => {
625        let called = false;
626        let r;
627        return (...args) => {
628            if (!called) {
629                called = true;
630                r = f.apply(null, args);
631            }
632            return r;
633        };
634    };
635
636    const fromHtml = (html, scope) => {
637        const doc = scope || document;
638        const div = doc.createElement('div');
639        div.innerHTML = html;
640        if (!div.hasChildNodes() || div.childNodes.length > 1) {
641            const message = 'HTML does not have a single root node';
642            // eslint-disable-next-line no-console
643            console.error(message, html);
644            throw new Error(message);
645        }
646        return fromDom$1(div.childNodes[0]);
647    };
648    const fromTag = (tag, scope) => {
649        const doc = scope || document;
650        const node = doc.createElement(tag);
651        return fromDom$1(node);
652    };
653    const fromText = (text, scope) => {
654        const doc = scope || document;
655        const node = doc.createTextNode(text);
656        return fromDom$1(node);
657    };
658    const fromDom$1 = (node) => {
659        // TODO: Consider removing this check, but left atm for safety
660        if (node === null || node === undefined) {
661            throw new Error('Node cannot be null or undefined');
662        }
663        return {
664            dom: node
665        };
666    };
667    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);
668    // tslint:disable-next-line:variable-name
669    const SugarElement = {
670        fromHtml,
671        fromTag,
672        fromText,
673        fromDom: fromDom$1,
674        fromPoint
675    };
676
677    const COMMENT = 8;
678    const DOCUMENT = 9;
679    const DOCUMENT_FRAGMENT = 11;
680    const ELEMENT = 1;
681    const TEXT = 3;
682
683    const is$1 = (element, selector) => {
684        const dom = element.dom;
685        if (dom.nodeType !== ELEMENT) {
686            return false;
687        }
688        else {
689            const elem = dom;
690            if (elem.matches !== undefined) {
691                return elem.matches(selector);
692            }
693            else if (elem.msMatchesSelector !== undefined) {
694                return elem.msMatchesSelector(selector);
695            }
696            else if (elem.webkitMatchesSelector !== undefined) {
697                return elem.webkitMatchesSelector(selector);
698            }
699            else if (elem.mozMatchesSelector !== undefined) {
700                // cast to any as mozMatchesSelector doesn't exist in TS DOM lib
701                return elem.mozMatchesSelector(selector);
702            }
703            else {
704                throw new Error('Browser lacks native selectors');
705            } // unfortunately we can't throw this on startup :(
706        }
707    };
708    const bypassSelector = (dom) => 
709    // Only elements, documents and shadow roots support querySelector
710    // shadow root element type is DOCUMENT_FRAGMENT
711    dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT ||
712        // IE fix for complex queries on empty nodes: http://jsfiddle.net/spyder/fv9ptr5L/
713        dom.childElementCount === 0;
714    const all$1 = (selector, scope) => {
715        const base = scope === undefined ? document : scope.dom;
716        return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);
717    };
718    const one = (selector, scope) => {
719        const base = scope === undefined ? document : scope.dom;
720        return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);
721    };
722
723    const eq = (e1, e2) => e1.dom === e2.dom;
724    const is = is$1;
725
726    const name = (element) => {
727        const r = element.dom.nodeName;
728        return r.toLowerCase();
729    };
730    const type = (element) => element.dom.nodeType;
731    const isType = (t) => (element) => type(element) === t;
732    const isComment = (element) => type(element) === COMMENT || name(element) === '#comment';
733    const isElement = isType(ELEMENT);
734    const isText = isType(TEXT);
735    const isDocument = isType(DOCUMENT);
736    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
737    const isTag = (tag) => (e) => isElement(e) && name(e) === tag;
738
739    /**
740     * The document associated with the current element
741     * NOTE: this will throw if the owner is null.
742     */
743    const owner = (element) => SugarElement.fromDom(element.dom.ownerDocument);
744    /**
745     * If the element is a document, return it. Otherwise, return its ownerDocument.
746     * @param dos
747     */
748    const documentOrOwner = (dos) => isDocument(dos) ? dos : owner(dos);
749    const parent = (element) => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
750    const parents = (element, isRoot) => {
751        const stop = isFunction(isRoot) ? isRoot : never;
752        // This is used a *lot* so it needs to be performant, not recursive
753        let dom = element.dom;
754        const ret = [];
755        while (dom.parentNode !== null && dom.parentNode !== undefined) {
756            const rawParent = dom.parentNode;
757            const p = SugarElement.fromDom(rawParent);
758            ret.push(p);
759            if (stop(p) === true) {
760                break;
761            }
762            else {
763                dom = rawParent;
764            }
765        }
766        return ret;
767    };
768    const prevSibling = (element) => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
769    const nextSibling = (element) => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
770    const children$3 = (element) => map(element.dom.childNodes, SugarElement.fromDom);
771    const child$3 = (element, index) => {
772        const cs = element.dom.childNodes;
773        return Optional.from(cs[index]).map(SugarElement.fromDom);
774    };
775    const firstChild = (element) => child$3(element, 0);
776
777    /**
778     * Is the element a ShadowRoot?
779     *
780     * Note: this is insufficient to test if any element is a shadow root, but it is sufficient to differentiate between
781     * a Document and a ShadowRoot.
782     */
783    const isShadowRoot = (dos) => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
784    const getRootNode = (e) => SugarElement.fromDom(e.dom.getRootNode());
785    /** If this element is in a ShadowRoot, return it. */
786    const getShadowRoot = (e) => {
787        const r = getRootNode(e);
788        return isShadowRoot(r) ? Optional.some(r) : Optional.none();
789    };
790    /** Return the host of a ShadowRoot.
791     *
792     * This function will throw if Shadow DOM is unsupported in the browser, or if the host is null.
793     * If you actually have a ShadowRoot, this shouldn't happen.
794     */
795    const getShadowHost = (e) => SugarElement.fromDom(e.dom.host);
796
797    const before = (marker, element) => {
798        const parent$1 = parent(marker);
799        parent$1.each((v) => {
800            v.dom.insertBefore(element.dom, marker.dom);
801        });
802    };
803    const after$1 = (marker, element) => {
804        const sibling = nextSibling(marker);
805        sibling.fold(() => {
806            const parent$1 = parent(marker);
807            parent$1.each((v) => {
808                append$1(v, element);
809            });
810        }, (v) => {
811            before(v, element);
812        });
813    };
814    const prepend = (parent, element) => {
815        const firstChild$1 = firstChild(parent);
816        firstChild$1.fold(() => {
817            append$1(parent, element);
818        }, (v) => {
819            parent.dom.insertBefore(element.dom, v.dom);
820        });
821    };
822    const append$1 = (parent, element) => {
823        parent.dom.appendChild(element.dom);
824    };
825    const wrap = (element, wrapper) => {
826        before(element, wrapper);
827        append$1(wrapper, element);
828    };
829
830    const after = (marker, elements) => {
831        each$1(elements, (x, i) => {
832            const e = i === 0 ? marker : elements[i - 1];
833            after$1(e, x);
834        });
835    };
836    const append = (parent, elements) => {
837        each$1(elements, (x) => {
838            append$1(parent, x);
839        });
840    };
841
842    const rawSet = (dom, key, value) => {
843        /*
844         * JQuery coerced everything to a string, and silently did nothing on text node/null/undefined.
845         *
846         * We fail on those invalid cases, only allowing numbers and booleans.
847         */
848        if (isString(value) || isBoolean(value) || isNumber(value)) {
849            dom.setAttribute(key, value + '');
850        }
851        else {
852            // eslint-disable-next-line no-console
853            console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
854            throw new Error('Attribute value was not simple');
855        }
856    };
857    const set$2 = (element, key, value) => {
858        rawSet(element.dom, key, value);
859    };
860    const setAll = (element, attrs) => {
861        const dom = element.dom;
862        each(attrs, (v, k) => {
863            rawSet(dom, k, v);
864        });
865    };
866    const get$2 = (element, key) => {
867        const v = element.dom.getAttribute(key);
868        // undefined is the more appropriate value for JS, and this matches JQuery
869        return v === null ? undefined : v;
870    };
871    const getOpt = (element, key) => Optional.from(get$2(element, key));
872    const remove$2 = (element, key) => {
873        element.dom.removeAttribute(key);
874    };
875    const clone = (element) => foldl(element.dom.attributes, (acc, attr) => {
876        acc[attr.name] = attr.value;
877        return acc;
878    }, {});
879
880    const remove$1 = (element) => {
881        const dom = element.dom;
882        if (dom.parentNode !== null) {
883            dom.parentNode.removeChild(dom);
884        }
885    };
886    const unwrap = (wrapper) => {
887        const children = children$3(wrapper);
888        if (children.length > 0) {
889            after(wrapper, children);
890        }
891        remove$1(wrapper);
892    };
893
894    const fromDom = (nodes) => map(nodes, SugarElement.fromDom);
895
896    // some elements, such as mathml, don't have style attributes
897    // others, such as angular elements, have style attributes that aren't a CSSStyleDeclaration
898    const isSupported = (dom) => 
899    // eslint-disable-next-line @typescript-eslint/unbound-method
900    dom.style !== undefined && isFunction(dom.style.getPropertyValue);
901
902    // Node.contains() is very, very, very good performance
903    // http://jsperf.com/closest-vs-contains/5
904    const inBody = (element) => {
905        // Technically this is only required on IE, where contains() returns false for text nodes.
906        // But it's cheap enough to run everywhere and Sugar doesn't have platform detection (yet).
907        const dom = isText(element) ? element.dom.parentNode : element.dom;
908        // use ownerDocument.body to ensure this works inside iframes.
909        // Normally contains is bad because an element "contains" itself, but here we want that.
910        if (dom === undefined || dom === null || dom.ownerDocument === null) {
911            return false;
912        }
913        const doc = dom.ownerDocument;
914        return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
915    };
916
917    const internalSet = (dom, property, value) => {
918        // This is going to hurt. Apologies.
919        // JQuery coerces numbers to pixels for certain property names, and other times lets numbers through.
920        // we're going to be explicit; strings only.
921        if (!isString(value)) {
922            // eslint-disable-next-line no-console
923            console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
924            throw new Error('CSS value must be a string: ' + value);
925        }
926        // removed: support for dom().style[property] where prop is camel case instead of normal property name
927        if (isSupported(dom)) {
928            dom.style.setProperty(property, value);
929        }
930    };
931    const internalRemove = (dom, property) => {
932        /*
933         * IE9 and above - MDN doesn't have details, but here's a couple of random internet claims
934         *
935         * http://help.dottoro.com/ljopsjck.php
936         * http://stackoverflow.com/a/7901886/7546
937         */
938        if (isSupported(dom)) {
939            dom.style.removeProperty(property);
940        }
941    };
942    const set$1 = (element, property, value) => {
943        const dom = element.dom;
944        internalSet(dom, property, value);
945    };
946    /*
947     * NOTE: For certain properties, this returns the "used value" which is subtly different to the "computed value" (despite calling getComputedStyle).
948     * Blame CSS 2.0.
949     *
950     * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
951     */
952    const get$1 = (element, property) => {
953        const dom = element.dom;
954        /*
955         * IE9 and above per
956         * https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle
957         *
958         * Not in numerosity, because it doesn't memoize and looking this up dynamically in performance critical code would be horrendous.
959         *
960         * JQuery has some magic here for IE popups, but we don't really need that.
961         * It also uses element.ownerDocument.defaultView to handle iframes but that hasn't been required since FF 3.6.
962         */
963        const styles = window.getComputedStyle(dom);
964        const r = styles.getPropertyValue(property);
965        // jquery-ism: If r is an empty string, check that the element is not in a document. If it isn't, return the raw value.
966        // Turns out we do this a lot.
967        return (r === '' && !inBody(element)) ? getUnsafeProperty(dom, property) : r;
968    };
969    // removed: support for dom().style[property] where prop is camel case instead of normal property name
970    // empty string is what the browsers (IE11 and Chrome) return when the propertyValue doesn't exists.
971    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';
972    /*
973     * Gets the raw value from the style attribute. Useful for retrieving "used values" from the DOM:
974     * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
975     *
976     * Returns NONE if the property isn't set, or the value is an empty string.
977     */
978    const getRaw$1 = (element, property) => {
979        const dom = element.dom;
980        const raw = getUnsafeProperty(dom, property);
981        return Optional.from(raw).filter((r) => r.length > 0);
982    };
983    const remove = (element, property) => {
984        const dom = element.dom;
985        internalRemove(dom, property);
986        if (is$2(getOpt(element, 'style').map(trim), '')) {
987            // No more styles left, remove the style attribute as well
988            remove$2(element, 'style');
989        }
990    };
991
992    const Dimension = (name, getOffset) => {
993        const set = (element, h) => {
994            if (!isNumber(h) && !h.match(/^[0-9]+$/)) {
995                throw new Error(name + '.set accepts only positive integer values. Value was ' + h);
996            }
997            const dom = element.dom;
998            if (isSupported(dom)) {
999                dom.style[name] = h + 'px';
1000            }
1001        };
1002        /*
1003         * jQuery supports querying width and height on the document and window objects.
1004         *
1005         * TBIO doesn't do this, so the code is removed to save space, but left here just in case.
1006         */
1007        /*
1008        var getDocumentWidth = (element) => {
1009          var dom = element.dom;
1010          if (Node.isDocument(element)) {
1011            var body = dom.body;
1012            var doc = dom.documentElement;
1013            return Math.max(
1014              body.scrollHeight,
1015              doc.scrollHeight,
1016              body.offsetHeight,
1017              doc.offsetHeight,
1018              doc.clientHeight
1019            );
1020          }
1021        };
1022      
1023        var getWindowWidth = (element) => {
1024          var dom = element.dom;
1025          if (dom.window === dom) {
1026            // There is no offsetHeight on a window, so use the clientHeight of the document
1027            return dom.document.documentElement.clientHeight;
1028          }
1029        };
1030      */
1031        const get = (element) => {
1032            const r = getOffset(element);
1033            // zero or null means non-standard or disconnected, fall back to CSS
1034            if (r <= 0 || r === null) {
1035                const css = get$1(element, name);
1036                // ugh this feels dirty, but it saves cycles
1037                return parseFloat(css) || 0;
1038            }
1039            return r;
1040        };
1041        // in jQuery, getOuter replicates (or uses) box-sizing: border-box calculations
1042        // although these calculations only seem relevant for quirks mode, and edge cases TBIO doesn't rely on
1043        const getOuter = get;
1044        const aggregate = (element, properties) => foldl(properties, (acc, property) => {
1045            const val = get$1(element, property);
1046            const value = val === undefined ? 0 : parseInt(val, 10);
1047            return isNaN(value) ? acc : acc + value;
1048        }, 0);
1049        const max = (element, value, properties) => {
1050            const cumulativeInclusions = aggregate(element, properties);
1051            // if max-height is 100px and your cumulativeInclusions is 150px, there is no way max-height can be 100px, so we return 0.
1052            const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;
1053            return absoluteMax;
1054        };
1055        return {
1056            set,
1057            get,
1058            getOuter,
1059            aggregate,
1060            max
1061        };
1062    };
1063
1064    const toNumber = (px, fallback) => toFloat(px).getOr(fallback);
1065    const getProp = (element, name, fallback) => toNumber(get$1(element, name), fallback);
1066    const calcContentBoxSize = (element, size, upper, lower) => {
1067        const paddingUpper = getProp(element, `padding-${upper}`, 0);
1068        const paddingLower = getProp(element, `padding-${lower}`, 0);
1069        const borderUpper = getProp(element, `border-${upper}-width`, 0);
1070        const borderLower = getProp(element, `border-${lower}-width`, 0);
1071        return size - paddingUpper - paddingLower - borderUpper - borderLower;
1072    };
1073    const getCalculatedWidth = (element, boxSizing) => {
1074        const dom = element.dom;
1075        const width = dom.getBoundingClientRect().width || dom.offsetWidth;
1076        return boxSizing === 'border-box' ? width : calcContentBoxSize(element, width, 'left', 'right');
1077    };
1078    const getInnerWidth = (element) => getCalculatedWidth(element, 'content-box');
1079
1080    Dimension('width', (element) => 
1081    // IMO passing this function is better than using dom['offset' + 'width']
1082    element.dom.offsetWidth);
1083    Dimension('width', (element) => {
1084        const dom = element.dom;
1085        return inBody(element) ? dom.getBoundingClientRect().width : dom.offsetWidth;
1086    });
1087    const getInner = getInnerWidth;
1088
1089    const NodeValue = (is, name) => {
1090        const get = (element) => {
1091            if (!is(element)) {
1092                throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
1093            }
1094            return getOption(element).getOr('');
1095        };
1096        const getOption = (element) => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
1097        const set = (element, value) => {
1098            if (!is(element)) {
1099                throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
1100            }
1101            element.dom.nodeValue = value;
1102        };
1103        return {
1104            get,
1105            getOption,
1106            set
1107        };
1108    };
1109
1110    const api = NodeValue(isText, 'text');
1111    const get = (element) => api.get(element);
1112    const set = (element, value) => api.set(element, value);
1113
1114    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
1115        if (is(scope, a)) {
1116            return Optional.some(scope);
1117        }
1118        else if (isFunction(isRoot) && isRoot(scope)) {
1119            return Optional.none();
1120        }
1121        else {
1122            return ancestor(scope, a, isRoot);
1123        }
1124    };
1125
1126    const ancestor$1 = (scope, predicate, isRoot) => {
1127        let element = scope.dom;
1128        const stop = isFunction(isRoot) ? isRoot : never;
1129        while (element.parentNode) {
1130            element = element.parentNode;
1131            const el = SugarElement.fromDom(element);
1132            if (predicate(el)) {
1133                return Optional.some(el);
1134            }
1135            else if (stop(el)) {
1136                break;
1137            }
1138        }
1139        return Optional.none();
1140    };
1141    const closest$2 = (scope, predicate, isRoot) => {
1142        // This is required to avoid ClosestOrAncestor passing the predicate to itself
1143        const is = (s, test) => test(s);
1144        return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);
1145    };
1146    const child$2 = (scope, predicate) => {
1147        const pred = (node) => predicate(SugarElement.fromDom(node));
1148        const result = find(scope.dom.childNodes, pred);
1149        return result.map(SugarElement.fromDom);
1150    };
1151
1152    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, (e) => is$1(e, selector), isRoot);
1153    const child$1 = (scope, selector) => child$2(scope, (e) => is$1(e, selector));
1154    const descendant = (scope, selector) => one(selector, scope);
1155    // Returns Some(closest ancestor element (sugared)) matching 'selector' up to isRoot, or None() otherwise
1156    const closest$1 = (scope, selector, isRoot) => {
1157        const is = (element, selector) => is$1(element, selector);
1158        return ClosestOrAncestor(is, ancestor, scope, selector, isRoot);
1159    };
1160
1161    const closest = (target) => closest$1(target, '[contenteditable]');
1162    const isEditable = (element, assumeEditable = false) => {
1163        if (inBody(element)) {
1164            return element.dom.isContentEditable;
1165        }
1166        else {
1167            // Find the closest contenteditable element and check if it's editable
1168            return closest(element).fold(constant(assumeEditable), (editable) => getRaw(editable) === 'true');
1169        }
1170    };
1171    const getRaw = (element) => element.dom.contentEditable;
1172
1173    const children$2 = (scope, predicate) => filter$1(children$3(scope), predicate);
1174    const descendants$1 = (scope, predicate) => {
1175        let result = [];
1176        // Recurse.toArray() might help here
1177        each$1(children$3(scope), (x) => {
1178            if (predicate(x)) {
1179                result = result.concat([x]);
1180            }
1181            result = result.concat(descendants$1(x, predicate));
1182        });
1183        return result;
1184    };
1185
1186    const children$1 = (scope, selector) => 
1187    // It may surprise you to learn this is exactly what JQuery does
1188    // TODO: Avoid all the wrapping and unwrapping
1189    children$2(scope, (e) => is$1(e, selector));
1190    const descendants = (scope, selector) => all$1(selector, scope);
1191
1192    const child = (scope, selector) => child$1(scope, selector).isSome();
1193
1194    /*
1195     NOTE: This file is partially duplicated in the following locations:
1196      - models/dom/table/core/TableUtils.ts
1197      - advtable
1198     Make sure that if making changes to this file, the other files are updated as well
1199     */
1200    const getNodeName = (elm) => elm.nodeName.toLowerCase();
1201    const getBody = (editor) => SugarElement.fromDom(editor.getBody());
1202    const getIsRoot = (editor) => (element) => eq(element, getBody(editor));
1203    const removePxSuffix = (size) => size ? size.replace(/px$/, '') : '';
1204    const addPxSuffix = (size) => /^\d+(\.\d+)?$/.test(size) ? size + 'px' : size;
1205    const getSelectionStart = (editor) => SugarElement.fromDom(editor.selection.getStart());
1206    const getSelectionEnd = (editor) => SugarElement.fromDom(editor.selection.getEnd());
1207    const isInEditableContext = (cell) => closest$2(cell, isTag('table')).forall(isEditable);
1208
1209    const validSectionList = ['tfoot', 'thead', 'tbody', 'colgroup'];
1210    const isValidSection = (parentName) => contains(validSectionList, parentName);
1211    const grid = (rows, columns) => ({
1212        rows,
1213        columns
1214    });
1215    const detail = (element, rowspan, colspan) => ({
1216        element,
1217        rowspan,
1218        colspan
1219    });
1220    const extended = (element, rowspan, colspan, row, column, isLocked) => ({
1221        element,
1222        rowspan,
1223        colspan,
1224        row,
1225        column,
1226        isLocked
1227    });
1228    const rowdetail = (element, cells, section) => ({
1229        element,
1230        cells,
1231        section
1232    });
1233    const bounds = (startRow, startCol, finishRow, finishCol) => ({
1234        startRow,
1235        startCol,
1236        finishRow,
1237        finishCol
1238    });
1239    const columnext = (element, colspan, column) => ({
1240        element,
1241        colspan,
1242        column
1243    });
1244    const colgroup = (element, columns) => ({
1245        element,
1246        columns
1247    });
1248
1249    const getAttrValue = (cell, name, fallback = 0) => getOpt(cell, name).map((value) => parseInt(value, 10)).getOr(fallback);
1250
1251    const firstLayer = (scope, selector) => {
1252        return filterFirstLayer(scope, selector, always);
1253    };
1254    const filterFirstLayer = (scope, selector, predicate) => {
1255        return bind(children$3(scope), (x) => {
1256            if (is$1(x, selector)) {
1257                return predicate(x) ? [x] : [];
1258            }
1259            else {
1260                return filterFirstLayer(x, selector, predicate);
1261            }
1262        });
1263    };
1264
1265    // lookup inside this table
1266    const lookup = (tags, element, isRoot = never) => {
1267        // If the element we're inspecting is the root, we definitely don't want it.
1268        if (isRoot(element)) {
1269            return Optional.none();
1270        }
1271        // This looks a lot like SelectorFind.closest, with one big exception - the isRoot check.
1272        // The code here will look for parents if passed a table, SelectorFind.closest with that specific isRoot check won't.
1273        if (contains(tags, name(element))) {
1274            return Optional.some(element);
1275        }
1276        const isRootOrUpperTable = (elm) => is$1(elm, 'table') || isRoot(elm);
1277        return ancestor(element, tags.join(','), isRootOrUpperTable);
1278    };
1279    /*
1280     * Identify the optional cell that element represents.
1281     */
1282    const cell = (element, isRoot) => lookup(['td', 'th'], element, isRoot);
1283    const cells = (ancestor) => firstLayer(ancestor, 'th,td');
1284    const columns = (ancestor) => {
1285        if (is$1(ancestor, 'colgroup')) {
1286            return children$1(ancestor, 'col');
1287        }
1288        else {
1289            return bind(columnGroups(ancestor), (columnGroup) => children$1(columnGroup, 'col'));
1290        }
1291    };
1292    const table = (element, isRoot) => closest$1(element, 'table', isRoot);
1293    const rows = (ancestor) => firstLayer(ancestor, 'tr');
1294    const columnGroups = (ancestor) => table(ancestor).fold(constant([]), (table) => children$1(table, 'colgroup'));
1295
1296    const isHeaderCell = isTag('th');
1297    const getRowHeaderType = (isHeaderRow, isHeaderCells) => {
1298        if (isHeaderRow && isHeaderCells) {
1299            return 'sectionCells';
1300        }
1301        else if (isHeaderRow) {
1302            return 'section';
1303        }
1304        else {
1305            return 'cells';
1306        }
1307    };
1308    const getRowType$1 = (row) => {
1309        // Header rows can use a combination of theads and ths - want to detect the different combinations
1310        const isHeaderRow = row.section === 'thead';
1311        const isHeaderCells = is$2(findCommonCellType(row.cells), 'th');
1312        if (row.section === 'tfoot') {
1313            return { type: 'footer' };
1314        }
1315        else if (isHeaderRow || isHeaderCells) {
1316            return { type: 'header', subType: getRowHeaderType(isHeaderRow, isHeaderCells) };
1317        }
1318        else {
1319            return { type: 'body' };
1320        }
1321    };
1322    const findCommonCellType = (cells) => {
1323        const headerCells = filter$1(cells, (cell) => isHeaderCell(cell.element));
1324        if (headerCells.length === 0) {
1325            return Optional.some('td');
1326        }
1327        else if (headerCells.length === cells.length) {
1328            return Optional.some('th');
1329        }
1330        else {
1331            return Optional.none();
1332        }
1333    };
1334    const findCommonRowType = (rows) => {
1335        const rowTypes = map(rows, (row) => getRowType$1(row).type);
1336        const hasHeader = contains(rowTypes, 'header');
1337        const hasFooter = contains(rowTypes, 'footer');
1338        if (!hasHeader && !hasFooter) {
1339            return Optional.some('body');
1340        }
1341        else {
1342            const hasBody = contains(rowTypes, 'body');
1343            if (hasHeader && !hasBody && !hasFooter) {
1344                return Optional.some('header');
1345            }
1346            else if (!hasHeader && !hasBody && hasFooter) {
1347                return Optional.some('footer');
1348            }
1349            else {
1350                return Optional.none();
1351            }
1352        }
1353    };
1354
1355    const fromRowsOrColGroups = (elems, getSection) => map(elems, (row) => {
1356        if (name(row) === 'colgroup') {
1357            const cells = map(columns(row), (column) => {
1358                const colspan = getAttrValue(column, 'span', 1);
1359                return detail(column, 1, colspan);
1360            });
1361            return rowdetail(row, cells, 'colgroup');
1362        }
1363        else {
1364            const cells$1 = map(cells(row), (cell) => {
1365                const rowspan = getAttrValue(cell, 'rowspan', 1);
1366                const colspan = getAttrValue(cell, 'colspan', 1);
1367                return detail(cell, rowspan, colspan);
1368            });
1369            return rowdetail(row, cells$1, getSection(row));
1370        }
1371    });
1372    const getParentSection = (group) => parent(group).map((parent) => {
1373        const parentName = name(parent);
1374        return isValidSection(parentName) ? parentName : 'tbody';
1375    }).getOr('tbody');
1376    /*
1377     * Takes a DOM table and returns a list of list of:
1378       element: row element
1379       cells: (id, rowspan, colspan) structs
1380     */
1381    const fromTable$1 = (table) => {
1382        const rows$1 = rows(table);
1383        const columnGroups$1 = columnGroups(table);
1384        const elems = [...columnGroups$1, ...rows$1];
1385        return fromRowsOrColGroups(elems, getParentSection);
1386    };
1387
1388    const LOCKED_COL_ATTR = 'data-snooker-locked-cols';
1389    const getLockedColumnsFromTable = (table) => getOpt(table, LOCKED_COL_ATTR)
1390        .bind((lockedColStr) => Optional.from(lockedColStr.match(/\d+/g)))
1391        .map((lockedCols) => mapToObject(lockedCols, always));
1392
1393    const key = (row, column) => {
1394        return row + ',' + column;
1395    };
1396    const getAt = (warehouse, row, column) => Optional.from(warehouse.access[key(row, column)]);
1397    const findItem = (warehouse, item, comparator) => {
1398        const filtered = filterItems(warehouse, (detail) => {
1399            return comparator(item, detail.element);
1400        });
1401        return filtered.length > 0 ? Optional.some(filtered[0]) : Optional.none();
1402    };
1403    const filterItems = (warehouse, predicate) => {
1404        const all = bind(warehouse.all, (r) => {
1405            return r.cells;
1406        });
1407        return filter$1(all, predicate);
1408    };
1409    const generateColumns = (rowData) => {
1410        const columnsGroup = {};
1411        let index = 0;
1412        each$1(rowData.cells, (column) => {
1413            const colspan = column.colspan;
1414            range(colspan, (columnIndex) => {
1415                const colIndex = index + columnIndex;
1416                columnsGroup[colIndex] = columnext(column.element, colspan, colIndex);
1417            });
1418            index += colspan;
1419        });
1420        return columnsGroup;
1421    };
1422    /*
1423     * From a list of list of Detail, generate three pieces of information:
1424     *  1. the grid size
1425     *  2. a data structure which can efficiently identify which cell is in which row,column position
1426     *  3. a list of all cells in order left-to-right, top-to-bottom
1427     */
1428    const generate = (list) => {
1429        // list is an array of objects, made by cells and elements
1430        // elements: is the TR
1431        // cells: is an array of objects representing the cells in the row.
1432        //        It is made of:
1433        //          colspan (merge cell)
1434        //          element
1435        //          rowspan (merge cols)
1436        const access = {};
1437        const cells = [];
1438        const tableOpt = head(list).map((rowData) => rowData.element).bind(table);
1439        const lockedColumns = tableOpt.bind(getLockedColumnsFromTable).getOr({});
1440        let maxRows = 0;
1441        let maxColumns = 0;
1442        let rowCount = 0;
1443        const { pass: colgroupRows, fail: rows } = partition(list, (rowData) => rowData.section === 'colgroup');
1444        // Handle rows first
1445        each$1(rows, (rowData) => {
1446            const currentRow = [];
1447            each$1(rowData.cells, (rowCell) => {
1448                let start = 0;
1449                // If this spot has been taken by a previous rowspan, skip it.
1450                while (access[key(rowCount, start)] !== undefined) {
1451                    start++;
1452                }
1453                const isLocked = hasNonNullableKey(lockedColumns, start.toString());
1454                const current = extended(rowCell.element, rowCell.rowspan, rowCell.colspan, rowCount, start, isLocked);
1455                // Occupy all the (row, column) positions that this cell spans for.
1456                for (let occupiedColumnPosition = 0; occupiedColumnPosition < rowCell.colspan; occupiedColumnPosition++) {
1457                    for (let occupiedRowPosition = 0; occupiedRowPosition < rowCell.rowspan; occupiedRowPosition++) {
1458                        const rowPosition = rowCount + occupiedRowPosition;
1459                        const columnPosition = start + occupiedColumnPosition;
1460                        const newpos = key(rowPosition, columnPosition);
1461                        access[newpos] = current;
1462                        maxColumns = Math.max(maxColumns, columnPosition + 1);
1463                    }
1464                }
1465                currentRow.push(current);
1466            });
1467            maxRows++;
1468            cells.push(rowdetail(rowData.element, currentRow, rowData.section));
1469            rowCount++;
1470        });
1471        // Handle colgroups
1472        // Note: Currently only a single colgroup is supported so just use the last one
1473        const { columns, colgroups } = last(colgroupRows).map((rowData) => {
1474            const columns = generateColumns(rowData);
1475            const colgroup$1 = colgroup(rowData.element, values(columns));
1476            return {
1477                colgroups: [colgroup$1],
1478                columns
1479            };
1480        }).getOrThunk(() => ({
1481            colgroups: [],
1482            columns: {}
1483        }));
1484        const grid$1 = grid(maxRows, maxColumns);
1485        return {
1486            grid: grid$1,
1487            access,
1488            all: cells,
1489            columns,
1490            colgroups
1491        };
1492    };
1493    const fromTable = (table) => {
1494        const list = fromTable$1(table);
1495        return generate(list);
1496    };
1497    const justCells = (warehouse) => bind(warehouse.all, (w) => w.cells);
1498    const justColumns = (warehouse) => values(warehouse.columns);
1499    const hasColumns = (warehouse) => keys(warehouse.columns).length > 0;
1500    const getColumnAt = (warehouse, columnIndex) => Optional.from(warehouse.columns[columnIndex]);
1501    const Warehouse = {
1502        fromTable,
1503        generate,
1504        getAt,
1505        findItem,
1506        filterItems,
1507        justCells,
1508        justColumns,
1509        hasColumns,
1510        getColumnAt
1511    };
1512
1513    const findInWarehouse = (warehouse, element) => findMap(warehouse.all, (r) =>
1513 find(r.cells, (e) => eq(element, e.element)));
1514    const extractCells = (warehouse, target, predicate) => {
1515        const details = map(target.selection, (cell$1) => {
1516            return cell(cell$1)
1517                .bind((lc) => findInWarehouse(warehouse, lc))
1518                .filter(predicate);
1519        });
1520        const cells = cat(details);
1521        return someIf(cells.length > 0, cells);
1522    };
1523    const onMergable = (_warehouse, target) => target.mergable;
1524    const onUnmergable = (_warehouse, target) => target.unmergable;
1525    const onCells = (warehouse, target) => extractCells(warehouse, target, always);
1526    const isUnlockedTableCell = (warehouse, cell) => findInWarehouse(warehouse, cell).exists((detail) => !detail.isLocked);
1527    const allUnlocked = (warehouse, cells) => forall(cells, (cell) => isUnlockedTableCell(warehouse, cell));
1528    // If any locked columns are present in the selection, then don't want to be able to merge
1529    const onUnlockedMergable = (warehouse, target) => onMergable(warehouse, target).filter((mergeable) => allUnlocked(warehouse, mergeable.cells));
1530    // If any locked columns are present in the selection, then don't want to be able to unmerge
1531    const onUnlockedUnmergable = (warehouse, target) => onUnmergable(warehouse, target).filter((cells) => allUnlocked(warehouse, cells));
1532
1533    const isCol = isTag('col');
1534    const isColgroup = isTag('colgroup');
1535    const isRow = (element) => name(element) === 'tr' || isColgroup(element);
1536    const elementToData = (element) => {
1537        const colspan = getAttrValue(element, 'colspan', 1);
1538        const rowspan = getAttrValue(element, 'rowspan', 1);
1539        return {
1540            element,
1541            colspan,
1542            rowspan
1543        };
1544    };
1545    // note that `toData` seems to be only for testing
1546    const modification = (generators, toData = elementToData) => {
1547        const nuCell = (data) => isCol(data.element) ? generators.col(data) : generators.cell(data);
1548        const nuRow = (data) => isColgroup(data.element) ? generators.colgroup(data) : generators.row(data);
1549        const add = (element) => {
1550            if (isRow(element)) {
1551                return nuRow({ element });
1552            }
1553            else {
1554                const cell = element;
1555                const replacement = nuCell(toData(cell));
1556                recent = Optional.some({ item: cell, replacement });
1557                return replacement;
1558            }
1559        };
1560        let recent = Optional.none();
1561        const getOrInit = (element, comparator) => {
1562            return recent.fold(() => {
1563                return add(element);
1564            }, (p) => {
1565                return comparator(element, p.item) ? p.replacement : add(element);
1566            });
1567        };
1568        return {
1569            getOrInit
1570        };
1571    };
1572    const transform = (tag) => {
1573        return (generators) => {
1574            const list = [];
1575            const find$1 = (element, comparator) => {
1576                return find(list, (x) => {
1577                    return comparator(x.item, element);
1578                });
1579            };
1580            const makeNew = (element) => {
1581                // Ensure scope is never set on a td element as it's a deprecated attribute
1582                const attrs = tag === 'td' ? { scope: null } : {};
1583                const cell = generators.replace(element, tag, attrs);
1584                list.push({
1585                    item: element,
1586                    sub: cell
1587                });
1588                return cell;
1589            };
1590            const replaceOrInit = (element, comparator) => {
1591                if (isRow(element) || isCol(element)) {
1592                    return element;
1593                }
1594                else {
1595                    const cell = element;
1596                    return find$1(cell, comparator).fold(() => {
1597                        return makeNew(cell);
1598                    }, (p) => {
1599                        return comparator(element, p.item) ? p.sub : makeNew(cell);
1600                    });
1601                }
1602            };
1603            return {
1604                replaceOrInit
1605            };
1606        };
1607    };
1608    const getScopeAttribute = (cell) => getOpt(cell, 'scope').map(
1609    // Attribute can be col, colgroup, row, and rowgroup.
1610    // As col and colgroup are to be treated as if they are the same, lob off everything after the first three characters and there is no difference.
1611    (attribute) => attribute.substr(0, 3));
1612    const merging = (generators) => {
1613        const unmerge = (cell) => {
1614            const scope = getScopeAttribute(cell);
1615            scope.each((attribute) => set$2(cell, 'scope', attribute));
1616            return () => {
1617                const raw = generators.cell({
1618                    element: cell,
1619                    colspan: 1,
1620                    rowspan: 1
1621                });
1622                // Remove any width calculations because they are no longer relevant.
1623                remove(raw, 'width');
1624                remove(cell, 'width');
1625                scope.each((attribute) => set$2(raw, 'scope', attribute));
1626                return raw;
1627            };
1628        };
1629        const merge = (cells) => {
1630            const getScopeProperty = () => {
1631                const stringAttributes = cat(map(cells, getScopeAttribute));
1632                if (stringAttributes.length === 0) {
1633                    return Optional.none();
1634                }
1635                else {
1636                    const baseScope = stringAttributes[0];
1637                    const scopes = ['row', 'col'];
1638                    const isMixed = exists(stringAttributes, (attribute) => {
1639                        return attribute !== baseScope && contains(scopes, attribute);
1640                    });
1641                    return isMixed ? Optional.none() : Optional.from(baseScope);
1642                }
1643            };
1644            remove(cells[0], 'width');
1645            getScopeProperty().fold(() => remove$2(cells[0], 'scope'), (attribute) => set$2(cells[0], 'scope', attribute + 'group'));
1646            return constant(cells[0]);
1647        };
1648        return {
1649            unmerge,
1650            merge
1651        };
1652    };
1653    const Generators = {
1654        modification,
1655        transform,
1656        merging
1657    };
1658
1659    var TagBoundaries = [
1660        'body',
1661        'p',
1662        'div',
1663        'article',
1664        'aside',
1665        'figcaption',
1666        'figure',
1667        'footer',
1668        'header',
1669        'nav',
1670        'section',
1671        'ol',
1672        'ul',
1673        'li',
1674        'table',
1675        'thead',
1676        'tbody',
1677        'tfoot',
1678        'caption',
1679        'tr',
1680        'td',
1681        'th',
1682        'h1',
1683        'h2',
1684        'h3',
1685        'h4',
1686        'h5',
1687        'h6',
1688        'blockquote',
1689        'pre',
1690        'address'
1691    ];
1692
1693    var DomUniverse = () => {
1694        const clone$1 = (element) => {
1695            return SugarElement.fromDom(element.dom.cloneNode(false));
1696        };
1697        const document = (element) => documentOrOwner(element).dom;
1698        const isBoundary = (element) => {
1699            if (!isElement(element)) {
1700                return false;
vendor: 4,334 bytes, lines 1701-1830
1701            }
1702            if (name(element) === 'body') {
1703                return true;
1704            }
1705            return contains(TagBoundaries, name(element));
1706        };
1707        const isEmptyTag = (element) => {
1708            if (!isElement(element)) {
1709                return false;
1710            }
1711            return contains(['br', 'img', 'hr', 'input'], name(element));
1712        };
1713        const isNonEditable = (element) => isElement(element) && get$2(element, 'contenteditable') === 'false';
1714        const comparePosition = (element, other) => {
1715            return element.dom.compareDocumentPosition(other.dom);
1716        };
1717        const copyAttributesTo = (source, destination) => {
1718            const as = clone(source);
1719            setAll(destination, as);
1720        };
1721        const isSpecial = (element) => {
1722            const tag = name(element);
1723            return contains([
1724                'script', 'noscript', 'iframe', 'noframes', 'noembed', 'title', 'style', 'textarea', 'xmp'
1725            ], tag);
1726        };
1727        const getLanguage = (element) => isElement(element) ? getOpt(element, 'lang') : Optional.none();
1728        return {
1729            up: constant({
1730                selector: ancestor,
1731                closest: closest$1,
1732                predicate: ancestor$1,
1733                all: parents
1734            }),
1735            down: constant({
1736                selector: descendants,
1737                predicate: descendants$1
1738            }),
1739            styles: constant({
1740                get: get$1,
1741                getRaw: getRaw$1,
1742                set: set$1,
1743                remove: remove
1744            }),
1745            attrs: constant({
1746                get: get$2,
1747                set: set$2,
1748                remove: remove$2,
1749                copyTo: copyAttributesTo
1750            }),
1751            insert: constant({
1752                before: before,
1753                after: after$1,
1754                afterAll: after,
1755                append: append$1,
1756                appendAll: append,
1757                prepend: prepend,
1758                wrap: wrap
1759            }),
1760            remove: constant({
1761                unwrap: unwrap,
1762                remove: remove$1
1763            }),
1764            create: constant({
1765                nu: SugarElement.fromTag,
1766                clone: clone$1,
1767                text: SugarElement.fromText
1768            }),
1769            query: constant({
1770                comparePosition,
1771                prevSibling: prevSibling,
1772                nextSibling: nextSibling
1773            }),
1774            property: constant({
1775                children: children$3,
1776                name: name,
1777                parent: parent,
1778                document,
1779                isText: isText,
1780                isComment: isComment,
1781                isElement: isElement,
1782                isSpecial,
1783                getLanguage,
1784                getText: get,
1785                setText: set,
1786                isBoundary,
1787                isEmptyTag,
1788                isNonEditable
1789            }),
1790            eq: eq,
1791            is: is
1792        };
1793    };
1794
1795    const leftRight = (left, right) => ({
1796        left,
1797        right
1798    });
1799    const brokenPath = (first, second, splits) => ({
1800        first,
1801        second,
1802        splits
1803    });
1804    const bisect = (universe, parent, child) => {
1805        const children = universe.property().children(parent);
1806        const index = findIndex(children, curry(universe.eq, child));
1807        return index.map((ind) => {
1808            return {
1809                before: children.slice(0, ind),
1810                after: children.slice(ind + 1)
1811            };
1812        });
1813    };
1814    /**
1815     * Clone parent to the RIGHT and move everything after child in the parent element into
1816     * a clone of the parent (placed after parent).
1817     */
1818    const breakToRight = (universe, parent, child) => {
1819        return bisect(universe, parent, child).map((parts) => {
1820            const second = universe.create().clone(parent);
1821            universe.insert().appendAll(second, parts.after);
1822            universe.insert().after(parent, second);
1823            return leftRight(parent, second);
1824        });
1825    };
1826    /**
1827     * Clone parent to the LEFT and move everything before and including child into
1828     * the a clone of the parent (placed before parent)
1829     */
1830    const breakToLeft = (universe, parent, child) => {
1831        return bisect(universe, parent, child).map((parts) => {
1832            const prior = universe.create().clone(parent);
1833            universe.insert().appendAll(prior, parts.before.concat([child]));
1834            universe.insert().appendAll(parent, parts.after);
1835            universe.insert().before(parent, prior);
1836            return leftRight(prior, parent);
1837        });
1838    };
1839    /*
1840     * Using the breaker, break from the child up to the top element defined by the predicate.
1841     * It returns three values:
1842     *   first: the top level element that completed the break
1843     *   second: the optional element representing second part of the top-level split if the breaking completed successfully to the top
1844     *   splits: a list of (Element, Element) pairs that represent the splits that have occurred on the way to the top.
1845     */
1846    const breakPath = (universe, item, isTop, breaker) => {
1847        const next = (child, group, splits) => {
1848            const fallback = brokenPath(child, Optional.none(), splits);
1849            // Found the top, so stop.
1850            if (isTop(child)) {
1851                return brokenPath(child, group, splits);
1852            }
1853            else {
1854                // Split the child at parent, and keep going
1855                return universe.property().parent(child).bind((parent) => {
1856                    return breaker(universe, parent, child).map((breakage) => {
1857                        const extra = [{ first: breakage.left, second: breakage.right }];
1858                        // Our isTop is based on the left-side parent, so keep it regardless of split.
1859                        const nextChild = isTop(parent) ? parent : breakage.left;
1860                        return next(nextChild, Optional.some(breakage.right), splits.concat(extra));
1861                    });
1862                }).getOr(fallback);
1863            }
1864        };
1865        return next(item, Optional.none(), []);
1866    };
1867
1868    const all = (universe, look, elements, f) => {
1869        const head = elements[0];
1870        const tail = elements.slice(1);
1871        return f(universe, look, head, tail);
1872    };
1873    /**
1874     * Check if look returns the same element for all elements, and return it if it exists.
1875     */
1876    const oneAll = (universe, look, elements) => {
1877        return elements.length > 0 ?
1878            all(universe, look, elements, unsafeOne) :
1879            Optional.none();
1880    };
1881    const unsafeOne = (universe, look, head, tail) => {
1882        const start = look(universe, head);
1883        return foldr(tail, (b, a) => {
1884            const current = look(universe, a);
1885            return commonElement(universe, b, current);
1886        }, start);
1887    };
1888    const commonElement = (universe, start, end) => {
1889        return start.bind((s) => {
1890            return end.filter(curry(universe.eq, s));
1891        });
1892    };
1893
1894    const sharedOne$1 = oneAll;
1895    breakToLeft;
1896    breakToRight;
1897    breakPath;
1898
1899    const universe = DomUniverse();
1900    const sharedOne = (look, elements) => {
1901        return sharedOne$1(universe, (_universe, element) => {
1902            return look(element);
1903        }, elements);
1904    };
1905
1906    const opGetRowsType = (table, target) => {
1907        const house = Warehouse.fromTable(table);
1908        const details = onCells(house, target);
1909        return details.bind((selectedCells) => {
1910            const lastSelectedCell = selectedCells[selectedCells.length - 1];
1911            const minRowRange = selectedCells[0].row;
1912            const maxRowRange = lastSelectedCell.row + lastSelectedCell.rowspan;
1913            const selectedRows = house.all.slice(minRowRange, maxRowRange);
1914            return findCommonRowType(selectedRows);
1915        }).getOr('');
1916    };
1917    Generators.transform('th');
1918    Generators.transform('td');
1919    const getRowsType = opGetRowsType;
1920
1921    // Note, something is *within* if it is completely contained within the bounds.
1922    const isWithin = (bounds, detail) => {
1923        return (detail.column >= bounds.startCol &&
1924            (detail.column + detail.colspan - 1) <= bounds.finishCol &&
1925            detail.row >= bounds.startRow &&
1926            (detail.row + detail.rowspan - 1) <= bounds.finishRow);
1927    };
1928    const isRectangular = (warehouse, bounds) => {
1929        let isRect = true;
1930        const detailIsWithin = curry(isWithin, bounds);
1931        for (let i = bounds.startRow; i <= bounds.finishRow; i++) {
1932            for (let j = bounds.startCol; j <= bounds.finishCol; j++) {
1933                isRect = isRect && Warehouse.getAt(warehouse, i, j).exists(detailIsWithin);
1934            }
1935        }
1936        return isRect ? Optional.some(bounds) : Optional.none();
1937    };
1938
1939    const getBounds = (detailA, detailB) => {
1940        return bounds(Math.min(detailA.row, detailB.row), Math.min(detailA.column, detailB.column), Math.max(detailA.row + detailA.rowspan - 1, detailB.row + detailB.rowspan - 1), Math.max(detailA.column + detailA.colspan - 1, detailB.column + detailB.colspan - 1));
1941    };
1942    const getAnyBox = (warehouse, startCell, finishCell) => {
1943        const startCoords = Warehouse.findItem(warehouse, startCell, eq);
1944        const finishCoords = Warehouse.findItem(warehouse, finishCell, eq);
1945        return startCoords.bind((sc) => {
1946            return finishCoords.map((fc) => {
1947                return getBounds(sc, fc);
1948            });
1949        });
1950    };
1951    const getBox$1 = (warehouse, startCell, finishCell) => {
1952        return getAnyBox(warehouse, startCell, finishCell).bind((bounds) => {
1953            return isRectangular(warehouse, bounds);
1954        });
1955    };
1956
1957    const getBox = (table, first, last) => {
1958        const warehouse = getWarehouse(table);
1959        return getBox$1(warehouse, first, last);
1960    };
1961    // Private method ... keep warehouse in snooker, please.
1962    const getWarehouse = Warehouse.fromTable;
1963
1964    var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');
1965
1966    const getTDTHOverallStyle = (dom, elm, name) => {
1967        const cells = dom.select('td,th', elm);
1968        let firstChildStyle;
1969        for (let i = 0; i < cells.length; i++) {
1970            const currentStyle = dom.getStyle(cells[i], name);
1971            if (isUndefined(firstChildStyle)) {
1972                firstChildStyle = currentStyle;
1973            }
1974            if (firstChildStyle !== currentStyle) {
1975                return '';
1976            }
1977        }
1978        return firstChildStyle;
1979    };
1980    const setAlign = (editor, elm, name) => {
1981        // Alignment formats may not use the same styles so ensure to remove any existing horizontal alignment format first
1982        global$2.each('left center right'.split(' '), (align) => {
1983            if (align !== name) {
1984                editor.formatter.remove('align' + align, {}, elm);
1985            }
1986        });
1987        if (name) {
1988            editor.formatter.apply('align' + name, {}, elm);
1989        }
1990    };
1991    const setVAlign = (editor, elm, name) => {
1992        // Alignment formats may not use the same styles so ensure to remove any existing vertical alignment format first
1993        global$2.each('top middle bottom'.split(' '), (align) => {
1994            if (align !== name) {
1995                editor.formatter.remove('valign' + align, {}, elm);
1996            }
1997        });
1998        if (name) {
1999            editor.formatter.apply('valign' + name, {}, elm);
2000        }
2001    };
2002
2003    /*
2004     NOTE: This file is duplicated in the following locations:
2005      - core/api/TableEvents.ts
2006      - models/dom/table/api/Events.ts
2007      - advtable
2008     Make sure that if making changes to this file, the other files are updated as well
2009     */
2010    const fireTableModified = (editor, table, data) => {
2011        editor.dispatch('TableModified', { ...data, table });
2012    };
2013
2014    var global$1 = tinymce.util.Tools.resolve('tinymce.Env');
2015
2016    const defaultTableToolbar = 'tableprops tabledelete | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledelete
2016col';
2017    const defaultCellBorderWidths = range(5, (i) => {
2018        const size = `${i + 1}px`;
2019        return { title: size, value: size };
2020    });
2021    const defaultCellBorderStyles = map(['Solid', 'Dotted', 'Dashed', 'Double', 'Groove', 'Ridge', 'Inset', 'Outset', 'None', 'Hidden'], (type) => {
2022        return { title: type, value: type.toLowerCase() };
2023    });
2024    // Note: This is also contained in the core Options.ts file
2025    const defaultWidth = '100%';
2026    const getPixelForcedWidth = (editor) => {
2027        var _a;
2028        // Determine the inner size of the parent block element where the table will be inserted
2029        const dom = editor.dom;
2030        const parentBlock = (_a = dom.getParent(editor.selection.getStart(), dom.isBlock)) !== null && _a !== void 0 ? _a : editor.getBody();
2031        return getInner(SugarElement.fromDom(parentBlock)) + 'px';
2032    };
2033    // Note: This is also contained in the core Options.ts file
2034    const determineDefaultStyles = (editor, defaultStyles) => {
2035        if (isResponsiveForced(editor) || !shouldStyleWithCss(editor)) {
2036            return defaultStyles;
2037        }
2038        else if (isPixelsForced(editor)) {
2039            return { ...defaultStyles, width: getPixelForcedWidth(editor) };
2040        }
2041        else {
2042            return { ...defaultStyles, width: defaultWidth };
2043        }
2044    };
2045    // Note: This is also contained in the core Options.ts file
2046    const determineDefaultAttributes = (editor, defaultAttributes) => {
2047        if (isResponsiveForced(editor) || shouldStyleWithCss(editor)) {
2048            return defaultAttributes;
2049        }
2050        else if (isPixelsForced(editor)) {
2051            return { ...defaultAttributes, width: getPixelForcedWidth(editor) };
2052        }
2053        else {
2054            return { ...defaultAttributes, width: defaultWidth };
2055        }
2056    };
2057    const option = (name) => (editor) => editor.options.get(name);
2058    const register = (editor) => {
2059        const registerOption = editor.options.register;
2060        registerOption('table_border_widths', {
2061            processor: 'object[]',
2062            default: defaultCellBorderWidths
2063        });
2064        registerOption('table_border_styles', {
2065            processor: 'object[]',
2066            default: defaultCellBorderStyles
2067        });
2068        registerOption('table_cell_advtab', {
2069            processor: 'boolean',
2070            default: true
2071        });
2072        registerOption('table_row_advtab', {
2073            processor: 'boolean',
2074            default: true
2075        });
2076        registerOption('table_advtab', {
2077            processor: 'boolean',
2078            default: true
2079        });
2080        registerOption('table_appearance_options', {
2081            processor: 'boolean',
2082            default: true
2083        });
2084        registerOption('table_grid', {
2085            processor: 'boolean',
2086            // Table grid relies on hover, which isn't available on touch devices so use the dialog instead
2087            default: !global$1.deviceType.isTouch()
2088        });
2089        registerOption('table_cell_class_list', {
2090            processor: 'object[]',
2091            default: []
2092        });
2093        registerOption('table_row_class_list', {
2094            processor: 'object[]',
2095            default: []
2096        });
2097        registerOption('table_class_list', {
2098            processor: 'object[]',
2099            default: []
2100        });
2101        registerOption('table_toolbar', {
2102            processor: 'string',
2103            default: defaultTableToolbar
2104        });
2105        registerOption('table_background_color_map', {
2106            processor: 'object[]',
2107            default: []
2108        });
2109        registerOption('table_border_color_map', {
2110            processor: 'object[]',
2111            default: []
2112        });
2113    };
2114    const getTableSizingMode = option('table_sizing_mode');
2115    const getTableBorderWidths = option('table_border_widths');
2116    const getTableBorderStyles = option('table_border_styles');
2117    const hasAdvancedCellTab = option('table_cell_advtab');
2118    const hasAdvancedRowTab = option('table_row_advtab');
2119    const hasAdvancedTableTab = option('table_advtab');
2120    const hasAppearanceOptions = option('table_appearance_options');
2121    const hasTableGrid = option('table_grid');
2122    const shouldStyleWithCss = option('table_style_by_css');
2123    const getCellClassList = option('table_cell_class_list');
2124    const getRowClassList = option('table_row_class_list');
2125    const getTableClassList = option('table_class_list');
2126    const getToolbar = option('table_toolbar');
2127    const getTableBackgroundColorMap = option('table_background_color_map');
2128    const getTableBorderColorMap = option('table_border_color_map');
2129    const isPixelsForced = (editor) => getTableSizingMode(editor) === 'fixed';
2130    const isResponsiveForced = (editor) => getTableSizingMode(editor) === 'responsive';
2131    const getDefaultStyles = (editor) => {
2132        // Note: The we don't rely on the default here as we need to dynamically lookup the widths based on the current editor state
2133        const options = editor.options;
2134        const defaultStyles = options.get('table_default_styles');
2135        return options.isSet('table_default_styles') ? defaultStyles : determineDefaultStyles(editor, defaultStyles);
2136    };
2137    const getDefaultAttributes = (editor) => {
2138        // Note: The we don't rely on the default here as we need to dynamically lookup the widths based on the current editor state
2139        const options = editor.options;
2140        const defaultAttributes = options.get('table_default_attributes');
2141        return options.isSet('table_default_attributes') ? defaultAttributes : determineDefaultAttributes(editor, defaultAttributes);
2142    };
2143
2144    const lookupTable = (container) => {
2145        return ancestor(container, 'table');
2146    };
2147    const retrieve$1 = (container, selector) => {
2148        const sels = descendants(container, selector);
2149        return sels.length > 0 ? Optional.some(sels) : Optional.none();
2150    };
2151    const getEdges = (container, firstSelectedSelector, lastSelectedSelector) => {
2152        return descendant(container, firstSelectedSelector).bind((first) => {
2153            return descendant(container, lastSelectedSelector).bind((last) => {
2154                return sharedOne(lookupTable, [first, last]).map((table) => {
2155                    return {
2156                        first,
2157                        last,
2158                        table
2159                    };
2160                });
2161            });
2162        });
2163    };
2164
2165    // Explicitly calling CellSelection.retrieve so that we can see the API signature.
2166    const retrieve = (container, selector) => {
2167        return retrieve$1(container, selector);
2168    };
2169    const retrieveBox = (container, firstSelectedSelector, lastSelectedSelector) => {
2170        return getEdges(container, firstSelectedSelector, lastSelectedSelector).bind((edges) => {
2171            const isRoot = (ancestor) => {
2172                return eq(container, ancestor);
2173            };
2174            const sectionSelector = 'thead,tfoot,tbody,table';
2175            const firstAncestor = ancestor(edges.first, sectionSelector, isRoot);
2176            const lastAncestor = ancestor(edges.last, sectionSelector, isRoot);
2177            return firstAncestor.bind((fA) => {
2178                return lastAncestor.bind((lA) => {
2179                    return eq(fA, lA) ? getBox(edges.table, edges.first, edges.last) : Optional.none();
2180                });
2181            });
2182        });
2183    };
2184
2185    const selection = identity;
2186    const unmergable = (selectedCells) => {
2187        const hasSpan = (elem, type) => getOpt(elem, type).exists((span) => parseInt(span, 10) > 1);
2188        const hasRowOrColSpan = (elem) => hasSpan(elem, 'rowspan') || hasSpan(elem, 'colspan');
2189        return selectedCells.length > 0 && forall(selectedCells, hasRowOrColSpan) ? Optional.some(selectedCells) : Optional.none();
2190    };
2191    const mergable = (table, selectedCells, ephemera) => {
2192        if (selectedCells.length <= 1) {
2193            return Optional.none();
2194        }
2195        else {
2196            return retrieveBox(table, ephemera.firstSelectedSelector, ephemera.lastSelectedSelector)
2197                .map((bounds) => ({ bounds, cells: selectedCells }));
2198        }
2199    };
2200
2201    /*
2202     NOTE: This file is duplicated in the following locations:
2203      - models/dom/table/selection/Ephemera.ts
2204      - advtable
2205     Make sure that if making changes to this file, the other files are updated as well
2206     */
2207    const strSelected = 'data-mce-selected';
2208    const strSelectedSelector = 'td[' + strSelected + '],th[' + strSelected + ']';
2209    const strFirstSelected = 'data-mce-first-selected';
2210    const strFirstSelectedSelector = 'td[' + strFirstSelected + '],th[' + strFirstSelected + ']';
2211    const strLastSelected = 'data-mce-last-selected';
2212    const strLastSelectedSelector = 'td[' + strLastSelected + '],th[' + strLastSelected + ']';
2213    const ephemera = {
2214        selected: strSelected,
2215        selectedSelector: strSelectedSelector,
2216        firstSelected: strFirstSelected,
2217        firstSelectedSelector: strFirstSelectedSelector,
2218        lastSelected: strLastSelected,
2219        lastSelectedSelector: strLastSelectedSelector
2220    };
2221
2222    /*
2223     NOTE: This file is partially duplicated in the following locations:
2224      - models/dom/table/selection/TableSelection.ts
2225      - advtable
2226     Make sure that if making changes to this file, the other files are updated as well
2227     */
2228    const getSelectionCellFallback = (element) => table(element).bind((table) => retrieve(table, ephemera.firstSelectedSelector)).fold(constant(element), (cells) => cells[0]);
2229    const getSelectionFromSelector = (selector) => (initCell, isRoot) => {
2230        const cellName = name(initCell);
2231        const cell = cellName === 'col' || cellName === 'colgroup' ? getSelectionCellFallback(initCell) : initCell;
2232        return closest$1(cell, selector, isRoot);
2233    };
2234    const getSelectionCellOrCaption = getSelectionFromSelector('th,td,caption');
2235    const getSelectionCell = getSelectionFromSelector('th,td');
2236    const getCellsFromSelection = (editor) => fromDom(editor.model.table.getSelectedCells());
2237    const getRowsFromSelection = (selected, selector) => {
2238        const cellOpt = getSelectionCell(selected);
2239        const rowsOpt = cellOpt.bind((cell) => table(cell))
2240            .map((table) => rows(table));
2241        return lift2(cellOpt, rowsOpt, (cell, rows) => filter$1(rows, (row) => exists(fromDom(row.dom.cells), (rowCell) => get$2(rowCell, selector) === '1' || eq(rowCell, cell)))).getOr([]);
2242    };
2243
2244    const verticalAlignValues = [
2245        {
2246            text: 'None',
2247            value: ''
2248        },
2249        {
2250            text: 'Top',
2251            value: 'top'
2252        },
2253        {
2254            text: 'Middle',
2255            value: 'middle'
2256        },
2257        {
2258            text: 'Bottom',
2259            value: 'bottom'
2260        }
2261    ];
2262
2263    const hexColour = (value) => ({
2264        value: normalizeHex(value)
2265    });
2266    const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
2267    const longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
2268    const isHexString = (hex) => shorthandRegex.test(hex) || longformRegex.test(hex);
2269    const normalizeHex = (hex) => removeLeading(hex, '#').toUpperCase();
2270    const fromString$1 = (hex) => isHexString(hex) ? Optional.some({ value: normalizeHex(hex) }) : Optional.none();
2271    const toHex = (component) => {
2272        const hex = component.toString(16);
2273        return (hex.length === 1 ? '0' + hex : hex).toUpperCase();
2274    };
2275    const fromRgba = (rgbaColour) => {
2276        const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);
2277        return hexColour(value);
2278    };
2279
2280    /* eslint-disable no-console */
2281    const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*\)\s*$/i;
2282    // This regex will match rgba(0, 0, 0, 0.5) or rgba(0, 0, 0, 50%) , or without commas
2283    const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*((?:\d?\.\d+|\d+)%?)\s*\)\s*$/i;
2284    const rgbaColour = (red, green, blue, alpha) => ({
2285        red,
2286        green,
2287        blue,
2288        alpha
2289    });
2290    const fromStringValues = (red, green, blue, alpha) => {
2291        const r = parseInt(red, 10);
2292        const g = parseInt(green, 10);
2293        const b = parseInt(blue, 10);
2294        const a = parseFloat(alpha);
2295        return rgbaColour(r, g, b, a);
2296    };
2297    const fromString = (rgbaString) => {
2298        const rgbMatch = rgbRegex.exec(rgbaString);
2299        if (rgbMatch !== null) {
2300            return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], '1'));
2301        }
2302        const rgbaMatch = rgbaRegex.exec(rgbaString);
2303        if (rgbaMatch !== null) {
2304            return Optional.some(fromStringValues(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], rgbaMatch[4]));
2305        }
2306        return Optional.none();
2307    };
2308
2309    const anyToHex = (color) => fromString$1(color)
2310        .orThunk(() => fromString(color).map(fromRgba))
2311        .getOrThunk(() => {
2312        // Not dealing with Hex or RGBA so use a canvas to parse the color
2313        const canvas = document.createElement('canvas');
2314        canvas.height = 1;
2315        canvas.width = 1;
2316        const canvasContext = canvas.getContext('2d');
2317        // all valid colors after this point
2318        canvasContext.clearRect(0, 0, canvas.width, canvas.height);
2319        // invalid colors will be shown as white - the first assignment will pass and the second may be ignored
2320        canvasContext.fillStyle = '#FFFFFF';
2321        canvasContext.fillStyle = color;
2322        canvasContext.fillRect(0, 0, 1, 1);
2323        const rgba = canvasContext.getImageData(0, 0, 1, 1).data;
2324        const r = rgba[0];
2325        const g = rgba[1];
2326        const b = rgba[2];
2327        const a = rgba[3];
2328        return fromRgba(rgbaColour(r, g, b, a));
2329    });
2330    const rgbaToHexString = (color) => fromString(color)
2331        .map(fromRgba)
2332        .map((h) => '#' + h.value)
2333        .getOr(color);
2334
2335    const onSetupToggle = (editor, formatName, formatValue) => {
2336        return (api) => {
2337            const boundCallback = unbindable();
2338            const isNone = isEmpty(formatValue);
2339            const init = () => {
2340                const selectedCells = getCellsFromSelection(editor);
2341                const checkNode = (cell) => editor.formatter.match(formatName, { value: formatValue }, cell.dom, isNone);
2342                // If value is empty (A None-entry in the list), check if the format is not set at all. Otherwise, check if the format is set to the correct value.
2343                if (isNone) {
2344                    api.setActive(!exists(selectedCells, checkNode));
2345                    boundCallback.set(editor.formatter.formatChanged(formatName, (match) => api.setActive(!match), true));
2346                }
2347                else {
2348                    api.setActive(forall(selectedCells, checkNode));
2349                    boundCallback.set(editor.formatter.formatChanged(formatName, api.setActive, false, { value: formatValue }));
2350                }
2351            };
2352            // The editor may or may not have been setup yet, so check for that
2353            editor.initialized ? init() : editor.on('init', init);
2354            return boundCallback.clear;
2355        };
2356    };
2357    const isListGroup = (item) => hasNonNullableKey(item, 'menu');
2358    const buildListItems = (items) => map(items, (item) => {
2359        // item.text is not documented - maybe deprecated option we can delete??
2360        const text = item.text || item.title || '';
2361        if (isListGroup(item)) {
2362            return {
2363                text,
2364                items: buildListItems(item.menu)
2365            };
2366        }
2367        else {
2368            return {
2369                text,
2370                value: item.value
2371            };
2372        }
2373    });
2374    const buildClassList = (classList) => {
2375        if (!classList.length) {
2376            return Optional.none();
2377        }
2378        return Optional.some(buildListItems([{ text: 'Select...', value: 'mce-no-match' }, ...classList]));
2379    };
2380    const buildMenuItems = (editor, items, format, onAction) => map(items, (item) => {
2381        // item.text is not documented - maybe deprecated option we can delete??
2382        const text = item.text || item.title;
2383        if (isListGroup(item)) {
2384            return {
2385                type: 'nestedmenuitem',
2386                text,
2387                getSubmenuItems: () => buildMenuItems(editor, item.menu, format, onAction)
2388            };
2389        }
2390        else {
2391            return {
2392                text,
2393                type: 'togglemenuitem',
2394                onAction: () => onAction(item.value),
2395                onSetup: onSetupToggle(editor, format, item.value)
2396            };
2397        }
2398    });
2399    const applyTableCellStyle = (editor, style) => (value) => {
2400        editor.execCommand('mceTableApplyCellStyle', false, { [style]: value });
2401    };
2402    const filterNoneItem = (list) => bind(list, (item) => {
2403        if (isListGroup(item)) {
2404            return [{ ...item, menu: filterNoneItem(item.menu) }];
2405        }
2406        else {
2407            return isNotEmpty(item.value) ? [item] : [];
2408        }
2409    });
2410    const generateMenuItemsCallback = (editor, items, format, onAction) => (callback) => callback(buildMenuItems(editor, items, format, onAction));
2411    const buildColorMenu = (editor, colorList, style) => {
2412        const colorMap = map(colorList, (entry) => ({
2413            text: entry.title,
2414            value: '#' + anyToHex(entry.value).value,
2415            type: 'choiceitem'
2416        }));
2417        return [{
2418                type: 'fancymenuitem',
2419                fancytype: 'colorswatch',
2420                initData: {
2421                    colors: colorMap.length > 0 ? colorMap : undefined,
2422                    allowCustomColors: false
2423                },
2424                onAction: (data) => {
2425                    const value = data.value === 'remove' ? '' : data.value;
2426                    editor.execCommand('mceTableApplyCellStyle', false, { [style]: value });
2427                }
2428            }];
2429    };
2430    const changeRowHeader = (editor) => () => {
2431        const currentType = editor.queryCommandValue('mceTableRowType');
2432        const newType = currentType === 'header' ? 'body' : 'header';
2433        editor.execCommand('mceTableRowType', false, { type: newType });
2434    };
2435    const changeColumnHeader = (editor) => () => {
2436        const currentType = editor.queryCommandValue('mceTableColType');
2437        const newType = currentType === 'th' ? 'td' : 'th';
2438        editor.execCommand('mceTableColType', false, { type: newType });
2439    };
2440
2441    const getClassList$1 = (editor) => buildClassList(getCellClassList(editor))
2442        .map((items) => ({
2443        name: 'class',
2444        type: 'listbox',
2445        label: 'Class',
2446        items
2447    }));
2448    const children = [
2449        {
2450            name: 'width',
2451            type: 'input',
2452            label: 'Width'
2453        },
2454        {
2455            name: 'celltype',
2456            type: 'listbox',
2457            label: 'Cell type',
2458            items: [
2459                { text: 'Cell', value: 'td' },
2460                { text: 'Header cell', value: 'th' }
2461            ]
2462        },
2463        {
2464            name: 'scope',
2465            type: 'listbox',
2466            label: 'Scope',
2467            items: [
2468                { text: 'None', value: '' },
2469                { text: 'Row', value: 'row' },
2470                { text: 'Column', value: 'col' },
2471                { text: 'Row group', value: 'rowgroup' },
2472                { text: 'Column group', value: 'colgroup' }
2473            ]
2474        },
2475        {
2476            name: 'halign',
2477            type: 'listbox',
2478            label: 'Horizontal align',
2479            items: [
2480                { text: 'None', value: '' },
2481                { text: 'Left', value: 'left' },
2482                { text: 'Center', value: 'center' },
2483                { text: 'Right', value: 'right' }
2484            ]
2485        },
2486        {
2487            name: 'valign',
2488            type: 'listbox',
2489            label: 'Vertical align',
2490            items: verticalAlignValues
2491        }
2492    ];
2493    const getItems$2 = (editor) => children.concat(getClassList$1(editor).toArray());
2494
2495    const getAdvancedTab = (editor, dialogName) => {
2496        const emptyBorderStyle = [{ text: 'Select...', value: '' }];
2497        const advTabItems = [
2498            {
2499                name: 'borderstyle',
2500                type: 'listbox',
2501                label: 'Border style',
2502                items: emptyBorderStyle.concat(buildListItems(getTableBorderStyles(editor)))
2503            },
2504            {
2505                name: 'bordercolor',
2506                type: 'colorinput',
2507                label: 'Border color'
2508            },
2509            {
2510                name: 'backgroundcolor',
2511                type: 'colorinput',
2512                label: 'Background color'
2513            }
2514        ];
2515        const borderWidth = {
2516            name: 'borderwidth',
2517            type: 'input',
2518            label: 'Border width'
2519        };
2520        const items = dialogName === 'cell' ? [borderWidth].concat(advTabItems) : advTabItems;
2521        return {
2522            title: 'Advanced',
2523            name: 'advanced',
2524            items
2525        };
2526    };
2527
2528    // The get node is required here because it can be transformed
2529    // when switching between tags (e.g. th and td)
2530    const normal = (editor, element) => {
2531        const dom = editor.dom;
2532        const setAttrib = (attr, value) => {
2533            dom.setAttrib(element, attr, value);
2534        };
2535        const setStyle = (prop, value) => {
2536            dom.setStyle(element, prop, value);
2537        };
2538        const setFormat = (formatName, value) => {
2539            // Remove format if given an empty string
2540            if (value === '') {
2541                editor.formatter.remove(formatName, { value: null }, element, true);
2542            }
2543            else {
2544                editor.formatter.apply(formatName, { value }, element);
2545            }
2546        };
2547        return {
2548            setAttrib,
2549            setStyle,
2550            setFormat
2551        };
2552    };
2553    const DomModifier = {
2554        normal
2555    };
2556
2557    const rgbToHex = (value) => startsWith(value, 'rgb') ? rgbaToHexString(value) : value;
2558    const extractAdvancedStyles = (elm) => {
2559        const element = SugarElement.fromDom(elm);
2560        return {
2561            borderwidth: getRaw$1(element, 'border-width').getOr(''),
2562            borderstyle: getRaw$1(element, 'border-style').getOr(''),
2563            bordercolor: getRaw$1(element, 'border-color').map(rgbToHex).getOr(''),
2564            backgroundcolor: getRaw$1(element, 'background-color').map(rgbToHex).getOr('')
2565        };
2566    };
2567    const getSharedValues = (data) => {
2568        // TODO surely there's a better way to do this??
2569        // Mutates baseData to return an object that contains only the values
2570        // that were the same across all objects in data
2571        const baseData = data[0];
2572        const comparisonData = data.slice(1);
2573        each$1(comparisonData, (items) => {
2574            each$1(keys(baseData), (key) => {
2575                each(items, (itemValue, itemKey) => {
2576                    const comparisonValue = baseData[key];
2577                    if (comparisonValue !== '' && key === itemKey) {
2578                        if (comparisonValue !== itemValue) {
2579                            baseData[key] = key === 'class' ? 'mce-no-match' : '';
2580                        }
2581                    }
2582                });
2583            });
2584        });
2585        return baseData;
2586    };
2587    // The extractDataFrom... functions are in this file partly for code reuse and partly so we can test them,
2588    // because some of these are crazy complicated
2589    const getAlignment = (formats, formatName, editor, elm) => find(formats, (name) => !isUndefined(editor.formatter.matchNode(elm, formatName + name))).getOr('');
2590    const getHAlignment = curry(getAlignment, ['left', 'center', 'right'], 'align');
2591    const getVAlignment = curry(getAlignment, ['top', 'middle', 'bottom'], 'valign');
2592    const extractDataFromSettings = (editor, hasAdvTableTab) => {
2593        const style = getDefaultStyles(editor);
2594        const attrs = getDefaultAttributes(editor);
2595        const extractAdvancedStyleData = () => ({
2596            borderstyle: get$3(style, 'border-style').getOr(''),
2597            bordercolor: rgbToHex(get$3(style, 'border-color').getOr('')),
2598            backgroundcolor: rgbToHex(get$3(style, 'background-color').getOr(''))
2599        });
2600        const defaultData = {
2601            height: '',
2602            width: '100%',
2603            cellspacing: '',
2604            cellpadding: '',
2605            caption: false,
2606            class: '',
2607            align: '',
2608            border: ''
2609        };
2610        const getBorder = () => {
2611            const borderWidth = style['border-width'];
2612            if (shouldStyleWithCss(editor) && borderWidth) {
2613                return { border: borderWidth };
2614            }
2615            return get$3(attrs, 'border').fold(() => ({}), (border) => ({ border }));
2616        };
2617        const advStyle = (hasAdvTableTab ? extractAdvancedStyleData() : {});
2618        const getCellPaddingCellSpacing = () => {
2619            const spacing = get$3(style, 'border-spacing').or(get$3(attrs, 'cellspacing')).fold(() => ({}), (cellspacing) => ({ cellspacing }));
2620            const padding = get$3(style, 'border-padding').or(get$3(attrs, 'cellpadding')).fold(() => ({}), (cellpadding) => ({ cellpadding }));
2621            return {
2622                ...spacing,
2623                ...padding
2624            };
2625        };
2626        const data = {
2627            ...defaultData,
2628            ...style,
2629            ...attrs,
2630            ...advStyle,
2631            ...getBorder(),
2632            ...getCellPaddingCellSpacing()
2633        };
2634        return data;
2635    };
2636    const getRowType = (elm) => table(SugarElement.fromDom(elm)).map((table) => {
2637        const target = { selection: fromDom(elm.cells) };
2638        return getRowsType(table, target);
2639    }).getOr('');
2640    const extractDataFromTableElement = (editor, elm, hasAdvTableTab) => {
2641        const getBorder = (dom, elm) => {
2642            // Cases (in order to check):
2643            // 1. shouldStyleWithCss - extract border-width style if it exists
2644            // 2. !shouldStyleWithCss && border attribute - set border attribute as value
2645            // 3. !shouldStyleWithCss && nothing on the table - grab styles from the first th or td
2646            const optBorderWidth = getRaw$1(SugarElement.fromDom(elm), 'border-width');
2647            if (shouldStyleWithCss(editor) && optBorderWidth.isSome()) {
2648                return optBorderWidth.getOr('');
2649            }
2650            return dom.getAttrib(elm, 'border') || getTDTHOverallStyle(editor.dom, elm, 'border-width')
2651                || getTDTHOverallStyle(editor.dom, elm, 'border') || '';
2652        };
2653        const dom = editor.dom;
2654        const cellspacing = shouldStyleWithCss(editor) ?
2655            dom.getStyle(elm, 'border-spacing') || dom.getAttrib(elm, 'cellspacing') :
2656            dom.getAttrib(elm, 'cellspacing') || dom.getStyle(elm, 'border-spacing');
2657        const cellpadding = shouldStyleWithCss(editor) ?
2658            getTDTHOverallStyle(dom, elm, 'padding') || dom.getAttrib(elm, 'cellpadding') :
2659            dom.getAttrib(elm, 'cellpadding') || getTDTHOverallStyle(dom, elm, 'padding');
2660        return {
2661            width: dom.getStyle(elm, 'width') || dom.getAttrib(elm, 'width'),
2662            height: dom.getStyle(elm, 'height') || dom.getAttrib(elm, 'height'),
2663            cellspacing: cellspacing !== null && cellspacing !== void 0 ? cellspacing : '',
2664            cellpadding: cellpadding !== null && cellpadding !== void 0 ? cellpadding : '',
2665            border: getBorder(dom, elm),
2666            caption: !!dom.select('caption', elm)[0],
2667            class: dom.getAttrib(elm, 'class', ''),
2668            align: getHAlignment(editor, elm),
2669            ...(hasAdvTableTab ? extractAdvancedStyles(elm) : {})
2670        };
2671    };
2672    const extractDataFromRowElement = (editor, elm, hasAdvancedRowTab) => {
2673        const dom = editor.dom;
2674        return {
2675            height: dom.getStyle(elm, 'height') || dom.getAttrib(elm, 'height'),
2676            class: dom.getAttrib(elm, 'class', ''),
2677            type: getRowType(elm),
2678            align: getHAlignment(editor, elm),
2679            ...(hasAdvancedRowTab ? extractAdvancedStyles(elm) : {})
2680        };
2681    };
2682    const extractDataFromCellElement = (editor, cell, hasAdvancedCellTab, column) => {
2683        const dom = editor.dom;
2684        const colElm = column.getOr(cell);
2685        const getStyle = (element, style) => dom.getStyle(element, style) || dom.getAttrib(element, style);
2686        return {
2687            width: getStyle(colElm, 'width'),
2688            scope: dom.getAttrib(cell, 'scope'),
2689            celltype: getNodeName(cell),
2690            class: dom.getAttrib(cell, 'class', ''),
2691            halign: getHAlignment(editor, cell),
2692            valign: getVAlignment(editor, cell),
2693            ...(hasAdvancedCellTab ? extractAdvancedStyles(cell) : {})
2694        };
2695    };
2696
2697    const getSelectedCells = (table, cells) => {
2698        const warehouse = Warehouse.fromTable(table);
2699        const allCells = Warehouse.justCells(warehouse);
2700        const filtered = filter$1(allCells, (cellA) => exists(cells, (cellB) => eq(cellA.element, cellB)));
2701        return map(filtered, (cell) => ({
2702            element: cell.element.dom,
2703            column: Warehouse.getColumnAt(warehouse, cell.column).map((col) => col.element.dom)
2704        }));
2705    };
2706    const updateSimpleProps$1 = (modifier, colModifier, data, shouldUpdate) => {
2707        if (shouldUpdate('scope')) {
2708            modifier.setAttrib('scope', data.scope);
2709        }
2710        if (shouldUpdate('class') && data.class !== 'mce-no-match') {
2711            modifier.setAttrib('class', data.class);
2712        }
2713        if (shouldUpdate('width')) {
2714            colModifier.setStyle('width', addPxSuffix(data.width));
2715        }
2716    };
2717    const updateAdvancedProps$1 = (modifier, data, shouldUpdate) => {
2718        if (shouldUpdate('backgroundcolor')) {
2719            modifier.setFormat('tablecellbackgroundcolor', data.backgroundcolor);
2720        }
2721        if (shouldUpdate('bordercolor')) {
2722            modifier.setFormat('tablecellbordercolor', data.bordercolor);
2723        }
2724        if (shouldUpdate('borderstyle')) {
2725            modifier.setFormat('tablecellborderstyle', data.borderstyle);
2726        }
2727        if (shouldUpdate('borderwidth')) {
2728            modifier.setFormat('tablecellborderwidth', addPxSuffix(data.borderwidth));
2729        }
2730    };
2731    const applyStyleData$1 = (editor, cells, data, wasChanged) => {
2732        const isSingleCell = cells.length === 1;
2733        each$1(cells, (item) => {
2734            const cellElm = item.element;
2735            const shouldOverrideCurrentValue = isSingleCell ? always : wasChanged;
2736            const modifier = DomModifier.normal(editor, cellElm);
2737            const colModifier = item.column.map((col) => DomModifier.normal(editor, col)).getOr(modifier);
2738            updateSimpleProps$1(modifier, colModifier, data, shouldOverrideCurrentValue);
2739            if (hasAdvancedCellTab(editor)) {
2740                updateAdvancedProps$1(modifier, data, shouldOverrideCurrentValue);
2741            }
2742            // Apply alignment
2743            if (wasChanged('halign')) {
2744                setAlign(editor, cellElm, data.halign);
2745            }
2746            // Apply vertical alignment
2747            if (wasChanged('valign')) {
2748                setVAlign(editor, cellElm, data.valign);
2749            }
2750        });
2751    };
2752    const applyStructureData$1 = (editor, data) => {
2753        // Switch cell type if applicable. Note that we specifically tell the command to not fire events
2754        // as we'll batch the events and fire a `TableModified` event at the end of the updates.
2755        editor.execCommand('mceTableCellType', false, { type: data.celltype, no_events: true });
2756    };
2757    const applyCellData = (editor, cells, oldData, data) => {
2758        const modifiedData = filter(data, (value, key) => oldData[key] !== value);
2759        if (size(modifiedData) > 0 && cells.length >= 1) {
2760            // Retrieve the table before the cells are modified as there is a case where cells
2761            // are replaced and the reference will be lost when trying to fire events.
2762            table(cells[0]).each((table) => {
2763                const selectedCells = getSelectedCells(table, cells);
2764                // style modified if there's at least one other change apart from 'celltype' and 'scope'
2765                const styleModified = size(filter(modifiedData, (_value, key) => key !== 'scope' && key !== 'celltype')) > 0;
2766                const structureModified = has(modifiedData, 'celltype');
2767                // Update the cells styling using the dialog data
2768                if (styleModified || has(modifiedData, 'scope')) {
2769                    applyStyleData$1(editor, selectedCells, data, curry(has, modifiedData));
2770                }
2771                // Update the cells structure using the dialog data
2772                if (structureModified) {
2773                    applyStructureData$1(editor, data);
2774                }
2775                fireTableModified(editor, table.dom, {
2776                    structure: structureModified,
2777                    style: styleModified,
2778                });
2779            });
2780        }
2781    };
2782    const onSubmitCellForm = (editor, cells, oldData, api) => {
2783        const data = api.getData();
2784        api.close();
2785        editor.undoManager.transact(() => {
2786            applyCellData(editor, cells, oldData, data);
2787            editor.focus();
2788        });
2789    };
2790    const getData$1 = (editor, cells) => {
2791        const cellsData = table(cells[0]).map((table) => map(getSelectedCells(table, cells), (item) => extractDataFromCellElement(editor, item.element, hasAdvancedCellTab(editor), item.column)));
2792        return getSharedValues(cellsData.getOrDie());
2793    };
2794    const open$2 = (editor) => {
2795        const cells = getCellsFromSelection(editor);
2796        // Check if there are any cells to operate on
2797        if (cells.length === 0) {
2798            return;
2799        }
2800        const data = getData$1(editor, cells);
2801        const dialogTabPanel = {
2802            type: 'tabpanel',
2803            tabs: [
2804                {
2805                    title: 'General',
2806                    name: 'general',
2807                    items: getItems$2(editor)
2808                },
2809                getAdvancedTab(editor, 'cell')
2810            ]
2811        };
2812        const dialogPanel = {
2813            type: 'panel',
2814            items: [
2815                {
2816                    type: 'grid',
2817                    columns: 2,
2818                    items: getItems$2(editor)
2819                }
2820            ]
2821        };
2822        editor.windowManager.open({
2823            title: 'Cell Properties',
2824            size: 'normal',
2825            body: hasAdvancedCellTab(editor) ? dialogTabPanel : dialogPanel,
2826            buttons: [
2827                {
2828                    type: 'cancel',
2829                    name: 'cancel',
2830                    text: 'Cancel'
2831                },
2832                {
2833                    type: 'submit',
2834                    name: 'save',
2835                    text: 'Save',
2836                    primary: true
2837                }
2838            ],
2839            initialData: data,
2840            onSubmit: curry(onSubmitCellForm, editor, cells, data)
2841        });
2842    };
2843
2844    const getClassList = (editor) => buildClassList(getRowClassList(editor))
2845        .map((items) => ({
2846        name: 'class',
2847        type: 'listbox',
2848        label: 'Class',
2849        items
2850    }));
2851    const formChildren = [
2852        {
2853            type: 'listbox',
2854            name: 'type',
2855            label: 'Row type',
2856            items: [
2857                { text: 'Header', value: 'header' },
2858                { text: 'Body', value: 'body' },
2859                { text: 'Footer', value: 'footer' }
2860            ]
2861        },
2862        {
2863            type: 'listbox',
2864            name: 'align',
2865            label: 'Alignment',
2866            items: [
2867                { text: 'None', value: '' },
2868                { text: 'Left', value: 'left' },
2869                { text: 'Center', value: 'center' },
2870                { text: 'Right', value: 'right' }
2871            ]
2872        },
2873        {
2874            label: 'Height',
2875            name: 'height',
2876            type: 'input'
2877        }
2878    ];
2879    const getItems$1 = (editor) => formChildren.concat(getClassList(editor).toArray());
2880
2881    const updateSimpleProps = (modifier, data, shouldUpdate) => {
2882        if (shouldUpdate('class') && data.class !== 'mce-no-match') {
2883            modifier.setAttrib('class', data.class);
2884        }
2885        if (shouldUpdate('height')) {
2886            modifier.setStyle('height', addPxSuffix(data.height));
2887        }
2888    };
2889    const updateAdvancedProps = (modifier, data, shouldUpdate) => {
2890        if (shouldUpdate('backgroundcolor')) {
2891            modifier.setStyle('background-color', data.backgroundcolor);
2892        }
2893        if (shouldUpdate('bordercolor')) {
2894            modifier.setStyle('border-color', data.bordercolor);
2895        }
2896        if (shouldUpdate('borderstyle')) {
2897            modifier.setStyle('border-style', data.borderstyle);
2898        }
2899    };
2900    const applyStyleData = (editor, rows, data, wasChanged) => {
2901        const isSingleRow = rows.length === 1;
2902        const shouldOverrideCurrentValue = isSingleRow ? always : wasChanged;
2903        each$1(rows, (rowElm) => {
2904            const rowCells = children$1(SugarElement.fromDom(rowElm), 'td,th');
2905            const modifier = DomModifier.normal(editor, rowElm);
2906            updateSimpleProps(modifier, data, shouldOverrideCurrentValue);
2907            if (hasAdvancedRowTab(editor)) {
2908                updateAdvancedProps(modifier, data, shouldOverrideCurrentValue);
2909            }
2910            // TINY-10617: Simplify number of height styles when applying height on tr
2911            if (wasChanged('height')) {
2912                each$1(rowCells, (cell) => {
2913                    editor.dom.setStyle(cell.dom, 'height', null);
2914                });
2915            }
2916            if (wasChanged('align')) {
2917                setAlign(editor, rowElm, data.align);
2918            }
2919        });
2920    };
2921    const applyStructureData = (editor, data) => {
2922        // Switch cell type if applicable. Note that we specifically tell the command to not fire events
2923        // as we'll batch the events and fire a `TableModified` event at the end of the updates.
2924        editor.execCommand('mceTableRowType', false, { type: data.type, no_events: true });
2925    };
2926    const applyRowData = (editor, rows, oldData, data) => {
2927        const modifiedData = filter(data, (value, key) => oldData[key] !== value);
2928        if (size(modifiedData) > 0) {
2929            const typeModified = has(modifiedData, 'type');
2930            // style modified if there's at least one other change apart from 'type'
2931            const styleModified = typeModified ? size(modifiedData) > 1 : true;
2932            // Update the rows styling using the dialog data
2933            if (styleModified) {
2934                applyStyleData(editor, rows, data, curry(has, modifiedData));
2935            }
2936            // Update the rows structure using the dialog data
2937            if (typeModified) {
2938                applyStructureData(editor, data);
2939            }
2940            table(SugarElement.fromDom(rows[0])).each((table) =>
2940 fireTableModified(editor, table.dom, {
2941                structure: typeModified,
2942                style: styleModified
2943            }));
2944        }
2945    };
2946    const onSubmitRowForm = (editor, rows, oldData, api) => {
2947        const data = api.getData();
2948        api.close();
2949        editor.undoManager.transact(() => {
2950            applyRowData(editor, rows, oldData, data);
2951            editor.focus();
2952        });
2953    };
2954    const open$1 = (editor) => {
2955        const rows = getRowsFromSelection(getSelectionStart(editor), ephemera.selected);
2956        // Check if there are any rows to operate on
2957        if (rows.length === 0) {
2958            return;
2959        }
2960        // Get current data and find shared values between rows
2961        const rowsData = map(rows, (rowElm) => extractDataFromRowElement(editor, rowElm.dom, hasAdvancedRowTab(editor)));
2962        const data = getSharedValues(rowsData);
2963        const dialogTabPanel = {
2964            type: 'tabpanel',
2965            tabs: [
2966                {
2967                    title: 'General',
2968                    name: 'general',
2969                    items: getItems$1(editor)
2970                },
2971                getAdvancedTab(editor, 'row')
2972            ]
2973        };
2974        const dialogPanel = {
2975            type: 'panel',
2976            items: [
2977                {
2978                    type: 'grid',
2979                    columns: 2,
2980                    items: getItems$1(editor)
2981                }
2982            ]
2983        };
2984        editor.windowManager.open({
2985            title: 'Row Properties',
2986            size: 'normal',
2987            body: hasAdvancedRowTab(editor) ? dialogTabPanel : dialogPanel,
2988            buttons: [
2989                {
2990                    type: 'cancel',
2991                    name: 'cancel',
2992                    text: 'Cancel'
2993                },
2994                {
2995                    type: 'submit',
2996                    name: 'save',
2997                    text: 'Save',
2998                    primary: true
2999                }
3000            ],
3001            initialData: data,
3002            onSubmit: curry(onSubmitRowForm, editor, map(rows, (r) => r.dom), data)
3003        });
3004    };
3005
3006    const getItems = (editor, classes, insertNewTable) => {
3007        const rowColCountItems = !insertNewTable ? [] : [
3008            {
3009                type: 'input',
3010                name: 'cols',
3011                label: 'Cols',
3012                inputMode: 'numeric'
3013            },
3014            {
3015                type: 'input',
3016                name: 'rows',
3017                label: 'Rows',
3018                inputMode: 'numeric'
3019            }
3020        ];
3021        const alwaysItems = [
3022            {
3023                type: 'input',
3024                name: 'width',
3025                label: 'Width'
3026            },
3027            {
3028                type: 'input',
3029                name: 'height',
3030                label: 'Height'
3031            }
3032        ];
3033        const appearanceItems = hasAppearanceOptions(editor) ? [
3034            {
3035                type: 'input',
3036                name: 'cellspacing',
3037                label: 'Cell spacing',
3038                inputMode: 'numeric'
3039            },
3040            {
3041                type: 'input',
3042                name: 'cellpadding',
3043                label: 'Cell padding',
3044                inputMode: 'numeric'
3045            },
3046            {
3047                type: 'input',
3048                name: 'border',
3049                label: 'Border width'
3050            },
3051            {
3052                type: 'label',
3053                label: 'Caption',
3054                items: [
3055                    {
3056                        type: 'checkbox',
3057                        name: 'caption',
3058                        label: 'Show caption'
3059                    }
3060                ]
3061            }
3062        ] : [];
3063        const alignmentItem = [
3064            {
3065                type: 'listbox',
3066                name: 'align',
3067                label: 'Alignment',
3068                items: [
3069                    { text: 'None', value: '' },
3070                    { text: 'Left', value: 'left' },
3071                    { text: 'Center', value: 'center' },
3072                    { text: 'Right', value: 'right' }
3073                ]
3074            }
3075        ];
3076        const classListItem = classes.length > 0 ? [
3077            {
3078                name: 'class',
3079                type: 'listbox',
3080                label: 'Class',
3081                items: classes
3082            }
3083        ] : [];
3084        return rowColCountItems.concat(alwaysItems).concat(appearanceItems).concat(alignmentItem).concat(classListItem);
3085    };
3086
3087    // Explore the layers of the table till we find the first layer of tds or ths
3088    const styleTDTH = (dom, elm, name, value) => {
3089        if (elm.tagName === 'TD' || elm.tagName === 'TH') {
3090            if (isString(name) && isNonNullable(value)) {
3091                dom.setStyle(elm, name, value);
3092            }
3093            else {
3094                dom.setStyles(elm, name);
3095            }
3096        }
3097        else {
3098            if (elm.children) {
3099                for (let i = 0; i < elm.children.length; i++) {
3100                    styleTDTH(dom, elm.children[i], name, value);
3101                }
3102            }
3103        }
3104    };
3105    const applyDataToElement = (editor, tableElm, data, shouldApplyOnCell) => {
3106        const dom = editor.dom;
3107        const attrs = {};
3108        const styles = {};
3109        const shouldStyleWithCss$1 = shouldStyleWithCss(editor);
3110        const hasAdvancedTableTab$1 = hasAdvancedTableTab(editor);
3111        const borderIsZero = parseFloat(data.border) === 0;
3112        if (!isUndefined(data.class) && data.class !== 'mce-no-match') {
3113            attrs.class = data.class;
3114        }
3115        styles.height = addPxSuffix(data.height);
3116        if (shouldStyleWithCss$1) {
3117            styles.width = addPxSuffix(data.width);
3118        }
3119        else if (dom.getAttrib(tableElm, 'width')) {
3120            attrs.width = removePxSuffix(data.width);
3121        }
3122        if (shouldStyleWithCss$1) {
3123            if (borderIsZero) {
3124                attrs.border = 0;
3125                styles['border-width'] = '';
3126            }
3127            else {
3128                styles['border-width'] = addPxSuffix(data.border);
3129                attrs.border = 1;
3130            }
3131            styles['border-spacing'] = addPxSuffix(data.cellspacing);
3132        }
3133        else {
3134            attrs.border = borderIsZero ? 0 : data.border;
3135            attrs.cellpadding = data.cellpadding;
3136            attrs.cellspacing = data.cellspacing;
3137        }
3138        // TINY-9837: Relevant data are applied on child TD/THs only if they have been modified since the previous dialog submission
3139        if (shouldStyleWithCss$1 && tableElm.children) {
3140            const cellStyles = {};
3141            if (borderIsZero) {
3142                cellStyles['border-width'] = '';
3143            }
3144            else if (shouldApplyOnCell.border) {
3145                cellStyles['border-width'] = addPxSuffix(data.border);
3146            }
3147            if (shouldApplyOnCell.cellpadding) {
3148                cellStyles.padding = addPxSuffix(data.cellpadding);
3149            }
3150            if (hasAdvancedTableTab$1 && shouldApplyOnCell.bordercolor) {
3151                cellStyles['border-color'] = data.bordercolor;
3152            }
3153            if (!isEmpty$1(cellStyles)) {
3154                for (let i = 0; i < tableElm.children.length; i++) {
3155                    styleTDTH(dom, tableElm.children[i], cellStyles);
3156                }
3157            }
3158        }
3159        if (hasAdvancedTableTab$1) {
3160            const advData = data;
3161            styles['background-color'] = advData.backgroundcolor;
3162            styles['border-color'] = advData.bordercolor;
3163            styles['border-style'] = advData.borderstyle;
3164        }
3165        dom.setStyles(tableElm, { ...getDefaultStyles(editor), ...styles });
3166        dom.setAttribs(tableElm, { ...getDefaultAttributes(editor), ...attrs });
3167    };
3168    const onSubmitTableForm = (editor, tableElm, oldData, api) => {
3169        const dom = editor.dom;
3170        const data = api.getData();
3171        const modifiedData = filter(data, (value, key) => oldData[key] !== value);
3172        api.close();
3173        editor.undoManager.transact(() => {
3174            if (!tableElm) {
3175                const cols = toInt(data.cols).getOr(1);
3176                const rows = toInt(data.rows).getOr(1);
3177                // Cases 1 & 3 - inserting a table
3178                editor.execCommand('mceInsertTable', false, { rows, columns: cols });
3179                tableElm = getSelectionCell(getSelectionStart(editor), getIsRoot(editor))
3180                    .bind((cell) => table(cell, getIsRoot(editor)))
3181                    .map((table) => table.dom)
3182                    .getOrDie();
3183            }
3184            if (size(modifiedData) > 0) {
3185                const applicableCellProperties = {
3186                    border: has(modifiedData, 'border'),
3187                    bordercolor: has(modifiedData, 'bordercolor'),
3188                    cellpadding: has(modifiedData, 'cellpadding')
3189                };
3190                applyDataToElement(editor, tableElm, data, applicableCellProperties);
3191                // Toggle caption on/off
3192                const captionElm = dom.select('caption', tableElm)[0];
3193                if (captionElm && !data.caption || !captionElm && data.caption) {
3194                    editor.execCommand('mceTableToggleCaption');
3195                }
3196                setAlign(editor, tableElm, data.align);
3197            }
3198            editor.focus();
3199            editor.addVisual();
3200            if (size(modifiedData) > 0) {
3201                const captionModified = has(modifiedData, 'caption');
3202                // style modified if there's at least one other change apart from 'caption'
3203                const styleModified = captionModified ? size(modifiedData) > 1 : true;
3204                fireTableModified(editor, tableElm, { structure: captionModified, style: styleModified });
3205            }
3206        });
3207    };
3208    const open = (editor, insertNewTable) => {
3209        const dom = editor.dom;
3210        let tableElm;
3211        let data = extractDataFromSettings(editor, hasAdvancedTableTab(editor));
3212        // Cases for creation/update of tables:
3213        // 1. isNew == true - called by mceInsertTable - we are inserting a new table so we don't care what the selection's parent is,
3214        //    and we need to add cols and rows input fields to the dialog
3215        // 2. isNew == false && selection parent is a table - update the table
3216        // 3. isNew == false && selection parent isn't a table - open dialog with default values and insert a table
3217        if (insertNewTable) {
3218            // Case 1 - isNew == true. We're inserting a new table so use defaults and add cols and rows + adv properties.
3219            data.cols = '1';
3220            data.rows = '1';
3221            if (hasAdvancedTableTab(editor)) {
3222                data.borderstyle = '';
3223                data.bordercolor = '';
3224                data.backgroundcolor = '';
3225            }
3226        }
3227        else {
3228            tableElm = dom.getParent(editor.selection.getStart(), 'table', editor.getBody());
3229            if (tableElm) {
3230                // Case 2 - isNew == false && table parent
3231                data = extractDataFromTableElement(editor, tableElm, hasAdvancedTableTab(editor));
3232            }
3233            else {
3234                // Case 3 - isNew == false && non-table parent. data is set to basic defaults so just add the adv properties if needed
3235                if (hasAdvancedTableTab(editor)) {
3236                    data.borderstyle = '';
3237                    data.bordercolor = '';
3238                    data.backgroundcolor = '';
3239                }
3240            }
3241        }
3242        const classes = buildClassList(getTableClassList(editor));
3243        if (classes.isSome()) {
3244            if (data.class) {
3245                data.class = data.class.replace(/\s*mce\-item\-table\s*/g, '');
3246            }
3247        }
3248        const generalPanel = {
3249            type: 'grid',
3250            columns: 2,
3251            items: getItems(editor, classes.getOr([]), insertNewTable)
3252        };
3253        const nonAdvancedForm = () => ({
3254            type: 'panel',
3255            items: [generalPanel]
3256        });
3257        const advancedForm = () => ({
3258            type: 'tabpanel',
3259            tabs: [
3260                {
3261                    title: 'General',
3262                    name: 'general',
3263                    items: [generalPanel]
3264                },
3265                getAdvancedTab(editor, 'table')
3266            ]
3267        });
3268        const dialogBody = hasAdvancedTableTab(editor) ? advancedForm() : nonAdvancedForm();
3269        editor.windowManager.open({
3270            title: 'Table Properties',
3271            size: 'normal',
3272            body: dialogBody,
3273            onSubmit: curry(onSubmitTableForm, editor, tableElm, data),
3274            buttons: [
3275                {
3276                    type: 'cancel',
3277                    name: 'cancel',
3278                    text: 'Cancel'
3279                },
3280                {
3281                    type: 'submit',
3282                    name: 'save',
3283                    text: 'Save',
3284                    primary: true
3285                }
3286            ],
3287            initialData: data
3288        });
3289    };
3290
3291    const registerCommands = (editor) => {
3292        const runAction = (f) => {
3293            if (isInEditableContext(getSelectionStart(editor))) {
3294                f();
3295            }
3296        };
3297        // Register dialog commands
3298        each({
3299            // AP-101 TableDialog.open renders a slightly different dialog if isNew is true
3300            mceTableProps: curry(open, editor, false),
3301            mceTableRowProps: curry(open$1, editor),
3302            mceTableCellProps: curry(open$2, editor),
3303            mceInsertTableDialog: curry(open, editor, true),
3304        }, (func, name) => editor.addCommand(name, () => runAction(func)));
3305    };
3306
3307    /*
3308     NOTE: This file is partially duplicated in the following locations:
3309      - models/dom/table/queries/TableTargets.ts
3310      - advtable
3311     Make sure that if making changes to this file, the other files are updated as well
3312     */
3313    const noMenu = (cell) => ({
3314        element: cell,
3315        mergable: Optional.none(),
3316        unmergable: Optional.none(),
3317        selection: [cell]
3318    });
3319    const forMenu = (selectedCells, table, cell) => ({
3320        element: cell,
3321        mergable: mergable(table, selectedCells, ephemera),
3322        unmergable: unmergable(selectedCells),
3323        selection: selection(selectedCells)
3324    });
3325
3326    const getSelectionTargets = (editor) => {
3327        const targets = Cell(Optional.none());
3328        const changeHandlers = Cell([]);
3329        let selectionDetails = Optional.none();
3330        const isCaption = isTag('caption');
3331        const isDisabledForSelection = (key) => selectionDetails.forall((details) => !details[key]);
3332        const getStart = () => getSelectionCellOrCaption(getSelectionStart(editor), getIsRoot(editor));
3333        const getEnd = () => getSelectionCellOrCaption(getSelectionEnd(editor), getIsRoot(editor));
3334        const findTargets = () => getStart().bind((startCellOrCaption) => flatten(lift2(table(startCellOrCaption), getEnd().bind(table), (startTable, endTable) => {
3335            if (eq(startTable, endTable)) {
3336                if (isCaption(startCellOrCaption)) {
3337                    return Optional.some(noMenu(startCellOrCaption));
3338                }
3339                else {
3340                    return Optional.some(forMenu(getCellsFromSelection(editor), startTable, startCellOrCaption));
3341                }
3342            }
3343            return Optional.none();
3344        })));
3345        const getExtractedDetails = (targets) => {
3346            const tableOpt = table(targets.element);
3347            return tableOpt.map((table) => {
3348                const warehouse = Warehouse.fromTable(table);
3349                const selectedCells = onCells(warehouse, targets).getOr([]);
3350                const locked = foldl(selectedCells, (acc, cell) => {
3351                    if (cell.isLocked) {
3352                        acc.onAny = true;
3353                        if (cell.column === 0) {
3354                            acc.onFirst = true;
3355                        }
3356                        else if (cell.column + cell.colspan >= warehouse.grid.columns) {
3357                            acc.onLast = true;
3358                        }
3359                    }
3360                    return acc;
3361                }, { onAny: false, onFirst: false, onLast: false });
3362                return {
3363                    mergeable: onUnlockedMergable(warehouse, targets).isSome(),
3364                    unmergeable: onUnlockedUnmergable(warehouse, targets).isSome(),
3365                    locked
3366                };
3367            });
3368        };
3369        const resetTargets = () => {
3370            // Reset the targets
3371            targets.set(cached(findTargets)());
3372            // Reset the selection details
3373            selectionDetails = targets.get().bind(getExtractedDetails);
3374            // Trigger change handlers
3375            each$1(changeHandlers.get(), call);
3376        };
3377        const setupHandler = (handler) => {
3378            // Execute the handler to set the initial state
3379            handler();
3380            // Register the handler so we can update the state when resetting targets
3381            changeHandlers.set(changeHandlers.get().concat([handler]));
3382            return () => {
3383                changeHandlers.set(filter$1(changeHandlers.get(), (h) => h !== handler));
3384            };
3385        };
3386        const onSetup = (api, isDisabled) => setupHandler(() => targets.get().fold(() => {
3387            api.setEnabled(false);
3388        }, (targets) => {
3389            api.setEnabled(!isDisabled(targets) && editor.selection.isEditable());
3390        }));
3391        const onSetupWithToggle = (api, isDisabled, isActive) => setupHandler(() => targets.get().fold(() => {
3392            api.setEnabled(false);
3393            api.setActive(false);
3394        }, (targets) => {
3395            api.setEnabled(!isDisabled(targets) && editor.selection.isEditable());
3396            api.setActive(isActive(targets));
3397        }));
3398        const isDisabledFromLocked = (lockedDisable) => selectionDetails.exists((details) => details.locked[lockedDisable]);
3399        const onSetupTable = (api) => onSetup(api, (_) => false);
3400        const onSetupCellOrRow = (api) => onSetup(api, (targets) => isCaption(targets.element));
3401        const onSetupColumn = (lockedDisable) => (api) => onSetup(api, (targets) => isCaption(targets.element) || isDisabledFromLocked(lockedDisable));
3402        const onSetupPasteable = (getClipboardData) => (api) => onSetup(api, (targets) => isCaption(targets.element) || getClipboardData().isNone());
3403        const onSetupPasteableColumn = (getClipboardData, lockedDisable) => (api) => onSetup(api, (targets) => isCaption(targets.element) || getClipboardData().isNone() || isDisabledFromLocked(lockedDisable));
3404        const onSetupMergeable = (api) => onSetup(api, (_targets) => isDisabledForSelection('mergeable'));
3405        const onSetupUnmergeable = (api) => onSetup(api, (_targets) => isDisabledForSelection('unmergeable'));
3406        const onSetupTableWithCaption = (api) => {
3407            return onSetupWithToggle(api, never, (targets) => {
3408                const tableOpt = table(targets.element, getIsRoot(editor));
3409                return tableOpt.exists((table) => child(table, 'caption'));
3410            });
3411        };
3412        const onSetupTableHeaders = (command, headerType) => (api) => {
3413            return onSetupWithToggle(api, (targets) => isCaption(targets.element), () => editor.queryCommandValue(command) === headerType);
3414        };
3415        const onSetupTableRowHeaders = onSetupTableHeaders('mceTableRowType', 'header');
3416        const onSetupTableColumnHeaders = onSetupTableHeaders('mceTableColType', 'th');
3417        editor.on('NodeChange ExecCommand TableSelectorChange', resetTargets);
3418        return {
3419            onSetupTable,
3420            onSetupCellOrRow,
3421            onSetupColumn,
3422            onSetupPasteable,
3423            onSetupPasteableColumn,
3424            onSetupMergeable,
3425            onSetupUnmergeable,
3426            resetTargets,
3427            onSetupTableWithCaption,
3428            onSetupTableRowHeaders,
3429            onSetupTableColumnHeaders,
3430            targets: targets.get
3431        };
3432    };
3433
3434    var global = tinymce.util.Tools.resolve('tinymce.FakeClipboard');
3435
3436    /*
3437     NOTE: This file is duplicated in the following locations:
3438      - models/dom/table/api/Clipboard.ts
3439     Make sure that if making changes to this file, the other files are updated as well
3440     */
3441    const tableTypeBase = 'x-tinymce/dom-table-';
3442    const tableTypeRow = tableTypeBase + 'rows';
3443    const tableTypeColumn = tableTypeBase + 'columns';
3444    const getData = (type) => {
3445        var _a;
3446        const items = (_a = global.read()) !== null && _a !== void 0 ? _a : [];
3447        return findMap(items, (item) => Optional.from(item.getType(type)));
3448    };
3449    const getRows = () => getData(tableTypeRow);
3450    const getColumns = () => getData(tableTypeColumn);
3451
3452    const onSetupEditable$1 = (editor) => (api) => {
3453        const nodeChanged = () => {
3454            api.setEnabled(editor.selection.isEditable());
3455        };
3456        editor.on('NodeChange', nodeChanged);
3457        nodeChanged();
3458        return () => {
3459            editor.off('NodeChange', nodeChanged);
3460        };
3461    };
3462    const addButtons = (editor, selectionTargets) => {
3463        editor.ui.registry.addMenuButton('table', {
3464            tooltip: 'Table',
3465            icon: 'table',
3466            onSetup: onSetupEditable$1(editor),
3467            fetch: (callback) => callback('inserttable | cell row column | advtablesort | tableprops deletetable')
3468        });
3469        const cmd = (command) => () => editor.execCommand(command);
3470        // TODO: TINY-8172 Unwind this when an alternative solution is found
3471        const addButtonIfRegistered = (name, spec) => {
3472            if (editor.queryCommandSupported(spec.command)) {
3473                editor.ui.registry.addButton(name, {
3474                    ...spec,
3475                    onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
3476                });
3477            }
3478        };
3479        // TODO: TINY-8172 Unwind this when an alternative solution is found
3480        const addToggleButtonIfRegistered = (name, spec) => {
3481            if (editor.queryCommandSupported(spec.command)) {
3482                editor.ui.registry.addToggleButton(name, {
3483                    ...spec,
3484                    onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
3485                });
3486            }
3487        };
3488        addButtonIfRegistered('tableprops', {
3489            tooltip: 'Table properties',
3490            command: 'mceTableProps',
3491            icon: 'table',
3492            onSetup: selectionTargets.onSetupTable
3493        });
3494        addButtonIfRegistered('tabledelete', {
3495            tooltip: 'Delete table',
3496            command: 'mceTableDelete',
3497            icon: 'table-delete-table',
3498            onSetup: selectionTargets.onSetupTable
3499        });
3500        addButtonIfRegistered('tablecellprops', {
3501            tooltip: 'Cell properties',
3502            command: 'mceTableCellProps',
3503            icon: 'table-cell-properties',
3504            onSetup: selectionTargets.onSetupCellOrRow
3505        });
3506        addButtonIfRegistered('tablemergecells', {
3507            tooltip: 'Merge cells',
3508            command: 'mceTableMergeCells',
3509            icon: 'table-merge-cells',
3510            onSetup: selectionTargets.onSetupMergeable
3511        });
3512        addButtonIfRegistered('tablesplitcells', {
3513            tooltip: 'Split cell',
3514            command: 'mceTableSplitCells',
3515            icon: 'table-split-cells',
3516            onSetup: selectionTargets.onSetupUnmergeable
3517        });
3518        addButtonIfRegistered('tableinsertrowbefore', {
3519            tooltip: 'Insert row before',
3520            command: 'mceTableInsertRowBefore',
3521            icon: 'table-insert-row-above',
3522            onSetup: selectionTargets.onSetupCellOrRow
3523        });
3524        addButtonIfRegistered('tableinsertrowafter', {
3525            tooltip: 'Insert row after',
3526            command: 'mceTableInsertRowAfter',
3527            icon: 'table-insert-row-after',
3528            onSetup: selectionTargets.onSetupCellOrRow
3529        });
3530        addButtonIfRegistered('tabledeleterow', {
3531            tooltip: 'Delete row',
3532            command: 'mceTableDeleteRow',
3533            icon: 'table-delete-row',
3534            onSetup: selectionTargets.onSetupCellOrRow
3535        });
3536        addButtonIfRegistered('tablerowprops', {
3537            tooltip: 'Row properties',
3538            command: 'mceTableRowProps',
3539            icon: 'table-row-properties',
3540            onSetup: selectionTargets.onSetupCellOrRow
3541        });
3542        addButtonIfRegistered('tableinsertcolbefore', {
3543            tooltip: 'Insert column before',
3544            command: 'mceTableInsertColBefore',
3545            icon: 'table-insert-column-before',
3546            onSetup: selectionTargets.onSetupColumn("onFirst" /* LockedDisable.onFirst */)
3547        });
3548        addButtonIfRegistered('tableinsertcolafter', {
3549            tooltip: 'Insert column after',
3550            command: 'mceTableInsertColAfter',
3551            icon: 'table-insert-column-after',
3552            onSetup: selectionTargets.onSetupColumn("onLast" /* LockedDisable.onLast */)
3553        });
3554        addButtonIfRegistered('tabledeletecol', {
3555            tooltip: 'Delete column',
3556            command: 'mceTableDeleteCol',
3557            icon: 'table-delete-column',
3558            onSetup: selectionTargets.onSetupColumn("onAny" /* LockedDisable.onAny */)
3559        });
3560        addButtonIfRegistered('tablecutrow', {
3561            tooltip: 'Cut row',
3562            command: 'mceTableCutRow',
3563            icon: 'cut-row',
3564            onSetup: selectionTargets.onSetupCellOrRow
3565        });
3566        addButtonIfRegistered('tablecopyrow', {
3567            tooltip: 'Copy row',
3568            command: 'mceTableCopyRow',
3569            icon: 'duplicate-row',
3570            onSetup: selectionTargets.onSetupCellOrRow
3571        });
3572        addButtonIfRegistered('tablepasterowbefore', {
3573            tooltip: 'Paste row before',
3574            command: 'mceTablePasteRowBefore',
3575            icon: 'paste-row-before',
3576            onSetup: selectionTargets.onSetupPasteable(getRows)
3577        });
3578        addButtonIfRegistered('tablepasterowafter', {
3579            tooltip: 'Paste row after',
3580            command: 'mceTablePasteRowAfter',
3581            icon: 'paste-row-after',
3582            onSetup: selectionTargets.onSetupPasteable(getRows)
3583        });
3584        addButtonIfRegistered('tablecutcol', {
3585            tooltip: 'Cut column',
3586            command: 'mceTableCutCol',
3587            icon: 'cut-column',
3588            onSetup: selectionTargets.onSetupColumn("onAny" /* LockedDisable.onAny */)
3589        });
3590        addButtonIfRegistered('tablecopycol', {
3591            tooltip: 'Copy column',
3592            command: 'mceTableCopyCol',
3593            icon: 'duplicate-column',
3594            onSetup: selectionTargets.onSetupColumn("onAny" /* LockedDisable.onAny */)
3595        });
3596        addButtonIfRegistered('tablepastecolbefore', {
3597            tooltip: 'Paste column before',
3598            command: 'mceTablePasteColBefore',
3599            icon: 'paste-column-before',
3600            onSetup: selectionTargets.onSetupPasteableColumn(getColumns, "onFirst" /* LockedDisable.onFirst */)
3601        });
3602        addButtonIfRegistered('tablepastecolafter', {
3603            tooltip: 'Paste column after',
3604            command: 'mceTablePasteColAfter',
3605            icon: 'paste-column-after',
3606            onSetup: selectionTargets.onSetupPasteableColumn(getColumns, "onLast" /* LockedDisable.onLast */)
3607        });
3608        addButtonIfRegistered('tableinsertdialog', {
3609            tooltip: 'Insert table',
3610            command: 'mceInsertTableDialog',
3611            icon: 'table',
3612            onSetup: onSetupEditable$1(editor)
3613        });
3614        const tableClassList = filterNoneItem(getTableClassList(editor));
3615        if (tableClassList.length !== 0 && editor.queryCommandSupported('mceTableToggleClass')) {
3616            editor.ui.registry.addMenuButton('tableclass', {
3617                icon: 'table-classes',
3618                tooltip: 'Table styles',
3619                fetch: generateMenuItemsCallback(editor, tableClassList, 'tableclass', (value) => editor.execCommand('mceTableToggleClass', false, value)),
3620                onSetup: selectionTargets.onSetupTable
3621            });
3622        }
3623        const tableCellClassList = filterNoneItem(getCellClassList(editor));
3624        if (tableCellClassList.length !== 0 && editor.queryCommandSupported('mceTableCellToggleClass')) {
3625            editor.ui.registry.addMenuButton('tablecellclass', {
3626                icon: 'table-cell-classes',
3627                tooltip: 'Cell styles',
3628                fetch: generateMenuItemsCallback(editor, tableCellClassList, 'tablecellclass', (value) => editor.execCommand('mceTableCellToggleClass', false, value)),
3629                onSetup: selectionTargets.onSetupCellOrRow
3630            });
3631        }
3632        // TODO: TINY-8172 Unwind this when an alternative solution is found
3633        if (editor.queryCommandSupported('mceTableApplyCellStyle')) {
3634            editor.ui.registry.addMenuButton('tablecellvalign', {
3635                icon: 'vertical-align',
3636                tooltip: 'Vertical align',
3637                fetch: generateMenuItemsCallback(editor, verticalAlignValues, 'tablecellverticalalign', applyTableCellStyle(editor, 'vertical-align')),
3638                onSetup: selectionTargets.onSetupCellOrRow
3639            });
3640            editor.ui.registry.addMenuButton('tablecellborderwidth', {
3641                icon: 'border-width',
3642                tooltip: 'Border width',
3643                fetch: generateMenuItemsCallback(editor, getTableBorderWidths(editor), 'tablecellborderwidth', applyTableCellStyle(editor, 'border-width')),
3644                onSetup: selectionTargets.onSetupCellOrRow
3645            });
3646            editor.ui.registry.addMenuButton('tablecellborderstyle', {
3647                icon: 'border-style',
3648                tooltip: 'Border style',
3649                fetch: generateMenuItemsCallback(editor, getTableBorderStyles(editor), 'tablecellborderstyle', applyTableCellStyle(editor, 'border-style')),
3650                onSetup: selectionTargets.onSetupCellOrRow
3651            });
3652            editor.ui.registry.addMenuButton('tablecellbackgroundcolor', {
3653                icon: 'cell-background-color',
3654                tooltip: 'Background color',
3655                fetch: (callback) => callback(buildColorMenu(editor, getTableBackgroundColorMap(editor), 'background-color')),
3656                onSetup: selectionTargets.onSetupCellOrRow
3657            });
3658            editor.ui.registry.addMenuButton('tablecellbordercolor', {
3659                icon: 'cell-border-color',
3660                tooltip: 'Border color',
3661                fetch: (callback) => callback(buildColorMenu(editor, getTableBorderColorMap(editor), 'border-color')),
3662                onSetup: selectionTargets.onSetupCellOrRow
3663            });
3664        }
3665        addToggleButtonIfRegistered('tablecaption', {
3666            tooltip: 'Table caption',
3667            icon: 'table-caption',
3668            command: 'mceTableToggleCaption',
3669            onSetup: selectionTargets.onSetupTableWithCaption
3670        });
3671        addToggleButtonIfRegistered('tablerowheader', {
3672            tooltip: 'Row header',
3673            icon: 'table-top-header',
3674            command: 'mceTableRowType',
3675            onAction: changeRowHeader(editor),
3676            onSetup: selectionTargets.onSetupTableRowHeaders
3677        });
3678        addToggleButtonIfRegistered('tablecolheader', {
3679            tooltip: 'Column header',
3680            icon: 'table-left-header',
3681            command: 'mceTableColType',
3682            onAction: changeColumnHeader(editor),
3683            onSetup: selectionTargets.onSetupTableColumnHeaders
3684        });
3685    };
3686    const addToolbars = (editor) => {
3687        const isEditableTable = (table) => editor.dom.is(table, 'table') && editor.getBody().contains(table) && editor.dom.isEditable(table.parentNode);
3688        const toolbar = getToolbar(editor);
3689        if (toolbar.length > 0) {
3690            editor.ui.registry.addContextToolbar('table', {
3691                predicate: isEditableTable,
3692                items: toolbar,
3693                scope: 'node',
3694                position: 'node'
3695            });
3696        }
3697    };
3698
3699    const onSetupEditable = (editor) => (api) => {
3700        const nodeChanged = () => {
3701            api.setEnabled(editor.selection.isEditable());
3702        };
3703        editor.on('NodeChange', nodeChanged);
3704        nodeChanged();
3705        return () => {
3706            editor.off('NodeChange', nodeChanged);
3707        };
3708    };
3709    const addMenuItems = (editor, selectionTargets) => {
3710        const cmd = (command) => () => editor.execCommand(command);
3711        // TODO: TINY-8172 Unwind this when an alternative solution is found
3712        const addMenuIfRegistered = (name, spec) => {
3713            if (editor.queryCommandSupported(spec.command)) {
3714                editor.ui.registry.addMenuItem(name, {
3715                    ...spec,
3716                    onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
3717                });
3718                return true;
3719            }
3720            else {
3721                return false;
3722            }
3723        };
3724        // TODO: TINY-8172 Unwind this when an alternative solution is found
3725        const addToggleMenuIfRegistered = (name, spec) => {
3726            if (editor.queryCommandSupported(spec.command)) {
3727                editor.ui.registry.addToggleMenuItem(name, {
3728                    ...spec,
3729                    onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
3730                });
3731            }
3732        };
3733        const insertTableAction = (data) => {
3734            editor.execCommand('mceInsertTable', false, {
3735                rows: data.numRows,
3736                columns: data.numColumns
3737            });
3738        };
3739        const hasRowMenuItems = [
3740            addMenuIfRegistered('tableinsertrowbefore', {
3741                text: 'Insert row before',
3742                icon: 'table-insert-row-above',
3743                command: 'mceTableInsertRowBefore',
3744                onSetup: selectionTargets.onSetupCellOrRow
3745            }),
3746            addMenuIfRegistered('tableinsertrowafter', {
3747                text: 'Insert row after',
3748                icon: 'table-insert-row-after',
3749                command: 'mceTableInsertRowAfter',
3750                onSetup: selectionTargets.onSetupCellOrRow
3751            }),
3752            addMenuIfRegistered('tabledeleterow', {
3753                text: 'Delete row',
3754                icon: 'table-delete-row',
3755                command: 'mceTableDeleteRow',
3756                onSetup: selectionTargets.onSetupCellOrRow
3757            }),
3758            addMenuIfRegistered('tablerowprops', {
3759                text: 'Row properties',
3760                icon: 'table-row-properties',
3761                command: 'mceTableRowProps',
3762                onSetup: selectionTargets.onSetupCellOrRow
3763            }),
3764            addMenuIfRegistered('tablecutrow', {
3765                text: 'Cut row',
3766                icon: 'cut-row',
3767                command: 'mceTableCutRow',
3768                onSetup: selectionTargets.onSetupCellOrRow
3769            }),
3770            addMenuIfRegistered('tablecopyrow', {
3771                text: 'Copy row',
3772                icon: 'duplicate-row',
3773                command: 'mceTableCopyRow',
3774                onSetup: selectionTargets.onSetupCellOrRow
3775            }),
3776            addMenuIfRegistered('tablepasterowbefore', {
3777                text: 'Paste row before',
3778                icon: 'paste-row-before',
3779                command: 'mceTablePasteRowBefore',
3780                onSetup: selectionTargets.onSetupPasteable(getRows)
3781            }),
3782            addMenuIfRegistered('tablepasterowafter', {
3783                text: 'Paste row after',
3784                icon: 'paste-row-after',
3785                command: 'mceTablePasteRowAfter',
3786                onSetup: selectionTargets.onSetupPasteable(getRows)
3787            }),
3788        ];
3789        const hasColumnMenuItems = [
3790            addMenuIfRegistered('tableinsertcolumnbefore', {
3791                text: 'Insert column before',
3792                icon: 'table-insert-column-before',
3793                command: 'mceTableInsertColBefore',
3794                onSetup: selectionTargets.onSetupColumn("onFirst" /* LockedDisable.onFirst */)
3795            }),
3796            addMenuIfRegistered('tableinsertcolumnafter', {
3797                text: 'Insert column after',
3798                icon: 'table-insert-column-after',
3799                command: 'mceTableInsertColAfter',
3800                onSetup: selectionTargets.onSetupColumn("onLast" /* LockedDisable.onLast */)
3801            }),
3802            addMenuIfRegistered('tabledeletecolumn', {
3803                text: 'Delete column',
3804                icon: 'table-delete-column',
3805                command: 'mceTableDeleteCol',
3806                onSetup: selectionTargets.onSetupColumn("onAny" /* LockedDisable.onAny */)
3807            }),
3808            addMenuIfRegistered('tablecutcolumn', {
3809                text: 'Cut column',
3810                icon: 'cut-column',
3811                command: 'mceTableCutCol',
3812                onSetup: selectionTargets.onSetupColumn("onAny" /* LockedDisable.onAny */)
3813            }),
3814            addMenuIfRegistered('tablecopycolumn', {
3815                text: 'Copy column',
3816                icon: 'duplicate-column',
3817                command: 'mceTableCopyCol',
3818                onSetup: selectionTargets.onSetupColumn("onAny" /* LockedDisable.onAny */)
3819            }),
3820            addMenuIfRegistered('tablepastecolumnbefore', {
3821                text: 'Paste column before',
3822                icon: 'paste-column-before',
3823                command: 'mceTablePasteColBefore',
3824                onSetup: selectionTargets.onSetupPasteableColumn(getColumns, "onFirst" /* LockedDisable.onFirst */)
3825            }),
3826            addMenuIfRegistered('tablepastecolumnafter', {
3827                text: 'Paste column after',
3828                icon: 'paste-column-after',
3829                command: 'mceTablePasteColAfter',
3830                onSetup: selectionTargets.onSetupPasteableColumn(getColumns, "onLast" /* LockedDisable.onLast */)
3831            }),
3832        ];
3833        const hasCellMenuItems = [
3834            addMenuIfRegistered('tablecellprops', {
3835                text: 'Cell properties',
3836                icon: 'table-cell-properties',
3837                command: 'mceTableCellProps',
3838                onSetup: selectionTargets.onSetupCellOrRow
3839            }),
3840            addMenuIfRegistered('tablemergecells', {
3841                text: 'Merge cells',
3842                icon: 'table-merge-cells',
3843                command: 'mceTableMergeCells',
3844                onSetup: selectionTargets.onSetupMergeable
3845            }),
3846            addMenuIfRegistered('tablesplitcells', {
3847                text: 'Split cell',
3848                icon: 'table-split-cells',
3849                command: 'mceTableSplitCells',
3850                onSetup: selectionTargets.onSetupUnmergeable
3851            }),
3852        ];
3853        if (!hasTableGrid(editor)) {
3854            editor.ui.registry.addMenuItem('inserttable', {
3855                text: 'Table',
3856                icon: 'table',
3857                onAction: cmd('mceInsertTableDialog'),
3858                onSetup: onSetupEditable(editor)
3859            });
3860        }
3861        else {
3862            editor.ui.registry.addNestedMenuItem('inserttable', {
3863                text: 'Table',
3864                icon: 'table',
3865                getSubmenuItems: () => [{ type: 'fancymenuitem', fancytype: 'inserttable', onAction: insertTableAction }],
3866                onSetup: onSetupEditable(editor)
3867            });
3868        }
3869        // TINY-3636: We want a way to use the dialog even when tablegrid true.
3870        // If tablegrid false then inserttable and inserttabledialog are the same,
3871        // but that's preferrable to breaking things at this point.
3872        editor.ui.registry.addMenuItem('inserttabledialog', {
3873            text: 'Insert table',
3874            icon: 'table',
3875            onAction: cmd('mceInsertTableDialog'),
3876            onSetup: onSetupEditable(editor)
3877        });
3878        addMenuIfRegistered('tableprops', {
3879            text: 'Table properties',
3880            onSetup: selectionTargets.onSetupTable,
3881            command: 'mceTableProps'
3882        });
3883        addMenuIfRegistered('deletetable', {
3884            text: 'Delete table',
3885            icon: 'table-delete-table',
3886            onSetup: selectionTargets.onSetupTable,
3887            command: 'mceTableDelete'
3888        });
3889        // if any of the row menu items returned true
3890        if (contains(hasRowMenuItems, true)) {
3891            editor.ui.registry.addNestedMenuItem('row', {
3892                type: 'nestedmenuitem',
3893                text: 'Row',
3894                getSubmenuItems: constant('tableinsertrowbefore tableinsertrowafter tabledeleterow tablerowprops | tablecutrow tablecopyrow tablepasterowbefore tablepasterowafter')
3895            });
3896        }
3897        if (contains(hasColumnMenuItems, true)) {
3898            editor.ui.registry.addNestedMenuItem('column', {
3899                type: 'nestedmenuitem',
3900                text: 'Column',
3901                getSubmenuItems: constant('tableinsertcolumnbefore tableinsertcolumnafter tabledeletecolumn | tablecutcolumn tablecopycolumn tablepastecolumnbefore tablepastecolumnafter')
3902            });
3903        }
3904        if (contains(hasCellMenuItems, true)) {
3905            editor.ui.registry.addNestedMenuItem('cell', {
3906                type: 'nestedmenuitem',
3907                text: 'Cell',
3908                getSubmenuItems: constant('tablecellprops tablemergecells tablesplitcells')
3909            });
3910        }
3911        editor.ui.registry.addContextMenu('table', {
3912            update: () => {
3913                // context menu fires before node change, so check the selection here first
3914                selectionTargets.resetTargets();
3915                // ignoring element since it's monitored elsewhere
3916                return selectionTargets.targets().fold(constant(''), (targets) => {
3917                    // If clicking in a caption, then we shouldn't show the cell/row/column options
3918                    if (name(targets.element) === 'caption') {
3919                        return 'tableprops deletetable';
3920                    }
3921                    else {
3922                        return 'cell row column | advtablesort | tableprops deletetable';
3923                    }
3924                });
3925            }
3926        });
3927        const tableClassList = filterNoneItem(getTableClassList(editor));
3928        if (tableClassList.length !== 0 && editor.queryCommandSupported('mceTableToggleClass')) {
3929            editor.ui.registry.addNestedMenuItem('tableclass', {
3930                icon: 'table-classes',
3931                text: 'Table styles',
3932                getSubmenuItems: () => buildMenuItems(editor, tableClassList, 'tableclass', (value) => editor.execCommand('mceTableToggleClass', false, value)),
3933                onSetup: selectionTargets.onSetupTable
3934            });
3935        }
3936        const tableCellClassList = filterNoneItem(getCellClassList(editor));
3937        if (tableCellClassList.length !== 0 && editor.queryCommandSupported('mceTableCellToggleClass')) {
3938            editor.ui.registry.addNestedMenuItem('tablecellclass', {
3939                icon: 'table-cell-classes',
3940                text: 'Cell styles',
3941                getSubmenuItems: () => buildMenuItems(editor, tableCellClassList, 'tablecellclass', (value) => editor.execCommand('mceTableCellToggleClass', false, value)),
3942                onSetup: selectionTargets.onSetupCellOrRow
3943            });
3944        }
3945        // TODO: TINY-8172 Unwind this when an alternative solution is found
3946        if (editor.queryCommandSupported('mceTableApplyCellStyle')) {
3947            editor.ui.registry.addNestedMenuItem('tablecellvalign', {
3948                icon: 'vertical-align',
3949                text: 'Vertical align',
3950                getSubmenuItems: () => buildMenuItems(editor, verticalAlignValues, 'tablecellverticalalign', applyTableCellStyle(editor, 'vertical-align')),
3951                onSetup: selectionTargets.onSetupCellOrRow
3952            });
3953            editor.ui.registry.addNestedMenuItem('tablecellborderwidth', {
3954                icon: 'border-width',
3955                text: 'Border width',
3956                getSubmenuItems: () => buildMenuItems(editor, getTableBorderWidths(editor), 'tablecellborderwidth', applyTableCellStyle(editor, 'border-width')),
3957                onSetup: selectionTargets.onSetupCellOrRow
3958            });
3959            editor.ui.registry.addNestedMenuItem('tablecellborderstyle', {
3960                icon: 'border-style',
3961                text: 'Border style',
3962                getSubmenuItems: () => buildMenuItems(editor, getTableBorderStyles(editor), 'tablecellborderstyle', applyTableCellStyle(editor, 'border-style')),
3963                onSetup: selectionTargets.onSetupCellOrRow
3964            });
3965            editor.ui.registry.addNestedMenuItem('tablecellbackgroundcolor', {
3966                icon: 'cell-background-color',
3967                text: 'Background color',
3968                getSubmenuItems: () => buildColorMenu(editor, getTableBackgroundColorMap(editor), 'background-color'),
3969                onSetup: selectionTargets.onSetupCellOrRow
3970            });
3971            editor.ui.registry.addNestedMenuItem('tablecellbordercolor', {
3972                icon: 'cell-border-color',
3973                text: 'Border color',
3974                getSubmenuItems: () => buildColorMenu(editor, getTableBorderColorMap(editor), 'border-color'),
3975                onSetup: selectionTargets.onSetupCellOrRow
3976            });
3977        }
3978        addToggleMenuIfRegistered('tablecaption', {
3979            icon: 'table-caption',
3980            text: 'Table caption',
3981            command: 'mceTableToggleCaption',
3982            onSetup: selectionTargets.onSetupTableWithCaption
3983        });
3984        addToggleMenuIfRegistered('tablerowheader', {
3985            text: 'Row header',
3986            icon: 'table-top-header',
3987            command: 'mceTableRowType',
3988            onAction: changeRowHeader(editor),
3989            onSetup: selectionTargets.onSetupTableRowHeaders
3990        });
3991        addToggleMenuIfRegistered('tablecolheader', {
3992            text: 'Column header',
3993            icon: 'table-left-header',
3994            command: 'mceTableColType',
3995            onAction: changeColumnHeader(editor),
3996            onSetup: selectionTargets.onSetupTableRowHeaders
3997        });
3998    };
3999
4000    const Plugin = (editor) => {
4001        const selectionTargets = getSelectionTargets(editor);
4002        register(editor);
4003        registerCommands(editor);
4004        addMenuItems(editor, selectionTargets);
4005        addButtons(editor, selectionTargets);
4006        addToolbars(editor);
4007    };
4008    var Plugin$1 = () => {
4009        global$3.add('table', Plugin);
4010    };
4011
4012    Plugin$1();
4013    /** *****
4014     * DO NOT EXPORT ANYTHING
4015     *
4016     * IF YOU DO ROLLUP WILL LEAVE A GLOBAL ON THE PAGE
4017     *******/
4018
4019})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.