PageSourceSearch

https://eredaktor.no/_content/ExireNexus.Backstage/lib/tinymce/themes/silver/theme.oee66qcb6q.js

js eredaktor.no collected 2026-10-09 05:02:40 UTC 1,591,150 bytes, 36,042 lines download raw bytes

vendor: 3,077 bytes, lines 1-98
1/**
2 * TinyMCE version 8.8.2 (2026-07-27)
3 */
4
5(function () {
6    'use strict';
7
8    /* eslint-disable @typescript-eslint/no-wrapper-object-types */
9    const getPrototypeOf$2 = Object.getPrototypeOf;
10    const hasProto = (v, constructor, predicate) => {
11        if (predicate(v, constructor.prototype)) {
12            return true;
13        }
14        else {
15            // String-based fallback time
16            return v.constructor?.name === constructor.name;
17        }
18    };
19    const typeOf = (x) => {
20        const t = typeof x;
21        if (x === null) {
22            return 'null';
23        }
24        else if (t === 'object' && Array.isArray(x)) {
25            return 'array';
26        }
27        else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
28            return 'string';
29        }
30        else {
31            return t;
32        }
33    };
34    const isType$1 = (type) => (value) => typeOf(value) === type;
35    const isSimpleType = (type) => (value) => typeof value === type;
36    const eq$1 = (t) => (a) => t === a;
37    const is$2 = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf$2(o) === proto);
38    const isString = isType$1('string');
39    const isObject = isType$1('object');
40    const isPlainObject = (value) => is$2(value, Object);
41    const isArray = isType$1('array');
42    const isNull = eq$1(null);
43    const isBoolean = isSimpleType('boolean');
44    const isUndefined = eq$1(undefined);
45    const isNullable = (a) => a === null || a === undefined;
46    const isNonNullable = (a) => !isNullable(a);
47    const isFunction = isSimpleType('function');
48    const isNumber = isSimpleType('number');
49    const isArrayOf = (value, pred) => {
50        if (isArray(value)) {
51            for (let i = 0, len = value.length; i < len; ++i) {
52                if (!(pred(value[i]))) {
53                    return false;
54                }
55            }
56            return true;
57        }
58        return false;
59    };
60
61    const noop = () => { };
62    const noarg = (f) => () => f();
63    /** Compose a unary function with an n-ary function */
64    const compose = (fa, fb) => {
65        return (...args) => {
66            return fa(fb.apply(null, args));
67        };
68    };
69    /** Compose two unary functions. Similar to compose, but avoids using Function.prototype.apply. */
70    const compose1 = (fbc, fab) => (a) => fbc(fab(a));
71    const constant$1 = (value) => {
72        return () => {
73            return value;
74        };
75    };
76    const identity = (x) => {
77        return x;
78    };
79    const tripleEquals = (a, b) => {
80        return a === b;
81    };
82    function curry(fn, ...initialArgs) {
83        return (...restArgs) => {
84            const all = initialArgs.concat(restArgs);
85            return fn.apply(null, all);
86        };
87    }
88    const not = (f) => (t) => !f(t);
89    const die = (msg) => {
90        return () => {
91            throw new Error(msg);
92        };
93    };
94    const apply$1 = (f) => {
95        return f();
96    };
97    const never = constant$1(false);
98    const always = constant$1
vendor: 10,618 bytes, lines 98-357
98(true);
99
100    /**
101     * The `Optional` type represents a value (of any type) that potentially does
102     * not exist. Any `Optional<T>` can either be a `Some<T>` (in which case the
103     * value does exist) or a `None` (in which case the value does not exist). This
104     * module defines a whole lot of FP-inspired utility functions for dealing with
105     * `Optional` objects.
106     *
107     * Comparison with null or undefined:
108     * - We don't get fancy null coalescing operators with `Optional`
109     * - We do get fancy helper functions with `Optional`
110     * - `Optional` support nesting, and allow for the type to still be nullable (or
111     * another `Optional`)
112     * - There is no option to turn off strict-optional-checks like there is for
113     * strict-null-checks
114     */
115    class Optional {
116        tag;
117        value;
118        // Sneaky optimisation: every instance of Optional.none is identical, so just
119        // reuse the same object
120        static singletonNone = new Optional(false);
121        // The internal representation has a `tag` and a `value`, but both are
122        // private: able to be console.logged, but not able to be accessed by code
123        constructor(tag, value) {
124            this.tag = tag;
125            this.value = value;
126        }
127        // --- Identities ---
128        /**
129         * Creates a new `Optional<T>` that **does** contain a value.
130         */
131        static some(value) {
132            return new Optional(true, value);
133        }
134        /**
135         * Create a new `Optional<T>` that **does not** contain a value. `T` can be
136         * any type because we don't actually have a `T`.
137         */
138        static none() {
139            return Optional.singletonNone;
140        }
141        /**
142         * Perform a transform on an `Optional` type. Regardless of whether this
143         * `Optional` contains a value or not, `fold` will return a value of type `U`.
144         * If this `Optional` does not contain a value, the `U` will be created by
145         * calling `onNone`. If this `Optional` does contain a value, the `U` will be
146         * created by calling `onSome`.
147         *
148         * For the FP enthusiasts in the room, this function:
149         * 1. Could be used to implement all of the functions below
150         * 2. Forms a catamorphism
151         */
152        fold(onNone, onSome) {
153            if (this.tag) {
154                return onSome(this.value);
155            }
156            else {
157                return onNone();
158            }
159        }
160        /**
161         * Determine if this `Optional` object contains a value.
162         */
163        isSome() {
164            return this.tag;
165        }
166        /**
167         * Determine if this `Optional` object **does not** contain a value.
168         */
169        isNone() {
170            return !this.tag;
171        }
172        // --- Functor (name stolen from Haskell / maths) ---
173        /**
174         * Perform a transform on an `Optional` object, **if** there is a value. If
175         * you provide a function to turn a T into a U, this is the function you use
176         * to turn an `Optional<T>` into an `Optional<U>`. If this **does** contain
177         * a value then the output will also contain a value (that value being the
178         * output of `mapper(this.value)`), and if this **does not** contain a value
179         * then neither will the output.
180         */
181        map(mapper) {
182            if (this.tag) {
183                return Optional.some(mapper(this.value));
184            }
185            else {
186                return Optional.none();
187            }
188        }
189        // --- Monad (name stolen from Haskell / maths) ---
190        /**
191         * Perform a transform on an `Optional` object, **if** there is a value.
192         * Unlike `map`, here the transform itself also returns an `Optional`.
193         */
194        bind(binder) {
195            if (this.tag) {
196                return binder(this.value);
197            }
198            else {
199                return Optional.none();
200            }
201        }
202        // --- Traversable (name stolen from Haskell / maths) ---
203        /**
204         * For a given predicate, this function finds out if there **exists** a value
205         * inside this `Optional` object that meets the predicate. In practice, this
206         * means that for `Optional`s that do not contain a value it returns false (as
207         * no predicate-meeting value exists).
208         */
209        exists(predicate) {
210            return this.tag && predicate(this.value);
211        }
212        /**
213         * For a given predicate, this function finds out if **all** the values inside
214         * this `Optional` object meet the predicate. In practice, this means that
215         * for `Optional`s that do not contain a value it returns true (as all 0
216         * objects do meet the predicate).
217         */
218        forall(predicate) {
219            return !this.tag || predicate(this.value);
220        }
221        filter(predicate) {
222            if (!this.tag || predicate(this.value)) {
223                return this;
224            }
225            else {
226                return Optional.none();
227            }
228        }
229        // --- Getters ---
230        /**
231         * Get the value out of the inside of the `Optional` object, using a default
232         * `replacement` value if the provided `Optional` object does not contain a
233         * value.
234         */
235        getOr(replacement) {
236            return this.tag ? this.value : replacement;
237        }
238        /**
239         * Get the value out of the inside of the `Optional` object, using a default
240         * `replacement` value if the provided `Optional` object does not contain a
241         * value.  Unlike `getOr`, in this method the `replacement` object is also
242         * `Optional` - meaning that this method will always return an `Optional`.
243         */
244        or(replacement) {
245            return this.tag ? this : replacement;
246        }
247        /**
248         * Get the value out of the inside of the `Optional` object, using a default
249         * `replacement` value if the provided `Optional` object does not contain a
250         * value. Unlike `getOr`, in this method the `replacement` value is
251         * "thunked" - that is to say that you don't pass a value to `getOrThunk`, you
252         * pass a function which (if called) will **return** the `value` you want to
253         * use.
254         */
255        getOrThunk(thunk) {
256            return this.tag ? this.value : thunk();
257        }
258        /**
259         * Get the value out of the inside of the `Optional` object, using a default
260         * `replacement` value if the provided Optional object does not contain a
261         * value.
262         *
263         * Unlike `or`, in this method the `replacement` value is "thunked" - that is
264         * to say that you don't pass a value to `orThunk`, you pass a function which
265         * (if called) will **return** the `value` you want to use.
266         *
267         * Unlike `getOrThunk`, in this method the `replacement` value is also
268         * `Optional`, meaning that this method will always return an `Optional`.
269         */
270        orThunk(thunk) {
271            return this.tag ? this : thunk();
272        }
273        /**
274         * Get the value out of the inside of the `Optional` object, throwing an
275         * exception if the provided `Optional` object does not contain a value.
276         *
277         * WARNING:
278         * You should only be using this function if you know that the `Optional`
279         * object **is not** empty (otherwise you're throwing exceptions in production
280         * code, which is bad).
281         *
282         * In tests this is more acceptable.
283         *
284         * Prefer other methods to this, such as `.each`.
285         */
286        getOrDie(message) {
287            if (!this.tag) {
288                throw new Error(message ?? 'Called getOrDie on None');
289            }
290            else {
291                return this.value;
292            }
293        }
294        // --- Interop with null and undefined ---
295        /**
296         * Creates an `Optional` value from a nullable (or undefined-able) input.
297         * Null, or undefined, is converted to `None`, and anything else is converted
298         * to `Some`.
299         */
300        static from(value) {
301            return isNonNullable(value) ? Optional.some(value) : Optional.none();
302        }
303        /**
304         * Converts an `Optional` to a nullable type, by getting the value if it
305         * exists, or returning `null` if it does not.
306         */
307        getOrNull() {
308            return this.tag ? this.value : null;
309        }
310        /**
311         * Converts an `Optional` to an undefined-able type, by getting the value if
312         * it exists, or returning `undefined` if it does not.
313         */
314        getOrUndefined() {
315            return this.value;
316        }
317        // --- Utilities ---
318        /**
319         * If the `Optional` contains a value, perform an action on that value.
320         * Unlike the rest of the methods on this type, `.each` has side-effects. If
321         * you want to transform an `Optional<T>` **into** something, then this is not
322         * the method for you. If you want to use an `Optional<T>` to **do**
323         * something, then this is the method for you - provided you're okay with not
324         * doing anything in the case where the `Optional` doesn't have a value inside
325         * it. If you're not sure whether your use-case fits into transforming
326         * **into** something or **doing** something, check whether it has a return
327         * value. If it does, you should be performing a transform.
328         */
329        each(worker) {
330            if (this.tag) {
331                worker(this.value);
332            }
333        }
334        /**
335         * Turn the `Optional` object into an array that contains all of the values
336         * stored inside the `Optional`. In practice, this means the output will have
337         * either 0 or 1 elements.
338         */
339        toArray() {
340            return this.tag ? [this.value] : [];
341        }
342        /**
343         * Turn the `Optional` object into a string for debugging or printing. Not
344         * recommended for production code, but good for debugging. Also note that
345         * these days an `Optional` object can be logged to the console directly, and
346         * its inner value (if it exists) will be visible.
347         */
348        toString() {
349            return this.tag ? `some(${this.value})` : 'none()';
350        }
351    }
352
353    const nativeSlice = Array.prototype.slice;
354    const nativeIndexOf = Array.prototype.indexOf;
355    const nativePush = Array.prototype.push;
356    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
357    const indexOf
vendor: 3,266 bytes, lines 357-462
357 = (xs, x) => {
358        // The rawIndexOf method does not wrap up in an option. This is for performance reasons.
359        const r = rawIndexOf(xs, x);
360        return r === -1 ? Optional.none() : Optional.some(r);
361    };
362    const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1;
363    const exists = (xs, pred) => {
364        for (let i = 0, len = xs.length; i < len; i++) {
365            const x = xs[i];
366            if (pred(x, i)) {
367                return true;
368            }
369        }
370        return false;
371    };
372    const range$2 = (num, f) => {
373        const r = [];
374        for (let i = 0; i < num; i++) {
375            r.push(f(i));
376        }
377        return r;
378    };
379    // It's a total micro optimisation, but these do make some difference.
380    // Particularly for browsers other than Chrome.
381    // - length caching
382    // http://jsperf.com/browser-diet-jquery-each-vs-for-loop/69
383    // - not using push
384    // http://jsperf.com/array-direct-assignment-vs-push/2
385    const chunk$1 = (array, size) => {
386        const r = [];
387        for (let i = 0; i < array.length; i += size) {
388            const s = nativeSlice.call(array, i, i + size);
389            r.push(s);
390        }
391        return r;
392    };
393    const map$2 = (xs, f) => {
394        // pre-allocating array size when it's guaranteed to be known
395        // http://jsperf.com/push-allocated-vs-dynamic/22
396        const len = xs.length;
397        const r = new Array(len);
398        for (let i = 0; i < len; i++) {
399            const x = xs[i];
400            r[i] = f(x, i);
401        }
402        return r;
403    };
404    // Unwound implementing other functions in terms of each.
405    // The code size is roughly the same, and it should allow for better optimisation.
406    // const each = function<T, U>(xs: T[], f: (x: T, i?: number, xs?: T[]) => void): void {
407    const each$1 = (xs, f) => {
408        for (let i = 0, len = xs.length; i < len; i++) {
409            const x = xs[i];
410            f(x, i);
411        }
412    };
413    const eachr = (xs, f) => {
414        for (let i = xs.length - 1; i >= 0; i--) {
415            const x = xs[i];
416            f(x, i);
417        }
418    };
419    const partition$3 = (xs, pred) => {
420        const pass = [];
421        const fail = [];
422        for (let i = 0, len = xs.length; i < len; i++) {
423            const x = xs[i];
424            const arr = pred(x, i) ? pass : fail;
425            arr.push(x);
426        }
427        return { pass, fail };
428    };
429    const filter$2 = (xs, pred) => {
430        const r = [];
431        for (let i = 0, len = xs.length; i < len; i++) {
432            const x = xs[i];
433            if (pred(x, i)) {
434                r.push(x);
435            }
436        }
437        return r;
438    };
439    const foldr = (xs, f, acc) => {
440        eachr(xs, (x, i) => {
441            acc = f(acc, x, i);
442        });
443        return acc;
444    };
445    const foldl = (xs, f, acc) => {
446        each$1(xs, (x, i) => {
447            acc = f(acc, x, i);
448        });
449        return acc;
450    };
451    const findUntil = (xs, pred, until) => {
452        for (let i = 0, len = xs.length; i < len; i++) {
453            const x = xs[i];
454            if (pred(x, i)) {
455                return Optional.some(x);
456            }
457            else if (until(x, i)) {
458                break;
459            }
460        }
461        return Optional.none();
462    };
vendor: 5,749 bytes, lines 463-636
463    const find$5 = (xs, pred) => {
464        return findUntil(xs, pred, never);
465    };
466    const findIndex$1 = (xs, pred) => {
467        for (let i = 0, len = xs.length; i < len; i++) {
468            const x = xs[i];
469            if (pred(x, i)) {
470                return Optional.some(i);
471            }
472        }
473        return Optional.none();
474    };
475    const flatten = (xs) => {
476        // Note, this is possible because push supports multiple arguments:
477        // http://jsperf.com/concat-push/6
478        // Note that in the past, concat() would silently work (very slowly) for array-like objects.
479        // With this change it will throw an error.
480        const r = [];
481        for (let i = 0, len = xs.length; i < len; ++i) {
482            // Ensure that each value is an array itself
483            if (!isArray(xs[i])) {
484                throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
485            }
486            nativePush.apply(r, xs[i]);
487        }
488        return r;
489    };
490    const bind$3 = (xs, f) => flatten(map$2(xs, f));
491    const forall = (xs, pred) => {
492        for (let i = 0, len = xs.length; i < len; ++i) {
493            const x = xs[i];
494            if (pred(x, i) !== true) {
495                return false;
496            }
497        }
498        return true;
499    };
500    const reverse = (xs) => {
501        const r = nativeSlice.call(xs, 0);
502        r.reverse();
503        return r;
504    };
505    const difference = (a1, a2) => filter$2(a1, (x) => !contains$2(a2, x));
506    const mapToObject = (xs, f) => {
507        const r = {};
508        for (let i = 0, len = xs.length; i < len; i++) {
509            const x = xs[i];
510            r[String(x)] = f(x, i);
511        }
512        return r;
513    };
514    const pure$2 = (x) => [x];
515    const sort = (xs, comparator) => {
516        const copy = nativeSlice.call(xs, 0);
517        copy.sort(comparator);
518        return copy;
519    };
520    const get$i = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
521    const head = (xs) => get$i(xs, 0);
522    const last$1 = (xs) => get$i(xs, xs.length - 1);
523    const from = isFunction(Array.from) ? Array.from : (x) => nativeSlice.call(x);
524    const findMap = (arr, f) => {
525        for (let i = 0; i < arr.length; i++) {
526            const r = f(arr[i], i);
527            if (r.isSome()) {
528                return r;
529            }
530        }
531        return Optional.none();
532    };
533
534    // There are many variations of Object iteration that are faster than the 'for-in' style:
535    // http://jsperf.com/object-keys-iteration/107
536    //
537    // Use the native keys if it is available (IE9+), otherwise fall back to manually filtering
538    const keys = Object.keys;
539    const hasOwnProperty = Object.hasOwnProperty;
540    const each = (obj, f) => {
541        const props = keys(obj);
542        for (let k = 0, len = props.length; k < len; k++) {
543            const i = props[k];
544            const x = obj[i];
545            f(x, i);
546        }
547    };
548    const map$1 = (obj, f) => {
549        return tupleMap(obj, (x, i) => ({
550            k: i,
551            v: f(x, i)
552        }));
553    };
554    const tupleMap = (obj, f) => {
555        const r = {};
556        each(obj, (x, i) => {
557            const tuple = f(x, i);
558            r[tuple.k] = tuple.v;
559        });
560        return r;
561    };
562    const objAcc = (r) => (x, i) => {
563        r[i] = x;
564    };
565    const internalFilter = (obj, pred, onTrue, onFalse) => {
566        each(obj, (x, i) => {
567            (pred(x, i) ? onTrue : onFalse)(x, i);
568        });
569    };
570    const bifilter = (obj, pred) => {
571        const t = {};
572        const f = {};
573        internalFilter(obj, pred, objAcc(t), objAcc(f));
574        return { t, f };
575    };
576    const filter$1 = (obj, pred) => {
577        const t = {};
578        internalFilter(obj, pred, objAcc(t), noop);
579        return t;
580    };
581    const mapToArray = (obj, f) => {
582        const r = [];
583        each(obj, (value, name) => {
584            r.push(f(value, name));
585        });
586        return r;
587    };
588    const find$4 = (obj, pred) => {
589        const props = keys(obj);
590        for (let k = 0, len = props.length; k < len; k++) {
591            const i = props[k];
592            const x = obj[i];
593            if (pred(x, i, obj)) {
594                return Optional.some(x);
595            }
596        }
597        return Optional.none();
598    };
599    const values = (obj) => {
600        return mapToArray(obj, identity);
601    };
602    const get$h = (obj, key) => {
603        return has$2(obj, key) ? Optional.from(obj[key]) : Optional.none();
604    };
605    const has$2 = (obj, key) => hasOwnProperty.call(obj, key);
606    const hasNonNullableKey = (obj, key) => has$2(obj, key) && obj[key] !== undefined && obj[key] !== null;
607
608    /*
609     * Generates a church encoded ADT (https://en.wikipedia.org/wiki/Church_encoding)
610     * For syntax and use, look at the test code.
611     */
612    const generate$7 = (cases) => {
613        // validation
614        if (!isArray(cases)) {
615            throw new Error('cases must be an array');
616        }
617        if (cases.length === 0) {
618            throw new Error('there must be at least one case');
619        }
620        const constructors = [];
621        // adt is mutated to add the individual cases
622        const adt = {};
623        each$1(cases, (acase, count) => {
624            const keys$1 = keys(acase);
625            // validation
626            if (keys$1.length !== 1) {
627                throw new Error('one and only one name per case');
628            }
629            const key = keys$1[0];
630            const value = acase[key];
631            // validation
632            if (adt[key] !== undefined) {
633                throw new Error('duplicate key detected:' + key);
634            }
635            else if (key === 'cata') {
636                throw new Error('cannot have a case named cata (sorry
636)');
637            }
638            else if (!isArray(value)) {
639                // this implicitly checks if acase is an object
640                throw new Error('case arguments must be an array');
641            }
642            constructors.push(key);
643            //
644            // constructor for key
645            //
646            adt[key] = (...args) => {
647                const argLength = args.length;
648                // validation
649                if (argLength !== value.length) {
650                    throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);
651                }
652                const match = (branches) => {
653                    const branchKeys = keys(branches);
654                    if (constructors.length !== branchKeys.length) {
655                        throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\nActual: ' + branchKeys.join(','));
656                    }
657                    const allReqd = forall(constructors, (reqKey) => {
658                        return contains$2(branchKeys, reqKey);
659                    });
660                    if (!allReqd) {
661                        throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\nRequired: ' + constructors.join(', '));
662                    }
663                    return branches[key].apply(null, args);
664                };
665                //
666                // the fold function for key
667                //
668                return {
669                    fold: (...foldArgs) => {
670                        // runtime validation
671                        if (foldArgs.length !== cases.length) {
672                            throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + foldArgs.length);
673                        }
674                        const target = foldArgs[count];
675                        return target.apply(null, args);
676                    },
677                    match,
678                    // NOTE: Only for debugging.
679                    log: (label) => {
680                        // eslint-disable-next-line no-console
681                        console.log(label, {
682                            constructors,
683                            constructor: key,
684                            params: args
685                        });
686                    }
687                };
688            };
689        });
690        return adt;
691    };
692    const Adt = {
693        generate: generate$7
694    };
695
696    const Cell = (initial) => {
697        let value = initial;
698        const get = () => {
699            return value;
700        };
701        const set = (v) => {
702            value = v;
703        };
704        return {
705            get,
706            set
707        };
708    };
709
710    const nu$d = (baseFn) => {
711        let data = Optional.none();
712        let callbacks = [];
713        /** map :: this LazyValue a -> (a -> b) -> LazyValue b */
714        const map = (f) => nu$d((nCallback) => {
715            get((data) => {
716                nCallback(f(data));
717            });
718        });
719        const get = (nCallback) => {
720            if (isReady()) {
721                call(nCallback);
722            }
723            else {
724                callbacks.push(nCallback);
725            }
726        };
727        const set = (x) => {
728            if (!isReady()) {
729                data = Optional.some(x);
730                run(callbacks);
731                callbacks = [];
732            }
733        };
734        const isReady = () => data.isSome();
735        const run = (cbs) => {
736            each$1(cbs, call);
737        };
738        const call = (cb) => {
739            data.each((x) => {
740                setTimeout(() => {
741                    cb(x);
742                }, 0);
743            });
744        };
745        // Lazy values cache the value and kick off immediately
746        baseFn(set);
747        return {
748            get,
749            map,
750            isReady
751        };
752    };
753    const pure$1 = (a) => nu$d((callback) => {
754        callback(a);
755    });
756    const LazyValue = {
757        nu: nu$d,
758        pure: pure$1
759    };
760
761    const errorReporter = (err) => {
762        // we can not throw the error in the reporter as it will just be black-holed
763        // by the Promise so we use a setTimeout to escape the Promise.
764        setTimeout(() => {
765            throw err;
766        }, 0);
767    };
768    const make$8 = (run) => {
769        const get = (callback) => {
770            run().then(callback, errorReporter);
771        };
772        /** map :: this Future a -> (a -> b) -> Future b */
773        const map = (fab) => {
774            return make$8(() => run().then(fab));
775        };
776        /** bind :: this Future a -> (a -> Future b) -> Future b */
777        const bind = (aFutureB) => {
778            return make$8(() => run().then((v) =>
778 aFutureB(v).toPromise()));
779        };
780        /** anonBind :: this Future a -> Future b -> Future b
781         *  Returns a future, which evaluates the first future, ignores the result, then evaluates the second.
782         */
783        const anonBind = (futureB) => {
784            return make$8(() => run().then(() => futureB.toPromise()));
785        };
786        const toLazy = () => {
787            return LazyValue.nu(get);
788        };
789        const toCached = () => {
790            let cache = null;
791            return make$8(() => {
792                if (cache === null) {
793                    cache = run();
794                }
795                return cache;
796            });
797        };
798        const toPromise = run;
799        return {
800            map,
801            bind,
802            anonBind,
803            toLazy,
804            toCached,
805            toPromise,
806            get
807        };
808    };
809    const nu$c = (baseFn) => {
810        return make$8(() => new Promise(baseFn));
811    };
812    /** a -> Future a */
813    const pure = (a) => {
814        return make$8(() => Promise.resolve(a));
815    };
816    const Future = {
817        nu: nu$c,
818        pure
819    };
820
821    /**
822     * Creates a new `Result<T, E>` that **does** contain a value.
823     */
824    const value$4 = (value) => {
825        const applyHelper = (fn) => fn(value);
826        const constHelper = constant$1(value);
827        const outputHelper = () => output;
828        const output = {
829            // Debug info
830            tag: true,
831            inner: value,
832            // Actual Result methods
833            fold: (_onError, onValue) => onValue(value),
834            isValue: always,
835            isError: never,
836            map: (mapper) => Result.value(mapper(value)),
837            mapError: outputHelper,
838            bind: applyHelper,
839            exists: applyHelper,
840            forall: applyHelper,
841            getOr: constHelper,
842            or: outputHelper,
843            getOrThunk: constHelper,
844            orThunk: outputHelper,
845            getOrDie: constHelper,
846            each: (fn) => {
847                // Can't write the function inline because we don't want to return something by mistake
848                fn(value);
849            },
850            toOptional: () => Optional.some(value),
851        };
852        return output;
853    };
854    /**
855     * Creates a new `Result<T, E>` that **does not** contain a value, and therefore
856     * contains an error.
857     */
858    const error$1 = (error) => {
859        const outputHelper = () => output;
860        const output = {
861            // Debug info
862            tag: false,
863            inner: error,
864            // Actual Result methods
865            fold: (onError, _onValue) => onError(error),
866            isValue: never,
867            isError: always,
868            map: outputHelper,
869            mapError: (mapper) => Result.error(mapper(error)),
870            bind: outputHelper,
871            exists: never,
872            forall: always,
873            getOr: identity,
874            or: identity,
875            getOrThunk: apply$1,
876            orThunk: apply$1,
877            getOrDie: die(String(error)),
878            each: noop,
879            toOptional: Optional.none,
880        };
881        return output;
882    };
883    /**
884     * Creates a new `Result<T, E>` from an `Optional<T>` and an `E`. If the
885     * `Optional` contains a value, so will the outputted `Result`. If it does not,
886     * the outputted `Result` will contain an error (and that error will be the
887     * error passed in).
888     */
889    const fromOption = (optional, err) => optional.fold(() => error$1(err), value$4);
890    const Result = {
891        value: value$4,
892        error: error$1,
893        fromOption
894    };
895
896    const wrap$2 = (delegate) => {
897        const toCached = () => {
898            return wrap$2(delegate.toCached());
899        };
900        const bindFuture = (f) => {
901            return wrap$2(delegate.bind((resA) => resA.fold((err) => (Future.pure(Result.error(err))), (a) => f(a))));
902        };
903        const bindResult = (f) => {
904            return wrap$2(delegate.map((resA) => resA.bind(f)));
905        };
906        const mapResult = (f) => {
907            return wrap$2(delegate.map((resA) => resA.map(f)));
908        };
909        const mapError = (f) => {
910            return wrap$2(delegate.map((resA) => resA.mapError(f)));
911        };
912        const foldResult = (whenError, whenValue) => {
913            return delegate.map((res) => res.fold(whenError, whenValue));
914        };
915        const withTimeout = (timeout, errorThunk) => {
916            return wrap$2(Future.nu((callback) => {
917                let timedOut = false;
918                const timer = setTimeout(() => {
919                    timedOut = true;
920                    callback(Result.error(errorThunk()));
921                }, timeout);
922                delegate.get((result) => {
923                    if (!timedOut) {
924                        clearTimeout(timer);
925                        callback(result);
926                    }
927                });
928            }));
929        };
930        return {
931            ...delegate,
932            toCached,
933            bindFuture,
934            bindResult,
935            mapResult,
936            mapError,
937            foldResult,
938            withTimeout
939        };
940    };
941    const nu$b = (worker) => {
942        return wrap$2(Future.nu(worker));
943    };
944    const value$3 = (value) => {
945        return wrap$2(Future.pure(Result.value(value)));
946    };
947    const error = (error) => {
948        return wrap$2(Future.pure(Result.error(error)));
949    };
950    const fromResult$1 = (result) => {
951        return wrap$2(Future.pure(result));
952    };
953    const fromFuture = (future) => {
954        return wrap$2(future.map(Result.value));
955    };
956    const fromPromise = (promise) => {
957        return nu$b((completer) => {
958            promise.then((value) => {
959                completer(Result.value(value));
960            }, (error) => {
961                completer(Result.error(error));
962            });
963        });
964    };
965    const FutureResult = {
966        nu: nu$b,
967        wrap: wrap$2,
968        pure: value$3,
969        value: value$3,
970        error,
971        fromResult: fromResult$1,
972        fromFuture,
973        fromPromise
974    };
975
976    // Use window object as the global if it's available since CSP will block script evals
977    // eslint-disable-next-line @typescript-eslint/no-implied-eval
978    const Global = typeof window !== 'undefined' ? window : Function('return this;')();
979
980    /**
981     * Adds two numbers, and wrap to a range.
982     * If the result overflows to the right, snap to the left.
983     * If the result overflows to the left, snap to the right.
984     */
985    const cycleBy = (value, delta, min, max) => {
986        const r = value + delta;
987        if (r > max) {
988            return min;
989        }
990        else if (r < min) {
991            return max;
992        }
993        else {
994            return r;
995        }
996    };
997    // ASSUMPTION: Max will always be larger than min
998    const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
999    // the division is meant to get a number between 0 and 1 for more information check this discussion: https://stackoverflow.com/questions/58285941/how-to-replace-math-random-with-crypto-getrandomvalues-and-keep-same-result
1000    const random = () => window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;
1001
1002    /**
1003     * Generate a unique identifier.
1004     *
1005     * The unique portion of the identifier only contains an underscore
1006     * and digits, so that it may safely be used within HTML attributes.
1007     *
1008     * The chance of generating a non-unique identifier has been minimized
1009     * by combining the current time, a random number and a one-up counter.
1010     *
1011     * generate :: String -> String
1012     */
1013    let unique = 0;
1014    const generate$6 = (prefix) => {
1015        const date = new Date();
1016        const time = date.getTime();
1017        const random$1 = Math.floor(random() * 1000000000);
1018        unique++;
1019        return prefix + '_' + random$1 + unique + String(time);
1020    };
1021
1022    const shallow$1 = (old, nu) => {
1023        return nu;
1024    };
1025    const deep$1 = (old, nu) => {
1026        const bothObjects = isPlainObject(old) && isPlainObject(nu);
1027        return bothObjects ? deepMerge(old, nu) : nu;
1028    };
1029    const baseMerge = (merger) => {
1030        return (...objects) => {
1031            if (objects.length === 0) {
1032                throw new Error(`Can't merge zero objects`);
1033            }
1034            const ret = {};
1035            for (let j = 0; j < objects.length; j++) {
1036                const curObject = objects[j];
1037                for (const key in curObject) {
1038                    if (has$2(curObject, key)) {
1039                        ret[key] = merger(ret[key], curObject[key]);
1040                    }
1041                }
1042            }
1043            return ret;
1044        };
1045    };
1046    const deepMerge = baseMerge(deep$1);
1047    const merge$1 = baseMerge(shallow$1);
1048
1049    /**
1050     * **Is** the value stored inside this Optional object equal to `rhs`?
1051     */
1052    const is$1 = (lhs, rhs, comparator = tripleEquals) => lhs.exists((left) => comparator(left, rhs));
1053    /**
1054     * Are these two Optional objects equal? Equality here means either they're both
1055     * `Some` (and the values are equal under the comparator) or they're both `None`.
1056     */
1057    const equals = (lhs, rhs, comparator = tripleEquals) => lift2(lhs, rhs, comparator).getOr(lhs.isNone() && rhs.isNone());
vendor: 71,133 bytes, lines 1058-2905
1058    const cat = (arr) => {
1059        const r = [];
1060        const push = (x) => {
1061            r.push(x);
1062        };
1063        for (let i = 0; i < arr.length; i++) {
1064            arr[i].each(push);
1065        }
1066        return r;
1067    };
1068    const sequence = (arr) => {
1069        const r = [];
1070        for (let i = 0; i < arr.length; i++) {
1071            const x = arr[i];
1072            if (x.isSome()) {
1073                r.push(x.getOrDie());
1074            }
1075            else {
1076                return Optional.none();
1077            }
1078        }
1079        return Optional.some(r);
1080    };
1081    /*
1082    Notes on the lift functions:
1083    - We used to have a generic liftN, but we were concerned about its type-safety, and the below variants were faster in microbenchmarks.
1084    - The getOrDie calls are partial functions, but are checked beforehand. This is faster and more convenient (but less safe) than folds.
1085    - && is used instead of a loop for simplicity and performance.
1086    */
1087    const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();
1088    const lift3 = (oa, ob, oc, f) => oa.isSome() && ob.isSome() && oc.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie(), oc.getOrDie())) : Optional.none();
1089    const mapFrom = (a, f) => (a !== undefined && a !== null) ? Optional.some(f(a)) : Optional.none();
1090    // This can help with type inference, by specifying the type param on the none case, so the caller doesn't have to.
1091    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();
1092
1093    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
1094    const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1095
1096    /** path :: ([String], JsObj?) -> JsObj */
1097    const path$1 = (parts, scope) => {
1098        let o = scope !== undefined && scope !== null ? scope : Global;
1099        for (let i = 0; i < parts.length && o !== undefined && o !== null; ++i) {
1100            o = o[parts[i]];
1101        }
1102        return o;
1103    };
1104    /** resolve :: (String, JsObj?) -> JsObj */
1105    const resolve = (p, scope) => {
1106        const parts = p.split('.');
1107        return path$1(parts, scope);
1108    };
1109
1110    Adt.generate([
1111        { bothErrors: ['error1', 'error2'] },
1112        { firstError: ['error1', 'value2'] },
1113        { secondError: ['value1', 'error2'] },
1114        { bothValues: ['value1', 'value2'] }
1115    ]);
1116    /** partition :: [Result a] -> { errors: [String], values: [a] } */
1117    const partition$2 = (results) => {
1118        const errors = [];
1119        const values = [];
1120        each$1(results, (result) => {
1121            result.fold((err) => {
1122                errors.push(err);
1123            }, (value) => {
1124                values.push(value);
1125            });
1126        });
1127        return { errors, values };
1128    };
1129
1130    const singleton$1 = (doRevoke) => {
1131        const subject = Cell(Optional.none());
1132        const revoke = () => subject.get().each(doRevoke);
1133        const clear = () => {
1134            revoke();
1135            subject.set(Optional.none());
1136        };
1137        const isSet = () => subject.get().isSome();
1138        const get = () => subject.get();
1139        const set = (s) => {
1140            revoke();
1141            subject.set(Optional.some(s));
1142        };
1143        return {
1144            clear,
1145            isSet,
1146            get,
1147            set
1148        };
1149    };
1150    const destroyable = () => singleton$1((s) => s.destroy());
1151    const unbindable = () => singleton$1((s) => s.unbind());
1152    const value$2 = () => {
1153        const subject = singleton$1(noop);
1154        const on = (f) => subject.get().each(f);
1155        return {
1156            ...subject,
1157            on
1158        };
1159    };
1160
1161    const addToEnd = (str, suffix) => {
1162        return str + suffix;
1163    };
1164    const removeFromStart = (str, numChars) => {
1165        return str.substring(numChars);
1166    };
1167
1168    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
1169    const removeLeading = (str, prefix) => {
1170        return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
1171    };
1172    const ensureTrailing = (str, suffix) => {
1173        return endsWith(str, suffix) ? str : addToEnd(str, suffix);
1174    };
1175    const contains$1 = (str, substr, start = 0, end) => {
1176        const idx = str.indexOf(substr, start);
1177        if (idx !== -1) {
1178            return isUndefined(end) ? true : idx + substr.length <= end;
1179        }
1180        else {
1181            return false;
1182        }
1183    };
1184    /** Does 'str' start with 'prefix'?
1185     *  Note: all strings start with the empty string.
1186     *        More formally, for all strings x, startsWith(x, "").
1187     *        This is so that for all strings x and y, startsWith(y + x, y)
1188     */
1189    const startsWith = (str, prefix) => {
1190        return checkRange(str, prefix, 0);
1191    };
1192    /** Does 'str' end with 'suffix'?
1193     *  Note: all strings end with the empty string.
1194     *        More formally, for all strings x, endsWith(x, "").
1195     *        This is so that for all strings x and y, endsWith(x + y, y)
1196     */
1197    const endsWith = (str, suffix) => {
1198        return checkRange(str, suffix, str.length - suffix.length);
1199    };
1200    const blank = (r) => (s) => s.replace(r, '');
1201    /** removes all leading and trailing spaces */
1202    const trim$1 = blank(/^\s+|\s+$/g);
1203    const isNotEmpty = (s) => s.length > 0;
1204    const isEmpty = (s) => !isNotEmpty(s);
1205    const toFloat = (value) => {
1206        const num = parseFloat(value);
1207        return isNaN(num) ? Optional.none() : Optional.some(num);
1208    };
1209
1210    // Run a function fn after rate ms. If another invocation occurs
1211    // during the time it is waiting, update the arguments f will run
1212    // with (but keep the current schedule)
1213    const adaptable = (fn, rate) => {
1214        let timer = null;
1215        let args = null;
1216        const cancel = () => {
1217            if (!isNull(timer)) {
1218                clearTimeout(timer);
1219                timer = null;
1220                args = null;
1221            }
1222        };
1223        const throttle = (...newArgs) => {
1224            args = newArgs;
1225            if (isNull(timer)) {
1226                timer = setTimeout(() => {
1227                    const tempArgs = args;
1228                    timer = null;
1229                    args = null;
1230                    fn.apply(null, tempArgs);
1231                }, rate);
1232            }
1233        };
1234        return {
1235            cancel,
1236            throttle
1237        };
1238    };
1239    // Run a function fn after rate ms. If another invocation occurs
1240    // during the time it is waiting, ignore it completely.
1241    const first$1 = (fn, rate) => {
1242        let timer = null;
1243        const cancel = () => {
1244            if (!isNull(timer)) {
1245                clearTimeout(timer);
1246                timer = null;
1247            }
1248        };
1249        const throttle = (...args) => {
1250            if (isNull(timer)) {
1251                timer = setTimeout(() => {
1252                    timer = null;
1253                    fn.apply(null, args);
1254                }, rate);
1255            }
1256        };
1257        return {
1258            cancel,
1259            throttle
1260        };
1261    };
1262    // Run a function fn after rate ms. If another invocation occurs
1263    // during the time it is waiting, reschedule the function again
1264    // with the new arguments.
1265    const last = (fn, rate) => {
1266        let timer = null;
1267        const cancel = () => {
1268            if (!isNull(timer)) {
1269                clearTimeout(timer);
1270                timer = null;
1271            }
1272        };
1273        const throttle = (...args) => {
1274            cancel();
1275            timer = setTimeout(() => {
1276                timer = null;
1277                fn.apply(null, args);
1278            }, rate);
1279        };
1280        return {
1281            cancel,
1282            throttle
1283        };
1284    };
1285
1286    const cached = (f) => {
1287        let called = false;
1288        let r;
1289        return (...args) => {
1290            if (!called) {
1291                called = true;
1292                r = f.apply(null, args);
1293            }
1294            return r;
1295        };
1296    };
1297
1298    const zeroWidth = '\uFEFF';
1299    const nbsp = '\u00A0';
1300
1301    const fromHtml$2 = (html, scope) => {
1302        const doc = scope || document;
1303        const div = doc.createElement('div');
1304        div.innerHTML = html;
1305        if (!div.hasChildNodes() || div.childNodes.length > 1) {
1306            const message = 'HTML does not have a single root node';
1307            // eslint-disable-next-line no-console
1308            console.error(message, html);
1309            throw new Error(message);
1310        }
1311        return fromDom(div.childNodes[0]);
1312    };
1313    const fromTag = (tag, scope) => {
1314        const doc = scope || document;
1315        const node = doc.createElement(tag);
1316        return fromDom(node);
1317    };
1318    const fromText = (text, scope) => {
1319        const doc = scope || document;
1320        const node = doc.createTextNode(text);
1321        return fromDom(node);
1322    };
1323    const fromDom = (node) => {
1324        // TODO: Consider removing this check, but left atm for safety
1325        if (node === null || node === undefined) {
1326            throw new Error('Node cannot be null or undefined');
1327        }
1328        return {
1329            dom: node
1330        };
1331    };
1332    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
1333    // tslint:disable-next-line:variable-name
1334    const SugarElement = {
1335        fromHtml: fromHtml$2,
1336        fromTag,
1337        fromText,
1338        fromDom,
1339        fromPoint
1340    };
1341
1342    // NOTE: Mutates the range.
1343    const setStart = (rng, situ) => {
1344        situ.fold((e) => {
1345            rng.setStartBefore(e.dom);
1346        }, (e, o) => {
1347            rng.setStart(e.dom, o);
1348        }, (e) => {
1349            rng.setStartAfter(e.dom);
1350        });
1351    };
1352    const setFinish = (rng, situ) => {
1353        situ.fold((e) => {
1354            rng.setEndBefore(e.dom);
1355        }, (e, o) => {
1356            rng.setEnd(e.dom, o);
1357        }, (e) => {
1358            rng.setEndAfter(e.dom);
1359        });
1360    };
1361    const relativeToNative = (win, startSitu, finishSitu) => {
1362        const range = win.document.createRange();
1363        setStart(range, startSitu);
1364        setFinish(range, finishSitu);
1365        return range;
1366    };
1367    const exactToNative = (win, start, soffset, finish, foffset) => {
1368        const rng = win.document.createRange();
1369        rng.setStart(start.dom, soffset);
1370        rng.setEnd(finish.dom, foffset);
1371        return rng;
1372    };
1373    const toRect = (rect) => ({
1374        left: rect.left,
1375        top: rect.top,
1376        right: rect.right,
1377        bottom: rect.bottom,
1378        width: rect.width,
1379        height: rect.height
1380    });
1381    const getFirstRect$1 = (rng) => {
1382        const rects = rng.getClientRects();
1383        // ASSUMPTION: The first rectangle is the start of the selection
1384        const rect = rects.length > 0 ? rects[0] : rng.getBoundingClientRect();
1385        return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();
1386    };
1387    const getBounds$3 = (rng) => {
1388        const rect = rng.getBoundingClientRect();
1389        return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();
1390    };
1391
1392    const adt$a = Adt.generate([
1393        { ltr: ['start', 'soffset', 'finish', 'foffset'] },
1394        { rtl: ['start', 'soffset', 'finish', 'foffset'] }
1395    ]);
1396    const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);
1397    const getRanges = (win, selection) => selection.match({
1398        domRange: (rng) => {
1399            return {
1400                ltr: constant$1(rng),
1401                rtl: Optional.none
1402            };
1403        },
1404        relative: (startSitu, finishSitu) => {
1405            return {
1406                ltr: cached(() => relativeToNative(win, startSitu, finishSitu)),
1407                rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu)))
1408            };
1409        },
1410        exact: (start, soffset, finish, foffset) => {
1411            return {
1412                ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),
1413                rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, soffset)))
1414            };
1415        }
1416    });
1417    const doDiagnose = (win, ranges) => {
1418        // If we cannot create a ranged selection from start > finish, it could be RTL
1419        const rng = ranges.ltr();
1420        if (rng.collapsed) {
1421            // Let's check if it's RTL ... if it is, then reversing the direction will not be collapsed
1422            const reversed = ranges.rtl().filter((rev) => rev.collapsed === false);
1423            return reversed.map((rev) => 
1424            // We need to use "reversed" here, because the original only has one point (collapsed)
1425            adt$a.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)).getOrThunk(() => fromRange(win, adt$a.ltr, rng));
1426        }
1427        else {
1428            return fromRange(win, adt$a.ltr, rng);
1429        }
1430    };
1431    const diagnose = (win, selection) => {
1432        const ranges = getRanges(win, selection);
1433        return doDiagnose(win, ranges);
1434    };
1435    const asLtrRange = (win, selection) => {
1436        const diagnosis = diagnose(win, selection);
1437        return diagnosis.match({
1438            ltr: (start, soffset, finish, foffset) => {
1439                const rng = win.document.createRange();
1440                rng.setStart(start.dom, soffset);
1441                rng.setEnd(finish.dom, foffset);
1442                return rng;
1443            },
1444            rtl: (start, soffset, finish, foffset) => {
1445                // NOTE: Reversing start and finish
1446                const rng = win.document.createRange();
1447                rng.setStart(finish.dom, foffset);
1448                rng.setEnd(start.dom, soffset);
1449                return rng;
1450            }
1451        });
1452    };
1453    adt$a.ltr;
1454    adt$a.rtl;
1455
1456    const DOCUMENT = 9;
1457    const DOCUMENT_FRAGMENT = 11;
1458    const ELEMENT = 1;
1459    const TEXT = 3;
1460
1461    const is = (element, selector) => {
1462        const dom = element.dom;
1463        if (dom.nodeType !== ELEMENT) {
1464            return false;
1465        }
1466        else {
1467            const elem = dom;
1468            if (elem.matches !== undefined) {
1469                return elem.matches(selector);
1470            }
1471            else if (elem.msMatchesSelector !== undefined) {
1472                return elem.msMatchesSelector(selector);
1473            }
1474            else if (elem.webkitMatchesSelector !== undefined) {
1475                return elem.webkitMatchesSelector(selector);
1476            }
1477            else if (elem.mozMatchesSelector !== undefined) {
1478                // cast to any as mozMatchesSelector doesn't exist in TS DOM lib
1479                return elem.mozMatchesSelector(selector);
1480            }
1481            else {
1482                throw new Error('Browser lacks native selectors');
1483            } // unfortunately we can't throw this on startup :(
1484        }
1485    };
1486    const bypassSelector = (dom) => 
1487    // Only elements, documents and shadow roots support querySelector
1488    // shadow root element type is DOCUMENT_FRAGMENT
1489    dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT ||
1490        // IE fix for complex queries on empty nodes: http://jsfiddle.net/spyder/fv9ptr5L/
1491        dom.childElementCount === 0;
1492    const all$3 = (selector, scope) => {
1493        const base = scope === undefined ? document : scope.dom;
1494        return bypassSelector(base) ? [] : map$2(base.querySelectorAll(selector), SugarElement.fromDom);
1495    };
1496    const one = (selector, scope) => {
1497        const base = scope === undefined ? document : scope.dom;
1498        return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);
1499    };
1500
1501    const eq = (e1, e2) => e1.dom === e2.dom;
1502    // Returns: true if node e1 contains e2, otherwise false.
1503    // (returns false if e1===e2: A node does not contain itself).
1504    const contains = (e1, e2) => {
1505        const d1 = e1.dom;
1506        const d2 = e2.dom;
1507        return d1 === d2 ? false : d1.contains(d2);
1508    };
1509
1510    const DeviceType = (os, browser, userAgent, mediaMatch) => {
1511        const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;
1512        const isiPhone = os.isiOS() && !isiPad;
1513        const isMobile = os.isiOS() || os.isAndroid();
1514        const isTouch = isMobile || mediaMatch('(pointer:coarse)');
1515        const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');
1516        const isPhone = isiPhone || isMobile && !isTablet;
1517        const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;
1518        const isDesktop = !isPhone && !isTablet && !iOSwebview;
1519        return {
1520            isiPad: constant$1(isiPad),
1521            isiPhone: constant$1(isiPhone),
1522            isTablet: constant$1(isTablet),
1523            isPhone: constant$1(isPhone),
1524            isTouch: constant$1(isTouch),
1525            isAndroid: os.isAndroid,
1526            isiOS: os.isiOS,
1527            isWebView: constant$1(iOSwebview),
1528            isDesktop: constant$1(isDesktop)
1529        };
1530    };
1531
1532    const firstMatch = (regexes, s) => {
1533        for (let i = 0; i < regexes.length; i++) {
1534            const x = regexes[i];
1535            if (x.test(s)) {
1536                return x;
1537            }
1538        }
1539        return undefined;
1540    };
1541    const find$3 = (regexes, agent) => {
1542        const r = firstMatch(regexes, agent);
1543        if (!r) {
1544            return { major: 0, minor: 0 };
1545        }
1546        const group = (i) => {
1547            return Number(agent.replace(r, '$' + i));
1548        };
1549        return nu$a(group(1), group(2));
1550    };
1551    const detect$4 = (versionRegexes, agent) => {
1552        const cleanedAgent = String(agent).toLowerCase();
1553        if (versionRegexes.length === 0) {
1554            return unknown$3();
1555        }
1556        return find$3(versionRegexes, cleanedAgent);
1557    };
1558    const unknown$3 = () => {
1559        return nu$a(0, 0);
1560    };
1561    const nu$a = (major, minor) => {
1562        return { major, minor };
1563    };
1564    const Version = {
1565        nu: nu$a,
1566        detect: detect$4,
1567        unknown: unknown$3
1568    };
1569
1570    const detectBrowser$1 = (browsers, userAgentData) => {
1571        return findMap(userAgentData.brands, (uaBrand) => {
1572            const lcBrand = uaBrand.brand.toLowerCase();
1573            return find$5(browsers, (browser) => lcBrand === browser.brand?.toLowerCase())
1574                .map((info) => ({
1575                current: info.name,
1576                version: Version.nu(parseInt(uaBrand.version, 10), 0)
1577            }));
1578        });
1579    };
1580
1581    const detect$3 = (candidates, userAgent) => {
1582        const agent = String(userAgent).toLowerCase();
1583        return find$5(candidates, (candidate) => {
1584            return candidate.search(agent);
1585        });
1586    };
1587    // They (browser and os) are the same at the moment, but they might
1588    // not stay that way.
1589    const detectBrowser = (browsers, userAgent) => {
1590        return detect$3(browsers, userAgent).map((browser) => {
1591            const version = Version.detect(browser.versionRegexes, userAgent);
1592            return {
1593                current: browser.name,
1594                version
1595            };
1596        });
1597    };
1598    const detectOs = (oses, userAgent) => {
1599        return detect$3(oses, userAgent).map((os) => {
1600            const version = Version.detect(os.versionRegexes, userAgent);
1601            return {
1602                current: os.name,
1603                version
1604            };
1605        });
1606    };
1607
1608    const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
1609    const checkContains = (target) => {
1610        return (uastring) => {
1611            return contains$1(uastring, target);
1612        };
1613    };
1614    const browsers = [
1615        // This is legacy Edge
1616        {
1617            name: 'Edge',
1618            versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],
1619            search: (uastring) => {
1620                return contains$1(uastring, 'edge/') && contains$1(uastring, 'chrome') && contains$1(uastring, 'safari') && contains$1(uastring, 'applewebkit');
1621            }
1622        },
1623        // This is Google Chrome and Chromium Edge
1624        {
1625            name: 'Chromium',
1626            brand: 'Chromium',
1627            versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/, normalVersionRegex],
1628            search: (uastring) => {
1629                return contains$1(uastring, 'chrome') && !contains$1(uastring, 'chromeframe');
1630            }
1631        },
1632        {
1633            name: 'IE',
1634            versionRegexes: [/.*?msie\ ?([0-9]+)\.([0-9]+).*/, /.*?rv:([0-9]+)\.([0-9]+).*/],
1635            search: (uastring) => {
1636                return contains$1(uastring, 'msie') || contains$1(uastring, 'trident');
1637            }
1638        },
1639        // INVESTIGATE: Is this still the Opera user agent?
1640        {
1641            name: 'Opera',
1642            versionRegexes: [normalVersionRegex, /.*?opera\/([0-9]+)\.([0-9]+).*/],
1643            search: checkContains('opera')
1644        },
1645        {
1646            name: 'Firefox',
1647            versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],
1648            search: checkContains('firefox')
1649        },
1650        {
1651            name: 'Safari',
1652            versionRegexes: [normalVersionRegex, /.*?cpu os ([0-9]+)_([0-9]+).*/],
1653            search: (uastring) => {
1654                return (contains$1(uastring, 'safari') || contains$1(uastring, 'mobile/')) && contains$1(uastring, 'applewebkit');
1655            }
1656        }
1657    ];
1658    const oses = [
1659        {
1660            name: 'Windows',
1661            search: checkContains('win'),
1662            versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]
1663        },
1664        {
1665            name: 'iOS',
1666            search: (uastring) => {
1667                return contains$1(uastring, 'iphone') || contains$1(uastring, 'ipad');
1668            },
1669            versionRegexes: [/.*?version\/\ ?([0-9]+)\.([0-9]+).*/, /.*cpu os ([0-9]+)_([0-9]+).*/, /.*cpu iphone os ([0-9]+)_([0-9]+).*/]
1670        },
1671        {
1672            name: 'Android',
1673            search: checkContains('android'),
1674            versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/]
1675        },
1676        {
1677            name: 'macOS',
1678            search: checkContains('mac os x'),
1679            versionRegexes: [/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]
1680        },
1681        {
1682            name: 'Linux',
1683            search: checkContains('linux'),
1684            versionRegexes: []
1685        },
1686        { name: 'Solaris',
1687            search: checkContains('sunos'),
1688            versionRegexes: []
1689        },
1690        {
1691            name: 'FreeBSD',
1692            search: checkContains('freebsd'),
1693            versionRegexes: []
1694        },
1695        {
1696            name: 'ChromeOS',
1697            search: checkContains('cros'),
1698            versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/]
1699        }
1700    ];
1701    const PlatformInfo = {
1702        browsers: constant$1(browsers),
1703        oses: constant$1(oses)
1704    };
1705
1706    const edge = 'Edge';
1707    const chromium = 'Chromium';
1708    const ie = 'IE';
1709    const opera = 'Opera';
1710    const firefox = 'Firefox';
1711    const safari = 'Safari';
1712    const unknown$2 = () => {
1713        return nu$9({
1714            current: undefined,
1715            version: Version.unknown()
1716        });
1717    };
1718    const nu$9 = (info) => {
1719        const current = info.current;
1720        const version = info.version;
1721        const isBrowser = (name) => () => current === name;
1722        return {
1723            current,
1724            version,
1725            isEdge: isBrowser(edge),
1726            isChromium: isBrowser(chromium),
1727            // NOTE: isIe just looks too weird
1728            isIE: isBrowser(ie),
1729            isOpera: isBrowser(opera),
1730            isFirefox: isBrowser(firefox),
1731            isSafari: isBrowser(safari)
1732        };
1733    };
1734    const Browser = {
1735        unknown: unknown$2,
1736        nu: nu$9,
1737        edge: constant$1(edge),
1738        chromium: constant$1(chromium),
1739        ie: constant$1(ie),
1740        opera: constant$1(opera),
1741        firefox: constant$1(firefox),
1742        safari: constant$1(safari)
1743    };
1744
1745    const windows = 'Windows';
1746    const ios = 'iOS';
1747    const android = 'Android';
1748    const linux = 'Linux';
1749    const macos = 'macOS';
1750    const solaris = 'Solaris';
1751    const freebsd = 'FreeBSD';
1752    const chromeos = 'ChromeOS';
1753    // Though there is a bit of dupe with this and Browser, trying to
1754    // reuse code makes it much harder to follow and change.
1755    const unknown$1 = () => {
1756        return nu$8({
1757            current: undefined,
1758            version: Version.unknown()
1759        });
1760    };
1761    const nu$8 = (info) => {
1762        const current = info.current;
1763        const version = info.version;
1764        const isOS = (name) => () => current === name;
1765        return {
1766            current,
1767            version,
1768            isWindows: isOS(windows),
1769            // TODO: Fix capitalisation
1770            isiOS: isOS(ios),
1771            isAndroid: isOS(android),
1772            isMacOS: isOS(macos),
1773            isLinux: isOS(linux),
1774            isSolaris: isOS(solaris),
1775            isFreeBSD: isOS(freebsd),
1776            isChromeOS: isOS(chromeos)
1777        };
1778    };
1779    const OperatingSystem = {
1780        unknown: unknown$1,
1781        nu: nu$8,
1782        windows: constant$1(windows),
1783        ios: constant$1(ios),
1784        android: constant$1(android),
1785        linux: constant$1(linux),
1786        macos: constant$1(macos),
1787        solaris: constant$1(solaris),
1788        freebsd: constant$1(freebsd),
1789        chromeos: constant$1(chromeos)
1790    };
1791
1792    const detect$2 = (userAgent, userAgentDataOpt, mediaMatch) => {
1793        const browsers = PlatformInfo.browsers();
1794        const oses = PlatformInfo.oses();
1795        const browser = userAgentDataOpt.bind((userAgentData) => detectBrowser$1(browsers, userAgentData))
1796            .orThunk(() => detectBrowser(browsers, userAgent))
1797            .fold(Browser.unknown, Browser.nu);
1798        const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);
1799        const deviceType = DeviceType(os, browser, userAgent, mediaMatch);
1800        return {
1801            browser,
1802            os,
1803            deviceType
1804        };
1805    };
1806    const PlatformDetection = {
1807        detect: detect$2
1808    };
1809
1810    const mediaMatch = (query) => window.matchMedia(query).matches;
1811    // IMPORTANT: Must be in a thunk, otherwise rollup thinks calling this immediately
1812    // causes side effects and won't tree shake this away
1813    // Note: navigator.userAgentData is not part of the native typescript types yet
1814    let platform = cached(() => PlatformDetection.detect(window.navigator.userAgent, Optional.from((window.navigator.userAgentData)), mediaMatch));
1815    const detect$1 = () => platform();
1816
1817    const unsafe = (name, scope) => {
1818        return resolve(name, scope);
1819    };
1820    const getOrDie$1 = (name, scope) => {
1821        const actual = unsafe(name, scope);
1822        if (actual === undefined || actual === null) {
1823            throw new Error(name + ' not available on this browser');
1824        }
1825        return actual;
1826    };
1827
1828    const getPrototypeOf$1 = Object.getPrototypeOf;
1829    /*
1830     * IE9 and above
1831     *
1832     * MDN no use on this one, but here's the link anyway:
1833     * https://developer.mozilla.org/en/docs/Web/API/HTMLElement
1834     */
1835    const sandHTMLElement = (scope) => {
1836        return getOrDie$1('HTMLElement', scope);
1837    };
1838    const isPrototypeOf = (x) => {
1839        // use Resolve to get the window object for x and just return undefined if it can't find it.
1840        // undefined scope later triggers using the global window.
1841        const scope = resolve('ownerDocument.defaultView', x);
1842        // TINY-7374: We can't rely on looking at the owner window HTMLElement as the element may have
1843        // been constructed in a different window and then appended to the current window document.
1844        return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\w*Element$/.test(getPrototypeOf$1(x).constructor.name));
1845    };
1846
1847    const name$3 = (element) => {
1848        const r = element.dom.nodeName;
1849        return r.toLowerCase();
1850    };
1851    const type$1 = (element) => element.dom.nodeType;
1852    const isType = (t) => (element) => type$1(element) === t;
1853    const isHTMLElement = (element) => isElement$1(element) && isPrototypeOf(element.dom);
1854    const isElement$1 = isType(ELEMENT);
1855    const isText = isType(TEXT);
1856    const isDocument = isType(DOCUMENT);
1857    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
1858    const isTag = (tag) => (e) => isElement$1(e) && name$3(e) === tag;
1859
1860    /**
1861     * The document associated with the current element
1862     * NOTE: this will throw if the owner is null.
1863     */
1864    const owner$4 = (element) => SugarElement.fromDom(element.dom.ownerDocument);
1865    /**
1866     * If the element is a document, return it. Otherwise, return its ownerDocument.
1867     * @param dos
1868     */
1869    const documentOrOwner = (dos) => isDocument(dos) ? dos : owner$4(dos);
1870    const documentElement = (element) => SugarElement.fromDom(documentOrOwner(element).dom.documentElement);
1871    /**
1872     * The window element associated with the element
1873     * NOTE: this will throw if the defaultView is null.
1874     */
1875    const defaultView = (element) => SugarElement.fromDom(documentOrOwner(element).dom.defaultView);
1876    const parent = (element) => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
1877    // Cast down to just be SugarElement<Node>
1878    const parentNode = (element) => parent(element);
1879    const parentElement = (element) => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);
1880    const parents = (element, isRoot) => {
1881        const stop = isFunction(isRoot) ? isRoot : never;
1882        // This is used a *lot* so it needs to be performant, not recursive
1883        let dom = element.dom;
1884        const ret = [];
1885        while (dom.parentNode !== null && dom.parentNode !== undefined) {
1886            const rawParent = dom.parentNode;
1887            const p = SugarElement.fromDom(rawParent);
1888            ret.push(p);
1889            if (stop(p) === true) {
1890                break;
1891            }
1892            else {
1893                dom = rawParent;
1894            }
1895        }
1896        return ret;
1897    };
1898    const offsetParent = (element) => Optional.from(element.dom.offsetParent).map(SugarElement.fromDom);
1899    const prevSibling = (element) => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
1900    const nextSibling = (element) => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
1901    const children = (element) => map$2(element.dom.childNodes, SugarElement.fromDom);
1902    const child$2 = (element, index) => {
1903        const cs = element.dom.childNodes;
1904        return Optional.from(cs[index]).map(SugarElement.fromDom);
1905    };
1906    const firstChild = (element) => child$2(element, 0);
1907    const spot = (element, offset) => ({
1908        element,
1909        offset
1910    });
1911    const leaf = (element, offset) => {
1912        const cs = children(element);
1913        return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset);
1914    };
1915
1916    const makeRange = (start, soffset, finish, foffset) => {
1917        const doc = owner$4(start);
1918        // TODO: We need to think about a better place to put native range creation code. Does it even belong in sugar?
1919        // Could the `Compare` checks (node.compareDocumentPosition) handle these situations better?
1920        const rng = doc.dom.createRange();
1921        rng.setStart(start.dom, soffset);
1922        rng.setEnd(finish.dom, foffset);
1923        return rng;
1924    };
1925    const after$2 = (start, soffset, finish, foffset) => {
1926        const r = makeRange(start, soffset, finish, foffset);
1927        const same = eq(start, finish) && soffset === foffset;
1928        return r.collapsed && !same;
1929    };
1930
1931    /**
1932     * Is the element a ShadowRoot?
1933     *
1934     * Note: this is insufficient to test if any element is a shadow root, but it is sufficient to differentiate between
1935     * a Document and a ShadowRoot.
1936     */
1937    const isShadowRoot = (dos) => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
1938    const getRootNode = (e) => SugarElement.fromDom(e.dom.getRootNode());
1939    /** Where content needs to go. ShadowRoot or document body */
1940    const getContentContainer = (dos) => 
1941    // Can't use SugarBody.body without causing a circular module reference (since SugarBody.inBody uses SugarShadowDom)
1942    isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body);
1943    /** Is this element either a ShadowRoot or a descendent of a ShadowRoot. */
1944    const isInShadowRoot = (e) => getShadowRoot(e).isSome();
1945    /** If this element is in a ShadowRoot, return it. */
1946    const getShadowRoot = (e) => {
1947        const r = getRootNode(e);
1948        return isShadowRoot(r) ? Optional.some(r) : Optional.none();
1949    };
1950    /** Return the host of a ShadowRoot.
1951     *
1952     * This function will throw if Shadow DOM is unsupported in the browser, or if the host is null.
1953     * If you actually have a ShadowRoot, this shouldn't happen.
1954     */
1955    const getShadowHost = (e) => SugarElement.fromDom(e.dom.host);
1956    /**
1957     * When Events bubble up through a ShadowRoot, the browser changes the target to be the shadow host.
1958     * This function gets the "original" event target if possible.
1959     * This only works if the shadow tree is open - if the shadow tree is closed, event.target is returned.
1960     * See: https://developers.google.com/web/fundamentals/web-components/shadowdom#events
1961     */
1962    const getOriginalEventTarget = (event) => {
1963        if (isNonNullable(event.target)) {
1964            const el = SugarElement.fromDom(event.target);
1965            if (isElement$1(el) && isOpenShadowHost(el)) {
1966                // When target element is inside Shadow DOM we need to take first element from composedPath
1967                // otherwise we'll get Shadow Root parent, not actual target element.
1968                if (event.composed && event.composedPath) {
1969                    const composedPath = event.composedPath();
1970                    if (composedPath) {
1971                        return head(composedPath);
1972                    }
1973                }
1974            }
1975        }
1976        return Optional.from(event.target);
1977    };
1978    /** Return true if the element is a host of an open shadow root.
1979     *  Return false if the element is a host of a closed shadow root, or if the element is not a host.
1980     */
1981    const isOpenShadowHost = (element) => isNonNullable(element.dom.shadowRoot);
1982
1983    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({
1984        target,
1985        x,
1986        y,
1987        stop,
1988        prevent,
1989        kill,
1990        raw
1991    });
1992    /** Wraps an Event in an EventArgs structure.
1993     * The returned EventArgs structure has its target set to the "original" target if possible.
1994     * See SugarShadowDom.getOriginalEventTarget
1995     */
1996    const fromRawEvent$1 = (rawEvent) => {
1997        const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));
1998        const stop = () => rawEvent.stopPropagation();
1999        const prevent = () => rawEvent.preventDefault();
2000        const kill = compose(prevent, stop); // more of a sequence than a compose, but same effect
2001        // FIX: Don't just expose the raw event. Need to identify what needs standardisation.
2002        return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);
2003    };
2004    const handle = (filter, handler) => (rawEvent) => {
2005        if (filter(rawEvent)) {
2006            handler(fromRawEvent$1(rawEvent));
2007        }
2008    };
2009    const binder = (element, event, filter, handler, useCapture) => {
2010        const wrapped = handle(filter, handler);
2011        // IE9 minimum
2012        element.dom.addEventListener(event, wrapped, useCapture);
2013        return {
2014            unbind: curry(unbind, element, event, wrapped, useCapture)
2015        };
2016    };
2017    const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false);
2018    const capture$1 = (element, event, filter, handler) => binder(element, event, filter, handler, true);
2019    const unbind = (element, event, handler, useCapture) => {
2020        // IE9 minimum
2021        element.dom.removeEventListener(event, handler, useCapture);
2022    };
2023
2024    const filter = always; // no filter on plain DomEvents
2025    const bind$1 = (element, event, handler) => bind$2(element, event, filter, handler);
2026    const capture = (element, event, handler) => capture$1(element, event, filter, handler);
2027    const fromRawEvent = fromRawEvent$1;
2028
2029    const getDocument = () => SugarElement.fromDom(document);
2030
2031    const focus$4 = (element, preventScroll = false) => element.dom.focus({ preventScroll });
2032    const blur$1 = (element) => element.dom.blur();
2033    const hasFocus = (element) => {
2034        const root = getRootNode(element).dom;
2035        return element.dom === root.activeElement;
2036    };
2037    // Note: assuming that activeElement will always be a HTMLElement (maybe we should add a runtime check?)
2038    const active$1 = (root = getDocument()) => Optional.from(root.dom.activeElement).map(SugarElement.fromDom);
2039    /**
2040     * Return the descendant element that has focus.
2041     * Use instead of SelectorFind.descendant(container, ':focus')
2042     *  because the :focus selector relies on keyboard focus.
2043     */
2044    const search = (element) => active$1(getRootNode(element))
2045        .filter((e) => element.dom.contains(e.dom));
2046
2047    const before$1 = (marker, element) => {
2048        const parent$1 = parent(marker);
2049        parent$1.each((v) => {
2050            v.dom.insertBefore(element.dom, marker.dom);
2051        });
2052    };
2053    const after$1 = (marker, element) => {
2054        const sibling = nextSibling(marker);
2055        sibling.fold(() => {
2056            const parent$1 = parent(marker);
2057            parent$1.each((v) => {
2058                append$2(v, element);
2059            });
2060        }, (v) => {
2061            before$1(v, element);
2062        });
2063    };
2064    const prepend$1 = (parent, element) => {
2065        const firstChild$1 = firstChild(parent);
2066        firstChild$1.fold(() => {
2067            append$2(parent, element);
2068        }, (v) => {
2069            parent.dom.insertBefore(element.dom, v.dom);
2070        });
2071    };
2072    const append$2 = (parent, element) => {
2073        parent.dom.appendChild(element.dom);
2074    };
2075    const appendAt = (parent, element, index) => {
2076        child$2(parent, index).fold(() => {
2077            append$2(parent, element);
2078        }, (v) => {
2079            before$1(v, element);
2080        });
2081    };
2082
2083    const append$1 = (parent, elements) => {
2084        each$1(elements, (x) => {
2085            append$2(parent, x);
2086        });
2087    };
2088
2089    const rawSet = (dom, key, value) => {
2090        /*
2091         * JQuery coerced everything to a string, and silently did nothing on text node/null/undefined.
2092         *
2093         * We fail on those invalid cases, only allowing numbers and booleans.
2094         */
2095        if (isString(value) || isBoolean(value) || isNumber(value)) {
2096            dom.setAttribute(key, value + '');
2097        }
2098        else {
2099            // eslint-disable-next-line no-console
2100            console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
2101            throw new Error('Attribute value was not simple');
2102        }
2103    };
2104    const set$9 = (element, key, value) => {
2105        rawSet(element.dom, key, value);
2106    };
2107    const setAll$1 = (element, attrs) => {
2108        const dom = element.dom;
2109        each(attrs, (v, k) => {
2110            rawSet(dom, k, v);
2111        });
2112    };
2113    const get$g = (element, key) => {
2114        const v = element.dom.getAttribute(key);
2115        // undefined is the more appropriate value for JS, and this matches JQuery
2116        return v === null ? undefined : v;
2117    };
2118    const getOpt = (element, key) => Optional.from(get$g(element, key));
2119    const has$1 = (element, key) => {
2120        const dom = element.dom;
2121        // return false for non-element nodes, no point in throwing an error
2122        return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;
2123    };
2124    const remove$8 = (element, key) => {
2125        element.dom.removeAttribute(key);
2126    };
2127    const clone$2 = (element) => foldl(element.dom.attributes, (acc, attr) => {
2128        acc[attr.name] = attr.value;
2129        return acc;
2130    }, {});
2131
2132    const empty = (element) => {
2133        // shortcut "empty node" trick. Requires IE 9.
2134        element.dom.textContent = '';
2135        // If the contents was a single empty text node, the above doesn't remove it. But, it's still faster in general
2136        // than removing every child node manually.
2137        // The following is (probably) safe for performance as 99.9% of the time the trick works and
2138        // Traverse.children will return an empty array.
2139        each$1(children(element), (rogue) => {
2140            remove$7(rogue);
2141        });
2142    };
2143    const remove$7 = (element) => {
2144        const dom = element.dom;
2145        if (dom.parentNode !== null) {
2146            dom.parentNode.removeChild(dom);
2147        }
2148    };
2149
2150    const clone$1 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));
2151    /** Shallow clone - just the tag, no children */
2152    const shallow = (original) => clone$1(original, false);
2153    /** Deep clone - everything copied including children */
2154    const deep = (original) => clone$1(original, true);
2155
2156    const fromHtml$1 = (html, scope) => {
2157        const doc = scope || document;
2158        const div = doc.createElement('div');
2159        div.innerHTML = html;
2160        return children(SugarElement.fromDom(div));
2161    };
2162
2163    const get$f = (element) => element.dom.innerHTML;
2164    const set$8 = (element, content) => {
2165        const owner = owner$4(element);
2166        const docDom = owner.dom;
2167        // FireFox has *terrible* performance when using innerHTML = x
2168        const fragment = SugarElement.fromDom(docDom.createDocumentFragment());
2169        const contentElements = fromHtml$1(content, docDom);
2170        append$1(fragment, contentElements);
2171        empty(element);
2172        append$2(element, fragment);
2173    };
2174    const getOuter$2 = (element) => {
2175        const container = SugarElement.fromTag('div');
2176        const clone = SugarElement.fromDom(element.dom.cloneNode(true));
2177        append$2(container, clone);
2178        return get$f(container);
2179    };
2180
2181    const getHtml = (element) => {
2182        if (isShadowRoot(element)) {
2183            return '#shadow-root';
2184        }
2185        else {
2186            const clone = shallow(element);
2187            return getOuter$2(clone);
2188        }
2189    };
2190
2191    const image = (image) => new Promise((resolve, reject) => {
2192        const loaded = () => {
2193            destroy();
2194            resolve(image);
2195        };
2196        const listeners = [
2197            bind$1(image, 'load', loaded),
2198            bind$1(image, 'error', () => {
2199                destroy();
2200                reject('Unable to load data from image: ' + image.dom.src);
2201            }),
2202        ];
2203        const destroy = () => each$1(listeners, (l) => l.unbind());
2204        if (image.dom.complete) {
2205            loaded();
2206        }
2207    });
2208
2209    // some elements, such as mathml, don't have style attributes
2210    // others, such as angular elements, have style attributes that aren't a CSSStyleDeclaration
2211    const isSupported = (dom) => dom.style !== undefined && isFunction(dom.style.getPropertyValue);
2212
2213    // Node.contains() is very, very, very good performance
2214    // http://jsperf.com/closest-vs-contains/5
2215    const inBody = (element) => {
2216        // Technically this is only required on IE, where contains() returns false for text nodes.
2217        // But it's cheap enough to run everywhere and Sugar doesn't have platform detection (yet).
2218        const dom = isText(element) ? element.dom.parentNode : element.dom;
2219        // use ownerDocument.body to ensure this works inside iframes.
2220        // Normally contains is bad because an element "contains" itself, but here we want that.
2221        if (dom === undefined || dom === null || dom.ownerDocument === null) {
2222            return false;
2223        }
2224        const doc = dom.ownerDocument;
2225        return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
2226    };
2227    const body = () => getBody(SugarElement.fromDom(document));
2228    const getBody = (doc) => {
2229        const b = doc.dom.body;
2230        if (b === null || b === undefined) {
2231            throw new Error('Body is not available yet');
2232        }
2233        return SugarElement.fromDom(b);
2234    };
2235
2236    const internalSet = (dom, property, value) => {
2237        // This is going to hurt. Apologies.
2238        // JQuery coerces numbers to pixels for certain property names, and other times lets numbers through.
2239        // we're going to be explicit; strings only.
2240        if (!isString(value)) {
2241            // eslint-disable-next-line no-console
2242            console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
2243            throw new Error('CSS value must be a string: ' + value);
2244        }
2245        // removed: support for dom().style[property] where prop is camel case instead of normal property name
2246        if (isSupported(dom)) {
2247            dom.style.setProperty(property, value);
2248        }
2249    };
2250    const internalRemove = (dom, property) => {
2251        /*
2252         * IE9 and above - MDN doesn't have details, but here's a couple of random internet claims
2253         *
2254         * http://help.dottoro.com/ljopsjck.php
2255         * http://stackoverflow.com/a/7901886/7546
2256         */
2257        if (isSupported(dom)) {
2258            dom.style.removeProperty(property);
2259        }
2260    };
2261    const set$7 = (element, property, value) => {
2262        const dom = element.dom;
2263        internalSet(dom, property, value);
2264    };
2265    const setAll = (element, css) => {
2266        const dom = element.dom;
2267        each(css, (v, k) => {
2268            internalSet(dom, k, v);
2269        });
2270    };
2271    const setOptions = (element, css) => {
2272        const dom = element.dom;
2273        each(css, (v, k) => {
2274            v.fold(() => {
2275                internalRemove(dom, k);
2276            }, (value) => {
2277                internalSet(dom, k, value);
2278            });
2279        });
2280    };
2281    /*
2282     * NOTE: For certain properties, this returns the "used value" which is subtly different to the "computed value" (despite calling getComputedStyle).
2283     * Blame CSS 2.0.
2284     *
2285     * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
2286     */
2287    const get$e = (element, property) => {
2288        const dom = element.dom;
2289        /*
2290         * IE9 and above per
2291         * https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle
2292         *
2293         * Not in numerosity, because it doesn't memoize and looking this up dynamically in performance critical code would be horrendous.
2294         *
2295         * JQuery has some magic here for IE popups, but we don't really need that.
2296         * It also uses element.ownerDocument.defaultView to handle iframes but that hasn't been required since FF 3.6.
2297         */
2298        const styles = window.getComputedStyle(dom);
2299        const r = styles.getPropertyValue(property);
2300        // 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.
2301        // Turns out we do this a lot.
2302        return (r === '' && !inBody(element)) ? getUnsafeProperty(dom, property) : r;
2303    };
2304    // removed: support for dom().style[property] where prop is camel case instead of normal property name
2305    // empty string is what the browsers (IE11 and Chrome) return when the propertyValue doesn't exists.
2306    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';
2307    /*
2308     * Gets the raw value from the style attribute. Useful for retrieving "used values" from the DOM:
2309     * https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
2310     *
2311     * Returns NONE if the property isn't set, or the value is an empty string.
2312     */
2313    const getRaw = (element, property) => {
2314        const dom = element.dom;
2315        const raw = getUnsafeProperty(dom, property);
2316        return Optional.from(raw).filter((r) => r.length > 0);
2317    };
2318    const getAllRaw = (element) => {
2319        const css = {};
2320        const dom = element.dom;
2321        if (isSupported(dom)) {
2322            for (let i = 0; i < dom.style.length; i++) {
2323                const ruleName = dom.style.item(i);
2324                css[ruleName] = dom.style[ruleName];
2325            }
2326        }
2327        return css;
2328    };
2329    const isValidValue$1 = (tag, property, value) => {
2330        const element = SugarElement.fromTag(tag);
2331        set$7(element, property, value);
2332        const style = getRaw(element, property);
2333        return style.isSome();
2334    };
2335    const remove$6 = (element, property) => {
2336        const dom = element.dom;
2337        internalRemove(dom, property);
2338        if (is$1(getOpt(element, 'style').map(trim$1), '')) {
2339            // No more styles left, remove the style attribute as well
2340            remove$8(element, 'style');
2341        }
2342    };
2343    /* NOTE: This function is here for the side effect it triggers.
2344    The value itself is not used.
2345    Be sure to not use the return value, and that it is not removed by a minifier.
2346     */
2347    const reflow = (e) => e.dom.offsetWidth;
2348
2349    const Dimension = (name, getOffset) => {
2350        const set = (element, h) => {
2351            if (!isNumber(h) && !h.match(/^[0-9]+$/)) {
2352                throw new Error(name + '.set accepts only positive integer values. Value was ' + h);
2353            }
2354            const dom = element.dom;
2355            if (isSupported(dom)) {
2356                dom.style[name] = h + 'px';
2357            }
2358        };
2359        /*
2360         * jQuery supports querying width and height on the document and window objects.
2361         *
2362         * TBIO doesn't do this, so the code is removed to save space, but left here just in case.
2363         */
2364        /*
2365        var getDocumentWidth = (element) => {
2366          var dom = element.dom;
2367          if (Node.isDocument(element)) {
2368            var body = dom.body;
2369            var doc = dom.documentElement;
2370            return Math.max(
2371              body.scrollHeight,
2372              doc.scrollHeight,
2373              body.offsetHeight,
2374              doc.offsetHeight,
2375              doc.clientHeight
2376            );
2377          }
2378        };
2379      
2380        var getWindowWidth = (element) => {
2381          var dom = element.dom;
2382          if (dom.window === dom) {
2383            // There is no offsetHeight on a window, so use the clientHeight of the document
2384            return dom.document.documentElement.clientHeight;
2385          }
2386        };
2387      */
2388        const get = (element) => {
2389            const r = getOffset(element);
2390            // zero or null means non-standard or disconnected, fall back to CSS
2391            if (r <= 0 || r === null) {
2392                const css = get$e(element, name);
2393                // ugh this feels dirty, but it saves cycles
2394                return parseFloat(css) || 0;
2395            }
2396            return r;
2397        };
2398        // in jQuery, getOuter replicates (or uses) box-sizing: border-box calculations
2399        // although these calculations only seem relevant for quirks mode, and edge cases TBIO doesn't rely on
2400        const getOuter = get;
2401        const aggregate = (element, properties) => foldl(properties, (acc, property) => {
2402            const val = get$e(element, property);
2403            const value = val === undefined ? 0 : parseInt(val, 10);
2404            return isNaN(value) ? acc : acc + value;
2405        }, 0);
2406        const max = (element, value, properties) => {
2407            const cumulativeInclusions = aggregate(element, properties);
2408            // if max-height is 100px and your cumulativeInclusions is 150px, there is no way max-height can be 100px, so we return 0.
2409            const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;
2410            return absoluteMax;
2411        };
2412        return {
2413            set,
2414            get,
2415            getOuter,
2416            aggregate,
2417            max
2418        };
2419    };
2420
2421    const api$2 = Dimension('height', (element) => {
2422        // getBoundingClientRect gives better results than offsetHeight for tables with captions on Firefox
2423        const dom = element.dom;
2424        return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;
2425    });
2426    const get$d = (element) => api$2.get(element);
2427    const getOuter$1 = (element) => api$2.getOuter(element);
2428    const setMax$1 = (element, value) => {
2429        // These properties affect the absolute max-height, they are not counted natively, we want to include these properties.
2430        const inclusions = ['margin-top', 'border-top-width', 'padding-top', 'padding-bottom', 'border-bottom-width', 'margin-bottom'];
2431        const absMax = api$2.max(element, value, inclusions);
2432        set$7(element, 'max-height', absMax + 'px');
2433    };
2434
2435    const isHidden$1 = (dom) => dom.offsetWidth <= 0 && dom.offsetHeight <= 0;
2436    const isVisible = (element) => !isHidden$1(element.dom);
2437
2438    const api$1 = Dimension('width', (element) => {
2439        const dom = element.dom;
2440        return inBody(element) ? dom.getBoundingClientRect().width : dom.offsetWidth;
2441    });
2442    const set$6 = (element, h) => api$1.set(element, h);
2443    const get$c = (element) => api$1.get(element);
2444    const getOuter = (element) => api$1.getOuter(element);
2445    const setMax = (element, value) => {
2446        // These properties affect the absolute max-height, they are not counted natively, we want to include these properties.
2447        const inclusions = ['margin-left', 'border-left-width', 'padding-left', 'padding-right', 'border-right-width', 'margin-right'];
2448        const absMax = api$1.max(element, value, inclusions);
2449        set$7(element, 'max-width', absMax + 'px');
2450    };
2451
2452    const r$1 = (left, top) => {
2453        const translate = (x, y) => r$1(left + x, top + y);
2454        return {
2455            left,
2456            top,
2457            translate
2458        };
2459    };
2460    // tslint:disable-next-line:variable-name
2461    const SugarPosition = r$1;
2462
2463    const boxPosition = (dom) => {
2464        const box = dom.getBoundingClientRect();
2465        return SugarPosition(box.left, box.top);
2466    };
2467    // Avoids falsy false fallthrough
2468    const firstDefinedOrZero = (a, b) => {
2469        if (a !== undefined) {
2470            return a;
2471        }
2472        else {
2473            return b !== undefined ? b : 0;
2474        }
2475    };
2476    const absolute$3 = (element) => {
2477        const doc = element.dom.ownerDocument;
2478        const body = doc.body;
2479        const win = doc.defaultView;
2480        const html = doc.documentElement;
2481        if (body === element.dom) {
2482            return SugarPosition(body.offsetLeft, body.offsetTop);
2483        }
2484        const scrollTop = firstDefinedOrZero(win?.pageYOffset, html.scrollTop);
2485        const scrollLeft = firstDefinedOrZero(win?.pageXOffset, html.scrollLeft);
2486        const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);
2487        const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);
2488        return viewport$1(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);
2489    };
2490    const viewport$1 = (element) => {
2491        const dom = element.dom;
2492        const doc = dom.ownerDocument;
2493        const body = doc.body;
2494        if (body === dom) {
2495            return SugarPosition(body.offsetLeft, body.offsetTop);
2496        }
2497        if (!inBody(element)) {
2498            return SugarPosition(0, 0);
2499        }
2500        return boxPosition(dom);
2501    };
2502
2503    // get scroll position (x,y) relative to document _doc (or global if not supplied)
2504    const get$b = (_DOC) => {
2505        const doc = _DOC !== undefined ? _DOC.dom : document;
2506        // ASSUMPTION: This is for cross-browser support, body works for Safari & EDGE, and when we have an iframe body scroller
2507        const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;
2508        const y = doc.body.scrollTop || doc.documentElement.scrollTop;
2509        return SugarPosition(x, y);
2510    };
2511    // Scroll content to (x,y) relative to document _doc (or global if not supplied)
2512    const to = (x, y, _DOC) => {
2513        const doc = _DOC !== undefined ? _DOC.dom : document;
2514        const win = doc.defaultView;
2515        if (win) {
2516            win.scrollTo(x, y);
2517        }
2518    };
2519
2520    const NodeValue = (is, name) => {
2521        const get = (element) => {
2522            if (!is(element)) {
2523                throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
2524            }
2525            return getOption(element).getOr('');
2526        };
2527        const getOption = (element) => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
2528        const set = (element, value) => {
2529            if (!is(element)) {
2530                throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
2531            }
2532            element.dom.nodeValue = value;
2533        };
2534        return {
2535            get,
2536            getOption,
2537            set
2538        };
2539    };
2540
2541    const api = NodeValue(isText, 'text');
2542    const get$a = (element) => api.get(element);
2543
2544    const onDirection = (isLtr, isRtl) => (element) => getDirection(element) === 'rtl' ? isRtl : isLtr;
2545    const getDirection = (element) => get$e(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';
2546
2547    // Methods for handling attributes that contain a list of values <div foo="alpha beta theta">
2548    const read$2 = (element, attr) => {
2549        const value = get$g(element, attr);
2550        return value === undefined || value === '' ? [] : value.split(' ');
2551    };
2552    const add$4 = (element, attr, id) => {
2553        const old = read$2(element, attr);
2554        const nu = old.concat([id]);
2555        set$9(element, attr, nu.join(' '));
2556        return true;
2557    };
2558    const remove$5 = (element, attr, id) => {
2559        const nu = filter$2(read$2(element, attr), (v) => v !== id);
2560        if (nu.length > 0) {
2561            set$9(element, attr, nu.join(' '));
2562        }
2563        else {
2564            remove$8(element, attr);
2565        }
2566        return false;
2567    };
2568
2569    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
2570        if (is(scope, a)) {
2571            return Optional.some(scope);
2572        }
2573        else if (isFunction(isRoot) && isRoot(scope)) {
2574            return Optional.none();
2575        }
2576        else {
2577            return ancestor(scope, a, isRoot);
2578        }
2579    };
2580
2581    const ancestor$2 = (scope, predicate, isRoot) => {
2582        let element = scope.dom;
2583        const stop = isFunction(isRoot) ? isRoot : never;
2584        while (element.parentNode) {
2585            element = element.parentNode;
2586            const el = SugarElement.fromDom(element);
2587            if (predicate(el)) {
2588                return Optional.some(el);
2589            }
2590            else if (stop(el)) {
2591                break;
2592            }
2593        }
2594        return Optional.none();
2595    };
2596    const closest$4 = (scope, predicate, isRoot) => {
2597        // This is required to avoid ClosestOrAncestor passing the predicate to itself
2598        const is = (s, test) => test(s);
2599        return ClosestOrAncestor(is, ancestor$2, scope, predicate, isRoot);
2600    };
2601    const sibling$1 = (scope, predicate) => {
2602        const element = scope.dom;
2603        if (!element.parentNode) {
2604            return Optional.none();
2605        }
2606        return child$1(SugarElement.fromDom(element.parentNode), (x) => !eq(scope, x) && predicate(x));
2607    };
2608    const child$1 = (scope, predicate) => {
2609        const pred = (node) => predicate(SugarElement.fromDom(node));
2610        const result = find$5(scope.dom.childNodes, pred);
2611        return result.map(SugarElement.fromDom);
2612    };
2613    const descendant$1 = (scope, predicate) => {
2614        const descend = (node) => {
2615            // tslint:disable-next-line:prefer-for-of
2616            for (let i = 0; i < node.childNodes.length; i++) {
2617                const child = SugarElement.fromDom(node.childNodes[i]);
2618                if (predicate(child)) {
2619                    return Optional.some(child);
2620                }
2621                const res = descend(node.childNodes[i]);
2622                if (res.isSome()) {
2623                    return res;
2624                }
2625            }
2626            return Optional.none();
2627        };
2628        return descend(scope.dom);
2629    };
2630
2631    // TODO: An internal SelectorFilter module that doesn't SugarElement.fromDom() everything
2632    const first = (selector) => one(selector);
2633    const ancestor$1 = (scope, selector, isRoot) => ancestor$2(scope, (e) => is(e, selector), isRoot);
2634    const sibling = (scope, selector) => sibling$1(scope, (e) => is(e, selector));
2635    const child = (scope, selector) => child$1(scope, (e) => is(e, selector));
2636    const descendant = (scope, selector) => one(selector, scope);
2637    // Returns Some(closest ancestor element (sugared)) matching 'selector' up to isRoot, or None() otherwise
2638    const closest$3 = (scope, selector, isRoot) => {
2639        const is$1 = (element, selector) => is(element, selector);
2640        return ClosestOrAncestor(is$1, ancestor$1, scope, selector, isRoot);
2641    };
2642
2643    const set$5 = (element, status) => {
2644        element.dom.checked = status;
2645    };
2646    const get$9 = (element) => element.dom.checked;
2647
2648    // IE11 Can return undefined for a classList on elements such as math, so we make sure it's not undefined before attempting to use it.
2649    const supports = (element) => element.dom.classList !== undefined;
2650    const get$8 = (element) => read$2(element, 'class');
2651    const add$3 = (element, clazz) => add$4(element, 'class', clazz);
2652    const remove$4 = (element, clazz) => remove$5(element, 'class', clazz);
2653    const toggle$4 = (element, clazz) => {
2654        if (contains$2(get$8(element), clazz)) {
2655            return remove$4(element, clazz);
2656        }
2657        else {
2658            return add$3(element, clazz);
2659        }
2660    };
2661
2662    /*
2663     * ClassList is IE10 minimum:
2664     * https://developer.mozilla.org/en-US/docs/Web/API/Element.classList
2665     *
2666     * Note that IE doesn't support the second argument to toggle (at all).
2667     * If it did, the toggler could be better.
2668     */
2669    const add$2 = (element, clazz) => {
2670        if (supports(element)) {
2671            element.dom.classList.add(clazz);
2672        }
2673        else {
2674            add$3(element, clazz);
2675        }
2676    };
2677    const cleanClass = (element) => {
2678        const classList = supports(element) ? element.dom.classList : get$8(element);
2679        // classList is a "live list", so this is up to date already
2680        if (classList.length === 0) {
2681            // No more classes left, remove the class attribute as well
2682            remove$8(element, 'class');
2683        }
2684    };
2685    const remove$3 = (element, clazz) => {
2686        if (supports(element)) {
2687            const classList = element.dom.classList;
2688            classList.remove(clazz);
2689        }
2690        else {
2691            remove$4(element, clazz);
2692        }
2693        cleanClass(element);
2694    };
2695    const toggle$3 = (element, clazz) => {
2696        const result = supports(element) ? element.dom.classList.toggle(clazz) : toggle$4(element, clazz);
2697        cleanClass(element);
2698        return result;
2699    };
2700    const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);
2701
2702    /*
2703     * ClassList is IE10 minimum:
2704     * https://developer.mozilla.org/en-US/docs/Web/API/Element.classList
2705     */
2706    const add$1 = (element, classes) => {
2707        each$1(classes, (x) => {
2708            add$2(element, x);
2709        });
2710    };
2711    const remove$2 = (element, classes) => {
2712        each$1(classes, (x) => {
2713            remove$3(element, x);
2714        });
2715    };
2716    const toggle$2 = (element, classes) => {
2717        each$1(classes, (x) => {
2718            toggle$3(element, x);
2719        });
2720    };
2721    const hasAll = (element, classes) => forall(classes, (clazz) => has(element, clazz));
2722    const getNative = (element) => {
2723        const classList = element.dom.classList;
2724        const r = new Array(classList.length);
2725        for (let i = 0; i < classList.length; i++) {
2726            const item = classList.item(i);
2727            if (item !== null) {
2728                r[i] = item;
2729            }
2730        }
2731        return r;
2732    };
2733    const get$7 = (element) => supports(element) ? getNative(element) : get$8(element);
2734
2735    const get$6 = (element) => element.dom.textContent;
2736
2737    const get$5 = (element) => element.dom.value;
2738    const set$4 = (element, value) => {
2739        if (value === undefined) {
2740            throw new Error('Value.set was undefined');
2741        }
2742        element.dom.value = value;
2743    };
2744
2745    const ancestors = (scope, predicate, isRoot) => filter$2(parents(scope, isRoot), predicate);
2746
2747    const descendants = (scope, selector) => all$3(selector, scope);
2748
2749    const closest$2 = (scope, predicate, isRoot) => closest$4(scope, predicate, isRoot).isSome();
2750
2751    const closest$1 = (scope, selector, isRoot) => closest$3(scope, selector, isRoot).isSome();
2752
2753    const ensureIsRoot = (isRoot) => isFunction(isRoot) ? isRoot : never;
2754    const ancestor = (scope, transform, isRoot) => {
2755        let element = scope.dom;
2756        const stop = ensureIsRoot(isRoot);
2757        while (element.parentNode) {
2758            element = element.parentNode;
2759            const el = SugarElement.fromDom(element);
2760            const transformed = transform(el);
2761            if (transformed.isSome()) {
2762                return transformed;
2763            }
2764            else if (stop(el)) {
2765                break;
2766            }
2767        }
2768        return Optional.none();
2769    };
2770    const closest = (scope, transform, isRoot) => {
2771        const current = transform(scope);
2772        const stop = ensureIsRoot(isRoot);
2773        return current.orThunk(() => stop(scope) ? Optional.none() : ancestor(scope, transform, stop));
2774    };
2775
2776    const create$5 = (start, soffset, finish, foffset) => ({
2777        start,
2778        soffset,
2779        finish,
2780        foffset
2781    });
2782    // tslint:disable-next-line:variable-name
2783    const SimRange = {
2784        create: create$5
2785    };
2786
2787    const adt$9 = Adt.generate([
2788        { before: ['element'] },
2789        { on: ['element', 'offset'] },
2790        { after: ['element'] }
2791    ]);
2792    // Probably don't need this given that we now have "match"
2793    const cata$2 = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);
2794    const getStart$1 = (situ) => situ.fold(identity, identity, identity);
2795    const before = adt$9.before;
2796    const on$1 = adt$9.on;
2797    const after = adt$9.after;
2798    // tslint:disable-next-line:variable-name
2799    const Situ = {
2800        before,
2801        on: on$1,
2802        after,
2803        cata: cata$2,
2804        getStart: getStart$1
2805    };
2806
2807    // Consider adding a type for "element"
2808    const adt$8 = Adt.generate([
2809        { domRange: ['rng'] },
2810        { relative: ['startSitu', 'finishSitu'] },
2811        { exact: ['start', 'soffset', 'finish', 'foffset'] }
2812    ]);
2813    const exactFromRange = (simRange) => adt$8.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);
2814    const getStart = (selection) => selection.match({
2815        domRange: (rng) => SugarElement.fromDom(rng.startContainer),
2816        relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),
2817        exact: (start, _soffset, _finish, _foffset) => start
2818    });
2819    const domRange = adt$8.domRange;
2820    const relative$1 = adt$8.relative;
2821    const exact = adt$8.exact;
2822    const getWin = (selection) => {
2823        const start = getStart(selection);
2824        return defaultView(start);
2825    };
2826    // This is out of place but it's API so I can't remove it
2827    const range$1 = SimRange.create;
2828    // tslint:disable-next-line:variable-name
2829    const SimSelection = {
2830        domRange,
2831        relative: relative$1,
2832        exact,
2833        exactFromRange,
2834        getWin,
2835        range: range$1
2836    };
2837
2838    const beforeSpecial = (element, offset) => {
2839        // From memory, we don't want to use <br> directly on Firefox because it locks the keyboard input.
2840        // It turns out that <img> directly on IE locks the keyboard as well.
2841        // If the offset is 0, use before. If the offset is 1, use after.
2842        // TBIO-3889: Firefox Situ.on <input> results in a child of the <input>; Situ.before <input> results in platform inconsistencies
2843        const name = name$3(element);
2844        if ('input' === name) {
2845            return Situ.after(element);
2846        }
2847        else if (!contains$2(['br', 'img'], name)) {
2848            return Situ.on(element, offset);
2849        }
2850        else {
2851            return offset === 0 ? Situ.before(element) : Situ.after(element);
2852        }
2853    };
2854    const preprocessExact = (start, soffset, finish, foffset) => {
2855        const startSitu = beforeSpecial(start, soffset);
2856        const finishSitu = beforeSpecial(finish, foffset);
2857        return SimSelection.relative(startSitu, finishSitu);
2858    };
2859
2860    const getNativeSelection = (win) => Optional.from(win.getSelection());
2861    const doSetNativeRange = (win, rng) => {
2862        getNativeSelection(win).each((selection) => {
2863            selection.removeAllRanges();
2864            selection.addRange(rng);
2865        });
2866    };
2867    const doSetRange = (win, start, soffset, finish, foffset) => {
2868        const rng = exactToNative(win, start, soffset, finish, foffset);
2869        doSetNativeRange(win, rng);
2870    };
2871    const setLegacyRtlRange = (win, selection, start, soffset, finish, foffset) => {
2872        selection.collapse(start.dom, soffset);
2873        selection.extend(finish.dom, foffset);
2874    };
2875    const setRangeFromRelative = (win, relative) => diagnose(win, relative).match({
2876        ltr: (start, soffset, finish, foffset) => {
2877            doSetRange(win, start, soffset, finish, foffset);
2878        },
2879        rtl: (start, soffset, finish, foffset) => {
2880            getNativeSelection(win).each((selection) => {
2881                // If this selection is backwards, then we need to use extend.
2882                if (selection.setBaseAndExtent) {
2883                    selection.setBaseAndExtent(start.dom, soffset, finish.dom, foffset);
2884                }
2885                else if (selection.extend) {
2886                    // This try catch is for older browsers (Firefox 52) as they're sometimes unable to handle setting backwards selections using selection.extend and error out.
2887                    try {
2888                        setLegacyRtlRange(win, selection, start, soffset, finish, foffset);
2889                    }
2890                    catch {
2891                        // If it does fail, try again with ltr.
2892                        doSetRange(win, finish, foffset, start, soffset);
2893                    }
2894                }
2895                else {
2896                    doSetRange(win, finish, foffset, start, soffset);
2897                }
2898            });
2899        }
2900    });
2901    const setExact = (win, start, soffset, finish, foffset) => {
2902        const relative = preprocessExact(start, soffset, finish, foffset);
2903        setRangeFromRelative(win, relative);
2904    };
2905    // NOTE: We are still reading the range because it gives subtly different behaviour
2906    // than using the anchorNode and focusNode. I'm not sure if this behaviour is any
2907    // better or worse; it's just different.
2908    const readRange = (selection) => {
2909        if (selection.rangeCount > 0) {
2910            const firstRng = selection.getRangeAt(0);
2911            const lastRng = selection.getRangeAt(selection.rangeCount - 1);
2912            return Optional.some(SimRange.create(SugarElement.fromDom(firstRng.startContainer), firstRng.startOffset, SugarElement.fromDom(lastRng.endContainer), lastRng.endOffset));
2913        }
2914        else {
2915            return Optional.none();
2916        }
2917    };
2918    const doGetExact = (selection) => {
2919        if (selection.anchorNode === null || selection.focusNode === null) {
2920            return readRange(selection);
2921        }
2922        else {
2923            const anchor = SugarElement.fromDom(selection.anchorNode);
2924            const focus = SugarElement.fromDom(selection.focusNode);
2925            // if this returns true anchor is _after_ focus, so we need a custom selection object to maintain the RTL selection
2926            return after$2(anchor, selection.anchorOffset, focus, selection.focusOffset) ? Optional.some(SimRange.create(anchor, selection.anchorOffset, focus, selection.focusOffset)) : readRange(selection);
2927        }
2928    };
2929    const getExact = (win) => 
2930    // We want to retrieve the selection as it is.
2931    getNativeSelection(win)
2932        .filter((sel) => sel.rangeCount > 0)
2933        .bind(doGetExact);
2934    const getFirstRect = (win, selection) => {
2935        const rng = asLtrRange(win, selection);
2936        return getFirstRect$1(rng);
2937    };
2938    const getBounds$2 = (win, selection) => {
2939        const rng = asLtrRange(win, selection);
2940        return getBounds$3(rng);
2941    };
2942
2943    const units = {
2944        // we don't really support all of these different ways to express a length
2945        unsupportedLength: [
2946            'em',
2947            'ex',
2948            'cap',
2949            'ch',
2950            'ic',
2951            'rem',
2952            'lh',
2953            'rlh',
2954            'vw',
2955            'vh',
2956            'vi',
2957            'vb',
2958            'vmin',
2959            'vmax',
2960            'cm',
2961            'mm',
2962            'Q',
2963            'in',
2964            'pc',
2965            'pt',
2966            'px'
2967        ],
2968        // these are the length values we do support
2969        fixed: ['px', 'pt'],
2970        relative: ['%'],
2971        empty: ['']
2972    };
2973    // Built from https://tc39.es/ecma262/#prod-StrDecimalLiteral
2974    // Matches a float followed by a trailing set of characters
2975    const pattern = (() => {
2976        const decimalDigits = '[0-9]+';
2977        const signedInteger = '[+-]?' + decimalDigits;
2978        const exponentPart = '[eE]' + signedInteger;
2979        const dot = '\\.';
2980        const opt = (input) => `(?:${input})?`;
2981        const unsignedDecimalLiteral = [
2982            'Infinity',
2983            decimalDigits + dot + opt(decimalDigits) + opt(exponentPart),
2984            dot + decimalDigits + opt(exponentPart),
2985            decimalDigits + opt(exponentPart)
2986        ].join('|');
2987        const float = `[+-]?(?:${unsignedDecimalLiteral})`;
2988        return new RegExp(`^(${float})(.*)$`);
2989    })();
2990    const isUnit = (unit, accepted) => exists(accepted, (acc) => exists(units[acc], (check) => unit === check));
2991    const parse = (input, accepted) => {
2992        const match = Optional.from(pattern.exec(input));
2993        return match.bind((array) => {
2994            const value = Number(array[1]);
2995            const unitRaw = array[2];
2996            if (isUnit(unitRaw, accepted)) {
2997                return Optional.some({
2998                    value,
2999                    unit: unitRaw
3000                });
3001            }
3002            else {
3003                return Optional.none();
3004            }
3005        });
3006    };
3007    const normalise = (input, accepted) => parse(input, accepted).map(({ value, unit }) => value + unit);
3008
3009    const get$4 = (_win) => {
3010        const win = _win === undefined ? window : _win;
3011        if (detect$1().browser.isFirefox()) {
3012            // TINY-7984: Firefox 91 is returning incorrect values for visualViewport.pageTop, so disable it for now
3013            return Optional.none();
3014        }
3015        else {
vendor: 17,505 bytes, lines 3016-3428
3016            return Optional.from(win.visualViewport);
3017        }
3018    };
3019    const bounds$1 = (x, y, width, height) => ({
3020        x,
3021        y,
3022        width,
3023        height,
3024        right: x + width,
3025        bottom: y + height
3026    });
3027    const getBounds$1 = (_win) => {
3028        const win = _win === undefined ? window : _win;
3029        const doc = win.document;
3030        const scroll = get$b(SugarElement.fromDom(doc));
3031        return get$4(win).fold(() => {
3032            const html = win.document.documentElement;
3033            // Don't use window.innerWidth/innerHeight here, as we don't want to include scrollbars
3034            // since the right/bottom position is based on the edge of the scrollbar not the window
3035            const width = html.clientWidth;
3036            const height = html.clientHeight;
3037            return bounds$1(scroll.left, scroll.top, width, height);
3038        }, (visualViewport) => 
3039        // iOS doesn't update the pageTop/pageLeft when element.scrollIntoView() is called, so we need to fallback to the
3040        // scroll position which will always be less than the page top/left values when page top/left are accurate/correct.
3041        bounds$1(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height));
3042    };
3043
3044    const walkUp = (navigation, doc) => {
3045        const frame = navigation.view(doc);
3046        return frame.fold(constant$1([]), (f) => {
3047            const parent = navigation.owner(f);
3048            const rest = walkUp(navigation, parent);
3049            return [f].concat(rest);
3050        });
3051    };
3052    // TODO: Why is this an option if it is always some?
3053    const pathTo = (element, navigation) => {
3054        const d = navigation.owner(element);
3055        const paths = walkUp(navigation, d);
3056        return Optional.some(paths);
3057    };
3058
3059    const view = (doc) => {
3060        // Only walk up to the document this script is defined in.
3061        // This prevents walking up to the parent window when the editor is in an iframe.
3062        const element = doc.dom === document ? Optional.none() : Optional.from(doc.dom.defaultView?.frameElement);
3063        return element.map(SugarElement.fromDom);
3064    };
3065    const owner$3 = (element) => owner$4(element);
3066
3067    var Navigation = /*#__PURE__*/Object.freeze({
3068        __proto__: null,
3069        view: view,
3070        owner: owner$3
3071    });
3072
3073    const find$2 = (element) => {
3074        const doc = getDocument();
3075        const scroll = get$b(doc);
3076        // Get the path of iframe elements to this element.
3077        const path = pathTo(element, Navigation);
3078        return path.fold(curry(absolute$3, element), (frames) => {
3079            const offset = viewport$1(element);
3080            const r = foldr(frames, (b, a) => {
3081                const loc = viewport$1(a);
3082                return {
3083                    left: b.left + loc.left,
3084                    top: b.top + loc.top
3085                };
3086            }, { left: 0, top: 0 });
3087            return SugarPosition(r.left + offset.left + scroll.left, r.top + offset.top + scroll.top);
3088        });
3089    };
3090
3091    const pointed = (point, width, height) => ({
3092        point,
3093        width,
3094        height
3095    });
3096    const rect = (x, y, width, height) => ({
3097        x,
3098        y,
3099        width,
3100        height
3101    });
3102    const bounds = (x, y, width, height) => ({
3103        x,
3104        y,
3105        width,
3106        height,
3107        right: x + width,
3108        bottom: y + height
3109    });
3110    const box$1 = (element) => {
3111        const xy = absolute$3(element);
3112        const w = getOuter(element);
3113        const h = getOuter$1(element);
3114        return bounds(xy.left, xy.top, w, h);
3115    };
3116    // NOTE: We used to use AriaFocus.preserve here, but there is no reason to do that now that
3117    // we are not changing the visibility of the element. Hopefully (2015-09-29).
3118    const absolute$2 = (element) => {
3119        const position = find$2(element);
3120        const width = getOuter(element);
3121        const height = getOuter$1(element);
3122        return bounds(position.left, position.top, width, height);
3123    };
3124    const constrain = (original, constraint) => {
3125        const left = Math.max(original.x, constraint.x);
3126        const top = Math.max(original.y, constraint.y);
3127        const right = Math.min(original.right, constraint.right);
3128        const bottom = Math.min(original.bottom, constraint.bottom);
3129        const width = right - left;
3130        const height = bottom - top;
3131        return bounds(left, top, width, height);
3132    };
3133    const constrainByMany = (original, constraints) => {
3134        return foldl(constraints, (acc, c) => constrain(acc, c), original);
3135    };
3136    const win = () => getBounds$1(window);
3137
3138    const isSource = (component, simulatedEvent) => eq(component.element, simulatedEvent.event.target);
3139
3140    const getOffsetParent = (element) => {
3141        // Firefox sets the offsetParent to the body when fixed instead of null like
3142        // all other browsers. So we need to check if the element is fixed and if so then
3143        // disregard the elements offsetParent.
3144        const isFixed = is$1(getRaw(element, 'position'), 'fixed');
3145        const offsetParent$1 = isFixed ? Optional.none() : offsetParent(element);
3146        return offsetParent$1.orThunk(() => {
3147            const marker = SugarElement.fromTag('span');
3148            // PERFORMANCE: Append the marker to the parent element, as adding it before the current element will
3149            // trigger the styles to be recalculated which is a little costly (particularly in scroll/resize events)
3150            return parent(element).bind((parent) => {
3151                append$2(parent, marker);
3152                const offsetParent$1 = offsetParent(marker);
3153                remove$7(marker);
3154                return offsetParent$1;
3155            });
3156        });
3157    };
3158    /*
3159     * This allows the absolute coordinates to be obtained by adding the
3160     * origin to the offset coordinates and not needing to know scroll.
3161     */
3162    const getOrigin = (element) => getOffsetParent(element).map(absolute$3).getOrThunk(() => SugarPosition(0, 0));
3163
3164    const describedBy = (describedElement, describeElement) => {
3165        const describeId = Optional.from(get$g(describedElement, 'id'))
3166            .getOrThunk(() => {
3167            const id = generate$6('aria');
3168            set$9(describeElement, 'id', id);
3169            return id;
3170        });
3171        set$9(describedElement, 'aria-describedby', describeId);
3172    };
3173    const remove$1 = (describedElement) => {
3174        remove$8(describedElement, 'aria-describedby');
3175    };
3176
3177    var SimpleResultType;
3178    (function (SimpleResultType) {
3179        SimpleResultType[SimpleResultType["Error"] = 0] = "Error";
3180        SimpleResultType[SimpleResultType["Value"] = 1] = "Value";
3181    })(SimpleResultType || (SimpleResultType = {}));
3182    const fold$1 = (res, onError, onValue) => res.stype === SimpleResultType.Error ? onError(res.serror) : onValue(res.svalue);
3183    const partition$1 = (results) => {
3184        const values = [];
3185        const errors = [];
3186        each$1(results, (obj) => {
3187            fold$1(obj, (err) => errors.push(err), (val) => values.push(val));
3188        });
3189        return { values, errors };
3190    };
3191    const mapError = (res, f) => {
3192        if (res.stype === SimpleResultType.Error) {
3193            return { stype: SimpleResultType.Error, serror: f(res.serror) };
3194        }
3195        else {
3196            return res;
3197        }
3198    };
3199    const map = (res, f) => {
3200        if (res.stype === SimpleResultType.Value) {
3201            return { stype: SimpleResultType.Value, svalue: f(res.svalue) };
3202        }
3203        else {
3204            return res;
3205        }
3206    };
3207    const bind = (res, f) => {
3208        if (res.stype === SimpleResultType.Value) {
3209            return f(res.svalue);
3210        }
3211        else {
3212            return res;
3213        }
3214    };
3215    const bindError = (res, f) => {
3216        if (res.stype === SimpleResultType.Error) {
3217            return f(res.serror);
3218        }
3219        else {
3220            return res;
3221        }
3222    };
3223    const svalue = (v) => ({ stype: SimpleResultType.Value, svalue: v });
3224    const serror = (e) => ({ stype: SimpleResultType.Error, serror: e });
3225    const toResult$1 = (res) => fold$1(res, Result.error, Result.value);
3226    const fromResult = (res) => res.fold(serror, svalue);
3227    const SimpleResult = {
3228        fromResult,
3229        toResult: toResult$1,
3230        svalue,
3231        partition: partition$1,
3232        serror,
3233        bind,
3234        bindError,
3235        map,
3236        mapError,
3237        fold: fold$1
3238    };
3239
3240    const formatObj = (input) => {
3241        return isObject(input) && keys(input).length > 100 ? ' removed due to size' : JSON.stringify(input, null, 2);
3242    };
3243    const formatErrors = (errors) => {
3244        const es = errors.length > 10 ? errors.slice(0, 10).concat([
3245            {
3246                path: [],
3247                getErrorInfo: constant$1('... (only showing first ten failures)')
3248            }
3249        ]) : errors;
3250        // TODO: Work out a better split between PrettyPrinter and SchemaError
3251        return map$2(es, (e) => {
3252            return 'Failed path: (' + e.path.join(' > ') + ')\n' + e.getErrorInfo();
3253        });
3254    };
3255
3256    const nu$7 = (path, getErrorInfo) => {
3257        return SimpleResult.serror([{
3258                path,
3259                // This is lazy so that it isn't calculated unnecessarily
3260                getErrorInfo
3261            }]);
3262    };
3263    const missingRequired = (path, key, obj) => nu$7(path, () => 'Could not find valid *required* value for "' + key + '" in ' + formatObj(obj));
3264    const missingKey = (path, key) => nu$7(path, () => 'Choice schema did not contain choice key: "' + key + '"');
3265    const missingBranch = (path, branches, branch) => nu$7(path, () => 'The chosen schema: "' + branch + '" did not exist in branches: ' + formatObj(branches));
3266    const unsupportedFields = (path, unsupported) => nu$7(path, () => 'There are unsupported fields: [' + unsupported.join(', ') + '] specified');
3267    const custom = (path, err) => nu$7(path, constant$1(err));
3268
3269    const value$1 = (validator) => {
3270        const extract = (path, val) => {
3271            return SimpleResult.bindError(validator(val), (err) => custom(path, err));
3272        };
3273        const toString = constant$1('val');
3274        return {
3275            extract,
3276            toString
3277        };
3278    };
3279    const anyValue$1 = value$1(SimpleResult.svalue);
3280
3281    const anyValue = constant$1(anyValue$1);
3282    const typedValue = (validator, expectedType) => value$1((a) => {
3283        const actualType = typeof a;
3284        return validator(a) ? SimpleResult.svalue(a) : SimpleResult.serror(`Expected type: ${expectedType} but got: ${actualType}`);
3285    });
3286    const number = typedValue(isNumber, 'number');
3287    const string = typedValue(isString, 'string');
3288    const boolean = typedValue(isBoolean, 'boolean');
3289    const functionProcessor = typedValue(isFunction, 'function');
3290    // Test if a value can be copied by the structured clone algorithm and hence sendable via postMessage
3291    // https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
3292    // from https://stackoverflow.com/a/32673910/7377237 with adjustments for typescript
3293    const isPostMessageable = (val) => {
3294        if (Object(val) !== val) { // Primitive value
3295            return true;
3296        }
3297        switch ({}.toString.call(val).slice(8, -1)) { // Class
3298            case 'Boolean':
3299            case 'Number':
3300            case 'String':
3301            case 'Date':
3302            case 'RegExp':
3303            case 'Blob':
3304            case 'FileList':
3305            case 'ImageData':
3306            case 'ImageBitmap':
3307            case 'ArrayBuffer':
3308                return true;
3309            case 'Array':
3310            case 'Object':
3311                return Object.keys(val).every((prop) => isPostMessageable(val[prop]));
3312            default:
3313                return false;
3314        }
3315    };
3316    const postMessageable = value$1((a) => {
3317        if (isPostMessageable(a)) {
3318            return SimpleResult.svalue(a);
3319        }
3320        else {
3321            return SimpleResult.serror('Expected value to be acceptable for sending via postMessage');
3322        }
3323    });
3324
3325    const required$2 = () => ({ tag: "required" /* FieldPresenceTag.Required */, process: {} });
3326    const defaultedThunk = (fallbackThunk) => ({ tag: "defaultedThunk" /* FieldPresenceTag.DefaultedThunk */, process: fallbackThunk });
3327    const defaulted$1 = (fallback) => defaultedThunk(constant$1(fallback));
3328    const asOption = () => ({ tag: "option" /* FieldPresenceTag.Option */, process: {} });
3329    const mergeWithThunk = (baseThunk) => ({ tag: "mergeWithThunk" /* FieldPresenceTag.MergeWithThunk */, process: baseThunk });
3330    const mergeWith = (base) => mergeWithThunk(constant$1(base));
3331
3332    const field$2 = (key, newKey, presence, prop) => ({ tag: "field" /* FieldTag.Field */, key, newKey, presence, prop });
3333    const customField$1 = (newKey, instantiator) => ({ tag: "custom" /* FieldTag.CustomField */, newKey, instantiator });
3334    const fold = (value, ifField, ifCustom) => {
3335        switch (value.tag) {
3336            case "field" /* FieldTag.Field */:
3337                return ifField(value.key, value.newKey, value.presence, value.prop);
3338            case "custom" /* FieldTag.CustomField */:
3339                return ifCustom(value.newKey, value.instantiator);
3340        }
3341    };
3342
3343    const mergeValues$1 = (values, base) => {
3344        return SimpleResult.svalue(deepMerge(base, merge$1.apply(undefined, values)));
3345    };
3346    const mergeErrors$1 = (errors) => compose(SimpleResult.serror, flatten)(errors);
3347    const consolidateObj = (objects, base) => {
3348        const partition = SimpleResult.partition(objects);
3349        return partition.errors.length > 0 ? mergeErrors$1(partition.errors) : mergeValues$1(partition.values, base);
3350    };
3351    const consolidateArr = (objects) => {
3352        const partitions = SimpleResult.partition(objects);
3353        return partitions.errors.length > 0 ? mergeErrors$1(partitions.errors) : SimpleResult.svalue(partitions.values);
3354    };
3355    const ResultCombine = {
3356        consolidateObj,
3357        consolidateArr
3358    };
3359
3360    const requiredAccess = (path, obj, key, bundle) => 
3361    // In required mode, if it is undefined, it is an error.
3362    get$h(obj, key).fold(() => missingRequired(path, key, obj), bundle);
3363    const fallbackAccess = (obj, key, fallback, bundle) => {
3364        const v = get$h(obj, key).getOrThunk(() => fallback(obj));
3365        return bundle(v);
3366    };
3367    const optionAccess = (obj, key, bundle) => bundle(get$h(obj, key));
3368    const optionDefaultedAccess = (obj, key, fallback, bundle) => {
3369        const opt = get$h(obj, key).map((val) => val === true ? fallback(obj) : val);
3370        return bundle(opt);
3371    };
3372    const extractField = (field, path, obj, key, prop) => {
3373        const bundle = (av) => prop.extract(path.concat([key]), av);
3374        const bundleAsOption = (optValue) => optValue.fold(() => SimpleResult.svalue(Optional.none()), (ov) => {
3375            const result = prop.extract(path.concat([key]), ov);
3376            return SimpleResult.map(result, Optional.some);
3377        });
3378        switch (field.tag) {
3379            case "required" /* FieldPresenceTag.Required */:
3380                return requiredAccess(path, obj, key, bundle);
3381            case "defaultedThunk" /* FieldPresenceTag.DefaultedThunk */:
3382                return fallbackAccess(obj, key, field.process, bundle);
3383            case "option" /* FieldPresenceTag.Option */:
3384                return optionAccess(obj, key, bundleAsOption);
3385            case "defaultedOptionThunk" /* FieldPresenceTag.DefaultedOptionThunk */:
3386                return optionDefaultedAccess(obj, key, field.process, bundleAsOption);
3387            case "mergeWithThunk" /* FieldPresenceTag.MergeWithThunk */: {
3388                return fallbackAccess(obj, key, constant$1({}), (v) => {
3389                    const result = deepMerge(field.process(obj), v);
3390                    return bundle(result);
3391                });
3392            }
3393        }
3394    };
3395    const extractFields = (path, obj, fields) => {
3396        const success = {};
3397        const errors = [];
3398        // PERFORMANCE: We use a for loop here instead of Arr.each as this is a hot code path
3399        for (const field of fields) {
3400            fold(field, (key, newKey, presence, prop) => {
3401                const result = extractField(presence, path, obj, key, prop);
3402                SimpleResult.fold(result, (err) => {
3403                    errors.push(...err);
3404                }, (res) => {
3405                    success[newKey] = res;
3406                });
3407            }, (newKey, instantiator) => {
3408                success[newKey] = instantiator(obj);
3409            });
3410        }
3411        return errors.length > 0 ? SimpleResult.serror(errors) : SimpleResult.svalue(success);
3412    };
3413    const valueThunk = (getDelegate) => {
3414        const extract = (path, val) => getDelegate().extract(path, val);
3415        const toString = () => getDelegate().toString();
3416        return {
3417            extract,
3418            toString
3419        };
3420    };
3421    // This is because Obj.keys can return things where the key is set to undefined.
3422    const getSetKeys = (obj) => keys(filter$1(obj, isNonNullable));
3423    const objOfOnly = (fields) => {
3424        const delegate = objOf(fields);
3425        const fieldNames = foldr(fields, (acc, value) => {
3426            return fold(value, (key) => deepMerge(acc, { [key]: true }), constant$1(acc));
3427        }, {});
3428        const extract = (path, o) => {
3429            const keys = isBoolean(o) ? [] : getSetKeys(o);
3430            const extra = filter$2(keys, (k) => !hasNonNullableKey(fieldNames, k));
3431            return extra.length === 0 ? delegate.extract(path, o) : unsupportedFields(path, extra);
3432        };
3433        return {
3434            extract,
3435            toString: delegate.toString
3436        };
3437    };
3438    const objOf = (values) => {
3439        const extract = (path, o) => extractFields(path, o, values);
3440        const toString = () => {
3441            const fieldStrings = map$2(values, (value) => fold(value, (key, _okey, _presence, prop) => key + ' -> ' + prop.toString(), (newKey, _instantiator) => 'state(' + newKey + ')'));
3442            return 'obj{\n' + fieldStrings.join('\n') + '}';
3443        };
3444        return {
3445            extract,
3446            toString
3447        };
3448    };
3449    const arrOf = (prop) => {
3450        const extract = (path, array) => {
3451            const results = map$2(array, (a, i) => prop.extract(path.concat(['[' + i + ']']), a));
3452            return ResultCombine.consolidateArr(results);
3453        };
3454        const toString = () => 'array(' + prop.toString() + ')';
3455        return {
3456            extract,
3457            toString
3458        };
3459    };
3460    const oneOf = (props, rawF) => {
3461        // If f is not supplied, then use identity.
3462        const f = rawF !== undefined ? rawF : identity;
3463        const extract = (path, val) => {
3464            const errors = [];
3465            // Return on first match
3466            for (const prop of props) {
3467                const res = prop.extract(path, val);
3468                if (res.stype === SimpleResultType.Value) {
3469                    return {
3470                        stype: SimpleResultType.Value,
3471                        svalue: f(res.svalue)
3472                    };
3473                }
3474                errors.push(res);
3475            }
3476            // All failed, return errors
3477            return ResultCombine.consolidateArr(errors);
3478        };
3479        const toString = () => 'oneOf(' + map$2(props, (prop) => prop.toString()).join(', ') + ')';
3480        return {
3481            extract,
3482            toString
3483        };
3484    };
3485    const setOf$1 = (validator, prop) => {
3486        const validateKeys = (path, keys) => arrOf(value$1(validator)).extract(path, keys);
3487        const extract = (path, o) => {
3488            //
3489            const keys$1 = keys(o);
3490            const validatedKeys = validateKeys(path, keys$1);
3491            return SimpleResult.bind(validatedKeys, (validKeys) => {
3492                const schema = map$2(validKeys, (vk) => {
3493                    return field$2(vk, vk, required$2(), prop);
3494                });
3495                return objOf(schema).extract(path, o);
3496            });
3497        };
3498        const toString = () => 'setOf(' + prop.toString() + ')';
3499        return {
3500            extract,
3501            toString
3502        };
3503    };
3504    const thunk = (_desc, processor) => {
3505        const getP = cached(processor);
3506        const extract = (path, val) => getP().extract(path, val);
3507        const toString = () => getP().toString();
3508        return {
3509            extract,
3510            toString
3511        };
3512    };
3513    const arrOfObj = compose(arrOf, objOf);
3514
3515    const chooseFrom = (path, input, branches, ch) => {
3516        const fields = get$h(branches, ch);
3517        return fields.fold(() => missingBranch(path, branches, ch), (vp) => vp.extract(path.concat(['branch: ' + ch]), input));
3518    };
3519    // The purpose of choose is to have a key which picks which of the schemas to follow.
3520    // The key will index into the object of schemas: branches
3521    const choose$2 = (key, branches) => {
3522        const extract = (path, input) => {
3523            const choice = get$h(input, key);
3524            return choice.fold(() => missingKey(path, key), (chosen) => chooseFrom(path, input, branches, chosen));
3525        };
3526        const toString = () => 'chooseOn(' + key + '). Possible values: ' + keys(branches);
3527        return {
3528            extract,
3529            toString
3530        };
3531    };
3532
3533    const arrOfVal = () => arrOf(anyValue$1);
3534    const valueOf = (validator) => value$1((v) => validator(v).fold(SimpleResult.serror, SimpleResult.svalue));
3535    const setOf = (validator, prop) => setOf$1((v) => SimpleResult.fromResult(validator(v)), prop);
3536    const extractValue = (label, prop, obj) => {
3537        const res = prop.extract([label], obj);
3538        return SimpleResult.mapError(res, (errs) => ({ input: obj, errors: errs }));
3539    };
3540    const asRaw = (label, prop, obj) => SimpleResult.toResult(extractValue(label, prop, obj));
3541    const getOrDie = (extraction) => {
3542        return extraction.fold((errInfo) => {
3543            // A readable version of the error.
3544            throw new Error(formatError(errInfo));
3545        }, identity);
3546    };
3547    const asRawOrDie$1 = (label, prop, obj) => getOrDie(asRaw(label, prop, obj));
3548    const formatError = (errInfo) => {
3549        return 'Errors: \n' + formatErrors(errInfo.errors).join('\n') +
3550            '\n\nInput object: ' + formatObj(errInfo.input);
3551    };
3552    const choose$1 = (key, branches) => choose$2(key, map$1(branches, objOf));
3553    const thunkOf = (desc, schema) => thunk(desc, schema);
3554
3555    const field$1 = field$2;
3556    const customField = customField$1;
3557    const validateEnum = (values) => valueOf((value) => contains$2(values, value) ?
3558        Result.value(value) :
3559        Result.error(`Unsupported value: "${value}", choose one of "${values.join(', ')}".`));
3560    const required$1 = (key) => field$1(key, key, required$2(), anyValue());
3561    const requiredOf = (key, schema) => field$1(key, key, required$2(), schema);
3562    const requiredNumber = (key) => requiredOf(key, number);
3563    const requiredString = (key) => requiredOf(key, string);
3564    const requiredStringEnum = (key, values) => field$1(key, key, required$2(), validateEnum(values));
3565    const requiredFunction = (key) => requiredOf(key, functionProcessor);
3566    const forbid = (key, message) => field$1(key, key, asOption(), value$1((_v) => SimpleResult.serror('The field: ' + key + ' is forbidden. ' + message)));
3567    const requiredObjOf = (key, objSchema) => field$1(key, key, required$2(), objOf(objSchema));
3568    const requiredArrayOfObj = (key, objFields) => field$1(key, key, required$2(), arrOfObj(objFields));
3569    const requiredArrayOf = (key, schema) => field$1(key, key, required$2(), arrOf(schema));
3570    const option$3 = (key) => field$1(key, key, asOption(), anyValue());
3571    const optionOf = (key, schema) => field$1(key, key, asOption(), schema);
3572    const optionNumber = (key) => optionOf(key, number);
3573    const optionString = (key) => optionOf(key, string);
3574    const optionStringEnum = (key, values) => optionOf(key, validateEnum(values));
3575    const optionBoolean = (key) => optionOf(key, boolean);
3576    const optionFunction = (key) => optionOf(key, functionProcessor);
3577    const optionArrayOf = (key, schema) => optionOf(key, arrOf(schema));
3578    const optionObjOf = (key, objSchema) => optionOf(key, objOf(objSchema));
3579    const optionObjOfOnly = (key, objSchema) => optionOf(key, objOfOnly(objSchema));
3580    const defaulted = (key, fallback) => field$1(key, key, defaulted$1(fallback), anyValue());
3581    const defaultedOf = (key, fallback, schema) => field$1(key, key, defaulted$1(fallback), schema);
3582    const defaultedNumber = (key, fallback) => defaultedOf(key, fallback, number);
3583    const defaultedString = (key, fallback) => defaultedOf(key, fallback, string);
3584    const defaultedStringEnum = (key, fallback, values) => defaultedOf(key, fallback, validateEnum(values));
3585    const defaultedBoolean = (key, fallback) => defaultedOf(key, fallback, boolean);
3586    const defaultedFunction = (key, fallback) => defaultedOf(key, fallback, functionProcessor);
3587    const defaultedPostMsg = (key, fallback) => defaultedOf(key, fallback, postMessageable);
3588    const defaultedArrayOf = (key, fallback, schema) => defaultedOf(key, fallback, arrOf(schema));
3589    const defaultedObjOf = (key, fallback, objSchema) => defaultedOf(key, fallback, objOf(objSchema));
3590
3591    const exclude$1 = (obj, fields) => {
3592        const r = {};
3593        each(obj, (v, k) => {
3594            if (!contains$2(fields, k)) {
3595                r[k] = v;
3596            }
3597        });
3598        return r;
3599    };
3600
3601    const wrap$1 = (key, value) => ({ [key]: value });
3602    const wrapAll$1 = (keyvalues) => {
3603        const r = {};
3604        each$1(keyvalues, (kv) => {
3605            r[kv.key] = kv.value;
3606        });
3607        return r;
3608    };
3609
3610    const exclude = (obj, fields) => exclude$1(obj, fields);
3611    const wrap = (key, value) => wrap$1(key, value);
3612    const wrapAll = (keyvalues) => wrapAll$1(keyvalues);
3613    const mergeValues = (values, base) => {
3614        return values.length === 0 ? Result.value(base) : Result.value(deepMerge(base, merge$1.apply(undefined, values))
3615        // Merger.deepMerge.apply(undefined, [ base ].concat(values))
3616        );
3617    };
3618    const mergeErrors = (errors) => Result.error(flatten(errors));
3619    const consolidate = (objs, base) => {
3620        const partitions = partition$2(objs);
3621        return partitions.errors.length > 0 ? mergeErrors(partitions.errors) : mergeValues(partitions.values, base);
3622    };
3623
3624    const constant = constant$1;
3625    const touchstart = constant('touchstart');
3626    const touchmove = constant('touchmove');
3627    const touchend = constant('touchend');
3628    const touchcancel = constant('touchcancel');
3629    const mousedown = constant('mousedown');
3630    const mousemove = constant('mousemove');
3631    const mouseout = constant('mouseout');
3632    const mouseup = constant('mouseup');
3633    const mouseover = constant('mouseover');
3634    // Not really a native event as it has to be simulated
3635    const focusin = constant('focusin');
3636    const focusout = constant('focusout');
vendor: 11,596 bytes, lines 3637-3886
3637    const keydown = constant('keydown');
3638    const keyup = constant('keyup');
3639    const input = constant('input');
3640    const change = constant('change');
3641    const click = constant('click');
3642    const transitioncancel = constant('transitioncancel');
3643    const transitionend = constant('transitionend');
3644    const transitionstart = constant('transitionstart');
3645    const selectstart = constant('selectstart');
3646    const pointerdown = constant('pointerdown');
3647    const pointermove = constant('pointermove');
3648    const pointerup = constant('pointerup');
3649    const lostpointercapture = constant('lostpointercapture');
3650
3651    const prefixName = (name) => constant$1('alloy.' + name);
3652    const alloy = { tap: prefixName('tap') };
3653    // This is used to pass focus to a component. A component might interpret
3654    // this event and pass the DOM focus to one of its children, depending on its
3655    // focus model.
3656    const focus$3 = prefixName('focus');
3657    // This event is fired a small amount of time after the blur has fired. This
3658    // allows the handler to know what was the focused element, and what is now.
3659    const postBlur = prefixName('blur.post');
3660    // This event is fired a small amount of time after the paste event has fired.
3661    const postPaste = prefixName('paste.post');
3662    // This event is fired by gui.broadcast*. It is defined by 'receivers'
3663    const receive = prefixName('receive');
3664    // This event is for executing buttons and things that have (mostly) enter actions
3665    const execute$5 = prefixName('execute');
3666    // This event is used by a menu to tell an item to focus itself because it has been
3667    // selected. This might automatically focus inside the item, it might focus the outer
3668    // part of the widget etc.
3669    const focusItem = prefixName('focus.item');
3670    // This event represents a touchstart and touchend on the same location, and fires on
3671    // the touchend
3672    const tap = alloy.tap;
3673    // This event represents a longpress on the same location
3674    const longpress = prefixName('longpress');
3675    // Fire by a child element to tell the outer element to close
3676    const sandboxClose = prefixName('sandbox.close');
3677    // Tell the typeahead to cancel any pending fetches (that haven't already executed)
3678    const typeaheadCancel = prefixName('typeahead.cancel');
3679    // Fired when adding to a world
3680    const systemInit = prefixName('system.init');
3681    // Fired when a touchmove on the document happens
3682    const documentTouchmove = prefixName('system.touchmove');
3683    // Fired when a touchend on the document happens
3684    const documentTouchend = prefixName('system.touchend');
3685    // Fired when the window scrolls
3686    const windowScroll = prefixName('system.scroll');
3687    // Fired when the window resizes
3688    const windowResize = prefixName('system.resize');
3689    const attachedToDom = prefixName('system.attached');
3690    const detachedFromDom = prefixName('system.detached');
3691    const dismissRequested = prefixName('system.dismissRequested');
3692    const repositionRequested = prefixName('system.repositionRequested');
3693    const focusShifted = prefixName('focusmanager.shifted');
3694    // Fired when slots are made hidden/shown
3695    const slotVisibility = prefixName('slotcontainer.visibility');
3696    // Used for containers outside the mothership that scroll. Used by docking.
3697    const externalElementScroll = prefixName('system.external.element.scroll');
3698    const changeTab = prefixName('change.tab');
3699    const dismissTab = prefixName('dismiss.tab');
3700    const highlight$1 = prefixName('highlight');
3701    const dehighlight$1 = prefixName('dehighlight');
3702
3703    const element = (elem) => getHtml(elem);
3704
3705    const unknown = 'unknown';
3706    /*
3707      typescipt qwerk:
3708      const debugging: boolean = true;
3709      if (boolean === false) {  -> this throws a type error! // TS2365:Operator '===' cannot be applied to types 'false' and 'true'
3710        https://www.typescriptlang.org/play/#src=const%20foo%3A%20boolean%20%3D%20true%3B%0D%0A%0D%0Aif%20(foo%20%3D%3D%3D%20false)%20%7B%0D%0A%20%20%20%20%0D%0A%7D
3711      }
3712    */
3713    const debugging = true;
3714    var EventConfiguration;
3715    (function (EventConfiguration) {
3716        EventConfiguration[EventConfiguration["STOP"] = 0] = "STOP";
3717        EventConfiguration[EventConfiguration["NORMAL"] = 1] = "NORMAL";
3718        EventConfiguration[EventConfiguration["LOGGING"] = 2] = "LOGGING";
3719    })(EventConfiguration || (EventConfiguration = {}));
3720    const eventConfig = Cell({});
3721    const makeEventLogger = (eventName, initialTarget) => {
3722        const sequence = [];
3723        const startTime = new Date().getTime();
3724        return {
3725            logEventCut: (_name, target, purpose) => {
3726                sequence.push({ outcome: 'cut', target, purpose });
3727            },
3728            logEventStopped: (_name, target, purpose) => {
3729                sequence.push({ outcome: 'stopped', target, purpose });
3730            },
3731            logNoParent: (_name, target, purpose) => {
3732                sequence.push({ outcome: 'no-parent', target, purpose });
3733            },
3734            logEventNoHandlers: (_name, target) => {
3735                sequence.push({ outcome: 'no-handlers-left', target });
3736            },
3737            logEventResponse: (_name, target, purpose) => {
3738                sequence.push({ outcome: 'response', purpose, target });
3739            },
3740            write: () => {
3741                const finishTime = new Date().getTime();
3742                if (contains$2(['mousemove', 'mouseover', 'mouseout', systemInit()], eventName)) {
3743                    return;
3744                }
3745                // eslint-disable-next-line no-console
3746                console.log(eventName, {
3747                    event: eventName,
3748                    time: finishTime - startTime,
3749                    target: initialTarget.dom,
3750                    sequence: map$2(sequence, (s) => {
3751                        if (!contains$2(['cut', 'stopped', 'response'], s.outcome)) {
3752                            return s.outcome;
3753                        }
3754                        else {
3755                            return '{' + s.purpose + '} ' + s.outcome + ' at (' + element(s.target) + ')';
3756                        }
3757                    })
3758                });
3759            }
3760        };
3761    };
3762    const processEvent = (eventName, initialTarget, f) => {
3763        const status = get$h(eventConfig.get(), eventName).orThunk(() => {
3764            const patterns = keys(eventConfig.get());
3765            return findMap(patterns, (p) => eventName.indexOf(p) > -1 ? Optional.some(eventConfig.get()[p]) : Optional.none());
3766        }).getOr(EventConfiguration.NORMAL);
3767        switch (status) {
3768            case EventConfiguration.NORMAL:
3769                return f(noLogger());
3770            case EventConfiguration.LOGGING: {
3771                const logger = makeEventLogger(eventName, initialTarget);
3772                const output = f(logger);
3773                logger.write();
3774                return output;
3775            }
3776            case EventConfiguration.STOP:
3777                // Does not even run the function to trigger event and listen to handlers
3778                return true;
3779        }
3780    };
3781    // Ignore these files in the error stack
3782    const path = [
3783        'alloy/data/Fields',
3784        'alloy/debugging/Debugging'
3785    ];
3786    const getTrace = () => {
3787        if (debugging === false) {
3788            return unknown;
3789        }
3790        const err = new Error();
3791        if (err.stack !== undefined) {
3792            const lines = err.stack.split('\n');
3793            return find$5(lines, (line) => line.indexOf('alloy') > 0 && !exists(path, (p) => line.indexOf(p) > -1)).getOr(unknown);
3794        }
3795        else {
3796            return unknown;
3797        }
3798    };
3799    const ignoreEvent = {
3800        logEventCut: noop,
3801        logEventStopped: noop,
3802        logNoParent: noop,
3803        logEventNoHandlers: noop,
3804        logEventResponse: noop,
3805        write: noop
3806    };
3807    const monitorEvent = (eventName, initialTarget, f) => processEvent(eventName, initialTarget, f);
3808    const noLogger = constant$1(ignoreEvent);
3809
3810    const menuFields = constant$1([
3811        required$1('menu'),
3812        required$1('selectedMenu')
3813    ]);
3814    const itemFields = constant$1([
3815        required$1('item'),
3816        required$1('selectedItem')
3817    ]);
3818    constant$1(objOf(itemFields().concat(menuFields())));
3819    const itemSchema$3 = constant$1(objOf(itemFields()));
3820
3821    const _initSize = requiredObjOf('initSize', [
3822        required$1('numColumns'),
3823        required$1('numRows')
3824    ]);
3825    const itemMarkers = () => requiredOf('markers', itemSchema$3());
3826    const tieredMenuMarkers = () => requiredObjOf('markers', [
3827        required$1('backgroundMenu')
3828    ].concat(menuFields()).concat(itemFields()));
3829    const markers$1 = (required) => requiredObjOf('markers', map$2(required, required$1));
3830    const onPresenceHandler = (label, fieldName, presence) => {
3831        // We care about where the handler was declared (in terms of which schema)
3832        getTrace();
3833        return field$1(fieldName, fieldName, presence, 
3834        // Apply some wrapping to their supplied function
3835        valueOf((f) => Result.value((...args) => {
3836            return f.apply(undefined, args);
3837        })));
3838    };
3839    const onHandler = (fieldName) => onPresenceHandler('onHandler', fieldName, defaulted$1(noop));
3840    const onKeyboardHandler = (fieldName) => onPresenceHandler('onKeyboardHandler', fieldName, defaulted$1(Optional.none));
3841    const onStrictHandler = (fieldName) => onPresenceHandler('onHandler', fieldName, required$2());
3842    const onStrictKeyboardHandler = (fieldName) => onPresenceHandler('onKeyboardHandler', fieldName, required$2());
3843    const output$1 = (name, value) => customField(name, constant$1(value));
3844    const snapshot = (name) => customField(name, identity);
3845    const initSize = constant$1(_initSize);
3846
3847    const markAsBehaviourApi = (f, apiName, apiFunction) => {
3848        const delegate = apiFunction.toString();
3849        const endIndex = delegate.indexOf(')') + 1;
3850        const openBracketIndex = delegate.indexOf('(');
3851        const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\s*/);
3852        f.toFunctionAnnotation = () => ({
3853            name: apiName,
3854            parameters: cleanParameters(parameters.slice(0, 1).concat(parameters.slice(3)))
3855        });
3856        return f;
3857    };
3858    // Remove any comment (/*) at end of parameter names
3859    const cleanParameters = (parameters) => map$2(parameters, (p) => endsWith(p, '/*') ? p.substring(0, p.length - '/*'.length) : p);
3860    const markAsExtraApi = (f, extraName) => {
3861        const delegate = f.toString();
3862        const endIndex = delegate.indexOf(')') + 1;
3863        const openBracketIndex = delegate.indexOf('(');
3864        const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\s*/);
3865        f.toFunctionAnnotation = () => ({
3866            name: extraName,
3867            parameters: cleanParameters(parameters)
3868        });
3869        return f;
3870    };
3871    const markAsSketchApi = (f, apiFunction) => {
3872        const delegate = apiFunction.toString();
3873        const endIndex = delegate.indexOf(')') + 1;
3874        const openBracketIndex = delegate.indexOf('(');
3875        const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\s*/);
3876        f.toFunctionAnnotation = () => ({
3877            name: 'OVERRIDE',
3878            parameters: cleanParameters(parameters.slice(1))
3879        });
3880        return f;
3881    };
3882
3883    const DelayedFunction = (fun, delay) => {
3884        let ref = null;
3885        const schedule = (...args) => {
3886            ref = setTimeout(() => {
3887                fun.apply(null, args);
3888                ref = null;
3889            }, delay);
3890        };
3891        const cancel = () => {
3892            if (ref !== null) {
3893                clearTimeout(ref);
3894                ref = null;
3895            }
3896        };
3897        return {
3898            cancel,
3899            schedule
3900        };
3901    };
3902
3903    const SIGNIFICANT_MOVE = 5;
3904    const LONGPRESS_DELAY = 400;
3905    const getTouch = (event) => {
3906        const raw = event.raw;
3907        if (raw.touches === undefined || raw.touches.length !== 1) {
3908            return Optional.none();
3909        }
3910        return Optional.some(raw.touches[0]);
3911    };
3912    // Check to see if the touch has changed a *significant* amount
3913    const isFarEnough = (touch, data) => {
3914        const distX = Math.abs(touch.clientX - data.x);
3915        const distY = Math.abs(touch.clientY - data.y);
3916        return distX > SIGNIFICANT_MOVE || distY > SIGNIFICANT_MOVE;
3917    };
3918    const monitor = (settings) => {
3919        /* A tap event is a combination of touchstart and touchend on the same element
3920         * without a *significant* touchmove in between.
3921         */
3922        const startData = value$2();
3923        const longpressFired = Cell(false);
3924        const longpress$1 = DelayedFunction((event) => {
3925            settings.triggerEvent(longpress(), event);
3926            longpressFired.set(true);
3927        }, LONGPRESS_DELAY);
3928        const handleTouchstart = (event) => {
3929            getTouch(event).each((touch) => {
3930                longpress$1.cancel();
3931                const data = {
3932                    x: touch.clientX,
3933                    y: touch.clientY,
3934                    target: event.target
3935                };
3936                longpress$1.schedule(event);
3937                longpressFired.set(false);
3938                startData.set(data);
3939            });
3940            return Optional.none();
3941        };
3942        const handleTouchmove = (event) => {
3943            longpress$1.cancel();
3944            getTouch(event).each((touch) => {
3945                startData.on((data) => {
3946                    if (isFarEnough(touch, data)) {
3947                        startData.clear();
3948                    }
3949                });
3950            });
3951            return Optional.none();
3952        };
3953        const handleTouchend = (event) => {
3954            longpress$1.cancel();
3955            const isSame = (data) => eq(data.target, event.target);
3956            return startData.get().filter(isSame).map((_data) => {
3957                if (longpressFired.get()) {
3958                    event.prevent();
3959                    return false;
3960                }
3961                else {
3962                    return settings.triggerEvent(tap(), event);
3963                }
3964            });
3965        };
3966        const handlers = wrapAll([
3967            { key: touchstart(), value: handleTouchstart },
3968            { key: touchmove(), value: handleTouchmove },
3969            { key: touchend(), value: handleTouchend }
3970        ]);
3971        const fireIfReady = (event, type) => get$h(handlers, type).bind((handler) => handler(event));
3972        return {
3973            fireIfReady
3974        };
3975    };
3976
3977    var FocusInsideModes;
3978    (function (FocusInsideModes) {
3979        FocusInsideModes["OnFocusMode"] = "onFocus";
3980        FocusInsideModes["OnEnterOrSpaceMode"] = "onEnterOrSpace";
3981        FocusInsideModes["OnApiMode"] = "onApi";
3982    })(FocusInsideModes || (FocusInsideModes = {}));
3983
3984    const _placeholder = 'placeholder';
3985    const adt$7 = Adt.generate([
3986        { single: ['required', 'valueThunk'] },
3987        { multiple: ['required', 'valueThunks'] }
3988    ]);
3989    const isSubstituted = (spec) => has$2(spec, 'uiType');
3990    const subPlaceholder = (owner, detail, compSpec, placeholders) => {
3991        if (owner.exists((o) => o !== compSpec.owner)) {
3992            return adt$7.single(true, constant$1(compSpec));
3993        }
3994        // Ignore having to find something for the time being.
3995        return get$h(placeholders, compSpec.name).fold(() => {
3996            throw new Error('Unknown placeholder component: ' + compSpec.name + '\nKnown: [' +
3997                keys(placeholders) + ']\nNamespace: ' + owner.getOr('none') + '\nSpec: ' + JSON.stringify(compSpec, null, 2));
3998        }, (newSpec) => 
3999        // Must return a single/multiple type
4000        newSpec.replace());
4001    };
4002    const scan = (owner, detail, compSpec, placeholders) => {
vendor: 16,137 bytes, lines 4003-4370
4003        if (isSubstituted(compSpec) && compSpec.uiType === _placeholder) {
4004            return subPlaceholder(owner, detail, compSpec, placeholders);
4005        }
4006        else {
4007            return adt$7.single(false, constant$1(compSpec));
4008        }
4009    };
4010    const substitute = (owner, detail, compSpec, placeholders) => {
4011        const base = scan(owner, detail, compSpec, placeholders);
4012        return base.fold((req, valueThunk) => {
4013            const value = isSubstituted(compSpec) ? valueThunk(detail, compSpec.config, compSpec.validated) : valueThunk(detail);
4014            const childSpecs = get$h(value, 'components').getOr([]);
4015            const substituted = bind$3(childSpecs, (c) => substitute(owner, detail, c, placeholders));
4016            return [
4017                {
4018                    ...value,
4019                    components: substituted
4020                }
4021            ];
4022        }, (req, valuesThunk) => {
4023            if (isSubstituted(compSpec)) {
4024                const values = valuesThunk(detail, compSpec.config, compSpec.validated);
4025                // Allow a preprocessing step for groups before returning the components
4026                const preprocessor = compSpec.validated.preprocess.getOr(identity);
4027                return preprocessor(values);
4028            }
4029            else {
4030                return valuesThunk(detail);
4031            }
4032        });
4033    };
4034    const substituteAll = (owner, detail, components, placeholders) => bind$3(components, (c) => substitute(owner, detail, c, placeholders));
4035    const oneReplace = (label, replacements) => {
4036        let called = false;
4037        const used = () => called;
4038        const replace = () => {
4039            if (called) {
4040                throw new Error('Trying to use the same placeholder more than once: ' + label);
4041            }
4042            called = true;
4043            return replacements;
4044        };
4045        const required = () => replacements.fold((req, _) => req, (req, _) => req);
4046        return {
4047            name: constant$1(label),
4048            required,
4049            used,
4050            replace
4051        };
4052    };
4053    const substitutePlaces = (owner, detail, components, placeholders) => {
4054        const ps = map$1(placeholders, (ph, name) => oneReplace(name, ph));
4055        const outcome = substituteAll(owner, detail, components, ps);
4056        each(ps, (p) => {
4057            if (p.used() === false && p.required()) {
4058                throw new Error('Placeholder: ' + p.name() + ' was not found in components list\nNamespace: ' + owner.getOr('none') + '\nComponents: ' +
4059                    JSON.stringify(detail.components, null, 2));
4060            }
4061        });
4062        return outcome;
4063    };
4064    const single$2 = adt$7.single;
4065    const multiple = adt$7.multiple;
4066    const placeholder = constant$1(_placeholder);
4067
4068    const adt$6 = Adt.generate([
4069        { required: ['data'] },
4070        { external: ['data'] },
4071        { optional: ['data'] },
4072        { group: ['data'] }
4073    ]);
4074    const fFactory = defaulted('factory', { sketch: identity });
4075    const fSchema = defaulted('schema', []);
4076    const fName = required$1('name');
4077    const fPname = field$1('pname', 'pname', defaultedThunk((typeSpec) => '<alloy.' + generate$6(typeSpec.name) + '>'), anyValue());
4078    // Groups cannot choose their schema.
4079    const fGroupSchema = customField('schema', () => [
4080        option$3('preprocess')
4081    ]);
4082    const fDefaults = defaulted('defaults', constant$1({}));
4083    const fOverrides = defaulted('overrides', constant$1({}));
4084    const requiredSpec = objOf([
4085        fFactory, fSchema, fName, fPname, fDefaults, fOverrides
4086    ]);
4087    const externalSpec = objOf([
4088        fFactory, fSchema, fName, fDefaults, fOverrides
4089    ]);
4090    const optionalSpec = objOf([
4091        fFactory, fSchema, fName, fPname, fDefaults, fOverrides
4092    ]);
4093    const groupSpec = objOf([
4094        fFactory, fGroupSchema, fName,
4095        required$1('unit'),
4096        fPname, fDefaults, fOverrides
4097    ]);
4098    const asNamedPart = (part) => {
4099        return part.fold(Optional.some, Optional.none, Optional.some, Optional.some);
4100    };
4101    const name$2 = (part) => {
4102        const get = (data) => data.name;
4103        return part.fold(get, get, get, get);
4104    };
4105    const asCommon = (part) => {
4106        return part.fold(identity, identity, identity, identity);
4107    };
4108    const convert = (adtConstructor, partSchema) => (spec) => {
4109        const data = asRawOrDie$1('Converting part type', partSchema, spec);
4110        return adtConstructor(data);
4111    };
4112    const required = convert(adt$6.required, requiredSpec);
4113    const external$1 = convert(adt$6.external, externalSpec);
4114    const optional = convert(adt$6.optional, optionalSpec);
4115    const group = convert(adt$6.group, groupSpec);
4116    const original = constant$1('entirety');
4117
4118    var PartType = /*#__PURE__*/Object.freeze({
4119        __proto__: null,
4120        required: required,
4121        external: external$1,
4122        optional: optional,
4123        group: group,
4124        asNamedPart: asNamedPart,
4125        name: name$2,
4126        asCommon: asCommon,
4127        original: original
4128    });
4129
4130    const combine$2 = (detail, data, partSpec, partValidated) => 
4131    // Extremely confusing names and types :(
4132    deepMerge(data.defaults(detail, partSpec, partValidated), partSpec, { uid: detail.partUids[data.name] }, data.overrides(detail, partSpec, partValidated));
4133    const subs = (owner, detail, parts) => {
4134        const internals = {};
4135        const externals = {};
4136        each$1(parts, (part) => {
4137            part.fold(
4138            // Internal
4139            (data) => {
4140                internals[data.pname] = single$2(true, (detail, partSpec, partValidated) => data.factory.sketch(combine$2(detail, data, partSpec, partValidated)));
4141            }, 
4142            // External
4143            (data) => {
4144                const partSpec = detail.parts[data.name];
4145                externals[data.name] = constant$1(data.factory.sketch(combine$2(detail, data, partSpec[original()]), partSpec) // This is missing partValidated
4146                );
4147                // no placeholders
4148            }, 
4149            // Optional
4150            (data) => {
4151                internals[data.pname] = single$2(false, (detail, partSpec, partValidated) => data.factory.sketch(combine$2(detail, data, partSpec, partValidated)));
4152            }, 
4153            // Group
4154            (data) => {
4155                internals[data.pname] = multiple(true, (detail, _partSpec, _partValidated) => {
4156                    const units = detail[data.name];
4157                    return map$2(units, (u) => 
4158                    // Group multiples do not take the uid because there is more than one.
4159                    data.factory.sketch(deepMerge(data.defaults(detail, u, _partValidated), u, data.overrides(detail, u))));
4160                });
4161            });
4162        });
4163        return {
4164            internals: constant$1(internals),
4165            externals: constant$1(externals)
4166        };
4167    };
4168
4169    // TODO: Make more functional if performance isn't an issue.
4170    const generate$5 = (owner, parts) => {
4171        const r = {};
4172        each$1(parts, (part) => {
4173            asNamedPart(part).each((np) => {
4174                const g = doGenerateOne(owner, np.pname);
4175                r[np.name] = (config) => {
4176                    const validated = asRawOrDie$1('Part: ' + np.name + ' in ' + owner, objOf(np.schema), config);
4177                    return {
4178                        ...g,
4179                        config,
4180                        validated
4181                    };
4182                };
4183            });
4184        });
4185        return r;
4186    };
4187    // Does not have the config.
4188    const doGenerateOne = (owner, pname) => ({
4189        uiType: placeholder(),
4190        owner,
4191        name: pname
4192    });
4193    const generateOne$1 = (owner, pname, config) => ({
4194        uiType: placeholder(),
4195        owner,
4196        name: pname,
4197        config,
4198        validated: {}
4199    });
4200    const schemas = (parts) => 
4201    // This actually has to change. It needs to return the schemas for things that will
4202    // not appear in the components list, which is only externals
4203    bind$3(parts, (part) => part.fold(Optional.none, Optional.some, Optional.none, Optional.none).map((data) => requiredObjOf(data.name, data.schema.concat([
4204        snapshot(original())
4205    ]))).toArray());
4206    const names = (parts) => map$2(parts, name$2);
4207    const substitutes = (owner, detail, parts) => subs(owner, detail, parts);
4208    const components$1 = (owner, detail, internals) => substitutePlaces(Optional.some(owner), detail, detail.components, internals);
4209    const getPart = (component, detail, partKey) => {
4210        const uid = detail.partUids[partKey];
4211        return component.getSystem().getByUid(uid).toOptional();
4212    };
4213    const getPartOrDie = (component, detail, partKey) => getPart(component, detail, partKey).getOrDie('Could not find part: ' + partKey);
4214    const getParts = (component, detail, partKeys) => {
4215        const r = {};
4216        const uids = detail.partUids;
4217        const system = component.getSystem();
4218        each$1(partKeys, (pk) => {
4219            r[pk] = constant$1(system.getByUid(uids[pk]));
4220        });
4221        return r;
4222    };
4223    const getAllParts = (component, detail) => {
4224        const system = component.getSystem();
4225        return map$1(detail.partUids, (pUid, _k) => constant$1(system.getByUid(pUid)));
4226    };
4227    const getAllPartNames = (detail) => keys(detail.partUids);
4228    const getPartsOrDie = (component, detail, partKeys) => {
4229        const r = {};
4230        const uids = detail.partUids;
4231        const system = component.getSystem();
4232        each$1(partKeys, (pk) => {
4233            r[pk] = constant$1(system.getByUid(uids[pk]).getOrDie());
4234        });
4235        return r;
4236    };
4237    const defaultUids = (baseUid, partTypes) => {
4238        const partNames = names(partTypes);
4239        return wrapAll(map$2(partNames, (pn) => ({ key: pn, value: baseUid + '-' + pn })));
4240    };
4241    const defaultUidsSchema = (partTypes) => field$1('partUids', 'partUids', mergeWithThunk((spec) => defaultUids(spec.uid, partTypes)), anyValue());
4242
4243    var AlloyParts = /*#__PURE__*/Object.freeze({
4244        __proto__: null,
4245        generate: generate$5,
4246        generateOne: generateOne$1,
4247        schemas: schemas,
4248        names: names,
4249        substitutes: substitutes,
4250        components: components$1,
4251        defaultUids: defaultUids,
4252        defaultUidsSchema: defaultUidsSchema,
4253        getAllParts: getAllParts,
4254        getAllPartNames: getAllPartNames,
4255        getPart: getPart,
4256        getPartOrDie: getPartOrDie,
4257        getParts: getParts,
4258        getPartsOrDie: getPartsOrDie
4259    });
4260
4261    const allAlignments = [
4262        'valignCentre',
4263        'alignLeft',
4264        'alignRight',
4265        'alignCentre',
4266        'top',
4267        'bottom',
4268        'left',
4269        'right',
4270        'inset'
4271    ];
4272    const nu$6 = (xOffset, yOffset, classes, insetModifier = 1) => {
4273        const insetXOffset = xOffset * insetModifier;
4274        const insetYOffset = yOffset * insetModifier;
4275        const getClasses = (prop) => get$h(classes, prop).getOr([]);
4276        const make = (xDelta, yDelta, alignmentsOn) => {
4277            const alignmentsOff = difference(allAlignments, alignmentsOn);
4278            return {
4279                offset: SugarPosition(xDelta, yDelta),
4280                classesOn: bind$3(alignmentsOn, getClasses),
4281                classesOff: bind$3(alignmentsOff, getClasses)
4282            };
4283        };
4284        return {
4285            southeast: () => make(-xOffset, yOffset, ['top', 'alignLeft']),
4286            southwest: () => make(xOffset, yOffset, ['top', 'alignRight']),
4287            south: () => make(-xOffset / 2, yOffset, ['top', 'alignCentre']),
4288            northeast: () => make(-xOffset, -yOffset, ['bottom', 'alignLeft']),
4289            northwest: () => make(xOffset, -yOffset, ['bottom', 'alignRight']),
4290            north: () => make(-xOffset / 2, -yOffset, ['bottom', 'alignCentre']),
4291            east: () => make(xOffset, -yOffset / 2, ['valignCentre', 'left']),
4292            west: () => make(-xOffset, -yOffset / 2, ['valignCentre', 'right']),
4293            insetNortheast: () => make(insetXOffset, insetYOffset, ['top', 'alignLeft', 'inset']),
4294            insetNorthwest: () => make(-insetXOffset, insetYOffset, ['top', 'alignRight', 'inset']),
4295            insetNorth: () => make(-insetXOffset / 2, insetYOffset, ['top', 'alignCentre', 'inset']),
4296            insetSoutheast: () => make(insetXOffset, -insetYOffset, ['bottom', 'alignLeft', 'inset']),
4297            insetSouthwest: () => make(-insetXOffset, -insetYOffset, ['bottom', 'alignRight', 'inset']),
4298            insetSouth: () => make(-insetXOffset / 2, -insetYOffset, ['bottom', 'alignCentre', 'inset']),
4299            insetEast: () => make(-insetXOffset, -insetYOffset / 2, ['valignCentre', 'right', 'inset']),
4300            insetWest: () => make(insetXOffset, -insetYOffset / 2, ['valignCentre', 'left', 'inset'])
4301        };
4302    };
4303    const fallback = () => nu$6(0, 0, {});
4304
4305    const nu$5 = (x, y, bubble, direction, placement, boundsRestriction, labelPrefix, alwaysFit = false) => ({
4306        x,
4307        y,
4308        bubble,
4309        direction,
4310        placement,
4311        restriction: boundsRestriction,
4312        label: `${labelPrefix}-${placement}`,
4313        alwaysFit
4314    });
4315
4316    const adt$5 = Adt.generate([
4317        { southeast: [] },
4318        { southwest: [] },
4319        { northeast: [] },
4320        { northwest: [] },
4321        { south: [] },
4322        { north: [] },
4323        { east: [] },
4324        { west: [] }
4325    ]);
4326    const cata$1 = (subject, southeast, southwest, northeast, northwest, south, north, east, west) => subject.fold(southeast, southwest, northeast, northwest, south, north, east, west);
4327    const cataVertical = (subject, south, middle, north) => subject.fold(south, south, north, north, south, north, middle, middle);
4328    const cataHorizontal = (subject, east, middle, west) => subject.fold(east, west, east, west, middle, middle, east, west);
4329    // TODO: Simplify with the typescript approach.
4330    const southeast$3 = adt$5.southeast;
4331    const southwest$3 = adt$5.southwest;
4332    const northeast$3 = adt$5.northeast;
4333    const northwest$3 = adt$5.northwest;
4334    const south$3 = adt$5.south;
4335    const north$3 = adt$5.north;
4336    const east$3 = adt$5.east;
4337    const west$3 = adt$5.west;
4338
4339    const getRestriction = (anchor, restriction) => {
4340        switch (restriction) {
4341            case 1 /* AnchorBoxBounds.LeftEdge */:
4342                return anchor.x;
4343            case 0 /* AnchorBoxBounds.RightEdge */:
4344                return anchor.x + anchor.width;
4345            case 2 /* AnchorBoxBounds.TopEdge */:
4346                return anchor.y;
4347            case 3 /* AnchorBoxBounds.BottomEdge */:
4348                return anchor.y + anchor.height;
4349        }
4350    };
4351    const boundsRestriction = (anchor, restrictions) => mapToObject(['left', 'right', 'top', 'bottom'], (dir) => get$h(restrictions, dir).map((restriction) => getRestriction(anchor, restriction)));
4352    const adjustBounds = (bounds$1, restriction, bubbleOffset) => {
4353        const applyRestriction = (dir, current) => restriction[dir].map((pos) => {
4354            const isVerticalAxis = dir === 'top' || dir === 'bottom';
4355            const offset = isVerticalAxis ? bubbleOffset.top : bubbleOffset.left;
4356            const comparator = dir === 'left' || dir === 'top' ? Math.max : Math.min;
4357            const newPos = comparator(pos, current) + offset;
4358            // Ensure the new restricted position is within the current bounds
4359            return isVerticalAxis ? clamp(newPos, bounds$1.y, bounds$1.bottom) : clamp(newPos, bounds$1.x, bounds$1.right);
4360        }).getOr(current);
4361        const adjustedLeft = applyRestriction('left', bounds$1.x);
4362        const adjustedTop = applyRestriction('top', bounds$1.y);
4363        const adjustedRight = applyRestriction('right', bounds$1.right);
4364        const adjustedBottom = applyRestriction('bottom', bounds$1.bottom);
4365        return bounds(adjustedLeft, adjustedTop, adjustedRight - adjustedLeft, adjustedBottom - adjustedTop);
4366    };
4367
4368    /*
4369      Layout for menus and inline context dialogs;
4370      Either above or below. Never left or right.
4371      Aligned to the left or right of the anchor as appropriate.
4372     */
4373    const labelPrefix$2 = 'layout';
4374    // display element to the right, left edge against the anchor
4375    const eastX$1 = (anchor) => anchor.x;
4376    // element centre aligned horizontally with the anchor
4377    const middleX$1 = (anchor, element) => anchor.x + (anchor.width / 2) - (element.width / 2);
4378    // display element to the left, right edge against the right of the anchor
4379    const westX$1 = (anchor, element) => anchor.x + anchor.width - element.width;
4380    // display element above, bottom edge against the top of the anchor
4381    const northY$2 = (anchor, element) => anchor.y - element.height;
4382    // display element below, top edge against the bottom of the anchor
4383    const southY$2 = (anchor) => anchor.y + anchor.height;
4384    // display element below, top edge against the bottom of the anchor
4385    const centreY$1 = (anchor, element) => anchor.y + (anchor.height / 2) - (element.height / 2);
4386    const eastEdgeX$1 = (anchor) => anchor.x + anchor.width;
4387    const westEdgeX$1 = (anchor, element) => anchor.x - element.width;
4388    const southeast$2 = (anchor, element, bubbles) => nu$5(eastX$1(anchor), southY$2(anchor), bubbles.southeast(), southeast$3(), "southeast" /* Placement.Southeast */, boundsRestriction(anchor, { left: 1 /* AnchorBoxBounds.LeftEdge */, top: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix$2);
4389    const southwest$2 = (anchor, element, bubbles) => nu$5(westX$1(anchor, element), southY$2(anchor), bubbles.southwest(), southwest$3(), "southwest" /* Placement.Southwest */, boundsRestriction(anchor, { right: 0 /* AnchorBoxBounds.RightEdge */, top: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix$2);
4390    const northeast$2 = (anchor, element, bubbles) => nu$5(eastX$1(anchor), northY$2(anchor, element), bubbles.northeast(), northeast$3(), "northeast" /* Placement.Northeast */, boundsRestriction(anchor, { left: 1 /* AnchorBoxBounds.LeftEdge */, bottom: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix$2);
4391    const northwest$2 = (anchor, element, bubbles) => nu$5(westX$1(anchor, element), northY$2(anchor, element), bubbles.northwest(), northwest$3(), "northwest" /* Placement.Northwest */, boundsRestriction(anchor, { right: 0 /* AnchorBoxBounds.RightEdge */, bottom: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix$2);
4392    const north$2 = (anchor, element, bubbles) => nu$5(middleX$1(anchor, element), northY$2(anchor, element), bubbles.north(), north$3(), "north" /* Placement.North */, boundsRestriction(anchor, { bottom: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix$2);
4393    const south$2 = (anchor, element, bubbles) => nu$5(middleX$1(anchor, element), southY$2(anchor), bubbles.south(), south$3(), "south" /* Placement.South */, boundsRestriction(anchor, { top: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix$2);
4394    const east$2 = (anchor, element, bubbles) => nu$5(eastEdgeX$1(anchor), centreY$1(anchor, element), bubbles.east(), east$3(), "east" /* Placement.East */, boundsRestriction(anchor, { left: 0 /* AnchorBoxBounds.RightEdge */ }), labelPrefix$2);
4395    const west$2 = (anchor, element, bubbles) => nu$5(westEdgeX$1(anchor, element), centreY$1(anchor, element), bubbles.west(), west$3(), "west" /* Placement.West */, boundsRestriction(anchor, { right: 1 /* AnchorBoxBounds.LeftEdge */ }), labelPrefix$2);
4396    const all$2 = () => [southeast$2, southwest$2, northeast$2, northwest$2, south$2, north$2, east$2, west$2];
4397    const allRtl$1 = () => [southwest$2, southeast$2, northwest$2, northeast$2, south$2, north$2, east$2, west$2];
4398    const aboveOrBelow = () => [northeast$2, northwest$2, southeast$2, southwest$2, north$2, south$2];
4399    const aboveOrBelowRtl = () => [northwest$2, northeast$2, southwest$2, southeast$2, north$2, south$2];
4400    const belowOrAbove = () => [southeast$2, southwest$2, northeast$2, northwest$2, south$2, north$2];
4401    const belowOrAboveRtl = () => [southwest$2, southeast$2, northwest$2, northeast$2, south$2, north$2];
4402
4403    const placementAttribute = 'data-alloy-placement';
4404    const setPlacement$1 = (element, placement) => {
4405        set$9(element, placementAttribute, placement);
4406    };
4407    const getPlacement = (element) => getOpt(element, placementAttribute);
4408    const reset$2 = (element) => remove$8(element, placementAttribute);
4409
4410    /*
4411      Layouts for things that overlay over the anchor element/box. These are designed to mirror
4412      the `Layout` logic.
4413
4414      As an example `Layout.north` will appear horizontally centered above the anchor, whereas
4415      `LayoutInset.north` will appear horizontally centered overlapping the top of the anchor.
4416     */
4417    const labelPrefix$1 = 'layout-inset';
4418    // returns left edge of anchor - used to display element to the left, left edge against the anchor
4419    const westEdgeX = (anchor) => anchor.x;
4420    // returns middle of anchor minus half the element width - used to horizontally centre element to the anchor
4421    const middleX = (anchor, element) => anchor.x + (anchor.width / 2) - (element.width / 2);
4422    // returns right edge of anchor minus element width - used to display element to the right, right edge against the anchor
4423    const eastEdgeX = (anchor, element) => anchor.x + anchor.width - element.width;
4424    // returns top edge - used to display element to the top, top edge against the anchor
4425    const northY$1 = (anchor) => anchor.y;
4426    // returns bottom edge minus element height - used to display element at the bottom, bottom edge against the anchor
4427    const southY$1 = (anchor, element) => anchor.y + anchor.height - element.height;
4428    // returns centre of anchor minus half the element height - used to vertically centre element to the anchor
4429    const centreY = (anchor, element) => anchor.y + (anchor.height / 2) - (element.height / 2);
4430    // positions element relative to the bottom right of the anchor
4431    const southwest$1 = (anchor, element, bubbles) => nu$5(eastEdgeX(anchor, element), southY$1(anchor, element), bubbles.insetSouthwest(), northwest$3(), "southwest" /* Placement.Southwest */, boundsRestriction(anchor, { right: 0 /* AnchorBoxBounds.RightEdge */, bottom: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix$1);
4432    // positions element relative to the bottom left of the anchor
4433    const southeast$1 = (anchor, element, bubbles) => nu$5(westEdgeX(anchor), southY$1(anchor, element), bubbles.insetSoutheast(), northeast$3(), "southeast" /* Placement.Southeast */, boundsRestriction(anchor, { left: 1 /* AnchorBoxBounds.LeftEdge */, bottom: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix$1);
4434    // positions element relative to the top right of the anchor
4435    const northwest$1 = (anchor, element, bubbles) => nu$5(eastEdgeX(anchor, element), northY$1(anchor), bubbles.insetNorthwest(), southwest$3(), "northwest" /* Placement.Northwest */, boundsRestriction(anchor, { right: 0 /* AnchorBoxBounds.RightEdge */, top: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix$1);
4436    // positions element relative to the top left of the anchor
4437    const northeast$1 = (anchor, element, bubbles) => nu$5(westEdgeX(anchor), northY$1(anchor), bubbles.insetNortheast(), southeast$3(), "northeast" /* Placement.Northeast */, boundsRestriction(anchor, { left: 1 /* AnchorBoxBounds.LeftEdge */, top: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix$1);
4438    // positions element relative to the top of the anchor, horizontally centered
4439    const north$1 = (anchor, element, bubbles) => nu$5(middleX(anchor, element), northY$1(anchor), bubbles.insetNorth(), south$3(), "north" /* Placement.North */, boundsRestriction(anchor, { top: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix$1);
4440    // positions element relative to the bottom of the anchor, horizontally centered
4441    const south$1 = (anchor, element, bubbles) => nu$5(middleX(anchor, element), southY$1(anchor, element), bubbles.insetSouth(), north$3(), "south" /* Placement.South */, boundsRestriction(anchor, { bottom: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix$1);
4442    // positions element with the right edge against the anchor, vertically centered
4443    const east$1 = (anchor, element, bubbles) => nu$5(eastEdgeX(anchor, element), centreY(anchor, element), bubbles.insetEast(), west$3(), "east" /* Placement.East */, boundsRestriction(anchor, { right: 0 /* AnchorBoxBounds.RightEdge */ }), labelPrefix$1);
4444    // positions element with the left each against the anchor, vertically centered
4445    const west$1 = (anchor, element, bubbles) => nu$5(westEdgeX(anchor), centreY(anchor, element), bubbles.insetWest(), east$3(), "west" /* Placement.West */, boundsRestriction(anchor, { left: 1 /* AnchorBoxBounds.LeftEdge */ }), labelPrefix$1);
4446    const lookupPreserveLayout = (lastPlacement) => {
4447        switch (lastPlacement) {
4448            case "north" /* Placement.North */:
4449                return north$1;
4450            case "northeast" /* Placement.Northeast */:
4451                return northeast$1;
4452            case "northwest" /* Placement.Northwest */:
4453                return northwest$1;
4454            case "south" /* Placement.South */:
4455                return south$1;
4456            case "southeast" /* Placement.Southeast */:
4457                return southeast$1;
4458            case "southwest" /* Placement.Southwest */:
4459                return southwest$1;
4460            case "east" /* Placement.East */:
4461                return east$1;
4462            case "west" /* Placement.West */:
4463                return west$1;
4464        }
4465    };
4466    const preserve$1 = (anchor, element, bubbles, placee, bounds) => {
4467        const layout = getPlacement(placee).map(lookupPreserveLayout).getOr(north$1);
4468        return layout(anchor, element, bubbles, placee, bounds);
4469    };
4470    const lookupFlippedLayout = (lastPlacement) => {
4471        switch (lastPlacement) {
4472            case "north" /* Placement.North */:
4473                return south$1;
4474            case "northeast" /* Placement.Northeast */:
4475                return southeast$1;
4476            case "northwest" /* Placement.Northwest */:
4477                return southwest$1;
4478            case "south" /* Placement.South */:
4479                return north$1;
4480            case "southeast" /* Placement.Southeast */:
4481                return northeast$1;
4482            case "southwest" /* Placement.Southwest */:
4483                return northwest$1;
4484            case "east" /* Placement.East */:
4485                return west$1;
4486            case "west" /* Placement.West */:
4487                return east$1;
4488        }
4489    };
4490    const flip = (anchor, element, bubbles, placee, bounds) => {
4491        const layout = getPlacement(placee).map(lookupFlippedLayout).getOr(north$1);
4492        return layout(anchor, element, bubbles, placee, bounds);
4493    };
4494
4495    // applies the max-height as determined by Bounder
vendor: 19,065 bytes, lines 4496-5011
4496    const setMaxHeight = (element, maxHeight) => {
4497        setMax$1(element, Math.floor(maxHeight));
4498    };
4499    // adds both max-height and overflow to constrain it
4500    const anchored = constant$1((element, available) => {
4501        setMaxHeight(element, available);
4502        setAll(element, {
4503            'overflow-x': 'hidden',
4504            'overflow-y': 'auto'
4505        });
4506    });
4507    /*
4508     * This adds max height, but not overflow - the effect of this is that elements can grow beyond the max height,
4509     * but if they run off the top they're pushed down.
4510     *
4511     * If the element expands below the screen height it will be cut off, but we were already doing that.
4512     */
4513    const expandable$1 = constant$1((element, available) => {
4514        setMaxHeight(element, available);
4515    });
4516
4517    // applies the max-width as determined by Bounder
4518    const expandable = constant$1((element, available) => {
4519        setMax(element, Math.floor(available));
4520    });
4521
4522    var AttributeValue;
4523    (function (AttributeValue) {
4524        AttributeValue["TopToBottom"] = "toptobottom";
4525        AttributeValue["BottomToTop"] = "bottomtotop";
4526    })(AttributeValue || (AttributeValue = {}));
4527    const Attribute = 'data-alloy-vertical-dir';
4528    const isBottomToTopDir = (el) => closest$2(el, (current) => isElement$1(current) && get$g(current, 'data-alloy-vertical-dir') === AttributeValue.BottomToTop);
4529
4530    var HighlightOnOpen;
4531    (function (HighlightOnOpen) {
4532        HighlightOnOpen[HighlightOnOpen["HighlightMenuAndItem"] = 0] = "HighlightMenuAndItem";
4533        HighlightOnOpen[HighlightOnOpen["HighlightJustMenu"] = 1] = "HighlightJustMenu";
4534        HighlightOnOpen[HighlightOnOpen["HighlightNone"] = 2] = "HighlightNone";
4535    })(HighlightOnOpen || (HighlightOnOpen = {}));
4536
4537    const NoState = {
4538        init: () => nu$4({
4539            readState: constant$1('No State required')
4540        })
4541    };
4542    const nu$4 = (spec) => spec;
4543
4544    const defaultEventHandler = {
4545        can: always,
4546        abort: never,
4547        run: noop
4548    };
4549    const nu$3 = (parts) => {
4550        if (!hasNonNullableKey(parts, 'can') && !hasNonNullableKey(parts, 'abort') && !hasNonNullableKey(parts, 'run')) {
4551            throw new Error('EventHandler defined by: ' + JSON.stringify(parts, null, 2) + ' does not have can, abort, or run!');
4552        }
4553        return {
4554            ...defaultEventHandler,
4555            ...parts
4556        };
4557    };
4558    const all$1 = (handlers, f) => (...args) => foldl(handlers, (acc, handler) => acc && f(handler).apply(undefined, args), true);
4559    const any = (handlers, f) => (...args) => foldl(handlers, (acc, handler) => acc || f(handler).apply(undefined, args), false);
4560    const read$1 = (handler) => isFunction(handler) ? {
4561        can: always,
4562        abort: never,
4563        run: handler
4564    } : handler;
4565    const fuse$1 = (handlers) => {
4566        const can = all$1(handlers, (handler) => handler.can);
4567        const abort = any(handlers, (handler) => handler.abort);
4568        const run = (...args) => {
4569            each$1(handlers, (handler) => {
4570                // ASSUMPTION: Return value is unimportant.
4571                handler.run.apply(undefined, args);
4572            });
4573        };
4574        return {
4575            can,
4576            abort,
4577            run
4578        };
4579    };
4580
4581    const emit = (component, event) => {
4582        dispatchWith(component, component.element, event, {});
4583    };
4584    const emitWith = (component, event, properties) => {
4585        dispatchWith(component, component.element, event, properties);
4586    };
4587    const emitExecute = (component) => {
4588        emit(component, execute$5());
4589    };
4590    const dispatch = (component, target, event) => {
4591        dispatchWith(component, target, event, {});
4592    };
4593    const dispatchWith = (component, target, event, properties) => {
4594        // NOTE: The order of spreading here means that it will maintain any target that
4595        // exists in the current properties. Because this function has been used for situations where
4596        // properties is either an emulated SugarEvent with no target (see TouchEvent) or
4597        // for emitting custom events that have no target, this likely hasn't been a problem.
4598        // But until we verify that nothing is relying on this ordering, there is an alternate
4599        // function below called retargetAndDispatchWith, which spreads in the other direction.
4600        const data = {
4601            target,
4602            ...properties
4603        };
4604        component.getSystem().triggerEvent(event, target, data);
4605    };
4606    const retargetAndDispatchWith = (component, target, eventName, properties) => {
4607        // This is essentially the same as dispatchWith, except the spreading order
4608        // means that it clobbers anything in the nativeEvent with "target". It also
4609        // expects what is being passed in to be a real sugar event, not just a data
4610        // blob
4611        const data = {
4612            ...properties,
4613            target
4614        };
4615        component.getSystem().triggerEvent(eventName, target, data);
4616    };
4617    const dispatchEvent = (component, target, event, simulatedEvent) => {
4618        component.getSystem().triggerEvent(event, target, simulatedEvent.event);
4619    };
4620
4621    const derive$2 = (configs) => wrapAll(configs);
4622    // const combine = (configs...);
4623    const abort = (name, predicate) => {
4624        return {
4625            key: name,
4626            value: nu$3({
4627                abort: predicate
4628            })
4629        };
4630    };
4631    const can = (name, predicate) => {
4632        return {
4633            key: name,
4634            value: nu$3({
4635                can: predicate
4636            })
4637        };
4638    };
4639    const preventDefault = (name) => {
4640        return {
4641            key: name,
4642            value: nu$3({
4643                run: (component, simulatedEvent) => {
4644                    simulatedEvent.event.prevent();
4645                }
4646            })
4647        };
4648    };
4649    const run$1 = (name, handler) => {
4650        return {
4651            key: name,
4652            value: nu$3({
4653                run: handler
4654            })
4655        };
4656    };
4657    // Extra can be used when your handler needs more context, and is declared in one spot
4658    // It's really just convenient partial application.
4659    const runActionExtra = (name, action, extra) => {
4660        return {
4661            key: name,
4662            value: nu$3({
4663                run: (component, simulatedEvent) => {
4664                    action.apply(undefined, [component, simulatedEvent].concat(extra));
4665                }
4666            })
4667        };
4668    };
4669    const runOnName = (name) => {
4670        return (handler) => run$1(name, handler);
4671    };
4672    const runOnSourceName = (name) => {
4673        return (handler) => ({
4674            key: name,
4675            value: nu$3({
4676                run: (component, simulatedEvent) => {
4677                    if (isSource(component, simulatedEvent)) {
4678                        handler(component, simulatedEvent);
4679                    }
4680                }
4681            })
4682        });
4683    };
4684    const redirectToUid = (name, uid) => {
4685        return run$1(name, (component, simulatedEvent) => {
4686            component.getSystem().getByUid(uid).each((redirectee) => {
4687                dispatchEvent(redirectee, redirectee.element, name, simulatedEvent);
4688            });
4689        });
4690    };
4691    const redirectToPart = (name, detail, partName) => {
4692        const uid = detail.partUids[partName];
4693        return redirectToUid(name, uid);
4694    };
4695    const runWithTarget = (name, f) => {
4696        return run$1(name, (component, simulatedEvent) => {
4697            const ev = simulatedEvent.event;
4698            const target = component.getSystem().getByDom(ev.target).getOrThunk(
4699            // If we don't find an alloy component for the target, I guess we go up the tree
4700            // until we find an alloy component? Performance concern?
4701            // TODO: Write tests for this.
4702            () => {
4703                const closest$1 = closest(ev.target, (el) => component.getSystem().getByDom(el).toOptional(), never);
4704                // If we still found nothing ... fire on component itself;
4705                return closest$1.getOr(component);
4706            });
4707            f(component, target, simulatedEvent);
4708        });
4709    };
4710    const cutter = (name) => {
4711        return run$1(name, (component, simulatedEvent) => {
4712            simulatedEvent.cut();
4713        });
4714    };
4715    const stopper = (name) => {
4716        return run$1(name, (component, simulatedEvent) => {
4717            simulatedEvent.stop();
4718        });
4719    };
4720    const runOnSource = (name, f) => {
4721        return runOnSourceName(name)(f);
4722    };
4723    const runOnAttached = runOnSourceName(attachedToDom());
4724    const runOnDetached = runOnSourceName(detachedFromDom());
4725    const runOnInit = runOnSourceName(systemInit());
4726    const runOnExecute$1 = runOnName(execute$5());
4727
4728    // Maybe we'll need to allow add/remove
4729    const nu$2 = (s) => ({
4730        classes: isUndefined(s.classes) ? [] : s.classes,
4731        attributes: isUndefined(s.attributes) ? {} : s.attributes,
4732        styles: isUndefined(s.styles) ? {} : s.styles
4733    });
4734    const merge = (defnA, mod) => ({
4735        ...defnA,
4736        attributes: { ...defnA.attributes, ...mod.attributes },
4737        styles: { ...defnA.styles, ...mod.styles },
4738        classes: defnA.classes.concat(mod.classes)
4739    });
4740
4741    const executeEvent = (bConfig, bState, executor) => runOnExecute$1((component) => {
4742        executor(component, bConfig, bState);
4743    });
4744    const loadEvent = (bConfig, bState, f) => runOnInit((component, _simulatedEvent) => {
4745        f(component, bConfig, bState);
4746    });
4747    const create$4 = (schema, name, active, apis, extra, state) => {
4748        const configSchema = objOfOnly(schema);
4749        const schemaSchema = optionObjOf(name, [
4750            optionObjOfOnly('config', schema)
4751        ]);
4752        return doCreate(configSchema, schemaSchema, name, active, apis, extra, state);
4753    };
4754    const createModes$1 = (modes, name, active, apis, extra, state) => {
4755        const configSchema = modes;
4756        const schemaSchema = optionObjOf(name, [
4757            optionOf('config', modes)
4758        ]);
4759        return doCreate(configSchema, schemaSchema, name, active, apis, extra, state);
4760    };
4761    const wrapApi = (bName, apiFunction, apiName) => {
4762        const f = (component, ...rest) => {
4763            const args = [component].concat(rest);
4764            return component.config({
4765                name: constant$1(bName)
4766            }).fold(() => {
4767                throw new Error('We could not find any behaviour configuration for: ' + bName + '. Using API: ' + apiName);
4768            }, (info) => {
4769                const rest = Array.prototype.slice.call(args, 1);
4770                return apiFunction.apply(undefined, [component, info.config, info.state].concat(rest));
4771            });
4772        };
4773        return markAsBehaviourApi(f, apiName, apiFunction);
4774    };
4775    // I think the "revoke" idea is fragile at best.
4776    const revokeBehaviour = (name) => ({
4777        key: name,
4778        value: undefined
4779    });
4780    const doCreate = (configSchema, schemaSchema, name, active, apis, extra, state) => {
4781        const getConfig = (info) => hasNonNullableKey(info, name) ? info[name]() : Optional.none();
4782        const wrappedApis = map$1(apis, (apiF, apiName) => wrapApi(name, apiF, apiName));
4783        const wrappedExtra = map$1(extra, (extraF, extraName) => markAsExtraApi(extraF, extraName));
4784        const me = {
4785            ...wrappedExtra,
4786            ...wrappedApis,
4787            revoke: curry(revokeBehaviour, name),
4788            config: (spec) => {
4789                const prepared = asRawOrDie$1(name + '-config', configSchema, spec);
4790                return {
4791                    key: name,
4792                    value: {
4793                        config: prepared,
4794                        me,
4795                        configAsRaw: cached(() => asRawOrDie$1(name + '-config', configSchema, spec)),
4796                        initialConfig: spec,
4797                        state
4798                    }
4799                };
4800            },
4801            schema: constant$1(schemaSchema),
4802            exhibit: (info, base) => {
4803                return lift2(getConfig(info), get$h(active, 'exhibit'), (behaviourInfo, exhibitor) => {
4804                    return exhibitor(base, behaviourInfo.config, behaviourInfo.state);
4805                }).getOrThunk(() => nu$2({}));
4806            },
4807            name: constant$1(name),
4808            handlers: (info) => {
4809                return getConfig(info).map((behaviourInfo) => {
4810                    const getEvents = get$h(active, 'events').getOr(() => ({}));
4811                    return getEvents(behaviourInfo.config, behaviourInfo.state);
4812                }).getOr({});
4813            }
4814        };
4815        return me;
4816    };
4817
4818    const derive$1 = (capabilities) => wrapAll(capabilities);
4819    const simpleSchema = objOfOnly([
4820        required$1('fields'),
4821        required$1('name'),
4822        defaulted('active', {}),
4823        defaulted('apis', {}),
4824        defaulted('state', NoState),
4825        defaulted('extra', {})
4826    ]);
4827    const create$3 = (data) => {
4828        const value = asRawOrDie$1('Creating behaviour: ' + data.name, simpleSchema, data);
4829        return create$4(value.fields, value.name, value.active, value.apis, value.extra, value.state);
4830    };
4831    const modeSchema = objOfOnly([
4832        required$1('branchKey'),
4833        required$1('branches'),
4834        required$1('name'),
4835        defaulted('active', {}),
4836        defaulted('apis', {}),
4837        defaulted('state', NoState),
4838        defaulted('extra', {})
4839    ]);
4840    const createModes = (data) => {
4841        const value = asRawOrDie$1('Creating behaviour: ' + data.name, modeSchema, data);
4842        return createModes$1(choose$1(value.branchKey, value.branches), value.name, value.active, value.apis, value.extra, value.state);
4843    };
4844    const revoke = constant$1(undefined);
4845
4846    // AlloyEventKeyAndHandler type argument needs to be any here to satisfy an array of handlers
4847    // where each item can be any subtype of EventFormat we can't use <T extends EventFormat> since
4848    // then each item would have to be the same type
4849    const events$j = (name, eventHandlers) => {
4850        const events = derive$2(eventHandlers);
4851        return create$3({
4852            fields: [
4853                required$1('enabled')
4854            ],
4855            name,
4856            active: {
4857                events: constant$1(events)
4858            }
4859        });
4860    };
4861    const config = (name, eventHandlers) => {
4862        const me = events$j(name, eventHandlers);
4863        return {
4864            key: name,
4865            value: {
4866                config: {},
4867                me,
4868                configAsRaw: constant$1({}),
4869                initialConfig: {},
4870                state: NoState
4871            }
4872        };
4873    };
4874
4875    const SetupBehaviourCellState = (initialState) => {
4876        const init = () => {
4877            const cell = Cell(initialState);
4878            const get = () => cell.get();
4879            const set = (newState) => cell.set(newState);
4880            const clear = () => cell.set(initialState);
4881            const readState = () => cell.get();
4882            return {
4883                get,
4884                set,
4885                clear,
4886                readState
4887            };
4888        };
4889        return {
4890            init
4891        };
4892    };
4893
4894    const focus$2 = (component, focusConfig) => {
4895        if (!focusConfig.ignore) {
4896            focus$4(component.element);
4897            focusConfig.onFocus(component);
4898        }
4899    };
4900    const blur = (component, focusConfig) => {
4901        if (!focusConfig.ignore) {
4902            blur$1(component.element);
4903        }
4904    };
4905    const isFocused = (component) => hasFocus(component.element);
4906
4907    var FocusApis = /*#__PURE__*/Object.freeze({
4908        __proto__: null,
4909        focus: focus$2,
4910        blur: blur,
4911        isFocused: isFocused
4912    });
4913
4914    // TODO: DomModification types
4915    const exhibit$6 = (base, focusConfig) => {
4916        const mod = focusConfig.ignore ? {} : {
4917            attributes: {
4918                tabindex: '-1'
4919            }
4920        };
4921        return nu$2(mod);
4922    };
4923    const events$i = (focusConfig) => derive$2([
4924        run$1(focus$3(), (component, simulatedEvent) => {
4925            focus$2(component, focusConfig);
4926            simulatedEvent.stop();
4927        })
4928    ].concat(focusConfig.stopMousedown ? [
4929        run$1(mousedown(), (_, simulatedEvent) => {
4930            // This setting is often used in tandem with ignoreFocus. Basically, if you
4931            // don't prevent default on a menu that has fake focus, then it can transfer
4932            // focus to the outer body when they click on it, which can break things
4933            // which dismiss on blur (e.g. typeahead)
4934            simulatedEvent.event.prevent();
4935        })
4936    ] : []));
4937
4938    var ActiveFocus = /*#__PURE__*/Object.freeze({
4939        __proto__: null,
4940        exhibit: exhibit$6,
4941        events: events$i
4942    });
4943
4944    var FocusSchema = [
4945        // TODO: Work out when we want to  call this. Only when it is has changed?
4946        onHandler('onFocus'),
4947        defaulted('stopMousedown', false),
4948        defaulted('ignore', false)
4949    ];
4950
4951    const Focusing = create$3({
4952        fields: FocusSchema,
4953        name: 'focusing',
4954        active: ActiveFocus,
4955        apis: FocusApis
4956        // Consider adding isFocused an an extra
4957    });
4958
4959    const BACKSPACE = [8];
4960    const TAB = [9];
4961    const ENTER = [13];
4962    const ESCAPE = [27];
4963    const SPACE = [32];
4964    const LEFT = [37];
4965    const UP = [38];
4966    const RIGHT = [39];
4967    const DOWN = [40];
4968
4969    const closeTooltips = constant$1('tooltipping.close.all');
4970    const dismissPopups = constant$1('dismiss.popups');
4971    const repositionPopups = constant$1('reposition.popups');
4972    const mouseReleased = constant$1('mouse.released');
4973
4974    const cyclePrev = (values, index, predicate) => {
4975        const before = reverse(values.slice(0, index));
4976        const after = reverse(values.slice(index + 1));
4977        return find$5(before.concat(after), predicate);
4978    };
4979    const tryPrev = (values, index, predicate) => {
4980        const before = reverse(values.slice(0, index));
4981        return find$5(before, predicate);
4982    };
4983    const cycleNext = (values, index, predicate) => {
4984        const before = values.slice(0, index);
4985        const after = values.slice(index + 1);
4986        return find$5(after.concat(before), predicate);
4987    };
4988    const tryNext = (values, index, predicate) => {
4989        const after = values.slice(index + 1);
4990        return find$5(after, predicate);
4991    };
4992
4993    const inSet = (keys) => (event) => {
4994        const raw = event.raw;
4995        return contains$2(keys, raw.which);
4996    };
4997    const and = (preds) => (event) => forall(preds, (pred) => pred(event));
4998    const isShift$1 = (event) => {
4999        const raw = event.raw;
5000        return raw.shiftKey === true;
5001    };
5002    const isControl = (event) => {
5003        const raw = event.raw;
5004        return raw.ctrlKey === true;
5005    };
5006    const isNotShift = not(isShift$1);
5007
5008    const rule = (matches, action) => ({
5009        matches,
5010        classification: action
5011    });
5012    const choose = (transitions, event) => {
5013        const transition = find$5(transitions, (t) => t.matches(event));
5014        return transition.map((t) => t.classification);
5015    };
5016
5017    // THIS IS NOT API YET
5018    const dehighlightAllExcept = (component, hConfig, hState, skip) => {
5019        const highlighted = descendants(component.element, '.' + hConfig.highlightClass);
5020        each$1(highlighted, (h) => {
5021            // We don't want to dehighlight anything that should be skipped.
5022            // Generally, this is because we are about to highlight that thing.
5023            const shouldSkip = exists(skip, (skipComp) => eq(skipComp.element, h));
5024            if (!shouldSkip) {
5025                remove$3(h, hConfig.highlightClass);
5026                component.getSystem().getByDom(h).each((target) => {
5027                    hConfig.onDehighlight(component, target);
5028                    emit(target, dehighlight$1());
5029                });
5030            }
5031        });
5032    };
5033    const dehighlightAll = (component, hConfig, hState) => dehighlightAllExcept(component, hConfig, hState, []);
5034    const dehighlight = (component, hConfig, hState, target) => {
5035        // Only act if it was highlighted.
5036        if (isHighlighted(component, hConfig, hState, target)) {
5037            remove$3(target.element, hConfig.highlightClass);
5038            hConfig.onDehighlight(component, target);
5039            emit(target, dehighlight$1());
5040        }
5041    };
5042    const highlight = (component, hConfig, hState, target) => {
5043        // If asked to highlight something, dehighlight everything else first except
5044        // for the new thing we are going to highlight. It's a rare case, but we don't
5045        // want to get an onDehighlight, onHighlight for the same item on a highlight call.
5046        // We also don't want to call onHighlight if it was already highlighted.
5047        //
5048        // Note, that there is an important distinction here: highlight is NOT a no-op
5049        // if target is already highlighted, because it will still dehighlight everything else.
5050        // However, it won't fire any onHighlight or onDehighlight handlers for the already
5051        // highlighted item. I'm not sure if this is behaviour we need to maintain, but it is now
5052        // tested. A simpler approach might just be to not do anything if it's already highlighted,
5053        // but that could leave us in an inconsistent state, where multiple items have highlights
5054        // even after a highlight call. This way, highlight validates the highlights in the
5055        // component, and ensures there is only one thing highlighted.
5056        dehighlightAllExcept(component, hConfig, hState, [target]);
5057        if (!isHighlighted(component, hConfig, hState, target)) {
5058            add$2(target.element, hConfig.highlightClass);
5059            hConfig.onHighlight(component, target);
5060            emit(target, highlight$1());
5061        }
5062    };
5063    const highlightFirst = (component, hConfig, hState) => {
5064        getFirst(component, hConfig).each((firstComp) => {
5065            highlight(component, hConfig, hState, firstComp);
5066        });
5067    };
5068    const highlightLast = (component, hConfig, hState) => {
5069        getLast(component, hConfig).each((lastComp) => {
5070            highlight(component, hConfig, hState, lastComp);
5071        });
5072    };
5073    const highlightAt = (component, hConfig, hState, index) => {
5074        getByIndex(component, hConfig, hState, index).fold((err) => {
5075            throw err;
5076        }, (firstComp) => {
5077            highlight(component, hConfig, hState, firstComp);
5078        });
5079    };
5080    const highlightBy = (component, hConfig, hState, predicate) => {
5081        const candidates = getCandidates(component, hConfig);
5082        const targetComp = find$5(candidates, predicate);
5083        targetComp.each((c) => {
5084            highlight(component, hConfig, hState, c);
5085        });
5086    };
5087    const isHighlighted = (component, hConfig, hState, queryTarget) => has(queryTarget.element, hConfig.highlightClass);
5088    const getHighlighted = (component, hConfig, _hState) => descendant(component.element, '.' + hConfig.highlightClass).bind((e) => component.getSystem().getByDom(e).toOptional());
5089    const getByIndex = (component, hConfig, hState, index) => {
5090        const items = descendants(component.element, '.' + hConfig.itemClass);
5091        return Optional.from(items[index]).fold(() => Result.error(new Error('No element found with index ' + index)), component.getSystem().getByDom);
5092    };
5093    const getFirst = (component, hConfig, _hState) =>
vendor: 5,457 bytes, lines 5093-5211
5093 descendant(component.element, '.' + hConfig.itemClass).bind((e) => component.getSystem().getByDom(e).toOptional());
5094    const getLast = (component, hConfig, _hState) => {
5095        const items = descendants(component.element, '.' + hConfig.itemClass);
5096        const last = items.length > 0 ? Optional.some(items[items.length - 1]) : Optional.none();
5097        return last.bind((c) => component.getSystem().getByDom(c).toOptional());
5098    };
5099    const getDelta$3 = (component, hConfig, hState, delta) => {
5100        const items = descendants(component.element, '.' + hConfig.itemClass);
5101        const current = findIndex$1(items, (item) => has(item, hConfig.highlightClass));
5102        return current.bind((selected) => {
5103            const dest = cycleBy(selected, delta, 0, items.length - 1);
5104            return component.getSystem().getByDom(items[dest]).toOptional();
5105        });
5106    };
5107    const getPrevious = (component, hConfig, hState) => getDelta$3(component, hConfig, hState, -1);
5108    const getNext = (component, hConfig, hState) => getDelta$3(component, hConfig, hState, +1);
5109    const getCandidates = (component, hConfig, _hState) => {
5110        const items = descendants(component.element, '.' + hConfig.itemClass);
5111        return cat(map$2(items, (i) => component.getSystem().getByDom(i).toOptional()));
5112    };
5113
5114    var HighlightApis = /*#__PURE__*/Object.freeze({
5115        __proto__: null,
5116        dehighlightAll: dehighlightAll,
5117        dehighlight: dehighlight,
5118        highlight: highlight,
5119        highlightFirst: highlightFirst,
5120        highlightLast: highlightLast,
5121        highlightAt: highlightAt,
5122        highlightBy: highlightBy,
5123        isHighlighted: isHighlighted,
5124        getHighlighted: getHighlighted,
5125        getFirst: getFirst,
5126        getLast: getLast,
5127        getPrevious: getPrevious,
5128        getNext: getNext,
5129        getCandidates: getCandidates
5130    });
5131
5132    var HighlightSchema = [
5133        required$1('highlightClass'),
5134        required$1('itemClass'),
5135        onHandler('onHighlight'),
5136        onHandler('onDehighlight')
5137    ];
5138
5139    const Highlighting = create$3({
5140        fields: HighlightSchema,
5141        name: 'highlighting',
5142        apis: HighlightApis
5143    });
5144
5145    const reportFocusShifting = (component, prevFocus, newFocus) => {
5146        const noChange = prevFocus.exists((p) => newFocus.exists((n) => eq(n, p)));
5147        if (!noChange) {
5148            emitWith(component, focusShifted(), {
5149                prevFocus,
5150                newFocus
5151            });
5152        }
5153    };
5154    const dom$2 = () => {
5155        const get = (component) => search(component.element);
5156        const set = (component, focusee) => {
5157            const prevFocus = get(component);
5158            component.getSystem().triggerFocus(focusee, component.element);
5159            const newFocus = get(component);
5160            reportFocusShifting(component, prevFocus, newFocus);
5161        };
5162        return {
5163            get,
5164            set
5165        };
5166    };
5167    const highlights = () => {
5168        const get = (component) => Highlighting.getHighlighted(component).map((item) => item.element);
5169        const set = (component, element) => {
5170            const prevFocus = get(component);
5171            component.getSystem().getByDom(element).fold(noop, (item) => {
5172                Highlighting.highlight(component, item);
5173            });
5174            const newFocus = get(component);
5175            reportFocusShifting(component, prevFocus, newFocus);
5176        };
5177        return {
5178            get,
5179            set
5180        };
5181    };
5182
5183    const typical = (infoSchema, stateInit, getKeydownRules, getKeyupRules, optFocusIn) => {
5184        const schema = () => infoSchema.concat([
5185            defaulted('focusManager', dom$2()),
5186            defaultedOf('focusInside', 'onFocus', valueOf((val) => contains$2(['onFocus', 'onEnterOrSpace', 'onApi'], val) ? Result.value(val) : Result.error('Invalid value for focusInside'))),
5187            output$1('handler', me),
5188            output$1('state', stateInit),
5189            output$1('sendFocusIn', optFocusIn)
5190        ]);
5191        const processKey = (component, simulatedEvent, getRules, keyingConfig, keyingState) => {
5192            const rules = getRules(component, simulatedEvent, keyingConfig, keyingState);
5193            return choose(rules, simulatedEvent.event).bind((rule) => rule(component, simulatedEvent, keyingConfig, keyingState));
5194        };
5195        const toEvents = (keyingConfig, keyingState) => {
5196            const onFocusHandler = keyingConfig.focusInside !== FocusInsideModes.OnFocusMode
5197                ? Optional.none()
5198                : optFocusIn(keyingConfig).map((focusIn) => run$1(focus$3(), (component, simulatedEvent) => {
5199                    focusIn(component, keyingConfig, keyingState);
5200                    simulatedEvent.stop();
5201                }));
5202            // On enter or space on root element, if using EnterOrSpace focus mode, fire a focusIn on the component
5203            const tryGoInsideComponent = (component, simulatedEvent) => {
5204                const isEnterOrSpace = inSet(SPACE.concat(ENTER))(simulatedEvent.event);
5205                if (keyingConfig.focusInside === FocusInsideModes.OnEnterOrSpaceMode && isEnterOrSpace && isSource(component, simulatedEvent)) {
5206                    optFocusIn(keyingConfig).each((focusIn) => {
5207                        focusIn(component, keyingConfig, keyingState);
5208                        simulatedEvent.stop();
5209                    });
5210                }
5211            };
5212            const keyboardEvents = [
5213                run$1(keydown(), (component, simulatedEvent) => {
5214                    processKey(component, simulatedEvent, getKeydownRules, keyingConfig, keyingState).fold(() => {
5215                        // Key wasn't handled ... so see if we should enter into the component (focusIn)
5216                        tryGoInsideComponent(component, simulatedEvent);
5217                    }, (_) => {
5218                        simulatedEvent.stop();
5219                    });
5220                }),
5221                run$1(keyup(), (component, simulatedEvent) => {
5222                    processKey(component, simulatedEvent, getKeyupRules, keyingConfig, keyingState).each((_) => {
5223                        simulatedEvent.stop();
5224                    });
5225                })
5226            ];
5227            return derive$2(onFocusHandler.toArray().concat(keyboardEvents));
5228        };
5229        const me = {
5230            schema,
5231            processKey,
5232            toEvents
5233        };
5234        return me;
5235    };
5236
5237    const create$2 = (cyclicField) => {
5238        const schema = [
5239            option$3('onEscape'),
5240            option$3('onEnter'),
5241            defaulted('selector', '[data-alloy-tabstop="true"]:not(:disabled)'),
5242            defaulted('firstTabstop', 0),
5243            defaulted('useTabstopAt', always),
5244            // Maybe later we should just expose isVisible
5245            option$3('visibilitySelector')
5246        ].concat([
5247            cyclicField
5248        ]);
5249        // TODO: Test this
5250        const isVisible = (tabbingConfig, element) => {
5251            const target = tabbingConfig.visibilitySelector
5252                .bind((sel) => closest$3(element, sel))
5253                .getOr(element);
5254            // NOTE: We can't use Visibility.isVisible, because the toolbar has width when it has closed, just not height.
5255            return get$d(target) > 0;
5256        };
5257        const findInitial = (component, tabbingConfig) => {
5258            const tabstops = descendants(component.element, tabbingConfig.selector);
5259            const visibles = filter$2(tabstops, (elem) => isVisible(tabbingConfig, elem));
5260            return Optional.from(visibles[tabbingConfig.firstTabstop]);
5261        };
5262        const findCurrent = (component, tabbingConfig) => tabbingConfig.focusManager.get(component)
5263            .bind((elem) => closest$3(elem, tabbingConfig.selector));
5264        const isTabstop = (tabbingConfig, element) => isVisible(tabbingConfig, element) && tabbingConfig.useTabstopAt(element);
5265        // Fire an alloy focus on the first visible element that matches the selector
5266        const focusIn = (component, tabbingConfig, _tabbingState) => {
5267            findInitial(component, tabbingConfig).each((target) => {
5268                tabbingConfig.focusManager.set(component, target);
5269            });
5270        };
5271        const goFromTabstop = (component, tabstops, stopIndex, tabbingConfig, cycle) => cycle(tabstops, stopIndex, (elem) => isTabstop(tabbingConfig, elem))
5272            .fold(
5273        // Even if there is only one, still capture the event if cycling
5274        () => tabbingConfig.cyclic ? Optional.some(true) : Optional.none(), (target) => {
5275            tabbingConfig.focusManager.set(component, target);
5276            // Kill the event
5277            return Optional.some(true);
5278        });
5279        const go = (component, _simulatedEvent, tabbingConfig, cycle) => {
5280            // 1. Find our current tabstop
5281            // 2. Find the index of that tabstop
5282            // 3. Cycle the tabstop
5283            // 4. Fire alloy focus on the resultant tabstop
5284            const tabstops = filter$2(descendants(component.element, tabbingConfig.selector), (element) => isVisible(tabbingConfig, element));
5285            return findCurrent(component, tabbingConfig).bind((tabstop) => {
5286                // focused component
5287                const optStopIndex = findIndex$1(tabstops, curry(eq, tabstop));
5288                return optStopIndex.bind((stopIndex) => goFromTabstop(component, tabstops, stopIndex, tabbingConfig, cycle));
5289            });
5290        };
5291        const goBackwards = (component, simulatedEvent, tabbingConfig) => {
5292            const navigate = tabbingConfig.cyclic ? cyclePrev : tryPrev;
5293            return go(component, simulatedEvent, tabbingConfig, navigate);
5294        };
5295        const goForwards = (component, simulatedEvent, tabbingConfig) => {
5296            const navigate = tabbingConfig.cyclic ? cycleNext : tryNext;
5297            return go(component, simulatedEvent, tabbingConfig, navigate);
5298        };
5299        const isFirstChild = (elem) => parentNode(elem).bind(firstChild).exists((child) => eq(child, elem));
5300        const goFromPseudoTabstop = (component, simulatedEvent, tabbingConfig) => findCurrent(component, tabbingConfig).filter((elem) => !tabbingConfig.useTabstopAt(elem))
5301            .bind((elem) => (isFirstChild(elem) ? goBackwards : goForwards)(component, simulatedEvent, tabbingConfig));
5302        const execute = (component, simulatedEvent, tabbingConfig) => tabbingConfig.onEnter.bind((f) => f(component, simulatedEvent));
5303        const exit = (component, simulatedEvent, tabbingConfig) => {
5304            component.getSystem().broadcastOn([closeTooltips()], {
5305                closedTooltip: () => {
5306                    simulatedEvent.stop();
5307                }
5308            });
5309            if (!simulatedEvent.isStopped()) {
5310                return tabbingConfig.onEscape.bind((f) => f(component, simulatedEvent));
5311            }
5312            else {
5313                return Optional.none();
5314            }
5315        };
5316        const getKeydownRules = constant$1([
5317            rule(and([isShift$1, inSet(TAB)]), goBackwards),
5318            rule(inSet(TAB), goForwards),
5319            rule(and([isNotShift, inSet(ENTER)]), execute)
5320        ]);
5321        const getKeyupRules = constant$1([
5322            rule(inSet(ESCAPE), exit),
5323            rule(inSet(TAB), goFromPseudoTabstop),
5324        ]);
vendor: 4,830 bytes, lines 5325-5429
5325        return typical(schema, NoState.init, getKeydownRules, getKeyupRules, () => Optional.some(focusIn));
5326    };
5327
5328    var AcyclicType = create$2(customField('cyclic', never));
5329
5330    var CyclicType = create$2(customField('cyclic', always));
5331
5332    const inside = (target) => ((isTag('input')(target) && get$g(target, 'type') !== 'radio') ||
5333        isTag('textarea')(target));
5334
5335    const doDefaultExecute = (component, _simulatedEvent, focused) => {
5336        // Note, we use to pass through simulatedEvent here and make target: component. This simplification
5337        // may be a problem
5338        dispatch(component, focused, execute$5());
5339        return Optional.some(true);
5340    };
5341    const defaultExecute = (component, simulatedEvent, focused) => {
5342        const isComplex = inside(focused) && inSet(SPACE)(simulatedEvent.event);
5343        return isComplex ? Optional.none() : doDefaultExecute(component, simulatedEvent, focused);
5344    };
5345    // On Firefox, pressing space fires a click event if the element maintains focus and fires a keyup. This
5346    // stops the keyup, which should stop the click. We might want to make this only work for buttons and Firefox etc,
5347    // but at this stage it's cleaner to just always do it. It makes sense that Keying that handles space should handle
5348    // keyup also. This does make the name confusing, though.
5349    const stopEventForFirefox = (_component, _simulatedEvent) => Optional.some(true);
5350
5351    const schema$z = [
5352        defaulted('execute', defaultExecute),
5353        defaulted('useSpace', false),
5354        defaulted('useEnter', true),
5355        defaulted('useControlEnter', false),
5356        defaulted('useDown', false)
5357    ];
5358    const execute$4 = (component, simulatedEvent, executeConfig) => executeConfig.execute(component, simulatedEvent, component.element);
5359    const getKeydownRules$5 = (component, _simulatedEvent, executeConfig, _executeState) => {
5360        const spaceExec = executeConfig.useSpace && !inside(component.element) ? SPACE : [];
5361        const enterExec = executeConfig.useEnter ? ENTER : [];
5362        const downExec = executeConfig.useDown ? DOWN : [];
5363        const execKeys = spaceExec.concat(enterExec).concat(downExec);
5364        return [
5365            rule(inSet(execKeys), execute$4)
5366        ].concat(executeConfig.useControlEnter ? [
5367            rule(and([isControl, inSet(ENTER)]), execute$4)
5368        ] : []);
5369    };
5370    const getKeyupRules$5 = (component, _simulatedEvent, executeConfig, _executeState) => executeConfig.useSpace && !inside(component.element) ?
5371        [rule(inSet(SPACE), stopEventForFirefox)] :
5372        [];
5373    var ExecutionType = typical(schema$z, NoState.init, getKeydownRules$5, getKeyupRules$5, () => Optional.none());
5374
5375    const flatgrid$1 = () => {
5376        const dimensions = value$2();
5377        const setGridSize = (numRows, numColumns) => {
5378            dimensions.set({ numRows, numColumns });
5379        };
5380        const getNumRows = () => dimensions.get().map((d) => d.numRows);
5381        const getNumColumns = () => dimensions.get().map((d) => d.numColumns);
5382        return nu$4({
5383            readState: () => dimensions.get().map((d) => ({
5384                numRows: String(d.numRows),
5385                numColumns: String(d.numColumns)
5386            })).getOr({
5387                numRows: '?',
5388                numColumns: '?'
5389            }),
5390            setGridSize,
5391            getNumRows,
5392            getNumColumns
5393        });
5394    };
5395    const init$g = (spec) => spec.state(spec);
5396
5397    var KeyingState = /*#__PURE__*/Object.freeze({
5398        __proto__: null,
5399        flatgrid: flatgrid$1,
5400        init: init$g
5401    });
5402
5403    // Looks up direction (considering LTR and RTL), finds the focused element,
5404    // and tries to move. If it succeeds, triggers focus and kills the event.
5405    const useH = (movement) => (component, simulatedEvent, config, state) => {
5406        const move = movement(component.element);
5407        return use(move, component, simulatedEvent, config, state);
5408    };
5409    const west = (moveLeft, moveRight) => {
5410        const movement = onDirection(moveLeft, moveRight);
5411        return useH(movement);
5412    };
5413    const east = (moveLeft, moveRight) => {
5414        const movement = onDirection(moveRight, moveLeft);
5415        return useH(movement);
5416    };
5417    const useV = (move) => (component, simulatedEvent, config, state) => use(move, component, simulatedEvent, config, state);
5418    const use = (move, component, simulatedEvent, config, state) => {
5419        const outcome = config.focusManager.get(component).bind((focused) => move(component.element, focused, config, state));
5420        return outcome.map((newFocus) => {
5421            config.focusManager.set(component, newFocus);
5422            return true;
5423        });
5424    };
5425    const north = useV;
5426    const south = useV;
5427    const move$1 = useV;
5428
5429    const locate = (candidates, predicate) =>
5429 findIndex$1(candidates, predicate).map((index) => ({
5430        index,
5431        candidates
5432    }));
5433
5434    const locateVisible = (container, current, selector) => {
5435        const predicate = (x) => eq(x, current);
5436        const candidates = descendants(container, selector);
5437        const visible = filter$2(candidates, isVisible);
5438        return locate(visible, predicate);
5439    };
5440    const findIndex = (elements, target) => findIndex$1(elements, (elem) => eq(target, elem));
5441
5442    const withGrid = (values, index, numCols, f) => {
5443        const oldRow = Math.floor(index / numCols);
5444        const oldColumn = index % numCols;
5445        return f(oldRow, oldColumn).bind((address) => {
5446            const newIndex = address.row * numCols + address.column;
5447            return newIndex >= 0 && newIndex < values.length ? Optional.some(values[newIndex]) : Optional.none();
5448        });
5449    };
5450    const cycleHorizontal$1 = (values, index, numRows, numCols, delta) => withGrid(values, index, numCols, (oldRow, oldColumn) => {
5451        const onLastRow = oldRow === numRows - 1;
5452        const colsInRow = onLastRow ? values.length - (oldRow * numCols) : numCols;
5453        const newColumn = cycleBy(oldColumn, delta, 0, colsInRow - 1);
5454        return Optional.some({
5455            row: oldRow,
5456            column: newColumn
5457        });
5458    });
5459    const cycleVertical$1 = (values, index, numRows, numCols, delta) => withGrid(values, index, numCols, (oldRow, oldColumn) => {
5460        const newRow = cycleBy(oldRow, delta, 0, numRows - 1);
5461        const onLastRow = newRow === numRows - 1;
vendor: 14,516 bytes, lines 5462-5719
5462        const colsInRow = onLastRow ? values.length - (newRow * numCols) : numCols;
5463        const newCol = clamp(oldColumn, 0, colsInRow - 1);
5464        return Optional.some({
5465            row: newRow,
5466            column: newCol
5467        });
5468    });
5469    const cycleRight$1 = (values, index, numRows, numCols) => cycleHorizontal$1(values, index, numRows, numCols, +1);
5470    const cycleLeft$1 = (values, index, numRows, numCols) => cycleHorizontal$1(values, index, numRows, numCols, -1);
5471    const cycleUp$1 = (values, index, numRows, numCols) => cycleVertical$1(values, index, numRows, numCols, -1);
5472    const cycleDown$1 = (values, index, numRows, numCols) => cycleVertical$1(values, index, numRows, numCols, +1);
5473
5474    const schema$y = [
5475        required$1('selector'),
5476        defaulted('execute', defaultExecute),
5477        onKeyboardHandler('onEscape'),
5478        defaulted('captureTab', false),
5479        initSize()
5480    ];
5481    const focusIn$4 = (component, gridConfig, _gridState) => {
5482        descendant(component.element, gridConfig.selector).each((first) => {
5483            gridConfig.focusManager.set(component, first);
5484        });
5485    };
5486    const findCurrent$1 = (component, gridConfig) => gridConfig.focusManager.get(component).bind((elem) => closest$3(elem, gridConfig.selector));
5487    const execute$3 = (component, simulatedEvent, gridConfig, _gridState) => findCurrent$1(component, gridConfig)
5488        .bind((focused) => gridConfig.execute(component, simulatedEvent, focused));
5489    const doMove$2 = (cycle) => (element, focused, gridConfig, gridState) => locateVisible(element, focused, gridConfig.selector)
5490        .bind((identified) => cycle(identified.candidates, identified.index, gridState.getNumRows().getOr(gridConfig.initSize.numRows), gridState.getNumColumns().getOr(gridConfig.initSize.numColumns)));
5491    const handleTab = (_component, _simulatedEvent, gridConfig) => gridConfig.captureTab ? Optional.some(true) : Optional.none();
5492    const doEscape$1 = (component, simulatedEvent, gridConfig) => gridConfig.onEscape(component, simulatedEvent);
5493    const moveLeft$3 = doMove$2(cycleLeft$1);
5494    const moveRight$3 = doMove$2(cycleRight$1);
5495    const moveNorth$1 = doMove$2(cycleUp$1);
5496    const moveSouth$1 = doMove$2(cycleDown$1);
5497    const getKeydownRules$4 = constant$1([
5498        rule(inSet(LEFT), west(moveLeft$3, moveRight$3)),
5499        rule(inSet(RIGHT), east(moveLeft$3, moveRight$3)),
5500        rule(inSet(UP), north(moveNorth$1)),
5501        rule(inSet(DOWN), south(moveSouth$1)),
5502        rule(and([isShift$1, inSet(TAB)]), handleTab),
5503        rule(and([isNotShift, inSet(TAB)]), handleTab),
5504        // Probably should make whether space is used configurable
5505        rule(inSet(SPACE.concat(ENTER)), execute$3)
5506    ]);
5507    const getKeyupRules$4 = constant$1([
5508        rule(inSet(ESCAPE), doEscape$1),
5509        rule(inSet(SPACE), stopEventForFirefox)
5510    ]);
5511    var FlatgridType = typical(schema$y, flatgrid$1, getKeydownRules$4, getKeyupRules$4, () => Optional.some(focusIn$4));
5512
5513    const f = (container, selector, current, delta, getNewIndex) => {
5514        const isDisabledButton = (candidate) => name$3(candidate) === 'button' && get$g(candidate, 'disabled') === 'disabled';
5515        const tryNewIndex = (initial, index, candidates) => getNewIndex(initial, index, delta, 0, candidates.length - 1, candidates[index], (newIndex) => isDisabledButton(candidates[newIndex]) ?
5516            tryNewIndex(initial, newIndex, candidates) :
5517            Optional.from(candidates[newIndex]));
5518        // I wonder if this will be a problem when the focused element is invisible (shouldn't happen)
5519        return locateVisible(container, current, selector).bind((identified) => {
5520            const index = identified.index;
5521            const candidates = identified.candidates;
5522            return tryNewIndex(index, index, candidates);
5523        });
5524    };
5525    const horizontalWithoutCycles = (container, selector, current, delta) => f(container, selector, current, delta, (prevIndex, v, d, min, max, oldCandidate, onNewIndex) => {
5526        const newIndex = clamp(v + d, min, max);
5527        return newIndex === prevIndex ? Optional.from(oldCandidate) : onNewIndex(newIndex);
5528    });
5529    const horizontal = (container, selector, current, delta) => f(container, selector, current, delta, (prevIndex, v, d, min, max, _oldCandidate, onNewIndex) => {
5530        const newIndex = cycleBy(v, d, min, max);
5531        // If we've cycled back to the original index, we've failed to find a new valid candidate
5532        return newIndex === prevIndex ? Optional.none() : onNewIndex(newIndex);
5533    });
5534
5535    const schema$x = [
5536        required$1('selector'),
5537        defaulted('getInitial', Optional.none),
5538        defaulted('execute', defaultExecute),
5539        onKeyboardHandler('onEscape'),
5540        defaulted('executeOnMove', false),
5541        defaulted('allowVertical', true),
5542        defaulted('allowHorizontal', true),
5543        defaulted('cycles', true)
5544    ];
5545    // TODO: Remove dupe.
5546    // TODO: Probably use this for not just execution.
5547    const findCurrent = (component, flowConfig) => flowConfig.focusManager.get(component).bind((elem) => closest$3(elem, flowConfig.selector));
5548    const execute$2 = (component, simulatedEvent, flowConfig) => findCurrent(component, flowConfig).bind((focused) => flowConfig.execute(component, simulatedEvent, focused));
5549    const focusIn$3 = (component, flowConfig, _state) => {
5550        flowConfig.getInitial(component).orThunk(() => descendant(component.element, flowConfig.selector)).each((first) => {
5551            flowConfig.focusManager.set(component, first);
5552        });
5553    };
5554    const moveLeft$2 = (element, focused, info) => (info.cycles ? horizontal : horizontalWithoutCycles)(element, info.selector, focused, -1);
5555    const moveRight$2 = (element, focused, info) => (info.cycles ? horizontal : horizontalWithoutCycles)(element, info.selector, focused, +1);
5556    const doMove$1 = (movement) => (component, simulatedEvent, flowConfig, flowState) => movement(component, simulatedEvent, flowConfig, flowState).bind(() => flowConfig.executeOnMove ?
5557        execute$2(component, simulatedEvent, flowConfig) :
5558        Optional.some(true));
5559    const doEscape = (component, simulatedEvent, flowConfig) => flowConfig.onEscape(component, simulatedEvent);
5560    const getKeydownRules$3 = (_component, _se, flowConfig, _flowState) => {
5561        const westMovers = [...flowConfig.allowHorizontal ? LEFT : []].concat(flowConfig.allowVertical ? UP : []);
5562        const eastMovers = [...flowConfig.allowHorizontal ? RIGHT : []].concat(flowConfig.allowVertical ? DOWN : []);
5563        return [
5564            rule(inSet(westMovers), doMove$1(west(moveLeft$2, moveRight$2))),
5565            rule(inSet(eastMovers), doMove$1(east(moveLeft$2, moveRight$2))),
5566            rule(inSet(ENTER), execute$2),
5567            rule(inSet(SPACE), execute$2)
5568        ];
5569    };
5570    const getKeyupRules$3 = constant$1([
5571        rule(inSet(SPACE), stopEventForFirefox),
5572        rule(inSet(ESCAPE), doEscape)
5573    ]);
5574    var FlowType = typical(schema$x, NoState.init, getKeydownRules$3, getKeyupRules$3, () => Optional.some(focusIn$3));
5575
5576    const toCell = (matrix, rowIndex, columnIndex) => Optional.from(matrix[rowIndex]).bind((row) => Optional.from(row[columnIndex]).map((cell) => ({
5577        rowIndex,
5578        columnIndex,
5579        cell
5580    })));
5581    const cycleHorizontal = (matrix, rowIndex, startCol, deltaCol) => {
5582        const row = matrix[rowIndex];
5583        const colsInRow = row.length;
5584        const newColIndex = cycleBy(startCol, deltaCol, 0, colsInRow - 1);
5585        return toCell(matrix, rowIndex, newColIndex);
5586    };
5587    const cycleVertical = (matrix, colIndex, startRow, deltaRow) => {
5588        const nextRowIndex = cycleBy(startRow, deltaRow, 0, matrix.length - 1);
5589        const colsInNextRow = matrix[nextRowIndex].length;
5590        const nextColIndex = clamp(colIndex, 0, colsInNextRow - 1);
5591        return toCell(matrix, nextRowIndex, nextColIndex);
5592    };
5593    const moveHorizontal = (matrix, rowIndex, startCol, deltaCol) => {
5594        const row = matrix[rowIndex];
5595        const colsInRow = row.length;
5596        const newColIndex = clamp(startCol + deltaCol, 0, colsInRow - 1);
5597        return toCell(matrix, rowIndex, newColIndex);
5598    };
5599    const moveVertical = (matrix, colIndex, startRow, deltaRow) => {
5600        const nextRowIndex = clamp(startRow + deltaRow, 0, matrix.length - 1);
5601        const colsInNextRow = matrix[nextRowIndex].length;
5602        const nextColIndex = clamp(colIndex, 0, colsInNextRow - 1);
5603        return toCell(matrix, nextRowIndex, nextColIndex);
5604    };
5605    // return address(Math.floor(index / columns), index % columns);
5606    const cycleRight = (matrix, startRow, startCol) => cycleHorizontal(matrix, startRow, startCol, +1);
5607    const cycleLeft = (matrix, startRow, startCol) => cycleHorizontal(matrix, startRow, startCol, -1);
5608    const cycleUp = (matrix, startRow, startCol) => cycleVertical(matrix, startCol, startRow, -1);
5609    const cycleDown = (matrix, startRow, startCol) => cycleVertical(matrix, startCol, startRow, +1);
5610    const moveLeft$1 = (matrix, startRow, startCol) => moveHorizontal(matrix, startRow, startCol, -1);
5611    const moveRight$1 = (matrix, startRow, startCol) => moveHorizontal(matrix, startRow, startCol, +1);
5612    const moveUp$1 = (matrix, startRow, startCol) => moveVertical(matrix, startCol, startRow, -1);
5613    const moveDown$1 = (matrix, startRow, startCol) => moveVertical(matrix, startCol, startRow, +1);
5614
5615    const schema$w = [
5616        requiredObjOf('selectors', [
5617            required$1('row'),
5618            required$1('cell')
5619        ]),
5620        // Used to determine whether pressing right/down at the end cycles back to the start/top
5621        defaulted('cycles', true),
5622        defaulted('previousSelector', Optional.none),
5623        defaulted('execute', defaultExecute)
5624    ];
5625    const focusIn$2 = (component, matrixConfig, _state) => {
5626        const focused = matrixConfig.previousSelector(component).orThunk(() => {
5627            const selectors = matrixConfig.selectors;
5628            return descendant(component.element, selectors.cell);
5629        });
5630        focused.each((cell) => {
5631            matrixConfig.focusManager.set(component, cell);
5632        });
5633    };
5634    const execute$1 = (component, simulatedEvent, matrixConfig) => search(component.element).bind((focused) => matrixConfig.execute(component, simulatedEvent, focused));
5635    const toMatrix = (rows, matrixConfig) => map$2(rows, (row) => descendants(row, matrixConfig.selectors.cell));
5636    const doMove = (ifCycle, ifMove) => (element, focused, matrixConfig) => {
5637        const move = matrixConfig.cycles ? ifCycle : ifMove;
5638        return closest$3(focused, matrixConfig.selectors.row).bind((inRow) => {
5639            const cellsInRow = descendants(inRow, matrixConfig.selectors.cell);
5640            return findIndex(cellsInRow, focused).bind((colIndex) => {
5641                const allRows = descendants(element, matrixConfig.selectors.row);
5642                return findIndex(allRows, inRow).bind((rowIndex) => {
5643                    // Now, make the matrix.
5644                    const matrix = toMatrix(allRows, matrixConfig);
5645                    return move(matrix, rowIndex, colIndex).map((next) => next.cell);
5646                });
5647            });
5648        });
5649    };
5650    const moveLeft = doMove(cycleLeft, moveLeft$1);
5651    const moveRight = doMove(cycleRight, moveRight$1);
5652    const moveNorth = doMove(cycleUp, moveUp$1);
5653    const moveSouth = doMove(cycleDown, moveDown$1);
5654    const getKeydownRules$2 = constant$1([
5655        rule(inSet(LEFT), west(moveLeft, moveRight)),
5656        rule(inSet(RIGHT), east(moveLeft, moveRight)),
5657        rule(inSet(UP), north(moveNorth)),
5658        rule(inSet(DOWN), south(moveSouth)),
5659        rule(inSet(SPACE.concat(ENTER)), execute$1)
5660    ]);
5661    const getKeyupRules$2 = constant$1([
5662        rule(inSet(SPACE), stopEventForFirefox)
5663    ]);
5664    var MatrixType = typical(schema$w, NoState.init, getKeydownRules$2, getKeyupRules$2, () => Optional.some(focusIn$2));
5665
5666    const schema$v = [
5667        required$1('selector'),
5668        defaulted('execute', defaultExecute)
5669    ];
5670    const execute = (component, simulatedEvent, menuConfig) => menuConfig.focusManager.get(component).bind((focused) => menuConfig.execute(component, simulatedEvent, focused));
5671    const focusIn$1 = (component, menuConfig, _state) => {
5672        // Maybe keep selection if it was there before
5673        descendant(component.element, menuConfig.selector).each((first) => {
5674            menuConfig.focusManager.set(component, first);
5675        });
5676    };
5677    const moveUp = (element, focused, info) => horizontal(element, info.selector, focused, -1);
5678    const moveDown = (element, focused, info) => horizontal(element, info.selector, focused, +1);
5679    const getKeydownRules$1 = constant$1([
5680        rule(inSet(UP), move$1(moveUp)),
5681        rule(inSet(DOWN), move$1(moveDown)),
5682        rule(inSet(ENTER), execute),
5683        rule(inSet(SPACE), execute)
5684    ]);
5685    const getKeyupRules$1 = constant$1([
5686        rule(inSet(SPACE), stopEventForFirefox)
5687    ]);
5688    var MenuType = typical(schema$v, NoState.init, getKeydownRules$1, getKeyupRules$1, () => Optional.some(focusIn$1));
5689
5690    const schema$u = [
5691        onKeyboardHandler('onSpace'),
5692        onKeyboardHandler('onEnter'),
5693        onKeyboardHandler('onShiftEnter'),
5694        onKeyboardHandler('onLeft'),
5695        onKeyboardHandler('onRight'),
5696        onKeyboardHandler('onTab'),
5697        onKeyboardHandler('onShiftTab'),
5698        onKeyboardHandler('onUp'),
5699        onKeyboardHandler('onDown'),
5700        onKeyboardHandler('onEscape'),
5701        defaulted('stopSpaceKeyup', false),
5702        option$3('focusIn')
5703    ];
5704    const getKeydownRules = (component, simulatedEvent, specialInfo) => [
5705        rule(inSet(SPACE), specialInfo.onSpace),
5706        rule(and([isNotShift, inSet(ENTER)]), specialInfo.onEnter),
5707        rule(and([isShift$1, inSet(ENTER)]), specialInfo.onShiftEnter),
5708        rule(and([isShift$1, inSet(TAB)]), specialInfo.onShiftTab),
5709        rule(and([isNotShift, inSet(TAB)]), specialInfo.onTab),
5710        rule(inSet(UP), specialInfo.onUp),
5711        rule(inSet(DOWN), specialInfo.onDown),
5712        rule(inSet(LEFT), specialInfo.onLeft),
5713        rule(inSet(RIGHT), specialInfo.onRight),
5714        rule(inSet(SPACE), specialInfo.onSpace)
5715    ];
5716    const getKeyupRules = (component, simulatedEvent, specialInfo) => [
5717        ...(specialInfo.stopSpaceKeyup ? [rule(inSet(SPACE), stopEventForFirefox)] : []),
5718        rule(inSet(ESCAPE), specialInfo.onEscape)
5719    ];
5720    var SpecialType = typical(schema$u, NoState.init, getKeydownRules, getKeyupRules, (specialInfo) => specialInfo.focusIn);
5721
5722    const acyclic = AcyclicType.schema();
5723    const cyclic = CyclicType.schema();
5724    const flow = FlowType.schema();
5725    const flatgrid = FlatgridType.schema();
5726    const matrix = MatrixType.schema();
5727    const execution = ExecutionType.schema();
5728    const menu = MenuType.schema();
5729    const special = SpecialType.schema();
5730
5731    var KeyboardBranches = /*#__PURE__*/Object.freeze({
5732        __proto__: null,
5733        acyclic: acyclic,
5734        cyclic: cyclic,
5735        flow: flow,
5736        flatgrid: flatgrid,
5737        matrix: matrix,
5738        execution: execution,
5739        menu: menu,
5740        special: special
5741    });
5742
5743    const isFlatgridState = (keyState) => hasNonNullableKey(keyState, 'setGridSize');
5744    const Keying = createModes({
5745        branchKey: 'mode',
5746        branches: KeyboardBranches,
5747        name: 'keying',
5748        active: {
5749            events: (keyingConfig, keyingState) => {
5750                const handler = keyingConfig.handler;
5751                return handler.toEvents(keyingConfig, keyingState);
5752            }
5753        },
5754        apis: {
5755            focusIn: (component, keyConfig, keyState) => {
5756                // If we have a custom sendFocusIn function, use that.
5757                // Otherwise, we just trigger focus on the outer element.
5758                keyConfig.sendFocusIn(keyConfig).fold(() => {
5759                    component.getSystem().triggerFocus(component.element, component.element);
5760                }, (sendFocusIn) => {
5761                    sendFocusIn(component, keyConfig, keyState);
5762                });
5763            },
5764            // These APIs are going to be interesting because they are not
5765            // available for all keying modes
5766            setGridSize: (component, keyConfig, keyState, numRows, numColumns) => {
5767                if (!isFlatgridState(keyState)) {
5768                    // eslint-disable-next-line no-console
5769                    console.error('Layout does not support setGridSize');
5770                }
5771                else {
5772                    keyState.setGridSize(numRows, numColumns);
5773                }
5774            }
5775        },
5776        state: KeyingState
5777    });
5778
5779    const premadeTag = generate$6('alloy-premade');
5780    const premade$1 = (comp) => {
5781        Object.defineProperty(comp.element.dom, premadeTag, {
5782            value: comp.uid,
5783            writable: true
5784        });
5785        return wrap(premadeTag, comp);
5786    };
5787    const isPremade = (element) => has$2(element.dom, premadeTag);
5788    const getPremade = (spec) => get$h(spec, premadeTag);
5789    const makeApi = (f) => markAsSketchApi((component, ...rest) => f(component.getApis(), component, ...rest), f);
5790
5791    const isConnected = (comp) => comp.getSystem().isConnected();
5792    const fireDetaching = (component) => {
5793        emit(component, detachedFromDom());
5794        const children = component.components();
5795        each$1(children, fireDetaching);
5796    };
5797    const fireAttaching = (component) => {
5798        const children = component.components();
5799        each$1(children, fireAttaching);
5800        emit(component, attachedToDom());
5801    };
5802    // Unlike attach, a virtualAttach makes no actual DOM changes.
5803    // This is because it should only be used in a situation
5804    // where we are patching an existing element.
5805    const virtualAttach = (parent, child) => {
5806        // So we still add it to the world
5807        parent.getSystem().addToWorld(child);
5808        // And we fire attaching ONLY if it's already in the DOM
5809        if (inBody(parent.element)) {
5810            fireAttaching(child);
5811        }
5812    };
5813    // Unlike detach, a virtualDetach makes no actual DOM changes.
5814    // This is because it's used in patching circumstances.
5815    const virtualDetach = (comp) => {
5816        fireDetaching(comp);
5817        comp.getSystem().removeFromWorld(comp);
5818    };
5819    const attach$1 = (parent, child) => {
5820        append$2(parent.element, child.element);
5821    };
5822    const detachChildren$1 = (component) => {
5823        // This will not detach the component, but will detach its children and sync at the end.
5824        each$1(component.components(), (childComp) => remove$7(childComp.element));
5825        // Clear the component also.
5826        empty(component.element);
5827        component.syncComponents();
5828    };
5829    const replaceChildren = (component, newSpecs, buildNewChildren) => {
5830        // Detach all existing children
5831        const subs = component.components();
5832        detachChildren$1(component);
5833        const newChildren = buildNewChildren(newSpecs);
5834        // Determine which components have been deleted and remove them from the world
5835        const deleted = difference(subs, newChildren);
5836        each$1(deleted, (comp) => {
5837            fireDetaching(comp);
5838            component.getSystem().removeFromWorld(comp);
5839        });
5840        // Add all new components
5841        each$1(newChildren, (childComp) => {
5842            // If the component isn't connected, ie is new, then we also need to add it to the world
5843            if (!isConnected(childComp)) {
5844                component.getSystem().addToWorld(childComp);
5845                attach$1(component, childComp);
5846                if (inBody(component.element)) {
5847                    fireAttaching(childComp);
5848                }
5849            }
5850            else {
5851                attach$1(component, childComp);
5852            }
5853        });
5854        component.syncComponents();
5855    };
5856    const virtualReplaceChildren = (component, newSpecs, buildNewChildren) => {
5857        // When replacing we don't want to fire detachedFromDom and attachedToDom again for a premade that has just had its position in the children moved around,
5858        // so we only detach initially if we aren't a premade. Premades will be detached later, but only if they are no longer in the child list.
5859        const subs = component.components();
5860        const existingComps = bind$3(newSpecs, (spec) => getPremade(spec).toArray());
5861        each$1(subs, (childComp) => {
5862            if (!contains$2(existingComps, childComp)) {
5863                virtualDetach(childComp);
5864            }
5865        });
5866        const newChildren = buildNewChildren(newSpecs);
5867        // Determine which components have been deleted and remove them from the world
5868        // It's probable the component has already been detached beforehand so only
5869        // detach what's still attached to the world (i.e removed premades)
5870        const deleted = difference(subs, newChildren);
5871        each$1(deleted, (deletedComp) => {
5872            if (isConnected(deletedComp)) {
5873                virtualDetach(deletedComp);
5874            }
5875        });
5876        // Add all new components
5877        each$1(newChildren, (childComp) => {
5878            // If the component isn't connected, ie is new, then we also need to add it to the world
5879            if (!isConnected(childComp)) {
5880                virtualAttach(component, childComp);
5881            }
5882        });
5883        component.syncComponents();
5884    };
5885
5886    const attach = (parent, child) => {
5887        attachWith(parent, child, append$2);
5888    };
5889    const attachWith = (parent, child, insertion) => {
5890        parent.getSystem().addToWorld(child);
5891        insertion(parent.element, child.element);
5892        if (inBody(parent.element)) {
5893            fireAttaching(child);
5894        }
5895        parent.syncComponents();
5896    };
5897    const doDetach = (component) => {
5898        fireDetaching(component);
5899        remove$7(component.element);
5900        component.getSystem().removeFromWorld(component);
5901    };
5902    const detach = (component) => {
5903        const parent$1 = parent(component.element).bind((p) => component.getSystem().getByDom(p).toOptional());
5904        doDetach(component);
5905        parent$1.each((p) => {
5906            p.syncComponents();
5907        });
5908    };
5909    const detachChildren = (component) => {
5910        // This will not detach the component, but will detach its children and sync at the end.
5911        const subs = component.components();
5912        each$1(subs, doDetach);
5913        // Clear the component also.
5914        empty(component.element);
5915        component.syncComponents();
5916    };
5917    const attachSystem = (element, guiSystem) => {
5918        attachSystemWith(element, guiSystem, append$2);
5919    };
5920    const attachSystemAfter = (element, guiSystem) => {
5921        attachSystemWith(element, guiSystem, after$1);
5922    };
5923    const attachSystemWith = (element, guiSystem, inserter) => {
5924        inserter(element, guiSystem.element);
5925        const children$1 = children(guiSystem.element);
5926        each$1(children$1, (child) => {
5927            guiSystem.getByDom(child).each(fireAttaching);
5928        });
5929    };
5930    const detachSystem = (guiSystem) => {
5931        const children$1 = children(guiSystem.element);
5932        each$1(children$1, (child) => {
5933            guiSystem.getByDom(child).each(fireDetaching);
5934        });
5935        remove$7(guiSystem.element);
5936    };
5937
5938    const determineObsoleted = (parent, index, oldObsoleted) => {
5939        // When dealing with premades, the process of building something may have moved existing nodes around, so we see
5940        // if the child at the index position is still the same. If it isn't, we need to introduce some complex behaviour
5941        //
5942        // Example:
5943        // ```<div><premade></premade><span></span></div>```
5944        // and then moving the premade inside a blockquote
5945        // ```<div><blockquote><premade></premade></blockquote><span></span></div>```
5946        //
5947        // so when you go to replace the first thing it would think there is only 1 child which w
vendor: 4,640 bytes, lines 5947-6044
5947ould be the span, so in
5948        // this case we insert a marker to keep the span in the same spot.
5949        const newObsoleted = child$2(parent, index);
5950        return newObsoleted.map((newObs) => {
5951            const elemChanged = oldObsoleted.exists((o) => !eq(o, newObs));
5952            // Adding a marker prevents the case where a premade is added to something shifting it from where
5953            // it was. That in turn un-synced all trailing children and made it so they couldn't be patched.
5954            if (elemChanged) {
5955                const oldTag = oldObsoleted.map(name$3).getOr('span');
5956                const marker = SugarElement.fromTag(oldTag);
5957                before$1(newObs, marker);
5958                return marker;
5959            }
5960            else {
5961                return newObs;
5962            }
5963        });
5964    };
5965    const ensureInDom = (parent, child, obsoleted) => {
5966        obsoleted.fold(
5967        // There is nothing here, so just append to the parent
5968        () => append$2(parent, child), (obs) => {
5969            if (!eq(obs, child)) {
5970                // This situation occurs when the DOM element that has been patched when building it is no
5971                // longer the one that we need to replace. This is probably caused by premades.
5972                before$1(obs, child);
5973                remove$7(obs);
5974            }
5975        });
5976    };
5977    const patchChildrenWith = (parent, nu, f) => {
5978        const builtChildren = map$2(nu, f);
5979        // Need to regather the children in case some of the previous children have moved
5980        // to an earlier index. So this just prunes any leftover children in the dom.
5981        const currentChildren = children(parent);
5982        each$1(currentChildren.slice(builtChildren.length), remove$7);
5983        return builtChildren;
5984    };
5985    const patchSpecChild = (parent, index, spec, build) => {
5986        // Before building anything, this is the DOM element we are going to try to use.
5987        const oldObsoleted = child$2(parent, index);
5988        const childComp = build(spec, oldObsoleted);
5989        const obsoleted = determineObsoleted(parent, index, oldObsoleted);
5990        ensureInDom(parent, childComp.element, obsoleted);
5991        return childComp;
5992    };
5993    const patchSpecChildren = (parent, specs, build) => patchChildrenWith(parent, specs, (spec, index) => patchSpecChild(parent, index, spec, build));
5994    const patchDomChildren = (parent, nodes) => patchChildrenWith(parent, nodes, (node, index) => {
5995        const optObsoleted = child$2(parent, index);
5996        ensureInDom(parent, node, optObsoleted);
5997        return node;
5998    });
5999
6000    const preserve = (f, container) => {
6001        const dos = getRootNode(container);
6002        const refocus = active$1(dos).bind((focused) => {
6003            const hasFocus = (elem) => eq(focused, elem);
6004            return hasFocus(container) ? Optional.some(container) : descendant$1(container, hasFocus);
6005        });
6006        const result = f(container);
6007        // If there is a focussed element, the F function may cause focus to be lost (such as by hiding elements). Restore it afterwards.
6008        refocus.each((oldFocus) => {
6009            active$1(dos).filter((newFocus) => eq(newFocus, oldFocus)).fold(() => {
6010                // Only refocus if the focus has changed, otherwise we break IE
6011                focus$4(oldFocus);
6012            }, noop);
6013        });
6014        return result;
6015    };
6016
6017    const withoutReuse = (parent, data) => {
6018        preserve(() => {
6019            replaceChildren(parent, data, () => map$2(data, parent.getSystem().build));
6020        }, parent.element);
6021    };
6022    const withReuse = (parent, data) => {
6023        // Note: We shouldn't need AriaPreserve since we're trying to keep the existing elements,
6024        // but let's just do it for now just to be safe.
6025        preserve(() => {
6026            virtualReplaceChildren(parent, data, () => {
6027                // Build the new children
6028                return patchSpecChildren(parent.element, data, parent.getSystem().buildOrPatch);
6029            });
6030        }, parent.element);
6031    };
6032
6033    const virtualReplace = (component, replacee, replaceeIndex, childSpec) => {
6034        virtualDetach(replacee);
6035        const child = patchSpecChild(component.element, replaceeIndex, childSpec, component.getSystem().buildOrPatch);
6036        virtualAttach(component, child);
6037        component.syncComponents();
6038    };
6039    const insert = (component, insertion, childSpec) => {
6040        const child = component.getSystem().build(childSpec);
6041        attachWith(component, child, insertion);
6042    };
6043    const replace = (component, replacee, replaceeIndex, childSpec) => {
6044        detach(replacee);
6045        insert(component, (p, c) => appendAt(p, c, replaceeIndex), childSpec);
6046    };
6047    const set$3 = (component, replaceConfig, replaceState, data) => {
6048        const replacer = replaceConfig.reuseDom ? withReuse : withoutReuse;
6049        return replacer(component, data);
6050    };
6051    const append = (component, replaceConfig, replaceState, appendee) => {
6052        insert(component, append$2, appendee);
6053    };
6054    const prepend = (component, replaceConfig, replaceState, prependee) => {
6055        insert(component, prepend$1, prependee);
6056    };
6057    // NOTE: Removee is going to be a component, not a spec.
6058    const remove = (component, replaceConfig, replaceState, removee) => {
6059        const children = contents(component);
6060        const foundChild = find$5(children, (child) => eq(removee.element, child.element));
6061        foundChild.each(detach);
6062    };
6063    // TODO: Rename
6064    const contents = (component, _replaceConfig) => component.components();
6065    const replaceAt = (component, replaceConfig, replaceState, replaceeIndex, replacer) => {
6066        const children = contents(component);
6067        return Optional.from(children[replaceeIndex]).map((replacee) => {
6068            replacer.fold(() => detach(replacee), (r) => {
6069                const replacer = replaceConfig.reuseDom ? virtualReplace : replace;
6070                replacer(component, replacee, replaceeIndex, r);
6071            });
6072            return replacee;
6073        });
6074    };
6075    const replaceBy = (component, replaceConfig, replaceState, replaceePred, replacer) => {
6076        const children = contents(component);
6077        return findIndex$1(children, replaceePred).bind((replaceeIndex) => replaceAt(component, replaceConfig, replaceState, replaceeIndex, replacer));
6078    };
6079
6080    var ReplaceApis = /*#__PURE__*/Object.freeze({
6081        __proto__: null,
6082        append: append,
6083        prepend: prepend,
6084        remove: remove,
6085        replaceAt: replaceAt,
6086        replaceBy: replaceBy,
6087        set: set$3,
6088        contents: contents
6089    });
6090
6091    const Replacing = create$3({
6092        fields: [
6093            defaultedBoolean('reuseDom', true)
6094        ],
6095        name: 'replacing',
6096        apis: ReplaceApis
6097    });
6098
6099    // The purpose of this check is to ensure that a simulated focus call is not going
6100    // to recurse infinitely. Essentially, if the originator of the focus call is the same
6101    // as the element receiving it, and it wasn't its own target, then stop the focus call
6102    // and log a warning.
6103    const isRecursive = (component, originator, target) => eq(originator, component.element) && !eq(originator, target);
6104    const events$h = derive$2([
6105        can(focus$3(), (component, simulatedEvent) => {
6106            // originator may not always be there. Will need to check this.
6107            const event = simulatedEvent.event;
6108            const originator = event.originator;
6109            const target = event.target;
6110            if (isRecursive(component, originator, target)) {
6111                // eslint-disable-next-line no-console
6112                console.warn(focus$3() + ' did not get interpreted by the desired target. ' +
6113                    '\nOriginator: ' + element(originator) +
6114                    '\nTarget: ' + element(target) +
6115                    '\nCheck the ' + focus$3() + ' event handlers');
6116                return false;
6117            }
6118            else {
6119                return true;
6120            }
6121        })
6122    ]);
6123
6124    var DefaultEvents = /*#__PURE__*/Object.freeze({
6125        __proto__: null,
6126        events: events$h
6127    });
6128
6129    const prefix$1 = constant$1('alloy-id-');
6130    const idAttr$1 = constant$1('data-alloy-id');
6131
6132    const prefix = prefix$1();
6133    const idAttr = idAttr$1();
6134    const write = (label, elem) => {
6135        const id = generate$6(prefix + label);
6136        writeOnly(elem, id);
6137        return id;
6138    };
6139    const writeOnly = (elem, uid) => {
6140        Object.defineProperty(elem.dom, idAttr, {
6141            value: uid,
6142            writable: true
6143        });
6144    };
6145    const read = (elem) => {
6146        const id = isElement$1(elem) ? elem.dom[idAttr] : null;
6147        return Optional.from(id);
6148    };
6149    const generate$4 = (prefix) => generate$6(prefix);
6150
6151    const make$7 = identity;
6152
6153    const NoContextApi = (getComp) => {
6154        const getMessage = (event) => `The component must be in a context to execute: ${event}` +
6155            (getComp ? '\n' + element(getComp().element) + ' is not in context.' : '');
6156        const fail = (event) => () => {
6157            throw new Error(getMessage(event));
6158        };
6159        const warn = (event) => () => {
6160            // eslint-disable-next-line no-console
6161            console.warn(getMessage(event));
6162        };
6163        return {
6164            debugInfo: constant$1('fake'),
6165            triggerEvent: warn('triggerEvent'),
6166            triggerFocus: warn('triggerFocus'),
6167            triggerEscape: warn('triggerEscape'),
6168            broadcast: warn('broadcast'),
6169            broadcastOn: warn('broadcastOn'),
6170            broadcastEvent: warn('broadcastEvent'),
6171            build: fail('build'),
6172            buildOrPatch: fail('buildOrPatch'),
6173            addToWorld: fail('addToWorld'),
6174            removeFromWorld: fail('removeFromWorld'),
6175            addToGui: fail('addToGui'),
6176            removeFromGui: fail('removeFromGui'),
6177            getByUid: fail('getByUid'),
6178            getByDom: fail('getByDom'),
6179            isConnected: never
6180        };
6181    };
6182    const singleton = NoContextApi();
6183
6184    const generateFrom$1 = (spec, all) => {
6185        /*
6186         * This takes a basic record of configured behaviours, defaults their state
6187         * and ensures that all the behaviours were valid. Will need to document
6188         * this entire process. Let's see where this is used.
6189         */
6190        const schema = map$2(all, (a) => 
6191        // Optional here probably just due to ForeignGui listing everything it supports. Can most likely
6192        // change it to strict once I fix the other errors.
6193        optionObjOf(a.name(), [
6194            required$1('config'),
6195            defaulted('state', NoState)
6196        ]));
6197        const validated = asRaw('component.behaviours', objOf(schema), spec.behaviours).fold((errInfo) => {
6198            throw new Error(formatError(errInfo) + '\nComplete spec:\n' +
6199                JSON.stringify(spec, null, 2));
6200        }, identity);
6201        return {
6202            list: all,
6203            data: map$1(validated, (optBlobThunk) => {
6204                const output = optBlobThunk.map((blob) => ({
6205                    config: blob.config,
6206                    state: blob.state.init(blob.config)
6207                }));
6208                return constant$1(output);
6209            })
6210        };
6211    };
6212    const getBehaviours$3 = (bData) => bData.list;
6213    const getData$3 = (bData) => bData.data;
6214
vendor: 4,670 bytes, lines 6215-6329
6215    const byInnerKey = (data, tuple) => {
6216        const r = {};
6217        each(data, (detail, key) => {
6218            each(detail, (value, indexKey) => {
6219                const chain = get$h(r, indexKey).getOr([]);
6220                r[indexKey] = chain.concat([
6221                    tuple(key, value)
6222                ]);
6223            });
6224        });
6225        return r;
6226    };
6227
6228    // Based on all the behaviour exhibits, and the original dom modification, identify
6229    // the overall combined dom modification that needs to occur
6230    const combine$1 = (info, baseMod, behaviours, base) => {
6231        // Clone the object so we can change it.
6232        const modsByBehaviour = { ...baseMod };
6233        each$1(behaviours, (behaviour) => {
6234            modsByBehaviour[behaviour.name()] = behaviour.exhibit(info, base);
6235        });
6236        // byAspect format: { classes: [ { name: Toggling, modification: [ 'selected' ] } ] }
6237        const byAspect = byInnerKey(modsByBehaviour, (name, modification) => ({ name, modification }));
6238        const combineObjects = (objects) => foldr(objects, (b, a) => ({ ...a.modification, ...b }), {});
6239        const combinedClasses = foldr(byAspect.classes, (b, a) => a.modification.concat(b), []);
6240        const combinedAttributes = combineObjects(byAspect.attributes);
6241        const combinedStyles = combineObjects(byAspect.styles);
6242        return nu$2({
6243            classes: combinedClasses,
6244            attributes: combinedAttributes,
6245            styles: combinedStyles
6246        });
6247    };
6248
6249    const sortKeys = (label, keyName, array, order) => {
6250        try {
6251            const sorted = sort(array, (a, b) => {
6252                const aKey = a[keyName];
6253                const bKey = b[keyName];
6254                const aIndex = order.indexOf(aKey);
6255                const bIndex = order.indexOf(bKey);
6256                if (aIndex === -1) {
6257                    throw new Error('The ordering for ' + label + ' does not have an entry for ' + aKey +
6258                        '.\nOrder specified: ' + JSON.stringify(order, null, 2));
6259                }
6260                if (bIndex === -1) {
6261                    throw new Error('The ordering for ' + label + ' does not have an entry for ' + bKey +
6262                        '.\nOrder specified: ' + JSON.stringify(order, null, 2));
6263                }
6264                if (aIndex < bIndex) {
6265                    return -1;
6266                }
6267                else if (bIndex < aIndex) {
6268                    return 1;
6269                }
6270                else {
6271                    return 0;
6272                }
6273            });
6274            return Result.value(sorted);
6275        }
6276        catch (err) {
6277            return Result.error([err]);
6278        }
6279    };
6280
6281    const uncurried = (handler, purpose) => ({
6282        handler,
6283        purpose
6284    });
6285    const curried = (handler, purpose) => ({
6286        cHandler: handler,
6287        purpose
6288    });
6289    const curryArgs = (descHandler, extraArgs) => curried(curry.apply(undefined, [descHandler.handler].concat(extraArgs)), descHandler.purpose);
6290    const getCurried = (descHandler) => descHandler.cHandler;
6291
6292    const behaviourTuple = (name, handler) => ({
6293        name,
6294        handler
6295    });
6296    const nameToHandlers = (behaviours, info) => {
6297        const r = {};
6298        each$1(behaviours, (behaviour) => {
6299            r[behaviour.name()] = behaviour.handlers(info);
6300        });
6301        return r;
6302    };
6303    const groupByEvents = (info, behaviours, base) => {
6304        const behaviourEvents = {
6305            ...base,
6306            ...nameToHandlers(behaviours, info)
6307        };
6308        // Now, with all of these events, we need to index by event name
6309        return byInnerKey(behaviourEvents, behaviourTuple);
6310    };
6311    const combine = (info, eventOrder, behaviours, base) => {
6312        const byEventName = groupByEvents(info, behaviours, base);
6313        return combineGroups(byEventName, eventOrder);
6314    };
6315    const assemble = (rawHandler) => {
6316        const handler = read$1(rawHandler);
6317        return (component, simulatedEvent, ...rest) => {
6318            const args = [component, simulatedEvent].concat(rest);
6319            if (handler.abort.apply(undefined, args)) {
6320                simulatedEvent.stop();
6321            }
6322            else if (handler.can.apply(undefined, args)) {
6323                handler.run.apply(undefined, args);
6324            }
6325        };
6326    };
6327    const missingOrderError = (eventName, tuples) => Result.error([
6328        'The event (' + eventName + ') has more than one behaviour that listens to it.\nWhen this occurs, you must ' +
6329            'specify an event ordering for the behaviours in your spec (e.g. [ "listing", "toggling" ]).\nThe behaviours that ' +
6330            'can trigger it are: ' + JSON.stringify(map$2(tuples, (c) => c.name), null, 2)
6331    ]);
6332    const fuse = (tuples, eventOrder, eventName) => {
6333        // ASSUMPTION: tuples.length will never be 0, because it wouldn't have an entry if it was 0
6334        const order = eventOrder[eventName];
6335        if (!order) {
6336            return missingOrderError(eventName, tuples);
6337        }
6338        else {
6339            return sortKeys('Event: ' + eventName, 'name', tuples, order).map((sortedTuples) => {
6340                const handlers = map$2(sortedTuples, (tuple) => tuple.handler);
6341                return fuse$1(handlers);
6342            });
6343        }
6344    };
6345    const combineGroups = (byEventName, eventOrder) => {
6346        const r = mapToArray(byEventName, (tuples, eventName) => {
6347            const combined = tuples.length === 1 ? Result.value(tuples[0].handler) : fuse(tuples, eventOrder, eventName);
6348            return combined.map((handler) => {
6349                const assembled = assemble(handler);
6350                const purpose = tuples.length > 1 ? filter$2(eventOrder[eventName], (o) => exists(tuples, (t) => t.name === o)).join(' > ') : tuples[0].name;
6351                return wrap(eventName, uncurried(assembled, purpose));
6352            });
6353        });
6354        return consolidate(r, {});
6355    };
6356
6357    const baseBehaviour = 'alloy.base.behaviour';
6358    const schema$t = objOf([
6359        field$1('dom', 'dom', required$2(), objOf([
6360            // Note, no children.
6361            required$1('tag'),
6362            defaulted('styles', {}),
6363            defaulted('classes', []),
6364            defaulted('attributes', {}),
6365            option$3('value'),
6366            option$3('innerHtml')
6367        ])),
6368        required$1('components'),
6369        required$1('uid'),
6370        defaulted('events', {}),
6371        defaulted('apis', {}),
6372        // Use mergeWith in the future when pre-built behaviours conflict
6373        field$1('eventOrder', 'eventOrder', mergeWith({
6374            // Note, not using constant behaviour names to avoid code size of unused behaviours
6375            [execute$5()]: ['disabling', baseBehaviour, 'toggling', 'typeaheadevents'],
6376            [focus$3()]: [baseBehaviour, 'focusing', 'keying'],
6377            [systemInit()]: [baseBehaviour, 'disabling', 'toggling', 'representing', 'tooltipping'],
6378            [input()]: [baseBehaviour, 'representing', 'streaming', 'invalidating'],
6379            [detachedFromDom()]: [baseBehaviour, 'representing', 'item-events', 'toolbar-button-events', 'tooltipping'],
6380            [mousedown()]: ['focusing', baseBehaviour, 'item-type-events'],
6381            [touchstart()]: ['focusing', baseBehaviour, 'item-type-events'],
6382            [mouseover()]: ['item-type-events', 'tooltipping'],
6383            [receive()]: ['receiving', 'reflecting', 'tooltipping']
6384        }), anyValue()),
6385        option$3('domModification')
6386    ]);
6387    const toInfo = (spec) => asRaw('custom.definition', schema$t, spec);
6388    const toDefinition = (detail) => 
6389    // EFFICIENCY: Consider not merging here.
6390    ({
6391        ...detail.dom,
6392        uid: detail.uid,
6393        domChildren: map$2(detail.components, (comp) => comp.element)
6394    });
6395    const toModification = (detail) => detail.domModification.fold(() => nu$2({}), nu$2);
6396    const toEvents = (info) => info.events;
6397
6398    const diffKeyValueSet = (newObj, oldObj) => {
6399        const newKeys = keys(newObj);
6400        const oldKeys = keys(oldObj);
6401        const toRemove = difference(oldKeys, newKeys);
6402        const toSet = bifilter(newObj, (v, k) => {
6403            return !has$2(oldObj, k) || v !== oldObj[k];
6404        }).t;
6405        return { toRemove, toSet };
6406    };
6407    const reconcileToDom = (definition, obsoleted) => {
6408        const { class: clazz, style, ...existingAttributes } = clone$2(obsoleted);
6409        const { toSet: attrsToSet, toRemove: attrsToRemove } = diffKeyValueSet(definition.attributes, existingAttributes);
6410        const updateAttrs = () => {
6411            each$1(attrsToRemove, (a) => remove$8(obsoleted, a));
6412            setAll$1(obsoleted, attrsToSet);
6413        };
6414        const existingStyles = getAllRaw(obsoleted);
6415        const { toSet: stylesToSet, toRemove: stylesToRemove } = diffKeyValueSet(definition.styles, existingStyles);
6416        const updateStyles = () => {
6417            each$1(stylesToRemove, (s) => remove$6(obsoleted, s));
6418            setAll(obsoleted, stylesToSet);
6419        };
6420        const existingClasses = get$7(obsoleted);
6421        const classesToRemove = difference(existingClasses, definition.classes);
6422        const classesToAdd = difference(definition.classes, existingClasses);
6423        const updateClasses = () => {
6424            add$1(obsoleted, classesToAdd);
6425            remove$2(obsoleted, classesToRemove);
6426        };
6427        const updateHtml = (html) => {
6428            set$8(obsoleted, html);
6429        };
6430        const updateChildren = () => {
6431            const children = definition.domChildren;
6432            patchDomChildren(obsoleted, children);
6433        };
vendor: 52,439 bytes, lines 6434-7594
6434        const updateValue = () => {
6435            const valueElement = obsoleted;
6436            const value = definition.value.getOrUndefined();
6437            if (value !== get$5(valueElement)) {
6438                // TINY-8736: Value.set throws an error in case the value is undefined
6439                set$4(valueElement, value ?? '');
6440            }
6441        };
6442        updateAttrs();
6443        updateClasses();
6444        updateStyles();
6445        // Patching can only support one form of children, so we only update the html or the children, but never both
6446        definition.innerHtml.fold(updateChildren, updateHtml);
6447        updateValue();
6448        return obsoleted;
6449    };
6450
6451    const introduceToDom = (definition) => {
6452        const subject = SugarElement.fromTag(definition.tag);
6453        setAll$1(subject, definition.attributes);
6454        add$1(subject, definition.classes);
6455        setAll(subject, definition.styles);
6456        // Remember: Order of innerHtml vs children is important.
6457        definition.innerHtml.each((html) => set$8(subject, html));
6458        // Children are already elements.
6459        const children = definition.domChildren;
6460        append$1(subject, children);
6461        definition.value.each((value) => {
6462            set$4(subject, value);
6463        });
6464        return subject;
6465    };
6466    const attemptPatch = (definition, obsoleted) => {
6467        try {
6468            const e = reconcileToDom(definition, obsoleted);
6469            return Optional.some(e);
6470        }
6471        catch {
6472            return Optional.none();
6473        }
6474    };
6475    // If a component has both innerHtml and children then we can't patch it
6476    const hasMixedChildren = (definition) => definition.innerHtml.isSome() && definition.domChildren.length > 0;
6477    const renderToDom = (definition, optObsoleted) => {
6478        // If the current tag doesn't match, let's not try to add anything further down the tree.
6479        // If it does match though and we don't have mixed children then attempt to patch attributes etc...
6480        const canBePatched = (candidate) => name$3(candidate) === definition.tag && !hasMixedChildren(definition) && !isPremade(candidate);
6481        const elem = optObsoleted
6482            .filter(canBePatched)
6483            .bind((obsoleted) => attemptPatch(definition, obsoleted))
6484            .getOrThunk(() => introduceToDom(definition));
6485        writeOnly(elem, definition.uid);
6486        return elem;
6487    };
6488
6489    // This goes through the list of behaviours defined for a particular spec (removing anything
6490    // that has been revoked), and returns the BehaviourType (e.g. Sliding)
6491    const getBehaviours$2 = (spec) => {
6492        const behaviours = get$h(spec, 'behaviours').getOr({});
6493        return bind$3(keys(behaviours), (name) => {
6494            const behaviour = behaviours[name];
6495            return isNonNullable(behaviour) ? [behaviour.me] : [];
6496        });
6497    };
6498    const generateFrom = (spec, all) => generateFrom$1(spec, all);
6499    const generate$3 = (spec) => {
6500        const all = getBehaviours$2(spec);
6501        return generateFrom(spec, all);
6502    };
6503
6504    // This is probably far too complicated. I think DomModification is probably
6505    // questionable as a concept. Maybe it should be deprecated.
6506    const getDomDefinition = (info, bList, bData) => {
6507        // Get the current DOM definition from the spec
6508        const definition = toDefinition(info);
6509        // Get the current DOM modification definition from the spec
6510        const infoModification = toModification(info);
6511        // Treat the DOM modification like it came from a behaviour
6512        const baseModification = {
6513            'alloy.base.modification': infoModification
6514        };
6515        // Combine the modifications from any defined behaviours
6516        const modification = bList.length > 0 ? combine$1(bData, baseModification, bList, definition) : infoModification;
6517        // Transform the DOM definition with the combined dom modifications to make a new DOM definition
6518        return merge(definition, modification);
6519    };
6520    const getEvents = (info, bList, bData) => {
6521        const baseEvents = {
6522            'alloy.base.behaviour': toEvents(info)
6523        };
6524        return combine(bData, info.eventOrder, bList, baseEvents).getOrDie();
6525    };
6526    const build$2 = (spec, obsoleted) => {
6527        const getMe = () => me;
6528        const systemApi = Cell(singleton);
6529        const info = getOrDie(toInfo(spec));
6530        const bBlob = generate$3(spec);
6531        const bList = getBehaviours$3(bBlob);
6532        const bData = getData$3(bBlob);
6533        const modDefinition = getDomDefinition(info, bList, bData);
6534        const item = renderToDom(modDefinition, obsoleted);
6535        const events = getEvents(info, bList, bData);
6536        const subcomponents = Cell(info.components);
6537        const connect = (newApi) => {
6538            systemApi.set(newApi);
6539        };
6540        const disconnect = () => {
6541            systemApi.set(NoContextApi(getMe));
6542        };
6543        const syncComponents = () => {
6544            // Update the component list with the current children
6545            const children$1 = children(item);
6546            // INVESTIGATE: Not sure about how to handle text nodes here.
6547            const subs = bind$3(children$1, (child) => systemApi.get().getByDom(child).fold(() => [], pure$2));
6548            subcomponents.set(subs);
6549        };
6550        // TYPIFY (any here is for the info.apis() pathway)
6551        const config = (behaviour) => {
6552            const b = bData;
6553            const f = isFunction(b[behaviour.name()]) ? b[behaviour.name()] : () => {
6554                throw new Error('Could not find ' + behaviour.name() + ' in ' + JSON.stringify(spec, null, 2));
6555            };
6556            return f();
6557        };
6558        const hasConfigured = (behaviour) => isFunction(bData[behaviour.name()]);
6559        const getApis = () => info.apis;
6560        const readState = (behaviourName) => bData[behaviourName]().map((b) => b.state.readState()).getOr('not enabled');
6561        const me = {
6562            uid: spec.uid,
6563            getSystem: systemApi.get,
6564            config,
6565            hasConfigured,
6566            spec,
6567            readState,
6568            getApis,
6569            connect,
6570            disconnect,
6571            element: item,
6572            syncComponents,
6573            components: subcomponents.get,
6574            events
6575        };
6576        return me;
6577    };
6578
6579    const buildSubcomponents = (spec, obsoleted) => {
6580        const components = get$h(spec, 'components').getOr([]);
6581        return obsoleted.fold(() => map$2(components, build$1), (obs) => map$2(components, (c, i) => {
6582            return buildOrPatch(c, child$2(obs, i));
6583        }));
6584    };
6585    const buildFromSpec = (userSpec, obsoleted) => {
6586        const { events: specEvents, ...spec } = make$7(userSpec);
6587        // Build the subcomponents. A spec hierarchy is built from the bottom up.
6588        // obsoleted is used to define which element we are attempting to replace
6589        // so that it might be used to patch the DOM instead of recreate it.
6590        const components = buildSubcomponents(spec, obsoleted);
6591        const completeSpec = {
6592            ...spec,
6593            events: { ...DefaultEvents, ...specEvents },
6594            components
6595        };
6596        return Result.value(
6597        // Note, this isn't a spec any more, because it has built children
6598        build$2(completeSpec, obsoleted));
6599    };
6600    const text$2 = (textContent) => {
6601        const element = SugarElement.fromText(textContent);
6602        return external({
6603            element
6604        });
6605    };
6606    const external = (spec) => {
6607        const extSpec = asRawOrDie$1('external.component', objOfOnly([
6608            required$1('element'),
6609            option$3('uid')
6610        ]), spec);
6611        const systemApi = Cell(NoContextApi());
6612        const connect = (newApi) => {
6613            systemApi.set(newApi);
6614        };
6615        const disconnect = () => {
6616            systemApi.set(NoContextApi(() => me));
6617        };
6618        const uid = extSpec.uid.getOrThunk(() => generate$4('external'));
6619        writeOnly(extSpec.element, uid);
6620        const me = {
6621            uid,
6622            getSystem: systemApi.get,
6623            config: Optional.none,
6624            hasConfigured: never,
6625            connect,
6626            disconnect,
6627            getApis: () => ({}),
6628            element: extSpec.element,
6629            spec,
6630            readState: constant$1('No state'),
6631            syncComponents: noop,
6632            components: constant$1([]),
6633            events: {}
6634        };
6635        return premade$1(me);
6636    };
6637    // We experimented with just having a counter for efficiency, but that fails for situations
6638    // where an external JS file is using alloy, and is contained within another
6639    // alloy root container. The ids can conflict, because the counters do not
6640    // know about each other (being parts of separate scripts).
6641    //
6642    // There are other solutions than this ... not sure if they are going to have better performance, though
6643    const uids = generate$4;
6644    const isSketchSpec$1 = (spec) => has$2(spec, 'uid');
6645    // INVESTIGATE: A better way to provide 'meta-specs'
6646    const buildOrPatch = (spec, obsoleted) => getPremade(spec).getOrThunk(() => {
6647        // EFFICIENCY: Consider not merging here, and passing uid through separately
6648        const userSpecWithUid = isSketchSpec$1(spec) ? spec : {
6649            uid: uids(''),
6650            ...spec
6651        };
6652        return buildFromSpec(userSpecWithUid, obsoleted).getOrDie();
6653    });
6654    const build$1 = (spec) => buildOrPatch(spec, Optional.none());
6655    const premade = premade$1;
6656
6657    // Mark this component as busy, or blocked.
6658    const block = (component, config, state, 
6659    // This works in conjunction with the 'getRoot' function in the config. To
6660    // attach a blocker component to the dom, ensure that 'getRoot' returns a
6661    // component, and this function returns the specification of the component to
6662    // attach.
6663    getBusySpec) => {
6664        set$9(component.element, 'aria-busy', true);
6665        const root = config.getRoot(component).getOr(component);
6666        const blockerBehaviours = derive$1([
6667            // Trap the "Tab" key and don't let it escape.
6668            Keying.config({
6669                mode: 'special',
6670                onTab: () => Optional.some(true),
6671                onShiftTab: () => Optional.some(true)
6672            }),
6673            Focusing.config({})
6674        ]);
6675        const blockSpec = getBusySpec(root, blockerBehaviours);
6676        const blocker = root.getSystem().build(blockSpec);
6677        Replacing.append(root, premade(blocker));
6678        if (blocker.hasConfigured(Keying) && config.focus) {
6679            Keying.focusIn(blocker);
6680        }
6681        if (!state.isBlocked()) {
6682            config.onBlock(component);
6683        }
6684        state.blockWith(() => Replacing.remove(root, blocker));
6685    };
6686    // Mark this component as unblocked, or not busy. This is a noop on a component
6687    // that isn't blocked.
6688    const unblock = (component, config, state) => {
6689        remove$8(component.element, 'aria-busy');
6690        if (state.isBlocked()) {
6691            config.onUnblock(component);
6692        }
6693        state.clear();
6694    };
6695    const isBlocked = (component, blockingConfig, blockingState) => blockingState.isBlocked();
6696
6697    var BlockingApis = /*#__PURE__*/Object.freeze({
6698        __proto__: null,
6699        block: block,
6700        unblock: unblock,
6701        isBlocked: isBlocked
6702    });
6703
6704    var BlockingSchema = [
6705        // The blocking behaviour places a blocking element over the DOM while the
6706        // component is in the blocked state. If a function is provided here that
6707        // returns Some, then the blocking element will be added as a child of the
6708        // element returned. Otherwise, it will be added as a child of the main
6709        // component.
6710        defaultedFunction('getRoot', Optional.none),
6711        // This boolean, if provided, will specify whether the blocking element is
6712        // focused when the component is first blocked
6713        defaultedBoolean('focus', true),
6714        // This function, if provided, will be called any time the component is
6715        // blocked (unless it was already blocked).
6716        onHandler('onBlock'),
6717        // This function, if provided, will be called any time the component is
6718        // unblocked (unless it was already unblocked).
6719        onHandler('onUnblock')
6720    ];
6721
6722    const init$f = () => {
6723        const blocker = destroyable();
6724        const blockWith = (destroy) => {
6725            blocker.set({ destroy });
6726        };
6727        return nu$4({
6728            readState: blocker.isSet,
6729            blockWith,
6730            clear: blocker.clear,
6731            isBlocked: blocker.isSet
6732        });
6733    };
6734
6735    var BlockingState = /*#__PURE__*/Object.freeze({
6736        __proto__: null,
6737        init: init$f
6738    });
6739
6740    // Mark a component as able to be "Blocked" or able to enter a busy state. See
6741    // BlockingSchema and BlockingApis for more details on how to configure this.
6742    const Blocking = create$3({
6743        fields: BlockingSchema,
6744        name: 'blocking',
6745        apis: BlockingApis,
6746        state: BlockingState
6747    });
6748
6749    const getCurrent = (component, composeConfig, _composeState) => composeConfig.find(component);
6750
6751    var ComposeApis = /*#__PURE__*/Object.freeze({
6752        __proto__: null,
6753        getCurrent: getCurrent
6754    });
6755
6756    const ComposeSchema = [
6757        required$1('find')
6758    ];
6759
6760    const Composing = create$3({
6761        fields: ComposeSchema,
6762        name: 'composing',
6763        apis: ComposeApis
6764    });
6765
6766    const getCoupled = (component, coupleConfig, coupleState, name) => coupleState.getOrCreate(component, coupleConfig, name);
6767    const getExistingCoupled = (component, coupleConfig, coupleState, name) => coupleState.getExisting(component, coupleConfig, name);
6768
6769    var CouplingApis = /*#__PURE__*/Object.freeze({
6770        __proto__: null,
6771        getCoupled: getCoupled,
6772        getExistingCoupled: getExistingCoupled
6773    });
6774
6775    var CouplingSchema = [
6776        requiredOf('others', setOf(Result.value, anyValue()))
6777    ];
6778
6779    // Unfortunately, the Coupling APIs currently throw errors when the coupled name
6780    // is not recognised. This is because if the wrong name is used, it is a
6781    // non-recoverable error, and the developer should be notified. However, there are
6782    // better ways to do this: (removing this API and only returning Optionals/Results)
6783    const init$e = () => {
6784        const coupled = {};
6785        const lookupCoupled = (coupleConfig, coupledName) => {
6786            const available = keys(coupleConfig.others);
6787            if (available.length === 0) {
6788                throw new Error('Cannot find any known coupled components');
6789            }
6790            else {
6791                return get$h(coupled, coupledName);
6792            }
6793        };
6794        const getOrCreate = (component, coupleConfig, name) => {
6795            return lookupCoupled(coupleConfig, name).getOrThunk(() => {
6796                // TODO: TINY-9014 Likely type error. coupleConfig.others[key] is
6797                // `() => ((comp: AlloyComponent) => AlloySpec)`,
6798                // but builder is being treated as a `(comp: AlloyComponent) => AlloySpec`
6799                const builder = get$h(coupleConfig.others, name).getOrDie('No information found for coupled component: ' + name);
6800                const spec = builder(component);
6801                const built = component.getSystem().build(spec);
6802                coupled[name] = built;
6803                return built;
6804            });
6805        };
6806        const getExisting = (component, coupleConfig, name) => {
6807            return lookupCoupled(coupleConfig, name).orThunk(() => {
6808                // Validate we recognise this coupled component's name.
6809                get$h(coupleConfig.others, name).getOrDie('No information found for coupled component: ' + name);
6810                // It's a valid name, so return None, because it hasn't been built yet.
6811                return Optional.none();
6812            });
6813        };
6814        const readState = constant$1({});
6815        return nu$4({
6816            readState,
6817            getExisting,
6818            getOrCreate
6819        });
6820    };
6821
6822    var CouplingState = /*#__PURE__*/Object.freeze({
6823        __proto__: null,
6824        init: init$e
6825    });
6826
6827    const Coupling = create$3({
6828        fields: CouplingSchema,
6829        name: 'coupling',
6830        apis: CouplingApis,
6831        state: CouplingState
6832    });
6833
6834    // Just use "disabled" attribute for these, not "aria-disabled"
6835    const nativeDisabled = [
6836        'input',
6837        'button',
6838        'textarea',
6839        'select'
6840    ];
6841    const onLoad$5 = (component, disableConfig, disableState) => {
6842        const f = disableConfig.disabled() ? disable : enable;
6843        f(component, disableConfig);
6844    };
6845    const hasNative = (component, config) => config.useNative === true && contains$2(nativeDisabled, name$3(component.element));
6846    const nativeIsDisabled = (component) => has$1(component.element, 'disabled');
6847    const nativeDisable = (component) => {
6848        set$9(component.element, 'disabled', 'disabled');
6849    };
6850    const nativeEnable = (component) => {
6851        remove$8(component.element, 'disabled');
6852    };
6853    const ariaIsDisabled = (component) => get$g(component.element, 'aria-disabled') === 'true';
6854    const ariaDisable = (component) => {
6855        set$9(component.element, 'aria-disabled', 'true');
6856    };
6857    const ariaEnable = (component) => {
6858        set$9(component.element, 'aria-disabled', 'false');
6859    };
6860    const disable = (component, disableConfig, _disableState) => {
6861        disableConfig.disableClass.each((disableClass) => {
6862            add$2(component.element, disableClass);
6863        });
6864        const f = hasNative(component, disableConfig) ? nativeDisable : ariaDisable;
6865        f(component);
6866        disableConfig.onDisabled(component);
6867    };
6868    const enable = (component, disableConfig, _disableState) => {
6869        disableConfig.disableClass.each((disableClass) => {
6870            remove$3(component.element, disableClass);
6871        });
6872        const f = hasNative(component, disableConfig) ? nativeEnable : ariaEnable;
6873        f(component);
6874        disableConfig.onEnabled(component);
6875    };
6876    const isDisabled$1 = (component, disableConfig) => hasNative(component, disableConfig) ? nativeIsDisabled(component) : ariaIsDisabled(component);
6877    const set$2 = (component, disableConfig, disableState, disabled) => {
6878        const f = disabled ? disable : enable;
6879        f(component, disableConfig);
6880    };
6881
6882    var DisableApis = /*#__PURE__*/Object.freeze({
6883        __proto__: null,
6884        enable: enable,
6885        disable: disable,
6886        isDisabled: isDisabled$1,
6887        onLoad: onLoad$5,
6888        set: set$2
6889    });
6890
6891    const exhibit$5 = (base, disableConfig) => nu$2({
6892        // Do not add the attribute yet, because it will depend on the node name
6893        // if we use "aria-disabled" or just "disabled"
6894        classes: disableConfig.disabled() ? disableConfig.disableClass.toArray() : []
6895    });
6896    const events$g = (disableConfig, disableState) => derive$2([
6897        abort(execute$5(), (component, _simulatedEvent) => isDisabled$1(component, disableConfig)),
6898        loadEvent(disableConfig, disableState, onLoad$5)
6899    ]);
6900
6901    var ActiveDisable = /*#__PURE__*/Object.freeze({
6902        __proto__: null,
6903        exhibit: exhibit$5,
6904        events: events$g
6905    });
6906
6907    var DisableSchema = [
6908        defaultedFunction('disabled', never),
6909        defaulted('useNative', true),
6910        option$3('disableClass'),
6911        onHandler('onDisabled'),
6912        onHandler('onEnabled')
6913    ];
6914
6915    const Disabling = create$3({
6916        fields: DisableSchema,
6917        name: 'disabling',
6918        active: ActiveDisable,
6919        apis: DisableApis
6920    });
6921
6922    const NuPositionCss = (position, left, top, right, bottom) => {
6923        const toPx = (num) => num + 'px';
6924        return {
6925            position,
6926            left: left.map(toPx),
6927            top: top.map(toPx),
6928            right: right.map(toPx),
6929            bottom: bottom.map(toPx)
6930        };
6931    };
6932    const toOptions = (position) => ({
6933        ...position,
6934        position: Optional.some(position.position)
6935    });
6936    const applyPositionCss = (element, position) => {
6937        setOptions(element, toOptions(position));
6938    };
6939
6940    const appear = (component, contextualInfo) => {
6941        const elem = component.element;
6942        add$2(elem, contextualInfo.transitionClass);
6943        remove$3(elem, contextualInfo.fadeOutClass);
6944        add$2(elem, contextualInfo.fadeInClass);
6945        contextualInfo.onShow(component);
6946    };
6947    const disappear = (component, contextualInfo) => {
6948        const elem = component.element;
6949        add$2(elem, contextualInfo.transitionClass);
6950        remove$3(elem, contextualInfo.fadeInClass);
6951        add$2(elem, contextualInfo.fadeOutClass);
6952        contextualInfo.onHide(component);
6953    };
6954    const isPartiallyVisible = (box, bounds) => box.y < bounds.bottom && box.bottom > bounds.y;
6955    const isTopCompletelyVisible = (box, bounds) => box.y >= bounds.y;
6956    const isBottomCompletelyVisible = (box, bounds) => box.bottom <= bounds.bottom;
6957    const forceTopPosition = (winBox, leftX, viewport) => ({
6958        location: 'top',
6959        leftX,
6960        topY: viewport.bounds.y - winBox.y
6961    });
6962    const forceBottomPosition = (winBox, leftX, viewport) => ({
6963        location: 'bottom',
6964        leftX,
6965        bottomY: winBox.bottom - viewport.bounds.bottom
6966    });
6967    const getDockedLeftPosition = (bounds) => {
6968        // Essentially, we are just getting the bounding client rect left here,
6969        // because winBox.x will be the scroll value.
6970        return bounds.box.x - bounds.win.x;
6971    };
6972    const tryDockingPosition = (modes, bounds, viewport) => {
6973        const winBox = bounds.win;
6974        const box = bounds.box;
6975        const leftX = getDockedLeftPosition(bounds);
6976        return findMap(modes, (mode) => {
6977            switch (mode) {
6978                case 'bottom':
6979                    return !isBottomCompletelyVisible(box, viewport.bounds) ? Optional.some(forceBottomPosition(winBox, leftX, viewport)) : Optional.none();
6980                case 'top':
6981                    return !isTopCompletelyVisible(box, viewport.bounds) ? Optional.some(forceTopPosition(winBox, leftX, viewport)) : Optional.none();
6982                default:
6983                    return Optional.none();
6984            }
6985        }).getOr({
6986            location: 'no-dock'
6987        });
6988    };
6989    const isVisibleForModes = (modes, box, viewport) => forall(modes, (mode) => {
6990        switch (mode) {
6991            case 'bottom':
6992                return isBottomCompletelyVisible(box, viewport.bounds);
6993            case 'top':
6994                return isTopCompletelyVisible(box, viewport.bounds);
6995        }
6996    });
6997    const getXYForRestoring = (pos, viewport) => {
6998        const priorY = viewport.optScrollEnv.fold(constant$1(pos.bounds.y), (scrollEnv) => scrollEnv.scrollElmTop + (pos.bounds.y - scrollEnv.currentScrollTop));
6999        return SugarPosition(pos.bounds.x, priorY);
7000    };
7001    const getXYForSaving = (box, viewport) => {
7002        const priorY = viewport.optScrollEnv.fold(constant$1(box.y), (scrollEnv) => box.y + scrollEnv.currentScrollTop - scrollEnv.scrollElmTop);
7003        return SugarPosition(box.x, priorY);
7004    };
7005    const getPrior = (elem, viewport, state) => state.getInitialPos().map((pos) => {
7006        const xy = getXYForRestoring(pos, viewport);
7007        return {
7008            box: bounds(xy.left, xy.top, get$c(elem), get$d(elem)),
7009            location: pos.location
7010        };
7011    });
7012    const storePrior = (elem, box, viewport, state, decision) => {
7013        const xy = getXYForSaving(box, viewport);
7014        const bounds$1 = bounds(xy.left, xy.top, box.width, box.height);
7015        state.setInitialPos({
7016            style: getAllRaw(elem),
7017            position: get$e(elem, 'position') || 'static',
7018            bounds: bounds$1,
7019            location: decision.location
7020        });
7021    };
7022    // When we are using APIs like forceDockToTop, then we only want to store the previous position
7023    // if we weren't already docked. Otherwise, we still want to move the component, but keep its old
7024    // restore values
7025    const storePriorIfNone = (elem, box, viewport, state, decision) => {
7026        state.getInitialPos().fold(() => storePrior(elem, box, viewport, state, decision), () => noop);
7027    };
7028    const revertToOriginal = (elem, box, state) => state.getInitialPos().bind((position) => {
7029        state.clearInitialPos();
7030        switch (position.position) {
7031            case 'static':
7032                return Optional.some({
7033                    morph: 'static'
7034                });
7035            case 'absolute':
7036                const offsetParent = getOffsetParent(elem).getOr(body());
7037                const offsetBox = box$1(offsetParent);
7038                // Adding the scrollDelta here may not be the right solution. The basic problem is that the
7039                // rest of the code isn't considering whether its absolute or not, and where the offset parent
7040                // is. In the situation where the offset parent is *inside* the scrolling environment, then
7041                // we don't need to consider the scroll, and that's what getXYForRestoring does ... it removes
7042                // the scroll. We don't need to consider the scroll because the sink is already affected by the
7043                // scroll. However, when the sink IS the scroller, its position is not moved by scrolling. But the
7044                // positions of everything inside it needs to consider the scroll. So we add the scroll value.
7045                //
7046                // This might also be a bit naive. It's possible that we need to check that the offsetParent
7047                // is THE scroller, not just that it has a scroll value. For example, if the offset parent
7048                // was the body, and the body had a scroll, this might give unexpected results. That's somewhat
7049                // countered by the fact that if the offset parent is outside the scroller, then you don't really
7050                // have a scrolling environment any more, because the offset parent isn't going to be impacted
7051                // at all by the scroller
7052                const scrollDelta = offsetParent.dom.scrollTop ?? 0;
7053                return Optional.some({
7054                    morph: 'absolute',
7055                    positionCss: NuPositionCss('absolute', get$h(position.style, 'left').map((_left) => box.x - offsetBox.x), get$h(position.style, 'top').map((_top) => box.y - offsetBox.y + scrollDelta), get$h(position.style, 'right').map((_right) => offsetBox.right - box.right), get$h(position.style, 'bottom').map((_bottom) => offsetBox.bottom - box.bottom))
7056                });
7057            default:
7058                return Optional.none();
7059        }
7060    });
7061    const tryMorphToOriginal = (elem, viewport, state) => getPrior(elem, viewport, state)
7062        .filter(({ box }) => isVisibleForModes(state.getModes(), box, viewport))
7063        .bind(({ box }) => revertToOriginal(elem, box, state));
7064    const tryDecisionToFixedMorph = (decision) => {
7065        switch (decision.location) {
7066            case 'top': {
7067                // We store our current position so we can revert to it once it's
7068                // visible again.
7069                return Optional.some({
7070                    morph: 'fixed',
7071                    positionCss: NuPositionCss('fixed', Optional.some(decision.leftX), Optional.some(decision.topY), Optional.none(), Optional.none())
7072                });
7073            }
7074            case 'bottom': {
7075                // We store our current position so we can revert to it once it's
7076                // visible again.
7077                return Optional.some({
7078                    morph: 'fixed',
7079                    positionCss: NuPositionCss('fixed', Optional.some(decision.leftX), Optional.none(), Optional.none(), Optional.some(decision.bottomY))
7080                });
7081            }
7082            default:
7083                return Optional.none();
7084        }
7085    };
7086    const tryMorphToFixed = (elem, viewport, state) => {
7087        const box = box$1(elem);
7088        const winBox = win();
7089        const decision = tryDockingPosition(state.getModes(), {
7090            win: winBox,
7091            box
7092        }, viewport);
7093        if (decision.location === 'top' || decision.location === 'bottom') {
7094            // We are moving from undocked to docked, so store the previous location
7095            // so that we can restore it when we switch out of docking (back to undocked)
7096            storePrior(elem, box, viewport, state, decision);
7097            return tryDecisionToFixedMorph(decision);
7098        }
7099        else {
7100            return Optional.none();
7101        }
7102    };
7103    const tryMorphToOriginalOrUpdateFixed = (elem, viewport, state) => {
7104        // When a "docked" element is docked to the top of a scroll container (due to optScrollEnv in
7105        // viewport), we need to reposition its fixed if the scroll container has itself moved its top position.
7106        // This isn't required when the docking is to the top of the window, because the entire window cannot
7107        // be scrolled up and down the page - it is the page.
7108        //
7109        // Imagine a situation where the toolbar has docked to the top of the scroll container, which is at
7110        // y = 200. Now, when the user scrolls the page another 50px down the page, the top of the scroll
7111        // container will now be 150px, but the "fixed" toolbar will still be at "200px". So this is a morph
7112        // from "fixed" to "fixed", but with new coordinates. So if we can't morph to original from "fixed",
7113        // we try to update our "fixed" position (if we have a scrolling environment in the viewport)
7114        return tryMorphToOriginal(elem, viewport, state)
7115            .orThunk(() => {
7116            // Importantly, we don't update our stored position for the element before "docking", because
7117            // this is a transition between "docked" and "docked", not "undocked" and "docked". We want to
7118            // keep our undocked position in our store, not a docked position.
7119            // So we don't change our stored position. We just improve our fixed.
7120            return viewport.optScrollEnv
7121                .bind((_) => getPrior(elem, viewport, state))
7122                .bind(({ box, location }) => {
7123                const winBox = win();
7124                const leftX = getDockedLeftPosition({ win: winBox, box });
7125                // Keep the same docking location
7126                const decision = location === 'top'
7127                    ? forceTopPosition(winBox, leftX, viewport)
7128                    : forceBottomPosition(winBox, leftX, viewport);
7129                return tryDecisionToFixedMorph(decision);
7130            });
7131        });
7132    };
7133    const tryMorph = (component, viewport, state) => {
7134        const elem = component.element;
7135        const isDocked = is$1(getRaw(elem, 'position'), 'fixed');
7136        return isDocked
7137            ? tryMorphToOriginalOrUpdateFixed(elem, viewport, state)
7138            : tryMorphToFixed(elem, viewport, state);
7139    };
7140    // The difference between the "calculate" functions and the "try" functions is that the "try" functions
7141    // will first consider whether there is a need to morph, whereas the "calculate" functions will just
7142    // give you the morph details, bypassing the check to see if it's needed
7143    const calculateMorphToOriginal = (component, viewport, state) => {
7144        const elem = component.element;
7145        return getPrior(elem, viewport, state)
7146            .bind(({ box }) => revertToOriginal(elem, box, state));
7147    };
7148    const forceDockWith = (elem, viewport, state, getDecision) => {
7149        const box = box$1(elem);
7150        const winBox = win();
7151        const leftX = getDockedLeftPosition({ win: winBox, box });
7152        const decision = getDecision(winBox, leftX, viewport);
7153        if (decision.location === 'bottom' || decision.location === 'top') {
7154            // We only want to store the values if we aren't already docking. If we are already docking, then
7155            // we just want to move the element, without updating where it started originally
7156            storePriorIfNone(elem, box, viewport, state, decision);
7157            return tryDecisionToFixedMorph(decision);
7158        }
7159        else {
7160            return Optional.none();
7161        }
7162    };
7163
7164    const morphToStatic = (component, config, state) => {
7165        state.setDocked(false);
7166        each$1(['left', 'right', 'top', 'bottom', 'position'], (prop) => remove$6(component.element, prop));
7167        config.onUndocked(component);
7168    };
7169    const morphToCoord = (component, config, state, position) => {
7170        const isDocked = position.position === 'fixed';
7171        state.setDocked(isDocked);
7172        applyPositionCss(component.element, position);
7173        const method = isDocked ? config.onDocked : config.onUndocked;
7174        method(component);
7175    };
7176    const updateVisibility = (component, config, state, viewport, morphToDocked = false) => {
7177        config.contextual.each((contextInfo) => {
7178            // Make the dockable component disappear if the context is outside the viewport
7179            contextInfo.lazyContext(component).each((box) => {
7180                const isVisible = isPartiallyVisible(box, viewport.bounds);
7181                if (isVisible !== state.isVisible()) {
7182                    state.setVisible(isVisible);
7183                    // If morphing to docked and the context isn't visible then immediately set
7184                    // the fadeout class and don't worry about transitioning, as the context
7185                    // would never have been in view while docked
7186                    if (morphToDocked && !isVisible) {
7187                        add$1(component.element, [contextInfo.fadeOutClass]);
7188                        contextInfo.onHide(component);
7189                    }
7190                    else {
7191                        const method = isVisible ? appear : disappear;
7192                        method(component, contextInfo);
7193                    }
7194                }
7195            });
7196        });
7197    };
7198    const applyFixedMorph = (component, config, state, viewport, morph) => {
7199        // This "updateVisibility" call is potentially duplicated with the
7200        // call in refreshInternal for isDocked. We might want to consolidate them.
7201        // The difference between them is the "morphToDocked" flag.
7202        updateVisibility(component, config, state, viewport, true);
7203        morphToCoord(component, config, state, morph.positionCss);
7204    };
7205    const applyMorph = (component, config, state, viewport, morph) => {
7206        // Apply the morph result depending on its type
7207        switch (morph.morph) {
7208            case 'static': {
7209                return morphToStatic(component, config, state);
7210            }
7211            case 'absolute': {
7212                return morphToCoord(component, config, state, morph.positionCss);
7213            }
7214            case 'fixed': {
7215                return applyFixedMorph(component, config, state, viewport, morph);
7216            }
7217        }
7218    };
7219    const refreshInternal = (component, config, state) => {
7220        // Absolute coordinates (considers scroll)
7221        const viewport = config.lazyViewport(component);
7222        updateVisibility(component, config, state, viewport);
7223        tryMorph(component, viewport, state).each((morph) => {
7224            applyMorph(component, config, state, viewport, morph);
7225        });
7226    };
7227    const resetInternal = (component, config, state) => {
7228        // Morph back to the original position
7229        const elem = component.element;
7230        state.setDocked(false);
7231        const viewport = config.lazyViewport(component);
7232        calculateMorphToOriginal(component, viewport, state).each((staticOrAbsoluteMorph) => {
7233            // This code is very similar to the "applyMorph" function above. The main difference
7234            // is that it doesn't consider fixed position, because something that is docking
7235            // can't currently start with fixed position
7236            switch (staticOrAbsoluteMorph.morph) {
7237                case 'static': {
7238                    morphToStatic(component, config, state);
7239                    break;
7240                }
7241                case 'absolute': {
7242                    morphToCoord(component, config, state, staticOrAbsoluteMorph.positionCss);
7243                    break;
7244                }
7245            }
7246        });
7247        // Remove contextual visibility classes
7248        state.setVisible(true);
7249        config.contextual.each((contextInfo) => {
7250            remove$2(elem, [contextInfo.fadeInClass, contextInfo.fadeOutClass, contextInfo.transitionClass]);
7251            contextInfo.onShow(component);
7252        });
7253        // Apply docking again to reset the position
7254        refresh$4(component, config, state);
7255    };
7256    const refresh$4 = (component, config, state) => {
7257        // Ensure the component is attached to the document/world, if not then do nothing as we can't
7258        // check if the component should be docked or not when in a detached state
7259        if (component.getSystem().isConnected()) {
7260            refreshInternal(component, config, state);
7261        }
7262    };
7263    const reset$1 = (component, config, state) => {
7264        // If the component is not docked then there's no need to reset the state,
7265        // so only reset when docked
7266        if (state.isDocked()) {
7267            resetInternal(component, config, state);
7268        }
7269    };
7270    const forceDockWithDecision = (getDecision) => (component, config, state) => {
7271        const viewport = config.lazyViewport(component);
7272        const optMorph = forceDockWith(component.element, viewport, state, getDecision);
7273        optMorph.each((morph) => {
7274            // ASSUMPTION: This "applyFixedMorph" sets state.setDocked to true.
7275            applyFixedMorph(component, config, state, viewport, morph);
7276        });
7277    };
7278    const forceDockToTop = forceDockWithDecision(forceTopPosition);
7279    const forceDockToBottom = forceDockWithDecision(forceBottomPosition);
7280    const isDocked$2 = (component, config, state) => state.isDocked();
7281    const setModes = (component, config, state, modes) => state.setModes(modes);
7282    const getModes = (component, config, state) => state.getModes();
7283
7284    var DockingApis = /*#__PURE__*/Object.freeze({
7285        __proto__: null,
7286        refresh: refresh$4,
7287        reset: reset$1,
7288        isDocked: isDocked$2,
7289        getModes: getModes,
7290        setModes: setModes,
7291        forceDockToTop: forceDockToTop,
7292        forceDockToBottom: forceDockToBottom
7293    });
7294
7295    const events$f = (dockInfo, dockState) => derive$2([
7296        runOnSource(transitionend(), (component, simulatedEvent) => {
7297            dockInfo.contextual.each((contextInfo) => {
7298                if (has(component.element, contextInfo.transitionClass)) {
7299                    remove$2(component.element, [contextInfo.transitionClass, contextInfo.fadeInClass]);
7300                    const notify = dockState.isVisible() ? contextInfo.onShown : contextInfo.onHidden;
7301                    notify(component);
7302                }
7303                simulatedEvent.stop();
7304            });
7305        }),
7306        run$1(windowScroll(), (component, _) => {
7307            refresh$4(component, dockInfo, dockState);
7308        }),
7309        run$1(externalElementScroll(), (component, _) => {
7310            refresh$4(component, dockInfo, dockState);
7311        }),
7312        run$1(windowResize(), (component, _) => {
7313            reset$1(component, dockInfo, dockState);
7314        })
7315    ]);
7316
7317    var ActiveDocking = /*#__PURE__*/Object.freeze({
7318        __proto__: null,
7319        events: events$f
7320    });
7321
7322    var DockingSchema = [
7323        optionObjOf('contextual', [
7324            requiredString('fadeInClass'),
7325            requiredString('fadeOutClass'),
7326            requiredString('transitionClass'),
7327            requiredFunction('lazyContext'),
7328            onHandler('onShow'),
7329            onHandler('onShown'),
7330            onHandler('onHide'),
7331            onHandler('onHidden')
7332        ]),
7333        defaultedFunction('lazyViewport', () => ({
7334            bounds: win(),
7335            optScrollEnv: Optional.none()
7336        })),
7337        defaultedArrayOf('modes', ['top', 'bottom'], string),
7338        onHandler('onDocked'),
7339        onHandler('onUndocked')
7340    ];
7341
7342    const init$d = (spec) => {
7343        const docked = Cell(false);
7344        const visible = Cell(true);
7345        const initialBounds = value$2();
7346        const modes = Cell(spec.modes);
7347        const readState = () => `docked:  ${docked.get()}, visible: ${visible.get()}, modes: ${modes.get().join(',')}`;
7348        return nu$4({
7349            isDocked: docked.get,
7350            setDocked: docked.set,
7351            getInitialPos: initialBounds.get,
7352            setInitialPos: initialBounds.set,
7353            clearInitialPos: initialBounds.clear,
7354            isVisible: visible.get,
7355            setVisible: visible.set,
7356            getModes: modes.get,
7357            setModes: modes.set,
7358            readState
7359        });
7360    };
7361
7362    var DockingState = /*#__PURE__*/Object.freeze({
7363        __proto__: null,
7364        init: init$d
7365    });
7366
7367    const Docking = create$3({
7368        fields: DockingSchema,
7369        name: 'docking',
7370        active: ActiveDocking,
7371        apis: DockingApis,
7372        state: DockingState
7373    });
7374
7375    /*
7376     * origin: the position (without scroll) of the offset parent
7377     * scroll: the scrolling position of the window
7378     *
7379     * fixed: the fixed coordinates to show for css
7380     * offset: the absolute coordinates to show for css when inside an offset parent
7381     * absolute: the absolute coordinates to show before considering the offset parent
7382     */
7383    const adt$4 = Adt.generate([
7384        { offset: ['x', 'y'] },
7385        { absolute: ['x', 'y'] },
7386        { fixed: ['x', 'y'] }
7387    ]);
7388    const subtract = (change) => (point) => point.translate(-change.left, -change.top);
7389    const add = (change) => (point) => point.translate(change.left, change.top);
7390    const transform = (changes) => (x, y) => foldl(changes, (rest, f) => f(rest), SugarPosition(x, y));
7391    const asFixed = (coord, scroll, origin) => coord.fold(
7392    // offset to fixed
7393    transform([add(origin), subtract(scroll)]), 
7394    // absolute to fixed
7395    transform([subtract(scroll)]), 
7396    // fixed to fixed
7397    transform([]));
7398    const asAbsolute = (coord, scroll, origin) => coord.fold(
7399    // offset to absolute
7400    transform([add(origin)]), 
7401    // absolute to absolute
7402    transform([]), 
7403    // fixed to absolute
7404    transform([add(scroll)]));
7405    const asOffset = (coord, scroll, origin) => coord.fold(
7406    // offset to offset
7407    transform([]), 
7408    // absolute to offset
7409    transform([subtract(origin)]), 
7410    // fixed to offset
7411    transform([add(scroll), subtract(origin)]));
7412    const withinRange = (coord1, coord2, xRange, yRange, scroll, origin) => {
7413        const a1 = asAbsolute(coord1, scroll, origin);
7414        const a2 = asAbsolute(coord2, scroll, origin);
7415        // console.log(`a1.left: ${a1.left}, a2.left: ${a2.left}, leftDelta: ${a1.left - a2.left}, xRange: ${xRange}, lD <= xRange: ${Math.abs(a1.left - a2.left) <= xRange}`);
7416        // console.log(`a1.top: ${a1.top}, a2.top: ${a2.top}, topDelta: ${a1.top - a2.top}, yRange: ${yRange}, lD <= xRange: ${Math.abs(a1.top - a2.top) <= yRange}`);
7417        return Math.abs(a1.left - a2.left) <= xRange &&
7418            Math.abs(a1.top - a2.top) <= yRange;
7419    };
7420    const getDeltas = (coord1, coord2, xRange, yRange, scroll, origin) => {
7421        const a1 = asAbsolute(coord1, scroll, origin);
7422        const a2 = asAbsolute(coord2, scroll, origin);
7423        const left = Math.abs(a1.left - a2.left);
7424        const top = Math.abs(a1.top - a2.top);
7425        return SugarPosition(left, top);
7426    };
7427    const toStyles = (coord, scroll, origin) => {
7428        const stylesOpt = coord.fold((x, y) => ({ position: Optional.some('absolute'), left: Optional.some(x + 'px'), top: Optional.some(y + 'px') }), // offset
7429        (x, y) => ({ position: Optional.some('absolute'), left: Optional.some((x - origin.left) + 'px'), top: Optional.some((y - origin.top) + 'px') }), // absolute
7430        (x, y) => ({ position: Optional.some('fixed'), left: Optional.some(x + 'px'), top: Optional.some(y + 'px') }) // fixed
7431        );
7432        return { right: Optional.none(), bottom: Optional.none(), ...stylesOpt };
7433    };
7434    const translate$2 = (coord, deltaX, deltaY) => coord.fold((x, y) => offset(x + deltaX, y + deltaY), (x, y) => absolute$1(x + deltaX, y + deltaY), (x, y) => fixed$1(x + deltaX, y + deltaY));
7435    const absorb = (partialCoord, originalCoord, scroll, origin) => {
7436        const absorbOne = (stencil, nu) => (optX, optY) => {
7437            const original = stencil(originalCoord, scroll, origin);
7438            return nu(optX.getOr(original.left), optY.getOr(original.top));
7439        };
7440        return partialCoord.fold(absorbOne(asOffset, offset), absorbOne(asAbsolute, absolute$1), absorbOne(asFixed, fixed$1));
7441    };
7442    const offset = adt$4.offset;
7443    const absolute$1 = adt$4.absolute;
7444    const fixed$1 = adt$4.fixed;
7445
7446    const parseAttrToInt = (element, name) => {
7447        const value = get$g(element, name);
7448        return isUndefined(value) ? NaN : parseInt(value, 10);
7449    };
7450    // NOTE: Moved from ego with some parameterisation
7451    const get$3 = (component, snapsInfo) => {
7452        const element = component.element;
7453        const x = parseAttrToInt(element, snapsInfo.leftAttr);
7454        const y = parseAttrToInt(element, snapsInfo.topAttr);
7455        return isNaN(x) || isNaN(y) ? Optional.none() : Optional.some(SugarPosition(x, y));
7456    };
7457    const set$1 = (component, snapsInfo, pt) => {
7458        const element = component.element;
7459        set$9(element, snapsInfo.leftAttr, pt.left + 'px');
7460        set$9(element, snapsInfo.topAttr, pt.top + 'px');
7461    };
7462    const clear = (component, snapsInfo) => {
7463        const element = component.element;
7464        remove$8(element, snapsInfo.leftAttr);
7465        remove$8(element, snapsInfo.topAttr);
7466    };
7467
7468    // Types of coordinates
7469    // SugarLocation: This is the position on the screen including scroll.
7470    // Absolute: This is the css setting that would be applied. Therefore, it subtracts
7471    // the origin of the relative offsetParent.
7472    // Fixed: This is the fixed position.
7473    /*
7474     So in attempt to make this more understandable, let's use offset, absolute, and fixed.
7475     and try and model individual combinators
7476    */
7477    /*
7478
7479     Relationships:
7480       - location -> absolute: should just need to subtract the position of the offset parent (origin)
7481       - location -> fixed: subtract the scrolling
7482       - absolute -> fixed: add the origin, and subtract the scrolling
7483       - absolute -> location: add the origin
7484       - fixed -> absolute: add the scrolling, remove the origin
7485       - fixed -> location: add the scrolling
7486
7487    /*
7488     * When the user is dragging around the element, and it snaps into place, it is important
7489     * for the next movement to be from its pre-snapped location, rather than the snapped location.
7490     * This is because if it is from the snapped location the next delta movement may not actually
7491     * be high enough to get it out of the snap area, and hence, it will just snap again (and again).
7492     */
7493    // This identifies the position of the draggable element as either its current position, or the position
7494    // that we put on it before we snapped it into place (before dropping). Once it's dropped, the presnap
7495    // position will go away. It is used to avoid the situation where you can't escape the snap unless you
7496    // move the mouse really quickly :)
7497    const getCoords = (component, snapInfo, coord, delta) => get$3(component, snapInfo).fold(() => coord, (fixed) => 
7498    // We have a pre-snap position, so we have to apply the delta ourselves
7499    fixed$1(fixed.left + delta.left, fixed.top + delta.top));
7500    const moveOrSnap = (component, snapInfo, coord, delta, scroll, origin) => {
7501        const newCoord = getCoords(component, snapInfo, coord, delta);
7502        const snap = snapInfo.mustSnap ? findClosestSnap(component, snapInfo, newCoord, scroll, origin) :
7503            findSnap(component, snapInfo, newCoord, scroll, origin);
7504        const fixedCoord = asFixed(newCoord, scroll, origin);
7505        set$1(component, snapInfo, fixedCoord);
7506        return snap.fold(() => ({
7507            coord: fixed$1(fixedCoord.left, fixedCoord.top),
7508            extra: Optional.none()
7509        })
7510        // No snap.
7511        // var newfixed = graph.boundToFixed(theatre, element, loc.left, loc.top, fixed.left, fixed.top, height);
7512        // presnaps.set(element, 'fixed', newfixed.left, newfixed.top);
7513        // return { position: 'fixed', left: newfixed.left + 'px', top: newfixed.top + 'px' };
7514        , (spanned) => ({
7515            coord: spanned.output,
7516            extra: spanned.extra
7517        }));
7518    };
7519    const stopDrag = (component, snapInfo) => {
7520        clear(component, snapInfo);
7521    };
7522    const findMatchingSnap = (snaps, newCoord, scroll, origin) => findMap(snaps, (snap) => {
7523        const sensor = snap.sensor;
7524        const inRange = withinRange(newCoord, sensor, snap.range.left, snap.range.top, scroll, origin);
7525        return inRange ? Optional.some({
7526            output: absorb(snap.output, newCoord, scroll, origin),
7527            extra: snap.extra
7528        }) : Optional.none();
7529    });
7530    const findClosestSnap = (component, snapInfo, newCoord, scroll, origin) => {
7531        // You need to pass in the absX and absY so that they can be used for things which only care about snapping one axis and keeping the other one.
7532        const snaps = snapInfo.getSnapPoints(component);
7533        const matchSnap = findMatchingSnap(snaps, newCoord, scroll, origin);
7534        return matchSnap.orThunk(() => {
7535            const bestSnap = foldl(snaps, (acc, snap) => {
7536                const sensor = snap.sensor;
7537                const deltas = getDeltas(newCoord, sensor, snap.range.left, snap.range.top, scroll, origin);
7538                return acc.deltas.fold(() => ({
7539                    deltas: Optional.some(deltas),
7540                    snap: Optional.some(snap)
7541                }), (bestDeltas) => {
7542                    const currAvg = (deltas.left + deltas.top) / 2;
7543                    const bestAvg = (bestDeltas.left + bestDeltas.top) / 2;
7544                    if (currAvg <= bestAvg) {
7545                        return {
7546                            deltas: Optional.some(deltas),
7547                            snap: Optional.some(snap)
7548                        };
7549                    }
7550                    else {
7551                        return acc;
7552                    }
7553                });
7554            }, {
7555                deltas: Optional.none(),
7556                snap: Optional.none()
7557            });
7558            return bestSnap.snap.map((snap) => ({
7559                output: absorb(snap.output, newCoord, scroll, origin),
7560                extra: snap.extra
7561            }));
7562        });
7563    };
7564    // x: the absolute position.left of the draggable element
7565    // y: the absolute position.top of the draggable element
7566    // deltaX: the amount the mouse has moved horizontally
7567    // deltaY: the amount the mouse has moved vertically
7568    const findSnap = (component, snapInfo, newCoord, scroll, origin) => {
7569        // You need to pass in the absX and absY so that they can be used for things which only care about snapping one axis and keeping the other one.
7570        const snaps = snapInfo.getSnapPoints(component);
7571        // HERE
7572        return findMatchingSnap(snaps, newCoord, scroll, origin);
7573    };
7574    const snapTo$1 = (snap, scroll, origin) => ({
7575        // TODO: This looks to be incorrect and needs fixing as DragCoord definitely needs a number
7576        // based drag coord for the second argument here, so this is probably a bug.
7577        coord: absorb(snap.output, snap.output, scroll, origin),
7578        extra: snap.extra
7579    });
7580
7581    const snapTo = (component, dragConfig, _state, snap) => {
7582        const target = dragConfig.getTarget(component.element);
7583        if (dragConfig.repositionTarget) {
7584            const doc = owner$4(component.element);
7585            const scroll = get$b(doc);
7586            const origin = getOrigin(target);
7587            const snapPin = snapTo$1(snap, scroll, origin);
7588            const styles = toStyles(snapPin.coord, scroll, origin);
7589            setOptions(target, styles);
7590        }
7591    };
7592
7593    var DraggingApis = /*#__PURE__*/Object.freeze({
7594        __proto__: null,
7595        snapTo: snapTo
7596    });
7597
7598    const field = (name, forbidden) => defaultedObjOf(name, {}, map$2(forbidden, (f) => forbid(f.name(), 'Cannot configure ' + f.name() + ' for ' + name)).concat([
7599        customField('dump', identity)
7600    ]));
7601    const get$2 = (data) => data.dump;
7602    const augment = (data, original) => ({
7603        ...derive$1(original),
7604        ...data.dump
7605    });
7606    // Is this used?
7607    const SketchBehaviours = {
7608        field,
7609        augment,
7610        get: get$2
7611    };
7612
7613    const base = (partSchemas, partUidsSchemas) => {
7614        const ps = partSchemas.length > 0 ? [
7615            requiredObjOf('parts', partSchemas)
7616        ] : [];
7617        return ps.concat([
7618            required$1('uid'),
7619            defaulted('dom', {}), // Maybe get rid of.
7620            defaulted('components', []),
7621            snapshot('originalSpec'),
7622            defaulted('debug.sketcher', {})
7623        ]).concat(partUidsSchemas);
7624    };
7625    const asRawOrDie = (label, schema, spec, partSchemas, partUidsSchemas) => {
7626        const baseS = base(partSchemas, partUidsSchemas);
7627        return asRawOrDie$1(label + ' [SpecSchema]', objOfOnly(baseS.concat(schema)), spec);
7628    };
7629
7630    const single$1 = (owner, schema, factory, spec) => {
7631        const specWithUid = supplyUid(spec);
7632        const detail = asRawOrDie(owner, schema, specWithUid, [], []);
7633        return factory(detail, specWithUid);
7634    };
7635    const composite$1 = (owner, schema, partTypes, factory, spec) => {
7636        const specWithUid = supplyUid(spec);
7637        // Identify any information required for external parts
7638        const partSchemas = schemas(partTypes);
7639        // Generate partUids for all parts (external and otherwise)
7640        const partUidsSchema = defaultUidsSchema(partTypes);
7641        const detail = asRawOrDie(owner, schema, specWithUid, partSchemas, [partUidsSchema]);
7642        // Create (internals, externals) substitutions
7643        const subs = substitutes(owner, detail, partTypes);
7644        // Work out the components by substituting internals
7645        const components = components$1(owner, detail, subs.internals());
7646        return factory(detail, components, specWithUid, subs.externals());
7647    };
7648    const hasUid = (spec) => has$2(spec, 'uid');
7649    const supplyUid = (spec) => {
7650        return hasUid(spec) ? spec : {
7651            ...spec,
7652            uid: generate$4('uid')
7653        };
7654    };
7655
7656    const isSketchSpec = (spec) => {
7657        return spec.uid !== undefined;
7658    };
7659    const singleSchema = objOfOnly([
7660        required$1('name'),
7661        required$1('factory'),
7662        required$1('configFields'),
7663        defaulted('apis', {}),
7664        defaulted('extraApis', {})
7665    ]);
7666    const compositeSchema = objOfOnly([
7667        required$1('name'),
7668        required$1('factory'),
7669        required$1('configFields'),
7670        required$1('partFields'),
7671        defaulted('apis', {}),
7672        defaulted('extraApis', {})
7673    ]);
7674    const single = (rawConfig) => {
7675        const config = asRawOrDie$1('Sketcher for ' + rawConfig.name, singleSchema, rawConfig);
7676        const sketch = (spec) => single$1(config.name, config.configFields, config.factory, spec);
7677        const apis = map$1(config.apis, makeApi);
7678        const extraApis = map$1(config.extraApis, (f, k) => markAsExtraApi(f, k));
7679        return {
7680            name: config.name,
7681            configFields: config.configFields,
7682            sketch,
7683            ...apis,
7684            ...extraApis
7685        };
7686    };
7687    const composite = (rawConfig) => {
7688        const config = asRawOrDie$1('Sketcher for ' + rawConfig.name, compositeSchema, rawConfig);
7689        const sketch = (spec) => composite$1(config.name, config.configFields, config.partFields, config.factory, spec);
7690        // These are constructors that will store their configuration.
7691        const parts = generate$5(config.name, config.partFields);
7692        const apis = map$1(config.apis, makeApi);
7693        const extraApis = map$1(config.extraApis, (f, k) => markAsExtraApi(f, k));
7694        return {
7695            name: config.name,
7696            partFields: config.partFields,
7697            configFields: config.configFields,
7698            sketch,
7699            parts,
7700            ...apis,
7701            ...extraApis
7702        };
7703    };
7704
vendor: 16,384 bytes, lines 7705-8097
7705    const factory$n = (detail) => {
7706        const { attributes, ...domWithoutAttributes } = detail.dom;
7707        return {
7708            uid: detail.uid,
7709            dom: {
7710                tag: 'div',
7711                attributes: {
7712                    role: 'presentation',
7713                    ...attributes
7714                },
7715                ...domWithoutAttributes
7716            },
7717            components: detail.components,
7718            behaviours: get$2(detail.containerBehaviours),
7719            events: detail.events,
7720            domModification: detail.domModification,
7721            eventOrder: detail.eventOrder
7722        };
7723    };
7724    const Container = single({
7725        name: 'Container',
7726        factory: factory$n,
7727        configFields: [
7728            defaulted('components', []),
7729            field('containerBehaviours', []),
7730            // TODO: Deprecate
7731            defaulted('events', {}),
7732            defaulted('domModification', {}),
7733            defaulted('eventOrder', {})
7734        ]
7735    });
7736
7737    const initialAttribute = 'data-initial-z-index';
7738    // We have to alter the z index of the alloy root of the blocker so that
7739    // it can have a z-index high enough to act as the "blocker". Just before
7740    // discarding it, we need to reset those z-indices back to what they
7741    // were. ASSUMPTION: the blocker has been added as a direct child of the root
7742    const resetZIndex = (blocker) => {
7743        parent(blocker.element).filter(isElement$1).each((root) => {
7744            getOpt(root, initialAttribute).fold(() => remove$6(root, 'z-index'), (zIndex) => set$7(root, 'z-index', zIndex));
7745            remove$8(root, initialAttribute);
7746        });
7747    };
7748    const changeZIndex = (blocker) => {
7749        parent(blocker.element).filter(isElement$1).each((root) => {
7750            getRaw(root, 'z-index').each((zindex) => {
7751                set$9(root, initialAttribute, zindex);
7752            });
7753            // Used to be a really high number, but it probably just has
7754            // to match the blocker
7755            set$7(root, 'z-index', get$e(blocker.element, 'z-index'));
7756        });
7757    };
7758    const instigate = (anyComponent, blocker) => {
7759        anyComponent.getSystem().addToGui(blocker);
7760        changeZIndex(blocker);
7761    };
7762    const discard = (blocker) => {
7763        resetZIndex(blocker);
7764        blocker.getSystem().removeFromGui(blocker);
7765    };
7766    const createComponent = (component, blockerClass, blockerEvents) => component.getSystem().build(Container.sketch({
7767        dom: {
7768            // Probably consider doing with classes?
7769            styles: {
7770                'left': '0px',
7771                'top': '0px',
7772                'width': '100%',
7773                'height': '100%',
7774                'position': 'fixed',
7775                'z-index': '1000000000000000'
7776            },
7777            classes: [blockerClass]
7778        },
7779        events: blockerEvents
7780    }));
7781
7782    var SnapSchema = optionObjOf('snaps', [
7783        required$1('getSnapPoints'),
7784        onHandler('onSensor'),
7785        required$1('leftAttr'),
7786        required$1('topAttr'),
7787        defaulted('lazyViewport', win),
7788        defaulted('mustSnap', false)
7789    ]);
7790
7791    const schema$s = [
7792        // Is this used?
7793        defaulted('useFixed', never),
7794        defaulted('getTarget', identity),
7795        defaulted('onDragStart', noop),
7796        defaulted('onDrag', noop),
7797        defaulted('repositionTarget', true),
7798        defaulted('onDrop', noop),
7799        defaultedFunction('getBounds', win),
7800        SnapSchema
7801    ];
7802
7803    const getCurrentCoord = (target) => lift3(getRaw(target, 'left'), getRaw(target, 'top'), getRaw(target, 'position'), (left, top, position) => {
7804        const nu = position === 'fixed' ? fixed$1 : offset;
7805        return nu(parseInt(left, 10), parseInt(top, 10));
7806    }).getOrThunk(() => {
7807        const location = absolute$3(target);
7808        return absolute$1(location.left, location.top);
7809    });
7810    const clampCoords = (component, coords, scroll, origin, startData) => {
7811        const bounds = startData.bounds;
7812        const absoluteCoord = asAbsolute(coords, scroll, origin);
7813        const newX = clamp(absoluteCoord.left, bounds.x, bounds.x + bounds.width - startData.width);
7814        const newY = clamp(absoluteCoord.top, bounds.y, bounds.y + bounds.height - startData.height);
7815        const newCoords = absolute$1(newX, newY);
7816        // Translate the absolute coord back into the previous type
7817        return coords.fold(
7818        // offset
7819        () => {
7820            const offset$1 = asOffset(newCoords, scroll, origin);
7821            return offset(offset$1.left, offset$1.top);
7822        }, 
7823        // absolute
7824        constant$1(newCoords), 
7825        // fixed
7826        () => {
7827            const fixed = asFixed(newCoords, scroll, origin);
7828            return fixed$1(fixed.left, fixed.top);
7829        });
7830    };
7831    const calcNewCoord = (component, optSnaps, currentCoord, scroll, origin, delta, startData) => {
7832        const newCoord = optSnaps.fold(() => {
7833            // When not docking, use fixed coordinates.
7834            const translated = translate$2(currentCoord, delta.left, delta.top);
7835            const fixedCoord = asFixed(translated, scroll, origin);
7836            return fixed$1(fixedCoord.left, fixedCoord.top);
7837        }, (snapInfo) => {
7838            const snapping = moveOrSnap(component, snapInfo, currentCoord, delta, scroll, origin);
7839            snapping.extra.each((extra) => {
7840                snapInfo.onSensor(component, extra);
7841            });
7842            return snapping.coord;
7843        });
7844        // Clamp the coords so that they are within the bounds
7845        return clampCoords(component, newCoord, scroll, origin, startData);
7846    };
7847    const dragBy = (component, dragConfig, startData, delta) => {
7848        const target = dragConfig.getTarget(component.element);
7849        if (dragConfig.repositionTarget) {
7850            const doc = owner$4(component.element);
7851            const scroll = get$b(doc);
7852            const origin = getOrigin(target);
7853            const currentCoord = getCurrentCoord(target);
7854            const newCoord = calcNewCoord(component, dragConfig.snaps, currentCoord, scroll, origin, delta, startData);
7855            const styles = toStyles(newCoord, scroll, origin);
7856            setOptions(target, styles);
7857        }
7858        // NOTE: On drag just goes with the original delta. It does not know about snapping.
7859        dragConfig.onDrag(component, target, delta);
7860    };
7861
7862    const calcStartData = (dragConfig, comp) => ({
7863        bounds: dragConfig.getBounds(),
7864        height: getOuter$1(comp.element),
7865        width: getOuter(comp.element)
7866    });
7867    const move = (component, dragConfig, dragState, dragMode, event) => {
7868        const delta = dragState.update(dragMode, event);
7869        const dragStartData = dragState.getStartData().getOrThunk(() => calcStartData(dragConfig, component));
7870        delta.each((dlt) => {
7871            dragBy(component, dragConfig, dragStartData, dlt);
7872        });
7873    };
7874    const stop = (component, blocker, dragConfig, dragState) => {
7875        blocker.each(discard);
7876        dragConfig.snaps.each((snapInfo) => {
7877            stopDrag(component, snapInfo);
7878        });
7879        const target = dragConfig.getTarget(component.element);
7880        dragState.reset();
7881        dragConfig.onDrop(component, target);
7882    };
7883    const handlers = (events) => (dragConfig, dragState) => {
7884        const updateStartState = (comp) => {
7885            const isBeingDragged = dragState.getStartData().isSome();
7886            dragState.setStartData(calcStartData(dragConfig, comp));
7887            if (!isBeingDragged) {
7888                const target = dragConfig.getTarget(comp.element);
7889                dragConfig.onDragStart(comp, target);
7890            }
7891        };
7892        return derive$2([
7893            run$1(windowScroll(), (comp) => {
7894                // Only update if we have some start data
7895                dragState.getStartData().each(() => updateStartState(comp));
7896            }),
7897            ...events(dragConfig, dragState, updateStartState)
7898        ]);
7899    };
7900
7901    const init$c = (dragApi) => derive$2([
7902        // When the user clicks on the blocker, something has probably gone slightly
7903        // wrong, so we'll just drop for safety. The blocker should really only
7904        // be there when the mouse is already down and not released, so a 'click'
7905        run$1(mousedown(), dragApi.forceDrop),
7906        // When the user releases the mouse on the blocker, that is a drop
7907        run$1(mouseup(), dragApi.drop),
7908        // As the user moves the mouse around (while pressed down), we move the
7909        // component around
7910        run$1(mousemove(), (comp, simulatedEvent) => {
7911            dragApi.move(simulatedEvent.event);
7912        }),
7913        // When the use moves outside the range, schedule a block to occur but
7914        // give it a chance to be cancelled.
7915        run$1(mouseout(), dragApi.delayDrop)
7916    ]);
7917
7918    const getData$2 = (event) => Optional.from(SugarPosition(event.x, event.y));
7919    // When dragging with the mouse, the delta is simply the difference
7920    // between the two position (previous/old and next/nu)
7921    const getDelta$2 = (old, nu) => SugarPosition(nu.left - old.left, nu.top - old.top);
7922
7923    var MouseData = /*#__PURE__*/Object.freeze({
7924        __proto__: null,
7925        getData: getData$2,
7926        getDelta: getDelta$2
7927    });
7928
7929    const events$e = (dragConfig, dragState, updateStartState) => [
7930        run$1(mousedown(), (component, simulatedEvent) => {
7931            const raw = simulatedEvent.event.raw;
7932            if (raw.button !== 0) {
7933                return;
7934            }
7935            simulatedEvent.stop();
7936            const stop$1 = () => stop(component, Optional.some(blocker), dragConfig, dragState);
7937            // If the user has moved something outside the area, and has not come back within
7938            // 200 ms, then drop
7939            const delayDrop = DelayedFunction(stop$1, 200);
7940            const dragApi = {
7941                drop: stop$1,
7942                delayDrop: delayDrop.schedule,
7943                forceDrop: stop$1,
7944                move: (event) => {
7945                    // Stop any pending drops caused by mouseout
7946                    delayDrop.cancel();
7947                    move(component, dragConfig, dragState, MouseData, event);
7948                }
7949            };
7950            const blocker = createComponent(component, dragConfig.blockerClass, init$c(dragApi));
7951            const start = () => {
7952                updateStartState(component);
7953                instigate(component, blocker);
7954            };
7955            start();
7956        })
7957    ];
7958    const schema$r = [
7959        ...schema$s,
7960        required$1('blockerClass'),
7961        output$1('dragger', {
7962            handlers: handlers(events$e)
7963        })
7964    ];
7965
7966    const init$b = (dragApi) => derive$2([
7967        // When the user taps on the blocker, something has probably gone slightly
7968        // wrong, so we'll just drop for safety. The blocker should really only
7969        // be there when their finger is already down and not released, so a 'tap'
7970        run$1(touchstart(), dragApi.forceDrop),
7971        // When the user releases their finger on the blocker, that is a drop
7972        run$1(touchend(), dragApi.drop),
7973        run$1(touchcancel(), dragApi.drop),
7974        // As the user moves their finger around (while pressed down), we move the
7975        // component around
7976        run$1(touchmove(), (comp, simulatedEvent) => {
7977            dragApi.move(simulatedEvent.event);
7978        })
7979    ]);
7980
7981    const getDataFrom = (touches) => {
7982        const touch = touches[0];
7983        return Optional.some(SugarPosition(touch.clientX, touch.clientY));
7984    };
7985    const getData$1 = (event) => {
7986        const raw = event.raw;
7987        const touches = raw.touches;
7988        return touches.length === 1 ? getDataFrom(touches) : Optional.none();
7989    };
7990    // When dragging the touch, the delta is simply the difference
7991    // between the two touch positions (previous/old and next/nu)
7992    const getDelta$1 = (old, nu) => SugarPosition(nu.left - old.left, nu.top - old.top);
7993
7994    var TouchData = /*#__PURE__*/Object.freeze({
7995        __proto__: null,
7996        getData: getData$1,
7997        getDelta: getDelta$1
7998    });
7999
8000    const events$d = (dragConfig, dragState, updateStartState) => {
8001        const blockerSingleton = value$2();
8002        const stopBlocking = (component) => {
8003            stop(component, blockerSingleton.get(), dragConfig, dragState);
8004            blockerSingleton.clear();
8005        };
8006        // Android fires events on the component at all times, while iOS initially fires on the component
8007        // but once moved off the component then fires on the element behind. As such we need to use
8008        // a blocker and then listen to both touchmove/touchend on both the component and blocker.
8009        return [
8010            run$1(touchstart(), (component, simulatedEvent) => {
8011                simulatedEvent.stop();
8012                const stop = () => stopBlocking(component);
8013                const dragApi = {
8014                    drop: stop,
8015                    // delayDrop is not used by touch
8016                    delayDrop: noop,
8017                    forceDrop: stop,
8018                    move: (event) => {
8019                        move(component, dragConfig, dragState, TouchData, event);
8020                    }
8021                };
8022                const blocker = createComponent(component, dragConfig.blockerClass, init$b(dragApi));
8023                blockerSingleton.set(blocker);
8024                const start = () => {
8025                    updateStartState(component);
8026                    instigate(component, blocker);
8027                };
8028                start();
8029            }),
8030            run$1(touchmove(), (component, simulatedEvent) => {
8031                simulatedEvent.stop();
8032                move(component, dragConfig, dragState, TouchData, simulatedEvent.event);
8033            }),
8034            run$1(touchend(), (component, simulatedEvent) => {
8035                simulatedEvent.stop();
8036                stopBlocking(component);
8037            }),
8038            run$1(touchcancel(), stopBlocking)
8039        ];
8040    };
8041    const schema$q = [
8042        ...schema$s,
8043        required$1('blockerClass'),
8044        output$1('dragger', {
8045            handlers: handlers(events$d)
8046        })
8047    ];
8048
8049    const events$c = (dragConfig, dragState, updateStartState) => [
8050        ...events$e(dragConfig, dragState, updateStartState),
8051        ...events$d(dragConfig, dragState, updateStartState)
8052    ];
8053    const schema$p = [
8054        ...schema$s,
8055        required$1('blockerClass'),
8056        output$1('dragger', {
8057            handlers: handlers(events$c)
8058        })
8059    ];
8060
8061    const getData = (event) => Optional.from(SugarPosition(event.x, event.y));
8062    const getDelta = (old, nu) => SugarPosition(nu.left - old.left, nu.top - old.top);
8063
8064    var PointerData = /*#__PURE__*/Object.freeze({
8065        __proto__: null,
8066        getData: getData,
8067        getDelta: getDelta
8068    });
8069
8070    const isLeftClick = (e) => e.button === 0;
8071    const events$b = (dragConfig, dragState, updateStartState) => {
8072        return [
8073            run$1(pointerdown(), (component, simulatedEvent) => {
8074                const raw = simulatedEvent.event.raw;
8075                if (!isLeftClick(raw)) {
8076                    return;
8077                }
8078                if (dragState.getStartData().isNone()) {
8079                    simulatedEvent.stop();
8080                    component.element.dom.setPointerCapture(raw.pointerId);
8081                    dragState.setActivePointerId(raw.pointerId);
8082                    updateStartState(component);
8083                }
8084            }),
8085            run$1(pointermove(), (component, simulatedEvent) => {
8086                const pointerId = simulatedEvent.event.raw.pointerId;
8087                lift2(dragState.getStartData(), dragState.getActivePointerId(), (_startData, activePointerId) => {
8088                    if (pointerId === activePointerId) {
8089                        move(component, dragConfig, dragState, PointerData, simulatedEvent.event);
8090                    }
8091                });
8092            }),
8093            run$1(pointerup(), (component, simulatedEvent) => {
8094                const pointerId = simulatedEvent.event.raw.pointerId;
8095                lift2(dragState.getStartData(), dragState.getActivePointerId(), (_startData, activePointerId) => {
8096                    if (pointerId === activePointerId) {
8097                        componen
8097t.element.dom.releasePointerCapture(pointerId);
8098                        stop(component, Optional.none(), dragConfig, dragState);
8099                    }
8100                });
8101            }),
8102            // Safety net — if capture is lost unexpectedly.
8103            // It sometimes happens that pointer capture is lost without pointerup event being emitted.
8104            // I could observe that using touchpad in chrome
8105            run$1(lostpointercapture(), (component) => {
8106                dragState.getStartData().each(() => {
8107                    stop(component, Optional.none(), dragConfig, dragState);
8108                });
8109            })
8110        ];
8111    };
8112    const schema$o = [
8113        ...schema$s,
8114        output$1('dragger', {
8115            handlers: handlers(events$b)
8116        })
8117    ];
8118
8119    const mouse = schema$r;
8120    const touch = schema$q;
8121    const mouseOrTouch = schema$p;
8122    const pointer = schema$o;
8123
8124    var DraggingBranches = /*#__PURE__*/Object.freeze({
8125        __proto__: null,
8126        mouse: mouse,
8127        touch: touch,
8128        mouseOrTouch: mouseOrTouch,
8129        pointer: pointer
8130    });
8131
8132    // NOTE: mode refers to the way that information is retrieved from
8133    // the user interaction. It can be things like MouseData, TouchData etc.
8134    const init$a = () => {
8135        // Dragging operates on the difference between the previous user
8136        // interaction and the next user interaction. Therefore, we store
8137        // the previous interaction so that we can compare it.
8138        const previous = value$2();
8139        // Dragging requires calculating the bounds, so we store that data initially
8140        // to reduce the amount of computation each mouse movement
8141        const startData = value$2();
8142        // In a multitouch environment, `pointerId` is used to distinguish pointers
8143        // (e.g. different fingers on a touchscreen).
8144        // This property is only used by pointer-event branches.
8145        const activePointerId = value$2();
8146        const reset = () => {
8147            previous.clear();
8148            startData.clear();
8149            activePointerId.clear();
8150        };
8151        // Return position delta between previous position and nu position,
8152        // or None if this is the first. Set the previous position to nu.
8153        const calculateDelta = (mode, nu) => {
8154            const result = previous.get().map((old) => mode.getDelta(old, nu));
8155            previous.set(nu);
8156            return result;
8157        };
8158        // NOTE: This dragEvent is the DOM touch event or mouse event
8159        const update = (mode, dragEvent) => mode.getData(dragEvent).bind((nuData) => calculateDelta(mode, nuData));
8160        const setStartData = (data) => {
8161            startData.set(data);
8162        };
8163        const getStartData = () => startData.get();
8164        const setActivePointerId = (id) => {
8165            activePointerId.set(id);
8166        };
8167        const getActivePointerId = () => activePointerId.get();
8168        const readState = constant$1({});
8169        return nu$4({
8170            readState,
8171            reset,
8172            update,
8173            getStartData,
8174            setStartData,
8175            setActivePointerId,
8176            getActivePointerId
8177        });
8178    };
8179
8180    var DragState = /*#__PURE__*/Object.freeze({
8181        __proto__: null,
8182        init: init$a
8183    });
8184
8185    const Dragging = createModes({
8186        branchKey: 'mode',
8187        branches: DraggingBranches,
8188        name: 'dragging',
8189        active: {
8190            events: (dragConfig, dragState) => {
8191                const dragger = dragConfig.dragger;
8192                return dragger.handlers(dragConfig, dragState);
8193            }
8194        },
8195        extra: {
8196            // Extra. Does not need component as input.
8197            snap: (sConfig) => ({
8198                sensor: sConfig.sensor,
8199                range: sConfig.range,
8200                output: sConfig.output,
8201                extra: Optional.from(sConfig.extra)
8202            })
8203        },
8204        state: DragState,
8205        apis: DraggingApis
8206    });
8207
8208    const ariaElements = [
8209        'input',
8210        'textarea'
8211    ];
8212    const isAriaElement = (elem) => {
8213        const name = name$3(elem);
8214        return contains$2(ariaElements, name);
8215    };
8216    const markValid = (component, invalidConfig) => {
8217        const elem = invalidConfig.getRoot(component).getOr(component.element);
8218        remove$3(elem, invalidConfig.invalidClass);
8219        invalidConfig.notify.each((notifyInfo) => {
8220            if (isAriaElement(component.element)) {
8221                set$9(component.element, 'aria-invalid', false);
8222            }
8223            notifyInfo.getContainer(component).each((container) => {
8224                set$8(container, notifyInfo.validHtml);
8225            });
8226            notifyInfo.onValid(component);
8227        });
8228    };
8229    const markInvalid = (component, invalidConfig, invalidState, text) => {
8230        const elem = invalidConfig.getRoot(component).getOr(component.element);
8231        add$2(elem, invalidConfig.invalidClass);
8232        invalidConfig.notify.each((notifyInfo) => {
8233            if (isAriaElement(component.element)) {
8234                set$9(component.element, 'aria-invalid', true);
8235            }
8236            notifyInfo.getContainer(component).each((container) => {
8237                // TODO: Should we just use Text here, not HTML?
8238                set$8(container, text);
8239            });
8240            notifyInfo.onInvalid(component, text);
8241        });
8242    };
8243    const query = (component, invalidConfig, _invalidState) => invalidConfig.validator.fold(() => Future.pure(Result.value(true)), (validatorInfo) => validatorInfo.validate(component));
8244    const run = (component, invalidConfig, invalidState) => {
8245        invalidConfig.notify.each((notifyInfo) => {
8246            notifyInfo.onValidate(component);
8247        });
8248        return query(component, invalidConfig).map((valid) => {
8249            if (component.getSystem().isConnected()) {
8250                return valid.fold((err) => {
8251                    markInvalid(component, invalidConfig, invalidState, err);
8252                    return Result.error(err);
8253                }, (v) => {
8254                    markValid(component, invalidConfig);
8255                    return Result.value(v);
8256                });
8257            }
8258            else {
8259                return Result.error('No longer in system');
8260            }
8261        });
8262    };
8263    const isInvalid = (component, invalidConfig) => {
8264        const elem = invalidConfig.getRoot(component).getOr(component.element);
8265        return has(elem, invalidConfig.invalidClass);
8266    };
8267
8268    var InvalidateApis = /*#__PURE__*/Object.freeze({
8269        __proto__: null,
8270        markValid: markValid,
8271        markInvalid: markInvalid,
8272        query: query,
8273        run: run,
8274        isInvalid: isInvalid
8275    });
8276
8277    const events$a = (invalidConfig, invalidState) => invalidConfig.validator.map((validatorInfo) => derive$2([
8278        run$1(validatorInfo.onEvent, (component) => {
8279            run(component, invalidConfig, invalidState).get(identity);
8280        })
8281    ].concat(validatorInfo.validateOnLoad ? [
8282        runOnAttached((component) => {
8283            run(component, invalidConfig, invalidState).get(noop);
8284        })
8285    ] : []))).getOr({});
8286
8287    var ActiveInvalidate = /*#__PURE__*/Object.freeze({
8288        __proto__: null,
8289        events: events$a
8290    });
8291
8292    var InvalidateSchema = [
8293        required$1('invalidClass'),
vendor: 18,572 bytes, lines 8294-8749
8294        defaulted('getRoot', Optional.none),
8295        // TODO: Completely rework the notify API
8296        optionObjOf('notify', [
8297            defaulted('aria', 'alert'),
8298            // Maybe we should use something else.
8299            defaulted('getContainer', Optional.none),
8300            defaulted('validHtml', ''),
8301            onHandler('onValid'),
8302            onHandler('onInvalid'),
8303            onHandler('onValidate')
8304        ]),
8305        optionObjOf('validator', [
8306            required$1('validate'),
8307            defaulted('onEvent', 'input'),
8308            defaulted('validateOnLoad', true)
8309        ])
8310    ];
8311
8312    const onLoad$4 = (component, repConfig, repState) => {
8313        repConfig.store.manager.onLoad(component, repConfig, repState);
8314    };
8315    const onUnload$2 = (component, repConfig, repState) => {
8316        repConfig.store.manager.onUnload(component, repConfig, repState);
8317    };
8318    const setValue$3 = (component, repConfig, repState, data) => {
8319        repConfig.store.manager.setValue(component, repConfig, repState, data);
8320    };
8321    const getValue$3 = (component, repConfig, repState) => repConfig.store.manager.getValue(component, repConfig, repState);
8322    const getState$2 = (component, repConfig, repState) => repState;
8323
8324    var RepresentApis = /*#__PURE__*/Object.freeze({
8325        __proto__: null,
8326        onLoad: onLoad$4,
8327        onUnload: onUnload$2,
8328        setValue: setValue$3,
8329        getValue: getValue$3,
8330        getState: getState$2
8331    });
8332
8333    const events$9 = (repConfig, repState) => {
8334        const es = repConfig.resetOnDom ? [
8335            runOnAttached((comp, _se) => {
8336                onLoad$4(comp, repConfig, repState);
8337            }),
8338            runOnDetached((comp, _se) => {
8339                onUnload$2(comp, repConfig, repState);
8340            })
8341        ] : [
8342            loadEvent(repConfig, repState, onLoad$4)
8343        ];
8344        return derive$2(es);
8345    };
8346
8347    var ActiveRepresenting = /*#__PURE__*/Object.freeze({
8348        __proto__: null,
8349        events: events$9
8350    });
8351
8352    const memory$1 = () => {
8353        const data = Cell(null);
8354        const readState = () => ({
8355            mode: 'memory',
8356            value: data.get()
8357        });
8358        const isNotSet = () => data.get() === null;
8359        const clear = () => {
8360            data.set(null);
8361        };
8362        return nu$4({
8363            set: data.set,
8364            get: data.get,
8365            isNotSet,
8366            clear,
8367            readState
8368        });
8369    };
8370    const manual = () => {
8371        const readState = noop;
8372        return nu$4({
8373            readState
8374        });
8375    };
8376    const dataset = () => {
8377        const dataByValue = Cell({});
8378        const dataByText = Cell({});
8379        const readState = () => ({
8380            mode: 'dataset',
8381            dataByValue: dataByValue.get(),
8382            dataByText: dataByText.get()
8383        });
8384        const clear = () => {
8385            dataByValue.set({});
8386            dataByText.set({});
8387        };
8388        // itemString can be matching value or text.
8389        // TODO: type problem - impossible to correctly return value when type parameter only exists in return type
8390        const lookup = (itemString) => get$h(dataByValue.get(), itemString).orThunk(() => get$h(dataByText.get(), itemString));
8391        const update = (items) => {
8392            const currentDataByValue = dataByValue.get();
8393            const currentDataByText = dataByText.get();
8394            const newDataByValue = {};
8395            const newDataByText = {};
8396            each$1(items, (item) => {
8397                newDataByValue[item.value] = item;
8398                get$h(item, 'meta').each((meta) => {
8399                    get$h(meta, 'text').each((text) => {
8400                        newDataByText[text] = item;
8401                    });
8402                });
8403            });
8404            dataByValue.set({
8405                ...currentDataByValue,
8406                ...newDataByValue
8407            });
8408            dataByText.set({
8409                ...currentDataByText,
8410                ...newDataByText
8411            });
8412        };
8413        return nu$4({
8414            readState,
8415            lookup,
8416            update,
8417            clear
8418        });
8419    };
8420    const init$9 = (spec) => spec.store.manager.state(spec);
8421
8422    var RepresentState = /*#__PURE__*/Object.freeze({
8423        __proto__: null,
8424        memory: memory$1,
8425        dataset: dataset,
8426        manual: manual,
8427        init: init$9
8428    });
8429
8430    const setValue$2 = (component, repConfig, repState, data) => {
8431        const store = repConfig.store;
8432        repState.update([data]);
8433        store.setValue(component, data);
8434        repConfig.onSetValue(component, data);
8435    };
8436    const getValue$2 = (component, repConfig, repState) => {
8437        const store = repConfig.store;
8438        const key = store.getDataKey(component);
8439        return repState.lookup(key).getOrThunk(() => store.getFallbackEntry(key));
8440    };
8441    const onLoad$3 = (component, repConfig, repState) => {
8442        const store = repConfig.store;
8443        store.initialValue.each((data) => {
8444            setValue$2(component, repConfig, repState, data);
8445        });
8446    };
8447    const onUnload$1 = (component, repConfig, repState) => {
8448        repState.clear();
8449    };
8450    var DatasetStore = [
8451        option$3('initialValue'),
8452        required$1('getFallbackEntry'),
8453        required$1('getDataKey'),
8454        required$1('setValue'),
8455        output$1('manager', {
8456            setValue: setValue$2,
8457            getValue: getValue$2,
8458            onLoad: onLoad$3,
8459            onUnload: onUnload$1,
8460            state: dataset
8461        })
8462    ];
8463
8464    const getValue$1 = (component, repConfig, _repState) => repConfig.store.getValue(component);
8465    const setValue$1 = (component, repConfig, _repState, data) => {
8466        repConfig.store.setValue(component, data);
8467        repConfig.onSetValue(component, data);
8468    };
8469    const onLoad$2 = (component, repConfig, _repState) => {
8470        repConfig.store.initialValue.each((data) => {
8471            repConfig.store.setValue(component, data);
8472        });
8473    };
8474    var ManualStore = [
8475        required$1('getValue'),
8476        defaulted('setValue', noop),
8477        option$3('initialValue'),
8478        output$1('manager', {
8479            setValue: setValue$1,
8480            getValue: getValue$1,
8481            onLoad: onLoad$2,
8482            onUnload: noop,
8483            state: NoState.init
8484        })
8485    ];
8486
8487    const setValue = (component, repConfig, repState, data) => {
8488        repState.set(data);
8489        repConfig.onSetValue(component, data);
8490    };
8491    const getValue = (component, repConfig, repState) => repState.get();
8492    const onLoad$1 = (component, repConfig, repState) => {
8493        repConfig.store.initialValue.each((initVal) => {
8494            if (repState.isNotSet()) {
8495                repState.set(initVal);
8496            }
8497        });
8498    };
8499    const onUnload = (component, repConfig, repState) => {
8500        repState.clear();
8501    };
8502    var MemoryStore = [
8503        option$3('initialValue'),
8504        output$1('manager', {
8505            setValue,
8506            getValue,
8507            onLoad: onLoad$1,
8508            onUnload,
8509            state: memory$1
8510        })
8511    ];
8512
8513    var RepresentSchema = [
8514        defaultedOf('store', { mode: 'memory' }, choose$1('mode', {
8515            memory: MemoryStore,
8516            manual: ManualStore,
8517            dataset: DatasetStore
8518        })),
8519        onHandler('onSetValue'),
8520        defaulted('resetOnDom', false)
8521    ];
8522
8523    // The self-reference is clumsy.
8524    const Representing = create$3({
8525        fields: RepresentSchema,
8526        name: 'representing',
8527        active: ActiveRepresenting,
8528        apis: RepresentApis,
8529        extra: {
8530            setValueFrom: (component, source) => {
8531                const value = Representing.getValue(source);
8532                Representing.setValue(component, value);
8533            }
8534        },
8535        state: RepresentState
8536    });
8537
8538    const Invalidating = create$3({
8539        fields: InvalidateSchema,
8540        name: 'invalidating',
8541        active: ActiveInvalidate,
8542        apis: InvalidateApis,
8543        extra: {
8544            // Note, this requires representing to be on the validatee
8545            validation: (validator) => {
8546                return (component) => {
8547                    const v = Representing.getValue(component);
8548                    return Future.pure(validator(v));
8549                };
8550            }
8551        }
8552    });
8553
8554    const exhibit$4 = (base, posConfig) => nu$2({
8555        classes: [],
8556        styles: posConfig.useFixed() ? {} : { position: 'relative' }
8557    });
8558
8559    var ActivePosition = /*#__PURE__*/Object.freeze({
8560        __proto__: null,
8561        exhibit: exhibit$4
8562    });
8563
8564    const adt$3 = Adt.generate([
8565        { none: [] },
8566        { relative: ['x', 'y', 'width', 'height'] },
8567        { fixed: ['x', 'y', 'width', 'height'] }
8568    ]);
8569    const positionWithDirection = (posName, decision, x, y, width, height) => {
8570        const decisionRect = decision.rect;
8571        const decisionX = decisionRect.x - x;
8572        const decisionY = decisionRect.y - y;
8573        const decisionWidth = decisionRect.width;
8574        const decisionHeight = decisionRect.height;
8575        const decisionRight = width - (decisionX + decisionWidth);
8576        const decisionBottom = height - (decisionY + decisionHeight);
8577        const left = Optional.some(decisionX);
8578        const top = Optional.some(decisionY);
8579        const right = Optional.some(decisionRight);
8580        const bottom = Optional.some(decisionBottom);
8581        const none = Optional.none();
8582        return cata$1(decision.direction, () => NuPositionCss(posName, left, top, none, none), // southeast
8583        () => NuPositionCss(posName, none, top, right, none), // southwest
8584        () => NuPositionCss(posName, left, none, none, bottom), // northeast
8585        () => NuPositionCss(posName, none, none, right, bottom), // northwest
8586        () => NuPositionCss(posName, left, top, none, none), // south
8587        () => NuPositionCss(posName, left, none, none, bottom), // north
8588        () => NuPositionCss(posName, left, top, none, none), // east
8589        () => NuPositionCss(posName, none, top, right, none) // west
8590        );
8591    };
8592    const reposition = (origin, decision) => origin.fold(() => {
8593        const decisionRect = decision.rect;
8594        return NuPositionCss('absolute', Optional.some(decisionRect.x), Optional.some(decisionRect.y), Optional.none(), Optional.none());
8595    }, (x, y, width, height) => {
8596        return positionWithDirection('absolute', decision, x, y, width, height);
8597    }, (x, y, width, height) => {
8598        return positionWithDirection('fixed', decision, x, y, width, height);
8599    });
8600    const toBox = (origin, element) => {
8601        const rel = curry(find$2, element);
8602        const position = origin.fold(rel, rel, () => {
8603            const scroll = get$b();
8604            // TODO: Make adding the scroll in OuterPosition.find optional.
8605            return find$2(element).translate(-scroll.left, -scroll.top);
8606        });
8607        const width = getOuter(element);
8608        const height = getOuter$1(element);
8609        return bounds(position.left, position.top, width, height);
8610    };
8611    const viewport = (origin, optBounds) => optBounds.fold(
8612    /* There are no bounds supplied */
8613    () => origin.fold(win, win, bounds), (bounds$1) => 
8614    /* Use any bounds supplied or remove the scroll position of the bounds for fixed. */
8615    origin.fold(constant$1(bounds$1), constant$1(bounds$1), () => {
8616        const pos = translate$1(origin, bounds$1.x, bounds$1.y);
8617        return bounds(pos.left, pos.top, bounds$1.width, bounds$1.height);
8618    }));
8619    const translate$1 = (origin, x, y) => {
8620        const pos = SugarPosition(x, y);
8621        const removeScroll = () => {
8622            const outerScroll = get$b();
8623            return pos.translate(-outerScroll.left, -outerScroll.top);
8624        };
8625        // This could use cata if it wasn't a circular reference
8626        return origin.fold(constant$1(pos), constant$1(pos), removeScroll);
8627    };
8628    const cata = (subject, onNone, onRelative, onFixed) => subject.fold(onNone, onRelative, onFixed);
8629    adt$3.none;
8630    const relative = adt$3.relative;
8631    const fixed = adt$3.fixed;
8632
8633    const anchor = (anchorBox, origin) => ({
8634        anchorBox,
8635        origin
8636    });
8637    const box = (anchorBox, origin) => anchor(anchorBox, origin);
8638
8639    const adt$2 = Adt.generate([
8640        { fit: ['reposition'] },
8641        { nofit: ['reposition', 'visibleW', 'visibleH', 'isVisible'] }
8642    ]);
8643    /**
8644     * This will attempt to determine if the box will fit within the specified bounds or if it needs to be repositioned.
8645     * It will return the following details:
8646     *  - if the original rect was in bounds (originInBounds & sizeInBounds). This is used to determine if we fitted
8647     *    without having to make adjustments.
8648     *  - the height and width that would be visible in the original location. (ie the overlap between the rect and
8649     *    the bounds or the distance between the boxes if there is no overlap)
8650     */
8651    const determinePosition = (box, bounds) => {
8652        const { x: boundsX, y: boundsY, right: boundsRight, bottom: boundsBottom } = bounds;
8653        const { x, y, right, bottom, width, height } = box;
8654        // simple checks for "is the top left inside the view"
8655        const xInBounds = x >= boundsX && x <= boundsRight;
8656        const yInBounds = y >= boundsY && y <= boundsBottom;
8657        const originInBounds = xInBounds && yInBounds;
8658        // simple checks for "is the bottom right inside the view"
8659        const rightInBounds = right <= boundsRight && right >= boundsX;
8660        const bottomInBounds = bottom <= boundsBottom && bottom >= boundsY;
8661        const sizeInBounds = rightInBounds && bottomInBounds;
8662        // measure how much of the width and height are visible. This should never be larger than the actual width or height
8663        // however it can be a negative value when offscreen. These values are generally are only needed for the "nofit" case
8664        const visibleW = Math.min(width, x >= boundsX ? boundsRight - x : right - boundsX);
8665        const visibleH = Math.min(height, y >= boundsY ? boundsBottom - y : bottom - boundsY);
8666        return {
8667            originInBounds,
8668            sizeInBounds,
8669            visibleW,
8670            visibleH
8671        };
8672    };
8673    /**
8674     * This will attempt to calculate and adjust the position of the box so that is stays within the specified bounds.
8675     * The end result will be a new restricted box of where it can safely be placed within the bounds.
8676     */
8677    const calcReposition = (box, bounds$1) => {
8678        const { x: boundsX, y: boundsY, right: boundsRight, bottom: boundsBottom } = bounds$1;
8679        const { x, y, width, height } = box;
8680        // measure the maximum x and y taking into account the height and width of the box
8681        const maxX = Math.max(boundsX, boundsRight - width);
8682        const maxY = Math.max(boundsY, boundsBottom - height);
8683        // Futz with the X value to ensure that we're not off the left or right of the screen
8684        const restrictedX = clamp(x, boundsX, maxX);
8685        // Futz with the Y value to ensure that we're not off the top or bottom of the screen
8686        const restrictedY = clamp(y, boundsY, maxY);
8687        // Determine the new height and width based on the restricted X/Y to keep the element in bounds
8688        const restrictedWidth = Math.min(restrictedX + width, boundsRight) - restrictedX;
8689        const restrictedHeight = Math.min(restrictedY + height, boundsBottom) - restrictedY;
8690        return bounds(restrictedX, restrictedY, restrictedWidth, restrictedHeight);
8691    };
8692    /**
8693     * Determine the maximum height and width available for where the box is positioned in the bounds, making sure
8694     * to account for which direction it's rendering in.
8695     */
8696    const calcMaxSizes = (direction, box, bounds) => {
8697        // Futz with the "height" of the popup to ensure if it doesn't fit it's capped at the available height.
8698        const upAvailable = constant$1(box.bottom - bounds.y);
8699        const downAvailable = constant$1(bounds.bottom - box.y);
8700        const maxHeight = cataVertical(direction, downAvailable, /* middle */ downAvailable, upAvailable);
8701        // Futz with the "width" of the popup to ensure if it doesn't fit it's capped at the available width.
8702        const westAvailable = constant$1(box.right - bounds.x);
8703        const eastAvailable = constant$1(bounds.right - box.x);
8704        const maxWidth = cataHorizontal(direction, eastAvailable, /* middle */ eastAvailable, westAvailable);
8705        return {
8706            maxWidth,
8707            maxHeight
8708        };
8709    };
8710    const attempt = (candidate, width, height, bounds$1) => {
8711        const bubble = candidate.bubble;
8712        const bubbleOffset = bubble.offset;
8713        // adjust the bounds to account for the layout and bubble restrictions
8714        const adjustedBounds = adjustBounds(bounds$1, candidate.restriction, bubbleOffset);
8715        // candidate position is excluding the bubble, so add those values as well
8716        const newX = candidate.x + bubbleOffset.left;
8717        const newY = candidate.y + bubbleOffset.top;
8718        const box = bounds(newX, newY, width, height);
8719        // determine the position of the box in relation to the bounds
8720        const { originInBounds, sizeInBounds, visibleW, visibleH } = determinePosition(box, adjustedBounds);
8721        // restrict the box if it won't fit in the bounds
8722        const fits = originInBounds && sizeInBounds;
8723        const fittedBox = fits ? box : calcReposition(box, adjustedBounds);
8724        // Determine if the box is at least partly visible in the bounds after applying the restrictions
8725        const isPartlyVisible = fittedBox.width > 0 && fittedBox.height > 0;
8726        // Determine the maximum height and width available in the bounds
8727        const { maxWidth, maxHeight } = calcMaxSizes(candidate.direction, fittedBox, bounds$1);
8728        const reposition = {
8729            rect: fittedBox,
8730            maxHeight,
8731            maxWidth,
8732            direction: candidate.direction,
8733            placement: candidate.placement,
8734            classes: {
8735                on: bubble.classesOn,
8736                off: bubble.classesOff
8737            },
8738            layout: candidate.label,
8739            testY: newY
8740        };
8741        // useful debugging that I don't want to lose
8742        // console.log(candidate.label);
8743        // console.table([{
8744        //   newY,
8745        //   limitY: fittedBox.y,
8746        //   boundsY: bounds.y,
8747        //   boundsBottom: bounds.bottom,
8748        //   newX,
8749        //   limitX: fittedBox.x,
8750        //   boundsX: bounds.x,
8751        //   boundsRight: bounds.right,
8752        //   candidateX: candidate.x,
8753        //   candidateY: candidate.y,
8754        //   width,
8755        //   height,
8756        //   isPartlyVisible
8757        // }]);
8758        // console.log(`maxWidth: ${maxWidth}, visibleW: ${visibleW}`);
8759        // console.log(`maxHeight: ${maxHeight}, visibleH: ${visibleH}`);
8760        // console.log('originInBounds:', originInBounds);
8761        // console.log('sizeInBounds:', sizeInBounds);
8762        // console.log(originInBounds && sizeInBounds ? 'fit' : 'nofit');
8763        // Take special note that we don't use the futz values in the nofit case; whether this position is a good fit is separate
8764        // to ensuring that if we choose it the popup is actually on screen properly.
8765        return fits || candidate.alwaysFit ? adt$2.fit(reposition) : adt$2.nofit(reposition, visibleW, visibleH, isPartlyVisible);
8766    };
8767    /**
8768     * Attempts to fit a box (generally a menu).
8769     *
8770     * candidates: an array of layout generators, generally obtained via api.Layout or api.LinkedLayout
8771     * anchorBox: the box on screen that triggered the menu, we must touch one of the edges as defined by the candidate layouts
8772     * elementBox: the popup (only width and height matter)
8773     * bubbles: the bubbles for the popup (see api.Bubble)
8774     * bounds: the screen
8775     */
8776    const attempts = (element, candidates, anchorBox, elementBox, bubbles, bounds) => {
8777        const panelWidth = elementBox.width;
8778        const panelHeight = elementBox.height;
8779        const attemptBestFit = (layout, reposition, visibleW, visibleH, isVisible) => {
8780            const next = layout(anchorBox, elementBox, bubbles, element, bounds);
8781            const attemptLayout = attempt(next, panelWidth, panelHeight, bounds);
8782            return attemptLayout.fold(constant$1(attemptLayout), (newReposition, newVisibleW, newVisibleH, newIsVisible) => {
8783                // console.log(`label: ${next.label}, newVisibleW: ${newVisibleW}, visibleW: ${visibleW}, newVisibleH: ${newVisibleH}, visibleH: ${visibleH}, newIsVisible: ${newIsVisible}, isVisible: ${isVisible}`);
8784                const improved = isVisible === newIsVisible ? (newVisibleH > visibleH || newVisibleW > visibleW) : (!isVisible && newIsVisible);
8785                // console.log('improved? ', improved);
vendor: 15,731 bytes, lines 8786-9135
8786                return improved ? attemptLayout : adt$2.nofit(reposition, visibleW, visibleH, isVisible);
8787            });
8788        };
8789        const abc = foldl(candidates, (b, a) => {
8790            const bestNext = curry(attemptBestFit, a);
8791            return b.fold(constant$1(b), bestNext);
8792        }, 
8793        // fold base case: No candidates, it's never going to be correct, so do whatever
8794        adt$2.nofit({
8795            rect: anchorBox,
8796            maxHeight: elementBox.height,
8797            maxWidth: elementBox.width,
8798            direction: southeast$3(),
8799            placement: "southeast" /* Placement.Southeast */,
8800            classes: {
8801                on: [],
8802                off: []
8803            },
8804            layout: 'none',
8805            testY: anchorBox.y
8806        }, -1, -1, false));
8807        // unwrapping 'reposition' from the adt, for both fit & nofit the first arg is the one we need,
8808        // so we can cheat and use Fun.identity
8809        return abc.fold(identity, identity);
8810    };
8811
8812    const properties = ['top', 'bottom', 'right', 'left'];
8813    const timerAttr = 'data-alloy-transition-timer';
8814    const isTransitioning$1 = (element, transition) => hasAll(element, transition.classes);
8815    const shouldApplyTransitionCss = (transition, decision, lastPlacement) => {
8816        // Don't apply transitions if there was no previous placement as it's transitioning from offscreen
8817        return lastPlacement.exists((placer) => {
8818            const mode = transition.mode;
8819            return mode === 'all' ? true : placer[mode] !== decision[mode];
8820        });
8821    };
8822    const hasChanges = (position, intermediate) => {
8823        // Round to 3 decimal points
8824        const round = (value) => parseFloat(value).toFixed(3);
8825        return find$4(intermediate, (value, key) => {
8826            const newValue = position[key].map(round);
8827            const val = value.map(round);
8828            return !equals(newValue, val);
8829        }).isSome();
8830    };
8831    const getTransitionDuration = (element) => {
8832        const get = (name) => {
8833            const style = get$e(element, name);
8834            const times = style.split(/\s*,\s*/);
8835            return filter$2(times, isNotEmpty);
8836        };
8837        const parse = (value) => {
8838            if (isString(value) && /^[\d.]+/.test(value)) {
8839                const num = parseFloat(value);
8840                return endsWith(value, 'ms') ? num : num * 1000;
8841            }
8842            else {
8843                return 0;
8844            }
8845        };
8846        const delay = get('transition-delay');
8847        const duration = get('transition-duration');
8848        return foldl(duration, (acc, dur, i) => {
8849            const time = parse(delay[i]) + parse(dur);
8850            return Math.max(acc, time);
8851        }, 0);
8852    };
8853    const setupTransitionListeners = (element, transition) => {
8854        const transitionEnd = unbindable();
8855        const transitionCancel = unbindable();
8856        let timer;
8857        const isSourceTransition = (e) => {
8858            // Ensure the transition event isn't from a pseudo element
8859            const pseudoElement = e.raw.pseudoElement ?? '';
8860            return eq(e.target, element) && isEmpty(pseudoElement) && contains$2(properties, e.raw.propertyName);
8861        };
8862        const transitionDone = (e) => {
8863            if (isNullable(e) || isSourceTransition(e)) {
8864                transitionEnd.clear();
8865                transitionCancel.clear();
8866                // Only cleanup the class/timer on transitionend not on a cancel. This is done as cancel
8867                // means the element has been repositioned and would need to keep transitioning
8868                const type = e?.raw.type;
8869                if (isNullable(type) || type === transitionend()) {
8870                    clearTimeout(timer);
8871                    remove$8(element, timerAttr);
8872                    remove$2(element, transition.classes);
8873                }
8874            }
8875        };
8876        const transitionStart = bind$1(element, transitionstart(), (e) => {
8877            if (isSourceTransition(e)) {
8878                transitionStart.unbind();
8879                transitionEnd.set(bind$1(element, transitionend(), transitionDone));
8880                transitionCancel.set(bind$1(element, transitioncancel(), transitionDone));
8881            }
8882        });
8883        // Request the next animation frame so we can roughly determine when the transition starts and then ensure
8884        // the transition is cleaned up. In addition add ~17ms to the delay as that's about about 1 frame at 60fps
8885        const duration = getTransitionDuration(element);
8886        window.requestAnimationFrame(() => {
8887            timer = setTimeout(transitionDone, duration + 17);
8888            set$9(element, timerAttr, timer);
8889        });
8890    };
8891    const startTransitioning = (element, transition) => {
8892        add$1(element, transition.classes);
8893        // Clear any existing cleanup timers
8894        getOpt(element, timerAttr).each((timerId) => {
8895            clearTimeout(parseInt(timerId, 10));
8896            remove$8(element, timerAttr);
8897        });
8898        setupTransitionListeners(element, transition);
8899    };
8900    const applyTransitionCss = (element, origin, position, transition, decision, lastPlacement) => {
8901        const shouldTransition = shouldApplyTransitionCss(transition, decision, lastPlacement);
8902        if (shouldTransition || isTransitioning$1(element, transition)) {
8903            // Set the new position first so we can calculate the computed position
8904            set$7(element, 'position', position.position);
8905            // Get the computed positions for the current element based on the new position CSS being applied
8906            const rect = toBox(origin, element);
8907            const intermediatePosition = reposition(origin, { ...decision, rect });
8908            const intermediateCssOptions = mapToObject(properties, (prop) => intermediatePosition[prop]);
8909            // Apply the intermediate styles and transition classes if something has changed
8910            if (hasChanges(position, intermediateCssOptions)) {
8911                setOptions(element, intermediateCssOptions);
8912                if (shouldTransition) {
8913                    startTransitioning(element, transition);
8914                }
8915                reflow(element);
8916            }
8917        }
8918        else {
8919            remove$2(element, transition.classes);
8920        }
8921    };
8922
8923    /*
8924     * This is the old repartee API. It is retained in a similar structure to the original form,
8925     * in case we decide to bring back the flexibility of working with non-standard positioning.
8926     */
8927    const elementSize = (p) => ({
8928        width: Math.ceil(getOuter(p)),
8929        height: getOuter$1(p)
8930    });
8931    const layout = (anchorBox, element, bubbles, options) => {
8932        // clear the potentially limiting factors before measuring
8933        remove$6(element, 'max-height');
8934        remove$6(element, 'max-width');
8935        const elementBox = elementSize(element);
8936        return attempts(element, options.preference, anchorBox, elementBox, bubbles, options.bounds);
8937    };
8938    const setClasses = (element, decision) => {
8939        const classInfo = decision.classes;
8940        remove$2(element, classInfo.off);
8941        add$1(element, classInfo.on);
8942    };
8943    /*
8944     * maxHeightFunction is a MaxHeight instance.
8945     * max-height is usually the distance between the edge of the popup and the screen; top of popup to bottom of screen for south, bottom of popup to top of screen for north.
8946     *
8947     * There are a few cases where we specifically don't want a max-height, which is why it's optional.
8948     */
8949    const setHeight = (element, decision, options) => {
8950        // The old API enforced MaxHeight.anchored() for fixed position. That no longer seems necessary.
8951        const maxHeightFunction = options.maxHeightFunction;
8952        maxHeightFunction(element, decision.maxHeight);
8953    };
8954    const setWidth = (element, decision, options) => {
8955        const maxWidthFunction = options.maxWidthFunction;
8956        maxWidthFunction(element, decision.maxWidth);
8957    };
8958    const position$2 = (element, decision, options) => {
8959        // This is a point of difference between Alloy and Repartee. Repartee appears to use Measure to calculate the available space for fixed origin
8960        // That is not ported yet.
8961        const positionCss = reposition(options.origin, decision);
8962        options.transition.each((transition) => {
8963            applyTransitionCss(element, options.origin, positionCss, transition, decision, options.lastPlacement);
8964        });
8965        applyPositionCss(element, positionCss);
8966    };
8967    const setPlacement = (element, decision) => {
8968        setPlacement$1(element, decision.placement);
8969    };
8970
8971    const defaultOr = (options, key, dephault) => options[key] === undefined ? dephault : options[key];
8972    // This takes care of everything when you are positioning UI that can go anywhere on the screen
8973    const simple = (anchor, element, bubble, layouts, lastPlacement, optBounds, overrideOptions, transition) => {
8974        // the only supported override at the moment. Once relative has been deleted, maybe this can be optional in the bag
8975        const maxHeightFunction = defaultOr(overrideOptions, 'maxHeightFunction', anchored());
8976        const maxWidthFunction = defaultOr(overrideOptions, 'maxWidthFunction', noop);
8977        const anchorBox = anchor.anchorBox;
8978        const origin = anchor.origin;
8979        const options = {
8980            bounds: viewport(origin, optBounds),
8981            origin,
8982            preference: layouts,
8983            maxHeightFunction,
8984            maxWidthFunction,
8985            lastPlacement,
8986            transition
8987        };
8988        return go(anchorBox, element, bubble, options);
8989    };
8990    // This is the old public API. If we ever need full customisability again, this is how to expose it
8991    const go = (anchorBox, element, bubble, options) => {
8992        const decision = layout(anchorBox, element, bubble, options);
8993        position$2(element, decision, options);
8994        setPlacement(element, decision);
8995        setClasses(element, decision);
8996        setHeight(element, decision, options);
8997        setWidth(element, decision, options);
8998        return {
8999            layout: decision.layout,
9000            placement: decision.placement
9001        };
9002    };
9003
9004    const nu$1 = identity;
9005
9006    const schema$n = () => optionObjOf('layouts', [
9007        required$1('onLtr'),
9008        required$1('onRtl'),
9009        option$3('onBottomLtr'),
9010        option$3('onBottomRtl')
9011    ]);
9012    const get$1 = (elem, info, defaultLtr, defaultRtl, defaultBottomLtr, defaultBottomRtl, dirElement) => {
9013        const isBottomToTop = dirElement.map(isBottomToTopDir).getOr(false);
9014        const customLtr = info.layouts.map((ls) => ls.onLtr(elem));
9015        const customRtl = info.layouts.map((ls) => ls.onRtl(elem));
9016        const ltr = isBottomToTop ?
9017            info.layouts.bind((ls) => ls.onBottomLtr.map((f) => f(elem)))
9018                .or(customLtr)
9019                .getOr(defaultBottomLtr) :
9020            customLtr.getOr(defaultLtr);
9021        const rtl = isBottomToTop ?
9022            info.layouts.bind((ls) => ls.onBottomRtl.map((f) => f(elem)))
9023                .or(customRtl)
9024                .getOr(defaultBottomRtl) :
9025            customRtl.getOr(defaultRtl);
9026        const f = onDirection(ltr, rtl);
9027        return f(elem);
9028    };
9029
9030    const placement$4 = (component, anchorInfo, origin) => {
9031        const hotspot = anchorInfo.hotspot;
9032        const anchorBox = toBox(origin, hotspot.element);
9033        const layouts = get$1(component.element, anchorInfo, belowOrAbove(), belowOrAboveRtl(), aboveOrBelow(), aboveOrBelowRtl(), Optional.some(anchorInfo.hotspot.element));
9034        return Optional.some(nu$1({
9035            anchorBox,
9036            bubble: anchorInfo.bubble.getOr(fallback()),
9037            overrides: anchorInfo.overrides,
9038            layouts
9039        }));
9040    };
9041    var HotspotAnchor = [
9042        required$1('hotspot'),
9043        option$3('bubble'),
9044        defaulted('overrides', {}),
9045        schema$n(),
9046        output$1('placement', placement$4)
9047    ];
9048
9049    const placement$3 = (component, anchorInfo, origin) => {
9050        const pos = translate$1(origin, anchorInfo.x, anchorInfo.y);
9051        const anchorBox = bounds(pos.left, pos.top, anchorInfo.width, anchorInfo.height);
9052        const layouts = get$1(component.element, anchorInfo, all$2(), allRtl$1(), 
9053        // No default bottomToTop layouts currently needed
9054        all$2(), allRtl$1(), Optional.none());
9055        return Optional.some(nu$1({
9056            anchorBox,
9057            bubble: anchorInfo.bubble,
9058            overrides: anchorInfo.overrides,
9059            layouts
9060        }));
9061    };
9062    var MakeshiftAnchor = [
9063        required$1('x'),
9064        required$1('y'),
9065        defaulted('height', 0),
9066        defaulted('width', 0),
9067        defaulted('bubble', fallback()),
9068        defaulted('overrides', {}),
9069        schema$n(),
9070        output$1('placement', placement$3)
9071    ];
9072
9073    const adt$1 = Adt.generate([
9074        { screen: ['point'] },
9075        { absolute: ['point', 'scrollLeft', 'scrollTop'] }
9076    ]);
9077    const toFixed = (pos) => 
9078    // TODO: Use new ADT methods
9079    pos.fold(identity, (point, scrollLeft, scrollTop) => point.translate(-scrollLeft, -scrollTop));
9080    const toAbsolute = (pos) => pos.fold(identity, identity);
9081    const sum = (points) => foldl(points, (b, a) => b.translate(a.left, a.top), SugarPosition(0, 0));
9082    const sumAsFixed = (positions) => {
9083        const points = map$2(positions, toFixed);
9084        return sum(points);
9085    };
9086    const sumAsAbsolute = (positions) => {
9087        const points = map$2(positions, toAbsolute);
9088        return sum(points);
9089    };
9090    const screen = adt$1.screen;
9091    const absolute = adt$1.absolute;
9092
9093    // In one mode, the window is inside an iframe. If that iframe is in the
9094    // same document as the positioning element (component), then identify the offset
9095    // difference between the iframe and the component.
9096    const getOffset = (component, origin, anchorInfo) => {
9097        const win = defaultView(anchorInfo.root).dom;
9098        const hasSameOwner = (frame) => {
9099            const frameOwner = owner$4(frame);
9100            const compOwner = owner$4(component.element);
9101            return eq(frameOwner, compOwner);
9102        };
9103        return Optional.from(win.frameElement).map(SugarElement.fromDom)
9104            .filter(hasSameOwner).map(absolute$3);
9105    };
9106    const getRootPoint = (component, origin, anchorInfo) => {
9107        const doc = owner$4(component.element);
9108        const outerScroll = get$b(doc);
9109        const offset = getOffset(component, origin, anchorInfo).getOr(outerScroll);
9110        return absolute(offset, outerScroll.left, outerScroll.top);
9111    };
9112
9113    const getBox = (left, top, width, height) => {
9114        const point = screen(SugarPosition(left, top));
9115        return Optional.some(pointed(point, width, height));
9116    };
9117    const calcNewAnchor = (optBox, rootPoint, anchorInfo, origin, elem) => optBox.map((box) => {
9118        const points = [rootPoint, box.point];
9119        const topLeft = cata(origin, () => sumAsAbsolute(points), () => sumAsAbsolute(points), () => sumAsFixed(points));
9120        const anchorBox = rect(topLeft.left, topLeft.top, box.width, box.height);
9121        const layoutsLtr = anchorInfo.showAbove ?
9122            aboveOrBelow() :
9123            belowOrAbove();
9124        const layoutsRtl = anchorInfo.showAbove ?
9125            aboveOrBelowRtl() :
9126            belowOrAboveRtl();
9127        const layouts = get$1(elem, anchorInfo, layoutsLtr, layoutsRtl, layoutsLtr, layoutsRtl, Optional.none());
9128        return nu$1({
9129            anchorBox,
9130            bubble: anchorInfo.bubble.getOr(fallback()),
9131            overrides: anchorInfo.overrides,
9132            layouts
9133        });
9134    });
9135
9136    const placement$2 = (component, anchorInfo, origin) => {
9137        const rootPoint = getRootPoint(component, origin, anchorInfo);
9138        return anchorInfo.node
9139            // Ensure the node is still attached, otherwise we can't get a valid client rect for a detached node
9140            .filter(inBody)
9141            .bind((target) => {
9142            const rect = target.dom.getBoundingClientRect();
9143            const nodeBox = getBox(rect.left, rect.top, rect.width, rect.height);
9144            const elem = anchorInfo.node.getOr(component.element);
9145            return calcNewAnchor(nodeBox, rootPoint, anchorInfo, origin, elem);
9146        });
9147    };
9148    var NodeAnchor = [
9149        required$1('node'),
9150        required$1('root'),
9151        option$3('bubble'),
9152        schema$n(),
9153        // chiefly MaxHeight.expandable()
9154        defaulted('overrides', {}),
9155        defaulted('showAbove', false),
9156        output$1('placement', placement$2)
9157    ];
9158
9159    const point = (element, offset) => ({
9160        element,
9161        offset
9162    });
9163    // NOTE: This only descends once.
9164    const descendOnce$1 = (element, offset) => {
9165        const children$1 = children(element);
9166        if (children$1.length === 0) {
9167            return point(element, offset);
9168        }
9169        else if (offset < children$1.length) {
9170            return point(children$1[offset], 0);
9171        }
9172        else {
9173            const last = children$1[children$1.length - 1];
9174            const len = isText(last) ? get$a(last).length : children(last).length;
9175            return point(last, len);
9176        }
9177    };
9178
9179    // A range from (a, 1) to (body, end) was giving the wrong bounds.
9180    const descendOnce = (element, offset) => isText(element) ? point(element, offset) : descendOnce$1(element, offset);
9181    const isSimRange = (detail) => detail.foffset !== undefined;
9182    const getAnchorSelection = (win, anchorInfo) => {
9183        // FIX TEST Test both providing a getSelection and not providing a getSelection
9184        const getSelection = anchorInfo.getSelection.getOrThunk(() => () => getExact(win));
9185        return getSelection().map((sel) => {
9186            if (isSimRange(sel)) {
9187                const modStart = descendOnce(sel.start, sel.soffset);
9188                const modFinish = descendOnce(sel.finish, sel.foffset);
9189                return SimSelection.range(modStart.element, modStart.offset, modFinish.element, modFinish.offset);
9190            }
9191            else {
9192                return sel;
9193            }
9194        });
9195    };
9196    const placement$1 = (component, anchorInfo, origin) => {
9197        const win = defaultView(anchorInfo.root).dom;
9198        const rootPoint = getRootPoint(component, origin, anchorInfo);
9199        const selectionBox = getAnchorSelection(win, anchorInfo).bind((sel) => {
9200            // This represents the *visual* rectangle of the selection.
9201            if (isSimRange(sel)) {
9202                const optRect = getBounds$2(win, SimSelection.exactFromRange(sel)).orThunk(() => {
9203                    const zeroWidth$1 = SugarElement.fromText(zeroWidth);
9204                    const beforeMutation = getExact(win);
9205                    before$1(sel.start, zeroWidth$1);
9206                    // Certain things like <p><br/></p> with (p, 0) or <br>) as collapsed selection do not return a client rectangle
9207                    const rect = getFirstRect(win, SimSelection.exact(zeroWidth$1, 0, zeroWidth$1, 1));
9208                    remove$7(zeroWidth$1);
9209                    beforeMutation.each((range) => setExact(win, range.start, range.soffset, range.finish, range.foffset));
9210                    return rect;
9211                });
9212                return optRect.bind((rawRect) => {
9213                    return getBox(rawRect.left, rawRect.top, rawRect.width, rawRect.height);
9214                });
9215            }
9216            else {
9217                const selectionRect = map$1(sel, (cell) => cell.dom.getBoundingClientRect());
9218                const bounds = {
9219                    left: Math.min(selectionRect.firstCell.left, selectionRect.lastCell.left),
9220                    right: Math.max(selectionRect.firstCell.right, selectionRect.lastCell.right),
vendor: 43,262 bytes, lines 9221-10256
9221                    top: Math.min(selectionRect.firstCell.top, selectionRect.lastCell.top),
9222                    bottom: Math.max(selectionRect.firstCell.bottom, selectionRect.lastCell.bottom)
9223                };
9224                return getBox(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);
9225            }
9226        });
9227        const targetElement = getAnchorSelection(win, anchorInfo)
9228            .bind((sel) => {
9229            if (isSimRange(sel)) {
9230                return isElement$1(sel.start) ? Optional.some(sel.start) : parentElement(sel.start);
9231            }
9232            else {
9233                return Optional.some(sel.firstCell);
9234            }
9235        });
9236        const elem = targetElement.getOr(component.element);
9237        return calcNewAnchor(selectionBox, rootPoint, anchorInfo, origin, elem);
9238    };
9239    var SelectionAnchor = [
9240        option$3('getSelection'),
9241        required$1('root'),
9242        option$3('bubble'),
9243        schema$n(),
9244        defaulted('overrides', {}),
9245        defaulted('showAbove', false),
9246        output$1('placement', placement$1)
9247    ];
9248
9249    /*
9250      Layout for submenus;
9251      Either left or right of the anchor menu item. Never above or below.
9252      Aligned to the top or bottom of the anchor as appropriate.
9253     */
9254    const labelPrefix = 'link-layout';
9255    // display element to the right, left edge against the right of the menu
9256    const eastX = (anchor) => anchor.x + anchor.width;
9257    // display element to the left, right edge against the left of the menu
9258    const westX = (anchor, element) => anchor.x - element.width;
9259    // display element pointing up, bottom edge against the bottom of the menu (usually to one side)
9260    const northY = (anchor, element) => anchor.y - element.height + anchor.height;
9261    // display element pointing down, top edge against the top of the menu (usually to one side)
9262    const southY = (anchor) => anchor.y;
9263    const southeast = (anchor, element, bubbles) => nu$5(eastX(anchor), southY(anchor), bubbles.southeast(), southeast$3(), "southeast" /* Placement.Southeast */, boundsRestriction(anchor, { left: 0 /* AnchorBoxBounds.RightEdge */, top: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix);
9264    const southwest = (anchor, element, bubbles) => nu$5(westX(anchor, element), southY(anchor), bubbles.southwest(), southwest$3(), "southwest" /* Placement.Southwest */, boundsRestriction(anchor, { right: 1 /* AnchorBoxBounds.LeftEdge */, top: 2 /* AnchorBoxBounds.TopEdge */ }), labelPrefix);
9265    const northeast = (anchor, element, bubbles) => nu$5(eastX(anchor), northY(anchor, element), bubbles.northeast(), northeast$3(), "northeast" /* Placement.Northeast */, boundsRestriction(anchor, { left: 0 /* AnchorBoxBounds.RightEdge */, bottom: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix);
9266    const northwest = (anchor, element, bubbles) => nu$5(westX(anchor, element), northY(anchor, element), bubbles.northwest(), northwest$3(), "northwest" /* Placement.Northwest */, boundsRestriction(anchor, { right: 1 /* AnchorBoxBounds.LeftEdge */, bottom: 3 /* AnchorBoxBounds.BottomEdge */ }), labelPrefix);
9267    const all = () => [southeast, southwest, northeast, northwest];
9268    const allRtl = () => [southwest, southeast, northwest, northeast];
9269
9270    const placement = (component, submenuInfo, origin) => {
9271        const anchorBox = toBox(origin, submenuInfo.item.element);
9272        const layouts = get$1(component.element, submenuInfo, all(), allRtl(), 
9273        // No default bottomToTop layouts currently needed
9274        all(), allRtl(), Optional.none());
9275        return Optional.some(nu$1({
9276            anchorBox,
9277            bubble: fallback(),
9278            overrides: submenuInfo.overrides,
9279            layouts
9280        }));
9281    };
9282    var SubmenuAnchor = [
9283        required$1('item'),
9284        schema$n(),
9285        defaulted('overrides', {}),
9286        output$1('placement', placement)
9287    ];
9288
9289    var AnchorSchema = choose$1('type', {
9290        selection: SelectionAnchor,
9291        node: NodeAnchor,
9292        hotspot: HotspotAnchor,
9293        submenu: SubmenuAnchor,
9294        makeshift: MakeshiftAnchor
9295    });
9296
9297    const TransitionSchema = [
9298        requiredArrayOf('classes', string),
9299        defaultedStringEnum('mode', 'all', ['all', 'layout', 'placement'])
9300    ];
9301    const PositionSchema = [
9302        defaulted('useFixed', never),
9303        option$3('getBounds')
9304    ];
9305    const PlacementSchema = [
9306        requiredOf('anchor', AnchorSchema),
9307        optionObjOf('transition', TransitionSchema)
9308    ];
9309
9310    const getFixedOrigin = () => {
9311        // Don't use window.innerWidth/innerHeight here, as we don't want to include scrollbars
9312        // since the right/bottom position is based on the edge of the scrollbar not the window
9313        const html = document.documentElement;
9314        return fixed(0, 0, html.clientWidth, html.clientHeight);
9315    };
9316    const getRelativeOrigin = (component) => {
9317        const position = absolute$3(component.element);
9318        const bounds = component.element.dom.getBoundingClientRect();
9319        // We think that this just needs to be kept consistent with Boxes.win. If we remove the scroll values from Boxes.win, we
9320        // should change this to just bounds.left and bounds.top from getBoundingClientRect
9321        return relative(position.left, position.top, bounds.width, bounds.height);
9322    };
9323    const place = (origin, anchoring, optBounds, placee, lastPlace, transition) => {
9324        const anchor = box(anchoring.anchorBox, origin);
9325        return simple(anchor, placee.element, anchoring.bubble, anchoring.layouts, lastPlace, optBounds, anchoring.overrides, transition);
9326    };
9327    const position$1 = (component, posConfig, posState, placee, placementSpec) => {
9328        const optWithinBounds = Optional.none();
9329        positionWithinBounds(component, posConfig, posState, placee, placementSpec, optWithinBounds);
9330    };
9331    const positionWithinBounds = (component, posConfig, posState, placee, placementSpec, optWithinBounds) => {
9332        const placeeDetail = asRawOrDie$1('placement.info', objOf(PlacementSchema), placementSpec);
9333        const anchorage = placeeDetail.anchor;
9334        const element = placee.element;
9335        const placeeState = posState.get(placee.uid);
9336        // Preserve the focus as IE 11 loses it when setting visibility to hidden
9337        preserve(() => {
9338            // We set it to be fixed, so that it doesn't interfere with the layout of anything
9339            // when calculating anchors
9340            set$7(element, 'position', 'fixed');
9341            const oldVisibility = getRaw(element, 'visibility');
9342            set$7(element, 'visibility', 'hidden');
9343            // We need to calculate the origin (esp. the bounding client rect) *after* we have done
9344            // all the preprocessing of the component and placee. Otherwise, the relative positions
9345            // (bottom and right) will be using the wrong dimensions
9346            const origin = posConfig.useFixed() ? getFixedOrigin() : getRelativeOrigin(component);
9347            anchorage.placement(component, anchorage, origin).each((anchoring) => {
9348                // If "within bounds" is specified, it overrides any Positioning config. Otherwise, we
9349                // use the Positioning config. We don't try to combine automatically here because they are
9350                // sometimes serving different purposes. If the Positioning config getBounds needs to be
9351                // combined with the optWithinBounds bounds, then it is the responsibility of the calling
9352                // code to combine them, and pass in the combined value as optWithinBounds. The optWithinBounds
9353                // will *always* override the Positioning config.
9354                const optBounds = optWithinBounds.orThunk(() => posConfig.getBounds.map(apply$1));
9355                // Place the element and then update the state for the placee
9356                const newState = place(origin, anchoring, optBounds, placee, placeeState, placeeDetail.transition);
9357                posState.set(placee.uid, newState);
9358            });
9359            oldVisibility.fold(() => {
9360                remove$6(element, 'visibility');
9361            }, (vis) => {
9362                set$7(element, 'visibility', vis);
9363            });
9364            // We need to remove position: fixed put on by above code if it is not needed.
9365            if (getRaw(element, 'left').isNone() &&
9366                getRaw(element, 'top').isNone() &&
9367                getRaw(element, 'right').isNone() &&
9368                getRaw(element, 'bottom').isNone() &&
9369                is$1(getRaw(element, 'position'), 'fixed')) {
9370                remove$6(element, 'position');
9371            }
9372        }, element);
9373    };
9374    const getMode = (component, pConfig, _pState) => pConfig.useFixed() ? 'fixed' : 'absolute';
9375    const reset = (component, pConfig, posState, placee) => {
9376        const element = placee.element;
9377        each$1(['position', 'left', 'right', 'top', 'bottom'], (prop) => remove$6(element, prop));
9378        reset$2(element);
9379        posState.clear(placee.uid);
9380    };
9381
9382    var PositionApis = /*#__PURE__*/Object.freeze({
9383        __proto__: null,
9384        position: position$1,
9385        positionWithinBounds: positionWithinBounds,
9386        getMode: getMode,
9387        reset: reset
9388    });
9389
9390    const init$8 = () => {
9391        let state = {};
9392        const set = (id, data) => {
9393            state[id] = data;
9394        };
9395        const get = (id) => get$h(state, id);
9396        const clear = (id) => {
9397            if (isNonNullable(id)) {
9398                delete state[id];
9399            }
9400            else {
9401                state = {};
9402            }
9403        };
9404        return nu$4({
9405            readState: () => state,
9406            clear,
9407            set,
9408            get
9409        });
9410    };
9411
9412    var PositioningState = /*#__PURE__*/Object.freeze({
9413        __proto__: null,
9414        init: init$8
9415    });
9416
9417    const Positioning = create$3({
9418        fields: PositionSchema,
9419        name: 'positioning',
9420        active: ActivePosition,
9421        apis: PositionApis,
9422        state: PositioningState
9423    });
9424
9425    const chooseChannels = (channels, message) => message.universal ? channels : filter$2(channels, (ch) => contains$2(message.channels, ch));
9426    const events$8 = (receiveConfig) => derive$2([
9427        run$1(receive(), (component, message) => {
9428            const channelMap = receiveConfig.channels;
9429            const channels = keys(channelMap);
9430            // NOTE: Receiving event ignores the whole simulated event part.
9431            // TODO: Think about the types for this, or find a better way for this to rely on receiving.
9432            const receivingData = message;
9433            const targetChannels = chooseChannels(channels, receivingData);
9434            each$1(targetChannels, (ch) => {
9435                const channelInfo = channelMap[ch];
9436                const channelSchema = channelInfo.schema;
9437                const data = asRawOrDie$1('channel[' + ch + '] data\nReceiver: ' + element(component.element), channelSchema, receivingData.data);
9438                channelInfo.onReceive(component, data);
9439            });
9440        })
9441    ]);
9442
9443    var ActiveReceiving = /*#__PURE__*/Object.freeze({
9444        __proto__: null,
9445        events: events$8
9446    });
9447
9448    var ReceivingSchema = [
9449        requiredOf('channels', setOf(
9450        // Allow any keys.
9451        Result.value, objOfOnly([
9452            onStrictHandler('onReceive'),
9453            defaulted('schema', anyValue())
9454        ])))
9455    ];
9456
9457    const Receiving = create$3({
9458        fields: ReceivingSchema,
9459        name: 'receiving',
9460        active: ActiveReceiving
9461    });
9462
9463    const events$7 = (reflectingConfig, reflectingState) => {
9464        const update = (component, data) => {
9465            reflectingConfig.updateState.each((updateState) => {
9466                const newState = updateState(component, data);
9467                reflectingState.set(newState);
9468            });
9469            // FIX: Partial duplication of Replacing + Receiving
9470            reflectingConfig.renderComponents.each((renderComponents) => {
9471                const newComponents = renderComponents(data, reflectingState.get());
9472                const replacer = reflectingConfig.reuseDom ? withReuse : withoutReuse;
9473                replacer(component, newComponents);
9474            });
9475        };
9476        return derive$2([
9477            run$1(receive(), (component, message) => {
9478                // NOTE: Receiving event ignores the whole simulated event part.
9479                // TODO: Think about the types for this, or find a better way for this to rely on receiving.
9480                const receivingData = message;
9481                if (!receivingData.universal) {
9482                    const channel = reflectingConfig.channel;
9483                    if (contains$2(receivingData.channels, channel)) {
9484                        update(component, receivingData.data);
9485                    }
9486                }
9487            }),
9488            runOnAttached((comp, _se) => {
9489                reflectingConfig.initialData.each((rawData) => {
9490                    update(comp, rawData);
9491                });
9492            })
9493        ]);
9494    };
9495
9496    var ActiveReflecting = /*#__PURE__*/Object.freeze({
9497        __proto__: null,
9498        events: events$7
9499    });
9500
9501    const getState$1 = (component, replaceConfig, reflectState) => reflectState;
9502
9503    var ReflectingApis = /*#__PURE__*/Object.freeze({
9504        __proto__: null,
9505        getState: getState$1
9506    });
9507
9508    var ReflectingSchema = [
9509        required$1('channel'),
9510        option$3('renderComponents'),
9511        option$3('updateState'),
9512        option$3('initialData'),
9513        defaultedBoolean('reuseDom', true)
9514    ];
9515
9516    const init$7 = () => {
9517        const cell = Cell(Optional.none());
9518        const clear = () => cell.set(Optional.none());
9519        const readState = () => cell.get().getOr('none');
9520        return {
9521            readState,
9522            get: cell.get,
9523            set: cell.set,
9524            clear
9525        };
9526    };
9527
9528    var ReflectingState = /*#__PURE__*/Object.freeze({
9529        __proto__: null,
9530        init: init$7
9531    });
9532
9533    const Reflecting = create$3({
9534        fields: ReflectingSchema,
9535        name: 'reflecting',
9536        active: ActiveReflecting,
9537        apis: ReflectingApis,
9538        state: ReflectingState
9539    });
9540
9541    // NOTE: A sandbox should not start as part of the world. It is expected to be
9542    // added to the sink on rebuild.
9543    const rebuild = (sandbox, sConfig, sState, data) => {
9544        sState.get().each((_data) => {
9545            // If currently has data, so it hasn't been removed yet. It is
9546            // being "re-opened"
9547            detachChildren(sandbox);
9548        });
9549        const point = sConfig.getAttachPoint(sandbox);
9550        attach(point, sandbox);
9551        // Must be after the sandbox is in the system
9552        const built = sandbox.getSystem().build(data);
9553        attach(sandbox, built);
9554        sState.set(built);
9555        return built;
9556    };
9557    // Open sandbox transfers focus to the opened menu
9558    const open$1 = (sandbox, sConfig, sState, data) => {
9559        const newState = rebuild(sandbox, sConfig, sState, data);
9560        sConfig.onOpen(sandbox, newState);
9561        return newState;
9562    };
9563    const setContent = (sandbox, sConfig, sState, data) => sState.get().map(() => rebuild(sandbox, sConfig, sState, data));
9564    // TODO AP-191 write a test for openWhileCloaked
9565    const openWhileCloaked = (sandbox, sConfig, sState, data, transaction) => {
9566        cloak(sandbox, sConfig);
9567        open$1(sandbox, sConfig, sState, data);
9568        transaction();
9569        decloak(sandbox, sConfig);
9570    };
9571    const close$1 = (sandbox, sConfig, sState) => {
9572        sState.get().each((data) => {
9573            detachChildren(sandbox);
9574            detach(sandbox);
9575            sConfig.onClose(sandbox, data);
9576            sState.clear();
9577        });
9578    };
9579    const isOpen$1 = (_sandbox, _sConfig, sState) => sState.isOpen();
9580    const isPartOf$1 = (sandbox, sConfig, sState, queryElem) => isOpen$1(sandbox, sConfig, sState) && sState.get().exists((data) => sConfig.isPartOf(sandbox, data, queryElem));
9581    const getState = (_sandbox, _sConfig, sState) => sState.get();
9582    const store = (sandbox, cssKey, attr, newValue) => {
9583        getRaw(sandbox.element, cssKey).fold(() => {
9584            remove$8(sandbox.element, attr);
9585        }, (v) => {
9586            set$9(sandbox.element, attr, v);
9587        });
9588        set$7(sandbox.element, cssKey, newValue);
9589    };
9590    const restore = (sandbox, cssKey, attr) => {
9591        getOpt(sandbox.element, attr).fold(() => remove$6(sandbox.element, cssKey), (oldValue) => set$7(sandbox.element, cssKey, oldValue));
9592    };
9593    const cloak = (sandbox, sConfig, _sState) => {
9594        const sink = sConfig.getAttachPoint(sandbox);
9595        // Use the positioning mode of the sink, so that it does not interfere with the sink's positioning
9596        // We add it here to stop it causing layout problems.
9597        set$7(sandbox.element, 'position', Positioning.getMode(sink));
9598        store(sandbox, 'visibility', sConfig.cloakVisibilityAttr, 'hidden');
9599    };
9600    const hasPosition = (element) => exists(['top', 'left', 'right', 'bottom'], (pos) => getRaw(element, pos).isSome());
9601    const decloak = (sandbox, sConfig, _sState) => {
9602        if (!hasPosition(sandbox.element)) {
9603            // If a position value was not added to the sandbox during cloaking, remove it
9604            // otherwise certain position values (absolute, relative) will impact the child that _was_ positioned
9605            remove$6(sandbox.element, 'position');
9606        }
9607        restore(sandbox, 'visibility', sConfig.cloakVisibilityAttr);
9608    };
9609
9610    var SandboxApis = /*#__PURE__*/Object.freeze({
9611        __proto__: null,
9612        cloak: cloak,
9613        decloak: decloak,
9614        open: open$1,
9615        openWhileCloaked: openWhileCloaked,
9616        close: close$1,
9617        isOpen: isOpen$1,
9618        isPartOf: isPartOf$1,
9619        getState: getState,
9620        setContent: setContent
9621    });
9622
9623    const events$6 = (sandboxConfig, sandboxState) => derive$2([
9624        run$1(sandboxClose(), (sandbox, _simulatedEvent) => {
9625            close$1(sandbox, sandboxConfig, sandboxState);
9626        })
9627    ]);
9628
9629    var ActiveSandbox = /*#__PURE__*/Object.freeze({
9630        __proto__: null,
9631        events: events$6
9632    });
9633
9634    var SandboxSchema = [
9635        onHandler('onOpen'),
9636        onHandler('onClose'),
9637        // Maybe this should be optional
9638        required$1('isPartOf'),
9639        required$1('getAttachPoint'),
9640        defaulted('cloakVisibilityAttr', 'data-precloak-visibility')
9641    ];
9642
9643    const init$6 = () => {
9644        const contents = value$2();
9645        const readState = constant$1('not-implemented');
9646        return nu$4({
9647            readState,
9648            isOpen: contents.isSet,
9649            clear: contents.clear,
9650            set: contents.set,
9651            get: contents.get
9652        });
9653    };
9654
9655    var SandboxState = /*#__PURE__*/Object.freeze({
9656        __proto__: null,
9657        init: init$6
9658    });
9659
9660    const Sandboxing = create$3({
9661        fields: SandboxSchema,
9662        name: 'sandboxing',
9663        active: ActiveSandbox,
9664        apis: SandboxApis,
9665        state: SandboxState
9666    });
9667
9668    const getAnimationRoot = (component, slideConfig) => slideConfig.getAnimationRoot.fold(() => component.element, (get) => get(component));
9669
9670    const getDimensionProperty = (slideConfig) => slideConfig.dimension.property;
9671    const getDimension = (slideConfig, elem) => slideConfig.dimension.getDimension(elem);
9672    const disableTransitions = (component, slideConfig) => {
9673        const root = getAnimationRoot(component, slideConfig);
9674        remove$2(root, [slideConfig.shrinkingClass, slideConfig.growingClass]);
9675    };
9676    const setShrunk = (component, slideConfig) => {
9677        remove$3(component.element, slideConfig.openClass);
9678        add$2(component.element, slideConfig.closedClass);
9679        set$7(component.element, getDimensionProperty(slideConfig), '0px');
9680        reflow(component.element);
9681    };
9682    const setGrown = (component, slideConfig) => {
9683        remove$3(component.element, slideConfig.closedClass);
9684        add$2(component.element, slideConfig.openClass);
9685        remove$6(component.element, getDimensionProperty(slideConfig));
9686    };
9687    const doImmediateShrink = (component, slideConfig, slideState, _calculatedSize) => {
9688        slideState.setCollapsed();
9689        // Force current dimension to begin transition
9690        set$7(component.element, getDimensionProperty(slideConfig), getDimension(slideConfig, component.element));
9691        // TINY-8710: we don't think reflow is required (as has been done elsewhere) as the animation is not needed
9692        disableTransitions(component, slideConfig);
9693        setShrunk(component, slideConfig);
9694        slideConfig.onStartShrink(component);
9695        slideConfig.onShrunk(component);
9696    };
9697    const doStartShrink = (component, slideConfig, slideState, calculatedSize) => {
9698        const size = calculatedSize.getOrThunk(() => getDimension(slideConfig, component.element));
9699        slideState.setCollapsed();
9700        // Force current dimension to begin transition
9701        set$7(component.element, getDimensionProperty(slideConfig), size);
9702        reflow(component.element);
9703        const root = getAnimationRoot(component, slideConfig);
9704        remove$3(root, slideConfig.growingClass);
9705        add$2(root, slideConfig.shrinkingClass); // enable transitions
9706        setShrunk(component, slideConfig);
9707        slideConfig.onStartShrink(component);
9708    };
9709    // A "smartShrink" will do an immediate shrink if no shrinking is scheduled to happen
9710    const doStartSmartShrink = (component, slideConfig, slideState) => {
9711        const size = getDimension(slideConfig, component.element);
9712        const shrinker = size === '0px' ? doImmediateShrink : doStartShrink;
9713        shrinker(component, slideConfig, slideState, Optional.some(size));
9714    };
9715    // Showing is complex due to the inability to transition to "auto".
9716    // We also can't cache the dimension as the parents may have resized since it was last shown.
9717    const doStartGrow = (component, slideConfig, slideState) => {
9718        // Start the growing animation styles
9719        const root = getAnimationRoot(component, slideConfig);
9720        // Record whether this is interrupting a shrink and its current size
9721        const wasShrinking = has(root, slideConfig.shrinkingClass);
9722        const beforeSize = getDimension(slideConfig, component.element);
9723        setGrown(component, slideConfig);
9724        const fullSize = getDimension(slideConfig, component.element);
9725        // If the grow is interrupting a shrink, use the size from before the grow as the start size
9726        // And reflow so that the animation works.
9727        const startPartialGrow = () => {
9728            set$7(component.element, getDimensionProperty(slideConfig), beforeSize);
9729            reflow(component.element);
9730        };
9731        // If the grow is not interrupting a shrink, start from 0 (shrunk)
9732        const startCompleteGrow = () => {
9733            setShrunk(component, slideConfig);
9734        };
9735        // Determine what the initial size for the grow operation should be.
9736        const setStartSize = wasShrinking ? startPartialGrow : startCompleteGrow;
9737        setStartSize();
9738        remove$3(root, slideConfig.shrinkingClass);
9739        add$2(root, slideConfig.growingClass);
9740        setGrown(component, slideConfig);
9741        set$7(component.element, getDimensionProperty(slideConfig), fullSize);
9742        slideState.setExpanded();
9743        slideConfig.onStartGrow(component);
9744    };
9745    const refresh$3 = (component, slideConfig, slideState) => {
9746        if (slideState.isExpanded()) {
9747            remove$6(component.element, getDimensionProperty(slideConfig));
9748            const fullSize = getDimension(slideConfig, component.element);
9749            set$7(component.element, getDimensionProperty(slideConfig), fullSize);
9750        }
9751    };
9752    const grow = (component, slideConfig, slideState) => {
9753        if (!slideState.isExpanded()) {
9754            doStartGrow(component, slideConfig, slideState);
9755        }
9756    };
9757    const shrink = (component, slideConfig, slideState) => {
9758        if (slideState.isExpanded()) {
9759            doStartSmartShrink(component, slideConfig, slideState);
9760        }
9761    };
9762    const immediateShrink = (component, slideConfig, slideState) => {
9763        if (slideState.isExpanded()) {
9764            doImmediateShrink(component, slideConfig, slideState);
9765        }
9766    };
9767    const hasGrown = (component, slideConfig, slideState) => slideState.isExpanded();
9768    const hasShrunk = (component, slideConfig, slideState) => slideState.isCollapsed();
9769    const isGrowing = (component, slideConfig, _slideState) => {
9770        const root = getAnimationRoot(component, slideConfig);
9771        return has(root, slideConfig.growingClass) === true;
9772    };
9773    const isShrinking = (component, slideConfig, _slideState) => {
9774        const root = getAnimationRoot(component, slideConfig);
9775        return has(root, slideConfig.shrinkingClass) === true;
9776    };
9777    const isTransitioning = (component, slideConfig, slideState) => isGrowing(component, slideConfig) || isShrinking(component, slideConfig);
9778    const toggleGrow = (component, slideConfig, slideState) => {
9779        const f = slideState.isExpanded() ? doStartSmartShrink : doStartGrow;
9780        f(component, slideConfig, slideState);
9781    };
9782    const immediateGrow = (component, slideConfig, slideState) => {
9783        if (!slideState.isExpanded()) {
9784            setGrown(component, slideConfig);
9785            set$7(component.element, getDimensionProperty(slideConfig), getDimension(slideConfig, component.element));
9786            // TINY-8710: we don't think reflow is required (as has been done elsewhere) as the animation is not needed
9787            // Keep disableTransition to handle the case where it's part way through transitioning
9788            disableTransitions(component, slideConfig);
9789            slideState.setExpanded();
9790            slideConfig.onStartGrow(component);
9791            slideConfig.onGrown(component);
9792        }
9793    };
9794
9795    var SlidingApis = /*#__PURE__*/Object.freeze({
9796        __proto__: null,
9797        refresh: refresh$3,
9798        grow: grow,
9799        shrink: shrink,
9800        immediateShrink: immediateShrink,
9801        hasGrown: hasGrown,
9802        hasShrunk: hasShrunk,
9803        isGrowing: isGrowing,
9804        isShrinking: isShrinking,
9805        isTransitioning: isTransitioning,
9806        toggleGrow: toggleGrow,
9807        disableTransitions: disableTransitions,
9808        immediateGrow: immediateGrow
9809    });
9810
9811    const exhibit$3 = (base, slideConfig, _slideState) => {
9812        const expanded = slideConfig.expanded;
9813        return expanded ? nu$2({
9814            classes: [slideConfig.openClass],
9815            styles: {}
9816        }) : nu$2({
9817            classes: [slideConfig.closedClass],
9818            styles: wrap(slideConfig.dimension.property, '0px')
9819        });
9820    };
9821    const events$5 = (slideConfig, slideState) => derive$2([
9822        runOnSource(transitionend(), (component, simulatedEvent) => {
9823            const raw = simulatedEvent.event.raw;
9824            // This will fire for all transitions, we're only interested in the dimension completion on source
9825            if (raw.propertyName === slideConfig.dimension.property) {
9826                disableTransitions(component, slideConfig); // disable transitions immediately (Safari animates the dimension removal below)
9827                if (slideState.isExpanded()) {
9828                    remove$6(component.element, slideConfig.dimension.property);
9829                } // when showing, remove the dimension so it is responsive
9830                const notify = slideState.isExpanded() ? slideConfig.onGrown : slideConfig.onShrunk;
9831                notify(component);
9832            }
9833        })
9834    ]);
9835
9836    var ActiveSliding = /*#__PURE__*/Object.freeze({
9837        __proto__: null,
9838        exhibit: exhibit$3,
9839        events: events$5
9840    });
9841
9842    var SlidingSchema = [
9843        required$1('closedClass'),
9844        required$1('openClass'),
9845        required$1('shrinkingClass'),
9846        required$1('growingClass'),
9847        // Element which shrinking and growing animations
9848        option$3('getAnimationRoot'),
9849        onHandler('onShrunk'),
9850        onHandler('onStartShrink'),
9851        onHandler('onGrown'),
9852        onHandler('onStartGrow'),
9853        defaulted('expanded', false),
9854        requiredOf('dimension', choose$1('property', {
9855            width: [
9856                output$1('property', 'width'),
9857                output$1('getDimension', (elem) => get$c(elem) + 'px')
9858            ],
9859            height: [
9860                output$1('property', 'height'),
9861                output$1('getDimension', (elem) => get$d(elem) + 'px')
9862            ]
9863        }))
9864    ];
9865
9866    const init$5 = (spec) => {
9867        const state = Cell(spec.expanded);
9868        const readState = () => 'expanded: ' + state.get();
9869        return nu$4({
9870            isExpanded: () => state.get() === true,
9871            isCollapsed: () => state.get() === false,
9872            setCollapsed: curry(state.set, false),
9873            setExpanded: curry(state.set, true),
9874            readState
9875        });
9876    };
9877
9878    var SlidingState = /*#__PURE__*/Object.freeze({
9879        __proto__: null,
9880        init: init$5
9881    });
9882
9883    const Sliding = create$3({
9884        fields: SlidingSchema,
9885        name: 'sliding',
9886        active: ActiveSliding,
9887        apis: SlidingApis,
9888        state: SlidingState
9889    });
9890
9891    const events$4 = (streamConfig, streamState) => {
9892        const streams = streamConfig.stream.streams;
9893        const processor = streams.setup(streamConfig, streamState);
9894        return derive$2([
9895            run$1(streamConfig.event, processor),
9896            runOnDetached(() => streamState.cancel())
9897        ].concat(streamConfig.cancelEvent.map((e) => [
9898            run$1(e, () => streamState.cancel())
9899        ]).getOr([])));
9900    };
9901
9902    var ActiveStreaming = /*#__PURE__*/Object.freeze({
9903        __proto__: null,
9904        events: events$4
9905    });
9906
9907    const throttle = (_config) => {
9908        const state = Cell(null);
9909        const readState = () => ({
9910            timer: state.get() !== null ? 'set' : 'unset'
9911        });
9912        const setTimer = (t) => {
9913            state.set(t);
9914        };
9915        const cancel = () => {
9916            const t = state.get();
9917            if (t !== null) {
9918                t.cancel();
9919            }
9920        };
9921        return nu$4({
9922            readState,
9923            setTimer,
9924            cancel
9925        });
9926    };
9927    const init$4 = (spec) => spec.stream.streams.state(spec);
9928
9929    var StreamingState = /*#__PURE__*/Object.freeze({
9930        __proto__: null,
9931        throttle: throttle,
9932        init: init$4
9933    });
9934
9935    const setup$e = (streamInfo, streamState) => {
9936        const sInfo = streamInfo.stream;
9937        const throttler = last(streamInfo.onStream, sInfo.delay);
9938        streamState.setTimer(throttler);
9939        return (component, simulatedEvent) => {
9940            throttler.throttle(component, simulatedEvent);
9941            if (sInfo.stopEvent) {
9942                simulatedEvent.stop();
9943            }
9944        };
9945    };
9946    var StreamingSchema = [
9947        requiredOf('stream', choose$1('mode', {
9948            throttle: [
9949                required$1('delay'),
9950                defaulted('stopEvent', true),
9951                output$1('streams', {
9952                    setup: setup$e,
9953                    state: throttle
9954                })
9955            ]
9956        })),
9957        defaulted('event', 'input'),
9958        option$3('cancelEvent'),
9959        onStrictHandler('onStream')
9960    ];
9961
9962    const Streaming = create$3({
9963        fields: StreamingSchema,
9964        name: 'streaming',
9965        active: ActiveStreaming,
9966        state: StreamingState
9967    });
9968
9969    const exhibit$2 = (base, tabConfig) => nu$2({
9970        attributes: wrapAll([
9971            { key: tabConfig.tabAttr, value: 'true' }
9972        ])
9973    });
9974
9975    var ActiveTabstopping = /*#__PURE__*/Object.freeze({
9976        __proto__: null,
9977        exhibit: exhibit$2
9978    });
9979
9980    var TabstopSchema = [
9981        defaulted('tabAttr', 'data-alloy-tabstop')
9982    ];
9983
9984    const Tabstopping = create$3({
9985        fields: TabstopSchema,
9986        name: 'tabstopping',
9987        active: ActiveTabstopping
9988    });
9989
9990    const updateAriaState = (component, toggleConfig, toggleState) => {
9991        const ariaInfo = toggleConfig.aria;
9992        ariaInfo.update(component, ariaInfo, toggleState.get());
9993    };
9994    const updateClass = (component, toggleConfig, toggleState) => {
9995        toggleConfig.toggleClass.each((toggleClass) => {
9996            if (toggleState.get()) {
9997                add$2(component.element, toggleClass);
9998            }
9999            else {
10000                remove$3(component.element, toggleClass);
10001            }
10002        });
10003    };
10004    const set = (component, toggleConfig, toggleState, state) => {
10005        const initialState = toggleState.get();
10006        toggleState.set(state);
10007        updateClass(component, toggleConfig, toggleState);
10008        updateAriaState(component, toggleConfig, toggleState);
10009        if (initialState !== state) {
10010            toggleConfig.onToggled(component, state);
10011        }
10012    };
10013    const toggle$1 = (component, toggleConfig, toggleState) => {
10014        set(component, toggleConfig, toggleState, !toggleState.get());
10015    };
10016    const on = (component, toggleConfig, toggleState) => {
10017        set(component, toggleConfig, toggleState, true);
10018    };
10019    const off = (component, toggleConfig, toggleState) => {
10020        set(component, toggleConfig, toggleState, false);
10021    };
10022    const isOn = (component, toggleConfig, toggleState) => toggleState.get();
10023    const onLoad = (component, toggleConfig, toggleState) => {
10024        // There used to be a bit of code in here that would only overwrite
10025        // the attribute if it didn't have a current value. I can't remember
10026        // what case that was for, so I'm removing it until it is required.
10027        set(component, toggleConfig, toggleState, toggleConfig.selected);
10028    };
10029
10030    var ToggleApis = /*#__PURE__*/Object.freeze({
10031        __proto__: null,
10032        onLoad: onLoad,
10033        toggle: toggle$1,
10034        isOn: isOn,
10035        on: on,
10036        off: off,
10037        set: set
10038    });
10039
10040    const exhibit$1 = () => nu$2({});
10041    const events$3 = (toggleConfig, toggleState) => {
10042        const execute = executeEvent(toggleConfig, toggleState, toggle$1);
10043        const load = loadEvent(toggleConfig, toggleState, onLoad);
10044        return derive$2(flatten([
10045            toggleConfig.toggleOnExecute ? [execute] : [],
10046            [load]
10047        ]));
10048    };
10049
10050    var ActiveToggle = /*#__PURE__*/Object.freeze({
10051        __proto__: null,
10052        exhibit: exhibit$1,
10053        events: events$3
10054    });
10055
10056    const updatePressed = (component, ariaInfo, status) => {
10057        set$9(component.element, 'aria-pressed', status);
10058        if (ariaInfo.syncWithExpanded) {
10059            updateExpanded(component, ariaInfo, status);
10060        }
10061    };
10062    const updateSelected = (component, ariaInfo, status) => {
10063        set$9(component.element, 'aria-selected', status);
10064    };
10065    const updateChecked = (component, ariaInfo, status) => {
10066        set$9(component.element, 'aria-checked', status);
10067    };
10068    const updateExpanded = (component, ariaInfo, status) => {
10069        set$9(component.element, 'aria-expanded', status);
10070    };
10071
10072    var ToggleSchema = [
10073        defaulted('selected', false),
10074        option$3('toggleClass'),
10075        defaulted('toggleOnExecute', true),
10076        onHandler('onToggled'),
10077        defaultedOf('aria', {
10078            mode: 'none'
10079        }, choose$1('mode', {
10080            pressed: [
10081                defaulted('syncWithExpanded', false),
10082                output$1('update', updatePressed)
10083            ],
10084            checked: [
10085                output$1('update', updateChecked)
10086            ],
10087            expanded: [
10088                output$1('update', updateExpanded)
10089            ],
10090            selected: [
10091                output$1('update', updateSelected)
10092            ],
10093            none: [
10094                output$1('update', noop)
10095            ]
10096        }))
10097    ];
10098
10099    const Toggling = create$3({
10100        fields: ToggleSchema,
10101        name: 'toggling',
10102        active: ActiveToggle,
10103        apis: ToggleApis,
10104        state: SetupBehaviourCellState(false)
10105    });
10106
10107    const ExclusivityChannel = generate$6('tooltip.exclusive');
10108    const ShowTooltipEvent = generate$6('tooltip.show');
10109    const HideTooltipEvent = generate$6('tooltip.hide');
10110    const ImmediateHideTooltipEvent = generate$6('tooltip.immediateHide');
10111    const ImmediateShowTooltipEvent = generate$6('tooltip.immediateShow');
10112
10113    const hideAllExclusive = (component, _tConfig, _tState) => {
10114        component.getSystem().broadcastOn([ExclusivityChannel], {});
10115    };
10116    const setComponents = (_component, _tConfig, tState, specs) => {
10117        tState.getTooltip().each((tooltip) => {
10118            if (tooltip.getSystem().isConnected()) {
10119                Replacing.set(tooltip, specs);
10120            }
10121        });
10122    };
10123    const isEnabled = (_component, _tConfig, tState) => tState.isEnabled();
10124    const setEnabled = (_component, _tConfig, tState, enabled) => tState.setEnabled(enabled);
10125    const immediateOpenClose = (component, _tConfig, _tState, open) => emit(component, open ? ImmediateShowTooltipEvent : ImmediateHideTooltipEvent);
10126
10127    var TooltippingApis = /*#__PURE__*/Object.freeze({
10128        __proto__: null,
10129        hideAllExclusive: hideAllExclusive,
10130        immediateOpenClose: immediateOpenClose,
10131        isEnabled: isEnabled,
10132        setComponents: setComponents,
10133        setEnabled: setEnabled
10134    });
10135
10136    const events$2 = (tooltipConfig, state) => {
10137        const hide = (comp) => {
10138            state.getTooltip().each((p) => {
10139                if (p.getSystem().isConnected()) {
10140                    detach(p);
10141                    tooltipConfig.onHide(comp, p);
10142                    state.clearTooltip();
10143                }
10144            });
10145            state.clearTimer();
10146        };
10147        const show = (comp) => {
10148            if (!state.isShowing() && state.isEnabled()) {
10149                hideAllExclusive(comp);
10150                const sink = tooltipConfig.lazySink(comp).getOrDie();
10151                const popup = comp.getSystem().build({
10152                    dom: tooltipConfig.tooltipDom,
10153                    components: tooltipConfig.tooltipComponents,
10154                    events: derive$2(tooltipConfig.mode === 'normal'
10155                        ? [
10156                            run$1(mouseover(), (_) => {
10157                                emit(comp, ShowTooltipEvent);
10158                            }),
10159                            run$1(mouseout(), (_) => {
10160                                emit(comp, HideTooltipEvent);
10161                            })
10162                        ]
10163                        : []),
10164                    behaviours: derive$1([
10165                        Replacing.config({})
10166                    ])
10167                });
10168                state.setTooltip(popup);
10169                attach(sink, popup);
10170                tooltipConfig.onShow(comp, popup);
10171                Positioning.position(sink, popup, { anchor: tooltipConfig.anchor(comp) });
10172            }
10173        };
10174        const reposition = (comp) => {
10175            state.getTooltip().each((tooltip) => {
10176                const sink = tooltipConfig.lazySink(comp).getOrDie();
10177                Positioning.position(sink, tooltip, { anchor: tooltipConfig.anchor(comp) });
10178            });
10179        };
10180        const getEvents = () => {
10181            switch (tooltipConfig.mode) {
10182                case 'normal':
10183                    return [
10184                        run$1(focusin(), (comp) => {
10185                            emit(comp, ImmediateShowTooltipEvent);
10186                        }),
10187                        run$1(postBlur(), (comp) => {
10188                            emit(comp, ImmediateHideTooltipEvent);
10189                        }),
10190                        run$1(mouseover(), (comp) => {
10191                            emit(comp, ShowTooltipEvent);
10192                        }),
10193                        run$1(mouseout(), (comp) => {
10194                            emit(comp, HideTooltipEvent);
10195                        })
10196                    ];
10197                case 'follow-highlight':
10198                    return [
10199                        run$1(highlight$1(), (comp, _se) => {
10200                            emit(comp, ShowTooltipEvent);
10201                        }),
10202                        run$1(dehighlight$1(), (comp) => {
10203                            emit(comp, HideTooltipEvent);
10204                        })
10205                    ];
10206                case 'children-normal':
10207                    return [
10208                        run$1(focusin(), (comp, se) => {
10209                            search(comp.element).each((_) => {
10210                                if (is(se.event.target, '[data-mce-tooltip]')) {
10211                                    state.getTooltip().fold(() => {
10212                                        emit(comp, ImmediateShowTooltipEvent);
10213                                    }, (tooltip) => {
10214                                        if (state.isShowing()) {
10215                                            tooltipConfig.onShow(comp, tooltip);
10216                                            reposition(comp);
10217                                        }
10218                                    });
10219                                }
10220                            });
10221                        }),
10222                        run$1(postBlur(), (comp) => {
10223                            search(comp.element).fold(() => {
10224                                emit(comp, ImmediateHideTooltipEvent);
10225                            }, noop);
10226                        }),
10227                        run$1(mouseover(), (comp) => {
10228                            descendant(comp.element, '[data-mce-tooltip]:hover').each((_) => {
10229                                state.getTooltip().fold(() => {
10230                                    emit(comp, ShowTooltipEvent);
10231                                }, (tooltip) => {
10232                                    if (state.isShowing()) {
10233                                        tooltipConfig.onShow(comp, tooltip);
10234                                        reposition(comp);
10235                                    }
10236                                });
10237                            });
10238                        }),
10239                        run$1(mouseout(), (comp) => {
10240                            descendant(comp.element, '[data-mce-tooltip]:hover').fold(() => {
10241                                emit(comp, HideTooltipEvent);
10242                            }, noop);
10243                        }),
10244                    ];
10245                default:
10246                    return [
10247                        run$1(focusin(), (comp, se) => {
10248                            search(comp.element).each((_) => {
10249                                if (is(se.event.target, '[data-mce-tooltip]')) {
10250                                    state.getTooltip().fold(() => {
10251                                        emit(comp, ImmediateShowTooltipEvent);
10252                                    }, (tooltip) => {
10253                                        if (state.isShowing()) {
10254                                            tooltipConfig.onShow(comp, tooltip);
10255                                            reposition(comp);
10256    
10256                                    }
10257                                    });
10258                                }
10259                            });
10260                        }),
10261                        run$1(postBlur(), (comp) => {
10262                            search(comp.element).fold(() => {
10263                                emit(comp, ImmediateHideTooltipEvent);
10264                            }, noop);
10265                        }),
10266                    ];
10267            }
10268        };
10269        return derive$2(flatten([
10270            [
10271                runOnInit((component) => {
10272                    tooltipConfig.onSetup(component);
10273                }),
10274                run$1(ShowTooltipEvent, (comp) => {
10275                    state.resetTimer(() => {
10276                        show(comp);
10277                    }, tooltipConfig.delayForShow());
10278                }),
10279                run$1(HideTooltipEvent, (comp) => {
10280                    state.resetTimer(() => {
10281                        hide(comp);
10282                    }, tooltipConfig.delayForHide());
10283                }),
10284                run$1(ImmediateShowTooltipEvent, (comp) => {
10285                    state.resetTimer(() => {
10286                        show(comp);
10287                    }, 0);
10288                }),
10289                run$1(ImmediateHideTooltipEvent, (comp) => {
10290                    state.resetTimer(() => {
10291                        hide(comp);
10292                    }, 0);
10293                }),
10294                run$1(receive(), (comp, message) => {
10295                    // TODO: Think about the types for this, or find a better way for this
10296                    // to rely on receiving.
10297                    const receivingData = message;
10298                    if (!receivingData.universal) {
10299                        if (contains$2(receivingData.channels, ExclusivityChannel) || contains$2(receivingData.channels, closeTooltips())) {
10300                            if (receivingData.data.closedTooltip && state.isShowing()) {
10301                                receivingData.data.closedTooltip();
10302                            }
10303                            hide(comp);
10304                        }
10305                    }
10306                }),
10307                runOnDetached((comp) => {
10308                    hide(comp);
10309                })
10310            ],
10311            (getEvents())
10312        ]));
10313    };
10314
10315    var ActiveTooltipping = /*#__PURE__*/Object.freeze({
10316        __proto__: null,
10317        events: events$2
10318    });
10319
10320    var TooltippingSchema = [
10321        required$1('lazySink'),
10322        required$1('tooltipDom'),
10323        defaulted('exclusive', true),
10324        defaulted('tooltipComponents', []),
10325        defaultedFunction('delayForShow', constant$1(300)),
10326        defaultedFunction('delayForHide', constant$1(100)),
10327        defaultedFunction('onSetup', noop),
10328        defaultedStringEnum('mode', 'normal', ['normal', 'follow-highlight', 'children-keyboard-focus', 'children-normal']),
10329        defaulted('anchor', (comp) => ({
10330            type: 'hotspot',
10331            hotspot: comp,
10332            layouts: {
10333                onLtr: constant$1([south$2, north$2, southeast$2, northeast$2, southwest$2, northwest$2]),
10334                onRtl: constant$1([south$2, north$2, southeast$2, northeast$2, southwest$2, northwest$2])
10335            },
10336            bubble: nu$6(0, -2, {}),
10337        })),
10338        onHandler('onHide'),
10339        onHandler('onShow'),
10340    ];
10341
10342    const init$3 = () => {
10343        const enabled = Cell(true);
10344        const timer = value$2();
10345        const popup = value$2();
10346        const clearTimer = () => {
10347            timer.on(clearTimeout);
10348        };
10349        const resetTimer = (f, delay) => {
10350            clearTimer();
10351            timer.set(setTimeout(f, delay));
10352        };
10353        const readState = constant$1('not-implemented');
10354        return nu$4({
10355            getTooltip: popup.get,
10356            isShowing: popup.isSet,
10357            setTooltip: popup.set,
10358            clearTooltip: popup.clear,
10359            clearTimer,
10360            resetTimer,
10361            readState,
10362            isEnabled: () => enabled.get(),
10363            setEnabled: (setToEnabled) => enabled.set(setToEnabled)
10364        });
10365    };
10366
10367    var TooltippingState = /*#__PURE__*/Object.freeze({
10368        __proto__: null,
10369        init: init$3
10370    });
10371
10372    const Tooltipping = create$3({
10373        fields: TooltippingSchema,
10374        name: 'tooltipping',
10375        active: ActiveTooltipping,
10376        state: TooltippingState,
10377        apis: TooltippingApis
10378    });
10379
10380    const exhibit = () => nu$2({
10381        styles: {
vendor: 11,719 bytes, lines 10382-10702
10382            '-webkit-user-select': 'none',
10383            'user-select': 'none',
10384            '-ms-user-select': 'none',
10385            '-moz-user-select': '-moz-none'
10386        },
10387        attributes: {
10388            unselectable: 'on'
10389        }
10390    });
10391    const events$1 = () => derive$2([
10392        abort(selectstart(), always)
10393    ]);
10394
10395    var ActiveUnselecting = /*#__PURE__*/Object.freeze({
10396        __proto__: null,
10397        events: events$1,
10398        exhibit: exhibit
10399    });
10400
10401    const Unselecting = create$3({
10402        fields: [],
10403        name: 'unselecting',
10404        active: ActiveUnselecting
10405    });
10406
10407    const getAttrs = (elem) => {
10408        const attributes = elem.dom.attributes !== undefined ? elem.dom.attributes : [];
10409        return foldl(attributes, (b, attr) => {
10410            // Make class go through the class path. Do not list it as an attribute.
10411            if (attr.name === 'class') {
10412                return b;
10413            }
10414            else {
10415                return { ...b, [attr.name]: attr.value };
10416            }
10417        }, {});
10418    };
10419    const getClasses = (elem) => Array.prototype.slice.call(elem.dom.classList, 0);
10420    const fromHtml = (html) => {
10421        const elem = SugarElement.fromHtml(html);
10422        const children$1 = children(elem);
10423        const attrs = getAttrs(elem);
10424        const classes = getClasses(elem);
10425        const contents = children$1.length === 0 ? {} : { innerHtml: get$f(elem) };
10426        return {
10427            tag: name$3(elem),
10428            classes,
10429            attributes: attrs,
10430            ...contents
10431        };
10432    };
10433
10434    const record = (spec) => {
10435        const uid = isSketchSpec(spec) && hasNonNullableKey(spec, 'uid') ? spec.uid : generate$4('memento');
10436        const get = (anyInSystem) => anyInSystem.getSystem().getByUid(uid).getOrDie();
10437        const getOpt = (anyInSystem) => anyInSystem.getSystem().getByUid(uid).toOptional();
10438        const asSpec = () => ({
10439            ...spec,
10440            uid
10441        });
10442        return {
10443            get,
10444            getOpt,
10445            asSpec
10446        };
10447    };
10448
10449    // TODO: ^ rename the parts/ api to composites, it will break mobile alloy now if we do
10450    const parts$g = AlloyParts;
10451    const partType$1 = PartType;
10452
10453    const fromSource = (event, source) => {
10454        const stopper = Cell(false);
10455        const cutter = Cell(false);
10456        const stop = () => {
10457            stopper.set(true);
10458        };
10459        const cut = () => {
10460            cutter.set(true);
10461        };
10462        return {
10463            stop,
10464            cut,
10465            isStopped: stopper.get,
10466            isCut: cutter.get,
10467            event,
10468            // Used only for tiered menu at the moment. It is an element, not a component
10469            setSource: source.set,
10470            getSource: source.get
10471        };
10472    };
10473    // Events that come from outside of the alloy root (e.g. window scroll)
10474    const fromExternal = (event) => {
10475        const stopper = Cell(false);
10476        const stop = () => {
10477            stopper.set(true);
10478        };
10479        return {
10480            stop,
10481            cut: noop, // cutting has no meaning for a broadcasted event
10482            isStopped: stopper.get,
10483            isCut: never,
10484            event,
10485            // Nor do targets really
10486            setSource: die('Cannot set source of a broadcasted event'),
10487            getSource: die('Cannot get source of a broadcasted event')
10488        };
10489    };
10490
10491    const isDangerous = (event) => {
10492        // Will trigger the Back button in the browser
10493        const keyEv = event.raw;
10494        return keyEv.which === BACKSPACE[0] && !contains$2(['input', 'textarea'], name$3(event.target)) && !closest$1(event.target, '[contenteditable="true"]');
10495    };
10496    const setup$d = (container, rawSettings) => {
10497        const settings = {
10498            stopBackspace: true,
10499            ...rawSettings
10500        };
10501        const pointerEvents = [
10502            'touchstart',
10503            'touchmove',
10504            'touchend',
10505            'touchcancel',
10506            'gesturestart',
10507            'mousedown',
10508            'mouseup',
10509            'mouseover',
10510            'mousemove',
10511            'mouseout',
10512            'click',
10513            'pointerdown',
10514            'pointermove',
10515            'pointerup',
10516            'lostpointercapture'
10517        ];
10518        const tapEvent = monitor(settings);
10519        // These events are just passed through ... no additional processing
10520        const simpleEvents = map$2(pointerEvents.concat([
10521            'selectstart',
10522            'input',
10523            'contextmenu',
10524            'change',
10525            'transitionend',
10526            'transitioncancel',
10527            // Test the drag events
10528            'drag',
10529            'dragstart',
10530            'dragend',
10531            'dragenter',
10532            'dragleave',
10533            'dragover',
10534            'drop',
10535            'keyup'
10536        ]), (type) => bind$1(container, type, (event) => {
10537            tapEvent.fireIfReady(event, type).each((tapStopped) => {
10538                if (tapStopped) {
10539                    event.kill();
10540                }
10541            });
10542            const stopped = settings.triggerEvent(type, event);
10543            if (stopped) {
10544                event.kill();
10545            }
10546        }));
10547        const pasteTimeout = value$2();
10548        const onPaste = bind$1(container, 'paste', (event) => {
10549            tapEvent.fireIfReady(event, 'paste').each((tapStopped) => {
10550                if (tapStopped) {
10551                    event.kill();
10552                }
10553            });
10554            const stopped = settings.triggerEvent('paste', event);
10555            if (stopped) {
10556                event.kill();
10557            }
10558            pasteTimeout.set(setTimeout(() => {
10559                settings.triggerEvent(postPaste(), event);
10560            }, 0));
10561        });
10562        const onKeydown = bind$1(container, 'keydown', (event) => {
10563            // Prevent default of backspace when not in input fields.
10564            const stopped = settings.triggerEvent('keydown', event);
10565            if (stopped) {
10566                event.kill();
10567            }
10568            else if (settings.stopBackspace && isDangerous(event)) {
10569                event.prevent();
10570            }
10571        });
10572        const onFocusIn = bind$1(container, 'focusin', (event) => {
10573            const stopped = settings.triggerEvent('focusin', event);
10574            if (stopped) {
10575                event.kill();
10576            }
10577        });
10578        const focusoutTimeout = value$2();
10579        const onFocusOut = bind$1(container, 'focusout', (event) => {
10580            const stopped = settings.triggerEvent('focusout', event);
10581            if (stopped) {
10582                event.kill();
10583            }
10584            // INVESTIGATE: Come up with a better way of doing this. Related target can be used, but not on FF.
10585            // It allows the active element to change before firing the blur that we will listen to
10586            // for things like closing popups
10587            focusoutTimeout.set(setTimeout(() => {
10588                settings.triggerEvent(postBlur(), event);
10589            }, 0));
10590        });
10591        const unbind = () => {
10592            each$1(simpleEvents, (e) => {
10593                e.unbind();
10594            });
10595            onKeydown.unbind();
10596            onFocusIn.unbind();
10597            onFocusOut.unbind();
10598            onPaste.unbind();
10599            pasteTimeout.on(clearTimeout);
10600            focusoutTimeout.on(clearTimeout);
10601        };
10602        return {
10603            unbind
10604        };
10605    };
10606
10607    const derive = (rawEvent, rawTarget) => {
10608        const source = get$h(rawEvent, 'target').getOr(rawTarget);
10609        return Cell(source);
10610    };
10611
10612    const adt = Adt.generate([
10613        { stopped: [] },
10614        { resume: ['element'] },
10615        { complete: [] }
10616    ]);
10617    const doTriggerHandler = (lookup, eventType, rawEvent, target, source, logger) => {
10618        const handler = lookup(eventType, target);
10619        const simulatedEvent = fromSource(rawEvent, source);
10620        return handler.fold(() => {
10621            // No handler, so complete.
10622            logger.logEventNoHandlers(eventType, target);
10623            return adt.complete();
10624        }, (handlerInfo) => {
10625            const descHandler = handlerInfo.descHandler;
10626            const eventHandler = getCurried(descHandler);
10627            eventHandler(simulatedEvent);
10628            // Now, check if the event was stopped.
10629            if (simulatedEvent.isStopped()) {
10630                logger.logEventStopped(eventType, handlerInfo.element, descHandler.purpose);
10631                return adt.stopped();
10632            }
10633            else if (simulatedEvent.isCut()) {
10634                logger.logEventCut(eventType, handlerInfo.element, descHandler.purpose);
10635                return adt.complete();
10636            }
10637            else {
10638                return parent(handlerInfo.element).fold(() => {
10639                    logger.logNoParent(eventType, handlerInfo.element, descHandler.purpose);
10640                    // No parent, so complete.
10641                    return adt.complete();
10642                }, (parent) => {
10643                    logger.logEventResponse(eventType, handlerInfo.element, descHandler.purpose);
10644                    // Resume at parent
10645                    return adt.resume(parent);
10646                });
10647            }
10648        });
10649    };
10650    const doTriggerOnUntilStopped = (lookup, eventType, rawEvent, rawTarget, source, logger) => doTriggerHandler(lookup, eventType, rawEvent, rawTarget, source, logger).fold(
10651    // stopped.
10652    always, 
10653    // Go again.
10654    (parent) => doTriggerOnUntilStopped(lookup, eventType, rawEvent, parent, source, logger), 
10655    // completed
10656    never);
10657    const triggerHandler = (lookup, eventType, rawEvent, target, logger) => {
10658        const source = derive(rawEvent, target);
10659        return doTriggerHandler(lookup, eventType, rawEvent, target, source, logger);
10660    };
10661    const broadcast = (listeners, rawEvent, _logger) => {
10662        const simulatedEvent = fromExternal(rawEvent);
10663        each$1(listeners, (listener) => {
10664            const descHandler = listener.descHandler;
10665            const handler = getCurried(descHandler);
10666            handler(simulatedEvent);
10667        });
10668        return simulatedEvent.isStopped();
10669    };
10670    const triggerUntilStopped = (lookup, eventType, rawEvent, logger) => triggerOnUntilStopped(lookup, eventType, rawEvent, rawEvent.target, logger);
10671    const triggerOnUntilStopped = (lookup, eventType, rawEvent, rawTarget, logger) => {
10672        const source = derive(rawEvent, rawTarget);
10673        return doTriggerOnUntilStopped(lookup, eventType, rawEvent, rawTarget, source, logger);
10674    };
10675
10676    const eventHandler = (element, descHandler) => ({
10677        element,
10678        descHandler
10679    });
10680    const broadcastHandler = (id, handler) => ({
10681        id,
10682        descHandler: handler
10683    });
10684    const EventRegistry = () => {
10685        const registry = {};
10686        const registerId = (extraArgs, id, events) => {
10687            each(events, (v, k) => {
10688                const handlers = registry[k] !== undefined ? registry[k] : {};
10689                handlers[id] = curryArgs(v, extraArgs);
10690                registry[k] = handlers;
10691            });
10692        };
10693        const findHandler = (handlers, elem) => read(elem)
10694            .bind((id) => get$h(handlers, id))
10695            .map((descHandler) => eventHandler(elem, descHandler));
10696        // Given just the event type, find all handlers regardless of element
10697        const filterByType = (type) => get$h(registry, type)
10698            .map((handlers) => mapToArray(handlers, (f, id) => broadcastHandler(id, f)))
10699            .getOr([]);
10700        // Given event type, and element, find the handler.
10701        const find = (isAboveRoot, type, target) => get$h(registry, type)
10702            .bind((handlers) => closest(target, (elem) =>
10702 findHandler(handlers, elem), isAboveRoot));
10703        const unregisterId = (id) => {
10704            // INVESTIGATE: Find a better way than mutation if we can.
10705            each(registry, (handlersById, _eventName) => {
10706                if (has$2(handlersById, id)) {
10707                    delete handlersById[id];
10708                }
10709            });
10710        };
10711        return {
10712            registerId,
10713            unregisterId,
10714            filterByType,
10715            find
10716        };
10717    };
10718
10719    const Registry = () => {
10720        const events = EventRegistry();
10721        // An index of uid -> built components
10722        const components = {};
10723        const readOrTag = (component) => {
10724            const elem = component.element;
10725            return read(elem).getOrThunk(() => 
10726            // No existing tag, so add one.
10727            write('uid-', component.element));
10728        };
10729        const failOnDuplicate = (component, tagId) => {
10730            const conflict = components[tagId];
10731            if (conflict === component) {
10732                unregister(component);
10733            }
10734            else {
10735                throw new Error('The tagId "' + tagId + '" is already used by: ' + element(conflict.element) + '\nCannot use it for: ' + element(component.element) + '\n' +
10736                    'The conflicting element is' + (inBody(conflict.element) ? ' ' : ' not ') + 'already in the DOM');
10737            }
10738        };
10739        const register = (component) => {
10740            const tagId = readOrTag(component);
10741            if (hasNonNullableKey(components, tagId)) {
10742                failOnDuplicate(component, tagId);
10743            }
10744            // Component is passed through an an extra argument to all events
10745            const extraArgs = [component];
10746            events.registerId(extraArgs, tagId, component.events);
10747            components[tagId] = component;
10748        };
10749        const unregister = (component) => {
10750            read(component.element).each((tagId) => {
10751                delete components[tagId];
10752                events.unregisterId(tagId);
10753            });
10754        };
10755        const filter = (type) => events.filterByType(type);
10756        const find = (isAboveRoot, type, target) => events.find(isAboveRoot, type, target);
10757        const getById = (id) => get$h(components, id);
10758        return {
10759            find,
10760            filter,
10761            register,
10762            unregister,
10763            getById
10764        };
10765    };
10766
10767    const takeover = (root) => {
10768        const isAboveRoot = (el) => parent(root.element).fold(always, (parent) => eq(el, parent));
10769        const registry = Registry();
10770        const lookup = (eventName, target) => registry.find(isAboveRoot, eventName, target);
10771        const domEvents = setup$d(root.element, {
10772            triggerEvent: (eventName, event) => {
10773                return monitorEvent(eventName, event.target, (logger) => triggerUntilStopped(lookup, eventName, event, logger));
10774            }
10775        });
10776        const systemApi = {
10777            // This is a real system
10778            debugInfo: constant$1('real'),
10779            triggerEvent: (eventName, target, data) => {
10780                monitorEvent(eventName, target, (logger) => 
10781                // The return value is not used because this is a fake event.
10782                triggerOnUntilStopped(lookup, eventName, data, target, logger));
10783            },
10784            triggerFocus: (target, originator) => {
10785                read(target).fold(() => {
10786                    // When the target is not within the alloy system, dispatch a normal focus event.
10787                    focus$4(target);
10788                }, (_alloyId) => {
10789                    monitorEvent(focus$3(), target, (logger) => {
10790                        // NOTE: This will stop at first handler.
10791                        triggerHandler(lookup, focus$3(), {
10792                            // originator is used by the default events to ensure that focus doesn't
10793                            // get called infinitely
10794                            originator,
10795                            kill: noop,
10796                            prevent: noop,
10797                            target
10798                        }, target, logger);
10799                        return false;
10800                    });
10801                });
10802            },
10803            triggerEscape: (comp, simulatedEvent) => {
10804                systemApi.triggerEvent('keydown', comp.element, simulatedEvent.event);
10805            },
10806            getByUid: (uid) => {
10807                return getByUid(uid);
10808            },
10809            getByDom: (elem) => {
10810                return getByDom(elem);
10811            },
10812            build: build$1,
10813            buildOrPatch: buildOrPatch,
10814            addToGui: (c) => {
10815                add(c);
10816            },
10817            removeFromGui: (c) => {
10818                remove(c);
10819            },
10820            addToWorld: (c) => {
10821                addToWorld(c);
10822            },
10823            removeFromWorld: (c) => {
10824                removeFromWorld(c);
10825            },
10826            broadcast: (message) => {
10827                broadcast$1(message);
10828            },
10829            broadcastOn: (channels, message) => {
10830                broadcastOn(channels, message);
10831            },
10832            broadcastEvent: (eventName, event) => {
10833                broadcastEvent(eventName, event);
10834            },
10835            isConnected: always
10836        };
10837        const addToWorld = (component) => {
10838            component.connect(systemApi);
10839            if (!isText(component.element)) {
10840                registry.register(component);
10841                each$1(component.components(), addToWorld);
10842                systemApi.triggerEvent(systemInit(), component.element, { target: component.element });
10843            }
10844        };
10845        const removeFromWorld = (component) => {
10846            if (!isText(component.element)) {
10847                each$1(component.components(), removeFromWorld);
10848                registry.unregister(component);
10849            }
10850            component.disconnect();
10851        };
10852        const add = (component) => {
10853            attach(root, component);
10854        };
10855        const remove = (component) => {
10856            detach(component);
10857        };
10858        const destroy = () => {
10859            // INVESTIGATE: something with registry?
10860            domEvents.unbind();
10861            remove$7(root.element);
10862        };
10863        const broadcastData = (data) => {
10864            const receivers = registry.filter(receive());
10865            each$1(receivers, (receiver) => {
10866                const descHandler = receiver.descHandler;
10867                const handler = getCurried(descHandler);
10868                handler(data);
10869            });
10870        };
10871        const broadcast$1 = (message) => {
10872            broadcastData({
10873                universal: true,
10874                data: message
10875            });
10876        };
10877        const broadcastOn = (channels, message) => {
10878            broadcastData({
10879                universal: false,
10880                channels,
10881                data: message
10882            });
10883        };
10884        // This doesn't follow usual DOM bubbling. It will just dispatch on all
10885        // targets that have the event. It is the general case of the more specialised
10886        // "message". "messages" may actually just go away. This is used for things
10887        // like window scroll.
10888        const broadcastEvent = (eventName, event) => {
10889            const listeners = registry.filter(eventName);
10890            return broadcast(listeners, event);
10891        };
10892        const getByUid = (uid) => registry.getById(uid).fold(() => Result.error(new Error('Could not find component with uid: "' + uid + '" in system.')), Result.value);
10893        const getByDom = (elem) => {
10894            const uid = read(elem).getOr('not found');
10895            return getByUid(uid);
10896        };
10897        addToWorld(root);
10898        return {
10899            root,
10900            element: root.element,
10901            destroy,
10902            add,
10903            remove,
10904            getByUid,
10905            getByDom,
10906            addToWorld,
10907            removeFromWorld,
10908            broadcast: broadcast$1,
10909            broadcastOn,
10910            broadcastEvent
10911        };
10912    };
10913
10914    const pointerEvents = () => {
10915        const onClick = (component, simulatedEvent) => {
10916            simulatedEvent.stop();
10917            emitExecute(component);
10918        };
10919        return [
10920            // Trigger execute when clicked
10921            run$1(click(), onClick),
10922            run$1(tap(), onClick),
10923            // Other mouse down listeners above this one should not get mousedown behaviour (like dragging)
10924            cutter(touchstart()),
10925            cutter(mousedown())
10926        ];
10927    };
10928    const events = (optAction) => {
10929        const executeHandler = (action) => runOnExecute$1((component, simulatedEvent) => {
10930            action(component);
10931            simulatedEvent.stop();
10932        });
10933        return derive$2(flatten([
10934            // Only listen to execute if it is supplied
10935            optAction.map(executeHandler).toArray(),
10936            pointerEvents()
10937        ]));
10938    };
10939
vendor: 5,576 bytes, lines 10940-11074
10940    const factory$m = (detail) => {
10941        const events$1 = events(detail.action);
10942        const tag = detail.dom.tag;
10943        const lookupAttr = (attr) => get$h(detail.dom, 'attributes').bind((attrs) => get$h(attrs, attr));
10944        // Button tags should not have a default role of button, and only buttons should
10945        // get a type of button.
10946        const getModAttributes = () => {
10947            if (tag === 'button') {
10948                // Default to type button, unless specified otherwise
10949                const type = lookupAttr('type').getOr('button');
10950                // Only use a role if it is specified
10951                const roleAttrs = lookupAttr('role').map((role) => ({ role })).getOr({});
10952                return {
10953                    type,
10954                    ...roleAttrs
10955                };
10956            }
10957            else {
10958                // We are not a button, so type is irrelevant (unless specified)
10959                // Default role to button
10960                const role = detail.role.getOr(lookupAttr('role').getOr('button'));
10961                return { role };
10962            }
10963        };
10964        return {
10965            uid: detail.uid,
10966            dom: detail.dom,
10967            components: detail.components,
10968            events: events$1,
10969            behaviours: SketchBehaviours.augment(detail.buttonBehaviours, [
10970                Focusing.config({}),
10971                Keying.config({
10972                    mode: 'execution',
10973                    // Note execution will capture keyup when the focus is on the button
10974                    // on Firefox, because otherwise it will fire a click event and double
10975                    // up on the action
10976                    useSpace: true,
10977                    useEnter: true
10978                })
10979            ]),
10980            domModification: {
10981                attributes: getModAttributes()
10982            },
10983            eventOrder: detail.eventOrder
10984        };
10985    };
10986    const Button = single({
10987        name: 'Button',
10988        factory: factory$m,
10989        configFields: [
10990            defaulted('uid', undefined),
10991            required$1('dom'),
10992            defaulted('components', []),
10993            SketchBehaviours.field('buttonBehaviours', [Focusing, Keying]),
10994            option$3('action'),
10995            option$3('role'),
10996            defaulted('eventOrder', {})
10997        ]
10998    });
10999
11000    const schema$m = constant$1([
11001        defaulted('shell', false),
11002        required$1('makeItem'),
11003        defaulted('setupItem', noop),
11004        SketchBehaviours.field('listBehaviours', [Replacing])
11005    ]);
11006    const customListDetail = () => ({
11007        behaviours: derive$1([
11008            Replacing.config({})
11009        ])
11010    });
11011    const itemsPart = optional({
11012        name: 'items',
11013        overrides: customListDetail
11014    });
11015    const parts$f = constant$1([
11016        itemsPart
11017    ]);
11018    const name$1 = constant$1('CustomList');
11019
11020    const factory$l = (detail, components, _spec, _external) => {
11021        const setItems = (list, items) => {
11022            getListContainer(list).fold(() => {
11023                // check that the group container existed. It may not have if the components
11024                // did not list anything, and shell was false.
11025                // eslint-disable-next-line no-console
11026                console.error('Custom List was defined to not be a shell, but no item container was specified in components');
11027                throw new Error('Custom List was defined to not be a shell, but no item container was specified in components');
11028            }, (container) => {
11029                // Get all the children of container, because they will be items.
11030                // And then use the item setGroup api
11031                const itemComps = Replacing.contents(container);
11032                const numListsRequired = items.length;
11033                const numListsToAdd = numListsRequired - itemComps.length;
11034                const itemsToAdd = numListsToAdd > 0 ?
11035                    range$2(numListsToAdd, () => detail.makeItem()) : [];
11036                const itemsToRemove = itemComps.slice(numListsRequired);
11037                each$1(itemsToRemove, (item) => Replacing.remove(container, item));
11038                each$1(itemsToAdd, (item) => Replacing.append(container, item));
11039                const builtLists = Replacing.contents(container);
11040                each$1(builtLists, (item, i) => {
11041                    detail.setupItem(list, item, items[i], i);
11042                });
11043            });
11044        };
11045        // In shell mode, the group overrides need to be added to the main container, and there can be no children
11046        const extra = detail.shell ? { behaviours: [Replacing.config({})], components: [] } : { behaviours: [], components };
11047        const getListContainer = (component) => detail.shell ? Optional.some(component) : getPart(component, detail, 'items');
11048        return {
11049            uid: detail.uid,
11050            dom: detail.dom,
11051            components: extra.components,
11052            behaviours: augment(detail.listBehaviours, extra.behaviours),
11053            apis: {
11054                setItems
11055            }
11056        };
11057    };
11058    const CustomList = composite({
11059        name: name$1(),
11060        configFields: schema$m(),
11061        partFields: parts$f(),
11062        factory: factory$l,
11063        apis: {
11064            setItems: (apis, list, items) => {
11065                apis.setItems(list, items);
11066            }
11067        }
11068    });
11069
11070    const attribute = 'aria-controls';
11071    const find$1 = (queryElem) => {
11072        const dependent = closest$4(queryElem, (elem) => {
11073            if (!isElement$1(elem)) {
11074                return false;
11075            }
11076            const id = get$g(elem, 'id');
11077            return id !== undefined && id.indexOf(attribute) > -1;
11078        });
11079        return dependent.bind((dep) => {
11080            const id = get$g(dep, 'id');
11081            const dos = getRootNode(dep);
11082            return descendant(dos, `[${attribute}="${id}"]`);
11083        });
11084    };
11085    const manager = () => {
11086        const ariaId = generate$6(attribute);
11087        const link = (elem) => {
11088            set$9(elem, attribute, ariaId);
11089        };
11090        const unlink = (elem) => {
11091            remove$8(elem, attribute);
11092        };
11093        return {
11094            id: ariaId,
11095            link,
11096            unlink,
11097        };
11098    };
11099
11100    const isAriaPartOf = (component, queryElem) => find$1(queryElem).exists((owner) => isPartOf(component, owner));
11101    const isPartOf = (component, queryElem) => closest$2(queryElem, (el) => eq(el, component.element), never) || isAriaPartOf(component, queryElem);
11102
11103    const hoverEvent = 'alloy.item-hover';
11104    const focusEvent = 'alloy.item-focus';
11105    const toggledEvent = 'alloy.item-toggled';
11106    const onHover = (item) => {
11107        // Firstly, check that the focus isn't already inside the item. This
11108        // is to handle situations like widgets where the widget is inside the item
11109        // and it has the focus, so as you slightly adjust the mouse, you don't
11110        // want to lose focus on the widget. Note, that because this isn't API based
11111        // (i.e. we are manually searching for focus), it may not be that flexible.
11112        if (search(item.element).isNone() || Focusing.isFocused(item)) {
11113            if (!Focusing.isFocused(item)) {
11114                Focusing.focus(item);
11115            }
11116            emitWith(item, hoverEvent, { item });
11117        }
11118    };
11119    const onFocus$1 = (item) => {
11120        emitWith(item, focusEvent, { item });
11121    };
11122    const onToggled = (item, state) => {
11123        emitWith(item, toggledEvent, { item, state });
11124    };
11125    const hover = constant$1(hoverEvent);
11126    const focus$1 = constant$1(focusEvent);
11127    const toggled = constant$1(toggledEvent);
11128
11129    // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
11130    const getItemRole = (detail) => detail.role.fold(() => detail.toggling
11131        .map((toggling) => toggling.exclusive ? 'menuitemradio' : 'menuitemcheckbox')
11132        .getOr('menuitem'), identity);
11133    const getTogglingSpec = (tConfig, isOption) => ({
11134        aria: {
11135            mode: isOption ? 'selected' : 'checked'
11136        },
11137        // Filter out the additional properties that are not in Toggling Behaviour's configuration (e.g. exclusive)
11138        ...filter$1(tConfig, (_value, name) => name !== 'exclusive'),
11139        onToggled: (component, state) => {
11140            if (isFunction(tConfig.onToggled)) {
11141                tConfig.onToggled(component, state);
11142            }
11143            onToggled(component, state);
11144        }
11145    });
11146    const builder$2 = (detail) => ({
11147        dom: detail.dom,
11148        domModification: {
11149            // INVESTIGATE: If more efficient, destructure attributes out
11150            ...detail.domModification,
11151            attributes: {
11152                'role': getItemRole(detail),
11153                ...detail.domModification.attributes,
11154                'aria-haspopup': detail.hasSubmenu,
11155                ...(detail.hasSubmenu ? { 'aria-expanded': false } : {})
11156            }
11157        },
11158        behaviours: SketchBehaviours.augment(detail.itemBehaviours, [
11159            // Investigate, is the Toggling.revoke still necessary here?
11160            detail.toggling.fold(Toggling.revoke, (tConfig) => Toggling.config(getTogglingSpec(tConfig, detail.role.exists((role) => role === 'option')))),
11161            Focusing.config({
11162                ignore: detail.ignoreFocus,
11163                // Rationale: because nothing is focusable, when you click
11164                // on the items to choose them, the focus jumps to the first
11165                // focusable outer container ... often the body. If we prevent
11166                // mouseDown ... that doesn't happen. But only tested on Chrome/FF.
11167                stopMousedown: detail.ignoreFocus,
11168                onFocus: (component) => {
11169                    onFocus$1(component);
11170                }
11171            }),
11172            Keying.config({
11173                mode: 'execution'
11174            }),
11175            Representing.config({
11176                store: {
11177                    mode: 'memory',
11178                    initialValue: detail.data
11179                }
11180            }),
11181            config('item-type-events', [
11182                // Treat clicks the same as a button
11183                ...pointerEvents(),
11184                run$1(mouseover(), onHover),
11185                run$1(focusItem(), Focusing.focus)
11186            ])
11187        ]),
11188        components: detail.components,
11189        eventOrder: detail.eventOrder
11190    });
11191    const schema$l = [
11192        required$1('data'),
11193        required$1('components'),
11194        required$1('dom'),
11195        defaulted('hasSubmenu', false),
11196        option$3('toggling'),
11197        option$3('role'),
11198        // Maybe this needs to have fewer behaviours
11199        SketchBehaviours.field('itemBehaviours', [Toggling, Focusing, Ke
11199ying, Representing]),
11200        defaulted('ignoreFocus', false),
11201        defaulted('domModification', {}),
11202        output$1('builder', builder$2),
11203        defaulted('eventOrder', {})
11204    ];
11205    var ItemType = schema$l;
11206
11207    const builder$1 = (detail) => ({
11208        dom: detail.dom,
11209        components: detail.components,
11210        events: derive$2([
11211            stopper(focusItem())
11212        ])
11213    });
11214    const schema$k = [
11215        required$1('dom'),
11216        required$1('components'),
11217        output$1('builder', builder$1)
11218    ];
11219    var SeparatorType = schema$k;
11220
11221    const owner$2 = constant$1('item-widget');
11222    const parts$e = constant$1([
11223        required({
11224            name: 'widget',
11225            overrides: (detail) => {
11226                return {
11227                    behaviours: derive$1([
11228                        Representing.config({
11229                            store: {
11230                                mode: 'manual',
11231                                getValue: (_component) => {
11232                                    return detail.data;
11233                                },
11234                                setValue: noop
11235                            }
11236                        })
11237                    ])
11238                };
11239            }
11240        })
11241    ]);
11242
11243    const builder = (detail) => {
11244        const subs = substitutes(owner$2(), detail, parts$e());
11245        const components = components$1(owner$2(), detail, subs.internals());
11246        const focusWidget = (component) => getPart(component, detail, 'widget').map((widget) => {
11247            Keying.focusIn(widget);
11248            return widget;
11249        });
11250        const onHorizontalArrow = (component, simulatedEvent) => inside(simulatedEvent.event.target) ? Optional.none() : (() => {
11251            if (detail.autofocus) {
11252                simulatedEvent.setSource(component.element);
11253                return Optional.none();
11254            }
11255            else {
11256                return Optional.none();
11257            }
11258        })();
11259        return {
11260            dom: detail.dom,
11261            components,
11262            domModification: detail.domModification,
11263            events: derive$2([
11264                runOnExecute$1((component, simulatedEvent) => {
11265                    focusWidget(component).each((_widget) => {
11266                        simulatedEvent.stop();
11267                    });
11268                }),
11269                run$1(mouseover(), onHover),
11270                run$1(focusItem(), (component, _simulatedEvent) => {
11271                    if (detail.autofocus) {
11272                        focusWidget(component);
11273                    }
11274                    else {
11275                        Focusing.focus(component);
11276                    }
11277                })
11278            ]),
11279            behaviours: SketchBehaviours.augment(detail.widgetBehaviours, [
11280                Representing.config({
11281                    store: {
11282                        mode: 'memory',
11283                        initialValue: detail.data
11284                    }
11285                }),
11286                Focusing.config({
11287                    ignore: detail.ignoreFocus,
11288                    // What about stopMousedown from ItemType?
11289                    onFocus: (component) => {
11290                        onFocus$1(component);
11291                    }
11292                }),
11293                Keying.config({
11294                    mode: 'special',
11295                    // This is required as long as Highlighting tries to focus the first thing (after focusItem fires)
11296                    focusIn: detail.autofocus ? (component) => {
11297                        focusWidget(component);
11298                    } : revoke(),
11299                    onLeft: onHorizontalArrow,
11300                    onRight: onHorizontalArrow,
11301                    onEscape: (component, simulatedEvent) => {
11302                        // If the outer list item didn't have focus,
11303                        // then focus it (i.e. escape the inner widget). Only do if not autofocusing
11304                        // Autofocusing should treat the widget like it is the only item, so it should
11305                        // let its outer menu handle escape
11306                        if (!Focusing.isFocused(component) && !detail.autofocus) {
11307                            Focusing.focus(component);
11308                            return Optional.some(true);
11309                        }
11310                        else if (detail.autofocus) {
11311                            simulatedEvent.setSource(component.element);
11312                            return Optional.none();
11313                        }
11314                        else {
11315                            return Optional.none();
11316                        }
11317                    }
11318                })
11319            ])
11320        };
11321    };
11322    const schema$j = [
11323        required$1('uid'),
11324        required$1('data'),
11325        required$1('components'),
11326        required$1('dom'),
11327        defaulted('autofocus', false),
11328        defaulted('ignoreFocus', false),
11329        SketchBehaviours.field('widgetBehaviours', [Representing, Focusing, Ke
vendor: 14,054 bytes, lines 11329-11654
11329ying]),
11330        defaulted('domModification', {}),
11331        // We don't have the uid at this point
11332        defaultUidsSchema(parts$e()),
11333        output$1('builder', builder)
11334    ];
11335    var WidgetType = schema$j;
11336
11337    const itemSchema$2 = choose$1('type', {
11338        widget: WidgetType,
11339        item: ItemType,
11340        separator: SeparatorType
11341    });
11342    const configureGrid = (detail, movementInfo) => ({
11343        mode: 'flatgrid',
11344        selector: '.' + detail.markers.item,
11345        initSize: {
11346            numColumns: movementInfo.initSize.numColumns,
11347            numRows: movementInfo.initSize.numRows
11348        },
11349        focusManager: detail.focusManager
11350    });
11351    const configureMatrix = (detail, movementInfo) => ({
11352        mode: 'matrix',
11353        selectors: {
11354            row: movementInfo.rowSelector,
11355            cell: '.' + detail.markers.item
11356        },
11357        previousSelector: movementInfo.previousSelector,
11358        focusManager: detail.focusManager
11359    });
11360    const configureMenu = (detail, _movementInfo) => ({
11361        mode: 'menu',
11362        selector: '.' + detail.markers.item,
11363        focusManager: detail.focusManager
11364    });
11365    const parts$d = constant$1([
11366        group({
11367            factory: {
11368                sketch: (spec) => {
11369                    const itemInfo = asRawOrDie$1('menu.spec item', itemSchema$2, spec);
11370                    return itemInfo.builder(itemInfo);
11371                }
11372            },
11373            name: 'items',
11374            unit: 'item',
11375            defaults: (detail, u) => {
11376                // Switch this to a common library
11377                // The WidgetItemSpec is just because it has uid, and the others don't
11378                // for some reason. So there is nothing guaranteeing that `u` is a WidgetItemSpec,
11379                // so we should probably rework this code.
11380                return has$2(u, 'uid') ? u : {
11381                    ...u,
11382                    uid: generate$4('item')
11383                };
11384            },
11385            overrides: (detail, u) => {
11386                return {
11387                    type: u.type,
11388                    ignoreFocus: detail.fakeFocus,
11389                    domModification: {
11390                        classes: [detail.markers.item]
11391                    }
11392                };
11393            }
11394        })
11395    ]);
11396    const schema$i = constant$1([
11397        optionString('role'),
11398        required$1('value'),
11399        required$1('items'),
11400        required$1('dom'),
11401        required$1('components'),
11402        defaulted('eventOrder', {}),
11403        field('menuBehaviours', [Highlighting, Representing, Composing, Keying]),
11404        defaultedOf('movement', {
11405            mode: 'menu'
11406        }, choose$1('mode', {
11407            grid: [
11408                initSize(),
11409                output$1('config', configureGrid)
11410            ],
11411            matrix: [
11412                output$1('config', configureMatrix),
11413                required$1('rowSelector'),
11414                defaulted('previousSelector', Optional.none),
11415            ],
11416            menu: [
11417                output$1('config', configureMenu)
11418            ]
11419        })),
11420        itemMarkers(),
11421        defaulted('fakeFocus', false),
11422        defaulted('focusManager', dom$2()),
11423        onHandler('onHighlight'),
11424        onHandler('onDehighlight'),
11425        defaulted('showMenuRole', true),
11426    ]);
11427
11428    const focus = constant$1('alloy.menu-focus');
11429
11430    const deselectOtherRadioItems = (menu, item) => {
11431        // TODO: TINY-8812 - This ideally should be done in a way such that a menu can have multiple radio groups.
11432        const checkedRadioItems = descendants(menu.element, '[role="menuitemradio"][aria-checked="true"]');
11433        each$1(checkedRadioItems, (ele) => {
11434            if (!eq(ele, item.element)) {
11435                menu.getSystem().getByDom(ele).each((c) => {
11436                    Toggling.off(c);
11437                });
11438            }
11439        });
11440    };
11441    const make$6 = (detail, components, _spec, _externals) => ({
11442        uid: detail.uid,
11443        dom: detail.dom,
11444        markers: detail.markers,
11445        behaviours: augment(detail.menuBehaviours, [
11446            Highlighting.config({
11447                // Highlighting for a menu is selecting items inside the menu
11448                highlightClass: detail.markers.selectedItem,
11449                itemClass: detail.markers.item,
11450                onHighlight: detail.onHighlight,
11451                onDehighlight: detail.onDehighlight
11452            }),
11453            Representing.config({
11454                store: {
11455                    mode: 'memory',
11456                    initialValue: detail.value
11457                }
11458            }),
11459            Composing.config({
11460                find: Optional.some
11461            }),
11462            Keying.config(detail.movement.config(detail, detail.movement))
11463        ]),
11464        events: derive$2([
11465            // This is dispatched from a menu to tell an item to be highlighted.
11466            run$1(focus$1(), (menu, simulatedEvent) => {
11467                // Highlight the item
11468                const event = simulatedEvent.event;
11469                menu.getSystem().getByDom(event.target).each((item) => {
11470                    Highlighting.highlight(menu, item);
11471                    simulatedEvent.stop();
11472                    // Trigger the focus event on the menu.
11473                    emitWith(menu, focus(), { menu, item });
11474                });
11475            }),
11476            // Highlight the item that the cursor is over. The onHighlight
11477            // code needs to handle updating focus if required
11478            run$1(hover(), (menu, simulatedEvent) => {
11479                const item = simulatedEvent.event.item;
11480                Highlighting.highlight(menu, item);
11481            }),
11482            // Enforce only a single radio menu item is toggled by finding any other toggled
11483            // radio menu items and untoggling them when a certain item is toggled
11484            run$1(toggled(), (menu, simulatedEvent) => {
11485                const { item, state } = simulatedEvent.event;
11486                if (state && get$g(item.element, 'role') === 'menuitemradio') {
11487                    deselectOtherRadioItems(menu, item);
11488                }
11489            })
11490        ]),
11491        components,
11492        eventOrder: detail.eventOrder,
11493        ...detail.showMenuRole ? {
11494            domModification: {
11495                attributes: {
11496                    role: detail.role.getOr('menu')
11497                }
11498            }
11499        } : {}
11500    });
11501
11502    const Menu = composite({
11503        name: 'Menu',
11504        configFields: schema$i(),
11505        partFields: parts$d(),
11506        factory: make$6
11507    });
11508
11509    const transpose$1 = (obj) => 
11510    // Assumes no duplicate fields.
11511    tupleMap(obj, (v, k) => ({ k: v, v: k }));
11512    const trace = (items, byItem, byMenu, finish) => 
11513    // Given a finishing submenu (which will be the value of expansions),
11514    // find the triggering item, find its menu, and repeat the process. If there
11515    // is no triggering item, we are done.
11516    get$h(byMenu, finish).bind((triggerItem) => get$h(items, triggerItem).bind((triggerMenu) => {
11517        const rest = trace(items, byItem, byMenu, triggerMenu);
11518        return Optional.some([triggerMenu].concat(rest));
11519    })).getOr([]);
11520    const generate$2 = (menus, expansions) => {
11521        const items = {};
11522        each(menus, (menuItems, menu) => {
11523            each$1(menuItems, (item) => {
11524                items[item] = menu;
11525            });
11526        });
11527        const byItem = expansions;
11528        const byMenu = transpose$1(expansions);
11529        // For each menu, calculate the backlog of submenus to get to it.
11530        const menuPaths = map$1(byMenu, (_triggerItem, submenu) => [submenu].concat(trace(items, byItem, byMenu, submenu)));
11531        return map$1(items, (menu) => get$h(menuPaths, menu).getOr([menu]));
11532    };
11533
11534    const init$2 = () => {
11535        const expansions = Cell({});
11536        const menus = Cell({});
11537        const paths = Cell({});
11538        const primary = value$2();
11539        // Probably think of a better way to store this information.
11540        const directory = Cell({});
11541        const clear = () => {
11542            expansions.set({});
11543            menus.set({});
11544            paths.set({});
11545            primary.clear();
11546        };
11547        const isClear = () => primary.get().isNone();
11548        const setMenuBuilt = (menuName, built) => {
11549            menus.set({
11550                ...menus.get(),
11551                [menuName]: {
11552                    type: 'prepared',
11553                    menu: built
11554                }
11555            });
11556        };
11557        const setContents = (sPrimary, sMenus, sExpansions, dir) => {
11558            primary.set(sPrimary);
11559            expansions.set(sExpansions);
11560            menus.set(sMenus);
11561            directory.set(dir);
11562            const sPaths = generate$2(dir, sExpansions);
11563            paths.set(sPaths);
11564        };
11565        const getTriggeringItem = (menuValue) => find$4(expansions.get(), (v, _k) => v === menuValue);
11566        const getTriggerData = (menuValue, getItemByValue, path) => getPreparedMenu(menuValue).bind((menu) => getTriggeringItem(menuValue).bind((triggeringItemValue) => getItemByValue(triggeringItemValue).map((triggeredItem) => ({
11567            triggeredMenu: menu,
11568            triggeringItem: triggeredItem,
11569            triggeringPath: path
11570        }))));
11571        const getTriggeringPath = (itemValue, getItemByValue) => {
11572            // Get the path up to the last item
11573            const extraPath = filter$2(lookupItem(itemValue).toArray(), (menuValue) => getPreparedMenu(menuValue).isSome());
11574            return get$h(paths.get(), itemValue).bind((path) => {
11575                // remember the path is [ most-recent-menu, next-most-recent-menu ]
11576                // convert each menu identifier into { triggeringItem: comp, menu: comp }
11577                // could combine into a fold ... probably a left to reverse ... but we'll take the
11578                // straightforward version when prototyping
11579                const revPath = reverse(extraPath.concat(path));
11580                const triggers = bind$3(revPath, (menuValue, menuIndex) => 
11581                // finding menuValue, it should match the trigger
11582                getTriggerData(menuValue, getItemByValue, revPath.slice(0, menuIndex + 1)).fold(() => is$1(primary.get(), menuValue) ? [] : [Optional.none()], (data) => [Optional.some(data)]));
11583                // Convert List<Optional<X>> to Optional<List<X>> if ALL are Some
11584                return sequence(triggers);
11585            });
11586        };
11587        // Given an item, return a list of all menus including the one that it triggered (if there is one)
11588        const expand = (itemValue) => get$h(expansions.get(), itemValue).map((menu) => {
11589            const current = get$h(paths.get(), itemValue).getOr([]);
11590            return [menu].concat(current);
11591        });
11592        const collapse = (itemValue) => 
11593        // Look up which key has the itemValue
11594        get$h(paths.get(), itemValue).bind((path) => path.length > 1 ? Optional.some(path.slice(1)) : Optional.none());
11595        const refresh = (itemValue) => get$h(paths.get(), itemValue);
11596        const getPreparedMenu = (menuValue) => lookupMenu(menuValue).bind(extractPreparedMenu);
11597        const lookupMenu = (menuValue) => get$h(menus.get(), menuValue);
11598        const lookupItem = (itemValue) => get$h(expansions.get(), itemValue);
11599        const otherMenus = (path) => {
11600            const menuValues = directory.get();
11601            return difference(keys(menuValues), path);
11602        };
11603        const getPrimary = () => primary.get().bind(getPreparedMenu);
11604        const getMenus = () => menus.get();
11605        return {
11606            setMenuBuilt,
11607            setContents,
11608            expand,
11609            refresh,
11610            collapse,
11611            lookupMenu,
11612            lookupItem,
11613            otherMenus,
11614            getPrimary,
11615            getMenus,
11616            clear,
11617            isClear,
11618            getTriggeringPath
11619        };
11620    };
11621    const extractPreparedMenu = (prep) => prep.type === 'prepared' ? Optional.some(prep.menu) : Optional.none();
11622    const LayeredState = {
11623        init: init$2,
11624        extractPreparedMenu
11625    };
11626
11627    const onMenuItemHighlightedEvent = generate$6('tiered-menu-item-highlight');
11628    const onMenuItemDehighlightedEvent = generate$6('tiered-menu-item-dehighlight');
11629
11630    const make$5 = (detail, _rawUiSpec) => {
11631        const submenuParentItems = value$2();
11632        // So the way to provide extra configuration for the menus that tiered menus create is just
11633        // to provide different menu specs when building up the TieredData. The TieredMenu itself
11634        // does not control it, except to set: markers, fakeFocus, onHighlight, and focusManager
11635        const buildMenus = (container, primaryName, menus) => map$1(menus, (spec, name) => {
11636            const makeSketch = () => Menu.sketch({
11637                ...spec,
11638                value: name,
11639                // The TieredMenu markers should be inherited by the Menu. "Markers" are things like
11640                // what is the class for the currently selected item
11641                markers: detail.markers,
11642                // If the TieredMenu has been configured with FakeFocus, it needs the menus that it generates
11643                // to preserve that configuration. Generally, FakeFocus is used for situations where the user
11644                // wants to keep focus inside some editable element (like an input, or editor content)
11645                fakeFocus: detail.fakeFocus,
11646                // The TieredMenu detail.onHighlight function only relates to selecting an item,
11647                // not a menu, and the menuComp it is passed is the menu, not the tiered menu.
11648                // This makes it a difficult handler to use for a tieredmenu, so we are
11649                // deprecating it.
11650                onHighlight: (menuComp, itemComp) => {
11651                    // Trigger an internal event so that we can listen to it at the tieredmenu
11652                    // level, and call detail.onHighlightItem handler with tmenu, menu, and item.
11653                    const highlightData = {
11654                        menuComp,
11655                        itemComp
11656                    };
11657                    emitWith(menuComp, onMenuItemHighlightedEvent, highlightData);
11658                },
11659                onDehighlight: (menuComp, itemComp) => {
11660                    const dehighlightData = {
11661                        menuComp,
11662                        itemComp
11663                    };
11664                    // Trigger an internal event so that we can listen to it at the tieredmenu
11665                    // level, and call detail.onDehighlightItem handler with tmenu, menu, and item.
11666                    emitWith(menuComp, onMenuItemDehighlightedEvent, dehighlightData);
11667                },
11668                // The Menu itself doesn't set the focusManager based on the value of fakeFocus. It only uses
11669                // its fakeFocus configuration for creating items that ignore focus, but it still needs to be
11670                // told which focusManager to use. Perhaps we should change this, though it does allow for more
11671                // complex focusManagers in single menus.
11672                focusManager: detail.fakeFocus ? highlights() : dom$2()
11673            });
11674            // Only build the primary at first. Build the others as needed.
11675            return name === primaryName ? {
11676                type: 'prepared',
11677                menu: container.getSystem().build(makeSketch())
11678            } : {
11679                type: 'notbuilt',
11680                nbMenu: makeSketch
11681            };
11682        });
11683        const layeredState = LayeredState.init();
11684        const setup = (container) => {
11685            const componentMap = buildMenus(container, detail.data.primary, detail.data.menus);
11686            const directory = toDirectory();
11687            layeredState.setContents(detail.data.primary, componentMap, detail.data.expansions, directory);
11688            return layeredState.getPrimary();
11689        };
11690        const getItemValue = (item) => Representing.getValue(item).value;
11691        // Find the first item with value `itemValue` in any of the menus inside this tiered menu structure
11692        const getItemByValue = (_container, menus, itemValue) => 
11693        // Can *greatly* improve the performance of this by calculating things up front.
11694        findMap(menus, (menu) => {
11695            if (!menu.getSystem().isConnected()) {
11696                return Optional.none();
11697            }
11698            const candidates = Highlighting.getCandidates(menu);
11699            return find$5(candidates, (c) => getItemValue(c) === itemValue);
11700        });
11701        const toDirectory = (_container) => map$1(detail.data.menus, (data, _menuName) => bind$3(data.items, (item) => item.type === 'separator' ? [] : [item.data.value]));
11702        // This just sets the active menu. It will not set any active items.
11703        const setActiveMenu = Highlighting.highlight;
11704        // The item highlighted as active is either the currently active item in the menu,
11705        // or the first one.
11706        const setActiveMenuAndItem = (container, menu) => {
11707            // Firstly, choose the active menu
11708            setActiveMenu(container, menu);
11709            // Then, choose the active item inside the active menu
11710            Highlighting.getHighlighted(menu).orThunk(() => Highlighting.getFirst(menu)).each((item) => {
11711                if (detail.fakeFocus) {
11712                    // When using fakeFocus, the items won't have a tab-index, so calling focusItem on them
11713                    // won't do anything. So we need to manually call highlighting, which is what fakeFocus
11714                    // uses. It would probably be better to use the focusManager specified.
11715                    Highlighting.highlight(menu, item);
11716                }
11717                else {
11718                    // We don't just use Focusing.focus here, because some items can have slightly different
11719                    // handling when they respond to a focusItem event. Widgets with autofocus, for example,
11720                    // will trigger a Keying.focusIn instead of Focusing.focus call, because they want to move
11721                    // the focus _inside_ the widget, not just to its outer level. The focusItem event
11722                    // performs a similar purpose to SystemEvents.focus() and potentially, could be consolidated.
11723                    dispatch(container, item.element, focusItem());
11724                }
11725            });
11726        };
11727        const getMenus = (state, menuValues) => cat(map$2(menuValues, (mv) => state.lookupMenu(mv).bind((prep) => prep.type === 'prepared' ? Optional.some(prep.menu) : Optional.none())));
11728        const closeOthers = (container, state, path) => {
11729            const others = getMenus(state, state.otherMenus(path));
11730            each$1(others, (o) => {
11731                // May not need to do the active menu thing.
11732                remove$2(o.element, [detail.markers.backgroundMenu]);
11733                if (!detail.stayInDom) {
11734                    Replacing.remove(container, o);
11735                }
11736            });
11737        };
11738        const getSubmenuParents = (container) => submenuParentItems.get().getOrThunk(() => {
11739            const r = {};
11740            const items = descendants(container.element, `.${detail.markers.item}`);
11741            const parentItems = filter$2(items, (i) => get$g(i, 'aria-haspopup') === 'true');
11742            each$1(parentItems, (i) => {
11743                container.getSystem().getByDom(i).each((itemComp) => {
vendor: 5,932 bytes, lines 11744-11852
11744                    const key = getItemValue(itemComp);
11745                    r[key] = itemComp;
11746                });
11747            });
11748            submenuParentItems.set(r);
11749            return r;
11750        });
11751        // Not ideal. Ideally, we would like a map of item keys to components.
11752        const updateAriaExpansions = (container, path) => {
11753            const parentItems = getSubmenuParents(container);
11754            each(parentItems, (v, k) => {
11755                // Really should turn path into a Set
11756                const expanded = contains$2(path, k);
11757                set$9(v.element, 'aria-expanded', expanded);
11758            });
11759        };
11760        const updateMenuPath = (container, state, path) => Optional.from(path[0]).bind((latestMenuName) => state.lookupMenu(latestMenuName).bind((menuPrep) => {
11761            if (menuPrep.type === 'notbuilt') {
11762                return Optional.none();
11763            }
11764            else {
11765                const activeMenu = menuPrep.menu;
11766                const rest = getMenus(state, path.slice(1));
11767                each$1(rest, (r) => {
11768                    add$2(r.element, detail.markers.backgroundMenu);
11769                });
11770                if (!inBody(activeMenu.element)) {
11771                    Replacing.append(container, premade(activeMenu));
11772                }
11773                // Remove the background-menu class from the active menu
11774                remove$2(activeMenu.element, [detail.markers.backgroundMenu]);
11775                setActiveMenuAndItem(container, activeMenu);
11776                closeOthers(container, state, path);
11777                return Optional.some(activeMenu);
11778            }
11779        }));
11780        let ExpandHighlightDecision;
11781        (function (ExpandHighlightDecision) {
11782            ExpandHighlightDecision[ExpandHighlightDecision["HighlightSubmenu"] = 0] = "HighlightSubmenu";
11783            ExpandHighlightDecision[ExpandHighlightDecision["HighlightParent"] = 1] = "HighlightParent";
11784        })(ExpandHighlightDecision || (ExpandHighlightDecision = {}));
11785        const buildIfRequired = (container, menuName, menuPrep) => {
11786            if (menuPrep.type === 'notbuilt') {
11787                const menu = container.getSystem().build(menuPrep.nbMenu());
11788                layeredState.setMenuBuilt(menuName, menu);
11789                return menu;
11790            }
11791            else {
11792                return menuPrep.menu;
11793            }
11794        };
11795        const expandRight = (container, item, decision = ExpandHighlightDecision.HighlightSubmenu) => {
11796            if (item.hasConfigured(Disabling) && Disabling.isDisabled(item)) {
11797                return Optional.some(item);
11798            }
11799            else {
11800                const value = getItemValue(item);
11801                return layeredState.expand(value).bind((path) => {
11802                    // Called when submenus are opened by keyboard AND hovering navigation
11803                    updateAriaExpansions(container, path);
11804                    // When expanding, always select the first.
11805                    return Optional.from(path[0]).bind((menuName) => layeredState.lookupMenu(menuName).bind((activeMenuPrep) => {
11806                        const activeMenu = buildIfRequired(container, menuName, activeMenuPrep);
11807                        // DUPE with above. Fix later.
11808                        if (!inBody(activeMenu.element)) {
11809                            Replacing.append(container, premade(activeMenu));
11810                        }
11811                        // updateMenuPath is the code which changes the active menu. We don't always
11812                        // want to change the active menu. Sometimes, we just want to show it (e.g. hover)
11813                        detail.onOpenSubmenu(container, item, activeMenu, reverse(path));
11814                        if (decision === ExpandHighlightDecision.HighlightSubmenu) {
11815                            Highlighting.highlightFirst(activeMenu);
11816                            return updateMenuPath(container, layeredState, path);
11817                        }
11818                        else {
11819                            Highlighting.dehighlightAll(activeMenu);
11820                            return Optional.some(item);
11821                        }
11822                    }));
11823                });
11824            }
11825        };
11826        const collapseLeft = (container, item) => {
11827            const value = getItemValue(item);
11828            return layeredState.collapse(value).bind((path) => {
11829                // Called when submenus are closed because of KEYBOARD navigation
11830                updateAriaExpansions(container, path);
11831                return updateMenuPath(container, layeredState, path).map((activeMenu) => {
11832                    detail.onCollapseMenu(container, item, activeMenu);
11833                    return activeMenu;
11834                });
11835            });
11836        };
11837        const updateView = (container, item) => {
11838            const value = getItemValue(item);
11839            return layeredState.refresh(value).bind((path) => {
11840                // Only this function collapses irrelevant submenus when navigating by HOVERING.
11841                // Does mean this is called twice when navigating by hovering, since both
11842                // updateView and expandRight are called by the ItemEvents.hover() handler
11843                updateAriaExpansions(container, path);
11844                return updateMenuPath(container, layeredState, path);
11845            });
11846        };
11847        const onRight = (container, item) => inside(item.element) ? Optional.none() : expandRight(container, item, ExpandHighlightDecision.HighlightSubmenu);
11848        const onLeft = (container, item) => 
11849        // Exclude inputs, textareas etc.
11850        inside(item.element) ? Optional.none() : collapseLeft(container, item);
11851        const onEscape = (container, item) => collapseLeft(container, item).orThunk(() => detail.onEscape(container, item).map(() => container) // This should only fire when the user presses ESC ... not any other close.
11852        );
11853        const keyOnItem = (f) => (container, simulatedEvent) => {
11854            // 2022-08-16 This seems to be the only code in alloy that actually uses
11855            // the getSource aspect of an event. Remember, that this code is firing
11856            // when an event bubbles up the tiered menu, e.g. left arrow key.
11857            // The only current code that sets the source manually is in the Widget item
11858            // type, and it only sets the source when it is using autofocus. Autofocus
11859            // is used to essentially treat the widget like it is the top-level item, so
11860            // when events originate from *within* the widget, their source is changed to
11861            // the top-level item. Consider removing EventSource from alloy altogether.
11862            return closest$3(simulatedEvent.getSource(), `.${detail.markers.item}`)
11863                .bind((target) => container.getSystem().getByDom(target).toOptional().bind((item) => f(container, item).map(always)));
11864        };
11865        // NOTE: Many of these events rely on identifying the current item by information
11866        // sent with the event. However, in situations where you are using fakeFocus, but
11867        // the real focus is still somewhere in the menu (e.g. search bar), this will lead to
11868        // an incorrect identification of the active item. Ideally, instead of pulling the
11869        // item from the event, we should just use Highlighting to identify the active item,
11870        // and operate on it. However, not all events will necessarily have to happen on the
11871        // active item, so we need to consider all the cases before making this change. For now,
11872        // there will be a known limitation that if the real focus is still inside the TieredMenu,
11873        // but the menu is using fakeFocus, then the actions will operate on the wrong targets.
11874        // A workaround for that is to stop or cut or redispatch the events in whichever
11875        // component has the real focus.
11876        // TODO: TINY-9011 Introduce proper handling of fakeFocus in TieredMenu
11877        const events = derive$2([
11878            // Set "active-menu" for the menu with focus
11879            run$1(focus(), (tmenu, simulatedEvent) => {
11880                // Ensure the item is actually part of this menu structure, and not part of another menu structure that's bubbling.
11881                const item = simulatedEvent.event.item;
11882                layeredState.lookupItem(getItemValue(item)).each(() => {
11883                    const menu = simulatedEvent.event.menu;
11884                    Highlighting.highlight(tmenu, menu);
11885                    const value = getItemValue(simulatedEvent.event.item);
11886                    layeredState.refresh(value).each((path) => closeOthers(tmenu, layeredState, path));
11887                });
11888            }),
11889            runOnExecute$1((component, simulatedEvent) => {
11890                // Trigger on execute on the targeted element
11891                // I.e. clicking on menu item
11892                const target = simulatedEvent.event.target;
11893                component.getSystem().getByDom(target).each((item) => {
11894                    const itemValue = getItemValue(item);
11895                    // INVESTIGATE: I don't know if this is doing anything any more. Check.
11896                    if (itemValue.indexOf('collapse-item') === 0) {
11897                        collapseLeft(component, item);
11898                    }
11899                    expandRight(component, item, ExpandHighlightDecision.HighlightSubmenu).fold(() => {
11900                        detail.onExecute(component, item);
11901                    }, noop);
11902                });
11903            }),
11904            // Open the menu as soon as it is added to the DOM
11905            runOnAttached((container, _simulatedEvent) => {
11906                setup(container).each((primary) => {
11907                    Replacing.append(container, premade(primary));
11908                    detail.onOpenMenu(container, primary);
11909                    if (detail.highlightOnOpen === HighlightOnOpen.HighlightMenuAndItem) {
11910                        setActiveMenuAndItem(container, primary);
11911                    }
11912                    else if (detail.highlightOnOpen === HighlightOnOpen.HighlightJustMenu) {
11913                        setActiveMenu(container, primary);
11914                    }
11915                });
11916            }),
11917            // Listen to the events bubbling up from menu about highlighting, and trigger
11918            // our handlers with tmenu, menu and item
11919            run$1(onMenuItemHighlightedEvent, (tmenuComp, se) => {
11920                detail.onHighlightItem(tmenuComp, se.event.menuComp, se.event.itemComp);
11921            }),
11922            run$1(onMenuItemDehighlightedEvent, (tmenuComp, se) => {
11923                detail.onDehighlightItem(tmenuComp, se.event.menuComp, se.event.itemComp);
11924            }),
11925            ...(detail.navigateOnHover ? [
11926                // Hide any irrelevant submenus and expand any submenus based
11927                // on hovered item
11928                run$1(hover(), (tmenu, simulatedEvent) => {
11929                    const item = simulatedEvent.event.item;
11930                    updateView(tmenu, item);
11931                    expandRight(tmenu, item, ExpandHighlightDecision.HighlightParent);
11932                    detail.onHover(tmenu, item);
11933                })
11934            ] : [])
11935        ]);
11936        const getActiveItem = (container) => Highlighting.getHighlighted(container).bind(Highlighting.getHighlighted);
11937        const collapseMenuApi = (container) => {
11938            getActiveItem(container).each((currentItem) => {
11939                collapseLeft(container, currentItem);
11940            });
11941        };
11942        const highlightPrimary = (container) => {
11943            layeredState.getPrimary().each((primary) => {
11944                setActiveMenuAndItem(container, primary);
11945            });
11946        };
11947        const extractMenuFromContainer = (container) => Optional.from(container.components()[0]).filter((comp) => get$g(comp.element, 'role') === 'menu');
11948        const repositionMenus = (container) => {
11949            // Get the primary menu
11950            const maybeActivePrimary = layeredState.getPrimary().bind((primary) => 
11951            // Get the triggering path (item, menu) up to the active item
11952            getActiveItem(container).bind((currentItem) => {
11953                const itemValue = getItemValue(currentItem);
11954                const allMenus = values(layeredState.getMenus());
11955                const preparedMenus = cat(map$2(allMenus, LayeredState.extractPreparedMenu));
11956                return layeredState.getTriggeringPath(itemValue, (v) => getItemByValue(container, preparedMenus, v));
11957            }).map((triggeringPath) => ({ primary, triggeringPath })));
11958            maybeActivePrimary.fold(() => {
11959                // When a menu is open but there is no activeItem, we get the menu from the container.
11960                extractMenuFromContainer(container).each((primaryMenu) => {
11961                    detail.onRepositionMenu(container, primaryMenu, []);
11962                });
11963            }, ({ primary, triggeringPath }) => {
11964                // Refresh all the menus up to the active item
11965                detail.onRepositionMenu(container, primary, triggeringPath);
11966            });
11967        };
11968        const apis = {
11969            collapseMenu: collapseMenuApi,
11970            highlightPrimary,
11971            repositionMenus
11972        };
11973        return {
11974            uid: detail.uid,
11975            dom: detail.dom,
11976            markers: detail.markers,
11977            behaviours: augment(detail.tmenuBehaviours, [
11978                Keying.config({
11979                    mode: 'special',
11980                    onRight: keyOnItem(onRight),
11981                    onLeft: keyOnItem(onLeft),
11982                    onEscape: keyOnItem(onEscape),
11983                    onTab: detail.onTab,
11984                    onShiftTab: detail.onShiftTab,
11985                    focusIn: (container, _keyInfo) => {
11986                        layeredState.getPrimary().each((primary) => {
11987                            dispatch(container, primary.element, focusItem());
11988                        });
11989                    }
11990                }),
11991                // Highlighting is used for highlighting the active menu
11992                Highlighting.config({
11993                    highlightClass: detail.markers.selectedMenu,
11994                    itemClass: detail.markers.menu
11995                }),
11996                Composing.config({
11997                    find: (container) => {
11998                        return Highlighting.getHighlighted(container);
11999                    }
12000                }),
12001                Replacing.config({})
12002            ]),
12003            eventOrder: detail.eventOrder,
12004            apis,
12005            events
12006        };
12007    };
12008    const collapseItem$1 = constant$1('collapse-item');
12009
12010    const tieredData = (primary, menus, expansions) => ({
12011        primary,
12012        menus,
12013        expansions
12014    });
12015    const singleData = (name, menu) => ({
12016        primary: name,
12017        menus: wrap(name, menu),
12018        expansions: {}
12019    });
12020    const collapseItem = (text) => ({
12021        value: generate$6(collapseItem$1()),
12022        meta: {
12023            text
12024        }
12025    });
12026    const tieredMenu = single({
12027        name: 'TieredMenu',
12028        configFields: [
12029            onStrictKeyboardHandler('onExecute'),
12030            onStrictKeyboardHandler('onEscape'),
12031            onKeyboardHandler('onTab'),
12032            onKeyboardHandler('onShiftTab'),
12033            onStrictHandler('onOpenMenu'),
12034            onStrictHandler('onOpenSubmenu'),
12035            onHandler('onRepositionMenu'),
12036            onHandler('onCollapseMenu'),
12037            // Ideally, we should validate that this is a valid value, but
12038            // this is an number-based enum, so it would just be a number.
12039            defaulted('highlightOnOpen', HighlightOnOpen.HighlightMenuAndItem),
12040            requiredObjOf('data', [
12041                required$1('primary'),
12042                required$1('menus'),
12043                required$1('expansions')
12044            ]),
12045            defaulted('fakeFocus', false),
12046            onHandler('onHighlightItem'),
12047            onHandler('onDehighlightItem'),
12048            onHandler('onHover'),
12049            tieredMenuMarkers(),
12050            required$1('dom'),
12051            defaulted('navigateOnHover', true),
12052            defaulted('stayInDom', false),
12053            field('tmenuBehaviours', [Keying, Highlighting, Composing, Replacing]),
12054            defaulted('eventOrder', {})
12055        ],
12056        apis: {
12057            collapseMenu: (apis, tmenu) => {
12058                apis.collapseMenu(tmenu);
12059            },
12060            // This will highlight the primary menu AND an item in the primary menu
12061            // Do not use just to set the active menu.
12062            highlightPrimary: (apis, tmenu) => {
12063                apis.highlightPrimary(tmenu);
12064            },
12065            repositionMenus: (apis, tmenu) => {
12066                apis.repositionMenus(tmenu);
12067            }
12068        },
12069        factory: make$5,
12070        extraApis: {
12071            tieredData,
12072            singleData,
12073            collapseItem
12074        }
12075    });
12076
12077    const suffix = constant$1('sink');
12078    const partType = constant$1(optional({
12079        name: suffix(),
12080        overrides: constant$1({
12081            dom: {
12082                tag: 'div'
12083            },
12084            behaviours: derive$1([
12085                Positioning.config({
12086                    // TODO: Make an internal sink also be able to be used with relative layouts
12087                    useFixed: always
12088                })
12089            ]),
12090            events: derive$2([
12091                // Sinks should not let keydown or click propagate
12092                cutter(keydown()),
12093                cutter(mousedown()),
12094                cutter(click())
12095            ])
12096        })
12097    }));
12098
12099    const schema$h = objOfOnly([
12100        defaulted('isExtraPart', never),
12101        optionObjOf('fireEventInstead', [
12102            defaulted('event', dismissRequested())
12103        ])
12104    ]);
12105    const receivingChannel$1 = (rawSpec) => {
12106        const detail = asRawOrDie$1('Dismissal', schema$h, rawSpec);
12107        return {
12108            [dismissPopups()]: {
12109                schema: objOfOnly([
12110                    required$1('target')
12111                ]),
12112                onReceive: (sandbox, data) => {
12113                    if (Sandboxing.isOpen(sandbox)) {
12114                        const isPart = Sandboxing.isPartOf(sandbox, data.target) || detail.isExtraPart(sandbox, data.target);
12115                        if (!isPart) {
12116                            detail.fireEventInstead.fold(() => Sandboxing.close(sandbox), (fe) => emit(sandbox, fe.event));
12117                        }
12118                    }
12119                }
12120            }
12121        };
12122    };
12123
12124    const schema$g = objOfOnly([
12125        optionObjOf('fireEventInstead', [
12126            defaulted('event', repositionRequested())
12127        ]),
12128        requiredFunction('doReposition')
12129    ]);
12130    const receivingChannel = (rawSpec) => {
12131        const detail = asRawOrDie$1('Reposition', schema$g, rawSpec);
12132        return {
12133            [repositionPopups()]: {
12134                onReceive: (sandbox) => {
12135                    if (Sandboxing.isOpen(sandbox)) {
12136                        detail.fireEventInstead.fold(() => detail.doReposition(sandbox), (fe) => emit(sandbox, fe.event));
12137                    }
12138                }
12139            }
12140        };
12141    };
12142
12143    const getAnchor = (detail, component) => {
12144        const hotspot = detail.getHotspot(component).getOr(component);
12145        const type = 'hotspot';
12146        const overrides = detail.getAnchorOverrides();
12147        return detail.layouts.fold(() => ({ type, hotspot, overrides }), (layouts) => ({ type, hotspot, overrides, layouts }));
12148    };
12149    const fetch$1 = (detail, mapFetch, component) => {
12150        const fetcher = detail.fetch;
12151        return fetcher(component).map(mapFetch);
12152    };
12153    const openF = (detail, mapFetch, anchor, component, sandbox, externals, highlightOnOpen) => {
12154        const futureData = fetch$1(detail, mapFetch, component);
12155        const getLazySink = getSink(component, detail);
12156        // When Tab or Shift+Tab bubbles up from inside an open menu, close the menu,
12157        // refocus the trigger, and re-emit the keydown so the trigger's parent
12158        // (toolbar, menubar, etc.) handles it via its own Keying config.
12159        const onTabOutOfMenu = (_tmenu, se) => {
12160            Focusing.focus(component);
12161            emitWith(component, 'keydown', { raw: se.event.raw });
12162            Sandboxing.close(sandbox);
12163            return Optional.some(true);
12164        };
12165        // TODO: Make this potentially a single menu also
12166        return futureData.map((tdata) => tdata.bind((data) => {
12167            const primaryMenu = data.menus[data.primary];
12168            Optional.from(primaryMenu).each((menu) => {
12169                detail.listRole.each((listRole) => {
12170                    menu.role = listRole;
12171                });
12172            });
12173            return Optional.from(tieredMenu.sketch({
12174                // Externals are configured by the "menu" part. It's called external because it isn't contained
12175                // within the DOM descendants of the dropdown. You can configure things like `fakeFocus` here.
12176                ...externals.menu(),
12177                uid: generate$4(''),
12178                data,
12179                highlightOnOpen,
12180                onOpenMenu: (tmenu, menu) => {
12181                    const sink = getLazySink().getOrDie();
12182                    Positioning.position(sink, menu, { anchor });
12183                    Sandboxing.decloak(sandbox);
12184                },
12185                onOpenSubmenu: (tmenu, item, submenu) => {
12186                    const sink = getLazySink().getOrDie();
12187                    Positioning.position(sink, submenu, {
12188                        anchor: {
12189                            type: 'submenu',
12190                            item
12191                        }
12192                    });
12193                    Sandboxing.decloak(sandbox);
12194                },
12195                onRepositionMenu: (tmenu, primaryMenu, submenuTriggers) => {
12196                    const sink = getLazySink().getOrDie();
12197                    Positioning.position(sink, primaryMenu, { anchor });
12198                    each$1(submenuTriggers, (st) => {
12199                        Positioning.position(sink, st.triggeredMenu, {
12200                            anchor: { type: 'submenu', item: st.triggeringItem }
12201                        });
12202                    });
12203                },
12204                onEscape: () => {
12205                    // Focus the triggering component after escaping the menu
12206                    Focusing.focus(component);
12207                    Sandboxing.close(sandbox);
12208                    return Optional.some(true);
12209                },
12210                onTab: onTabOutOfMenu,
12211                onShiftTab: onTabOutOfMenu
12212            }));
12213        }));
12214    };
12215    // onOpenSync is because some operations need to be applied immediately, not wrapped in a future
12216    // It can avoid things like flickering due to asynchronous bouncing
12217    const open = (detail, mapFetch, hotspot, sandbox, externals, onOpenSync, highlightOnOpen) => {
12218        const anchor = getAnchor(detail, hotspot);
12219        const processed = openF(detail, mapFetch, anchor, hotspot, sandbox, externals, highlightOnOpen);
12220        return processed.map((tdata) => {
12221            // If we have data, display a menu. Else, close the menu if it was open
12222            tdata.fold(() => {
12223                if (Sandboxing.isOpen(sandbox)) {
12224                    Sandboxing.close(sandbox);
12225                }
12226            }, (data) => {
12227                Sandboxing.cloak(sandbox);
12228                Sandboxing.open(sandbox, data);
12229                onOpenSync(sandbox);
12230            });
12231            return sandbox;
12232        });
12233    };
12234    const close = (detail, mapFetch, component, sandbox, _externals, _onOpenSync, _highlightOnOpen) => {
12235        Sandboxing.close(sandbox);
12236        return Future.pure(sandbox);
12237    };
12238    const togglePopup = (detail, mapFetch, hotspot, externals, onOpenSync, highlightOnOpen) => {
12239        const sandbox = Coupling.getCoupled(hotspot, 'sandbox');
12240        const showing = Sandboxing.isOpen(sandbox);
12241        const action = showing ? close : open;
12242        return action(detail, mapFetch, hotspot, sandbox, externals, onOpenSync, highlightOnOpen);
12243    };
12244    const matchWidth = (hotspot, container, useMinWidth) => {
12245        const menu = Composing.getCurrent(container).getOr(container);
12246        const buttonWidth = get$c(hotspot.element);
12247        if (useMinWidth) {
12248            set$7(menu.element, 'min-width', buttonWidth + 'px');
12249        }
12250        else {
12251            set$6(menu.element, buttonWidth);
12252        }
12253    };
12254    const getSink = (anyInSystem, sinkDetail) => anyInSystem
12255        .getSystem()
12256        .getByUid(sinkDetail.uid + '-' + suffix())
12257        .map((internalSink) => () => Result.value(internalSink))
12258        .getOrThunk(() => sinkDetail.lazySink.fold(() => () => Result.error(new Error('No internal sink is specified, nor could an external sink be found')), (lazySinkFn) => () => lazySinkFn(anyInSystem)));
12259    const doRepositionMenus = (sandbox) => {
12260        Sandboxing.getState(sandbox).each((tmenu) => {
12261            tieredMenu.repositionMenus(tmenu);
12262        });
12263    };
12264    const makeSandbox$1 = (detail, hotspot, extras) => {
vendor: 4,405 bytes, lines 12265-12372
12265        const ariaControls = manager();
12266        const onOpen = (component, menu) => {
12267            const anchor = getAnchor(detail, hotspot);
12268            ariaControls.link(hotspot.element);
12269            if (detail.matchWidth) {
12270                matchWidth(anchor.hotspot, menu, detail.useMinWidth);
12271            }
12272            detail.onOpen(anchor, component, menu);
12273            if (extras !== undefined && extras.onOpen !== undefined) {
12274                extras.onOpen(component, menu);
12275            }
12276            tieredMenu.repositionMenus(menu);
12277        };
12278        const onClose = (component, menu) => {
12279            ariaControls.unlink(hotspot.element);
12280            lazySink().getOr(menu).element.dom.dispatchEvent(new window.FocusEvent('focusout'));
12281            if (extras !== undefined && extras.onClose !== undefined) {
12282                extras.onClose(component, menu);
12283            }
12284        };
12285        const lazySink = getSink(hotspot, detail);
12286        return {
12287            dom: {
12288                tag: 'div',
12289                classes: detail.sandboxClasses,
12290                // TODO: Add aria-selected attribute
12291                attributes: {
12292                    id: ariaControls.id,
12293                }
12294            },
12295            behaviours: SketchBehaviours.augment(detail.sandboxBehaviours, [
12296                Representing.config({
12297                    store: {
12298                        mode: 'memory',
12299                        initialValue: hotspot
12300                    }
12301                }),
12302                Sandboxing.config({
12303                    onOpen,
12304                    onClose,
12305                    isPartOf: (container, data, queryElem) => {
12306                        return isPartOf(data, queryElem) || isPartOf(hotspot, queryElem);
12307                    },
12308                    getAttachPoint: () => {
12309                        return lazySink().getOrDie();
12310                    }
12311                }),
12312                // The Composing of the dropdown here is the the active menu of the TieredMenu
12313                // inside the sandbox.
12314                Composing.config({
12315                    find: (sandbox) => {
12316                        return Sandboxing.getState(sandbox).bind((menu) => Composing.getCurrent(menu));
12317                    }
12318                }),
12319                Receiving.config({
12320                    channels: {
12321                        ...receivingChannel$1({
12322                            isExtraPart: never
12323                        }),
12324                        ...receivingChannel({
12325                            doReposition: doRepositionMenus
12326                        })
12327                    }
12328                })
12329            ])
12330        };
12331    };
12332    const repositionMenus = (comp) => {
12333        const sandbox = Coupling.getCoupled(comp, 'sandbox');
12334        doRepositionMenus(sandbox);
12335    };
12336
12337    // TODO: Roll this back into Fields at some point
12338    // Unfortunately there appears to be a cyclical dependency or something that's preventing it, but for now this will do as it's home
12339    const sandboxFields = () => [
12340        defaulted('sandboxClasses', []),
12341        SketchBehaviours.field('sandboxBehaviours', [Composing, Receiving, Sandboxing, Representing])
12342    ];
12343
12344    const schema$f = constant$1([
12345        required$1('dom'),
12346        required$1('fetch'),
12347        onHandler('onOpen'),
12348        onKeyboardHandler('onExecute'),
12349        defaulted('getHotspot', Optional.some),
12350        defaulted('getAnchorOverrides', constant$1({})),
12351        schema$n(),
12352        field('dropdownBehaviours', [Toggling, Coupling, Keying, Focusing]),
12353        required$1('toggleClass'),
12354        defaulted('eventOrder', {}),
12355        option$3('lazySink'),
12356        defaulted('matchWidth', false),
12357        defaulted('useMinWidth', false),
12358        option$3('role'),
12359        option$3('listRole'),
12360    ].concat(sandboxFields()));
12361    const parts$c = constant$1([
12362        external$1({
12363            schema: [
12364                tieredMenuMarkers(),
12365                // Defining a defaulted field isn't necessary when dealing with
12366                // external parts, because the post-boulder part spec is not passed
12367                // through to any of these functions (defaults, overrides etc.). So all
12368                // this does is make it a bit clearer what you should expect, but remember
12369                // that the default value here is irrelevant!
12370                defaulted('fakeFocus', false)
12371            ],
12372            name: 'menu',
12373            defaults: (detail) => {
12374                return {
12375                    onExecute: detail.onExecute
12376                };
12377            }
12378        }),
12379        partType()
12380    ]);
12381
12382    const factory$k = (detail, components, _spec, externals) => {
12383        const lookupAttr = (attr) => get$h(detail.dom, 'attributes').bind((attrs) => get$h(attrs, attr));
12384        const switchToMenu = (sandbox) => {
12385            Sandboxing.getState(sandbox).each((tmenu) => {
12386                // This will highlight the menu AND the item
12387                tieredMenu.highlightPrimary(tmenu);
12388            });
12389        };
12390        const togglePopup$1 = (dropdownComp, onOpenSync, highlightOnOpen) => {
12391            return togglePopup(detail, identity, dropdownComp, externals, onOpenSync, highlightOnOpen);
12392        };
12393        const action = (component) => {
12394            const onOpenSync = switchToMenu;
12395            togglePopup$1(component, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);
12396        };
12397        const apis = {
12398            expand: (comp) => {
12399                if (!Toggling.isOn(comp)) {
12400                    togglePopup$1(comp, noop, HighlightOnOpen.HighlightNone).get(noop);
12401                }
12402            },
12403            open: (comp) => {
12404                if (!Toggling.isOn(comp)) {
12405                    togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).get(noop);
12406                }
12407            },
12408            refetch: (comp) => {
12409                // Generally, the triggers for a refetch should make it so that the
12410                // sandbox has been created, but it's not guaranteed, so we still handle the
12411                // case where there isn't yet a sandbox.
12412                const optSandbox = Coupling.getExistingCoupled(comp, 'sandbox');
12413                return optSandbox.fold(() => {
12414                    // If we don't have a sandbox, refetch is the same as open,
12415                    // except we return when it is completed.
12416                    return togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem)
12417                        .map(noop);
12418                }, (sandboxComp) => {
12419                    // We are intentionally not preserving the selected items when
12420                    // triggering a refetch, and will just highlight the first item.
12421                    // Note: this will mean that submenus will close. If we want to start
12422                    // preserving the selected items, we can't rely on the components themselves,
12423                    // so we'd need to use the item and menu values through Representing.
12424                    // However, be aware that alloy menus and items often have randomised values,
12425                    // so these might not be reliable either.
12426                    // NOTE: We use DropdownUtils.open directly, because we want it to 'open',
12427                    // even if it's already open. If we just used apis.open, it wouldn't do
12428                    // anything if it was already open, which means we wouldn't see the new
12429                    // refetched data.
12430                    return open(detail, identity, comp, 
12431                    // NOTE: The TieredMenu is inside the sandbox. They aren't the same component.
12432                    sandboxComp, externals, noop, HighlightOnOpen.HighlightMenuAndItem).map(noop);
12433                });
12434            },
12435            isOpen: Toggling.isOn,
12436            close: (comp) => {
12437                if (Toggling.isOn(comp)) {
12438                    togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).get(noop);
12439                }
12440            },
12441            // If we are open, refresh the menus in the tiered menu system
12442            repositionMenus: (comp) => {
12443                if (Toggling.isOn(comp)) {
12444                    repositionMenus(comp);
12445                }
12446            }
12447        };
12448        const triggerExecute = (comp, _se) => {
12449            emitExecute(comp);
12450            return Optional.some(true);
12451        };
12452        return {
12453            uid: detail.uid,
12454            dom: detail.dom,
12455            components,
12456            behaviours: augment(detail.dropdownBehaviours, [
12457                Toggling.config({
12458                    toggleClass: detail.toggleClass,
12459                    aria: {
12460                        mode: 'expanded'
12461                    }
12462                }),
12463                Coupling.config({
12464                    others: {
12465                        sandbox: (hotspot) => {
12466                            return makeSandbox$1(detail, hotspot, {
12467                                onOpen: () =>
vendor: 34,477 bytes, lines 12467-13326
12467 Toggling.on(hotspot),
12468                                onClose: () => Toggling.off(hotspot)
12469                            });
12470                        }
12471                    }
12472                }),
12473                Keying.config({
12474                    mode: 'special',
12475                    onSpace: triggerExecute,
12476                    onEnter: triggerExecute,
12477                    onDown: (comp, _se) => {
12478                        if (Dropdown.isOpen(comp)) {
12479                            const sandbox = Coupling.getCoupled(comp, 'sandbox');
12480                            switchToMenu(sandbox);
12481                        }
12482                        else {
12483                            Dropdown.open(comp);
12484                        }
12485                        return Optional.some(true);
12486                    },
12487                    onEscape: (comp, _se) => {
12488                        if (Dropdown.isOpen(comp)) {
12489                            Dropdown.close(comp);
12490                            return Optional.some(true);
12491                        }
12492                        else {
12493                            return Optional.none();
12494                        }
12495                    }
12496                }),
12497                Focusing.config({})
12498            ]),
12499            events: events(Optional.some(action)),
12500            eventOrder: {
12501                ...detail.eventOrder,
12502                // Order, the button state is toggled first, so assumed !selected means close.
12503                [execute$5()]: ['disabling', 'toggling', 'alloy.base.behaviour']
12504            },
12505            apis,
12506            domModification: {
12507                attributes: {
12508                    'aria-haspopup': detail.listRole.getOr('true'),
12509                    ...detail.role.fold(() => ({}), (role) => ({ role })),
12510                    ...detail.dom.tag === 'button' ? { type: lookupAttr('type').getOr('button') } : {}
12511                }
12512            }
12513        };
12514    };
12515    const Dropdown = composite({
12516        name: 'Dropdown',
12517        configFields: schema$f(),
12518        partFields: parts$c(),
12519        factory: factory$k,
12520        apis: {
12521            open: (apis, comp) => apis.open(comp),
12522            refetch: (apis, comp) => apis.refetch(comp),
12523            expand: (apis, comp) => apis.expand(comp),
12524            close: (apis, comp) => apis.close(comp),
12525            isOpen: (apis, comp) => apis.isOpen(comp),
12526            repositionMenus: (apis, comp) => apis.repositionMenus(comp)
12527        }
12528    });
12529
12530    const owner$1 = 'form';
12531    const schema$e = [
12532        field('formBehaviours', [Representing])
12533    ];
12534    const getPartName$1 = (name) => '<alloy.field.' + name + '>';
12535    const sketch$2 = (fSpec) => {
12536        const parts = (() => {
12537            const record = [];
12538            const field = (name, config) => {
12539                record.push(name);
12540                return generateOne$1(owner$1, getPartName$1(name), config);
12541            };
12542            return {
12543                field,
12544                record: constant$1(record)
12545            };
12546        })();
12547        const spec = fSpec(parts);
12548        const partNames = parts.record();
12549        // Unlike other sketches, a form does not know its parts in advance (as they represent each field
12550        // in a particular form). Therefore, it needs to calculate the part names on the fly
12551        const fieldParts = map$2(partNames, (n) => required({ name: n, pname: getPartName$1(n) }));
12552        return composite$1(owner$1, schema$e, fieldParts, make$4, spec);
12553    };
12554    const toResult = (o, e) => o.fold(() => Result.error(e), Result.value);
12555    const make$4 = (detail, components) => ({
12556        uid: detail.uid,
12557        dom: detail.dom,
12558        components,
12559        // Form has an assumption that every field must have composing, and that the composed element has representing.
12560        behaviours: augment(detail.formBehaviours, [
12561            Representing.config({
12562                store: {
12563                    mode: 'manual',
12564                    getValue: (form) => {
12565                        const resPs = getAllParts(form, detail);
12566                        return map$1(resPs, (resPThunk, pName) => resPThunk().bind((v) => {
12567                            const opt = Composing.getCurrent(v);
12568                            return toResult(opt, new Error(`Cannot find a current component to extract the value from for form part '${pName}': ` + element(v.element)));
12569                        }).map(Representing.getValue));
12570                    },
12571                    setValue: (form, values) => {
12572                        each(values, (newValue, key) => {
12573                            getPart(form, detail, key).each((wrapper) => {
12574                                Composing.getCurrent(wrapper).each((field) => {
12575                                    Representing.setValue(field, newValue);
12576                                });
12577                            });
12578                        });
12579                    }
12580                }
12581            })
12582        ]),
12583        apis: {
12584            getField: (form, key) => {
12585                // Returns an Optional (not a result);
12586                return getPart(form, detail, key).bind(Composing.getCurrent);
12587            }
12588        }
12589    });
12590    const Form = {
12591        getField: makeApi((apis, component, key) => apis.getField(component, key)),
12592        sketch: sketch$2
12593    };
12594
12595    const schema$d = constant$1([
12596        required$1('dom'),
12597        defaulted('shell', true),
12598        field('toolbarBehaviours', [Replacing])
12599    ]);
12600    // TODO: Dupe with Toolbar
12601    const enhanceGroups = () => ({
12602        behaviours: derive$1([
12603            Replacing.config({})
12604        ])
12605    });
12606    const parts$b = constant$1([
12607        // Note, is the container for putting all the groups in, not a group itself.
12608        optional({
12609            name: 'groups',
12610            overrides: enhanceGroups
12611        })
12612    ]);
12613
12614    const factory$j = (detail, components, _spec, _externals) => {
12615        const setGroups = (toolbar, groups) => {
12616            getGroupContainer(toolbar).fold(() => {
12617                // check that the group container existed. It may not have if the components
12618                // did not list anything, and shell was false.
12619                // eslint-disable-next-line no-console
12620                console.error('Toolbar was defined to not be a shell, but no groups container was specified in components');
12621                throw new Error('Toolbar was defined to not be a shell, but no groups container was specified in components');
12622            }, (container) => {
12623                Replacing.set(container, groups);
12624            });
12625        };
12626        const getGroupContainer = (component) => detail.shell ? Optional.some(component) : getPart(component, detail, 'groups');
12627        // In shell mode, the group overrides need to be added to the main container, and there can be no children
12628        const extra = detail.shell ? { behaviours: [Replacing.config({})], components: [] } : { behaviours: [], components };
12629        return {
12630            uid: detail.uid,
12631            dom: detail.dom,
12632            components: extra.components,
12633            behaviours: augment(detail.toolbarBehaviours, extra.behaviours),
12634            apis: {
12635                setGroups,
12636                refresh: noop
12637            },
12638            domModification: {
12639                attributes: {
12640                    role: 'toolbar'
12641                }
12642            }
12643        };
12644    };
12645    const Toolbar = composite({
12646        name: 'Toolbar',
12647        configFields: schema$d(),
12648        partFields: parts$b(),
12649        factory: factory$j,
12650        apis: {
12651            setGroups: (apis, toolbar, groups) => {
12652                apis.setGroups(toolbar, groups);
12653            }
12654        }
12655    });
12656
12657    const schema$c = constant$1([
12658        markers$1(['toggledClass']),
12659        required$1('lazySink'),
12660        requiredFunction('fetch'),
12661        optionFunction('getBounds'),
12662        optionObjOf('fireDismissalEventInstead', [
12663            defaulted('event', dismissRequested())
12664        ]),
12665        schema$n(),
12666        onHandler('onToggled'),
12667    ]);
12668    const parts$a = constant$1([
12669        external$1({
12670            name: 'button',
12671            overrides: (detail) => ({
12672                dom: {
12673                    attributes: {
12674                        'aria-haspopup': 'true'
12675                    }
12676                },
12677                buttonBehaviours: derive$1([
12678                    Toggling.config({
12679                        toggleClass: detail.markers.toggledClass,
12680                        aria: {
12681                            mode: 'expanded'
12682                        },
12683                        toggleOnExecute: false,
12684                        /**
12685                         * For FloatingToolbars, we can hook up our `onToggled` handler directly to the Toggling
12686                         * because we don't have to worry about any animations.
12687                         *
12688                         * Unfortunately, for SlidingToolbars, Toggling is more directly hooked into the animation for growing,
12689                         * so to have an event `onToggled` that doesn't care about the animation, we can't just hook into the Toggling config.
12690                         */
12691                        onToggled: detail.onToggled
12692                    })
12693                ])
12694            })
12695        }),
12696        external$1({
12697            factory: Toolbar,
12698            schema: schema$d(),
12699            name: 'toolbar',
12700            overrides: (detail) => {
12701                return {
12702                    toolbarBehaviours: derive$1([
12703                        Keying.config({
12704                            mode: 'cyclic',
12705                            onEscape: (comp) => {
12706                                getPart(comp, detail, 'button').each(Focusing.focus);
12707                                // Don't return true here, as we need to allow the sandbox to handle the escape to close the overflow
12708                                return Optional.none();
12709                            }
12710                        })
12711                    ])
12712                };
12713            }
12714        })
12715    ]);
12716
12717    const shouldSkipFocus = value$2();
12718    const toggleWithoutFocusing = (button, externals) => {
12719        shouldSkipFocus.set(true);
12720        toggle(button, externals);
12721        shouldSkipFocus.clear();
12722    };
12723    const toggle = (button, externals) => {
12724        const toolbarSandbox = Coupling.getCoupled(button, 'toolbarSandbox');
12725        if (Sandboxing.isOpen(toolbarSandbox)) {
12726            Sandboxing.close(toolbarSandbox);
12727        }
12728        else {
12729            Sandboxing.open(toolbarSandbox, externals.toolbar());
12730        }
12731    };
12732    const position = (button, toolbar, detail, layouts) => {
12733        const bounds = detail.getBounds.map((bounder) => bounder());
12734        const sink = detail.lazySink(button).getOrDie();
12735        Positioning.positionWithinBounds(sink, toolbar, {
12736            anchor: {
12737                type: 'hotspot',
12738                hotspot: button,
12739                layouts,
12740                overrides: {
12741                    maxWidthFunction: expandable()
12742                }
12743            }
12744        }, bounds);
12745    };
12746    const setGroups$1 = (button, toolbar, detail, layouts, groups) => {
12747        Toolbar.setGroups(toolbar, groups);
12748        position(button, toolbar, detail, layouts);
12749        Toggling.on(button);
12750    };
12751    const makeSandbox = (button, spec, detail) => {
12752        const ariaControls = manager();
12753        const onOpen = (sandbox, toolbar) => {
12754            const skipFocus = shouldSkipFocus.get().getOr(false);
12755            detail.fetch().get((groups) => {
12756                setGroups$1(button, toolbar, detail, spec.layouts, groups);
12757                ariaControls.link(button.element);
12758                if (!skipFocus) {
12759                    Keying.focusIn(toolbar);
12760                }
12761            });
12762        };
12763        const onClose = () => {
12764            // Toggle and focus the button
12765            Toggling.off(button);
12766            if (!shouldSkipFocus.get().getOr(false)) {
12767                Focusing.focus(button);
12768            }
12769            ariaControls.unlink(button.element);
12770        };
12771        return {
12772            dom: {
12773                tag: 'div',
12774                attributes: {
12775                    id: ariaControls.id
12776                }
12777            },
12778            behaviours: derive$1([
12779                Keying.config({
12780                    mode: 'special',
12781                    onEscape: (comp) => {
12782                        Sandboxing.close(comp);
12783                        return Optional.some(true);
12784                    }
12785                }),
12786                Sandboxing.config({
12787                    onOpen,
12788                    onClose,
12789                    isPartOf: (container, data, queryElem) => {
12790                        return isPartOf(data, queryElem) || isPartOf(button, queryElem);
12791                    },
12792                    getAttachPoint: () => {
12793                        return detail.lazySink(button).getOrDie();
12794                    }
12795                }),
12796                Receiving.config({
12797                    channels: {
12798                        ...receivingChannel$1({
12799                            isExtraPart: never,
12800                            ...detail.fireDismissalEventInstead.map((fe) => ({ fireEventInstead: { event: fe.event } })).getOr({})
12801                        }),
12802                        ...receivingChannel({
12803                            doReposition: () => {
12804                                Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each((toolbar) => {
12805                                    position(button, toolbar, detail, spec.layouts);
12806                                });
12807                            }
12808                        })
12809                    }
12810                })
12811            ])
12812        };
12813    };
12814    const factory$i = (detail, components, spec, externals) => ({
12815        ...Button.sketch({
12816            ...externals.button(),
12817            action: (button) => {
12818                toggle(button, externals);
12819            },
12820            buttonBehaviours: SketchBehaviours.augment({ dump: externals.button().buttonBehaviours }, [
12821                Coupling.config({
12822                    others: {
12823                        toolbarSandbox: (button) => {
12824                            return makeSandbox(button, spec, detail);
12825                        }
12826                    }
12827                })
12828            ])
12829        }),
12830        apis: {
12831            setGroups: (button, groups) => {
12832                Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each((toolbar) => {
12833                    setGroups$1(button, toolbar, detail, spec.layouts, groups);
12834                });
12835            },
12836            reposition: (button) => {
12837                Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each((toolbar) => {
12838                    position(button, toolbar, detail, spec.layouts);
12839                });
12840            },
12841            toggle: (button) => {
12842                toggle(button, externals);
12843            },
12844            toggleWithoutFocusing: (button) => {
12845                toggleWithoutFocusing(button, externals);
12846            },
12847            getToolbar: (button) => {
12848                return Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox'));
12849            },
12850            isOpen: (button) => {
12851                return Sandboxing.isOpen(Coupling.getCoupled(button, 'toolbarSandbox'));
12852            }
12853        }
12854    });
12855    const FloatingToolbarButton = composite({
12856        name: 'FloatingToolbarButton',
12857        factory: factory$i,
12858        configFields: schema$c(),
12859        partFields: parts$a(),
12860        apis: {
12861            setGroups: (apis, button, groups) => {
12862                apis.setGroups(button, groups);
12863            },
12864            reposition: (apis, button) => {
12865                apis.reposition(button);
12866            },
12867            toggle: (apis, button) => {
12868                apis.toggle(button);
12869            },
12870            toggleWithoutFocusing: (apis, button) => {
12871                apis.toggleWithoutFocusing(button);
12872            },
12873            getToolbar: (apis, button) => apis.getToolbar(button),
12874            isOpen: (apis, button) => apis.isOpen(button)
12875        }
12876    });
12877
12878    const schema$b = constant$1([
12879        defaulted('prefix', 'form-field'),
12880        field('fieldBehaviours', [Composing, Representing])
12881    ]);
12882    const parts$9 = constant$1([
12883        optional({
12884            schema: [required$1('dom')],
12885            name: 'label'
12886        }),
12887        optional({
12888            factory: {
12889                sketch: (spec) => {
12890                    return {
12891                        uid: spec.uid,
12892                        dom: {
12893                            tag: 'span',
12894                            styles: {
12895                                display: 'none'
12896                            },
12897                            attributes: {
12898                                'aria-hidden': 'true'
12899                            },
12900                            innerHtml: spec.text
12901                        }
12902                    };
12903                }
12904            },
12905            schema: [required$1('text')],
12906            name: 'aria-descriptor'
12907        }),
12908        required({
12909            factory: {
12910                sketch: (spec) => {
12911                    const excludeFactory = exclude(spec, ['factory']);
12912                    return spec.factory.sketch(excludeFactory);
12913                }
12914            },
12915            schema: [required$1('factory')],
12916            name: 'field'
12917        })
12918    ]);
12919
12920    const factory$h = (detail, components, _spec, _externals) => {
12921        const behaviours = augment(detail.fieldBehaviours, [
12922            Composing.config({
12923                find: (container) => {
12924                    return getPart(container, detail, 'field');
12925                }
12926            }),
12927            Representing.config({
12928                store: {
12929                    mode: 'manual',
12930                    getValue: (field) => {
12931                        return Composing.getCurrent(field).bind(Representing.getValue);
12932                    },
12933                    setValue: (field, value) => {
12934                        Composing.getCurrent(field).each((current) => {
12935                            Representing.setValue(current, value);
12936                        });
12937                    }
12938                }
12939            })
12940        ]);
12941        const events = derive$2([
12942            // Used to be systemInit
12943            runOnAttached((component, _simulatedEvent) => {
12944                const ps = getParts(component, detail, ['label', 'field', 'aria-descriptor']);
12945                ps.field().each((field) => {
12946                    const id = generate$6(detail.prefix);
12947                    ps.label().each((label) => {
12948                        // TODO: Find a nicer way of doing this.
12949                        set$9(label.element, 'for', id);
12950                        set$9(field.element, 'id', id);
12951                    });
12952                    ps['aria-descriptor']().each((descriptor) => {
12953                        const descriptorId = generate$6(detail.prefix);
12954                        set$9(descriptor.element, 'id', descriptorId);
12955                        set$9(field.element, 'aria-describedby', descriptorId);
12956                    });
12957                });
12958            })
12959        ]);
12960        const apis = {
12961            getField: (container) => getPart(container, detail, 'field'),
12962            getLabel: (container) => 
12963            // TODO: Use constants for part names
12964            getPart(container, detail, 'label')
12965        };
12966        return {
12967            uid: detail.uid,
12968            dom: detail.dom,
12969            components,
12970            behaviours,
12971            events,
12972            apis
12973        };
12974    };
12975    const FormField = composite({
12976        name: 'FormField',
12977        configFields: schema$b(),
12978        partFields: parts$9(),
12979        factory: factory$h,
12980        apis: {
12981            getField: (apis, comp) => apis.getField(comp),
12982            getLabel: (apis, comp) => apis.getLabel(comp)
12983        }
12984    });
12985
12986    const schema$a = constant$1([
12987        defaulted('field1Name', 'field1'),
12988        defaulted('field2Name', 'field2'),
12989        onStrictHandler('onLockedChange'),
12990        markers$1(['lockClass']),
12991        defaulted('locked', false),
12992        SketchBehaviours.field('coupledFieldBehaviours', [Composing, Representing]),
12993        defaultedFunction('onInput', noop)
12994    ]);
12995    const getField = (comp, detail, partName) => getPart(comp, detail, partName).bind(Composing.getCurrent);
12996    const coupledPart = (selfName, otherName) => required({
12997        factory: FormField,
12998        name: selfName,
12999        overrides: (detail) => {
13000            return {
13001                fieldBehaviours: derive$1([
13002                    config('coupled-input-behaviour', [
13003                        run$1(input(), (me) => {
13004                            getField(me, detail, otherName).each((other) => {
13005                                getPart(me, detail, 'lock').each((lock) => {
13006                                    // TODO IMPROVEMENT: Allow locker to fire onLockedChange if it is turned on after being off.
13007                                    if (Toggling.isOn(lock)) {
13008                                        detail.onLockedChange(me, other, lock);
13009                                    }
13010                                    detail.onInput(me);
13011                                });
13012                            });
13013                        })
13014                    ])
13015                ])
13016            };
13017        }
13018    });
13019    const parts$8 = constant$1([
13020        coupledPart('field1', 'field2'),
13021        coupledPart('field2', 'field1'),
13022        required({
13023            factory: Button,
13024            schema: [
13025                required$1('dom')
13026            ],
13027            name: 'lock',
13028            overrides: (detail) => {
13029                return {
13030                    buttonBehaviours: derive$1([
13031                        Toggling.config({
13032                            selected: detail.locked,
13033                            toggleClass: detail.markers.lockClass,
13034                            aria: {
13035                                mode: 'pressed'
13036                            }
13037                        })
13038                    ])
13039                };
13040            }
13041        })
13042    ]);
13043
13044    const factory$g = (detail, components, _spec, _externals) => ({
13045        uid: detail.uid,
13046        dom: detail.dom,
13047        components,
13048        behaviours: SketchBehaviours.augment(detail.coupledFieldBehaviours, [
13049            Composing.config({ find: Optional.some }),
13050            Representing.config({
13051                store: {
13052                    mode: 'manual',
13053                    getValue: (comp) => {
13054                        const parts = getPartsOrDie(comp, detail, ['field1', 'field2']);
13055                        return {
13056                            [detail.field1Name]: Representing.getValue(parts.field1()),
13057                            [detail.field2Name]: Representing.getValue(parts.field2())
13058                        };
13059                    },
13060                    setValue: (comp, value) => {
13061                        const parts = getPartsOrDie(comp, detail, ['field1', 'field2']);
13062                        if (hasNonNullableKey(value, detail.field1Name)) {
13063                            Representing.setValue(parts.field1(), value[detail.field1Name]);
13064                        }
13065                        if (hasNonNullableKey(value, detail.field2Name)) {
13066                            Representing.setValue(parts.field2(), value[detail.field2Name]);
13067                        }
13068                    }
13069                }
13070            })
13071        ]),
13072        apis: {
13073            getField1: (component) => getPart(component, detail, 'field1'),
13074            getField2: (component) => getPart(component, detail, 'field2'),
13075            getLock: (component) => getPart(component, detail, 'lock')
13076        }
13077    });
13078    const FormCoupledInputs = composite({
13079        name: 'FormCoupledInputs',
13080        configFields: schema$a(),
13081        partFields: parts$8(),
13082        factory: factory$g,
13083        apis: {
13084            getField1: (apis, component) => apis.getField1(component),
13085            getField2: (apis, component) => apis.getField2(component),
13086            getLock: (apis, component) => apis.getLock(component)
13087        }
13088    });
13089
13090    const factory$f = (detail, _spec) => {
13091        const options = map$2(detail.options, (option) => ({
13092            dom: {
13093                tag: 'option',
13094                value: option.value,
13095                innerHtml: option.text
13096            }
13097        }));
13098        const initialValues = detail.data.map((v) => wrap('initialValue', v)).getOr({});
13099        return {
13100            uid: detail.uid,
13101            dom: {
13102                tag: 'select',
13103                classes: detail.selectClasses,
13104                attributes: detail.selectAttributes
13105            },
13106            components: options,
13107            behaviours: augment(detail.selectBehaviours, [
13108                Focusing.config({}),
13109                Representing.config({
13110                    store: {
13111                        mode: 'manual',
13112                        getValue: (select) => {
13113                            return get$5(select.element);
13114                        },
13115                        setValue: (select, newValue) => {
13116                            const firstOption = head(detail.options);
13117                            // This is probably generically useful ... may become a part of Representing.
13118                            const found = find$5(detail.options, (opt) => opt.value === newValue);
13119                            if (found.isSome()) {
13120                                set$4(select.element, newValue);
13121                            }
13122                            else if (select.element.dom.selectedIndex === -1 && newValue === '') {
13123                                /*
13124                                  Sometimes after a redial alloy tries to set a new value, but if no value has been set in the data this used to fail. Now we set the value to the first option in the list if:
13125                                    The index is out of range, indicating that the list of options have changed, or was never set.
13126                                    The user is not trying to set a specific value (which would be user error)
13127                                */
13128                                firstOption.each((value) => set$4(select.element, value.value));
13129                            }
13130                        },
13131                        ...initialValues
13132                    }
13133                })
13134            ])
13135        };
13136    };
13137    const HtmlSelect = single({
13138        name: 'HtmlSelect',
13139        configFields: [
13140            required$1('options'),
13141            field('selectBehaviours', [Focusing, Representing]),
13142            defaulted('selectClasses', []),
13143            defaulted('selectAttributes', {}),
13144            option$3('data')
13145        ],
13146        factory: factory$f
13147    });
13148
13149    const makeMenu = (detail, menuSandbox, placementSpec, menuSpec, getBounds) => {
13150        const lazySink = () => detail.lazySink(menuSandbox);
13151        const layouts = menuSpec.type === 'horizontal' ? { layouts: {
13152                onLtr: () => belowOrAbove(),
13153                onRtl: () => belowOrAboveRtl()
13154            } } : {};
13155        const isFirstTierSubmenu = (triggeringPaths) => triggeringPaths.length === 2; // primary and first tier menu === 2 items
13156        const getSubmenuLayouts = (triggeringPaths) => isFirstTierSubmenu(triggeringPaths) ? layouts : {};
13157        return tieredMenu.sketch({
13158            dom: {
13159                tag: 'div'
13160            },
13161            data: menuSpec.data,
13162            markers: menuSpec.menu.markers,
13163            highlightOnOpen: menuSpec.menu.highlightOnOpen,
13164            fakeFocus: menuSpec.menu.fakeFocus,
13165            onEscape: () => {
13166                // Note for the future: this should possibly also call detail.onHide
13167                Sandboxing.close(menuSandbox);
13168                detail.onEscape.map((handler) => handler(menuSandbox));
13169                return Optional.some(true);
13170            },
13171            onExecute: () => {
13172                return Optional.some(true);
13173            },
13174            onOpenMenu: (tmenu, menu) => {
13175                Positioning.positionWithinBounds(lazySink().getOrDie(), menu, placementSpec, getBounds());
13176            },
13177            onOpenSubmenu: (tmenu, item, submenu, triggeringPaths) => {
13178                const sink = lazySink().getOrDie();
13179                Positioning.position(sink, submenu, {
13180                    anchor: {
13181                        type: 'submenu',
13182                        item,
13183                        ...getSubmenuLayouts(triggeringPaths)
13184                    }
13185                });
13186            },
13187            onRepositionMenu: (tmenu, primaryMenu, submenuTriggers) => {
13188                const sink = lazySink().getOrDie();
13189                Positioning.positionWithinBounds(sink, primaryMenu, placementSpec, getBounds());
13190                each$1(submenuTriggers, (st) => {
13191                    const submenuLayouts = getSubmenuLayouts(st.triggeringPath);
13192                    Positioning.position(sink, st.triggeredMenu, {
13193                        anchor: { type: 'submenu', item: st.triggeringItem, ...submenuLayouts }
13194                    });
13195                });
13196            }
13197        });
13198    };
13199    const factory$e = (detail, spec) => {
13200        const isPartOfRelated = (sandbox, queryElem) => {
13201            const related = detail.getRelated(sandbox);
13202            return related.exists((rel) => isPartOf(rel, queryElem));
13203        };
13204        const setContent = (sandbox, thing) => {
13205            // Keep the same location, and just change the content.
13206            Sandboxing.setContent(sandbox, thing);
13207        };
13208        const showAt = (sandbox, thing, placementSpec) => {
13209            const getBounds = Optional.none;
13210            showWithinBounds(sandbox, thing, placementSpec, getBounds);
13211        };
13212        const showWithinBounds = (sandbox, thing, placementSpec, getBounds) => {
13213            const sink = detail.lazySink(sandbox).getOrDie();
13214            Sandboxing.openWhileCloaked(sandbox, thing, () => Positioning.positionWithinBounds(sink, sandbox, placementSpec, getBounds()));
13215            Representing.setValue(sandbox, Optional.some({
13216                mode: 'position',
13217                config: placementSpec,
13218                getBounds
13219            }));
13220        };
13221        // TODO AP-191 write a test for showMenuAt
13222        const showMenuAt = (sandbox, placementSpec, menuSpec) => {
13223            showMenuWithinBounds(sandbox, placementSpec, menuSpec, Optional.none);
13224        };
13225        const showMenuWithinBounds = (sandbox, placementSpec, menuSpec, getBounds) => {
13226            const menu = makeMenu(detail, sandbox, placementSpec, menuSpec, getBounds);
13227            Sandboxing.open(sandbox, menu);
13228            Representing.setValue(sandbox, Optional.some({
13229                mode: 'menu',
13230                menu
13231            }));
13232        };
13233        const hide = (sandbox) => {
13234            if (Sandboxing.isOpen(sandbox)) {
13235                Representing.setValue(sandbox, Optional.none());
13236                Sandboxing.close(sandbox);
13237            }
13238        };
13239        const getContent = (sandbox) => Sandboxing.getState(sandbox);
13240        const reposition = (sandbox) => {
13241            if (Sandboxing.isOpen(sandbox)) {
13242                Representing.getValue(sandbox).each((state) => {
13243                    switch (state.mode) {
13244                        case 'menu':
13245                            Sandboxing.getState(sandbox).each(tieredMenu.repositionMenus);
13246                            break;
13247                        case 'position':
13248                            const sink = detail.lazySink(sandbox).getOrDie();
13249                            Positioning.positionWithinBounds(sink, sandbox, state.config, state.getBounds());
13250                            break;
13251                    }
13252                });
13253            }
13254        };
13255        const apis = {
13256            setContent,
13257            showAt,
13258            showWithinBounds,
13259            showMenuAt,
13260            showMenuWithinBounds,
13261            hide,
13262            getContent,
13263            reposition,
13264            isOpen: Sandboxing.isOpen
13265        };
13266        return {
13267            uid: detail.uid,
13268            dom: detail.dom,
13269            behaviours: augment(detail.inlineBehaviours, [
13270                Sandboxing.config({
13271                    isPartOf: (sandbox, data, queryElem) => {
13272                        return isPartOf(data, queryElem) || isPartOfRelated(sandbox, queryElem);
13273                    },
13274                    getAttachPoint: (sandbox) => {
13275                        return detail.lazySink(sandbox).getOrDie();
13276                    },
13277                    onOpen: (sandbox) => {
13278                        detail.onShow(sandbox);
13279                    },
13280                    onClose: (sandbox) => {
13281                        detail.onHide(sandbox);
13282                    }
13283                }),
13284                Representing.config({
13285                    store: {
13286                        mode: 'memory',
13287                        initialValue: Optional.none()
13288                    }
13289                }),
13290                Receiving.config({
13291                    channels: {
13292                        ...receivingChannel$1({
13293                            isExtraPart: spec.isExtraPart,
13294                            ...detail.fireDismissalEventInstead.map((fe) => ({ fireEventInstead: { event: fe.event } })).getOr({})
13295                        }),
13296                        ...receivingChannel({
13297                            ...detail.fireRepositionEventInstead.map((fe) => ({ fireEventInstead: { event: fe.event } })).getOr({}),
13298                            doReposition: reposition
13299                        })
13300                    }
13301                })
13302            ]),
13303            eventOrder: detail.eventOrder,
13304            apis
13305        };
13306    };
13307    const InlineView = single({
13308        name: 'InlineView',
13309        configFields: [
13310            required$1('lazySink'),
13311            onHandler('onShow'),
13312            onHandler('onHide'),
13313            optionFunction('onEscape'),
13314            field('inlineBehaviours', [Sandboxing, Representing, Receiving]),
13315            optionObjOf('fireDismissalEventInstead', [
13316                defaulted('event', dismissRequested())
13317            ]),
13318            optionObjOf('fireRepositionEventInstead', [
13319                defaulted('event', repositionRequested())
13320            ]),
13321            defaulted('getRelated', Optional.none),
13322            defaulted('isExtraPart', never),
13323            defaulted('eventOrder', Optional.none)
13324        ],
13325        factory: factory$e,
13326        apis: {
13327            showAt: (apis, component, anchor, thing) => {
13328                apis.showAt(component, anchor, thing);
13329            },
13330            showWithinBounds: (apis, component, anchor, thing, bounds) => {
13331                apis.showWithinBounds(component, anchor, thing, bounds);
13332            },
13333            showMenuAt: (apis, component, anchor, menuSpec) => {
13334                apis.showMenuAt(component, anchor, menuSpec);
13335            },
13336            showMenuWithinBounds: (apis, component, anchor, menuSpec, bounds) => {
13337                apis.showMenuWithinBounds(component, anchor, menuSpec, bounds);
13338            },
13339            hide: (apis, component) => {
13340                apis.hide(component);
13341            },
13342            isOpen: (apis, component) => apis.isOpen(component),
13343            getContent: (apis, component) => apis.getContent(component),
13344            setContent: (apis, component, thing) => {
13345                apis.setContent(component, thing);
13346            },
13347            reposition: (apis, component) => {
13348                apis.reposition(component);
13349            }
13350        }
13351    });
13352
13353    const schema$9 = constant$1([
13354        defaultedString('type', 'text'),
13355        option$3('data'),
13356        defaulted('inputAttributes', {}),
13357        defaulted('inputStyles', {}),
13358        defaulted('tag', 'input'),
13359        defaulted('inputClasses', []),
13360        onHandler('onSetValue'),
13361        defaultedFunction('fromInputValue', identity),
13362        defaultedFunction('toInputValue', identity),
13363        defaulted('styles', {}),
13364        defaulted('eventOrder', {}),
13365        field('inputBehaviours', [Representing, Focusing]),
13366        defaulted('selectOnFocus', true)
13367    ]);
13368    const focusBehaviours = (detail) => derive$1([
13369        Focusing.config({
13370            onFocus: !detail.selectOnFocus ? noop : (component) => {
13371                const input = component.element;
13372                const value = get$5(input);
13373                // TODO: There are probably more types that can't handle setSelectionRange
13374                if (detail.type !== 'range') {
13375                    input.dom.setSelectionRange(0, value.length);
13376                }
13377            }
13378        })
13379    ]);
13380    const behaviours = (detail) => ({
13381        ...focusBehaviours(detail),
13382        ...augment(detail.inputBehaviours, [
13383            Representing.config({
13384                store: {
13385                    mode: 'manual',
13386                    // Propagating its Optional
13387                    ...detail.data.map((data) => ({ initialValue: data })).getOr({}),
13388                    getValue: (input) => {
13389                        return detail.fromInputValue(get$5(input.element));
13390                    },
13391                    setValue: (input, data) => {
13392                        const current = get$5(input.element);
13393                        // Only set it if it has changed ... otherwise the cursor goes to the end.
13394                        if (current !== data) {
13395                            set$4(input.element, detail.toInputValue(data));
13396                        }
13397                    }
13398                },
13399                onSetValue: detail.onSetValue
13400            })
13401        ])
13402    });
13403    const dom$1 = (detail) => ({
13404        tag: detail.tag,
13405        attributes: {
13406            type: detail.type,
13407            ...detail.inputAttributes
13408        },
13409        styles: detail.inputStyles,
13410        classes: detail.inputClasses
13411    });
13412
13413    const factory$d = (detail, _spec) => ({
13414        uid: detail.uid,
13415        dom: dom$1(detail),
13416        // No children.
13417        components: [],
13418        behaviours: behaviours(detail),
13419        eventOrder: detail.eventOrder
13420    });
13421    const Input = single({
13422        name: 'Input',
13423        configFields: schema$9(),
13424        factory: factory$d
13425    });
13426
13427    const parts$7 = generate$5(owner$2(), parts$e());
13428
13429    const labelledBy = (labelledElement, labelElement) => {
13430        const labelId = getOpt(labelledElement, 'id')
13431            .fold(() => {
13432            const id = generate$6('dialog-label');
13433            set$9(labelElement, 'id', id);
13434            return id;
13435        }, identity);
13436        set$9(labelledElement, 'aria-labelledby', labelId);
13437    };
13438
13439    const schema$8 = constant$1([
13440        required$1('lazySink'),
13441        option$3('dragBlockClass'),
vendor: 5,204 bytes, lines 13442-13581
13442        defaultedFunction('getBounds', win),
13443        defaulted('useTabstopAt', always),
13444        defaulted('firstTabstop', 0),
13445        defaulted('eventOrder', {}),
13446        defaultedStringEnum('role', 'dialog', ['dialog', 'alertdialog']),
13447        field('modalBehaviours', [Keying]),
13448        onKeyboardHandler('onExecute'),
13449        onStrictKeyboardHandler('onEscape')
13450    ]);
13451    const basic = { sketch: identity };
13452    const parts$6 = constant$1([
13453        optional({
13454            name: 'draghandle',
13455            overrides: (detail, spec) => {
13456                return {
13457                    behaviours: derive$1([
13458                        Dragging.config({
13459                            mode: 'mouse',
13460                            getTarget: (handle) => {
13461                                return ancestor$1(handle, '[role="dialog"]').getOr(handle);
13462                            },
13463                            blockerClass: detail.dragBlockClass.getOrDie(
13464                            // TODO: Support errors in Optional getOrDie.
13465                            new Error('The drag blocker class was not specified for a dialog with a drag handle: \n' +
13466                                JSON.stringify(spec, null, 2)).message),
13467                            getBounds: detail.getDragBounds
13468                        })
13469                    ])
13470                };
13471            }
13472        }),
13473        required({
13474            schema: [required$1('dom')],
13475            name: 'title'
13476        }),
13477        required({
13478            factory: basic,
13479            schema: [required$1('dom')],
13480            name: 'close'
13481        }),
13482        required({
13483            factory: basic,
13484            schema: [required$1('dom')],
13485            name: 'body'
13486        }),
13487        optional({
13488            factory: basic,
13489            schema: [required$1('dom')],
13490            name: 'footer'
13491        }),
13492        external$1({
13493            factory: {
13494                sketch: (spec, detail) => 
13495                // Merging should take care of the uid
13496                ({
13497                    ...spec,
13498                    dom: detail.dom,
13499                    components: detail.components
13500                })
13501            },
13502            schema: [
13503                defaulted('dom', {
13504                    tag: 'div',
13505                    styles: {
13506                        position: 'fixed',
13507                        left: '0px',
13508                        top: '0px',
13509                        right: '0px',
13510                        bottom: '0px'
13511                    }
13512                }),
13513                defaulted('components', [])
13514            ],
13515            name: 'blocker'
13516        })
13517    ]);
13518
13519    const factory$c = (detail, components, spec, externals) => {
13520        const dialogComp = value$2();
13521        // TODO IMPROVEMENT: Make close actually close the dialog by default!
13522        const showDialog = (dialog) => {
13523            dialogComp.set(dialog);
13524            const sink = detail.lazySink(dialog).getOrDie();
13525            const externalBlocker = externals.blocker();
13526            const blocker = sink.getSystem().build({
13527                ...externalBlocker,
13528                components: externalBlocker.components.concat([
13529                    premade(dialog)
13530                ]),
13531                behaviours: derive$1([
13532                    Focusing.config({}),
13533                    config('dialog-blocker-events', [
13534                        // Ensure we use runOnSource otherwise this would cause an infinite loop, as `focusIn` would fire a `focusin` which would then get responded to and so forth
13535                        runOnSource(focusin(), () => {
13536                            Blocking.isBlocked(dialog) ? noop() : Keying.focusIn(dialog);
13537                        })
13538                    ])
13539                ])
13540            });
13541            attach(sink, blocker);
13542            Keying.focusIn(dialog);
13543        };
13544        const hideDialog = (dialog) => {
13545            dialogComp.clear();
13546            parent(dialog.element).each((blockerDom) => {
13547                dialog.getSystem().getByDom(blockerDom).each((blocker) => {
13548                    detach(blocker);
13549                });
13550            });
13551        };
13552        const getDialogBody = (dialog) => getPartOrDie(dialog, detail, 'body');
13553        const getDialogFooter = (dialog) => getPart(dialog, detail, 'footer');
13554        const setBusy = (dialog, getBusySpec) => {
13555            Blocking.block(dialog, getBusySpec);
13556        };
13557        const setIdle = (dialog) => {
13558            Blocking.unblock(dialog);
13559        };
13560        const modalEventsId = generate$6('modal-events');
13561        const eventOrder = {
13562            ...detail.eventOrder,
13563            [attachedToDom()]: [modalEventsId].concat(detail.eventOrder['alloy.system.attached'] || [])
13564        };
13565        const browser = detect$1();
13566        return {
13567            uid: detail.uid,
13568            dom: detail.dom,
13569            components,
13570            apis: {
13571                show: showDialog,
13572                hide: hideDialog,
13573                getBody: getDialogBody,
13574                getFooter: getDialogFooter,
13575                setIdle,
13576                setBusy
13577            },
13578            eventOrder,
13579            domModification: {
13580                attributes: {
13581                    'role': detail.role,
13582                    'aria-modal': 'true'
13583                }
13584            },
13585            behaviours: augment(detail.modalBehaviours, [
13586                Replacing.config({}),
13587                Keying.config({
13588                    mode: 'cyclic',
13589                    onEnter: detail.onExecute,
13590                    onEscape: detail.onEscape,
13591                    useTabstopAt: detail.useTabstopAt,
13592                    firstTabstop: detail.firstTabstop
13593                }),
13594                Blocking.config({
13595                    getRoot: dialogComp.get
13596                }),
13597                config(modalEventsId, [
13598                    runOnAttached((c) => {
13599                        // TINY-10808 - Workaround to address the dialog header not being announced on VoiceOver with aria-labelledby, ideally we should use the aria-labelledby
13600                        const titleElm = getPartOrDie(c, detail, 'title').element;
13601                        const title = get$6(titleElm);
13602                        if (isNonNullable(title) && title !== '') {
13603                            if (browser.os.isMacOS()) {
13604                                set$9(c.element, 'aria-label', title);
13605                            }
13606                            else {
13607                                labelledBy(c.element, titleElm);
13608                            }
13609                        }
13610                    })
13611                ])
13612            ])
13613        };
13614    };
13615    const ModalDialog = composite({
13616        name: 'ModalDialog',
13617        configFields: schema$8(),
13618        partFields: parts$6(),
13619        factory: factory$c,
13620        apis: {
13621            show: (apis, dialog) => {
13622                apis.show(dialog);
13623            },
13624            hide: (apis, dialog) => {
13625                apis.hide(dialog);
13626            },
13627            getBody: (apis, dialog) => apis.getBody(dialog),
13628            getFooter: (apis, dialog) => apis.getFooter(dialog),
13629            setBusy: (apis, dialog, getBusySpec) => {
13630                apis.setBusy(dialog, getBusySpec);
13631            },
13632            setIdle: (apis, dialog) => {
13633                apis.setIdle(dialog);
13634            }
13635        }
13636    });
13637
13638    const labelPart = optional({
13639        schema: [required$1('dom')],
13640        name: 'label'
13641    });
13642    const edgePart = (name) => optional({
13643        name: '' + name + '-edge',
13644        overrides: (detail) => {
13645            const action = detail.model.manager.edgeActions[name];
13646            // Not all edges have actions for all sliders.
13647            // A horizontal slider will only have left and right, for instance,
13648            // ignoring top, bottom and diagonal edges as they don't make sense in context of those sliders.
13649            return action.fold(() => ({}), (a) => ({
13650                events: derive$2([
13651                    runActionExtra(touchstart(), (comp, se, d) => a(comp, d), [detail]),
13652                    runActionExtra(mousedown(), (comp, se, d) => a(comp, d), [detail]),
13653                    runActionExtra(mousemove(), (comp, se, det) => {
13654                        if (det.mouseIsDown.get()) {
13655                            a(comp, det);
13656                        }
13657                    }, [detail])
13658                ])
13659            }));
13660        }
13661    });
13662    // When the user touches the top left edge, it should move the thumb
13663    const tlEdgePart = edgePart('top-left');
13664    // When the user touches the top edge, it should move the thumb
13665    const tedgePart = edgePart('top');
13666    // When the user touches the top right edge, it should move the thumb
13667    const trEdgePart = edgePart('top-right');
13668    // When the user touches the right edge, it should move the thumb
13669    const redgePart = edgePart('right');
13670    // When the user touches the bottom right edge, it should move the thumb
13671    const brEdgePart = edgePart('bottom-right');
13672    // When the user touches the bottom edge, it should move the thumb
13673    const bedgePart = edgePart('bottom');
13674    // When the user touches the bottom left edge, it should move the thumb
13675    const blEdgePart = edgePart('bottom-left');
13676    // When the user touches the left edge, it should move the thumb
13677    const ledgePart = edgePart('left');
13678    // The thumb part needs to have position absolute to be positioned correctly
13679    const thumbPart = required({
13680        name: 'thumb',
13681        defaults: constant$1({
13682            dom: {
13683                styles: { position: 'absolute' }
13684            }
13685        }),
13686        overrides: (detail) => {
13687            return {
13688                events: derive$2([
13689                    // If the user touches the thumb itself, pretend they touched the spectrum instead. This
13690                    // allows sliding even when they touchstart the current value
13691                    redirectToPart(touchstart(), detail, 'spectrum'),
13692                    redirectToPart(touchmove(), detail, 'spectrum'),
13693                    redirectToPart(touchend(), detail, 'spectrum'),
13694                    redirectToPart(mousedown(), detail, 'spectrum'),
13695                    redirectToPart(mousemove(), detail, 'spectrum'),
13696                    redirectToPart(mouseup(), detail, 'spectrum')
13697                ])
13698            };
13699        }
13700    });
13701    const isShift = (event) => isShift$1(event.event);
13702    const spectrumPart = required({
13703        schema: [
13704            customField('mouseIsDown', () => Cell(false))
13705        ],
13706        name: 'spectrum',
13707        overrides: (detail) => {
13708            const modelDetail = detail.model;
13709            const model = modelDetail.manager;
13710            const setValueFrom = (component, simulatedEvent) => model.getValueFromEvent(simulatedEvent).map((value) => model.setValueFrom(component, detail, value));
13711            return {
13712                behaviours: derive$1([
13713                    // Move left and right along the spectrum
13714                    Keying.config({
13715                        mode: 'special',
13716                        onLeft: (spectrum, event) => model.onLeft(spectrum, detail, isShift(event)),
13717                        onRight: (spectrum, event) => model.onRight(spectrum, detail, isShift(event)),
13718                        onUp: (spectrum, event) => model.onUp(spectrum, detail, isShift(event)),
13719                        onDown: (spectrum, event) => model.onDown(spectrum, detail, isShift(event))
13720                    }),
13721                    Tabstopping.config({}),
13722                    Focusing.config({})
13723                ]),
13724                events: derive$2([
13725                    run$1(touchstart(), setValueFrom),
13726                    run$1(touchmove(), setValueFrom),
13727                    run$1(mousedown(), setValueFrom),
13728                    run$1(mousemove(), (spectrum, se) => {
13729                        if (detail.mouseIsDown.get()) {
13730                            setValueFrom(spectrum, se);
13731                        }
13732                    })
13733                ])
13734            };
13735        }
13736    });
13737    var SliderParts = [
13738        labelPart,
13739        ledgePart,
13740        redgePart,
13741        tedgePart,
13742        bedgePart,
13743        tlEdgePart,
13744        trEdgePart,
13745        blEdgePart,
13746        brEdgePart,
13747        thumbPart,
13748        spectrumPart
13749    ];
13750
13751    const _sliderChangeEvent = 'slider.change.value';
13752    const sliderChangeEvent = constant$1(_sliderChangeEvent);
13753    const isTouchEvent$2 = (evt) => evt.type.indexOf('touch') !== -1;
13754    const getEventSource = (simulatedEvent) => {
13755        const evt = simulatedEvent.event.raw;
13756        if (isTouchEvent$2(evt)) {
13757            const touchEvent = evt;
13758            return touchEvent.touches !== undefined && touchEvent.touches.length === 1 ?
13759                Optional.some(touchEvent.touches[0]).map((t) => SugarPosition(t.clientX, t.clientY)) : Optional.none();
13760        }
13761        else {
13762            const mouseEvent = evt;
13763            return mouseEvent.clientX !== undefined ? Optional.some(mouseEvent).map((me) => SugarPosition(me.clientX, me.clientY)) : Optional.none();
13764        }
13765    };
13766
13767    const t = 'top', r = 'right', b = 'bottom', l = 'left';
13768    // Values
13769    const minX = (detail) => detail.model.minX;
13770    const minY = (detail) => detail.model.minY;
13771    const min1X = (detail) => detail.model.minX - 1;
13772    const min1Y = (detail) => detail.model.minY - 1;
13773    const maxX = (detail) => detail.model.maxX;
13774    const maxY = (detail) => detail.model.maxY;
13775    const max1X = (detail) => detail.model.maxX + 1;
13776    const max1Y = (detail) => detail.model.maxY + 1;
13777    const range = (detail, max, min) => max(detail) - min(detail);
13778    const xRange = (detail) => range(detail, maxX, minX);
13779    const yRange = (detail) => range(detail, maxY, minY);
13780    const halfX = (detail) => xRange(detail) / 2;
13781    const halfY = (detail) => yRange(detail) / 2;
13782    const step = (detail, useMultiplier) => useMultiplier ? detail.stepSize * detail.speedMultiplier : detail.stepSize;
13783    const snap = (detail) => detail.snapToGrid;
13784    const snapStart = (detail) => detail.snapStart;
13785    const rounded = (detail) => detail.rounded;
13786    // Not great but... /shrug
13787    const hasEdge = (detail, edgeName) => detail[edgeName + '-edge'] !== undefined;
13788    const hasLEdge = (detail) => hasEdge(detail, l);
13789    const hasREdge = (detail) => hasEdge(detail, r);
13790    const hasTEdge = (detail) => hasEdge(detail, t);
13791    const hasBEdge = (detail) => hasEdge(detail, b);
13792    // Ew, any
13793    const currentValue = (detail) => detail.model.value.get();
13794
13795    const xyValue = (x, y) => ({
13796        x,
13797        y
13798    });
13799    const fireSliderChange$3 = (component, value) => {
13800        emitWith(component, sliderChangeEvent(), { value });
13801    };
13802    // North West XY
13803    const setToTLEdgeXY = (edge, detail) => {
13804        fireSliderChange$3(edge, xyValue(min1X(detail), min1Y(detail)));
13805    };
13806    // North
13807    const setToTEdge = (edge, detail) => {
13808        fireSliderChange$3(edge, min1Y(detail));
13809    };
13810    // North XY
13811    const setToTEdgeXY = (edge, detail) => {
13812        fireSliderChange$3(edge, xyValue(halfX(detail), min1Y(detail)));
13813    };
13814    // North East XY
13815    const setToTREdgeXY = (edge, detail) => {
13816        fireSliderChange$3(edge, xyValue(max1X(detail), min1Y(detail)));
13817    };
13818    // East
13819    const setToREdge = (edge, detail) => {
13820        fireSliderChange$3(edge, max1X(detail));
13821    };
13822    // East XY
13823    const setToREdgeXY = (edge, detail) => {
13824        fireSliderChange$3(edge, xyValue(max1X(detail), halfY(detail)));
13825    };
13826    // South East XY
13827    const setToBREdgeXY = (edge, detail) => {
13828        fireSliderChange$3(edge, xyValue(max1X(detail), max1Y(detail)));
13829    };
13830    // South
13831    const setToBEdge = (edge, detail) => {
13832        fireSliderChange$3(edge, max1Y(detail));
13833    };
13834    // South XY
13835    const setToBEdgeXY = (edge, detail) => {
13836        fireSliderChange$3(edge, xyValue(halfX(detail), max1Y(detail)));
13837    };
13838    // South West XY
13839    const setToBLEdgeXY = (edge, detail) => {
13840        fireSliderChange$3(edge, xyValue(min1X(detail), max1Y(detail)));
13841    };
13842    // West
13843    const setToLEdge = (edge, detail) => {
13844        fireSliderChange$3(edge, min1X(detail));
13845    };
13846    // West XY
13847    const setToLEdgeXY = (edge, detail) => {
13848        fireSliderChange$3(edge, xyValue(min1X(detail), halfY(detail)));
13849    };
13850
13851    const reduceBy = (value, min, max, step) => {
13852        if (value < min) {
13853            return value;
13854        }
13855        else if (value > max) {
13856            return max;
13857        }
13858        else if (value === min) {
13859            return min - 1;
13860        }
13861        else {
13862            return Math.max(min, value - step);
13863        }
13864    };
13865    const increaseBy = (value, min, max, step) => {
13866        if (value > max) {
13867            return value;
13868        }
13869        else if (value < min) {
13870            return min;
13871        }
13872        else if (value === max) {
13873            return max + 1;
13874        }
13875        else {
13876            return Math.min(max, value + step);
13877        }
13878    };
13879    const capValue = (value, min, max) => Math.max(min, Math.min(max, value));
13880    const snapValueOf = (value, min, max, step, snapStart) => 
13881    // We are snapping by the step size. Therefore, find the nearest multiple of
13882    // the step
13883    snapStart.fold(() => {
13884        // There is no initial snapping start, so just go from the minimum
13885        const initValue = value - min;
13886        const extraValue = Math.round(initValue / step) * step;
13887        return capValue(min + extraValue, min - 1, max + 1);
13888    }, (start) => {
13889        // There is an initial snapping start, so using that as the starting point,
13890        // calculate the nearest snap position based on the value
13891        const remainder = (value - start) % step;
13892        const adjustment = Math.round(remainder / step);
13893        const rawSteps = Math.floor((value - start) / step);
13894        const maxSteps = Math.floor((max - start) / step);
13895        const numSteps = Math.min(maxSteps, rawSteps + adjustment);
13896        const r = start + (numSteps * step);
13897        return Math.max(start, r);
13898    });
13899    const findOffsetOf = (value, min, max) => Math.min(max, Math.max(value, min)) - min;
13900    const findValueOf = (args) => {
13901        const { min, max, range, value, step, snap, snapStart, rounded, hasMinEdge, hasMaxEdge, minBound, maxBound, screenRange } = args;
13902        const capMin = hasMinEdge ? min - 1 : min;
13903        const capMax = hasMaxEdge ? max + 1 : max;
13904        if (value < minBound) {
13905            return capMin;
13906        }
13907        else if (value > maxBound) {
13908            return capMax;
13909        }
13910        else {
13911            const offset = findOffsetOf(value, minBound, maxBound);
13912            const newValue = capValue(((offset / screenRange) * range) + min, capMin, capMax);
13913            if (snap && newValue >= min && newValue <= max) {
13914                return snapValueOf(newValue, min, max, step, snapStart);
13915            }
13916            else if (rounded) {
13917                return Math.round(newValue);
13918            }
13919            else {
13920                return newValue;
13921            }
13922        }
13923    };
13924    const findOffsetOfValue$2 = (args) => {
13925        const { min, max, range, value, hasMinEdge, hasMaxEdge, maxBound, maxOffset, centerMinEdge, centerMaxEdge } = args;
13926        if (value < min) {
13927            return hasMinEdge ? 0 : centerMinEdge;
13928        }
13929        else if (value > max) {
13930            return hasMaxEdge ? maxBound : centerMaxEdge;
13931        }
13932        else {
13933            // position along the slider
13934            return (value - min) / range * maxOffset;
13935        }
13936    };
13937
13938    const top = 'top', right = 'right', bottom = 'bottom', left = 'left', width = 'width', height = 'height';
13939    // Screen offsets from bounding client rect
13940    const getBounds = (component) => component.element.dom.getBoundingClientRect();
13941    const getBoundsProperty = (bounds, property) => bounds[property];
13942    const getMinXBounds = (component) => {
13943        const bounds = getBounds(component);
13944        return getBoundsProperty(bounds, left);
13945    };
13946    const getMaxXBounds = (component) => {
13947        const bounds = getBounds(component);
13948        return getBoundsProperty(bounds, right);
13949    };
13950    const getMinYBounds = (component) => {
13951        const bounds = getBounds(component);
13952        return getBoundsProperty(bounds, top);
13953    };
13954    const getMaxYBounds = (component) => {
13955        const bounds = getBounds(component);
13956        return getBoundsProperty(bounds, bottom);
13957    };
13958    const getXScreenRange = (component) => {
13959        const bounds = getBounds(component);
13960        return getBoundsProperty(bounds, width);
13961    };
13962    const getYScreenRange = (component) => {
13963        const bounds = getBounds(component);
13964        return getBoundsProperty(bounds, height);
13965    };
13966    const getCenterOffsetOf = (componentMinEdge, componentMaxEdge, spectrumMinEdge) => (componentMinEdge + componentMaxEdge) / 2 - spectrumMinEdge;
13967    const getXCenterOffSetOf = (component, spectrum) => {
13968        const componentBounds = getBounds(component);
13969        const spectrumBounds = getBounds(spectrum);
13970        const componentMinEdge = getBoundsProperty(componentBounds, left);
13971        const componentMaxEdge = getBoundsProperty(componentBounds, right);
13972        const spectrumMinEdge = getBoundsProperty(spectrumBounds, left);
13973        return getCenterOffsetOf(componentMinEdge, componentMaxEdge, spectrumMinEdge);
13974    };
13975    const getYCenterOffSetOf = (component, spectrum) => {
13976        const componentBounds = getBounds(component);
13977        const spectrumBounds = getBounds(spectrum);
13978        const componentMinEdge = getBoundsProperty(componentBounds, top);
13979        const componentMaxEdge = getBoundsProperty(componentBounds, bottom);
13980        const spectrumMinEdge = getBoundsProperty(spectrumBounds, top);
13981        return getCenterOffsetOf(componentMinEdge, componentMaxEdge, spectrumMinEdge);
13982    };
13983
13984    // fire slider change event with x value
13985    const fireSliderChange$2 = (spectrum, value) => {
13986        emitWith(spectrum, sliderChangeEvent(), { value });
13987    };
13988    // find the value of the x offset of where the mouse was clicked from the model.
13989    const findValueOfOffset$1 = (spectrum, detail, left) => {
13990        const args = {
13991            min: minX(detail),
13992            max: maxX(detail),
13993            range: xRange(detail),
13994            value: left,
13995            step: step(detail),
13996            snap: snap(detail),
13997            snapStart: snapStart(detail),
13998            rounded: rounded(detail),
13999            hasMinEdge: hasLEdge(detail),
14000            hasMaxEdge: hasREdge(detail),
14001            minBound: getMinXBounds(spectrum),
14002            maxBound: getMaxXBounds(spectrum),
14003            screenRange: getXScreenRange(spectrum)
14004        };
14005        return findValueOf(args);
14006    };
14007    // find the value and fire a slider change event, returning the value
14008    const setValueFrom$2 = (spectrum, detail, value) => {
14009        const xValue = findValueOfOffset$1(spectrum, detail, value);
14010        const sliderVal = xValue;
14011        fireSliderChange$2(spectrum, sliderVal);
14012        return xValue;
14013    };
14014    // fire a slider change event with the minimum value
14015    const setToMin$2 = (spectrum, detail) => {
14016        const min = minX(detail);
14017        fireSliderChange$2(spectrum, min);
14018    };
14019    // fire a slider change event with the maximum value
14020    const setToMax$2 = (spectrum, detail) => {
14021        const max = maxX(detail);
14022        fireSliderChange$2(spectrum, max);
14023    };
14024    // move in a direction by step size. Fire change at the end
14025    const moveBy$2 = (direction, spectrum, detail, useMultiplier) => {
14026        const f = (direction > 0) ? increaseBy : reduceBy;
14027        const xValue = f(currentValue(detail), minX(detail), maxX(detail), step(detail, useMultiplier));
14028        fireSliderChange$2(spectrum, xValue);
14029        return Optional.some(xValue);
14030    };
14031    const handleMovement$2 = (direction) => (spectrum, detail, useMultiplier) => moveBy$2(direction, spectrum, detail, useMultiplier).map(always);
14032    // get x offset from event
14033    const getValueFromEvent$2 = (simulatedEvent) => {
14034        const pos = getEventSource(simulatedEvent);
14035        return pos.map((p) => p.left);
14036    };
14037    // find the x offset of a given value from the model
14038    const findOffsetOfValue$1 = (spectrum, detail, value, minEdge, maxEdge) => {
14039        const minOffset = 0;
14040        const maxOffset = getXScreenRange(spectrum);
14041        const centerMinEdge = minEdge.bind((edge) => Optional.some(getXCenterOffSetOf(edge, spectrum))).getOr(minOffset);
14042        const centerMaxEdge = maxEdge.bind((edge) => Optional.some(getXCenterOffSetOf(edge, spectrum))).getOr(maxOffset);
14043        const args = {
14044            min: minX(detail),
14045            max: maxX(detail),
14046            range: xRange(detail),
14047            value,
14048            hasMinEdge: hasLEdge(detail),
14049            hasMaxEdge: hasREdge(detail),
14050            minBound: getMinXBounds(spectrum),
14051            minOffset,
14052            maxBound: getMaxXBounds(spectrum),
14053            maxOffset,
14054            centerMinEdge,
14055            centerMaxEdge
14056        };
14057        return findOffsetOfValue$2(args);
14058    };
14059    // find left offset for absolute positioning from a given value
14060    const findPositionOfValue$1 = (slider, spectrum, value, minEdge, maxEdge, detail) => {
14061        const offset = findOffsetOfValue$1(spectrum, detail, value, minEdge, maxEdge);
14062        return (getMinXBounds(spectrum) - getMinXBounds(slider)) + offset;
14063    };
14064    // update the position of the thumb from the slider's current value
14065    const setPositionFromValue$2 = (slider, thumb, detail, edges) => {
14066        const value = currentValue(detail);
14067        const pos = findPositionOfValue$1(slider, edges.getSpectrum(slider), value, edges.getLeftEdge(slider), edges.getRightEdge(slider), detail);
14068        const thumbRadius = get$c(thumb.element) / 2;
14069        set$7(thumb.element, 'left', (pos - thumbRadius) + 'px');
14070    };
14071    // Key Events
14072    const onLeft$2 = handleMovement$2(-1);
14073    const onRight$2 = handleMovement$2(1);
14074    const onUp$2 = Optional.none;
14075    const onDown$2 = Optional.none;
14076    // Edge Click Actions
14077    const edgeActions$2 = {
14078        'top-left': Optional.none(),
14079        'top': Optional.none(),
14080        'top-right': Optional.none(),
14081        'right': Optional.some(setToREdge),
14082        'bottom-right': Optional.none(),
14083        'bottom': Optional.none(),
14084        'bottom-left': Optional.none(),
14085        'left': Optional.some(setToLEdge)
14086    };
14087
14088    var HorizontalModel = /*#__PURE__*/Object.freeze({
14089        __proto__: null,
14090        setValueFrom: setValueFrom$2,
14091        setToMin: setToMin$2,
14092        setToMax: setToMax$2,
14093        findValueOfOffset: findValueOfOffset$1,
14094        getValueFromEvent: getValueFromEvent$2,
14095        findPositionOfValue: findPositionOfValue$1,
14096        setPositionFromValue: setPositionFromValue$2,
14097        onLeft: onLeft$2,
14098        onRight: onRight$2,
14099        onUp: onUp$2,
14100        onDown: onDown$2,
14101        edgeActions: edgeActions$2
14102    });
14103
14104    // fire slider change event with y value
14105    const fireSliderChange$1 = (spectrum, value) => {
14106        emitWith(spectrum, sliderChangeEvent(), { value });
14107    };
14108    // find the value of the y offset of where the mouse was clicked from the model.
14109    const findValueOfOffset = (spectrum, detail, top) => {
14110        const args = {
14111            min: minY(detail),
14112            max: maxY(detail),
14113            range: yRange(detail),
14114            value: top,
14115            step: step(detail),
14116            snap: snap(detail),
14117            snapStart: snapStart(detail),
14118            rounded: rounded(detail),
14119            hasMinEdge: hasTEdge(detail),
14120            hasMaxEdge: hasBEdge(detail),
14121            minBound: getMinYBounds(spectrum),
14122            maxBound: getMaxYBounds(spectrum),
14123            screenRange: getYScreenRange(spectrum)
14124        };
14125        return findValueOf(args);
14126    };
14127    // find the value and fire a slider change event, returning the value
14128    const setValueFrom$1 = (spectrum, detail, value) => {
14129        const yValue = findValueOfOffset(spectrum, detail, value);
14130        const sliderVal = yValue;
14131        fireSliderChange$1(spectrum, sliderVal);
14132        return yValue;
14133    };
14134    // fire a slider change event with the minimum value
14135    const setToMin$1 = (spectrum, detail) => {
14136        const min = minY(detail);
14137        fireSliderChange$1(spectrum, min);
14138    };
14139    // fire a slider change event with the maximum value
14140    const setToMax$1 = (spectrum, detail) => {
14141        const max = maxY(detail);
14142        fireSliderChange$1(spectrum, max);
14143    };
14144    // move in a direction by step size. Fire change at the end
14145    const moveBy$1 = (direction, spectrum, detail, useMultiplier) => {
14146        const f = (direction > 0) ? increaseBy : reduceBy;
14147        const yValue = f(currentValue(detail), minY(detail), maxY(detail), step(detail, useMultiplier));
14148        fireSliderChange$1(spectrum, yValue);
14149        return Optional.some(yValue);
14150    };
14151    const handleMovement$1 = (direction) => (spectrum, detail, useMultiplier) => moveBy$1(direction, spectrum, detail, useMultiplier).map(always);
14152    // get y offset from event
14153    const getValueFromEvent$1 = (simulatedEvent) => {
14154        const pos = getEventSource(simulatedEvent);
14155        return pos.map((p) => {
14156            return p.top;
14157        });
14158    };
14159    // find the y offset of a given value from the model
14160    const findOffsetOfValue = (spectrum, detail, value, minEdge, maxEdge) => {
14161        const minOffset = 0;
14162        const maxOffset = getYScreenRange(spectrum);
14163        const centerMinEdge = minEdge.bind((edge) => Optional.some(getYCenterOffSetOf(edge, spectrum))).getOr(minOffset);
14164        const centerMaxEdge = maxEdge.bind((edge) => Optional.some(getYCenterOffSetOf(edge, spectrum))).getOr(maxOffset);
14165        const args = {
14166            min: minY(detail),
14167            max: maxY(detail),
14168            range: yRange(detail),
14169            value,
14170            hasMinEdge: hasTEdge(detail),
14171            hasMaxEdge: hasBEdge(detail),
14172            minBound: getMinYBounds(spectrum),
14173            minOffset,
14174            maxBound: getMaxYBounds(spectrum),
14175            maxOffset,
14176            centerMinEdge,
14177            centerMaxEdge
14178        };
14179        return findOffsetOfValue$2(args);
14180    };
14181    // find left offset for absolute positioning from a given value
14182    const findPositionOfValue = (slider, spectrum, value, minEdge, maxEdge, detail) => {
14183        const offset = findOffsetOfValue(spectrum, detail, value, minEdge, maxEdge);
14184        return (getMinYBounds(spectrum) - getMinYBounds(slider)) + offset;
14185    };
14186    // update the position of the thumb from the slider's current value
14187    const setPositionFromValue$1 = (slider, thumb, detail, edges) => {
14188        const value = currentValue(detail);
14189        const pos = findPositionOfValue(slider, edges.getSpectrum(slider), value, edges.getTopEdge(slider), edges.getBottomEdge(slider), detail);
14190        const thumbRadius = get$d(thumb.element) / 2;
14191        set$7(thumb.element, 'top', (pos - thumbRadius) + 'px');
14192    };
14193    // Key Events
14194    const onLeft$1 = Optional.none;
14195    const onRight$1 = Optional.none;
14196    const onUp$1 = handleMovement$1(-1);
14197    const onDown$1 = handleMovement$1(1);
14198    // Edge Click Actions
14199    const edgeActions$1 = {
14200        'top-left': Optional.none(),
14201        'top': Optional.some(setToTEdge),
14202        'top-right': Optional.none(),
14203        'right': Optional.none(),
14204        'bottom-right': Optional.none(),
14205        'bottom': Optional.some(setToBEdge),
vendor: 4,234 bytes, lines 14206-14297
14206        'bottom-left': Optional.none(),
14207        'left': Optional.none()
14208    };
14209
14210    var VerticalModel = /*#__PURE__*/Object.freeze({
14211        __proto__: null,
14212        setValueFrom: setValueFrom$1,
14213        setToMin: setToMin$1,
14214        setToMax: setToMax$1,
14215        findValueOfOffset: findValueOfOffset,
14216        getValueFromEvent: getValueFromEvent$1,
14217        findPositionOfValue: findPositionOfValue,
14218        setPositionFromValue: setPositionFromValue$1,
14219        onLeft: onLeft$1,
14220        onRight: onRight$1,
14221        onUp: onUp$1,
14222        onDown: onDown$1,
14223        edgeActions: edgeActions$1
14224    });
14225
14226    // fire slider change event with xy value
14227    const fireSliderChange = (spectrum, value) => {
14228        emitWith(spectrum, sliderChangeEvent(), { value });
14229    };
14230    const sliderValue = (x, y) => ({
14231        x,
14232        y
14233    });
14234    // find both values of x and y offsets of where the mouse was clicked from the model.
14235    // then fire a slider change event with those values, returning the values
14236    const setValueFrom = (spectrum, detail, value) => {
14237        const xValue = findValueOfOffset$1(spectrum, detail, value.left);
14238        const yValue = findValueOfOffset(spectrum, detail, value.top);
14239        const val = sliderValue(xValue, yValue);
14240        fireSliderChange(spectrum, val);
14241        return val;
14242    };
14243    // move in a direction by step size. Fire change at the end
14244    const moveBy = (direction, isVerticalMovement, spectrum, detail, useMultiplier) => {
14245        const f = (direction > 0) ? increaseBy : reduceBy;
14246        const xValue = isVerticalMovement ? currentValue(detail).x :
14247            f(currentValue(detail).x, minX(detail), maxX(detail), step(detail, useMultiplier));
14248        const yValue = !isVerticalMovement ? currentValue(detail).y :
14249            f(currentValue(detail).y, minY(detail), maxY(detail), step(detail, useMultiplier));
14250        fireSliderChange(spectrum, sliderValue(xValue, yValue));
14251        return Optional.some(xValue);
14252    };
14253    const handleMovement = (direction, isVerticalMovement) => (spectrum, detail, useMultiplier) => moveBy(direction, isVerticalMovement, spectrum, detail, useMultiplier).map(always);
14254    // fire a slider change event with the minimum value
14255    const setToMin = (spectrum, detail) => {
14256        const mX = minX(detail);
14257        const mY = minY(detail);
14258        fireSliderChange(spectrum, sliderValue(mX, mY));
14259    };
14260    // fire a slider change event with the maximum value
14261    const setToMax = (spectrum, detail) => {
14262        const mX = maxX(detail);
14263        const mY = maxY(detail);
14264        fireSliderChange(spectrum, sliderValue(mX, mY));
14265    };
14266    // get event data as a SugarPosition
14267    const getValueFromEvent = (simulatedEvent) => getEventSource(simulatedEvent);
14268    // update the position of the thumb from the slider's current value
14269    const setPositionFromValue = (slider, thumb, detail, edges) => {
14270        const value = currentValue(detail);
14271        const xPos = findPositionOfValue$1(slider, edges.getSpectrum(slider), value.x, edges.getLeftEdge(slider), edges.getRightEdge(slider), detail);
14272        const yPos = findPositionOfValue(slider, edges.getSpectrum(slider), value.y, edges.getTopEdge(slider), edges.getBottomEdge(slider), detail);
14273        const thumbXRadius = get$c(thumb.element) / 2;
14274        const thumbYRadius = get$d(thumb.element) / 2;
14275        set$7(thumb.element, 'left', (xPos - thumbXRadius) + 'px');
14276        set$7(thumb.element, 'top', (yPos - thumbYRadius) + 'px');
14277    };
14278    // Key Events
14279    const onLeft = handleMovement(-1, false);
14280    const onRight = handleMovement(1, false);
14281    const onUp = handleMovement(-1, true);
14282    const onDown = handleMovement(1, true);
14283    // Edge Click Actions
14284    const edgeActions = {
14285        'top-left': Optional.some(setToTLEdgeXY),
14286        'top': Optional.some(setToTEdgeXY),
14287        'top-right': Optional.some(setToTREdgeXY),
14288        'right': Optional.some(setToREdgeXY),
14289        'bottom-right': Optional.some(setToBREdgeXY),
14290        'bottom': Optional.some(setToBEdgeXY),
14291        'bottom-left': Optional.some(setToBLEdgeXY),
14292        'left': Optional.some(setToLEdgeXY)
14293    };
14294
14295    var TwoDModel = /*#__PURE__*/Object.freeze({
14296        __proto__: null,
14297        setValueFrom: setValueFrom,
14298        setToMin: setToMin,
14299        setToMax: setToMax,
14300        getValueFromEvent: getValueFromEvent,
14301        setPositionFromValue: setPositionFromValue,
14302        onLeft: onLeft,
14303        onRight: onRight,
14304        onUp: onUp,
14305        onDown: onDown,
14306        edgeActions: edgeActions
14307    });
14308
14309    const SliderSchema = [
14310        defaulted('stepSize', 1),
14311        defaulted('speedMultiplier', 10),
14312        defaulted('onChange', noop),
14313        defaulted('onChoose', noop),
14314        defaulted('onInit', noop),
14315        defaulted('onDragStart', noop),
14316        defaulted('onDragEnd', noop),
14317        defaulted('snapToGrid', false),
14318        defaulted('rounded', true),
14319        option$3('snapStart'),
14320        requiredOf('model', choose$1('mode', {
14321            x: [
14322                defaulted('minX', 0),
14323                defaulted('maxX', 100),
14324                customField('value', (spec) => Cell(spec.mode.minX)),
14325                required$1('getInitialValue'),
14326                output$1('manager', HorizontalModel)
14327            ],
14328            y: [
14329                defaulted('minY', 0),
14330                defaulted('maxY', 100),
14331                customField('value', (spec) => Cell(spec.mode.minY)),
14332                required$1('getInitialValue'),
14333                output$1('manager', VerticalModel)
14334            ],
14335            xy: [
14336                defaulted('minX', 0),
14337                defaulted('maxX', 100),
14338                defaulted('minY', 0),
14339                defaulted('maxY', 100),
14340                customField('value', (spec) => Cell({
14341                    x: spec.mode.minX,
14342                    y: spec.mode.minY
14343                })),
14344                required$1('getInitialValue'),
14345                output$1('manager', TwoDModel)
14346            ]
14347        })),
14348        field('sliderBehaviours', [Keying, Representing]),
14349        customField('mouseIsDown', () => Cell(false))
14350    ];
14351
14352    const sketch$1 = (detail, components, _spec, _externals) => {
14353        const getThumb = (component) => getPartOrDie(component, detail, 'thumb');
14354        const getSpectrum = (component) => getPartOrDie(component, detail, 'spectrum');
14355        const getLeftEdge = (component) => getPart(component, detail, 'left-edge');
14356        const getRightEdge = (component) => getPart(component, detail, 'right-edge');
14357        const getTopEdge = (component) => getPart(component, detail, 'top-edge');
14358        const getBottomEdge = (component) => getPart(component, detail, 'bottom-edge');
14359        const modelDetail = detail.model;
14360        const model = modelDetail.manager;
14361        const refresh = (slider, thumb) => {
14362            model.setPositionFromValue(slider, thumb, detail, {
14363                getLeftEdge,
14364                getRightEdge,
14365                getTopEdge,
14366                getBottomEdge,
14367                getSpectrum
14368            });
14369        };
14370        const setValue = (slider, newValue) => {
14371            modelDetail.value.set(newValue);
14372            const thumb = getThumb(slider);
14373            refresh(slider, thumb);
14374        };
14375        const changeValue = (slider, newValue) => {
14376            setValue(slider, newValue);
14377            const thumb = getThumb(slider);
14378            detail.onChange(slider, thumb, newValue);
14379            return Optional.some(true);
14380        };
14381        const resetToMin = (slider) => {
14382            model.setToMin(slider, detail);
14383        };
14384        const resetToMax = (slider) => {
14385            model.setToMax(slider, detail);
14386        };
14387        const choose = (slider) => {
14388            const fireOnChoose = () => {
14389                getPart(slider, detail, 'thumb').each((thumb) => {
14390                    const value = modelDetail.value.get();
14391                    detail.onChoose(slider, thumb, value);
14392                });
14393            };
14394            const wasDown = detail.mouseIsDown.get();
14395            detail.mouseIsDown.set(false);
14396            // We don't want this to fire if the mouse wasn't pressed down over anything other than the slider.
14397            if (wasDown) {
14398                fireOnChoose();
14399            }
14400        };
14401        const onDragStart = (slider, simulatedEvent) => {
14402            simulatedEvent.stop();
14403            detail.mouseIsDown.set(true);
vendor: 17,979 bytes, lines 14404-14881
14404            detail.onDragStart(slider, getThumb(slider));
14405        };
14406        const onDragEnd = (slider, simulatedEvent) => {
14407            simulatedEvent.stop();
14408            detail.onDragEnd(slider, getThumb(slider));
14409            choose(slider);
14410        };
14411        const focusWidget = (component) => {
14412            getPart(component, detail, 'spectrum').map(Keying.focusIn);
14413        };
14414        return {
14415            uid: detail.uid,
14416            dom: detail.dom,
14417            components,
14418            behaviours: augment(detail.sliderBehaviours, [
14419                Keying.config({
14420                    mode: 'special',
14421                    focusIn: focusWidget
14422                }),
14423                Representing.config({
14424                    store: {
14425                        mode: 'manual',
14426                        getValue: (_) => {
14427                            return modelDetail.value.get();
14428                        },
14429                        setValue
14430                    }
14431                }),
14432                Receiving.config({
14433                    channels: {
14434                        [mouseReleased()]: {
14435                            onReceive: choose
14436                        }
14437                    }
14438                })
14439            ]),
14440            events: derive$2([
14441                run$1(sliderChangeEvent(), (slider, simulatedEvent) => {
14442                    changeValue(slider, simulatedEvent.event.value);
14443                }),
14444                runOnAttached((slider, _simulatedEvent) => {
14445                    // Set the initial value
14446                    const getInitial = modelDetail.getInitialValue();
14447                    modelDetail.value.set(getInitial);
14448                    const thumb = getThumb(slider);
14449                    refresh(slider, thumb);
14450                    const spectrum = getSpectrum(slider);
14451                    // Call onInit instead of onChange for the first value.
14452                    detail.onInit(slider, thumb, spectrum, modelDetail.value.get());
14453                }),
14454                run$1(touchstart(), onDragStart),
14455                run$1(touchend(), onDragEnd),
14456                run$1(mousedown(), (component, event) => {
14457                    focusWidget(component);
14458                    onDragStart(component, event);
14459                }),
14460                run$1(mouseup(), onDragEnd),
14461            ]),
14462            apis: {
14463                resetToMin,
14464                resetToMax,
14465                setValue,
14466                refresh
14467            },
14468            domModification: {
14469                styles: {
14470                    position: 'relative'
14471                }
14472            }
14473        };
14474    };
14475
14476    const Slider = composite({
14477        name: 'Slider',
14478        configFields: SliderSchema,
14479        partFields: SliderParts,
14480        factory: sketch$1,
14481        apis: {
14482            setValue: (apis, slider, value) => {
14483                apis.setValue(slider, value);
14484            },
14485            resetToMin: (apis, slider) => {
14486                apis.resetToMin(slider);
14487            },
14488            resetToMax: (apis, slider) => {
14489                apis.resetToMax(slider);
14490            },
14491            refresh: (apis, slider) => {
14492                apis.refresh(slider);
14493            }
14494        }
14495    });
14496
14497    const owner = 'container';
14498    const schema$7 = [
14499        field('slotBehaviours', [])
14500    ];
14501    const getPartName = (name) => '<alloy.field.' + name + '>';
14502    const sketch = (sSpec) => {
14503        // As parts.slot is called, record all of the parts that are registered
14504        // as part of this SlotContainer.
14505        const parts = (() => {
14506            const record = [];
14507            const slot = (name, config) => {
14508                record.push(name);
14509                return generateOne$1(owner, getPartName(name), config);
14510            };
14511            return {
14512                slot,
14513                record: constant$1(record)
14514            };
14515        })();
14516        const spec = sSpec(parts);
14517        const partNames = parts.record();
14518        // Like a Form, a SlotContainer does not know its parts in advance. So the
14519        // record lists the names of the parts to put in the schema.
14520        // TODO: Find a nice way to remove dupe with Form
14521        const fieldParts = map$2(partNames, (n) => required({ name: n, pname: getPartName(n) }));
14522        return composite$1(owner, schema$7, fieldParts, make$3, spec);
14523    };
14524    const make$3 = (detail, components) => {
14525        const getSlotNames = (_) => getAllPartNames(detail);
14526        const getSlot = (container, key) => getPart(container, detail, key);
14527        const onSlot = (f, def) => (container, key) => getPart(container, detail, key).map((slot) => f(slot, key)).getOr(def);
14528        const onSlots = (f) => (container, keys) => {
14529            each$1(keys, (key) => f(container, key));
14530        };
14531        const doShowing = (comp, _key) => get$g(comp.element, 'aria-hidden') !== 'true';
14532        const doShow = (comp, key) => {
14533            // NOTE: May need to restore old values.
14534            if (!doShowing(comp)) {
14535                const element = comp.element;
14536                remove$6(element, 'display');
14537                remove$8(element, 'aria-hidden');
14538                emitWith(comp, slotVisibility(), { name: key, visible: true });
14539            }
14540        };
14541        const doHide = (comp, key) => {
14542            // NOTE: May need to save old values.
14543            if (doShowing(comp)) {
14544                const element = comp.element;
14545                set$7(element, 'display', 'none');
14546                set$9(element, 'aria-hidden', 'true');
14547                emitWith(comp, slotVisibility(), { name: key, visible: false });
14548            }
14549        };
14550        const isShowing = onSlot(doShowing, false);
14551        const hideSlot = onSlot(doHide);
14552        const hideSlots = onSlots(hideSlot);
14553        const hideAllSlots = (container) => hideSlots(container, getSlotNames());
14554        const showSlot = onSlot(doShow);
14555        const apis = {
14556            getSlotNames,
14557            getSlot,
14558            isShowing,
14559            hideSlot,
14560            hideAllSlots,
14561            showSlot
14562        };
14563        return {
14564            uid: detail.uid,
14565            dom: detail.dom,
14566            components,
14567            behaviours: get$2(detail.slotBehaviours),
14568            apis
14569        };
14570    };
14571    // No type safety doing it this way. But removes dupe.
14572    // We could probably use spread operator to help here.
14573    const slotApis = map$1({
14574        getSlotNames: (apis, c) => apis.getSlotNames(c),
14575        getSlot: (apis, c, key) => apis.getSlot(c, key),
14576        isShowing: (apis, c, key) => apis.isShowing(c, key),
14577        hideSlot: (apis, c, key) => apis.hideSlot(c, key),
14578        hideAllSlots: (apis, c) => apis.hideAllSlots(c),
14579        showSlot: (apis, c, key) => apis.showSlot(c, key)
14580    }, (value) => makeApi(value));
14581    const SlotContainer = {
14582        ...slotApis,
14583        ...{ sketch }
14584    };
14585
14586    const generate$1 = (xs, f) => {
14587        const init = {
14588            len: 0,
14589            list: []
14590        };
14591        const r = foldl(xs, (b, a) => {
14592            const value = f(a, b.len);
14593            return value.fold(constant$1(b), (v) => ({
14594                len: v.finish,
14595                list: b.list.concat([v])
14596            }));
14597        }, init);
14598        return r.list;
14599    };
14600
14601    const output = (within, extra, withinWidth) => ({
14602        within,
14603        extra,
14604        withinWidth
14605    });
14606    const apportion = (units, total, len) => {
14607        const parray = generate$1(units, (unit, current) => {
14608            const width = len(unit);
14609            return Optional.some({
14610                element: unit,
14611                start: current,
14612                finish: current + width,
14613                width
14614            });
14615        });
14616        const within = filter$2(parray, (unit) => unit.finish <= total);
14617        const withinWidth = foldr(within, (acc, el) => acc + el.width, 0);
14618        const extra = parray.slice(within.length);
14619        return {
14620            within,
14621            extra,
14622            withinWidth
14623        };
14624    };
14625    const toUnit = (parray) => map$2(parray, (unit) => unit.element);
14626    const fitLast = (within, extra, withinWidth) => {
14627        const fits = toUnit(within.concat(extra));
14628        return output(fits, [], withinWidth);
14629    };
14630    const overflow = (within, extra, overflower, withinWidth) => {
14631        const fits = toUnit(within).concat([overflower]);
14632        return output(fits, toUnit(extra), withinWidth);
14633    };
14634    const fitAll = (within, extra, withinWidth) => output(toUnit(within), [], withinWidth);
14635    const tryFit = (total, units, len) => {
14636        const divide = apportion(units, total, len);
14637        return divide.extra.length === 0 ? Optional.some(divide) : Optional.none();
14638    };
14639    const partition = (total, units, len, overflower) => {
14640        // Firstly, we try without the overflower.
14641        const divide = tryFit(total, units, len).getOrThunk(() => 
14642        // If that doesn't work, overflow
14643        apportion(units, total - len(overflower), len));
14644        const within = divide.within;
14645        const extra = divide.extra;
14646        const withinWidth = divide.withinWidth;
14647        if (extra.length === 1 && extra[0].width <= len(overflower)) {
14648            return fitLast(within, extra, withinWidth);
14649        }
14650        else if (extra.length >= 1) {
14651            return overflow(within, extra, overflower, withinWidth);
14652        }
14653        else {
14654            return fitAll(within, extra, withinWidth);
14655        }
14656    };
14657
14658    const setGroups = (toolbar, storedGroups) => {
14659        const bGroups = map$2(storedGroups, (g) => premade(g));
14660        Toolbar.setGroups(toolbar, bGroups);
14661    };
14662    const findFocusedComp = (comps) => findMap(comps, (comp) => search(comp.element).bind((focusedElm) => comp.getSystem().getByDom(focusedElm).toOptional()));
14663    const refresh$2 = (toolbar, detail, setOverflow) => {
14664        // Ensure we have toolbar groups to render
14665        const builtGroups = detail.builtGroups.get();
14666        if (builtGroups.length === 0) {
14667            return;
14668        }
14669        const primary = getPartOrDie(toolbar, detail, 'primary');
14670        const overflowGroup = Coupling.getCoupled(toolbar, 'overflowGroup');
14671        // Set the primary toolbar to have visibility hidden;
14672        set$7(primary.element, 'visibility', 'hidden');
14673        const groups = builtGroups.concat([overflowGroup]);
14674        // Store the current focus state
14675        const focusedComp = findFocusedComp(groups);
14676        // Clear the overflow toolbar
14677        setOverflow([]);
14678        // Put all the groups inside the primary toolbar
14679        setGroups(primary, groups);
14680        const availableWidth = get$c(primary.element);
14681        const overflows = partition(availableWidth, detail.builtGroups.get(), (comp) => Math.ceil(comp.element.dom.getBoundingClientRect().width), overflowGroup);
14682        if (overflows.extra.length === 0) {
14683            // Not ideal. Breaking abstraction somewhat, though remove is better than insert
14684            // Can just reset the toolbar groups also ... but may be a bit slower.
14685            Replacing.remove(primary, overflowGroup);
14686            setOverflow([]);
14687        }
14688        else {
14689            setGroups(primary, overflows.within);
14690            setOverflow(overflows.extra);
14691        }
14692        remove$6(primary.element, 'visibility');
14693        reflow(primary.element);
14694        // Restore the focus
14695        focusedComp.each(Focusing.focus);
14696    };
14697
14698    const schema$6 = constant$1([
14699        field('splitToolbarBehaviours', [Coupling]),
14700        customField('builtGroups', () => Cell([]))
14701    ]);
14702
14703    const schema$5 = constant$1([
14704        markers$1(['overflowToggledClass']),
14705        optionFunction('getOverflowBounds'),
14706        required$1('lazySink'),
14707        customField('overflowGroups', () => Cell([])),
14708        onHandler('onOpened'),
14709        onHandler('onClosed')
14710    ].concat(schema$6()));
14711    const parts$5 = constant$1([
14712        required({
14713            factory: Toolbar,
14714            schema: schema$d(),
14715            name: 'primary'
14716        }),
14717        external$1({
14718            schema: schema$d(),
14719            name: 'overflow'
14720        }),
14721        external$1({
14722            name: 'overflow-button'
14723        }),
14724        external$1({
14725            name: 'overflow-group'
14726        })
14727    ]);
14728
14729    const schema$4 = constant$1([
14730        required$1('items'),
14731        markers$1(['itemSelector']),
14732        field('tgroupBehaviours', [Keying])
14733    ]);
14734    const parts$4 = constant$1([
14735        group({
14736            name: 'items',
14737            unit: 'item'
14738        })
14739    ]);
14740
14741    const factory$b = (detail, components, _spec, _externals) => ({
14742        uid: detail.uid,
14743        dom: detail.dom,
14744        components,
14745        behaviours: augment(detail.tgroupBehaviours, [
14746            Keying.config({
14747                mode: 'flow',
14748                selector: detail.markers.itemSelector
14749            })
14750        ]),
14751        domModification: {
14752            attributes: {
14753                role: 'group'
14754            }
14755        }
14756    });
14757    const ToolbarGroup = composite({
14758        name: 'ToolbarGroup',
14759        configFields: schema$4(),
14760        partFields: parts$4(),
14761        factory: factory$b
14762    });
14763
14764    const buildGroups = (comps) => map$2(comps, (g) => premade(g));
14765    const refresh$1 = (toolbar, memFloatingToolbarButton, detail) => {
14766        refresh$2(toolbar, detail, (overflowGroups) => {
14767            detail.overflowGroups.set(overflowGroups);
14768            memFloatingToolbarButton.getOpt(toolbar).each((floatingToolbarButton) => {
14769                FloatingToolbarButton.setGroups(floatingToolbarButton, buildGroups(overflowGroups));
14770            });
14771        });
14772    };
14773    const factory$a = (detail, components, spec, externals) => {
14774        const memFloatingToolbarButton = record(FloatingToolbarButton.sketch({
14775            fetch: () => Future.nu((resolve) => {
14776                resolve(buildGroups(detail.overflowGroups.get()));
14777            }),
14778            layouts: {
14779                onLtr: () => [southwest$2, southeast$2],
14780                onRtl: () => [southeast$2, southwest$2],
14781                onBottomLtr: () => [northwest$2, northeast$2],
14782                onBottomRtl: () => [northeast$2, northwest$2]
14783            },
14784            getBounds: spec.getOverflowBounds,
14785            lazySink: detail.lazySink,
14786            fireDismissalEventInstead: {},
14787            markers: {
14788                toggledClass: detail.markers.overflowToggledClass
14789            },
14790            parts: {
14791                button: externals['overflow-button'](),
14792                toolbar: externals.overflow()
14793            },
14794            onToggled: (comp, state) => detail[state ? 'onOpened' : 'onClosed'](comp)
14795        }));
14796        return {
14797            uid: detail.uid,
14798            dom: detail.dom,
14799            components,
14800            behaviours: augment(detail.splitToolbarBehaviours, [
14801                Coupling.config({
14802                    others: {
14803                        overflowGroup: () => {
14804                            return ToolbarGroup.sketch({
14805                                ...externals['overflow-group'](),
14806                                items: [
14807                                    memFloatingToolbarButton.asSpec()
14808                                ]
14809                            });
14810                        }
14811                    }
14812                })
14813            ]),
14814            apis: {
14815                setGroups: (toolbar, groups) => {
14816                    detail.builtGroups.set(map$2(groups, toolbar.getSystem().build));
14817                    refresh$1(toolbar, memFloatingToolbarButton, detail);
14818                },
14819                refresh: (toolbar) => refresh$1(toolbar, memFloatingToolbarButton, detail),
14820                toggle: (toolbar) => {
14821                    memFloatingToolbarButton.getOpt(toolbar).each((floatingToolbarButton) => {
14822                        FloatingToolbarButton.toggle(floatingToolbarButton);
14823                    });
14824                },
14825                toggleWithoutFocusing: (toolbar) => {
14826                    memFloatingToolbarButton.getOpt(toolbar).each(FloatingToolbarButton.toggleWithoutFocusing);
14827                },
14828                isOpen: (toolbar) => memFloatingToolbarButton.getOpt(toolbar).map(FloatingToolbarButton.isOpen).getOr(false),
14829                reposition: (toolbar) => {
14830                    memFloatingToolbarButton.getOpt(toolbar).each((floatingToolbarButton) => {
14831                        FloatingToolbarButton.reposition(floatingToolbarButton);
14832                    });
14833                },
14834                getOverflow: (toolbar) => memFloatingToolbarButton.getOpt(toolbar).bind(FloatingToolbarButton.getToolbar)
14835            },
14836            domModification: {
14837                attributes: { role: 'group' }
14838            }
14839        };
14840    };
14841    const SplitFloatingToolbar = composite({
14842        name: 'SplitFloatingToolbar',
14843        configFields: schema$5(),
14844        partFields: parts$5(),
14845        factory: factory$a,
14846        apis: {
14847            setGroups: (apis, toolbar, groups) => {
14848                apis.setGroups(toolbar, groups);
14849            },
14850            refresh: (apis, toolbar) => {
14851                apis.refresh(toolbar);
14852            },
14853            reposition: (apis, toolbar) => {
14854                apis.reposition(toolbar);
14855            },
14856            toggle: (apis, toolbar) => {
14857                apis.toggle(toolbar);
14858            },
14859            toggleWithoutFocusing: (apis, toolbar) => {
14860                apis.toggle(toolbar);
14861            },
14862            isOpen: (apis, toolbar) => apis.isOpen(toolbar),
14863            getOverflow: (apis, toolbar) => apis.getOverflow(toolbar)
14864        }
14865    });
14866
14867    const schema$3 = constant$1([
14868        markers$1(['closedClass', 'openClass', 'shrinkingClass', 'growingClass', 'overflowToggledClass']),
14869        onHandler('onOpened'),
14870        onHandler('onClosed')
14871    ].concat(schema$6()));
14872    const parts$3 = constant$1([
14873        required({
14874            factory: Toolbar,
14875            schema: schema$d(),
14876            name: 'primary'
14877        }),
14878        required({
14879            factory: Toolbar,
14880            schema: schema$d(),
14881            name: 'overflow',
14882            overrides: (detail) => {
14883                return {
14884                    toolbarBehaviours: derive$1([
14885                        Sliding.config({
14886                            dimension: {
14887                                property: 'height'
14888                            },
14889                            closedClass: detail.markers.closedClass,
14890                            openClass: detail.markers.openClass,
14891                            shrinkingClass: detail.markers.shrinkingClass,
14892                            growingClass: detail.markers.growingClass,
14893                            onShrunk: (comp) => {
14894                                getPart(comp, detail, 'overflow-button').each((button) => {
14895                                    Toggling.off(button);
14896                                });
14897                                detail.onClosed(comp);
14898                            },
14899                            onGrown: (comp) => {
14900                                detail.onOpened(comp);
14901                            },
14902                            onStartGrow: (comp) => {
14903                                getPart(comp, detail, 'overflow-button').each(Toggling.on);
14904                            }
14905                        }),
14906                        Keying.config({
14907                            mode: 'acyclic',
14908                            onEscape: (comp) => {
14909                                getPart(comp, detail, 'overflow-button').each(Focusing.focus);
14910                                return Optional.some(true);
14911                            }
14912                        })
14913                    ])
14914                };
14915            }
14916        }),
14917        external$1({
14918            name: 'overflow-button',
14919            overrides: (detail) => ({
14920                buttonBehaviours: derive$1([
14921                    Toggling.config({
14922                        toggleClass: detail.markers.overflowToggledClass,
14923                        aria: {
14924                            mode: 'expanded'
14925                        },
14926                        toggleOnExecute: false
14927                    })
14928                ])
14929            })
14930        }),
14931        external$1({
14932            name: 'overflow-group'
14933        })
14934    ]);
14935
14936    const isOpen = (toolbar, detail) => getPart(toolbar, detail, 'overflow').map(Sliding.hasGrown).getOr(false);
14937    const toggleToolbar = (toolbar, detail, skipFocus) => {
14938        // Make sure that the toolbar needs to toggled by checking for overflow button presence
14939        getPart(toolbar, detail, 'overflow-button')
14940            .each((oveflowButton) => {
14941            getPart(toolbar, detail, 'overflow').each((overf) => {
14942                refresh(toolbar, detail);
14943                if (Sliding.hasShrunk(overf)) {
14944                    const fn = detail.onOpened;
14945                    detail.onOpened = (comp) => {
14946                        if (!skipFocus) {
14947                            Keying.focusIn(overf);
14948                        }
14949                        fn(comp);
14950                        detail.onOpened = fn;
14951                    };
14952                }
14953                else {
14954                    const fn = detail.onClosed;
14955                    detail.onClosed = (comp) => {
14956                        if (!skipFocus) {
14957                            Focusing.focus(oveflowButton);
14958                        }
14959                        fn(comp);
14960                        detail.onClosed = fn;
14961                    };
14962                }
14963                Sliding.toggleGrow(overf);
14964            });
14965        });
14966    };
14967    const refresh = (toolbar, detail) => {
14968        getPart(toolbar, detail, 'overflow').each((overflow) => {
14969            refresh$2(toolbar, detail, (groups) => {
14970                const builtGroups = map$2(groups, (g) => premade(g));
14971                Toolbar.setGroups(overflow, builtGroups);
14972            });
14973            getPart(toolbar, detail, 'overflow-button').each((button) => {
14974                if (Sliding.hasGrown(overflow)) {
14975                    Toggling.on(button);
14976                }
14977            });
14978            Sliding.refresh(overflow);
14979        });
14980    };
14981    const factory$9 = (detail, components, spec, externals) => {
14982        const toolbarToggleEvent = 'alloy.toolbar.toggle';
14983        const doSetGroups = (toolbar, groups) => {
14984            const built = map$2(groups, toolbar.getSystem().build);
14985            detail.builtGroups.set(built);
14986        };
14987        return {
14988            uid: detail.uid,
14989            dom: detail.dom,
14990            components,
14991            behaviours: augment(detail.splitToolbarBehaviours, [
14992                Coupling.config({
14993                    others: {
14994                        overflowGroup: (toolbar) => {
14995                            return ToolbarGroup.sketch({
14996                                ...externals['overflow-group'](),
14997                                items: [
14998                                    Button.sketch({
14999                                        ...externals['overflow-button'](),
15000                                        action: (_button) => {
15001                                            emit(toolbar, toolbarToggleEvent);
15002                                        }
15003                                    })
15004                                ]
15005                            });
15006                        }
15007                    }
15008                }),
15009                config('toolbar-toggle-events', [
15010                    run$1(toolbarToggleEvent, (toolbar) => {
15011                        toggleToolbar(toolbar, detail, false);
15012                    })
15013                ])
15014            ]),
15015            apis: {
15016                setGroups: (toolbar, groups) => {
15017                    doSetGroups(toolbar, groups);
15018                    refresh(toolbar, detail);
15019                },
15020                refresh: (toolbar) => refresh(toolbar, detail),
15021                toggle: (toolbar) => {
15022                    toggleToolbar(toolbar, detail, false);
15023                },
15024                toggleWithoutFocusing: (toolbar) => {
15025                    toggleToolbar(toolbar, detail, true);
15026                },
15027                isOpen: (toolbar) => isOpen(toolbar, detail)
15028            },
15029            domModification: {
15030                attributes: { role: 'group' }
15031            }
15032        };
15033    };
15034    const SplitSlidingToolbar = composite({
15035        name: 'SplitSlidingToolbar',
15036        configFields: schema$3(),
15037        partFields: parts$3(),
15038        factory: factory$9,
15039        apis: {
15040            setGroups: (apis, toolbar, groups) => {
15041                apis.setGroups(toolbar, groups);
15042            },
15043            refresh: (apis, toolbar) => {
15044                apis.refresh(toolbar);
15045            },
15046            toggle: (apis, toolbar) => {
15047                apis.toggle(toolbar);
15048            },
15049            isOpen: (apis, toolbar) => apis.isOpen(toolbar)
15050        }
15051    });
15052
15053    const factory$8 = (detail, _spec) => ({
15054        uid: detail.uid,
15055        dom: detail.dom,
15056        components: detail.components,
15057        events: events(detail.action),
15058        behaviours: augment(detail.tabButtonBehaviours, [
15059            Focusing.config({}),
15060            Keying.config({
15061                mode: 'execution',
vendor: 12,279 bytes, lines 15062-15389
15062                useSpace: true,
15063                useEnter: true
15064            }),
15065            Representing.config({
15066                store: {
15067                    mode: 'memory',
15068                    initialValue: detail.value
15069                }
15070            })
15071        ]),
15072        domModification: detail.domModification
15073    });
15074    const TabButton = single({
15075        name: 'TabButton',
15076        configFields: [
15077            defaulted('uid', undefined),
15078            required$1('value'),
15079            field$1('dom', 'dom', mergeWithThunk(() => ({
15080                attributes: {
15081                    'role': 'tab',
15082                    // NOTE: This is used in TabSection to connect "labelledby"
15083                    'id': generate$6('aria'),
15084                    'aria-selected': 'false'
15085                }
15086            })), anyValue()),
15087            option$3('action'),
15088            defaulted('domModification', {}),
15089            field('tabButtonBehaviours', [Focusing, Keying, Representing]),
15090            required$1('view')
15091        ],
15092        factory: factory$8
15093    });
15094
15095    const schema$2 = constant$1([
15096        required$1('tabs'),
15097        required$1('dom'),
15098        defaulted('clickToDismiss', false),
15099        field('tabbarBehaviours', [Highlighting, Keying]),
15100        markers$1(['tabClass', 'selectedClass'])
15101    ]);
15102    const tabsPart = group({
15103        factory: TabButton,
15104        name: 'tabs',
15105        unit: 'tab',
15106        overrides: (barDetail) => {
15107            const dismissTab$1 = (tabbar, button) => {
15108                Highlighting.dehighlight(tabbar, button);
15109                emitWith(tabbar, dismissTab(), {
15110                    tabbar,
15111                    button
15112                });
15113            };
15114            const changeTab$1 = (tabbar, button) => {
15115                Highlighting.highlight(tabbar, button);
15116                emitWith(tabbar, changeTab(), {
15117                    tabbar,
15118                    button
15119                });
15120            };
15121            return {
15122                action: (button) => {
15123                    const tabbar = button.getSystem().getByUid(barDetail.uid).getOrDie();
15124                    const activeButton = Highlighting.isHighlighted(tabbar, button);
15125                    const response = (() => {
15126                        if (activeButton && barDetail.clickToDismiss) {
15127                            return dismissTab$1;
15128                        }
15129                        else if (!activeButton) {
15130                            return changeTab$1;
15131                        }
15132                        else {
15133                            return noop;
15134                        }
15135                    })();
15136                    response(tabbar, button);
15137                },
15138                domModification: {
15139                    classes: [barDetail.markers.tabClass]
15140                }
15141            };
15142        }
15143    });
15144    const parts$2 = constant$1([
15145        tabsPart
15146    ]);
15147
15148    const factory$7 = (detail, components, _spec, _externals) => ({
15149        'uid': detail.uid,
15150        'dom': detail.dom,
15151        components,
15152        'debug.sketcher': 'Tabbar',
15153        'domModification': {
15154            attributes: {
15155                role: 'tablist'
15156            }
15157        },
15158        'behaviours': augment(detail.tabbarBehaviours, [
15159            Highlighting.config({
15160                highlightClass: detail.markers.selectedClass,
15161                itemClass: detail.markers.tabClass,
15162                // https://www.w3.org/TR/2010/WD-wai-aria-practices-20100916/#tabpanel
15163                // Consider a more seam-less way of combining highlighting and toggling
15164                onHighlight: (tabbar, tab) => {
15165                    // TODO: Integrate highlighting and toggling in a nice way
15166                    set$9(tab.element, 'aria-selected', 'true');
15167                },
15168                onDehighlight: (tabbar, tab) => {
15169                    set$9(tab.element, 'aria-selected', 'false');
15170                }
15171            }),
15172            Keying.config({
15173                mode: 'flow',
15174                getInitial: (tabbar) => {
15175                    // Restore focus to the previously highlighted tab.
15176                    return Highlighting.getHighlighted(tabbar).map((tab) => tab.element);
15177                },
15178                selector: '.' + detail.markers.tabClass,
15179                executeOnMove: true
15180            })
15181        ])
15182    });
15183    const Tabbar = composite({
15184        name: 'Tabbar',
15185        configFields: schema$2(),
15186        partFields: parts$2(),
15187        factory: factory$7
15188    });
15189
15190    const factory$6 = (detail, _spec) => ({
15191        uid: detail.uid,
15192        dom: detail.dom,
15193        behaviours: augment(detail.tabviewBehaviours, [
15194            Replacing.config({})
15195        ]),
15196        domModification: {
15197            attributes: { role: 'tabpanel' }
15198        }
15199    });
15200    const Tabview = single({
15201        name: 'Tabview',
15202        configFields: [
15203            field('tabviewBehaviours', [Replacing])
15204        ],
15205        factory: factory$6
15206    });
15207
15208    const schema$1 = constant$1([
15209        defaulted('selectFirst', true),
15210        onHandler('onChangeTab'),
15211        onHandler('onDismissTab'),
15212        defaulted('tabs', []),
15213        field('tabSectionBehaviours', [])
15214    ]);
15215    const barPart = required({
15216        factory: Tabbar,
15217        schema: [
15218            required$1('dom'),
15219            requiredObjOf('markers', [
15220                required$1('tabClass'),
15221                required$1('selectedClass')
15222            ])
15223        ],
15224        name: 'tabbar',
15225        defaults: (detail) => {
15226            return {
15227                tabs: detail.tabs
15228            };
15229        }
15230    });
15231    const viewPart = required({
15232        factory: Tabview,
15233        name: 'tabview'
15234    });
15235    const parts$1 = constant$1([
15236        barPart,
15237        viewPart
15238    ]);
15239
15240    const factory$5 = (detail, components, _spec, _externals) => {
15241        const changeTab$1 = (button) => {
15242            const tabValue = Representing.getValue(button);
15243            getPart(button, detail, 'tabview').each((tabview) => {
15244                const tabWithValue = find$5(detail.tabs, (t) => t.value === tabValue);
15245                tabWithValue.each((tabData) => {
15246                    const panel = tabData.view();
15247                    // Update the tabview to refer to the current tab.
15248                    getOpt(button.element, 'id').each((id) => {
15249                        set$9(tabview.element, 'aria-labelledby', id);
15250                    });
15251                    Replacing.set(tabview, panel);
15252                    detail.onChangeTab(tabview, button, panel);
15253                });
15254            });
15255        };
15256        const changeTabBy = (section, byPred) => {
15257            getPart(section, detail, 'tabbar').each((tabbar) => {
15258                byPred(tabbar).each(emitExecute);
15259            });
15260        };
15261        return {
15262            uid: detail.uid,
15263            dom: detail.dom,
15264            components,
15265            behaviours: get$2(detail.tabSectionBehaviours),
15266            events: derive$2(flatten([
15267                detail.selectFirst ? [
15268                    runOnAttached((section, _simulatedEvent) => {
15269                        changeTabBy(section, Highlighting.getFirst);
15270                    })
15271                ] : [],
15272                [
15273                    run$1(changeTab(), (section, simulatedEvent) => {
15274                        const button = simulatedEvent.event.button;
15275                        changeTab$1(button);
15276                    }),
15277                    run$1(dismissTab(), (section, simulatedEvent) => {
15278                        const button = simulatedEvent.event.button;
15279                        detail.onDismissTab(section, button);
15280                    })
15281                ]
15282            ])),
15283            apis: {
15284                getViewItems: (section) => {
15285                    return getPart(section, detail, 'tabview').map((tabview) => Replacing.contents(tabview)).getOr([]);
15286                },
15287                // How should "clickToDismiss" interact with this? At the moment, it will never dismiss
15288                showTab: (section, tabKey) => {
15289                    // We only change the tab if it isn't currently active because that takes
15290                    // the whole "dismiss" issue out of the equation.
15291                    const getTabIfNotActive = (tabbar) => {
15292                        const candidates = Highlighting.getCandidates(tabbar);
15293                        const optTab = find$5(candidates, (c) => Representing.getValue(c) === tabKey);
15294                        return optTab.filter((tab) => !Highlighting.isHighlighted(tabbar, tab));
15295                    };
15296                    changeTabBy(section, getTabIfNotActive);
15297                }
15298            }
15299        };
15300    };
15301    const TabSection = composite({
15302        name: 'TabSection',
15303        configFields: schema$1(),
15304        partFields: parts$1(),
15305        factory: factory$5,
15306        apis: {
15307            getViewItems: (apis, component) => apis.getViewItems(component),
15308            showTab: (apis, component, tabKey) => {
15309                apis.showTab(component, tabKey);
15310            }
15311        }
15312    });
15313
15314    // When showing a value in an input field, which part of the item do we use?
15315    const setValueFromItem = (model, input, item) => {
15316        const itemData = Representing.getValue(item);
15317        Representing.setValue(input, itemData);
15318        setCursorAtEnd(input);
15319    };
15320    const setSelectionOn = (input, f) => {
15321        const el = input.element;
15322        const value = get$5(el);
15323        const node = el.dom;
15324        // Only do for valid input types.
15325        if (get$g(el, 'type') !== 'number') {
15326            f(node, value);
15327        }
15328    };
15329    const setCursorAtEnd = (input) => {
15330        setSelectionOn(input, (node, value) => node.setSelectionRange(value.length, value.length));
15331    };
15332    const setSelectionToEnd = (input, startOffset) => {
15333        setSelectionOn(input, (node, value) => node.setSelectionRange(startOffset, value.length));
15334    };
15335    const attemptSelectOver = (model, input, item) => {
15336        if (!model.selectsOver) {
15337            return Optional.none();
15338        }
15339        else {
15340            const currentValue = Representing.getValue(input);
15341            const inputDisplay = model.getDisplayText(currentValue);
15342            const itemValue = Representing.getValue(item);
15343            const itemDisplay = model.getDisplayText(itemValue);
15344            return itemDisplay.indexOf(inputDisplay) === 0 ?
15345                Optional.some(() => {
15346                    setValueFromItem(model, input, item);
15347                    setSelectionToEnd(input, inputDisplay.length);
15348                })
15349                : Optional.none();
15350        }
15351    };
15352
15353    const itemExecute = constant$1('alloy.typeahead.itemexecute');
15354
15355    // TODO: Fix this.
15356    const make$2 = (detail, components, spec, externals) => {
15357        const navigateList = (comp, simulatedEvent, highlighter) => {
15358            /*
15359             * If we have an open Sandbox with an active menu,
15360             * but no highlighted item, then highlight the menu
15361             *
15362             * If we have an open Sandbox with an active menu,
15363             * and there is a highlighted item, simulated a keydown
15364             * on the menu
15365             *
15366             * If we have a closed sandbox, open the sandbox
15367             *
15368             * Regardless, this is a user initiated action. End previewing.
15369             */
15370            detail.previewing.set(false);
15371            const sandbox = Coupling.getCoupled(comp, 'sandbox');
15372            if (Sandboxing.isOpen(sandbox)) {
15373                Composing.getCurrent(sandbox).each((menu) => {
15374                    Highlighting.getHighlighted(menu).fold(() => {
15375                        highlighter(menu);
15376                    }, () => {
15377                        dispatchEvent(sandbox, menu.element, 'keydown', simulatedEvent);
15378                    });
15379                });
15380            }
15381            else {
15382                const onOpenSync = (sandbox) => {
15383                    Composing.getCurrent(sandbox).each(highlighter);
15384                };
15385                open(detail, mapFetch(comp), comp, sandbox, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);
15386            }
15387        };
15388        // Due to the fact that typeahead probably need to separate value from text, they can't reuse
15389        // (easily) the same representing logic as input fields.
15390        const focusBehaviours$1 = focusBehaviours(detail);
15391        const mapFetch = (comp) => (tdata) => tdata.map((data) => {
15392            const menus = values(data.menus);
15393            const items = bind$3(menus, (menu) => filter$2(menu.items, (item) => item.type === 'item'));
15394            const repState = Representing.getState(comp);
15395            repState.update(map$2(items, (item) => item.data));
15396            return data;
15397        });
15398        // This function (getActiveMenu) is intended to make it easier to read what is happening
15399        // without having to decipher the Highlighting and Composing calls.
15400        const getActiveMenu = (sandboxComp) => Composing.getCurrent(sandboxComp);
15401        const typeaheadCustomEvents = 'typeaheadevents';
15402        const behaviours = [
15403            Focusing.config({}),
15404            Representing.config({
15405                onSetValue: detail.onSetValue,
15406                store: {
15407                    mode: 'dataset',
15408                    getDataKey: (comp) => get$5(comp.element),
15409                    // This really needs to be configurable
15410                    getFallbackEntry: (itemString) => ({
15411                        value: itemString,
15412                        meta: {}
15413                    }),
15414                    setValue: (comp, data) => {
15415                        set$4(comp.element, detail.model.getDisplayText(data));
15416                    },
15417                    ...detail.initialData.map((d) => wrap('initialValue', d)).getOr({})
15418                }
15419            }),
15420            Streaming.config({
15421                stream: {
15422                    mode: 'throttle',
15423                    delay: detail.responseTime,
15424                    stopEvent: false
15425                },
15426                onStream: (component, _simulatedEvent) => {
15427                    const sandbox = Coupling.getCoupled(component, 'sandbox');
15428                    const focusInInput = Focusing.isFocused(component);
15429                    // You don't want it to change when something else has triggered the change.
15430                    if (focusInInput) {
15431                        if (get$5(component.element).length >= detail.minChars) {
15432                            // Get the value of the previously active (selected/highlighted) item. We
15433                            // are going to try to preserve this.
15434                            const previousValue = getActiveMenu(sandbox).bind((activeMenu) => Highlighting.getHighlighted(activeMenu).map(Representing.getValue));
15435                            // Turning previewing ON here every keystroke is unnecessary, but relies
15436                            // on the fact that it will be turned off if required by highlighting events.
15437                            // So even if previewing was supposed to be off, turning it on here is
15438                            // just temporary, because the onOpenSync below will trigger a highlight
15439                            // if there was meant to be one, which will turn it off if required.
15440                            detail.previewing.set(true);
15441                            const onOpenSync = (_sandbox) => {
15442                                // This getActiveMenu relies on a menu being highlighted / active
15443                                getActiveMenu(sandbox).each((activeMenu) => {
15444                                    // The folds can make this hard to follow, but the basic gist of it is
15445                                    // that we want to see if we need to highlight one of the items in the
15446                                    // menu that we just opened. If we do highlight an item, then that
15447                                    // highlighting action will clear previewing (handled by the TieredMenu
15448                                    // part configuration for onHighlight). Note: that onOpenSync runs
15449                                    // *after* the highlightOnOpen setting.
15450                                    //
15451                                    // 1. If in "selectsOver" mode and we don't have a previous item,
15452                                    // then highlight the first one. This one will be used as the basis
15453                                    // for the "selectsOver" text selection. The act of highlighting the
15454                                    // first item will take us out of previewing mode. If the "selectsOver"
15455                                    // operation fails, it should clear the highlight, and restore previewing
15456                                    // 2. If not in "selectsOver" mode, and we don't have a previous item,
15457                                    // then we don't highlight anything. This will keep us in previewing
15458                                    // mode until the menu is interacted with (hover, navigation etc.)
15459                                    // 3. If we have a previous item, then try and rehighlight it. But if
15460                                    // we can't, the just highlight the first. Either action will take us
15461                                    // out of previewing mode.
15462                                    previousValue.fold(() => {
15463                                        // We are using "selectOver", so we need *something* to highlight
15464                                        if (detail.model.selectsOver) {
15465                                            Highlighting.highlightFirst(activeMenu);
15466                                        }
15467                                        // We aren't using "selectOver", so don't highlight anything
15468                                        // to preserve our "previewing" mode.
15469                                    }, (pv) => {
15470                                        // We have a previous item, so if we can't rehighlight it, then
15471                                        // we'll change to the first item. We want to keep some selection.
15472                                        Highlighting.highlightBy(activeMenu, (item) => {
15473                                            const itemData = Representing.getValue(item);
15474                                            return itemData.value === pv.value;
15475                                        });
15476                                        // Highlight first if could not find it?
15477                                        Highlighting.getHighlighted(activeMenu).orThunk(() => {
15478                                            Highlighting.highlightFirst(activeMenu);
15479                                            return Optional.none();
15480                                        });
15481                                    });
15482                                });
15483                            };
15484                            open(detail, mapFetch(component), component, sandbox, externals, onOpenSync, 
15485                            // The onOpenSync takes care of what should be given the highlights, but
15486                            // we want to highlight just the menu so that the onOpenSync can find the
15487                            // activeMenu.
15488                            HighlightOnOpen.HighlightJustMenu).get(noop);
15489                        }
15490                    }
15491                },
15492                cancelEvent: typeaheadCancel()
15493            }),
15494            Keying.config({
vendor: 12,560 bytes, lines 15495-15713
15495                mode: 'special',
15496                onDown: (comp, simulatedEvent) => {
15497                    // The navigation here will stop the "previewing" mode, because
15498                    // now the menu will get focus (fake focus, but focus nevertheless)
15499                    navigateList(comp, simulatedEvent, Highlighting.highlightFirst);
15500                    return Optional.some(true);
15501                },
15502                onEscape: (comp) => {
15503                    // Escape only has handling if the sandbox is visible. It has no meaning
15504                    // to the input itself.
15505                    const sandbox = Coupling.getCoupled(comp, 'sandbox');
15506                    if (Sandboxing.isOpen(sandbox)) {
15507                        Sandboxing.close(sandbox);
15508                        return Optional.some(true);
15509                    }
15510                    return Optional.none();
15511                },
15512                onUp: (comp, simulatedEvent) => {
15513                    // The navigation here will stop the "previewing" mode, because
15514                    // now the menu will get focus (fake focus, but focus nevertheless)
15515                    navigateList(comp, simulatedEvent, Highlighting.highlightLast);
15516                    return Optional.some(true);
15517                },
15518                onEnter: (comp) => {
15519                    const sandbox = Coupling.getCoupled(comp, 'sandbox');
15520                    const sandboxIsOpen = Sandboxing.isOpen(sandbox);
15521                    // 'Previewing' means that items are shown but none has been actively selected by the user.
15522                    // When previewing, all keyboard input should still be processed by the
15523                    // input itself, not the menu. The menu is not considered to have focus.
15524                    // 'Previewing' is turned on by (streaming) keystrokes, and turned off by
15525                    // successful interaction with the menu (navigation, highlighting, hovering).
15526                    // So if we aren't previewing, and the dropdown sandbox is open, then
15527                    // we process <enter> keys on the items in the menu. All this will do
15528                    // is trigger an itemExecute event. The typeahead events (in the spec below)
15529                    // are responsible for doing something with that event.
15530                    if (sandboxIsOpen && !detail.previewing.get()) {
15531                        return getActiveMenu(sandbox).bind((activeMenu) => Highlighting.getHighlighted(activeMenu)).map((item) => {
15532                            // And item was selected, so trigger execute and consider the
15533                            // <enter> key 'handled'
15534                            emitWith(comp, itemExecute(), { item });
15535                            return true;
15536                        });
15537                    }
15538                    else {
15539                        // We are either previewing, or the sandbox isn't open, so we should
15540                        // process the <enter> key inside the input itself. This should cancel
15541                        // any attempt to fetch data (the typeaheadCancel), and trigger the execute.
15542                        // We also close the sandbox if it's open.
15543                        const currentValue = Representing.getValue(comp);
15544                        emit(comp, typeaheadCancel());
15545                        detail.onExecute(sandbox, comp, currentValue);
15546                        // If we're open and previewing, close the sandbox after firing execute.
15547                        if (sandboxIsOpen) {
15548                            Sandboxing.close(sandbox);
15549                        }
15550                        return Optional.some(true);
15551                    }
15552                }
15553            }),
15554            Toggling.config({
15555                toggleClass: detail.markers.openClass,
15556                aria: {
15557                    mode: 'expanded'
15558                }
15559            }),
15560            Coupling.config({
15561                others: {
15562                    sandbox: (hotspot) => {
15563                        return makeSandbox$1(detail, hotspot, {
15564                            onOpen: () => Toggling.on(hotspot),
15565                            onClose: () => {
15566                                // TINY-9280: Remove aria-activedescendant that is set when menu item is highlighted
15567                                detail.lazyTypeaheadComp.get().each((input) => remove$8(input.element, 'aria-activedescendant'));
15568                                Toggling.off(hotspot);
15569                            }
15570                        });
15571                    }
15572                }
15573            }),
15574            config(typeaheadCustomEvents, [
15575                runOnAttached((typeaheadComp) => {
15576                    // Set up the reference to the typeahead, so that it can retrieved from
15577                    // the tiered menu part, even if the tieredmenu is in a different
15578                    // system / alloy root / mothership.
15579                    detail.lazyTypeaheadComp.set(Optional.some(typeaheadComp));
15580                }),
15581                runOnDetached((_typeaheadComp) => {
15582                    detail.lazyTypeaheadComp.set(Optional.none());
15583                }),
15584                runOnExecute$1((comp) => {
15585                    const onOpenSync = noop;
15586                    togglePopup(detail, mapFetch(comp), comp, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);
15587                }),
15588                run$1(itemExecute(), (comp, se) => {
15589                    const sandbox = Coupling.getCoupled(comp, 'sandbox');
15590                    // Copy the value from the executed item into the input, because it was "chosen"
15591                    setValueFromItem(detail.model, comp, se.event.item);
15592                    emit(comp, typeaheadCancel());
15593                    detail.onItemExecute(comp, sandbox, se.event.item, Representing.getValue(comp));
15594                    Sandboxing.close(sandbox);
15595                    setCursorAtEnd(comp);
15596                })
15597            ].concat(detail.dismissOnBlur ? [
15598                run$1(postBlur(), (typeahead) => {
15599                    const sandbox = Coupling.getCoupled(typeahead, 'sandbox');
15600                    // Only close the sandbox if the focus isn't inside it!
15601                    if (search(sandbox.element).isNone()) {
15602                        Sandboxing.close(sandbox);
15603                    }
15604                })
15605            ] : []))
15606        ];
15607        // The order specified here isn't important. Alloy just requires a
15608        // deterministic order for the configured behaviours.
15609        const eventOrder = {
15610            [detachedFromDom()]: [
15611                Representing.name(),
15612                Streaming.name(),
15613                typeaheadCustomEvents
15614            ],
15615            ...detail.eventOrder,
15616        };
15617        return {
15618            uid: detail.uid,
15619            dom: dom$1(deepMerge(detail, {
15620                // TODO: Add aria-activedescendant attribute
15621                inputAttributes: {
15622                    'role': 'combobox',
15623                    'aria-autocomplete': 'list',
15624                    'aria-haspopup': 'true'
15625                }
15626            })),
15627            behaviours: {
15628                ...focusBehaviours$1,
15629                ...augment(detail.typeaheadBehaviours, behaviours)
15630            },
15631            eventOrder
15632        };
15633    };
15634
15635    const schema = constant$1([
15636        option$3('lazySink'),
15637        required$1('fetch'),
15638        defaulted('minChars', 5),
15639        defaulted('responseTime', 1000),
15640        onHandler('onOpen'),
15641        // TODO: Remove dupe with Dropdown
15642        defaulted('getHotspot', Optional.some),
15643        defaulted('getAnchorOverrides', constant$1({})),
15644        defaulted('layouts', Optional.none()),
15645        defaulted('eventOrder', {}),
15646        // Information about what these model settings do can be found in TypeaheadTypes
15647        defaultedObjOf('model', {}, [
15648            defaulted('getDisplayText', (itemData) => itemData.meta !== undefined && itemData.meta.text !== undefined ? itemData.meta.text : itemData.value),
15649            defaulted('selectsOver', true),
15650            defaulted('populateFromBrowse', true)
15651        ]),
15652        onHandler('onSetValue'),
15653        onKeyboardHandler('onExecute'),
15654        onHandler('onItemExecute'),
15655        defaulted('inputClasses', []),
15656        defaulted('inputAttributes', {}),
15657        defaulted('inputStyles', {}),
15658        defaulted('matchWidth', true),
15659        defaulted('useMinWidth', false),
15660        defaulted('dismissOnBlur', true),
15661        markers$1(['openClass']),
15662        option$3('initialData'),
15663        option$3('listRole'),
15664        field('typeaheadBehaviours', [
15665            Focusing, Representing, Streaming, Keying, Toggling, Coupling
15666        ]),
15667        customField('lazyTypeaheadComp', () => Cell(Optional.none)),
15668        customField('previewing', () => Cell(true))
15669    ].concat(schema$9()).concat(sandboxFields()));
15670    const parts = constant$1([
15671        external$1({
15672            schema: [
15673                tieredMenuMarkers()
15674            ],
15675            name: 'menu',
15676            overrides: (detail) => {
15677                return {
15678                    fakeFocus: true,
15679                    onHighlightItem: (_tmenu, menu, item) => {
15680                        if (!detail.previewing.get()) {
15681                            // We need to use this type of reference, rather than just looking
15682                            // it up from the system by uid, because the input and the tieredmenu
15683                            // might be in different systems.
15684                            detail.lazyTypeaheadComp.get().each((input) => {
15685                                if (detail.model.populateFromBrowse) {
15686                                    setValueFromItem(detail.model, input, item);
15687                                }
15688                                // The focus is retained on the input element when the menu is shown, unlike the combobox, in which the focus is passed to the menu.
15689                                // This results in screen readers not being able to announce the menu or highlighted item.
15690                                // The solution is to tell screen readers which menu item is highlighted using the `aria-activedescendant` attribute.
15691                                // TINY-9280: The aria attribute is removed when the menu is closed.
15692                                // Since `onDehighlight` is called only when highlighting a new menu item, this will be handled in
15693                                // https://github.com/tinymce/tinymce/blob/2d8c1c034e8aa484b868a0c44605489ee0ca9cd4/modules/alloy/src/main/ts/ephox/alloy/ui/composite/TypeaheadSpec.ts#L282
15694                                getOpt(item.element, 'id').each((id) => set$9(input.element, 'aria-activedescendant', id));
15695                            });
15696                        }
15697                        else {
15698                            // ASSUMPTION: Currently, any interaction with the menu via the keyboard or the mouse
15699                            // will firstly clear previewing mode before triggering any highlights
15700                            // so if we are still in previewing mode by the time we get to the highlight call,
15701                            // that means that the highlight was triggered NOT by the user interacting
15702                            // with the menu, but instead by the Highlighting API call that happens automatically
15703                            // when a streamed keyboard input event is updating its results. That call will
15704                            // try to keep any active highlight if there already was one (defaulting to first
15705                            // if it can't find the original), but if there wasn't an active highlight, but
15706                            // it is using "selectsOver", it will just highlight the first item. In this
15707                            // latter case, it is only doing that so that selectsOver has something to copy.
15708                            // So all of the complex code below is trying to handle whether we should stay
15709                            // in previewing mode after this highlight, and the ONLY case where we should stay
15710                            // in previewing mode is that we were in previewing mode, we are using selectsOver,
15711                            // and the selectsOver failed to succeed. In that case, to stay in previewing mode,
15712                            // we want to cancel the highlight that we just made via the highlighting API
15713                            // and reset previewing to true. Otherwise, all codepaths should set previewing
15714                            // to false, because now we have a valid highlight.
15715                            //
15716                            // As of 2022-08-18, the selectsOver model is not in use by TinyMCE, so
15717                            // this subtle interaction is unfortunately largely untested. Also, if we can't
15718                            // get a reference to the typeahead input by lazyTypeaheadComp, then we don't
15719                            // change previewing, either. Note also, that it is likely that if we checked
15720                            // if selectsOver would succeed before setting the highlight in the streaming
15721                            // response, this could might be a lot easier to follow.
15722                            detail.lazyTypeaheadComp.get().each((input) => {
15723                                attemptSelectOver(detail.model, input, item).fold(
15724                                // If we are in "previewing" mode and we can't select over the
15725                                // thing that is first, then clear the highlight.
15726                                // Hopefully, this doesn't cause a flicker. Find a better
15727                                // way to do this.
15728                                () => {
15729                                    // If using "selectOver", we essentially want to cancel the highlight
15730                                    // that was only invoked just so that we'd have something to selectOver,
15731                                    // so we dehighlight, and then, importantly, *DON'T* clear previewing.
15732                                    // We'll set it to be true to be explicit, although it should
15733                                    // always be true if it reached here (unless an above function changed
15734                                    // it)
15735                                    if (detail.model.selectsOver) {
15736                                        Highlighting.dehighlight(menu, item);
15737                                        detail.previewing.set(true);
15738                                    }
15739                                    else {
15740                                        // Because we aren't using selectsOver mode, we now want to keep
15741                                        // whatever highlight we just made, and because we have a highlighted
15742                                        // item in the menu, we are no longer previewing.
15743                                        detail.previewing.set(false);
15744                                    }
15745                                }, ((selectOverTextInInput) => {
15746                                    // We have made a selection in the menu, and have selected over text
15747                                    // in the input, so clear previewing.
15748                                    selectOverTextInInput();
15749                                    detail.previewing.set(false);
15750                                }));
15751                            });
15752                        }
15753                    },
15754                    // Because the focus stays inside the input, this onExecute is fired when the
15755                    // user "clicks" on an item. The focusing behaviour should be configured
15756                    // so that items don't get focus, but they prevent a mousedown event from
15757                    // firing so that the typeahead doesn't lose focus. This is the handler
15758                    // for clicking on an item. We need to close the sandbox, update the typeahead
15759                    // to show the item clicked on, and fire an execute.
15760                    onExecute: (_menu, item) => {
15761                        // Note: This will only work when the typeahead and menu are in the same system.
15762                        return detail.lazyTypeaheadComp.get().map((typeahead) => {
15763                            emitWith(typeahead, itemExecute(), { item });
15764                            return true;
15765                        });
15766                    },
15767                    onHover: (menu, item) => {
15768                        // Hovering is also a user-initiated action, so previewing mode is over.
15769                        // TODO: Have a better API for managing state in between parts.
15770                        detail.previewing.set(false);
15771                        detail.lazyTypeaheadComp.get().each((input) => {
15772                            if (detail.model.populateFromBrowse) {
15773                                setValueFromItem(detail.model, input, item);
15774                            }
15775                        });
15776                    }
15777                };
15778            }
15779        })
15780    ]);
15781
15782    const Typeahead = composite({
15783        name: 'Typeahead',
15784        configFields: schema(),
15785        partFields: parts(),
15786        factory: make$2
15787    });
15788
15789    var global$b = tinymce.util.Tools.resolve('tinymce.ThemeManager');
15790
15791    var global$a = tinymce.util.Tools.resolve('tinymce.util.Delay');
15792
15793    var global$9 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
15794
15795    var global$8 = tinymce.util.Tools.resolve('tinymce.EditorManager');
15796
15797    var global$7 = tinymce.util.Tools.resolve('tinymce.Env');
15798
15799    const ToolbarMode$1 = {
15800        default: 'wrap',
15801        floating: 'floating',
15802        sliding: 'sliding',
15803        scrolling: 'scrolling'
15804    };
15805    const ToolbarLocation$1 = {
15806        auto: 'auto',
15807        top: 'top',
15808        bottom: 'bottom'
15809    };
15810    const option$2 = (name) => (editor) => editor.options.get(name);
15811    const wrapOptional = (fn) => (editor) => Optional.from(fn(editor));
15812    const register$f = (editor) => {
15813        const isPhone = global$7.deviceType.isPhone();
15814        const isMobile = global$7.deviceType.isTablet() || isPhone;
15815        const registerOption = editor.options.register;
15816        const stringOrFalseProcessor = (value) => isString(value) || value === false;
15817        const stringOrNumberProcessor = (value) => isString(value) || isNumber(value);
15818        registerOption('skin', {
15819            processor: (value) => isString(value) || value === false,
15820            default: 'oxide'
15821        });
15822        registerOption('skin_url', {
15823            processor: 'string'
15824        });
15825        registerOption('height', {
15826            processor: stringOrNumberProcessor,
15827            default: Math.max(editor.getElement().offsetHeight, 400)
15828        });
15829        registerOption('width', {
15830            processor: stringOrNumberProcessor,
15831            default: global$9.DOM.getStyle(editor.getElement(), 'width')
15832        });
15833        registerOption('min_height', {
15834            processor: 'number',
15835            default: 100
15836        });
15837        registerOption('min_width', {
15838            processor: 'number'
15839        });
15840        registerOption('max_height', {
15841            processor: 'number'
15842        });
15843        registerOption('max_width', {
15844            processor: 'number'
15845        });
15846        registerOption('style_formats', {
15847            processor: 'object[]'
15848        });
15849        registerOption('style_formats_merge', {
15850            processor: 'boolean',
15851            default: false
15852        });
15853        registerOption('style_formats_autohide', {
15854            processor: 'boolean',
15855            default: false
15856        });
15857        registerOption('line_height_formats', {
15858            processor: 'string',
15859            default: '1 1.1 1.2 1.3 1.4 1.5 2'
15860        });
15861        registerOption('font_family_formats', {
15862            processor: 'string',
15863            default: 'Andale Mono=andale mono,monospace;' +
15864                'Arial=arial,helvetica,sans-serif;' +
15865                'Arial Black=arial black,sans-serif;' +
15866                'Book Antiqua=book antiqua,palatino,serif;' +
15867                'Comic Sans MS=comic sans ms,sans-serif;' +
15868                'Courier New=courier new,courier,monospace;' +
15869                'Georgia=georgia,palatino,serif;' +
15870                'Helvetica=helvetica,arial,sans-serif;' +
15871                'Impact=impact,sans-serif;' +
15872                'Symbol=symbol;' +
15873                'Tahoma=tahoma,arial,helvetica,sans-serif;' +
15874                'Terminal=terminal,monaco,monospace;' +
15875                'Times New Roman=times new roman,times,serif;' +
15876                'Trebuchet MS=trebuchet ms,geneva,sans-serif;' +
15877                'Verdana=verdana,geneva,sans-serif;' +
15878                'Webdings=webdings;' +
15879                'Wingdings=wingdings,zapf dingbats'
15880        });
15881        registerOption('font_size_formats', {
15882            processor: 'string',
15883            default: '8pt 10pt 12pt 14pt 18pt 24pt 36pt'
15884        });
15885        registerOption('font_size_input_default_unit', {
15886            processor: 'string',
15887            default: 'pt'
15888        });
15889        registerOption('block_formats', {
15890            processor: 'string',
15891            default: 'Paragraph=p;' +
15892                'Heading 1=h1;' +
15893                'Heading 2=h2;' +
15894                'Heading 3=h3;' +
15895                'Heading 4=h4;' +
15896                'Heading 5=h5;' +
15897                'Heading 6=h6;' +
15898                'Preformatted=pre'
15899        });
15900        registerOption('content_langs', {
15901            processor: 'object[]'
15902        });
15903        registerOption('removed_menuitems', {
15904            processor: 'string',
15905            default: ''
15906        });
15907        registerOption('menubar', {
15908            processor: (value) => isString(value) || isBoolean(value),
15909            // Phones don't have a lot of screen space so disable the menubar
15910            default: !isPhone
15911        });
15912        registerOption('menu', {
15913            processor: 'object',
15914            default: {}
15915        });
15916        registerOption('toolbar', {
15917            processor: (value) => {
15918                if (isBoolean(value) || isString(value) || isArray(value)) {
15919                    return { value, valid: true };
15920                }
15921                else {
15922                    return { valid: false, message: 'Must be a boolean, string or array.' };
15923                }
15924            },
15925            default: true
15926        });
15927        // Register the toolbarN variations: toolbar1 -> toolbar9
15928        range$2(9, (num) => {
15929            registerOption('toolbar' + (num + 1), {
15930                processor: 'string'
15931            });
15932        });
15933        registerOption('toolbar_mode', {
15934            processor: 'string',
15935            // Use the default side-scrolling toolbar for tablets/phones
15936            default: isMobile ? 'scrolling' : 'floating'
15937        });
15938        registerOption('toolbar_groups', {
15939            processor: 'object',
15940            default: {}
15941        });
15942        registerOption('toolbar_location', {
15943            processor: 'string',
15944            default: ToolbarLocation$1.auto
15945        });
15946        registerOption('toolbar_persist', {
15947            processor: 'boolean',
15948            default: false
15949        });
15950        registerOption('toolbar_sticky', {
15951            processor: 'boolean',
15952            default: editor.inline
15953        });
15954        registerOption('toolbar_sticky_offset', {
15955            processor: 'number',
15956            default: 0
15957        });
15958        registerOption('fixed_toolbar_container', {
15959            processor: 'string',
15960            default: ''
15961        });
15962        registerOption('fixed_toolbar_container_target', {
15963            processor: 'object'
15964        });
15965        registerOption('ui_mode', {
15966            processor: 'string',
15967            default: 'combined'
15968        });
15969        registerOption('file_picker_callback', {
15970            processor: 'function'
15971        });
15972        registerOption('file_picker_validator_handler', {
15973            processor: 'function'
15974        });
15975        registerOption('file_picker_types', {
15976            processor: 'string'
15977        });
15978        registerOption('typeahead_urls', {
15979            processor: 'boolean',
15980            default: true
15981        });
15982        registerOption('anchor_top', {
15983            processor: stringOrFalseProcessor,
15984            default: '#top'
15985        });
15986        registerOption('anchor_bottom', {
15987            processor: stringOrFalseProcessor,
15988            default: '#bottom'
15989        });
15990        registerOption('draggable_modal', {
15991            processor: 'boolean',
15992            default: false
15993        });
15994        registerOption('statusbar', {
15995            processor: 'boolean',
15996            default: true
15997        });
15998        registerOption('elementpath', {
15999            processor: 'boolean',
16000            default: true
16001        });
16002        registerOption('branding', {
16003            processor: 'boolean',
16004            default: true
16005        });
16006        registerOption('promotion', {
16007            processor: 'boolean',
16008            default: true
16009        });
16010        registerOption('resize', {
16011            processor: (value) => value === 'both' || isBoolean(value),
16012            // Editor resize doesn't work on touch devices at this stage
16013            default: !global$7.deviceType.isTouch()
16014        });
16015        registerOption('sidebar_show', {
16016            processor: 'string'
16017        });
16018        registerOption('view_show', {
16019            processor: 'string'
16020        });
16021        // This option is being registered in the theme instead of the help plugin as it cannot be accessed from the theme when registered there
16022        registerOption('help_accessibility', {
16023            processor: 'boolean',
16024            default: editor.hasPlugin('help')
16025        });
16026        registerOption('default_font_stack', {
16027            processor: 'string[]',
16028            default: []
16029        });
16030    };
16031    const isReadOnly = option$2('readonly');
16032    const isDisabled = option$2('disabled');
16033    const getHeightOption = option$2('height');
16034    const getWidthOption = option$2('width');
16035    const getMinWidthOption = wrapOptional(option$2('min_width'));
16036    const getMinHeightOption = wrapOptional(option$2('min_height'));
16037    const getMaxWidthOption = wrapOptional(option$2('max_width'));
16038    const getMaxHeightOption = wrapOptional(option$2('max_height'));
16039    const getUserStyleFormats = wrapOptional(option$2('style_formats'));
16040    const shouldMergeStyleFormats = option$2('style_formats_merge');
16041    const shouldAutoHideStyleFormats = option$2('style_formats_autohide');
16042    const getContentLanguages = option$2('content_langs');
16043    const getRemovedMenuItems = option$2('removed_menuitems');
16044    const getToolbarMode = option$2('toolbar_mode');
16045    const getToolbarGroups = option$2('toolbar_groups');
16046    const getToolbarLocation = option$2('toolbar_location');
16047    const fixedContainerSelector = option$2('fixed_toolbar_container');
16048    const fixedToolbarContainerTarget = option$2('fixed_toolbar_container_target');
16049    const isToolbarPersist = option$2('toolbar_persist');
16050    const getStickyToolbarOffset = option$2('toolbar_sticky_offset');
16051    const getMenubar = option$2('menubar');
16052    const getToolbar = option$2('toolbar');
16053    const getFilePickerCallback = option$2('file_picker_callback');
16054    const getFilePickerValidatorHandler = option$2('file_picker_validator_handler');
16055    const getFontSizeInputDefaultUnit = option$2('font_size_input_default_unit');
16056    const getFilePickerTypes = option$2('file_picker_types');
16057    const useTypeaheadUrls = option$2('typeahead_urls');
16058    const getAnchorTop = option$2('anchor_top');
16059    const getAnchorBottom = option$2('anchor_bottom');
16060    const isDraggableModal$1 = option$2('draggable_modal');
16061    const useStatusBar = option$2('statusbar');
16062    const useElementPath = option$2('elementpath');
16063    const useBranding = option$2('branding');
16064    const getResize = option$2('resize');
16065    const getPasteAsText = option$2('paste_as_text');
16066    const getSidebarShow = option$2('sidebar_show');
16067    const getViewShow = option$2('view_show');
16068    const promotionEnabled = option$2('promotion');
16069    const useHelpAccessibility = option$2('help_accessibility');
16070    const getDefaultFontStack = option$2('default_font_stack');
16071    const getSkin = option$2('skin');
16072    const isSkinDisabled = (editor) => editor.options.get('skin') === false;
16073    const isMenubarEnabled = (editor) => editor.options.get('menubar') !== false;
16074    const getSkinUrl = (editor) => {
16075        const skinUrl = editor.options.get('skin_url');
16076        if (isSkinDisabled(editor)) {
16077            return skinUrl;
16078        }
16079        else {
16080            if (skinUrl) {
16081                return editor.documentBaseURI.toAbsolute(skinUrl);
16082            }
16083            else {
16084                const skin = editor.options.get('skin');
16085                return global$8.baseURL + '/skins/ui/' + skin;
16086            }
16087        }
16088    };
16089    const getSkinUrlOption = (editor) => Optional.from(editor.options.get('skin_url'));
16090    const getLineHeightFormats = (editor) => editor.options.get('line_height_formats').split(' ');
16091    const isToolbarEnabled = (editor) => {
16092        const toolbar = getToolbar(editor);
16093        const isToolbarString = isString(toolbar);
16094        const isToolbarObjectArray = isArray(toolbar) && toolbar.length > 0;
16095        // Toolbar is enabled if its value is true, a string or non-empty object array, but not string array
16096        return !isMultipleToolbars(editor) && (isToolbarObjectArray || isToolbarString || toolbar === true);
16097    };
16098    // Convert toolbar<n> into toolbars array
16099    const getMultipleToolbarsOption = (editor) => {
16100        const toolbars = range$2(9, (num) => editor.options.get('toolbar' + (num + 1)));
16101        const toolbarArray = filter$2(toolbars, isString);
16102        return someIf(toolbarArray.length > 0, toolbarArray);
16103    };
16104    // Check if multiple toolbars is enabled
16105    // Multiple toolbars is enabled if toolbar value is a string array or if toolbar<n> is present
16106    const isMultipleToolbars = (editor) => getMultipleToolbarsOption(editor).fold(() => {
16107        const toolbar = getToolbar(editor);
16108        return isArrayOf(toolbar, isString) && toolbar.length > 0;
16109    }, always);
16110    const isToolbarLocationBottom = (editor) => getToolbarLocation(editor) === ToolbarLocation$1.bottom;
16111    const fixedContainerTarget = (editor) => {
16112        if (!editor.inline) {
16113            // fixed_toolbar_container(_target) is only available in inline mode
16114            return Optional.none();
16115        }
16116        const selector = fixedContainerSelector(editor) ?? '';
16117        if (selector.length > 0) {
16118            // If we have a valid selector
16119            return descendant(body(), selector);
16120        }
16121        const element = fixedToolbarContainerTarget(editor);
16122        if (isNonNullable(element)) {
16123            // If we have a valid target
16124            return Optional.some(SugarElement.fromDom(element));
16125        }
16126        return Optional.none();
16127    };
16128    const useFixedContainer = (editor) => editor.inline && fixedContainerTarget(editor).isSome();
16129    const getUiContainer = (editor) => {
16130        const fixedContainer = fixedContainerTarget(editor);
16131        return fixedContainer.getOrThunk(() => getContentContainer(getRootNode(SugarElement.fromDom(editor.getElement()))));
16132    };
16133    const isDistractionFree = (editor) => editor.inline && !isMenubarEnabled(editor) && !isToolbarEnabled(editor) && !isMultipleToolbars(editor);
16134    const isStickyToolbar = (editor) => {
16135        const isStickyToolbar = editor.options.get('toolbar_sticky');
16136        return (isStickyToolbar || editor.inline) && !useFixedContainer(editor) && !isDistractionFree(editor);
16137    };
16138    const isSplitUiMode = (editor) => !useFixedContainer(editor) && editor.options.get('ui_mode') === 'split';
16139    const getMenus = (editor) => {
16140        const menu = editor.options.get('menu');
16141        return map$1(menu, (menu) => ({ ...menu, items: menu.items }));
16142    };
16143
16144    var Options = /*#__PURE__*/Object.freeze({
16145        __proto__: null,
16146        ToolbarMode: ToolbarMode$1,
16147        ToolbarLocation: ToolbarLocation$1,
16148        getAnchorBottom: getAnchorBottom,
16149        getAnchorTop: getAnchorTop,
16150        getContentLanguages: getContentLanguages,
16151        getDefaultFontStack: getDefaultFontStack,
16152        getFilePickerCallback: getFilePickerCallback,
16153        getFilePickerTypes: getFilePickerTypes,
16154        getFilePickerValidatorHandler: getFilePickerValidatorHandler,
16155        getFontSizeInputDefaultUnit: getFontSizeInputDefaultUnit,
16156        getHeightOption: getHeightOption,
16157        getLineHeightFormats: getLineHeightFormats,
16158        getMaxHeightOption: getMaxHeightOption,
16159        getMaxWidthOption: getMaxWidthOption,
16160        getMenubar: getMenubar,
16161        getMenus: getMenus,
16162        getMinHeightOption: getMinHeightOption,
16163        getMinWidthOption: getMinWidthOption,
16164        getMultipleToolbarsOption: getMultipleToolbarsOption,
16165        getPasteAsText: getPasteAsText,
16166        getRemovedMenuItems: getRemovedMenuItems,
16167        getResize: getResize,
16168        getSidebarShow: getSidebarShow,
16169        getSkin: getSkin,
16170        getSkinUrl: getSkinUrl,
16171        getSkinUrlOption: getSkinUrlOption,
16172        getStickyToolbarOffset: getStickyToolbarOffset,
16173        getToolbar: getToolbar,
16174        getToolbarGroups: getToolbarGroups,
16175        getToolbarLocation: getToolbarLocation,
16176        getToolbarMode: getToolbarMode,
16177        getUiContainer: getUiContainer,
16178        getUserStyleFormats: getUserStyleFormats,
16179        getViewShow: getViewShow,
16180        getWidthOption: getWidthOption,
16181        isDisabled: isDisabled,
16182        isDistractionFree: isDistractionFree,
16183        isDraggableModal: isDraggableModal$1,
16184        isMenubarEnabled: isMenubarEnabled,
16185        isMultipleToolbars: isMultipleToolbars,
16186        isReadOnly: isReadOnly,
16187        isSkinDisabled: isSkinDisabled,
16188        isSplitUiMode: isSplitUiMode,
16189        isStickyToolbar: isStickyToolbar,
16190        isToolbarEnabled: isToolbarEnabled,
16191        isToolbarLocationBottom: isToolbarLocationBottom,
16192        isToolbarPersist: isToolbarPersist,
16193        promotionEnabled: promotionEnabled,
16194        register: register$f,
16195        shouldAutoHideStyleFormats: shouldAutoHideStyleFormats,
16196        shouldMergeStyleFormats: shouldMergeStyleFormats,
16197        useBranding: useBranding,
16198        useElementPath: useElementPath,
16199        useFixedContainer: useFixedContainer,
16200        useHelpAccessibility: useHelpAccessibility,
16201        useStatusBar: useStatusBar,
16202        useTypeaheadUrls: useTypeaheadUrls
16203    });
16204
16205    // See https://developer.mozilla.org/en-US/docs/Glossary/Scroll_container for what makes an element scrollable
16206    const nonScrollingOverflows = ['visible', 'hidden', 'clip'];
16207    const isScrollingOverflowValue = (value) => trim$1(value).length > 0 && !contains$2(nonScrollingOverflows, value);
16208    const isScroller = (elem) => {
16209        if (isHTMLElement(elem)) {
16210            const overflowX = get$e(elem, 'overflow-x');
16211            const overflowY = get$e(elem, 'overflow-y');
16212            return isScrollingOverflowValue(overflowX) || isScrollingOverflowValue(overflowY);
16213        }
16214        else {
16215            return false;
16216        }
16217    };
16218    const isFullscreen = (editor) => editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen();
16219    // NOTE: Calculating the list of scrolling ancestors each time this function is called might
16220    // be unnecessary. It will depend on its usage.
16221    const detect = (editor, popupSinkElem) => {
16222        const ancestorsScrollers = ancestors(popupSinkElem, isScroller);
16223        // If there is no scrollable container, we try to see if it's in a shadow root, and try to traverse beyond the host of shadow root to retrieve the scrollable container
16224        // If it is not within a ShadowRoot, since if there's a scrollable container as the ancestors, then it would not execute the code below, or return an empty array if it's not in a ShadowRoot
16225        const scrollers = ancestorsScrollers.length === 0
16226            ? getShadowRoot(popupSinkElem).map(getShadowHost).map((x) => ancestors(x, isScroller)).getOr([])
16227            : ancestorsScrollers;
16228        return head(scrollers)
16229            .map((element) => ({
16230            element,
16231            // A list of all scrolling elements above the nearest scroller,
16232            // ordered from closest to popup -> closest to top of document
16233            others: scrollers.slice(1),
16234            isFullscreen: () => isFullscreen(editor)
16235        }));
16236    };
16237    const detectWhenSplitUiMode = (editor, popupSinkElem) => isSplitUiMode(editor) ? detect(editor, popupSinkElem) : Optional.none();
16238    // Using all the scrolling viewports in the ancestry, limit the absolute
16239    // coordinates of window so that the bounds are limited by all the scrolling
16240    // viewports.
16241    const getBoundsFrom = (sc) => {
16242        const scrollableBoxes = [
16243            // sc.element is the main scroller, others are *additional* scrollers above that
16244            // we need to combine all of them to constrain the bounds
16245            ...map$2(sc.others, box$1),
16246            win()
16247        ];
16248        return sc.isFullscreen() ? win() : constrainByMany(box$1(sc.element), scrollableBoxes);
16249    };
16250
16251    /*! @license DOMPurify 3.4.12 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.12/LICENSE */
16252
16253    function _arrayLikeToArray(r, a) {
16254      (null == a || a > r.length) && (a = r.length);
16255      for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
16256      return n;
16257    }
16258    function _arrayWithHoles(r) {
16259      if (Array.isArray(r)) return r;
16260    }
16261    function _iterableToArrayLimit(r, l) {
16262      var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
16263      if (null != t) {
16264        var e,
16265          n,
16266          i,
16267          u,
16268          a = [],
16269          f = true,
16270          o = false;
vendor: 9,367 bytes, lines 16271-16518
16271        try {
16272          if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
16273        } catch (r) {
16274          o = true, n = r;
16275        } finally {
16276          try {
16277            if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
16278          } finally {
16279            if (o) throw n;
16280          }
16281        }
16282        return a;
16283      }
16284    }
16285    function _nonIterableRest() {
16286      throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
16287    }
16288    function _slicedToArray(r, e) {
16289      return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
16290    }
16291    function _unsupportedIterableToArray(r, a) {
16292      if (r) {
16293        if ("string" == typeof r) return _arrayLikeToArray(r, a);
16294        var t = {}.toString.call(r).slice(8, -1);
16295        return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
16296      }
16297    }
16298
16299    const entries = Object.entries,
16300      setPrototypeOf = Object.setPrototypeOf,
16301      isFrozen = Object.isFrozen,
16302      getPrototypeOf = Object.getPrototypeOf,
16303      getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
16304    let freeze = Object.freeze,
16305      seal = Object.seal,
16306      create$1 = Object.create; // eslint-disable-line import/no-mutable-exports
16307    let _ref = typeof Reflect !== 'undefined' && Reflect,
16308      apply = _ref.apply,
16309      construct = _ref.construct;
16310    if (!freeze) {
16311      freeze = function freeze(x) {
16312        return x;
16313      };
16314    }
16315    if (!seal) {
16316      seal = function seal(x) {
16317        return x;
16318      };
16319    }
16320    if (!apply) {
16321      apply = function apply(func, thisArg) {
16322        for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
16323          args[_key - 2] = arguments[_key];
16324        }
16325        return func.apply(thisArg, args);
16326      };
16327    }
16328    if (!construct) {
16329      construct = function construct(Func) {
16330        for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
16331          args[_key2 - 1] = arguments[_key2];
16332        }
16333        return new Func(...args);
16334      };
16335    }
16336    const arrayForEach = unapply(Array.prototype.forEach);
16337    const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
16338    const arrayPop = unapply(Array.prototype.pop);
16339    const arrayPush = unapply(Array.prototype.push);
16340    const arraySplice = unapply(Array.prototype.splice);
16341    const arrayIsArray = Array.isArray;
16342    const stringToLowerCase = unapply(String.prototype.toLowerCase);
16343    const stringToString = unapply(String.prototype.toString);
16344    const stringMatch = unapply(String.prototype.match);
16345    const stringReplace = unapply(String.prototype.replace);
16346    const stringIndexOf = unapply(String.prototype.indexOf);
16347    const stringTrim = unapply(String.prototype.trim);
16348    const numberToString = unapply(Number.prototype.toString);
16349    const booleanToString = unapply(Boolean.prototype.toString);
16350    const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
16351    const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
16352    const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
16353    const objectToString = unapply(Object.prototype.toString);
16354    const regExpTest = unapply(RegExp.prototype.test);
16355    const typeErrorCreate = unconstruct(TypeError);
16356    /**
16357     * Creates a new function that calls the given function with a specified thisArg and arguments.
16358     *
16359     * @param func - The function to be wrapped and called.
16360     * @returns A new function that calls the given function with a specified thisArg and arguments.
16361     */
16362    function unapply(func) {
16363      return function (thisArg) {
16364        if (thisArg instanceof RegExp) {
16365          thisArg.lastIndex = 0;
16366        }
16367        for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
16368          args[_key3 - 1] = arguments[_key3];
16369        }
16370        return apply(func, thisArg, args);
16371      };
16372    }
16373    /**
16374     * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
16375     *
16376     * @param func - The constructor function to be wrapped and called.
16377     * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
16378     */
16379    function unconstruct(Func) {
16380      return function () {
16381        for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
16382          args[_key4] = arguments[_key4];
16383        }
16384        return construct(Func, args);
16385      };
16386    }
16387    /**
16388     * Add properties to a lookup table
16389     *
16390     * @param set - The set to which elements will be added.
16391     * @param array - The array containing elements to be added to the set.
16392     * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
16393     * @returns The modified set with added elements.
16394     */
16395    function addToSet(set, array) {
16396      let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
16397      if (setPrototypeOf) {
16398        // Make 'in' and truthy checks like Boolean(set.constructor)
16399        // independent of any properties defined on Object.prototype.
16400        // Prevent prototype setters from intercepting set as a this value.
16401        setPrototypeOf(set, null);
16402      }
16403      if (!arrayIsArray(array)) {
16404        return set;
16405      }
16406      let l = array.length;
16407      while (l--) {
16408        let element = array[l];
16409        if (typeof element === 'string') {
16410          const lcElement = transformCaseFunc(element);
16411          if (lcElement !== element) {
16412            // Config presets (e.g. tags.js, attrs.js) are immutable.
16413            if (!isFrozen(array)) {
16414              array[l] = lcElement;
16415            }
16416            element = lcElement;
16417          }
16418        }
16419        set[element] = true;
16420      }
16421      return set;
16422    }
16423    /**
16424     * Clean up an array to harden against CSPP
16425     *
16426     * @param array - The array to be cleaned.
16427     * @returns The cleaned version of the array
16428     */
16429    function cleanArray(array) {
16430      for (let index = 0; index < array.length; index++) {
16431        const isPropertyExist = objectHasOwnProperty(array, index);
16432        if (!isPropertyExist) {
16433          array[index] = null;
16434        }
16435      }
16436      return array;
16437    }
16438    /**
16439     * Shallow clone an object
16440     *
16441     * @param object - The object to be cloned.
16442     * @returns A new object that copies the original.
16443     */
16444    function clone(object) {
16445      const newObject = create$1(null);
16446      for (const _ref2 of entries(object)) {
16447        var _ref3 = _slicedToArray(_ref2, 2);
16448        const property = _ref3[0];
16449        const value = _ref3[1];
16450        const isPropertyExist = objectHasOwnProperty(object, property);
16451        if (isPropertyExist) {
16452          if (arrayIsArray(value)) {
16453            newObject[property] = cleanArray(value);
16454          } else if (value && typeof value === 'object' && value.constructor === Object) {
16455            newObject[property] = clone(value);
16456          } else {
16457            newObject[property] = value;
16458          }
16459        }
16460      }
16461      return newObject;
16462    }
16463    /**
16464     * Convert non-node values into strings without depending on direct property access.
16465     *
16466     * @param value - The value to stringify.
16467     * @returns A string representation of the provided value.
16468     */
16469    function stringifyValue(value) {
16470      switch (typeof value) {
16471        case 'string':
16472          {
16473            return value;
16474          }
16475        case 'number':
16476          {
16477            return numberToString(value);
16478          }
16479        case 'boolean':
16480          {
16481            return booleanToString(value);
16482          }
16483        case 'bigint':
16484          {
16485            return bigintToString ? bigintToString(value) : '0';
16486          }
16487        case 'symbol':
16488          {
16489            return symbolToString ? symbolToString(value) : 'Symbol()';
16490          }
16491        case 'undefined':
16492          {
16493            return objectToString(value);
16494          }
16495        case 'function':
16496        case 'object':
16497          {
16498            if (value === null) {
16499              return objectToString(value);
16500            }
16501            const valueAsRecord = value;
16502            const valueToString = lookupGetter(valueAsRecord, 'toString');
16503            if (typeof valueToString === 'function') {
16504              const stringified = valueToString(valueAsRecord);
16505              return typeof stringified === 'string' ? stringified : objectToString(stringified);
16506            }
16507            return objectToString(value);
16508          }
16509        default:
16510          {
16511            return objectToString(value);
16512          }
16513      }
16514    }
16515    /**
16516     * This method automatically checks if the prop is function or getter and behaves accordingly.
16517     *
16518     * @param object - The object to look up the getter function in its prototype chain.
16519     * @param prop - The property name for which to find the getter function.
16520     * @returns The getter function found in the prototype chain or a fallback function.
16521     */
16522    function lookupGetter(object, prop) {
16523      while (object !== null) {
16524        const desc = getOwnPropertyDescriptor(object, prop);
16525        if (desc) {
16526          if (desc.get) {
16527            return unapply(desc.get);
16528          }
16529          if (typeof desc.value === 'function') {
16530            return unapply(desc.value);
16531          }
16532        }
16533        object = getPrototypeOf(object);
16534      }
16535      function fallbackValue() {
16536        return null;
16537      }
16538      return fallbackValue;
16539    }
16540    function isRegex(value) {
16541      try {
16542        regExpTest(value, '');
16543        return true;
16544      } catch (_unused) {
16545        return false;
16546      }
16547    }
16548
16549    const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
16550    const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
16551    const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
16552    // List of SVG elements that are disallowed by default.
16553    // We still need to know them so that we can do namespace
16554    // checks properly in case one wants to add them to
16555    // allow-list.
16556    const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
16557    const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
16558    // Similarly to SVG, we want to know all MathML elements,
16559    // even those that we disallow by default.
16560    const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
16561    const text$1
vendor: 15,995 bytes, lines 16561-16796
16561 = freeze(['#text']);
16562
16563    const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
16564    const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dominant-baseline', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-orientation', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
16565    const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
16566    const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
16567
16568    const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
16569    const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
16570    const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
16571    const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
16572    const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
16573    const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
16574    );
16575    const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
16576    const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
16577    );
16578    const DOCTYPE_NAME = seal(/^html$/i);
16579    const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
16580    // Markup-significant character probes used by _sanitizeElements.
16581    // Shared module-level instances are safe despite the sticky /g flags:
16582    // unapply() resets lastIndex for RegExp receivers before every call.
16583    const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
16584    const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
16585    const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
16586    const SELF_CLOSING_TAG = seal(/\/>/i);
16587
16588    // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
16589    const NODE_TYPE = {
16590      element: 1,
16591      attribute: 2,
16592      text: 3,
16593      cdataSection: 4,
16594      entityReference: 5,
16595      // Deprecated
16596      entityNode: 6,
16597      // Deprecated
16598      processingInstruction: 7,
16599      comment: 8,
16600      document: 9,
16601      documentType: 10,
16602      documentFragment: 11,
16603      notation: 12 // Deprecated
16604    };
16605    const getGlobal = function getGlobal() {
16606      return typeof window === 'undefined' ? null : window;
16607    };
16608    /**
16609     * Creates a no-op policy for internal use only.
16610     * Don't export this function outside this module!
16611     * @param trustedTypes The policy factory.
16612     * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
16613     * @return The policy created (or null, if Trusted Types
16614     * are not supported or creating the policy failed).
16615     */
16616    const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
16617      if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
16618        return null;
16619      }
16620      // Allow the callers to control the unique policy name
16621      // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
16622      // Policy creation with duplicate names throws in Trusted Types.
16623      let suffix = null;
16624      const ATTR_NAME = 'data-tt-policy-suffix';
16625      if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
16626        suffix = purifyHostElement.getAttribute(ATTR_NAME);
16627      }
16628      const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
16629      try {
16630        return trustedTypes.createPolicy(policyName, {
16631          createHTML(html) {
16632            return html;
16633          },
16634          createScriptURL(scriptUrl) {
16635            return scriptUrl;
16636          }
16637        });
16638      } catch (_) {
16639        // Policy creation failed (most likely another DOMPurify script has
16640        // already run). Skip creating the policy, as this will only cause errors
16641        // if TT are enforced.
16642        console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
16643        return null;
16644      }
16645    };
16646    const _createHooksMap = function _createHooksMap() {
16647      return {
16648        afterSanitizeAttributes: [],
16649        afterSanitizeElements: [],
16650        afterSanitizeShadowDOM: [],
16651        beforeSanitizeAttributes: [],
16652        beforeSanitizeElements: [],
16653        beforeSanitizeShadowDOM: [],
16654        uponSanitizeAttribute: [],
16655        uponSanitizeElement: [],
16656        uponSanitizeShadowNode: []
16657      };
16658    };
16659    /**
16660     * Resolve a set-valued configuration option: a fresh set built from
16661     * cfg[key] when it is an own array property (seeded with a clone of
16662     * options.base when given, case-normalized via options.transform),
16663     * the fallback set otherwise.
16664     *
16665     * @param cfg the cloned, prototype-free configuration object
16666     * @param key the configuration property to read
16667     * @param fallback the set to use when the option is absent or not an array
16668     * @param options transform and optional base set to merge into
16669     * @returns the resolved set
16670     */
16671    const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
16672      return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
16673    };
16674    function createDOMPurify() {
16675      let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
16676      const DOMPurify = root => createDOMPurify(root);
16677      DOMPurify.version = '3.4.12';
16678      DOMPurify.removed = [];
16679      if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
16680        // Not running in a browser, provide a factory function
16681        // so that you can pass your own Window
16682        DOMPurify.isSupported = false;
16683        return DOMPurify;
16684      }
16685      let document = window.document;
16686      const originalDocument = document;
16687      const currentScript = originalDocument.currentScript;
16688      window.DocumentFragment;
16689        const HTMLTemplateElement = window.HTMLTemplateElement,
16690        Node = window.Node,
16691        Element = window.Element,
16692        NodeFilter = window.NodeFilter,
16693        _window$NamedNodeMap = window.NamedNodeMap;
16694        _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;
16695        window.HTMLFormElement;
16696        const DOMParser = window.DOMParser,
16697        trustedTypes = window.trustedTypes;
16698      const ElementPrototype = Element.prototype;
16699      const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
16700      const remove = lookupGetter(ElementPrototype, 'remove');
16701      const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
16702      const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
16703      const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
16704      const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');
16705      const getAttributes = lookupGetter(ElementPrototype, 'attributes');
16706      const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
16707      const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
16708      // As per issue #47, the web-components registry is inherited by a
16709      // new document created via createHTMLDocument. As per the spec
16710      // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
16711      // a new empty registry is used when creating a template contents owner
16712      // document, so we use that as our parent document to ensure nothing
16713      // is inherited.
16714      if (typeof HTMLTemplateElement === 'function') {
16715        const template = document.createElement('template');
16716        if (template.content && template.content.ownerDocument) {
16717          document = template.content.ownerDocument;
16718        }
16719      }
16720      let trustedTypesPolicy;
16721      let emptyHTML = '';
16722      // The instance's own internal Trusted Types policy. Unlike a caller-supplied
16723      // `TRUSTED_TYPES_POLICY`, this is created at most once — Trusted Types throws
16724      // on duplicate policy names — and is the only policy allowed to persist
16725      // across configurations and survive `clearConfig()`.
16726      let defaultTrustedTypesPolicy;
16727      let defaultTrustedTypesPolicyResolved = false;
16728      // Tracks whether we are already inside a call to the configured Trusted Types
16729      // policy (`createHTML` or `createScriptURL`). If a supplied policy callback
16730      // itself calls `DOMPurify.sanitize` (the cause of #1422), `sanitize` would
16731      // re-enter the policy and recurse until the stack overflows. We detect that
16732      // re-entry and throw a clear, actionable error instead. The guard is shared
16733      // across both callbacks, because either one re-entering `sanitize` triggers
16734      // the same unbounded recursion.
16735      let IN_TRUSTED_TYPES_POLICY = 0;
16736      const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() {
16737        if (IN_TRUSTED_TYPES_POLICY > 0) {
16738          throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or ' + 'createScriptURL) must not call DOMPurify.sanitize, as that causes ' + 'infinite recursion. Do not pass a policy whose callbacks wrap ' + 'DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted ' + 'Types" section of the README.');
16739        }
16740      };
16741      const _createTrustedHTML = function _createTrustedHTML(html) {
16742        _assertNotInTrustedTypesPolicy();
16743        IN_TRUSTED_TYPES_POLICY++;
16744        try {
16745          return trustedTypesPolicy.createHTML(html);
16746        } finally {
16747          IN_TRUSTED_TYPES_POLICY--;
16748        }
16749      };
16750      const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) {
16751        _assertNotInTrustedTypesPolicy();
16752        IN_TRUSTED_TYPES_POLICY++;
16753        try {
16754          return trustedTypesPolicy.createScriptURL(scriptUrl);
16755        } finally {
16756          IN_TRUSTED_TYPES_POLICY--;
16757        }
16758      };
16759      // Lazily resolve (and cache) the instance's internal default policy.
16760      // Resolution is attempted at most once: a successful `createPolicy` cannot be
16761      // repeated (Trusted Types throws on duplicate names), and a failed or
16762      // unsupported attempt must not be retried on every parse.
16763      const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() {
16764        if (!defaultTrustedTypesPolicyResolved) {
16765          defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
16766          defaultTrustedTypesPolicyResolved = true;
16767        }
16768        return defaultTrustedTypesPolicy;
16769      };
16770      const _document = document,
16771        implementation = _document.implementation,
16772        createNodeIterator = _document.createNodeIterator,
16773        createDocumentFragment = _document.createDocumentFragment,
16774        getElementsByTagName = _document.getElementsByTagName;
16775      const importNode = originalDocument.importNode;
16776      let hooks = _createHooksMap();
16777      /**
16778       * Expose whether this browser supports running the full DOMPurify.
16779       */
16780      DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
16781      const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
16782        ERB_EXPR$1 = ERB_EXPR,
16783        TMPLIT_EXPR$1 = TMPLIT_EXPR,
16784        DATA_ATTR$1 = DATA_ATTR,
16785        ARIA_ATTR$1 = ARIA_ATTR,
16786        IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
16787        ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
16788        CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
16789      let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
16790      /**
16791       * We consider the elements and attributes below to be safe. Ideally
16792       * don't add any new ones but feel free to remove unwanted ones.
16793       */
16794      /* allowed element names */
16795      let ALLOWED_TAGS = null;
16796      const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text
vendor: 1,666 bytes, lines 16796-16831
16796$1]);
16797      /* Allowed attribute names */
16798      let ALLOWED_ATTR = null;
16799      const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
16800      /*
16801       * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
16802       * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
16803       * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
16804       * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
16805       */
16806      let CUSTOM_ELEMENT_HANDLING = Object.seal(create$1(null, {
16807        tagNameCheck: {
16808          writable: true,
16809          configurable: false,
16810          enumerable: true,
16811          value: null
16812        },
16813        attributeNameCheck: {
16814          writable: true,
16815          configurable: false,
16816          enumerable: true,
16817          value: null
16818        },
16819        allowCustomizedBuiltInElements: {
16820          writable: true,
16821          configurable: false,
16822          enumerable: true,
16823          value: false
16824        }
16825      }));
16826      /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
16827      let FORBID_TAGS = null;
16828      /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
16829      let FORBID_ATTR = null;
16830      /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
16831      const EXTRA_ELEMENT_HANDLING = Object.seal(create$1
vendor: 11,859 bytes, lines 16831-17032
16831(null, {
16832        tagCheck: {
16833          writable: true,
16834          configurable: false,
16835          enumerable: true,
16836          value: null
16837        },
16838        attributeCheck: {
16839          writable: true,
16840          configurable: false,
16841          enumerable: true,
16842          value: null
16843        }
16844      }));
16845      /* Decide if ARIA attributes are okay */
16846      let ALLOW_ARIA_ATTR = true;
16847      /* Decide if custom data attributes are okay */
16848      let ALLOW_DATA_ATTR = true;
16849      /* Decide if unknown protocols are okay */
16850      let ALLOW_UNKNOWN_PROTOCOLS = false;
16851      /* Decide if self-closing tags in attributes are allowed.
16852       * Usually removed due to a mXSS issue in jQuery 3.0 */
16853      let ALLOW_SELF_CLOSE_IN_ATTR = true;
16854      /* Output should be safe for common template engines.
16855       * This means, DOMPurify removes data attributes, mustaches and ERB
16856       */
16857      let SAFE_FOR_TEMPLATES = false;
16858      /* Output should be safe even for XML used within HTML and alike.
16859       * This means, DOMPurify removes comments when containing risky content.
16860       */
16861      let SAFE_FOR_XML = true;
16862      /* Decide if document with <html>... should be returned */
16863      let WHOLE_DOCUMENT = false;
16864      /* Track whether config is already set on this instance of DOMPurify. */
16865      let SET_CONFIG = false;
16866      /* Pristine allowlist bindings captured at setConfig() time. On the
16867       * persistent-config path sanitize() restores the sets from these before
16868       * the per-walk hook clone-guard, so a hook's in-call widening cannot
16869       * carry across calls. Null until setConfig() is called; reset by
16870       * clearConfig(). */
16871      let SET_CONFIG_ALLOWED_TAGS = null;
16872      let SET_CONFIG_ALLOWED_ATTR = null;
16873      /* Decide if all elements (e.g. style, script) must be children of
16874       * document.body. By default, browsers might move them to document.head */
16875      let FORCE_BODY = false;
16876      /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
16877       * string (or a TrustedHTML object if Trusted Types are supported).
16878       * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
16879       */
16880      let RETURN_DOM = false;
16881      /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
16882       * string  (or a TrustedHTML object if Trusted Types are supported) */
16883      let RETURN_DOM_FRAGMENT = false;
16884      /* Try to return a Trusted Type object instead of a string, return a string in
16885       * case Trusted Types are not supported  */
16886      let RETURN_TRUSTED_TYPE = false;
16887      /* Output should be free from DOM clobbering attacks?
16888       * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
16889       */
16890      let SANITIZE_DOM = true;
16891      /* Achieve full DOM Clobbering protection by isolating the namespace of named
16892       * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
16893       *
16894       * HTML/DOM spec rules that enable DOM Clobbering:
16895       *   - Named Access on Window (§7.3.3)
16896       *   - DOM Tree Accessors (§3.1.5)
16897       *   - Form Element Parent-Child Relations (§4.10.3)
16898       *   - Iframe srcdoc / Nested WindowProxies (§4.8.5)
16899       *   - HTMLCollection (§4.2.10.2)
16900       *
16901       * Namespace isolation is implemented by prefixing `id` and `name` attributes
16902       * with a constant string, i.e., `user-content-`
16903       */
16904      let SANITIZE_NAMED_PROPS = false;
16905      const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
16906      /* Keep element content when removing element? */
16907      let KEEP_CONTENT = true;
16908      /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
16909       * of importing it into a new Document and returning a sanitized copy */
16910      let IN_PLACE = false;
16911      /* Allow usage of profiles like html, svg and mathMl */
16912      let USE_PROFILES = {};
16913      /* Tags to ignore content of when KEEP_CONTENT is true */
16914      let FORBID_CONTENTS = null;
16915      const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script',
16916      // <selectedcontent> mirrors the selected <option>'s subtree, cloned by
16917      // the UA (customizable <select>) — including any on* handlers — and the
16918      // engine re-mirrors synchronously whenever a removal changes which
16919      // option/selectedcontent is current, even inside DOMPurify's inert
16920      // DOMParser document. Hoisting its children on removal re-inserts a fresh
16921      // mirror target ahead of the walk, which the engine refills, looping
16922      // forever (DoS) and amplifying output. Dropping its content on removal
16923      // (rather than hoisting) breaks that cascade; the content is a duplicate
16924      // of the option, which is sanitized on its own. See campaign-3 F1/F6.
16925      'selectedcontent', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
16926      /* Tags that are safe for data: URIs */
16927      let DATA_URI_TAGS = null;
16928      const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
16929      /* Attributes safe for values like "javascript:" */
16930      let URI_SAFE_ATTRIBUTES = null;
16931      const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
16932      const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
16933      const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
16934      const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
16935      /* Document namespace */
16936      let NAMESPACE = HTML_NAMESPACE;
16937      let IS_EMPTY_INPUT = false;
16938      /* Allowed XHTML+XML namespaces */
16939      let ALLOWED_NAMESPACES = null;
16940      const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
16941      const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(['mi', 'mo', 'mn', 'ms', 'mtext']);
16942      let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
16943      const DEFAULT_HTML_INTEGRATION_POINTS = freeze(['annotation-xml']);
16944      let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
16945      // Certain elements are allowed in both SVG and HTML
16946      // namespace. We need to specify them explicitly
16947      // so that they don't get erroneously deleted from
16948      // HTML namespace.
16949      const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
16950      /* Parsing of strict XHTML documents */
16951      let PARSER_MEDIA_TYPE = null;
16952      const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
16953      const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
16954      let transformCaseFunc = null;
16955      /* Keep a reference to config to pass to hooks */
16956      let CONFIG = null;
16957      /* Ideally, do not touch anything below this line */
16958      /* ______________________________________________ */
16959      const formElement = document.createElement('form');
16960      const isRegexOrFunction = function isRegexOrFunction(testValue) {
16961        return testValue instanceof RegExp || testValue instanceof Function;
16962      };
16963      /**
16964       * _parseConfig
16965       *
16966       * @param cfg optional config literal
16967       */
16968      // eslint-disable-next-line complexity
16969      const _parseConfig = function _parseConfig() {
16970        let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16971        if (CONFIG && CONFIG === cfg) {
16972          return;
16973        }
16974        /* Shield configuration object from tampering */
16975        if (!cfg || typeof cfg !== 'object') {
16976          cfg = {};
16977        }
16978        /* Shield configuration object from prototype pollution */
16979        cfg = clone(cfg);
16980        PARSER_MEDIA_TYPE =
16981        // eslint-disable-next-line unicorn/prefer-includes
16982        SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
16983        // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
16984        transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
16985        /* Set configuration parameters */
16986        ALLOWED_TAGS = _resolveSetOption(cfg, 'ALLOWED_TAGS', DEFAULT_ALLOWED_TAGS, {
16987          transform: transformCaseFunc
16988        });
16989        ALLOWED_ATTR = _resolveSetOption(cfg, 'ALLOWED_ATTR', DEFAULT_ALLOWED_ATTR, {
16990          transform: transformCaseFunc
16991        });
16992        ALLOWED_NAMESPACES = _resolveSetOption(cfg, 'ALLOWED_NAMESPACES', DEFAULT_ALLOWED_NAMESPACES, {
16993          transform: stringToString
16994        });
16995        URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, 'ADD_URI_SAFE_ATTR', DEFAULT_URI_SAFE_ATTRIBUTES, {
16996          transform: transformCaseFunc,
16997          base: DEFAULT_URI_SAFE_ATTRIBUTES
16998        });
16999        DATA_URI_TAGS = _resolveSetOption(cfg, 'ADD_DATA_URI_TAGS', DEFAULT_DATA_URI_TAGS, {
17000          transform: transformCaseFunc,
17001          base: DEFAULT_DATA_URI_TAGS
17002        });
17003        FORBID_CONTENTS = _resolveSetOption(cfg, 'FORBID_CONTENTS', DEFAULT_FORBID_CONTENTS, {
17004          transform: transformCaseFunc
17005        });
17006        FORBID_TAGS = _resolveSetOption(cfg, 'FORBID_TAGS', clone({}), {
17007          transform: transformCaseFunc
17008        });
17009        FORBID_ATTR = _resolveSetOption(cfg, 'FORBID_ATTR', clone({}), {
17010          transform: transformCaseFunc
17011        });
17012        USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
17013        ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
17014        ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
17015        ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
17016        ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
17017        SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
17018        SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
17019        WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
17020        RETURN_DOM = cfg.RETURN_DOM || false; // Default false
17021        RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
17022        RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
17023        FORCE_BODY = cfg.FORCE_BODY || false; // Default false
17024        SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
17025        SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
17026        KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
17027        IN_PLACE = cfg.IN_PLACE || false; // Default false
17028        IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
17029        NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
17030        MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS); // Default built-in map
17031        HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS); // Default built-in map
17032        const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create$
170321(null);
17033        CUSTOM_ELEMENT_HANDLING = create$1(null);
17034        if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
17035          CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
17036        }
17037        if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
17038          CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
17039        }
17040        if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
17041          CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
17042        }
17043        seal(CUSTOM_ELEMENT_HANDLING);
17044        if (SAFE_FOR_TEMPLATES) {
17045          ALLOW_DATA_ATTR = false;
17046        }
17047        if (RETURN_DOM_FRAGMENT) {
17048          RETURN_DOM = true;
17049        }
17050        /* Parse profile info */
17051        if (USE_PROFILES) {
17052          ALLOWED_TAGS = addToSet({}, text$1);
17053          ALLOWED_ATTR = create$1
vendor: 82,657 bytes, lines 17053-18674
17053(null);
17054          if (USE_PROFILES.html === true) {
17055            addToSet(ALLOWED_TAGS, html$1);
17056            addToSet(ALLOWED_ATTR, html);
17057          }
17058          if (USE_PROFILES.svg === true) {
17059            addToSet(ALLOWED_TAGS, svg$1);
17060            addToSet(ALLOWED_ATTR, svg);
17061            addToSet(ALLOWED_ATTR, xml);
17062          }
17063          if (USE_PROFILES.svgFilters === true) {
17064            addToSet(ALLOWED_TAGS, svgFilters);
17065            addToSet(ALLOWED_ATTR, svg);
17066            addToSet(ALLOWED_ATTR, xml);
17067          }
17068          if (USE_PROFILES.mathMl === true) {
17069            addToSet(ALLOWED_TAGS, mathMl$1);
17070            addToSet(ALLOWED_ATTR, mathMl);
17071            addToSet(ALLOWED_ATTR, xml);
17072          }
17073        }
17074        /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
17075         * leaking across calls when switching from function to array config */
17076        EXTRA_ELEMENT_HANDLING.tagCheck = null;
17077        EXTRA_ELEMENT_HANDLING.attributeCheck = null;
17078        /* Merge configuration parameters */
17079        if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
17080          if (typeof cfg.ADD_TAGS === 'function') {
17081            EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
17082          } else if (arrayIsArray(cfg.ADD_TAGS)) {
17083            if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
17084              ALLOWED_TAGS = clone(ALLOWED_TAGS);
17085            }
17086            addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
17087          }
17088        }
17089        if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
17090          if (typeof cfg.ADD_ATTR === 'function') {
17091            EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
17092          } else if (arrayIsArray(cfg.ADD_ATTR)) {
17093            if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
17094              ALLOWED_ATTR = clone(ALLOWED_ATTR);
17095            }
17096            addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
17097          }
17098        }
17099        if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
17100          addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
17101        }
17102        if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
17103          if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
17104            FORBID_CONTENTS = clone(FORBID_CONTENTS);
17105          }
17106          addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
17107        }
17108        if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
17109          if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
17110            FORBID_CONTENTS = clone(FORBID_CONTENTS);
17111          }
17112          addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
17113        }
17114        /* Add #text in case KEEP_CONTENT is set to true */
17115        if (KEEP_CONTENT) {
17116          ALLOWED_TAGS['#text'] = true;
17117        }
17118        /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
17119        if (WHOLE_DOCUMENT) {
17120          addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
17121        }
17122        /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
17123        if (ALLOWED_TAGS.table) {
17124          addToSet(ALLOWED_TAGS, ['tbody']);
17125          delete FORBID_TAGS.tbody;
17126        }
17127        // Re-derive the active Trusted Types policy from this configuration on
17128        // every parse. The active policy must never be sticky closure state that
17129        // outlives the config that set it: a caller-supplied policy left in place
17130        // after `clearConfig()` — or after a later call that supplied none, or
17131        // `TRUSTED_TYPES_POLICY: null` — could sign a subsequent "default"
17132        // `RETURN_TRUSTED_TYPE` result with a foreign, possibly unsafe policy.
17133        // See GHSA-vxr8-fq34-vvx9.
17134        if (cfg.TRUSTED_TYPES_POLICY) {
17135          if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
17136            throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
17137          }
17138          if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
17139            throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
17140          }
17141          // A caller-supplied policy applies to this configuration only.
17142          const previousTrustedTypesPolicy = trustedTypesPolicy;
17143          trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
17144          // Sign local variables required by `sanitize`. If the supplied policy's
17145          // `createHTML` is circular (i.e. it calls `DOMPurify.sanitize`), this
17146          // throws via the re-entrancy guard. Restore the previous policy first so
17147          // the instance is not left in a poisoned state. See #1422.
17148          try {
17149            emptyHTML = _createTrustedHTML('');
17150          } catch (error) {
17151            trustedTypesPolicy = previousTrustedTypesPolicy;
17152            throw error;
17153          }
17154        } else if (cfg.TRUSTED_TYPES_POLICY === null) {
17155          // Explicit opt-out for this call: perform no Trusted Types signing and
17156          // create nothing (so a strict `trusted-types` CSP that disallows a
17157          // `dompurify` policy can still call `sanitize` from inside its own
17158          // policy — see #1422). Resetting to `undefined` rather than a sticky
17159          // `null` also drops any previously retained caller policy, so it cannot
17160          // resurface on a later call, while still allowing the next config-less
17161          // call to restore the internal default policy. See GHSA-vxr8-fq34-vvx9.
17162          trustedTypesPolicy = undefined;
17163          emptyHTML = '';
17164        } else {
17165          // No policy supplied: keep the currently active policy if one is set — a
17166          // previously supplied policy is intentionally sticky across config-less
17167          // calls — otherwise fall back to the instance's own internal policy,
17168          // created at most once. (A policy supplied for a *single* call still
17169          // lingers by design; what must not linger is a policy whose configuration
17170          // has been torn down via `clearConfig()`, which restores the default.)
17171          if (trustedTypesPolicy === undefined) {
17172            trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
17173          }
17174          // Sign internal variables only when a policy is active. A falsy policy
17175          // (Trusted Types unsupported, creation failed, or an explicit opt-out)
17176          // leaves `emptyHTML` as a plain string, so we never call `.createHTML` on
17177          // a non-policy and throw. See #1422.
17178          if (trustedTypesPolicy && typeof emptyHTML === 'string') {
17179            emptyHTML = _createTrustedHTML('');
17180          }
17181        }
17182        // Prevent further manipulation of configuration.
17183        // Not available in IE8, Safari 5, etc.
17184        if (freeze) {
17185          freeze(cfg);
17186        }
17187        CONFIG = cfg;
17188      };
17189      /* Keep track of all possible SVG and MathML tags
17190       * so that we can perform the namespace checks
17191       * correctly. */
17192      const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
17193      const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
17194      /**
17195       * Namespace rules for an element in the SVG namespace.
17196       *
17197       * @param tagName the element's lowercase tag name
17198       * @param parent the (possibly simulated) parent node
17199       * @param parentTagName the parent's lowercase tag name
17200       * @returns true if a spec-compliant parser could produce this element
17201       */
17202      const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) {
17203        // The only way to switch from HTML namespace to SVG
17204        // is via <svg>. If it happens via any other tag, then
17205        // it should be killed.
17206        if (parent.namespaceURI === HTML_NAMESPACE) {
17207          return tagName === 'svg';
17208        }
17209        // The only way to switch from MathML to SVG is via <svg>
17210        // if the parent is either <annotation-xml> or a MathML
17211        // text integration point.
17212        if (parent.namespaceURI === MATHML_NAMESPACE) {
17213          return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
17214        }
17215        // We only allow elements that are defined in SVG
17216        // spec. All others are disallowed in SVG namespace.
17217        return Boolean(ALL_SVG_TAGS[tagName]);
17218      };
17219      /**
17220       * Namespace rules for an element in the MathML namespace.
17221       *
17222       * @param tagName the element's lowercase tag name
17223       * @param parent the (possibly simulated) parent node
17224       * @param parentTagName the parent's lowercase tag name
17225       * @returns true if a spec-compliant parser could produce this element
17226       */
17227      const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) {
17228        // The only way to switch from HTML namespace to MathML
17229        // is via <math>. If it happens via any other tag, then
17230        // it should be killed.
17231        if (parent.namespaceURI === HTML_NAMESPACE) {
17232          return tagName === 'math';
17233        }
17234        // The only way to switch from SVG to MathML is via
17235        // <math> and HTML integration points
17236        if (parent.namespaceURI === SVG_NAMESPACE) {
17237          return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
17238        }
17239        // We only allow elements that are defined in MathML
17240        // spec. All others are disallowed in MathML namespace.
17241        return Boolean(ALL_MATHML_TAGS[tagName]);
17242      };
17243      /**
17244       * Namespace rules for an element in the HTML namespace.
17245       *
17246       * @param tagName the element's lowercase tag name
17247       * @param parent the (possibly simulated) parent node
17248       * @param parentTagName the parent's lowercase tag name
17249       * @returns true if a spec-compliant parser could produce this element
17250       */
17251      const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) {
17252        // The only way to switch from SVG to HTML is via
17253        // HTML integration points, and from MathML to HTML
17254        // is via MathML text integration points
17255        if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
17256          return false;
17257        }
17258        if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
17259          return false;
17260        }
17261        // We disallow tags that are specific for MathML
17262        // or SVG and should never appear in HTML namespace
17263        return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
17264      };
17265      /**
17266       * @param element a DOM element whose namespace is being checked
17267       * @returns Return false if the element has a
17268       *  namespace that a spec-compliant parser would never
17269       *  return. Return true otherwise.
17270       */
17271      const _checkValidNamespace = function _checkValidNamespace(element) {
17272        let parent = getParentNode(element);
17273        // In JSDOM, if we're inside shadow DOM, then parentNode
17274        // can be null. We just simulate parent in this case.
17275        if (!parent || !parent.tagName) {
17276          parent = {
17277            namespaceURI: NAMESPACE,
17278            tagName: 'template'
17279          };
17280        }
17281        const tagName = stringToLowerCase(element.tagName);
17282        const parentTagName = stringToLowerCase(parent.tagName);
17283        if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
17284          return false;
17285        }
17286        if (element.namespaceURI === SVG_NAMESPACE) {
17287          return _checkSvgNamespace(tagName, parent, parentTagName);
17288        }
17289        if (element.namespaceURI === MATHML_NAMESPACE) {
17290          return _checkMathMlNamespace(tagName, parent, parentTagName);
17291        }
17292        if (element.namespaceURI === HTML_NAMESPACE) {
17293          return _checkHtmlNamespace(tagName, parent, parentTagName);
17294        }
17295        // For XHTML and XML documents that support custom namespaces
17296        if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
17297          return true;
17298        }
17299        // The code should never reach this place (this means
17300        // that the element somehow got namespace that is not
17301        // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
17302        // Return false just in case.
17303        return false;
17304      };
17305      /**
17306       * _forceRemove
17307       *
17308       * @param node a DOM node
17309       */
17310      const _forceRemove = function _forceRemove(node) {
17311        arrayPush(DOMPurify.removed, {
17312          element: node
17313        });
17314        try {
17315          // eslint-disable-next-line unicorn/prefer-dom-node-remove
17316          getParentNode(node).removeChild(node);
17317        } catch (_) {
17318          /* The normal detach failed — this is reached for a parentless node
17319             (getParentNode() is null, so .removeChild throws). Element.prototype
17320             .remove() is itself a spec no-op on a parentless node, so a recorded
17321             "removal" would otherwise hand the caller back an intact,
17322             payload-bearing node (e.g. a detached IN_PLACE root the mXSS canary or
17323             the style-with-element-child rule decided to kill). Fail closed by
17324             throwing — exactly as a clobbered root does at the IN_PLACE entry —
17325             rather than trying to "neutralize" the node via its own methods.
17326             Neutralizing would mean calling getAttributeNames()/removeAttribute()
17327             on the node, both of which a <form> root can clobber via a named child
17328             (and _isClobbered does not even probe getAttributeNames), so the
17329             neutralize step could itself be silently defeated, leaving the payload
17330             intact. A throw touches only the cached, clobber-safe remove() and
17331             getParentNode(). Generalizes GHSA-r47g-fvhr-h676 (clobbered-form root)
17332             to every root-kill reason. REPORT-3.
17333                    This lives inside the catch, so it never fires for a normally-removed
17334             in-tree node: those have a parent, removeChild() succeeds, and the
17335             catch is not entered. Only a kept (parentless) root reaches here. */
17336          remove(node);
17337          if (!getParentNode(node)) {
17338            throw typeErrorCreate('a node selected for removal could not be detached from its tree ' + 'and cannot be safely returned; refusing to sanitize in place');
17339          }
17340        }
17341      };
17342      /**
17343       * _neutralizeRoot
17344       *
17345       * Fail-closed teardown of an in-place root after the sanitize walk aborts
17346       * (campaign-3 F2). An internal throw mid-walk — e.g. a page-registered
17347       * custom element's reaction detaches a node so `_forceRemove`'s deliberate
17348       * parentless guard throws, or any other re-entrant engine mutation — would
17349       * otherwise leave the caller's *live* tree half-sanitized, with everything
17350       * after the abort point still carrying its handlers. There is no safe way
17351       * to resume the walk (the tree mutated under us), so we strip the root bare:
17352       * remove every child and every attribute, then let the caller's catch see
17353       * the original error. Clobber-safe (cached `remove`/`childNodes`/`attributes`
17354       * getters; the root was already clobber-pre-flighted at the IN_PLACE entry).
17355       *
17356       * @param root the in-place root to empty
17357       */
17358      const _neutralizeRoot = function _neutralizeRoot(root) {
17359        /* Strip every disallowed attribute (on* handlers included) off the whole
17360           subtree BEFORE detaching anything. Detaching first would hand back
17361           handler-bearing originals (e.g. an already-loading `<img onerror>`)
17362           whose queued resource event still fires in page scope after we throw.
17363           Clobber-safe reads; a doomed clobbered node's own attributes are
17364           irrelevant while its non-clobbered descendants are reached and scrubbed. */
17365        _neutralizeSubtree(root);
17366        const childNodes = getChildNodes(root);
17367        if (childNodes) {
17368          const snapshot = [];
17369          arrayForEach(childNodes, child => {
17370            arrayPush(snapshot, child);
17371          });
17372          arrayForEach(snapshot, child => {
17373            try {
17374              remove(child);
17375            } catch (_) {
17376              /* Best-effort teardown; a still-attached child is handled below */
17377            }
17378          });
17379        }
17380        const attributes = getAttributes(root);
17381        if (attributes) {
17382          for (let i = attributes.length - 1; i >= 0; --i) {
17383            const attribute = attributes[i];
17384            const name = attribute && attribute.name;
17385            if (typeof name === 'string') {
17386              try {
17387                root.removeAttribute(name);
17388              } catch (_) {
17389                /* Clobbered removeAttribute — ignore (fail-closed best effort) */
17390              }
17391            }
17392          }
17393        }
17394      };
17395      /**
17396       * _removeAttribute
17397       *
17398       * @param name an Attribute name
17399       * @param element a DOM node
17400       */
17401      const _removeAttribute = function _removeAttribute(name, element) {
17402        try {
17403          arrayPush(DOMPurify.removed, {
17404            attribute: element.getAttributeNode(name),
17405            from: element
17406          });
17407        } catch (_) {
17408          arrayPush(DOMPurify.removed, {
17409            attribute: null,
17410            from: element
17411          });
17412        }
17413        element.removeAttribute(name);
17414        // We void attribute values for unremovable "is" attributes
17415        if (name === 'is') {
17416          if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
17417            try {
17418              _forceRemove(element);
17419            } catch (_) {}
17420          } else {
17421            try {
17422              element.setAttribute(name, '');
17423            } catch (_) {}
17424          }
17425        }
17426      };
17427      /**
17428       * _stripDisallowedAttributes
17429       *
17430       * Removes every attribute the active configuration does not allow from a
17431       * single element, using the same allowlist as the main attribute pass (so
17432       * `on*` handlers go, but no `/^on/` blocklist is introduced). Used only to
17433       * neutralise nodes that are being discarded from an in-place tree.
17434       *
17435       * @param element the element to strip
17436       */
17437      const _stripDisallowedAttributes = function _stripDisallowedAttributes(element) {
17438        const attributes = getAttributes(element);
17439        if (!attributes) {
17440          return;
17441        }
17442        for (let i = attributes.length - 1; i >= 0; --i) {
17443          const attribute = attributes[i];
17444          const name = attribute && attribute.name;
17445          if (typeof name !== 'string' || ALLOWED_ATTR[transformCaseFunc(name)]) {
17446            continue;
17447          }
17448          try {
17449            element.removeAttribute(name);
17450          } catch (_) {
17451            /* Clobbered removeAttribute on a doomed node — ignore */
17452          }
17453        }
17454      };
17455      /**
17456       * _neutralizeSubtree
17457       *
17458       * Completes the audit-5 F1 fix across every removal path. The KEEP_CONTENT
17459       * move-hoist neutralises only disallowed-tag removals; clobber, mXSS-canary,
17460       * namespace, comment, processing-instruction and KEEP_CONTENT:false removals
17461       * all drop their subtree wholesale via `_forceRemove`. On the IN_PLACE path
17462       * those dropped nodes are detached from the caller's LIVE tree but a
17463       * handler-bearing original among them (an `<img onerror>`/`<video>` that was
17464       * loading) keeps its queued resource event, which fires in page scope after
17465       * sanitize returns. This walks a removed subtree and strips every attribute
17466       * the active configuration does not allow — so `on*` handlers are cancelled
17467       * through the SAME allowlist that governs kept nodes, not a separate `/^on/`
17468       * blocklist. Run synchronously before sanitize returns, i.e. before any
17469       * queued event can fire. Hook-free by design: these nodes leave the output,
17470       * so firing attribute hooks for them would be surprising. Clobber-safe reads;
17471       * a doomed clobbered node may shadow `removeAttribute` (its own attributes are
17472       * irrelevant — it is discarded — while its non-clobbered descendants, e.g.
17473       * the `<img>`, are reached and scrubbed).
17474       *
17475       * @param root the root of a removed subtree to neutralise
17476       */
17477      const _neutralizeSubtree = function _neutralizeSubtree(root) {
17478        const stack = [root];
17479        while (stack.length > 0) {
17480          const node = stack.pop();
17481          const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
17482          if (nodeType === NODE_TYPE.element) {
17483            _stripDisallowedAttributes(node);
17484          }
17485          const childNodes = getChildNodes(node);
17486          if (childNodes) {
17487            for (let i = childNodes.length - 1; i >= 0; --i) {
17488              stack.push(childNodes[i]);
17489            }
17490          }
17491        }
17492      };
17493      /**
17494       * _neutralizePatchLinkage
17495       *
17496       * IN_PLACE entry pre-pass (declarative-partial-updates / streaming
17497       * hardening, https://github.com/WICG/declarative-partial-updates).
17498       *
17499       * The main walk strips patch linkage (`for`/`patchsrc`) and removes range
17500       * markers (PIs / markup comments) node-by-node, in document order, AS it
17501       * reaches each node. On a live in-place root that leaves a window: from the
17502       * moment the root is connected until the walk arrives at a given node, that
17503       * node's linkage is live. A patch applied on connection/stream can fire as
17504       * a microtask during the walk and inject or teleport an unsanitized DOM
17505       * range into a region the iterator has already passed and will not revisit,
17506       * so the post-return "tree is sanitized" contract is violated. Sweep the
17507       * whole tree once up front and sever every linkage before the walk begins,
17508       * closing that window.
17509       *
17510       * This CANNOT undo a patch that already fired before sanitize ran — that is
17511       * the irreducible "do not IN_PLACE a live-connected attacker tree" caveat —
17512       * but it closes everything from sanitize-start onward. Gated on SAFE_FOR_XML
17513       * to group with the rest of the declarative-partial-updates handling and
17514       * stay overridable, consistent with the codebase.
17515       *
17516       * Clobber-safe traversal (cached childNodes getter); per-node try/catch so a
17517       * clobbered root cannot defeat the sweep of its non-clobbered descendants.
17518       *
17519       * NOTE (pending real-Chrome confirmation, see test/declarative-patch-probe
17520       * .html Q1): this mirrors the existing policy of keeping `for` on
17521       * <label>/<output>. If the shipping feature can drive a patch through a
17522       * surviving `for`-on-label/output + `id` pair, this pre-pass and the
17523       * attribute check at _isBasicCustomElement's caller must additionally drop
17524       * that pair on the IN_PLACE path. Left as-is until the taxonomy is verified.
17525       *
17526       * @param root the in-place root to sweep
17527       */
17528      const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
17529        if (!SAFE_FOR_XML) {
17530          return;
17531        }
17532        const stack = [root];
17533        while (stack.length > 0) {
17534          const node = stack.pop();
17535          const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
17536          /* Remove range markers (the target side of a patch linkage): every
17537             processing instruction, and any markup-bearing comment. */
17538          if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
17539            try {
17540              remove(node);
17541            } catch (_) {
17542              /* Best-effort */
17543            }
17544            continue;
17545          }
17546          /* Strip patch-source attributes (the source side) off elements. */
17547          if (nodeType === NODE_TYPE.element) {
17548            const element = node;
17549            const lcTag = transformCaseFunc(getNodeName ? getNodeName(node) : node.nodeName);
17550            try {
17551              if (element.hasAttribute && element.hasAttribute('patchsrc')) {
17552                element.removeAttribute('patchsrc');
17553              }
17554              if (element.hasAttribute && element.hasAttribute('for') && lcTag !== 'label' && lcTag !== 'output') {
17555                element.removeAttribute('for');
17556              }
17557            } catch (_) {
17558              /* Clobbered removeAttribute/hasAttribute on a doomed node — ignore */
17559            }
17560          }
17561          const childNodes = getChildNodes(node);
17562          if (childNodes) {
17563            for (let i = childNodes.length - 1; i >= 0; --i) {
17564              stack.push(childNodes[i]);
17565            }
17566          }
17567        }
17568      };
17569      /**
17570       * _initDocument
17571       *
17572       * @param dirty - a string of dirty markup
17573       * @return a DOM, filled with the dirty markup
17574       */
17575      const _initDocument = function _initDocument(dirty) {
17576        /* Create a HTML document */
17577        let doc = null;
17578        let leadingWhitespace = null;
17579        if (FORCE_BODY) {
17580          dirty = '<remove></remove>' + dirty;
17581        } else {
17582          /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
17583          const matches = stringMatch(dirty, /^[\r\n\t ]+/);
17584          leadingWhitespace = matches && matches[0];
17585        }
17586        if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
17587          // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
17588          dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
17589        }
17590        const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
17591        /*
17592         * Use the DOMParser API by default, fallback later if needs be
17593         * DOMParser not work for svg when has multiple root element.
17594         */
17595        if (NAMESPACE === HTML_NAMESPACE) {
17596          try {
17597            doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
17598          } catch (_) {}
17599        }
17600        /* Use createHTMLDocument in case DOMParser is not available */
17601        if (!doc || !doc.documentElement) {
17602          doc = implementation.createDocument(NAMESPACE, 'template', null);
17603          try {
17604            doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
17605          } catch (_) {
17606            // Syntax error if dirtyPayload is invalid xml
17607          }
17608        }
17609        const body = doc.body || doc.documentElement;
17610        if (dirty && leadingWhitespace) {
17611          body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
17612        }
17613        /* Work on whole document or just its body */
17614        if (NAMESPACE === HTML_NAMESPACE) {
17615          return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
17616        }
17617        return WHOLE_DOCUMENT ? doc.documentElement : body;
17618      };
17619      /**
17620       * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
17621       *
17622       * @param root The root element or node to start traversing on.
17623       * @return The created NodeIterator
17624       */
17625      const _createNodeIterator = function _createNodeIterator(root) {
17626        return createNodeIterator.call(root.ownerDocument || root, root,
17627        // eslint-disable-next-line no-bitwise
17628        NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
17629      };
17630      /**
17631       * Replace template expression syntax (mustache, ERB, template
17632       * literal) with a space; shared by all SAFE_FOR_TEMPLATES scrub
17633       * sites. Order matters: mustache, then ERB, then template literal.
17634       *
17635       * @param value the string to scrub
17636       * @returns the scrubbed string
17637       */
17638      const _stripTemplateExpressions = function _stripTemplateExpressions(value) {
17639        value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
17640        value = stringReplace(value, ERB_EXPR$1, ' ');
17641        value = stringReplace(value, TMPLIT_EXPR$1, ' ');
17642        return value;
17643      };
17644      /**
17645       * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
17646       * character data of an element subtree. Used as the final safety net for
17647       * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
17648       * which only form after text-node normalization (e.g. fragments split across
17649       * stripped elements) cannot survive into a template-evaluating framework.
17650       *
17651       * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
17652       * in place rather than round-tripping through innerHTML. This preserves
17653       * descendant node references (important for IN_PLACE callers), avoids a
17654       * serialize/reparse cycle, and reads literal character data — which means
17655       * `<%...%>` in text content matches the ERB regex against its real bytes
17656       * instead of the HTML-entity-escaped form innerHTML would produce.
17657       *
17658       * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
17659       * attributes is performed during the per-node `_sanitizeAttributes` pass.
17660       *
17661       * @param node The root element whose character data should be scrubbed.
17662       */
17663      const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
17664        var _node$querySelectorAl;
17665        node.normalize();
17666        const walker = createNodeIterator.call(node.ownerDocument || node, node,
17667        // eslint-disable-next-line no-bitwise
17668        NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
17669        let currentNode = walker.nextNode();
17670        while (currentNode) {
17671          currentNode.data = _stripTemplateExpressions(currentNode.data);
17672          currentNode = walker.nextNode();
17673        }
17674        // NodeIterator does not descend into <template>.content per the DOM spec,
17675        // so we must explicitly recurse into each template's content fragment,
17676        // mirroring the approach used by _sanitizeShadowDOM.
17677        const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, 'template');
17678        if (templates) {
17679          arrayForEach(templates, tmpl => {
17680            if (_isDocumentFragment(tmpl.content)) {
17681              _scrubTemplateExpressions2(tmpl.content);
17682            }
17683          });
17684        }
17685      };
17686      /**
17687       * _isClobbered
17688       *
17689       * Detect DOM-clobbering on HTMLFormElement nodes. Form is the only HTML
17690       * interface with [LegacyOverrideBuiltIns]; a descendant element with a
17691       * `name` attribute matching a prototype property shadows that property
17692       * on direct reads. We use this check at the IN_PLACE entry-point and
17693       * during attribute sanitization to refuse clobbered forms.
17694       *
17695       * @param element element to check for clobbering attacks
17696       * @return true if clobbered, false if safe
17697       */
17698      const _isClobbered = function _isClobbered(element) {
17699        // Realm-independent tag-name probe. If we can't determine the tag
17700        // name at all, we can't reason about clobbering — return false
17701        // (the caller's other defences still apply).
17702        const realTagName = getNodeName ? getNodeName(element) : null;
17703        if (typeof realTagName !== 'string') {
17704          return false;
17705        }
17706        if (transformCaseFunc(realTagName) !== 'form') {
17707          return false;
17708        }
17709        return typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' ||
17710        // Realm-safe NamedNodeMap detection: equality against the cached
17711        // prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
17712        // makes the direct read diverge from the cached read; a clean form
17713        // (same-realm OR foreign-realm) has both reads pointing at the same
17714        // canonical NamedNodeMap.
17715        element.attributes !== getAttributes(element) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function' ||
17716        // NodeType clobbering probe. Cached Node.prototype.nodeType getter
17717        // returns the integer 1 for any Element regardless of realm; direct
17718        // read on a clobbered form (e.g. <input name="nodeType">) returns
17719        // the named child element. Cheap addition — nodeType is read from
17720        // an internal slot, no serialization cost — and removes a residual
17721        // clobbering surface used by several mXSS / PI / comment branches
17722        // in _sanitizeElements that compare currentNode.nodeType directly.
17723        element.nodeType !== getNodeType(element) ||
17724        // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
17725        // "childNodes" shadows the prototype getter. Direct reads of
17726        // form.childNodes from a clobbered form return the named child
17727        // instead of the real NodeList, so any walk that reads it directly
17728        // skips the form's real children. Compare the direct read to the
17729        // cached Node.prototype getter — when the form's named-property
17730        // getter intercepts the read, the two values differ and we flag
17731        // the form. This catches every clobbering child type (input,
17732        // select, etc.) regardless of whether the named child happens to
17733        // carry a numeric .length, which a typeof-based probe would miss
17734        // (e.g. HTMLSelectElement.length is a defined unsigned-long).
17735        element.childNodes !== getChildNodes(element);
17736      };
17737      /**
17738       * Checks whether the given value is a DocumentFragment from any realm.
17739       *
17740       * The realm-independent replacement reads `nodeType` through the cached
17741       * Node.prototype getter and compares to the DOCUMENT_FRAGMENT_NODE
17742       * constant (11). nodeType is a numeric value resolved from the node's
17743       * internal slot, identical across realms for the same kind of node.
17744       *
17745       * @param value object to check
17746       * @return true if value is a DocumentFragment-shaped node from any realm
17747       */
17748      const _isDocumentFragment = function _isDocumentFragment(value) {
17749        if (!getNodeType || typeof value !== 'object' || value === null) {
17750          return false;
17751        }
17752        try {
17753          return getNodeType(value) === NODE_TYPE.documentFragment;
17754        } catch (_) {
17755          return false;
17756        }
17757      };
17758      /**
17759       * Checks whether the given object is a DOM node, including nodes that
17760       * originate from a different window/realm (e.g. an iframe's
17761       * contentDocument). The previous `value instanceof Node` check was
17762       * realm-bound: nodes from a different window failed it, causing
17763       * sanitize() to silently stringify them and reset IN_PLACE to false,
17764       * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
17765       *
17766       * @param value object to check whether it's a DOM node
17767       * @return true if value is a DOM node from any realm
17768       */
17769      const _isNode = function _isNode(value) {
17770        if (!getNodeType || typeof value !== 'object' || value === null) {
17771          return false;
17772        }
17773        try {
17774          return typeof getNodeType(value) === 'number';
17775        } catch (_) {
17776          return false;
17777        }
17778      };
17779      function _executeHooks(hooks, currentNode, data) {
17780        if (hooks.length === 0) {
17781          return;
17782        }
17783        arrayForEach(hooks, hook => {
17784          hook.call(DOMPurify, currentNode, data, CONFIG);
17785        });
17786      }
17787      /**
17788       * Structural-threat checks that condemn a node regardless of the
17789       * allowlists: mXSS via namespace confusion, risky CSS construction,
17790       * processing instructions, markup-bearing comments. Pure predicate;
17791       * the caller removes. Check order is load-bearing.
17792       *
17793       * @param currentNode the node to inspect
17794       * @param tagName the node's transformCaseFunc'd tag name
17795       * @return true if the node must be removed
17796       */
17797      const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
17798        /* Detect mXSS attempts abusing namespace confusion */
17799        if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
17800          return true;
17801        }
17802        /* Remove risky CSS construction leading to mXSS */
17803        if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
17804          return true;
17805        }
17806        /* Remove any occurrence of processing instructions */
17807        if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
17808          return true;
17809        }
17810        /* Remove any kind of possibly harmful comments */
17811        if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
17812          return true;
17813        }
17814        return false;
17815      };
17816      /**
17817       * Handle a node whose tag is forbidden or not allowlisted: keep
17818       * allowed custom elements (false return exits _sanitizeElements
17819       * early - the namespace and fallback-tag removal checks are
17820       * intentionally skipped for kept custom elements), else hoist
17821       * content per KEEP_CONTENT and remove.
17822       *
17823       * A kept custom element is the ONLY case in which this function
17824       * returns false, so the caller uses that return value to run the
17825       * afterSanitizeElements hook on the kept element and keep the
17826       * element-hook lifecycle consistent with normal allowlisted
17827       * elements (GHSA-c2j3-45gr-mqc4).
17828       *
17829       * @param currentNode the disallowed node
17830       * @param tagName the node's transformCaseFunc'd tag name
17831       * @return true if the node was removed, false if kept
17832       */
17833      const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName) {
17834        /* Check if we have a custom element to handle */
17835        if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
17836          if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
17837            return false;
17838          }
17839          if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
17840            return false;
17841          }
17842        }
17843        /* Keep content except for bad-listed elements.
17844             Use the cached prototype getters exclusively — the previous code
17845             had `|| currentNode.parentNode` / `|| currentNode.childNodes`
17846             fallbacks, but the cached getters always return the canonical
17847             value (or null for a real parent-less node), so the fallback
17848             path was dead in safe cases and a clobbering surface in unsafe
17849             ones. Falsy cached results stay falsy; the `if (childNodes &&
17850             parentNode)` check already gates correctly. */
17851        if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
17852          const parentNode = getParentNode(currentNode);
17853          const childNodes = getChildNodes(currentNode);
17854          if (childNodes && parentNode) {
17855            const childCount = childNodes.length;
17856            /* In-place: hoist the *original* children so the iterator visits
17857                 and sanitises them through the same allowlist pass as every other
17858                 node. The caller built the tree in the live document, so the
17859                 originals carry already-queued resource events (`<img onerror>`,
17860                 `<video>`/`<audio>` error, lazy/`onload`, …); cloning would leave
17861                 those originals detached but still armed, firing in page scope
17862                 while the returned tree looked clean. Moving is safe in-place: the
17863                 root is pre-validated as an allowed tag and so is never the node
17864                 being removed, which keeps `parentNode` inside the iterator root
17865                 and the relocated child inside the serialised tree.
17866                          Otherwise (string / DOM-copy paths): clone. The iterator is rooted
17867                 at — and the result serialised from — `body`, so a restrictive
17868                 ALLOWED_TAGS that removes `body` itself must leave its content in
17869                 place, which only cloning does; and those paths parse into an
17870                 inert document, so their discarded originals never had a queued
17871                 event to neutralise.
17872                          `childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
17873                 valid whether we move (drops the trailing entry) or clone (leaves
17874                 the list intact). */
17875            for (let i = childCount - 1; i >= 0; --i) {
17876              const hoisted = IN_PLACE ? childNodes[i] : cloneNode(childNodes[i], true);
17877              parentNode.insertBefore(hoisted, getNextSibling(currentNode));
17878            }
17879          }
17880        }
17881        _forceRemove(currentNode);
17882        return true;
17883      };
17884      /**
17885       * _sanitizeElements
17886       *
17887       * @protect nodeName
17888       * @protect textContent
17889       * @protect removeChild
17890       * @param currentNode to check for permission to exist
17891       * @return true if node was killed, false if left alive
17892       */
17893      // eslint-disable-next-line complexity
17894      const _sanitizeElements = function _sanitizeElements(currentNode, root) {
17895        /* Execute a hook if present */
17896        _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
17897        /* A hook may have detached the node — treat it as removed (see the
17898           detached-node comment after the uponSanitizeElement hook below). */
17899        if (currentNode !== root && getParentNode(currentNode) === null) {
17900          return true;
17901        }
17902        /* Check if element is clobbered or can clobber */
17903        if (_isClobbered(currentNode)) {
17904          _forceRemove(currentNode);
17905          return true;
17906        }
17907        /* Now let's check the element's type and name */
17908        const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
17909        /* Execute a hook if present */
17910        _executeHooks(hooks.uponSanitizeElement, currentNode, {
17911          tagName,
17912          allowedTags: ALLOWED_TAGS
17913        });
17914        /* A hook may have detached the node from the tree — a long-standing
17915           user pattern (issue #469; draw.io-style foreignObject filtering).
17916           Per the cached, unclobberable parentNode getter the node is
17917           genuinely out of the tree, so it can reach neither the serialized
17918           output nor an IN_PLACE live tree; treat it as removed and stop
17919           processing it. Without this guard, the unsafe-node / namespace
17920           checks below would call _forceRemove on a parentless node and hit
17921           the REPORT-3 fail-closed throw — which exists for nodes DOMPurify
17922           wants gone but *cannot* detach (clobbered / parentless roots), the
17923           opposite of a node that is already safely gone. The walk root is
17924           exempt: a detached IN_PLACE root is legitimate input and must still
17925           be fully sanitized, and a kill-decision on it must keep hitting the
17926           REPORT-3 throw. Nodes detached by hooks are the hook's
17927           responsibility: they are not recorded in DOMPurify.removed and are
17928           not neutralized by the post-walk IN_PLACE pass. */
17929        if (currentNode !== root && getParentNode(currentNode) === null) {
17930          return true;
17931        }
17932        /* Remove mXSS vectors, processing instructions and risky comments */
17933        if (_isUnsafeNode(currentNode, tagName)) {
17934          _forceRemove(currentNode);
17935          return true;
17936        }
17937        /* Remove element if anything forbids its presence */
17938        if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
17939          const removed = _sanitizeDisallowedNode(currentNode, tagName);
17940          /* A false return means the node is a custom element kept via
17941             CUSTOM_ELEMENT_HANDLING - the only keep path through
17942             _sanitizeDisallowedNode. Run afterSanitizeElements on it so the
17943             element-hook lifecycle matches normal allowlisted elements: a
17944             security policy applied in this hook (e.g. stripping an attribute
17945             from every surviving element) must not silently skip kept custom
17946             elements (GHSA-c2j3-45gr-mqc4). This mirrors the normal-element
17947             tail below - the hook runs, then the walker's subsequent
17948             _sanitizeAttributes pass sanitizes the element's attributes. The
17949             deliberately skipped namespace and fallback-tag removal checks stay
17950             skipped; they are removal decisions, not the hook contract. */
17951          if (removed === false) {
17952            _executeHooks(hooks.afterSanitizeElements, currentNode, null);
17953          }
17954          return removed;
17955        }
17956        /* Check whether element has a valid namespace.
17957           Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
17958           nodeType getter rather than `instanceof Element`, which is realm-
17959           bound and short-circuits to false for any node minted in a different
17960           realm — letting a foreign-realm element with a forbidden namespace
17961           slip past the namespace check entirely. */
17962        const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
17963        if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
17964          _forceRemove(currentNode);
17965          return true;
17966        }
17967        /* Make sure that older browsers don't get fallback-tag mXSS */
17968        if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
17969          _forceRemove(currentNode);
17970          return true;
17971        }
17972        /* Sanitize element content to be template-safe */
17973        if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
17974          /* Get the element's text content */
17975          const content = _stripTemplateExpressions(currentNode.textContent);
17976          if (currentNode.textContent !== content) {
17977            arrayPush(DOMPurify.removed, {
17978              element: currentNode.cloneNode()
17979            });
17980            currentNode.textContent = content;
17981          }
17982        }
17983        /* Execute a hook if present */
17984        _executeHooks(hooks.afterSanitizeElements, currentNode, null);
17985        return false;
17986      };
17987      /**
17988       * _isValidAttribute
17989       *
17990       * @param lcTag Lowercase tag name of containing element.
17991       * @param lcName Lowercase attribute name.
17992       * @param value Attribute value.
17993       * @return Returns true if `value` is valid, otherwise false.
17994       */
17995      // eslint-disable-next-line complexity
17996      const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
17997        /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
17998        if (FORBID_ATTR[lcName]) {
17999          return false;
18000        }
18001        /* Reject declarative-partial-updates patch-linkage attributes
18002           (https://github.com/WICG/declarative-partial-updates).
18003                Empirical note (Chrome 150, verified — see
18004           test/declarative-patch-probe-v3.html): expansion is NOT applied after
18005           sanitization. For the string path it fires during sanitize()'s own
18006           parse, so the walk sees and sanitizes the fully materialized expanded
18007           tree — teleports into MathML/SVG integration points included; a
18008           weaponized `<template for>`->`<img onerror>` comes back with the handler
18009           stripped. For the IN_PLACE path it fires on connection, before the walk.
18010           Either way DOMPurify is NOT blind to the patch.
18011                This removal is therefore defense-in-depth rather than the sole barrier:
18012           it prevents live linkage from surviving into the OUTPUT and re-expanding
18013           in the caller's context, and keeps behaviour deterministic if a future
18014           engine defers expansion. `for` is legitimate only on <label>/<output>;
18015           anywhere else (notably <template for>) it links the element to a patch
18016           target and teleports or removes an arbitrary DOM range by id/marker name.
18017           `patchsrc` fetches remote markup and is treated as a script-loading
18018           mechanism (CSP). Gated on SAFE_FOR_XML so the removal groups with the
18019           other structural-threat checks and stays overridable, consistent with
18020           the rest of the codebase. PI range markers are already removed by
18021           _isUnsafeNode. */
18022        if (SAFE_FOR_XML && lcName === 'patchsrc') {
18023          return false;
18024        }
18025        if (SAFE_FOR_XML && lcName === 'for' && lcTag !== 'label' && lcTag !== 'output') {
18026          return false;
18027        }
18028        /* Make sure attribute cannot clobber */
18029        if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
18030          return false;
18031        }
18032        const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
18033        /* Allow valid data-* attributes: At least one character after "-"
18034            (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
18035            XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
18036            We don't need to check the value; it's always URI safe. */
18037        if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted) {
18038          if (
18039          // First condition does a very basic check if a) it's basically a valid custom element tagname AND
18040          // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
18041          // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
18042          _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||
18043          // Alternative, second condition checks if it's an `is`-attribute, AND
18044          // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
18045          lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
18046            return false;
18047          }
18048          /* Check value is safe. First, is attr inert? If so, is safe */
18049        } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
18050          return false;
18051        } else ;
18052        return true;
18053      };
18054      /* Names the HTML spec reserves from valid-custom-element-name; these must
18055       * never be treated as basic custom elements even when a permissive
18056       * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
18057      const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
18058      /**
18059       * _isBasicCustomElement
18060       * checks if at least one dash is included in tagName, and it's not the first char
18061       * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
18062       *
18063       * @param tagName name of the tag of the node to sanitize
18064       * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
18065       */
18066      const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
18067        return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
18068      };
18069      /**
18070       * Wrap an attribute value in the matching Trusted Types object when
18071       * the active policy requires it. Namespaced attributes pass through
18072       * unchanged (no TT support yet, see
18073       * https://bugs.chromium.org/p/chromium/issues/detail?id=1305293).
18074       *
18075       * @param lcTag lowercase tag name of the containing element
18076       * @param lcName lowercase attribute name
18077       * @param namespaceURI the attribute's namespace, if any
18078       * @param value the attribute value to wrap
18079       * @return the value, wrapped when Trusted Types demand it
18080       */
18081      const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value) {
18082        if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function' && !namespaceURI) {
18083          switch (trustedTypes.getAttributeType(lcTag, lcName)) {
18084            case 'TrustedHTML':
18085              {
18086                return _createTrustedHTML(value);
18087              }
18088            case 'TrustedScriptURL':
18089              {
18090                return _createTrustedScriptURL(value);
18091              }
18092          }
18093        }
18094        return value;
18095      };
18096      /**
18097       * Write a modified attribute value back onto the element. On
18098       * success, re-probe for clobbering introduced by the new value and
18099       * remove the element when found; otherwise pop the removal entry
18100       * recorded by the earlier _removeAttribute (long-standing pairing
18101       * with the SANITIZE_NAMED_PROPS path - do not "fix" casually). On
18102       * failure, remove the attribute instead.
18103       *
18104       * @param currentNode the element carrying the attribute
18105       * @param name the attribute name as present on the element
18106       * @param namespaceURI the attribute's namespace, if any
18107       * @param value the new attribute value
18108       */
18109      const _setAttributeValue = function _setAttributeValue(currentNode, name, namespaceURI, value) {
18110        try {
18111          if (namespaceURI) {
18112            currentNode.setAttributeNS(namespaceURI, name, value);
18113          } else {
18114            /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
18115            currentNode.setAttribute(name, value);
18116          }
18117          if (_isClobbered(currentNode)) {
18118            _forceRemove(currentNode);
18119          } else {
18120            arrayPop(DOMPurify.removed);
18121          }
18122        } catch (_) {
18123          _removeAttribute(name, currentNode);
18124        }
18125      };
18126      /**
18127       * _sanitizeAttributes
18128       *
18129       * @protect attributes
18130       * @protect nodeName
18131       * @protect removeAttribute
18132       * @protect setAttribute
18133       *
18134       * @param currentNode to sanitize
18135       */
18136      const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
18137        /* Execute a hook if present */
18138        _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
18139        const attributes = currentNode.attributes;
18140        /* Check if we have attributes; if not we might have a text node */
18141        if (!attributes || _isClobbered(currentNode)) {
18142          return;
18143        }
18144        const hookEvent = {
18145          attrName: '',
18146          attrValue: '',
18147          keepAttr: true,
18148          allowedAttributes: ALLOWED_ATTR,
18149          forceKeepAttr: undefined
18150        };
18151        let l = attributes.length;
18152        const lcTag = transformCaseFunc(currentNode.nodeName);
18153        /* Go backwards over all attributes; safely remove bad ones */
18154        while (l--) {
18155          const attr = attributes[l];
18156          const name = attr.name,
18157            namespaceURI = attr.namespaceURI,
18158            attrValue = attr.value;
18159          const lcName = transformCaseFunc(name);
18160          const initValue = attrValue;
18161          let value = name === 'value' ? initValue : stringTrim(initValue);
18162          /* Execute a hook if present */
18163          hookEvent.attrName = lcName;
18164          hookEvent.attrValue = value;
18165          hookEvent.keepAttr = true;
18166          hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
18167          _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
18168          value = hookEvent.attrValue;
18169          /* Full DOM Clobbering protection via namespace isolation,
18170           * Prefix id and name attributes with `user-content-`
18171           */
18172          if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
18173            // Remove the attribute with this value
18174            _removeAttribute(name, currentNode);
18175            // Prefix the value and later re-create the attribute with the sanitized value
18176            value = SANITIZE_NAMED_PROPS_PREFIX + value;
18177          }
18178          // Else: already prefixed, leave the attribute alone — the prefix is
18179          // itself the clobbering protection, and re-applying it is incorrect.
18180          /* Work around a security issue with comments inside attributes */
18181          if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
18182            _removeAttribute(name, currentNode);
18183            continue;
18184          }
18185          /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
18186          if (lcName === 'attributename' && stringMatch(value, 'href')) {
18187            _removeAttribute(name, currentNode);
18188            continue;
18189          }
18190          /* Did the hooks force-keep the attribute? */
18191          if (hookEvent.forceKeepAttr) {
18192            continue;
18193          }
18194          /* Did the hooks approve of the attribute? */
18195          if (!hookEvent.keepAttr) {
18196            _removeAttribute(name, currentNode);
18197            continue;
18198          }
18199          /* Work around a security issue in jQuery 3.0 */
18200          if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
18201            _removeAttribute(name, currentNode);
18202            continue;
18203          }
18204          /* Sanitize attribute content to be template-safe */
18205          if (SAFE_FOR_TEMPLATES) {
18206            value = _stripTemplateExpressions(value);
18207          }
18208          /* Is `value` valid for this attribute? */
18209          if (!_isValidAttribute(lcTag, lcName, value)) {
18210            _removeAttribute(name, currentNode);
18211            continue;
18212          }
18213          /* Handle attributes that require Trusted Types */
18214          value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
18215          /* Handle invalid data-* attribute set by try-catching it */
18216          if (value !== initValue) {
18217            _setAttributeValue(currentNode, name, namespaceURI, value);
18218          }
18219        }
18220        /* Execute a hook if present */
18221        _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
18222      };
18223      /**
18224       * _sanitizeShadowDOM
18225       *
18226       * @param fragment to iterate over recursively
18227       */
18228      const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
18229        let shadowNode = null;
18230        const shadowIterator = _createNodeIterator(fragment);
18231        /* Execute a hook if present */
18232        _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
18233        while (shadowNode = shadowIterator.nextNode()) {
18234          /* Execute a hook if present */
18235          _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
18236          /* Sanitize tags and elements */
18237          _sanitizeElements(shadowNode, fragment);
18238          /* Check attributes next */
18239          _sanitizeAttributes(shadowNode);
18240          /* Deep shadow DOM detected.
18241             Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType against the
18242             DOCUMENT_FRAGMENT_NODE constant rather than instanceof, so we
18243             recurse into <template>.content from foreign realms too. */
18244          if (_isDocumentFragment(shadowNode.content)) {
18245            _sanitizeShadowDOM2(shadowNode.content);
18246          }
18247          /* An element iterated here may itself host an attached
18248             shadow root. The default NodeIterator does not enter shadow
18249             trees, so a shadow root nested inside template.content was
18250             previously reached by no walk at all (the pre-pass at
18251             _sanitizeAttachedShadowRoots descends via childNodes, which
18252             doesn't enter template.content; the template-content recursion
18253             above iterates the content but never inspected shadowRoot).
18254             Walk it explicitly. The nodeType guard avoids reading
18255             shadowRoot off text / comment / CDATA / PI nodes that the
18256             iterator also surfaces. */
18257          const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
18258          if (shadowNodeType === NODE_TYPE.element) {
18259            const innerSr = getShadowRoot(shadowNode);
18260            if (_isDocumentFragment(innerSr)) {
18261              _sanitizeAttachedShadowRoots(innerSr);
18262              _sanitizeShadowDOM2(innerSr);
18263            }
18264          }
18265        }
18266        /* Execute a hook if present */
18267        _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
18268      };
18269      /**
18270       * _sanitizeAttachedShadowRoots
18271       *
18272       * Walks `root` and feeds every attached shadow root we encounter into
18273       * the existing _sanitizeShadowDOM pipeline. The default node iterator
18274       * does not descend into shadow trees, so nodes inside an attached
18275       * shadow root would otherwise be skipped entirely.
18276       *
18277       * Two real input paths put attached shadow roots in front of us:
18278       *   1. IN_PLACE on a DOM node that already has shadow roots attached.
18279       *   2. DOM-node input where importNode(dirty, true) deep-clones the
18280       *      shadow root because it was created with `clonable: true`.
18281       *
18282       * This pass runs once, up front, so the main iteration loop (and the
18283       * existing _sanitizeShadowDOM template-content recursion) stay
18284       * untouched — string-input paths are not affected.
18285       *
18286       * @param root the subtree root to walk for attached shadow roots
18287       */
18288      const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots(root) {
18289        /* Iterative (explicit stack) rather than per-child recursion. DOM APIs
18290           impose no depth cap, so an attacker-shaped tree (JSON/CRDT/editor data
18291           built straight into the DOM — the IN_PLACE surface) deeper than the JS
18292           call-stack budget would otherwise overflow native recursion here and
18293           throw at the IN_PLACE entry pre-pass, before a single node is
18294           sanitized, leaving the caller's live tree untouched (fail-open). See
18295           campaign-3 F4. A heap stack keeps depth off the call stack.
18296                Each work item is either a node to descend into, or a deferred
18297           `_sanitizeShadowDOM` for an already-walked shadow root. The deferred
18298           form preserves the original post-order discipline: a shadow root's
18299           nested shadow roots are discovered before the outer shadow is
18300           sanitized (which may remove hosts). Pushes are in reverse of the
18301           desired processing order (LIFO): template content, then children, then
18302           the shadow-sanitize, then the shadow walk — so the order matches the
18303           previous recursion exactly. */
18304        const stack = [{
18305          node: root,
18306          shadow: null
18307        }];
18308        while (stack.length > 0) {
18309          const item = stack.pop();
18310          /* Deferred shadow-DOM sanitisation: runs after its subtree was walked. */
18311          if (item.shadow) {
18312            _sanitizeShadowDOM2(item.shadow);
18313            continue;
18314          }
18315          const node = item.node;
18316          const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
18317          const isElement = nodeType === NODE_TYPE.element;
18318          /* (pushed last → processed first) Children, snapshotted in reverse so
18319             the first child is processed first. Snapshotting matters because a
18320             hook may detach siblings mid-walk. */
18321          const childNodes = getChildNodes(node);
18322          if (childNodes) {
18323            for (let i = childNodes.length - 1; i >= 0; --i) {
18324              stack.push({
18325                node: childNodes[i],
18326                shadow: null
18327              });
18328            }
18329          }
18330          /* (pushed before children → processed after them, matching the old
18331             "template content last" order) When the node is a <template>,
18332             descend into its content. */
18333          if (isElement) {
18334            const rootName = getNodeName ? getNodeName(node) : null;
18335            if (typeof rootName === 'string' && transformCaseFunc(rootName) === 'template') {
18336              const content = node.content;
18337              if (_isDocumentFragment(content)) {
18338                stack.push({
18339                  node: content,
18340                  shadow: null
18341                });
18342              }
18343            }
18344          }
18345          /* Shadow root (processed first): walk its subtree, then sanitise it.
18346             Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
18347             rather than `instanceof DocumentFragment`, which is realm-bound and
18348             silently skipped foreign-realm shadow roots (e.g.
18349             iframe.contentDocument attachShadow). */
18350          if (isElement) {
18351            const sr = getShadowRoot(node);
18352            if (_isDocumentFragment(sr)) {
18353              /* Push the deferred sanitise first so it pops after the shadow
18354                 walk we push next, i.e. nested shadow roots are discovered
18355                 before this one is sanitised. */
18356              stack.push({
18357                node: null,
18358                shadow: sr
18359              }, {
18360                node: sr,
18361                shadow: null
18362              });
18363            }
18364          }
18365        }
18366      };
18367      // eslint-disable-next-line complexity
18368      DOMPurify.sanitize = function (dirty) {
18369        let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
18370        let body = null;
18371        let importedNode = null;
18372        let currentNode = null;
18373        let returnNode = null;
18374        /* Make sure we have a string to sanitize.
18375          DO NOT return early, as this will return the wrong type if
18376          the user has requested a DOM object rather than a string */
18377        IS_EMPTY_INPUT = !dirty;
18378        if (IS_EMPTY_INPUT) {
18379          dirty = '<!-->';
18380        }
18381        /* Stringify, in case dirty is an object */
18382        if (typeof dirty !== 'string' && !_isNode(dirty)) {
18383          dirty = stringifyValue(dirty);
18384          if (typeof dirty !== 'string') {
18385            throw typeErrorCreate('dirty is not a string, aborting');
18386          }
18387        }
18388        /* Return dirty HTML if DOMPurify cannot run */
18389        if (!DOMPurify.isSupported) {
18390          return dirty;
18391        }
18392        /* Assign config vars */
18393        if (SET_CONFIG) {
18394          /* Persistent setConfig() path: _parseConfig is skipped, so the sets are
18395           * not re-derived per call. Restore them from the pristine bindings
18396           * captured at setConfig() time so a previous call's hook clone (mutated
18397           * below) does not carry over. */
18398          ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
18399          ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
18400        } else {
18401          _parseConfig(cfg);
18402        }
18403        /* Clone the hook-mutable allowlists before the walk whenever an
18404         * uponSanitize* hook is registered. The hook event exposes ALLOWED_TAGS
18405         * and ALLOWED_ATTR by reference (as allowedTags / allowedAttributes), so
18406         * a hook that widens them would otherwise mutate the shared set
18407         * permanently: across later calls and across every element. Cloning per
18408         * walk keeps documented in-call widening working while scoping it to the
18409         * call. A single guard for both config paths - the per-call path rebinds
18410         * the sets in _parseConfig each call, the persistent path restores them
18411         * from the captured bindings just above - so the two cannot diverge. */
18412        if (hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) {
18413          ALLOWED_TAGS = clone(ALLOWED_TAGS);
18414        }
18415        if (hooks.uponSanitizeAttribute.length > 0) {
18416          ALLOWED_ATTR = clone(ALLOWED_ATTR);
18417        }
18418        /* Clean up removed elements */
18419        DOMPurify.removed = [];
18420        /* Resolve IN_PLACE for this call without mutating persistent config.
18421           Writing the IN_PLACE closure variable here leaks under setConfig(),
18422           where _parseConfig is skipped on later calls: a single string call would
18423           disable in-place mode for every subsequent node call, returning a
18424           sanitized copy while leaving the caller's node — which in-place callers
18425           keep using and whose return value they ignore — unsanitized. REPORT-2. */
18426        const inPlace = IN_PLACE && typeof dirty !== 'string' && _isNode(dirty);
18427        if (inPlace) {
18428          /* Declarative-partial-updates / streaming pre-pass: sever every patch
18429             linkage across the live tree BEFORE the walk, so no patch can fire
18430             mid-walk and inject into an already-processed region. Runs first, so
18431             it also covers the forbidden/clobbered roots that throw below. */
18432          _neutralizePatchLinkage(dirty);
18433          /* Do some early pre-sanitization to avoid unsafe root nodes.
18434             Read nodeName through the cached prototype getter — a clobbering
18435             child named "nodeName" on the form root would otherwise shadow
18436             the property and let this check skip the root-allowlist
18437             validation entirely. */
18438          const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
18439          if (typeof nn === 'string') {
18440            const tagName = transformCaseFunc(nn);
18441            if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
18442              /* Fail closed on a live root: neutralize handlers/children before
18443                 throwing, exactly as the mid-walk abort path does. */
18444              _neutralizeRoot(dirty);
18445              throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
18446            }
18447          }
18448          /* Pre-flight the root through _isClobbered. The iterator-driven
18449             removal path can not detach a parent-less root: _forceRemove
18450             falls through to Element.prototype.remove(), which per spec
18451             is a no-op on a node with no parent. A clobbered root would
18452             then survive the main loop with its attributes uninspected,
18453             because _sanitizeAttributes early-returns on _isClobbered. The
18454             result would be an attacker-controlled form, complete with any
18455             event-handler attributes the caller passed in, handed back to
18456             the application unsanitized. Refuse to sanitize such a root
18457             the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
18458          if (_isClobbered(dirty)) {
18459            /* Fail closed on a live clobbered root before throwing.
18460               _neutralizeRoot's reads are clobber-safe (cached getters); the
18461               form's non-clobbered descendants, e.g. an armed <img>, are scrubbed. */
18462            _neutralizeRoot(dirty);
18463            throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
18464          }
18465          /* Sanitize attached shadow roots before the main iterator runs.
18466             The iterator does not descend into shadow trees. Same fail-closed
18467             barrier as the main walk (campaign-3 F2): a custom-element reaction
18468             inside a shadow root could abort this pre-pass before the walk runs,
18469             which would otherwise leave the entire live tree unsanitized. */
18470          try {
18471            _sanitizeAttachedShadowRoots(dirty);
18472          } catch (error) {
18473            _neutralizeRoot(dirty);
18474            throw error;
18475          }
18476        } else if (_isNode(dirty)) {
18477          /* If dirty is a DOM element, append to an empty document to avoid
18478             elements being stripped by the parser */
18479          body = _initDocument('<!---->');
18480          importedNode = body.ownerDocument.importNode(dirty, true);
18481          if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
18482            /* Node is already a body, use as is */
18483            body = importedNode;
18484          } else if (importedNode.nodeName === 'HTML') {
18485            body = importedNode;
18486          } else {
18487            // eslint-disable-next-line unicorn/prefer-dom-node-append
18488            body.appendChild(importedNode);
18489          }
18490          /* Clonable shadow roots are deep-cloned by importNode(); sanitize
18491             them before the main iterator runs, since the iterator does not
18492             descend into shadow trees. The walk routes every read through a
18493             cached prototype getter so clobbering descendants on a form root
18494             cannot hide a shadow host from this pass. */
18495          _sanitizeAttachedShadowRoots(importedNode);
18496        } else {
18497          /* Exit directly if we have nothing to do */
18498          if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
18499          // eslint-disable-next-line unicorn/prefer-includes
18500          dirty.indexOf('<') === -1) {
18501            return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
18502          }
18503          /* Initialize the document to work on */
18504          body = _initDocument(dirty);
18505          /* Check we have a DOM node from the data */
18506          if (!body) {
18507            return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
18508          }
18509        }
18510        /* Remove first element node (ours) if FORCE_BODY is set */
18511        if (body && FORCE_BODY) {
18512          _forceRemove(body.firstChild);
18513        }
18514        /* Get node iterator */
18515        const walkRoot = inPlace ? dirty : body;
18516        const nodeIterator = _createNodeIterator(walkRoot);
18517        /* Now start iterating over the created document.
18518           The walk runs inside an exception barrier (campaign-3 F2): a re-entrant
18519           engine/custom-element mutation can detach a node mid-walk so
18520           `_forceRemove`'s parentless guard throws, aborting the loop. Without the
18521           barrier the caller's in-place tree would be left half-sanitized with the
18522           unvisited tail still armed. On any throw we fail closed — strip the
18523           in-place root bare — then rethrow so the existing throw contract is
18524           preserved. (String/DOM-copy paths never return the partial body, so the
18525           propagating throw is already fail-closed there.) */
18526        try {
18527          while (currentNode = nodeIterator.nextNode()) {
18528            /* Sanitize tags and elements */
18529            _sanitizeElements(currentNode, walkRoot);
18530            /* Check attributes next */
18531            _sanitizeAttributes(currentNode);
18532            /* Shadow DOM detected, sanitize it.
18533               Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
18534               instead of instanceof, so foreign-realm <template>.content is
18535               walked correctly. */
18536            if (_isDocumentFragment(currentNode.content)) {
18537              _sanitizeShadowDOM2(currentNode.content);
18538            }
18539          }
18540        } catch (error) {
18541          if (inPlace) {
18542            _neutralizeRoot(dirty);
18543            /* Nodes _forceRemove'd earlier in the aborted walk are already
18544               detached from the root, so _neutralizeRoot's subtree pass does not
18545               reach them. Defuse them too, mirroring the success-path loop below. */
18546            arrayForEach(DOMPurify.removed, entry => {
18547              if (entry.element) {
18548                _neutralizeSubtree(entry.element);
18549              }
18550            });
18551          }
18552          throw error;
18553        }
18554        /* If we sanitized `dirty` in-place, return it. */
18555        if (inPlace) {
18556          /* Fail-closed completion of the audit-5 F1 fix: every node removed from
18557             the caller's live tree is detached but may still hold a queued
18558             resource-event handler that fires in page scope after we return. The
18559             move-hoist covers only disallowed-tag KEEP_CONTENT removals; strip the
18560             non-allow-listed attributes off every other removed subtree (clobber,
18561             mXSS, namespace, comments, KEEP_CONTENT:false, …) so those handlers are
18562             cancelled before any event can fire. Runs synchronously, pre-return. */
18563          arrayForEach(DOMPurify.removed, entry => {
18564            if (entry.element) {
18565              _neutralizeSubtree(entry.element);
18566            }
18567          });
18568          if (SAFE_FOR_TEMPLATES) {
18569            _scrubTemplateExpressions2(dirty);
18570          }
18571          return dirty;
18572        }
18573        /* Return sanitized string or DOM */
18574        if (RETURN_DOM) {
18575          if (SAFE_FOR_TEMPLATES) {
18576            _scrubTemplateExpressions2(body);
18577          }
18578          if (RETURN_DOM_FRAGMENT) {
18579            returnNode = createDocumentFragment.call(body.ownerDocument);
18580            while (body.firstChild) {
18581              // eslint-disable-next-line unicorn/prefer-dom-node-append
18582              returnNode.appendChild(body.firstChild);
18583            }
18584          } else {
18585            returnNode = body;
18586          }
18587          if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
18588            /*
18589              AdoptNode() is not used because internal state is not reset
18590              (e.g. the past names map of a HTMLFormElement), this is safe
18591              in theory but we would rather not risk another attack vector.
18592              The state that is cloned by importNode() is explicitly defined
18593              by the specs.
18594            */
18595            returnNode = importNode.call(originalDocument, returnNode, true);
18596          }
18597          return returnNode;
18598        }
18599        let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
18600        /* Serialize doctype if allowed */
18601        if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
18602          serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
18603        }
18604        /* Sanitize final string template-safe */
18605        if (SAFE_FOR_TEMPLATES) {
18606          serializedHTML = _stripTemplateExpressions(serializedHTML);
18607        }
18608        return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
18609      };
18610      DOMPurify.setConfig = function () {
18611        let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
18612        _parseConfig(cfg);
18613        SET_CONFIG = true;
18614        SET_CONFIG_ALLOWED_TAGS = ALLOWED_TAGS;
18615        SET_CONFIG_ALLOWED_ATTR = ALLOWED_ATTR;
18616      };
18617      DOMPurify.clearConfig = function () {
18618        CONFIG = null;
18619        SET_CONFIG = false;
18620        SET_CONFIG_ALLOWED_TAGS = null;
18621        SET_CONFIG_ALLOWED_ATTR = null;
18622        // Drop any caller-supplied Trusted Types policy so it cannot poison later
18623        // `RETURN_TRUSTED_TYPE` output. The internal default policy (cached, and
18624        // never recreated — Trusted Types throws on duplicate names) is restored by
18625        // the next `_parseConfig`. See GHSA-vxr8-fq34-vvx9.
18626        trustedTypesPolicy = defaultTrustedTypesPolicy;
18627        emptyHTML = '';
18628      };
18629      DOMPurify.isValidAttribute = function (tag, attr, value) {
18630        /* Initialize shared config vars if necessary. */
18631        if (!CONFIG) {
18632          _parseConfig({});
18633        }
18634        const lcTag = transformCaseFunc(tag);
18635        const lcName = transformCaseFunc(attr);
18636        return _isValidAttribute(lcTag, lcName, value);
18637      };
18638      DOMPurify.addHook = function (entryPoint, hookFunction) {
18639        if (typeof hookFunction !== 'function') {
18640          return;
18641        }
18642        /* Reject unknown entry points. Without this, a non-hook key (e.g.
18643         * '__proto__') indexes off the prototype chain rather than a real
18644         * hook array, and arrayPush then writes to Object.prototype. Guard
18645         * with an own-property check against the known hook names. */
18646        if (!objectHasOwnProperty(hooks, entryPoint)) {
18647          return;
18648        }
18649        arrayPush(hooks[entryPoint], hookFunction);
18650      };
18651      DOMPurify.removeHook = function (entryPoint, hookFunction) {
18652        if (!objectHasOwnProperty(hooks, entryPoint)) {
18653          return undefined;
18654        }
18655        if (hookFunction !== undefined) {
18656          const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
18657          return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
18658        }
18659        return arrayPop(hooks[entryPoint]);
18660      };
18661      DOMPurify.removeHooks = function (entryPoint) {
18662        if (!objectHasOwnProperty(hooks, entryPoint)) {
18663          return;
18664        }
18665        hooks[entryPoint] = [];
18666      };
18667      DOMPurify.removeAllHooks = function () {
18668        hooks = _createHooksMap();
18669      };
18670      return DOMPurify;
18671    }
18672    var purify = createDOMPurify();
18673
18674    
18674const sanitizeHtmlString = (html) => purify().sanitize(html);
18675
18676    var global$6 = tinymce.util.Tools.resolve('tinymce.util.I18n');
18677
18678    // Icons that need to be transformed in RTL
18679    const rtlTransform = {
18680        'indent': true,
18681        'outdent': true,
18682        'table-insert-column-after': true,
18683        'table-insert-column-before': true,
18684        'paste-column-after': true,
18685        'paste-column-before': true,
18686        'unordered-list': true,
18687        'list-bull-circle': true,
18688        'list-bull-disc': true,
18689        'list-bull-default': true,
18690        'list-bull-square': true
18691    };
18692    const defaultIconName = 'temporary-placeholder';
18693    const defaultIcon = (icons) => () => get$h(icons, defaultIconName).getOr('!not found!');
18694    const getIconName = (name, icons) => {
18695        const lcName = name.toLowerCase();
18696        // If in rtl mode then try to see if we have a rtl icon to use instead
18697        if (global$6.isRtl()) {
18698            const rtlName = ensureTrailing(lcName, '-rtl');
18699            return has$2(icons, rtlName) ? rtlName : lcName;
18700        }
18701        else {
18702            return lcName;
18703        }
18704    };
18705    const lookupIcon = (name, icons) => get$h(icons, getIconName(name, icons));
18706    const get = (name, iconProvider) => {
18707        const icons = iconProvider();
18708        return lookupIcon(name, icons).getOrThunk(defaultIcon(icons));
18709    };
18710    const getOr = (name, iconProvider, fallbackIcon) => {
18711        const icons = iconProvider();
18712        return lookupIcon(name, icons).or(fallbackIcon).getOrThunk(defaultIcon(icons));
18713    };
18714    const needsRtlTransform = (iconName) => global$6.isRtl() ? has$2(rtlTransform, iconName) : false;
18715    const addFocusableBehaviour = () => config('add-focusable', [
18716        runOnAttached((comp) => {
18717            // set focusable=false on SVGs to prevent focusing the toolbar when tabbing into the editor
18718            child(comp.element, 'svg').each((svg) => set$9(svg, 'focusable', 'false'));
18719        })
18720    ]);
18721    const renderIcon$3 = (spec, iconName, icons, fallbackIcon) => {
18722        // If RTL, add the flip icon class if the icon doesn't have a `-rtl` icon available.
18723        const rtlIconClasses = needsRtlTransform(iconName) ? ['tox-icon--flip'] : [];
18724        const iconHtml = get$h(icons, getIconName(iconName, icons)).or(fallbackIcon).getOrThunk(defaultIcon(icons));
18725        return {
18726            dom: {
18727                tag: spec.tag,
18728                attributes: spec.attributes ?? {},
18729                classes: spec.classes.concat(rtlIconClasses),
18730                innerHtml: iconHtml
18731            },
18732            behaviours: derive$1([
18733                ...spec.behaviours ?? [],
18734                addFocusableBehaviour()
18735            ]),
18736            eventOrder: spec.eventOrder ?? {}
18737        };
18738    };
18739    const render$4 = (iconName, spec, iconProvider, fallbackIcon = Optional.none()) => renderIcon$3(spec, iconName, iconProvider(), fallbackIcon);
18740    const renderFirst = (iconNames, spec, iconProvider) => {
18741        const icons = iconProvider();
18742        const iconName = find$5(iconNames, (name) => has$2(icons, getIconName(name, icons)));
18743        return renderIcon$3(spec, iconName.getOr(defaultIconName), icons, Optional.none());
18744    };
18745
18746    const notificationIconMap = {
18747        success: 'checkmark',
18748        error: 'warning',
18749        err: 'error',
18750        warning: 'warning',
18751        warn: 'warning',
18752        info: 'info'
18753    };
18754    const factory$4 = (detail) => {
18755        // For using the alert banner as a standalone banner
18756        const notificationTextId = generate$6('notification-text');
18757        const memBannerText = record({
18758            dom: fromHtml(`<p id=${notificationTextId}>${sanitizeHtmlString(detail.backstageProvider.translate(detail.text))}</p>`),
18759            behaviours: derive$1([
18760                Replacing.config({})
18761            ])
18762        });
18763        const renderPercentBar = (percent) => ({
18764            dom: {
18765                tag: 'div',
18766                classes: ['tox-bar'],
18767                styles: {
18768                    width: `${percent}%`
18769                }
18770            }
18771        });
18772        const renderPercentText = (percent) => ({
18773            dom: {
18774                tag: 'div',
18775                classes: ['tox-text'],
18776                innerHtml: `${percent}%`
18777            }
18778        });
18779        const memBannerProgress = record({
18780            dom: {
18781                tag: 'div',
18782                classes: detail.progress ? ['tox-progress-bar', 'tox-progress-indicator'] : ['tox-progress-bar']
18783            },
18784            components: [
18785                {
18786                    dom: {
18787                        tag: 'div',
18788                        classes: ['tox-bar-container']
18789                    },
18790                    components: [
18791                        renderPercentBar(0)
18792                    ]
18793                },
18794                renderPercentText(0)
18795            ],
18796            behaviours: derive$1([
18797                Replacing.config({})
18798            ])
18799        });
18800        const updateProgress = (comp, percent) => {
18801            if (comp.getSystem().isConnected()) {
18802                memBannerProgress.getOpt(comp).each((progress) => {
18803                    Replacing.set(progress, [
18804                        {
18805                            dom: {
18806                                tag: 'div',
18807                                classes: ['tox-bar-container']
18808                            },
18809                            components: [
18810                                renderPercentBar(percent)
18811                            ]
18812                        },
18813                        renderPercentText(percent)
18814                    ]);
18815                });
18816            }
18817        };
18818        const updateText = (comp, text) => {
18819            if (comp.getSystem().isConnected()) {
18820                const banner = memBannerText.get(comp);
18821                Replacing.set(banner, [
18822                    text$2(text)
18823                ]);
18824            }
18825        };
18826        const apis = {
18827            updateProgress,
18828            updateText
18829        };
18830        const iconChoices = flatten([
18831            detail.icon.toArray(),
18832            [detail.level],
18833            Optional.from(notificationIconMap[detail.level]).toArray()
18834        ]);
18835        const memButton = record(Button.sketch({
18836            dom: {
18837                tag: 'button',
18838                classes: ['tox-notification__dismiss', 'tox-button', 'tox-button--naked', 'tox-button--icon'],
18839                attributes: {
18840                    'aria-label': detail.backstageProvider.translate('Close')
18841                }
18842            },
18843            components: [
18844                render$4('close', {
18845                    tag: 'span',
18846                    classes: ['tox-icon'],
18847                }, detail.iconProvider)
18848            ],
18849            buttonBehaviours: derive$1([
18850                Tabstopping.config({}),
18851                Tooltipping.config({
18852                    ...detail.backstageProvider.tooltips.getConfig({
18853                        tooltipText: detail.backstageProvider.translate('Close')
18854                    })
18855                })
18856            ]),
18857            action: (comp) => {
18858                detail.onAction(comp);
18859            }
18860        }));
18861        const notificationIconSpec = renderFirst(iconChoices, { tag: 'div', classes: ['tox-notification__icon'] }, detail.iconProvider);
18862        const notificationBodySpec = {
18863            dom: {
18864                tag: 'div',
18865                classes: ['tox-notification__body']
18866            },
18867            components: [
18868                memBannerText.asSpec()
18869            ],
18870            behaviours: derive$1([
18871                Replacing.config({})
18872            ])
18873        };
18874        const components = [notificationIconSpec, notificationBodySpec];
18875        return {
18876            uid: detail.uid,
18877            dom: {
18878                tag: 'div',
18879                attributes: {
18880                    'role': 'alert',
18881                    'aria-labelledby': notificationTextId
18882                },
18883                classes: ['tox-notification', 'tox-notification--in', `tox-notification--${detail.level}`],
18884            },
18885            behaviours: derive$1([
18886                Tabstopping.config({}),
18887                Focusing.config({}),
18888                Keying.config({
18889                    mode: 'special',
18890                    onEscape: (comp) => {
18891                        detail.onAction(comp);
18892                        return Optional.some(true);
18893                    }
18894                })
18895            ]),
18896            components: components
18897                .concat(detail.progress ? [memBannerProgress.asSpec()] : [])
18898                .concat([memButton.asSpec()]),
18899            apis
18900        };
18901    };
18902    const Notification = single({
18903        name: 'Notification',
18904        factory: factory$4,
18905        configFields: [
18906            defaultedStringEnum('level', 'info', ['success', 'error', 'warning', 'warn', 'info']),
18907            required$1('progress'),
18908            option$3('icon'),
18909            required$1('onAction'),
18910            required$1('text'),
18911            required$1('iconProvider'),
18912            required$1('backstageProvider'),
18913        ],
18914        apis: {
18915            updateProgress: (apis, comp, percent) => {
18916                apis.updateProgress(comp, percent);
18917            },
18918            updateText: (apis, comp, text) => {
18919                apis.updateText(comp, text);
18920            }
18921        }
18922    });
18923
18924    var NotificationManagerImpl = (editor, extras, uiMothership, notificationRegion) => {
18925        const sharedBackstage = extras.backstage.shared;
18926        const getBoundsContainer = () => SugarElement.fromDom(editor.queryCommandValue('ToggleView') === '' ? editor.getContentAreaContainer() : editor.getContainer());
18927        const getBounds = () => {
18928            const contentArea = box$1(getBoundsContainer());
18929            return Optional.some(contentArea);
18930        };
18931        const clampComponentsToBounds = (components) => {
18932            getBounds().each((bounds) => {
18933                each$1(components, (comp) => {
18934                    remove$6(comp.element, 'width');
18935                    if (get$c(comp.element) > bounds.width) {
18936                        set$7(comp.element, 'width', bounds.width + 'px');
18937                    }
18938                });
18939            });
18940        };
18941        const open = (settings, closeCallback, isEditorOrUIFocused) => {
18942            const close = () => {
18943                const removeNotificationAndReposition = (region) => {
18944                    Replacing.remove(region, notification);
18945                    reposition();
18946                };
18947                const manageRegionVisibility = (region, editorOrUiFocused) => {
18948                    if (children(region.element).length === 0) {
18949                        handleEmptyRegion(region, editorOrUiFocused);
18950                    }
18951                    else {
18952                        handleRegionWithChildren(region, editorOrUiFocused);
18953                    }
18954                };
18955                const handleEmptyRegion = (region, editorOrUIFocused) => {
18956                    InlineView.hide(region);
18957                    notificationRegion.clear();
18958                    if (editorOrUIFocused) {
18959                        editor.focus();
18960                    }
18961                };
18962                const handleRegionWithChildren = (region, editorOrUIFocused) => {
18963                    if (editorOrUIFocused) {
18964                        Keying.focusIn(region);
18965                    }
18966                };
18967                notificationRegion.on((region) => {
18968                    closeCallback();
18969                    const editorOrUIFocused = isEditorOrUIFocused();
18970                    removeNotificationAndReposition(region);
18971                    manageRegionVisibility(region, editorOrUIFocused);
18972                });
18973            };
18974            const shouldApplyDocking = () => !isStickyToolbar(editor) || !sharedBackstage.header.isPositionedAtTop();
18975            const notification = build$1(Notification.sketch({
18976                text: settings.text,
18977                level: contains$2(['success', 'error', 'warning', 'warn', 'info'], settings.type) ? settings.type : undefined,
18978                progress: settings.progressBar === true,
18979                icon: settings.icon,
18980                onAction: close,
18981                iconProvider: sharedBackstage.providers.icons,
18982                backstageProvider: sharedBackstage.providers,
18983            }));
18984            if (!notificationRegion.isSet()) {
18985                const notificationWrapper = build$1(InlineView.sketch({
18986                    dom: {
18987                        tag: 'div',
18988                        classes: ['tox-notifications-container'],
18989                        attributes: {
18990                            'aria-label': 'Notifications',
18991                            'role': 'region'
18992                        }
18993                    },
18994                    lazySink: sharedBackstage.getSink,
18995                    fireDismissalEventInstead: {},
18996                    ...sharedBackstage.header.isPositionedAtTop() ? {}
18996 : { fireRepositionEventInstead: {} },
18997                    inlineBehaviours: derive$1([
18998                        Keying.config({
18999                            mode: 'cyclic',
19000                            selector: '.tox-notification, .tox-notification a, .tox-notification button',
19001                        }),
19002                        Replacing.config({}),
19003                        ...(shouldApplyDocking()
19004                            ? [
19005                                Docking.config({
19006                                    contextual: {
19007                                        lazyContext: () => Optional.some(box$1(getBoundsContainer())),
19008                                        fadeInClass: 'tox-notification-container-dock-fadein',
19009                                        fadeOutClass: 'tox-notification-container-dock-fadeout',
19010                                        transitionClass: 'tox-notification-container-dock-transition'
19011                                    },
19012                                    modes: ['top'],
19013                                    lazyViewport: (comp) => {
19014                                        const optScrollingContext = detectWhenSplitUiMode(editor, comp.element);
19015                                        return optScrollingContext
19016                                            .map((sc) => {
19017                                            const combinedBounds = getBoundsFrom(sc);
19018                                            return {
19019                                                bounds: combinedBounds,
19020                                                optScrollEnv: Optional.some({
19021                                                    currentScrollTop: sc.element.dom.scrollTop,
19022                                                    scrollElmTop: absolute$3(sc.element).top
19023                                                })
19024                                            };
19025                                        }).getOrThunk(() => ({
19026                                            bounds: win(),
19027                                            optScrollEnv: Optional.none()
19028                                        }));
19029                                    }
19030                                })
19031                            ] : [])
19032                    ])
19033                }));
19034                const notificationSpec = premade(notification);
19035                const anchorOverrides = {
19036                    maxHeightFunction: expandable$1()
19037                };
19038                const anchor = {
19039                    ...sharedBackstage.anchors.banner(),
19040                    overrides: anchorOverrides
19041                };
19042                notificationRegion.set(notificationWrapper);
19043                uiMothership.add(notificationWrapper);
19044                InlineView.showWithinBounds(notificationWrapper, notificationSpec, { anchor }, getBounds);
19045            }
19046            else {
19047                const notificationSpec = premade(notification);
19048                notificationRegion.on((notificationWrapper) => {
19049                    Replacing.append(notificationWrapper, notificationSpec);
19050                    InlineView.reposition(notificationWrapper);
19051                    if (notification.hasConfigured(Docking)) {
19052                        Docking.refresh(notificationWrapper);
19053                    }
19054                    clampComponentsToBounds(notificationWrapper.components());
19055                });
19056            }
19057            if (isNumber(settings.timeout) && settings.timeout > 0) {
19058                global$a.setEditorTimeout(editor, () => {
19059                    close();
19060                }, settings.timeout);
19061            }
19062            const reposition = () => {
19063                notificationRegion.on((region) => {
19064                    InlineView.reposition(region);
19065                    if (region.hasConfigured(Docking)) {
19066                        Docking.refresh(region);
19067                    }
19068                    clampComponentsToBounds(region.components());
19069                });
19070            };
19071            const thisNotification = {
19072                close,
19073                reposition,
19074                text: (nuText) => {
19075                    // check if component is still mounted
19076                    Notification.updateText(notification, nuText);
19077                },
19078                settings,
19079                getEl: () => notification.element.dom,
19080                progressBar: {
19081                    value: (percent) => {
19082                        Notification.updateProgress(notification, percent);
19083                    }
19084                }
19085            };
19086            return thisNotification;
19087        };
19088        const close = (notification) => {
19089            notification.close();
19090        };
19091        const getArgs = (notification) => {
19092            return notification.settings;
19093        };
19094        return {
19095            open,
19096            close,
19097            getArgs
19098        };
19099    };
19100
19101    const setup$c = (api, editor) => {
19102        const redirectKeyToItem = (item, e) => {
19103            emitWith(item, keydown(), { raw: e });
19104        };
19105        const getItem = () => api.getMenu().bind(Highlighting.getHighlighted);
19106        editor.on('keydown', (e) => {
19107            const keyCode = e.which;
19108            // If the autocompleter isn't activated then do nothing
19109            if (!api.isActive()) {
19110                return;
19111            }
19112            if (api.isMenuOpen()) {
19113                // Pressing <enter> executes any item currently selected, or does nothing
19114                if (keyCode === 13) {
19115                    getItem().each(emitExecute);
19116                    e.preventDefault();
19117                    // Pressing <down> either highlights the first option, or moves down the menu
19118                }
19119                else if (keyCode === 40) {
19120                    getItem().fold(
19121                    // No current item, so highlight the first one
19122                    () => {
19123                        api.getMenu().each(Highlighting.highlightFirst);
19124                    }, 
19125                    // There is a current item, so move down in the menu
19126                    (item) => {
19127                        redirectKeyToItem(item, e);
19128                    });
19129                    e.preventDefault();
19130                    e.stopImmediatePropagation();
19131                    // Pressing <up>, <left>, <right> gets redirected to the selected item
19132                }
19133                else if (keyCode === 37 || keyCode === 38 || keyCode === 39) {
19134                    getItem().each((item) => {
19135                        redirectKeyToItem(item, e);
19136                        e.preventDefault();
19137                        e.stopImmediatePropagation();
19138                    });
19139                }
19140            }
19141            else {
19142                // Pressing <enter>, <down> or <up> closes the autocompleter when it's active but the menu isn't open
19143                if (keyCode === 13 || keyCode === 38 || keyCode === 40) {
19144                    api.cancelIfNecessary();
19145                }
19146            }
19147        });
19148        editor.on('NodeChange', () => {
19149            // Close if active, not in the middle of an onAction callback and we're no longer inside the autocompleter span
19150            if (api.isActive() && !api.isProcessingAction() && !editor.queryCommandState('mceAutoCompleterInRange')) {
19151                api.cancelIfNecessary();
19152            }
19153        });
19154    };
19155    const AutocompleterEditorEvents = {
19156        setup: setup$c
19157    };
19158
19159    var ItemResponse;
19160    (function (ItemResponse) {
19161        ItemResponse[ItemResponse["CLOSE_ON_EXECUTE"] = 0] = "CLOSE_ON_EXECUTE";
19162        ItemResponse[ItemResponse["BUBBLE_TO_SANDBOX"] = 1] = "BUBBLE_TO_SANDBOX";
19163    })(ItemResponse || (ItemResponse = {}));
19164    var ItemResponse$1 = ItemResponse;
19165
19166    const navClass = 'tox-menu-nav__js';
19167    const selectableClass = 'tox-collection__item';
19168    const colorClass = 'tox-swatch';
19169    const presetClasses = {
19170        normal: navClass,
19171        color: colorClass
19172    };
19173    const tickedClass = 'tox-collection__item--enabled';
19174    const groupHeadingClass = 'tox-collection__group-heading';
19175    const iconClass = 'tox-collection__item-icon';
19176    const imageClass = 'tox-collection__item-image';
19177    const imageSelectorClasll = 'tox-collection__item-image-selector';
19178    const textClass = 'tox-collection__item-label';
19179    const accessoryClass = 'tox-collection__item-accessory';
19180    const caretClass = 'tox-collection__item-caret';
19181    const checkmarkClass = 'tox-collection__item-checkmark';
19182    const activeClass = 'tox-collection__item--active';
19183    const containerClass = 'tox-collection__item-container';
19184    const containerColumnClass = 'tox-collection__item-container--column';
19185    const containerRowClass = 'tox-collection__item-container--row';
19186    const containerAlignRightClass = 'tox-collection__item-container--align-right';
19187    const containerAlignLeftClass = 'tox-collection__item-container--align-left';
19188    const containerValignTopClass = 'tox-collection__item-container--valign-top';
19189    const containerValignMiddleClass = 'tox-collection__item-container--valign-middle';
19190    const containerValignBottomClass = 'tox-collection__item-container--valign-bottom';
19191    const classForPreset = (presets) => get$h(presetClasses, presets).getOr(navClass);
19192
19193    const forMenu = (presets) => {
19194        if (presets === 'color') {
19195            return 'tox-swatches';
19196        }
19197        else {
19198            return 'tox-menu';
19199        }
19200    };
19201    const classes = (presets) => ({
19202        backgroundMenu: 'tox-background-menu',
19203        selectedMenu: 'tox-selected-menu',
19204        selectedItem: 'tox-collection__item--active',
19205        hasIcons: 'tox-menu--has-icons',
19206        menu: forMenu(presets),
19207        tieredMenu: 'tox-tiered-menu'
19208    });
19209
19210    const markers = (presets) => {
19211        const menuClasses = classes(presets);
19212        return {
19213            backgroundMenu: menuClasses.backgroundMenu,
19214            selectedMenu: menuClasses.selectedMenu,
19215            menu: menuClasses.menu,
19216            selectedItem: menuClasses.selectedItem,
19217            item: classForPreset(presets)
19218        };
19219    };
19220    const dom = (hasIcons, columns, presets) => {
19221        const menuClasses = classes(presets);
19222        return {
19223            tag: 'div',
19224            classes: flatten([
19225                [menuClasses.menu, `tox-menu-${columns}-column`],
19226                hasIcons ? [menuClasses.hasIcons] : []
19227            ])
19228        };
19229    };
19230    const components = [
19231        Menu.parts.items({})
19232    ];
19233    // NOTE: Up to here.
19234    const part = (hasIcons, columns, presets) => {
19235        const menuClasses = classes(presets);
19236        const d = {
19237            tag: 'div',
19238            classes: flatten([
19239                [menuClasses.tieredMenu]
19240            ])
19241        };
19242        return {
19243            dom: d,
19244            markers: markers(presets)
19245        };
19246    };
19247
19248    // This event is triggered by a menu item from a dropdown when it wants the
19249    // dropdown to refetch its contents based on a search string.
19250    const refetchTriggerEvent = generate$6('refetch-trigger-event');
19251    // This event is triggerd by a menu item from a dropdown, when it wants to
19252    // redispatch that event to the currently active item of that dropdown menu. It will
19253    // be used in situations where the event should be firing on the item with fake focus,
19254    // but instead it is firing on the item with real focus (e.g of real focus:
19255    // menu search field)
19256    const redirectMenuItemInteractionEvent = generate$6('redirect-menu-item-interaction');
19257
19258    // This is not stored in ItemClasses, because the searcher is not actually
19259    // contained within items. It isn't part of their navigation, and it
19260    // isn't maintained by menus. It is just part of the first menu, but
19261    // not its items.
19262    const menuSearcherClass = 'tox-menu__searcher';
19263    // Ideally, we'd be using mementos to find it again, but we'd need to pass
19264    // that memento onto the dropdown, which isn't going to have it. Especially,
19265    // because the dropdown isn't responsible for putting this searcher component
19266    // into the menu, NestedMenus is.
19267    const findWithinSandbox = (sandboxComp) => {
19268        return descendant(sandboxComp.element, `.${menuSearcherClass}`).bind((inputElem) => sandboxComp.getSystem().getByDom(inputElem).toOptional());
19269    };
19270    // There is nothing sandbox-specific about this code. It just needs to be
19271    // a container that wraps the search field.
19272    const findWithinMenu = findWithinSandbox;
19273    const restoreState = (inputComp, searcherState) => {
19274        Representing.setValue(inputComp, searcherState.fetchPattern);
19275        inputComp.element.dom.selectionStart = searcherState.selectionStart;
19276        inputComp.element.dom.selectionEnd = searcherState.selectionEnd;
19277    };
19278    const saveState = (inputComp) => {
19279        const fetchPattern = Representing.getValue(inputComp);
19280        const selectionStart = inputComp.element.dom.selectionStart;
19281        const selectionEnd = inputComp.element.dom.selectionEnd;
19282        return {
19283            fetchPattern,
19284            selectionStart,
19285            selectionEnd
19286        };
19287    };
19288    // Make sure there is ARIA communicating the currently active item in the results.
19289    const setActiveDescendant = (inputComp, active) => {
19290        getOpt(active.element, 'id')
19291            .each((id) => set$9(inputComp.element, 'aria-activedescendant', id));
19292    };
19293    const renderMenuSearcher = (spec) => {
19294        const handleByBrowser = (comp, se) => {
19295            // We "cut" this event, so that the browser still handles it, but it is not processed
19296            // by any of the above alloy components. We could also do this by stopping propagation,
19297            // but not preventing default, but it's probably good to allow some overarching thing
vendor: 5,325 bytes, lines 19298-19374
19298            // in the DOM (outside of alloy) to stop it if they want to.
19299            se.cut();
19300            // Returning a Some here (regardless of boolean value) is going to call `stop` on the
19301            // simulated event, which is going to call: preventDefault and stopPropagation. We want
19302            // neither of these things to happen, so we return None here to say that it hasn't been
19303            // handled. But because we've cut it, it will not propagate to any other alloy components
19304            return Optional.none();
19305        };
19306        const handleByHighlightedItem = (comp, se) => {
19307            // Because we need to redispatch based on highlighted items that we don't know about here,
19308            // we are going to emit an event, that the sandbox listens to, and the sandbox will
19309            // redispatch the event.
19310            const eventData = {
19311                interactionEvent: se.event,
19312                eventType: se.event.raw.type
19313            };
19314            emitWith(comp, redirectMenuItemInteractionEvent, eventData);
19315            return Optional.some(true);
19316        };
19317        const customSearcherEventsName = 'searcher-events';
19318        return {
19319            dom: {
19320                tag: 'div',
19321                // NOTE: This is very intentionally NOT the navigation class, because
19322                // we don't want the searcher to be part of the navigation. This class
19323                // is just for styling consistency. Perhaps it should be its own class.
19324                classes: [selectableClass]
19325            },
19326            components: [
19327                Input.sketch({
19328                    inputClasses: [menuSearcherClass, 'tox-textfield'],
19329                    inputAttributes: {
19330                        ...(spec.placeholder.map((placeholder) => ({ placeholder: spec.i18n(placeholder) })).getOr({})),
19331                        // This ARIA is based on the algolia example documented in TINY-8952
19332                        'type': 'search',
19333                        'aria-autocomplete': 'list'
19334                    },
19335                    inputBehaviours: derive$1([
19336                        config(customSearcherEventsName, [
19337                            // When the user types into the search field, we want to retrigger
19338                            // a fetch on the dropdown. This will be fired from within the
19339                            // dropdown's sandbox, so the dropdown is going to have to listen
19340                            // for it there. See CommonDropdown.ts.
19341                            run$1(
19342                            // Use "input" to handle keydown, paste etc.
19343                            input(), (inputComp) => {
19344                                emit(inputComp, refetchTriggerEvent);
19345                            }),
19346                            run$1(keydown(), (inputComp, se) => {
19347                                // The Special Keying config type since TINY-7005 processes the Escape
19348                                // key on keyup, not keydown. We need to stop the keydown event for this
19349                                // input, because some browsers (e.g. Chrome) will process a keydown
19350                                // for Escape inside an input[type=search] by clearing the input value,
19351                                // and then triggering an "input" event. This "input" event will trigger
19352                                // a refetch, which if it completes before the keyup is fired for Escape,
19353                                // will go back to only showing one level of menu. Then, when the escape
19354                                // keyup is processed by Keying, it will close the single remaining menu.
19355                                // This has the effect of closing *all* menus that are open when Escape is
19356                                // pressed instead of the last one. So, instead, we are going to kill the
19357                                // keydown event, so that it doesn't have the default browser behaviour, and
19358                                // won't trigger an input (and then Refetch). Then the keyup will still fire
19359                                // so just one level of the menu will close. This is all based on the underlying
19360                                // assumption that preventDefault and/or stop on a keydown does not suppress
19361                                // the related keyup. All of the documentation found so far, suggests it should
19362                                // only suppress the keypress, not the keyup, but that might not be across all
19363                                // browsers, or implemented consistently.
19364                                if (se.event.raw.key === 'Escape') {
19365                                    se.stop();
19366                                }
19367                            })
19368                        ]),
19369                        // In addition to input handling, we want special handling for
19370                        // Up/Down/Left/Right/Enter/Escape/Space. We can divide these into two categories
19371                        //  - events that we don't want to allow the overall menu system to process (left and right and space)
19372                        //  - events that we want to redispatch on the "highlighted item" based on the
19373                        // current fake focus.
19374                        Keying.config({
19375                            mode: 'special',
19376                            onLeft: handleByBrowser,
19377                            onRight: handleByBrowser,
19378                            onSpace: handleByBrowser,
19379                            onEnter: handleByHighlightedItem,
19380                            onEscape: handleByHighlightedItem,
19381                            onUp: handleByHighlightedItem,
19382                            onDown: handleByHighlightedItem
19383                        })
19384                    ]),
19385                    // Because we have customised handling for keydown, and we are configuring
19386                    // Keying, we need to specify which "behaviour" (custom events or keying) gets to
19387                    // process the keydown event first. In this situation, we want to stop escape before
19388                    // anything happens (although it really isn't necessary)
19389                    eventOrder: {
19390                        keydown: [customSearcherEventsName, Keying.name()]
19391                    }
19392                })
19393            ]
19394        };
19395    };
19396
19397    const searchResultsClass = 'tox-collection--results__js';
19398    // NOTE: this is operating on the the final AlloySpec
19399    const augmentWithAria = (item) => {
19400        if (item.dom) {
19401            return {
19402                ...item,
19403                dom: {
19404                    ...item.dom,
19405                    attributes: {
19406                        ...item.dom.attributes ?? {},
19407                        'id': generate$6('aria-item-search-result-id'),
19408                        'aria-selected': 'false'
19409                    }
19410                }
19411            };
19412        }
19413        else {
19414            return item;
19415        }
19416    };
19417
19418    const widgetAriaLabel = 'Use arrow keys to navigate.';
19419    const chunk = (rowDom, numColumns) => (items) => {
19420        const chunks = chunk$1(items, numColumns);
19421        return map$2(chunks, (c) => ({
19422            dom: rowDom,
19423            components: c
19424        }));
19425    };
19426    const forSwatch = (columns) => ({
19427        dom: {
19428            tag: 'div',
19429            classes: ['tox-menu', 'tox-swatches-menu'],
19430            attributes: {
19431                'aria-label': global$6.translate(widgetAriaLabel)
19432            }
19433        },
19434        components: [
19435            {
19436                dom: {
19437                    tag: 'div',
19438                    classes: ['tox-swatches']
19439                },
19440                components: [
19441                    Menu.parts.items({
19442                        preprocess: columns !== 'auto' ? chunk({
19443                            tag: 'div',
19444                            classes: ['tox-swatches__row']
19445                        }, columns) : identity
19446                    })
19447                ]
19448            }
19449        ]
19450    });
19451    const forImageSelector = (columns) => ({
19452        dom: {
19453            tag: 'div',
19454            classes: ['tox-menu', 'tox-image-selector-menu']
19455        },
19456        components: [
19457            {
19458                dom: {
19459                    tag: 'div',
19460                    classes: ['tox-image-selector']
19461                },
19462                components: [
19463                    Menu.parts.items({
19464                        preprocess: columns !== 'auto' ? chunk({
19465                            tag: 'div',
19466                            classes: ['tox-image-selector__row']
19467                        }, columns) : identity
19468                    })
19469                ]
19470            }
19471        ]
19472    });
19473    const forToolbar = (columns) => ({
19474        dom: {
19475            tag: 'div',
19476            // TODO: Configurable lg setting?
19477            classes: ['tox-menu', 'tox-collection', 'tox-collection--toolbar', 'tox-collection--toolbar-lg']
19478        },
19479        components: [
19480            Menu.parts.items({
19481                preprocess: chunk({
19482                    tag: 'div',
19483                    classes: ['tox-collection__group']
19484                }, columns)
19485            })
19486        ]
19487    });
19488    // NOTE: That type signature isn't quite true.
19489    const preprocessCollection = (items, isSeparator) => {
19490        const allSplits = [];
19491        let currentSplit = [];
19492        each$1(items, (item, i) => {
19493            if (isSeparator(item, i)) {
19494                if (currentSplit.length > 0) {
19495                    allSplits.push(currentSplit);
19496                }
19497                currentSplit = [];
19498                if (has$2(item.dom, 'innerHtml') || item.components && item.components.length > 0) {
19499                    currentSplit.push(item);
19500                }
19501            }
19502            else {
19503                currentSplit.push(item);
19504            }
19505        });
19506        if (currentSplit.length > 0) {
19507            allSplits.push(currentSplit);
19508        }
19509        return map$2(allSplits, (s) => ({
19510            dom: {
19511                tag: 'div',
19512                classes: ['tox-collection__group']
19513            },
19514            components: s
19515        }));
19516    };
19517    const insertItemsPlaceholder = (columns, initItems, onItem) => {
vendor: 10,274 bytes, lines 19518-19743
19518        return Menu.parts.items({
19519            preprocess: (rawItems) => {
19520                // Add any information to the items that is required. For example
19521                // when the items are results in a searchable menu, we need them to have
19522                // an ID that can be referenced by aria-activedescendant
19523                const enrichedItems = map$2(rawItems, onItem);
19524                if (columns !== 'auto' && columns > 1) {
19525                    return chunk({
19526                        tag: 'div',
19527                        classes: ['tox-collection__group']
19528                    }, columns)(enrichedItems);
19529                }
19530                else {
19531                    return preprocessCollection(enrichedItems, (_item, i) => initItems[i].type === 'separator');
19532                }
19533            }
19534        });
19535    };
19536    const hasWidget = (items) => exists(items, (item) => item.type === 'widget');
19537    const forCollection = (columns, initItems, _hasIcons = true) => ({
19538        dom: {
19539            tag: 'div',
19540            classes: ['tox-menu', 'tox-collection'].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid']),
19541            attributes: {
19542                // widget item can be inserttable, colorswatch or imageselect - all of them are navigated with arrow keys
19543                ...hasWidget(initItems) ? { 'aria-label': global$6.translate(widgetAriaLabel) } : {}
19544            },
19545        },
19546        components: [
19547            // We don't need to add IDs for each item because there are no
19548            // aria relationships we need to maintain
19549            insertItemsPlaceholder(columns, initItems, identity)
19550        ]
19551    });
19552    const forCollectionWithSearchResults = (columns, initItems, _hasIcons = true) => {
19553        // A collection with results is exactly like a collection, except it also has
19554        // an ID and class on its outer div to allow for aria-controls relationships, and ids
19555        // on its items.
19556        // This connects the search bar with the list box.
19557        const ariaControlsSearchResults = generate$6('aria-controls-search-results');
19558        return {
19559            dom: {
19560                tag: 'div',
19561                classes: ['tox-menu', 'tox-collection', searchResultsClass].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid']),
19562                attributes: {
19563                    id: ariaControlsSearchResults
19564                }
19565            },
19566            components: [
19567                // For each item, it needs to have an ID, so that we can refer to it
19568                // by the aria-activedescendant attribute
19569                insertItemsPlaceholder(columns, initItems, augmentWithAria)
19570            ]
19571        };
19572    };
19573    // Does a searchable menu *really* support columns !== 1 ?
19574    const forCollectionWithSearchField = (columns, initItems, searchField) => {
19575        // This connects the search bar with the list box.
19576        const ariaControlsSearchResults = generate$6('aria-controls-search-results');
19577        return {
19578            dom: {
19579                tag: 'div',
19580                classes: ['tox-menu', 'tox-collection'].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid'])
19581            },
19582            components: [
19583                // Importantly, the search bar is not in the "items" part, which means that it is
19584                // not given any of the item decorations by default. In order to ensure that is
19585                // not part of the navigation, however, we need to prevent it from getting the nav
19586                // class. For general collection menu items, it is navClass, which is:
19587                // tox-menu-nav__js. So simply, do not add this class when creating
19588                // the search, so that it isn't in the navigation. Ideally, it would only ever look
19589                // inside its items section, but the items aren't guaranteed to have a separate
19590                // container, and navigation candidates are found anywhere inside the menu
19591                // container. We could add configuration to alloy's Menu movement, where there was
19592                // a 'navigation container' that all items would be in. That could be another
19593                // way to solve the problem. For now, we'll just manually avoid adding the navClass
19594                renderMenuSearcher({
19595                    i18n: global$6.translate,
19596                    placeholder: searchField.placeholder
19597                }),
19598                {
19599                    // We need a separate container for the items, because this is the container
19600                    // that multiple tox-collection__groups might go into, and will be the container
19601                    // that the search bar controls.
19602                    dom: {
19603                        tag: 'div',
19604                        classes: [
19605                            ...(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid']),
19606                            searchResultsClass
19607                        ],
19608                        attributes: {
19609                            id: ariaControlsSearchResults
19610                        }
19611                    },
19612                    components: [
19613                        // For each item, it needs to have an ID, so that we can refer to it
19614                        // by the aria-activedescendant attribute
19615                        insertItemsPlaceholder(columns, initItems, augmentWithAria)
19616                    ]
19617                }
19618            ]
19619        };
19620    };
19621    const forHorizontalCollection = (initItems, _hasIcons = true) => ({
19622        dom: {
19623            tag: 'div',
19624            classes: ['tox-collection', 'tox-collection--horizontal']
19625        },
19626        components: [
19627            Menu.parts.items({
19628                preprocess: (items) => preprocessCollection(items, (_item, i) => initItems[i].type === 'separator')
19629            })
19630        ]
19631    });
19632
19633    const menuHasIcons = (xs) => exists(xs, (item) => 'icon' in item && item.icon !== undefined);
19634    const handleError = (error) => {
19635        // eslint-disable-next-line no-console
19636        console.error(formatError(error));
19637        // eslint-disable-next-line no-console
19638        console.log(error);
19639        return Optional.none();
19640    };
19641    const createHorizontalPartialMenuWithAlloyItems = (value, _hasIcons, items, _columns, _menuLayout) => {
19642        // Horizontal collections do not support different menu layout structures currently.
19643        const structure = forHorizontalCollection(items);
19644        return {
19645            value,
19646            dom: structure.dom,
19647            components: structure.components,
19648            items
19649        };
19650    };
19651    const createPartialMenuWithAlloyItems = (value, hasIcons, items, columns, menuLayout) => {
19652        const getNormalStructure = () => {
19653            if (menuLayout.menuType !== 'searchable') {
19654                return forCollection(columns, items);
19655            }
19656            else {
19657                return menuLayout.searchMode.searchMode === 'search-with-field'
19658                    ? forCollectionWithSearchField(columns, items, menuLayout.searchMode)
19659                    : forCollectionWithSearchResults(columns, items);
19660            }
19661        };
19662        if (menuLayout.menuType === 'color') {
19663            const structure = forSwatch(columns);
19664            return {
19665                value,
19666                dom: structure.dom,
19667                components: structure.components,
19668                items
19669            };
19670        }
19671        else if (menuLayout.menuType === 'imageselector' && columns !== 'auto') {
19672            const structure = forImageSelector(columns);
19673            return {
19674                value,
19675                dom: structure.dom,
19676                components: structure.components,
19677                items
19678            };
19679        }
19680        else if (menuLayout.menuType === 'normal' && columns === 'auto') {
19681            const structure = forCollection(columns, items);
19682            return {
19683                value,
19684                dom: structure.dom,
19685                components: structure.components,
19686                items
19687            };
19688        }
19689        else if (menuLayout.menuType === 'normal' || menuLayout.menuType === 'searchable') {
19690            const structure = getNormalStructure();
19691            return {
19692                value,
19693                dom: structure.dom,
19694                components: structure.components,
19695                items
19696            };
19697        }
19698        else if (menuLayout.menuType === 'listpreview' && columns !== 'auto') {
19699            const structure = forToolbar(columns);
19700            return {
19701                value,
19702                dom: structure.dom,
19703                components: structure.components,
19704                items
19705            };
19706        }
19707        else {
19708            return {
19709                value,
19710                dom: dom(hasIcons, columns, menuLayout.menuType),
19711                components: components,
19712                items
19713            };
19714        }
19715    };
19716
19717    const type = requiredString('type');
19718    const name = requiredString('name');
19719    const label = requiredString('label');
19720    const text = requiredString('text');
19721    const title = requiredString('title');
19722    const icon = requiredString('icon');
19723    const url = requiredString('url');
19724    const value = requiredString('value');
19725    const fetch = requiredFunction('fetch');
19726    const getSubmenuItems = requiredFunction('getSubmenuItems');
19727    const onAction = requiredFunction('onAction');
19728    const onItemAction = requiredFunction('onItemAction');
19729    const onSetup = defaultedFunction('onSetup', () => noop);
19730    const optionalName = optionString('name');
19731    const optionalText = optionString('text');
19732    const optionalRole = optionString('role');
19733    const optionalIcon = optionString('icon');
19734    const optionalTooltip = optionString('tooltip');
19735    const optionalChevronTooltip = optionString('chevronTooltip');
19736    const optionalLabel = optionString('label');
19737    const optionalShortcut = optionString('shortcut');
19738    const optionalSelect = optionFunction('select');
19739    const active = defaultedBoolean('active', false);
19740    const borderless = defaultedBoolean('borderless', false);
19741    const enabled = defaultedBoolean('enabled', true);
19742    const primary = defaultedBoolean('primary', false);
19743    const defaultedColumns = (num) => defaulted('columns', num);
19744    const defaultedMeta = defaulted('meta', {});
19745    const defaultedOnAction = defaultedFunction('onAction', noop);
19746    const defaultedType = (type) => defaultedString('type', type);
19747    const generatedName = (namePrefix) => field$1('name', 'name', defaultedThunk(() => generate$6(`${namePrefix}-name`)), string);
19748    const generatedValue = (valuePrefix) => field$1('value', 'value', defaultedThunk(() => generate$6(`${valuePrefix}-value`)), anyValue());
19749
19750    const alertBannerFields = [
19751        type,
19752        text,
19753        requiredStringEnum('level', ['info', 'warn', 'error', 'success']),
19754        icon,
19755        defaulted('url', '')
19756    ];
19757    const alertBannerSchema = objOf(alertBannerFields);
19758
19759    const createBarFields = (itemsField) => [
19760        type,
19761        itemsField
19762    ];
19763
19764    const buttonFields = [
19765        type,
19766        text,
19767        enabled,
19768        generatedName('button'),
19769        optionalIcon,
19770        borderless,
19771        // this should be defaulted to `secondary` but the implementation needs to manage the deprecation
19772        optionStringEnum('buttonType', ['primary', 'secondary', 'toolbar']),
19773        // this should be removed, but must live here because FieldSchema doesn't have a way to manage deprecated fields
19774        primary,
19775        defaultedString('context', 'mode:design')
19776    ];
19777    const buttonSchema = objOf(buttonFields);
19778
19779    const formComponentFields = [
19780        type,
19781        name
19782    ];
19783    const formComponentWithLabelFields = formComponentFields.concat([
19784        optionalLabel
19785    ]);
19786
19787    const checkboxFields = formComponentFields.concat([
19788        label,
19789        enabled,
19790        defaultedString('context', 'mode:design')
19791    ]);
19792    const checkboxSchema = objOf(checkboxFields);
19793    const checkboxDataProcessor = boolean;
19794
19795    const collectionFields = formComponentWithLabelFields.concat([
19796        defaultedColumns('auto'),
19797        defaultedString('context', 'mode:design')
19798    ]);
19799    const collectionSchema = objOf(collectionFields);
19800    // TODO: Make type for CollectionItem
19801    const collectionDataProcessor = arrOfObj([
19802        value,
19803        text,
19804        icon
19805    ]);
19806
19807    const colorInputFields = formComponentWithLabelFields.concat([
19808        defaultedString('storageKey', 'default'),
19809        defaultedString('context', 'mode:design'),
19810    ]);
19811    const colorInputSchema = objOf(colorInputFields);
19812    const colorInputDataProcessor = string;
19813
19814    const colorPickerFields = formComponentWithLabelFields;
19815    const colorPickerSchema = objOf(colorPickerFields);
19816    const colorPickerDataProcessor = string;
19817
19818    const customEditorFields = formComponentFields.concat([
19819        defaultedString('tag', 'textarea'),
19820        requiredString('scriptId'),
19821        requiredString('scriptUrl'),
19822        optionFunction('onFocus'),
19823        defaultedPostMsg('settings', undefined)
19824    ]);
19825    const customEditorFieldsOld = formComponentFields.concat([
19826        defaultedString('tag', 'textarea'),
19827        requiredFunction('init')
19828    ]);
19829    const customEditorSchema = valueOf((v) => asRaw('customeditor.old', objOfOnly(customEditorFieldsOld), v).orThunk(() => asRaw('customeditor.new', objOfOnly(customEditorFields), v)));
19830    const customEditorDataProcessor = string;
19831
19832    const commonMenuItemFields = [
19833        enabled,
19834        optionalText,
19835        optionalRole,
19836        optionalShortcut,
19837        generatedValue('menuitem'),
19838        defaultedMeta,
19839        defaultedString('context', 'mode:design')
19840    ];
19841
19842    const dialogToggleMenuItemSchema = objOf([
19843        type,
19844        name
19845    ].concat(commonMenuItemFields));
19846    const dialogToggleMenuItemDataProcessor = boolean;
19847
19848    const baseFooterButtonFields = [
19849        generatedName('button'),
19850        optionalIcon,
19851        defaultedStringEnum('align', 'end', ['start', 'end']),
19852        // this should be removed, but must live here because FieldSchema doesn't have a way to manage deprecated fields
19853        primary,
19854        enabled,
19855        // this should be defaulted to `secondary` but the implementation needs to manage the deprecation
19856        optionStringEnum('buttonType', ['primary', 'secondary']),
19857        defaultedString('context', 'mode:design')
19858    ];
19859    const dialogFooterButtonFields = [
19860        ...baseFooterButtonFields,
19861        text
19862    ];
19863    const normalFooterButtonFields = [
19864        requiredStringEnum('type', ['submit', 'cancel', 'custom']),
19865        ...dialogFooterButtonFields
19866    ];
19867    const menuFooterButtonFields = [
19868        requiredStringEnum('type', ['menu']),
19869        optionalText,
19870        optionalTooltip,
19871        optionalIcon,
19872        requiredArrayOf('items', dialogToggleMenuItemSchema),
19873        ...baseFooterButtonFields
19874    ];
19875    const toggleButtonSpecFields = [
19876        ...baseFooterButtonFields,
19877        requiredStringEnum('type', ['togglebutton']),
19878        optionalTooltip,
19879        optionalIcon,
19880        optionalText,
19881        defaultedBoolean('active', false)
19882    ];
19883    const dialogFooterButtonSchema = choose$1('type', {
19884        submit: normalFooterButtonFields,
19885        cancel: normalFooterButtonFields,
19886        custom: normalFooterButtonFields,
19887        menu: menuFooterButtonFields,
19888        togglebutton: toggleButtonSpecFields
19889    });
19890
19891    const dropZoneFields = formComponentWithLabelFields.concat([
19892        defaultedString('context', 'mode:design'),
19893        optionString('dropAreaLabel'),
19894        optionString('buttonLabel'),
19895        optionString('allowedFileTypes'),
19896        optionArrayOf('allowedFileExtensions', string),
19897        defaultedFunction('onInvalidFiles', () => Promise.resolve())
19898    ]);
19899    const dropZoneSchema = objOf(dropZoneFields);
19900    const dropZoneDataProcessor = arrOfVal();
19901
19902    const createGridFields = (itemsField) => [
19903        type,
19904        requiredNumber('columns'),
19905        itemsField
19906    ];
19907
19908    const htmlPanelFields = [
19909        type,
19910        requiredString('html'),
19911        defaultedStringEnum('presets', 'presentation', ['presentation', 'document']),
19912        defaultedFunction('onInit', noop),
19913        defaultedBoolean('stretched', false),
19914    ];
19915    const htmlPanelSchema = objOf(htmlPanelFields);
19916
19917    const iframeFields = formComponentWithLabelFields.concat([
19918        defaultedBoolean('border', false),
19919        defaultedBoolean('sandboxed', true),
19920        defaultedBoolean('streamContent', false),
19921        defaultedBoolean('transparent', true)
19922    ]);
19923    const iframeSchema = objOf(iframeFields);
19924    const iframeDataProcessor = string;
19925
19926    const imagePreviewSchema = objOf(formComponentFields.concat([
19927        optionString('height'),
19928    ]));
19929    const imagePreviewDataProcessor = objOf([
19930        requiredString('url'),
19931        optionNumber('zoom'),
19932        optionNumber('cachedWidth'),
19933        optionNumber('cachedHeight'),
19934    ]);
19935
19936    const inputFields = formComponentWithLabelFields.concat([
19937        optionString('inputMode'),
19938        optionString('placeholder'),
19939        defaultedBoolean('maximized', false),
19940        enabled,
19941        defaultedString('context', 'mode:design'),
19942    ]);
19943    const inputSchema = objOf(inputFields);
19944    const inputDataProcessor = string;
19945
19946    const createLabelFields = (itemsField) => [
19947        type,
19948        label,
19949        itemsField,
19950        defaultedStringEnum('align', 'start', ['start', 'center', 'end']),
19951        optionString('for')
19952    ];
19953
19954    const listBoxSingleItemFields = [
19955        text,
19956        value
19957    ];
19958    const listBoxNestedItemFields = [
19959        text,
19960        requiredArrayOf('items', thunkOf('items', () => listBoxItemSchema))
19961    ];
19962    const listBoxItemSchema = oneOf([
19963        objOf(listBoxSingleItemFields),
19964        objOf(listBoxNestedItemFields)
19965    ]);
19966    const listBoxFields = formComponentWithLabelFields.concat([
19967        requiredArrayOf('items', listBoxItemSchema),
19968        enabled,
19969        defaultedString('context', 'mode:design')
19970    ]);
19971    const listBoxSchema = objOf(listBoxFields);
19972    const listBoxDataProcessor = string;
19973
19974    const selectBoxFields = formComponentWithLabelFields.concat([
19975        requiredArrayOfObj('items', [
19976            text,
19977            value
19978        ]),
19979        defaultedNumber('size', 1),
19980        enabled,
19981        defaultedString('context', 'mode:design')
19982    ]);
19983    const selectBoxSchema = objOf(selectBoxFields);
19984    const selectBoxDataProcessor = string;
19985
19986    const sizeInputFields = formComponentWithLabelFields.concat([
19987        defaultedBoolean('constrain', true),
19988        enabled,
19989        defaultedString('context', 'mode:design')
19990    ]);
19991    const sizeInputSchema = objOf(sizeInputFields);
19992    const sizeInputDataProcessor = objOf([
19993        requiredString('width'),
19994        requiredString('height')
19995    ]);
19996
19997    const sliderFields = formComponentFields.concat([
19998        label,
19999        defaultedNumber('min', 0),
20000        defaultedNumber('max', 0),
20001    ]);
20002    const sliderSchema = objOf(sliderFields);
20003    const sliderInputDataProcessor = number;
20004
20005    const tableFields = [
20006        type,
20007        requiredArrayOf('header', string),
20008        requiredArrayOf('cells', arrOf(string))
20009    ];
20010    const tableSchema = objOf(tableFields);
20011
20012    const textAreaFields = formComponentWithLabelFields.concat([
20013        optionString('placeholder'),
20014        defaultedBoolean('maximized', false),
20015        enabled,
20016        defaultedString('context', 'mode:design'),
20017        optionBoolean('spellcheck'),
20018    ]);
20019    const textAreaSchema = objOf(textAreaFields);
20020    const textAreaDataProcessor = string;
20021
20022    const baseMenuButtonFields = [
20023        defaultedString('buttonType', 'default'),
20024        optionString('text'),
20025        optionString('tooltip'),
20026        optionString('icon'),
20027        defaultedOf('search', false, 
20028        // So our boulder validation are:
20029        // a) boolean -> we need to map it into an Option
20030        // b) object -> we need to map it into a Some
20031        oneOf([
20032            // Unfortunately, due to objOf not checking to see that the
20033            // input is an object, the boolean check MUST be first
20034            boolean,
20035            objOf([
20036                optionString('placeholder')
20037            ])
20038        ], 
20039        // This function allows you to standardise the output.
20040        (x) => {
20041            if (isBoolean(x)) {
20042                return x ? Optional.some({ placeholder: Optional.none() }) : Optional.none();
20043            }
20044            else {
20045                return Optional.some(x);
20046            }
20047        })),
20048        requiredFunction('fetch'),
20049        defaultedFunction('onSetup', () => noop),
20050        defaultedString('context', 'mode:design')
20051    ];
20052
20053    const MenuButtonSchema = objOf([
20054        type,
20055        ...baseMenuButtonFields
20056    ]);
20057    const createMenuButton = (spec) => asRaw('menubutton', MenuButtonSchema, spec);
20058
20059    const baseTreeItemFields = [
20060        requiredStringEnum('type', ['directory', 'leaf']),
20061        title,
20062        requiredString('id'),
20063        optionOf('menu', MenuButtonSchema),
20064        optionString('customStateIcon'),
20065        optionString('customStateIconTooltip'),
20066    ];
20067    const treeItemLeafFields = baseTreeItemFields;
20068    const treeItemLeafSchema = objOf(treeItemLeafFields);
20069    const treeItemDirectoryFields = baseTreeItemFields.concat([
20070        requiredArrayOf('children', thunkOf('children', () => {
20071            return choose$2('type', {
20072                directory: treeItemDirectorySchema,
20073                leaf: treeItemLeafSchema,
20074            });
20075        })),
20076    ]);
20077    const treeItemDirectorySchema = objOf(treeItemDirectoryFields);
20078    const treeItemSchema = choose$2('type', {
20079        directory: treeItemDirectorySchema,
20080        leaf: treeItemLeafSchema,
20081    });
20082    const treeFields = [
20083        type,
20084        requiredArrayOf('items', treeItemSchema),
20085        optionFunction('onLeafAction'),
20086        optionFunction('onToggleExpand'),
20087        defaultedArrayOf('defaultExpandedIds', [], string),
20088        optionString('defaultSelectedId'),
20089    ];
20090    const treeSchema = objOf(treeFields);
20091
20092    const urlInputFields = formComponentWithLabelFields.concat([
20093        defaultedStringEnum('filetype', 'file', ['image', 'media', 'file']),
20094        enabled,
20095        optionString('picker_text'),
20096        defaultedString('context', 'mode:design')
20097    ]);
20098    const urlInputSchema = objOf(urlInputFields);
20099    const urlInputDataProcessor = objOf([
20100        value,
20101        defaultedMeta
20102    ]);
20103
20104    const createItemsField = (name) => field$1('items', 'items', required$2(), arrOf(valueOf((v) => asRaw(`Checking item of ${name}`, itemSchema$1, v).fold((sErr) => Result.error(formatError(sErr)), (passValue) => Result.value(passValue)))));
20105    // We're using a thunk here so we can refer to panel fields
20106    const itemSchema$1 = valueThunk(() => choose$2('type', {
20107        alertbanner: alertBannerSchema,
20108        bar: objOf(createBarFields(createItemsField('bar'))),
20109        button: buttonSchema,
20110        checkbox: checkboxSchema,
20111        colorinput: colorInputSchema,
20112        colorpicker: colorPickerSchema,
20113        dropzone: dropZoneSchema,
20114        grid: objOf(createGridFields(createItemsField('grid'))),
20115        iframe: iframeSchema,
20116        input: inputSchema,
20117        listbox: listBoxSchema,
20118        selectbox: selectBoxSchema,
20119        sizeinput: sizeInputSchema,
20120        slider: sliderSchema,
20121        textarea: textAreaSchema,
20122        urlinput: urlInputSchema,
20123        customeditor: customEditorSchema,
20124        htmlpanel: htmlPanelSchema,
20125        imagepreview: imagePreviewSchema,
20126        collection: collectionSchema,
20127        label: objOf(createLabelFields(createItemsField('label'))),
20128        table: tableSchema,
20129        tree: treeSchema,
20130        panel: panelSchema
20131    }));
20132    const panelFields = [
20133        type,
20134        defaulted('classes', []),
20135        requiredArrayOf('items', itemSchema$1)
20136    ];
20137    const panelSchema = objOf(panelFields);
20138
20139    const tabFields = [
20140        generatedName('tab'),
20141        title,
20142        requiredArrayOf('items', itemSchema$1)
20143    ];
20144    const tabPanelFields = [
20145        type,
20146        requiredArrayOfObj('tabs', tabFields),
20147        defaultedBoolean('dynamicHeight', false)
20148    ];
20149    const tabPanelSchema = objOf(tabPanelFields);
20150
20151    const dialogButtonFields = dialogFooterButtonFields;
20152    const dialogButtonSchema = dialogFooterButtonSchema;
20153    const dialogSchema = objOf([
20154        requiredString('title'),
20155        requiredOf('body', choose$2('type', {
20156            panel: panelSchema,
20157            tabpanel: tabPanelSchema
20158        })),
20159        defaultedString('size', 'normal'),
20160        defaultedArrayOf('buttons', [], dialogButtonSchema),
20161        defaulted('initialData', {}),
20162        defaultedFunction('onAction', noop),
20163        defaultedFunction('onChange', noop),
20164        defaultedFunction('onSubmit', noop),
20165        defaultedFunction('onClose', noop),
20166        defaultedFunction('onCancel', noop),
20167        defaultedFunction('onTabChange', noop)
20168    ]);
20169    const createDialog = (spec) => asRaw('dialog', dialogSchema, spec);
20170
20171    const urlDialogButtonSchema = objOf([
20172        requiredStringEnum('type', ['cancel', 'custom']),
20173        ...dialogButtonFields
20174    ]);
20175    const urlDialogSchema = objOf([
20176        requiredString('title'),
20177        requiredString('url'),
20178        optionNumber('height'),
20179        optionNumber('width'),
20180        optionArrayOf('buttons', urlDialogButtonSchema),
20181        defaultedFunction('onAction', noop),
20182        defaultedFunction('onCancel', noop),
20183        defaultedFunction('onClose', noop),
20184        defaultedFunction('onMessage', noop)
20185    ]);
20186    const createUrlDialog = (spec) => asRaw('dialog', urlDialogSchema, spec);
20187
20188    // eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
20189    const getAllObjects = (obj) => {
20190        if (isObject(obj)) {
20191            return [obj].concat(bind$3(values(obj), getAllObjects));
20192        }
20193        else if (isArray(obj)) {
20194            return bind$3(obj, getAllObjects);
20195        }
20196        else {
20197            return [];
20198        }
20199    };
20200
20201    const isNamedItem = (obj) => isString(obj.type) && isString(obj.name);
20202    const dataProcessors = {
20203        checkbox: checkboxDataProcessor,
20204        colorinput: colorInputDataProcessor,
20205        colorpicker: colorPickerDataProcessor,
20206        dropzone: dropZoneDataProcessor,
20207        input: inputDataProcessor,
20208        iframe: iframeDataProcessor,
20209        imagepreview: imagePreviewDataProcessor,
20210        selectbox: selectBoxDataProcessor,
20211        sizeinput: sizeInputDataProcessor,
20212        slider: sliderInputDataProcessor,
20213        listbox: listBoxDataProcessor,
20214        size: sizeInputDataProcessor,
20215        textarea: textAreaDataProcessor,
20216        urlinput: urlInputDataProcessor,
20217        customeditor: customEditorDataProcessor,
20218        collection: collectionDataProcessor,
20219        togglemenuitem: dialogToggleMenuItemDataProcessor
20220    };
20221    const getDataProcessor = (item) => Optional.from(dataProcessors[item.type]);
20222    const getNamedItems = (structure) => filter$2(getAllObjects(structure), isNamedItem);
20223
20224    const createDataValidator = (structure) => {
20225        const namedItems = getNamedItems(structure);
20226        const fields = bind$3(namedItems, (item) => getDataProcessor(item).fold(() => [], (schema) => [requiredOf(item.name, schema)]));
20227        return objOf(fields);
20228    };
20229
20230    const extract = (structure) => {
20231        const internalDialog = getOrDie(createDialog(structure));
20232        const dataValidator = createDataValidator(structure);
20233        // We used to validate data here, but it's done when loading the dialog in tinymce
20234        const initialData = structure.initialData ?? {};
20235        return {
20236            internalDialog,
20237            dataValidator,
20238            initialData
20239        };
20240    };
20241    const DialogManager = {
20242        open: (factory, structure) => {
20243            const extraction = extract(structure);
20244            return factory(extraction.internalDialog, extraction.initialData, extraction.dataValidator);
20245        },
20246        openUrl: (factory, structure) => {
20247            const internalDialog = getOrDie(createUrlDialog(structure));
20248            return factory(internalDialog);
20249        },
20250        redial: (structure) => extract(structure)
20251    };
20252
20253    const separatorMenuItemSchema = objOf([
20254        type,
20255        optionalText
20256    ]);
20257    const createSeparatorMenuItem = (spec) => asRaw('separatormenuitem', separatorMenuItemSchema, spec);
20258
20259    const autocompleterItemSchema = objOf([
20260        // Currently, autocomplete items don't support configuring type, active, disabled, meta
20261        defaultedType('autocompleteitem'),
20262        active,
20263        enabled,
20264        defaultedMeta,
20265        value,
20266        optionalText,
20267        optionalIcon
20268    ]);
20269    objOf([
20270        type,
20271        requiredString('trigger'),
20272        defaultedNumber('minChars', 1),
20273        defaultedColumns(1),
20274        defaultedNumber('maxResults', 10),
20275        optionFunction('matches'),
20276        fetch,
20277        onAction,
20278        defaultedArrayOf('highlightOn', [], string)
20279    ]);
20280    const createSeparatorItem = (spec) => asRaw('Autocompleter.Separator', separatorMenuItemSchema, spec);
20281    const createAutocompleterItem = (spec) => asRaw('Autocompleter.Item', autocompleterItemSchema, spec);
20282
20283    const baseToolbarButtonFields = [
20284        enabled,
20285        optionalTooltip,
20286        optionalIcon,
20287        optionalText,
20288        onSetup,
20289        defaultedString('context', 'mode:design')
20290    ];
20291    const toolbarButtonSchema = objOf([
20292        type,
20293        onAction,
20294        optionalShortcut
20295    ].concat(baseToolbarButtonFields));
20296    const createToolbarButton = (spec) => asRaw('toolbarbutton', toolbarButtonSchema, spec);
20297
20298    const baseToolbarToggleButtonFields = [
20299        active
20300    ].concat(baseToolbarButtonFields);
20301    const toggleButtonSchema = objOf(baseToolbarToggleButtonFields.concat([
20302        type,
20303        onAction,
20304        optionalShortcut
20305    ]));
20306    const createToggleButton = (spec) => asRaw('ToggleButton', toggleButtonSchema, spec);
20307
20308    const contextBarFields = [
20309        defaultedFunction('predicate', never),
20310        defaultedStringEnum('scope', 'node', ['node', 'editor']),
20311        defaultedStringEnum('position', 'selection', ['node', 'selection', 'line'])
20312    ];
20313
20314    const contextButtonFields = baseToolbarButtonFields.concat([
20315        defaultedType('contextformbutton'),
20316        defaultedString('align', 'end'),
20317        primary,
20318        onAction,
20319        customField('original', identity)
20320    ]);
20321    const contextToggleButtonFields = baseToolbarToggleButtonFields.concat([
20322        defaultedType('contextformbutton'),
20323        defaultedString('align', 'end'),
20324        primary,
20325        onAction,
20326        customField('original', identity)
20327    ]);
20328    const launchButtonFields$1 = baseToolbarButtonFields.concat([
20329        defaultedType('contextformbutton')
20330    ]);
20331    const launchToggleButtonFields = baseToolbarToggleButtonFields.concat([
20332        defaultedType('contextformtogglebutton')
20333    ]);
20334    const toggleOrNormal = choose$1('type', {
20335        contextformbutton: contextButtonFields,
20336        contextformtogglebutton: contextToggleButtonFields
20337    });
20338    const baseContextFormFields = [
20339        optionalLabel,
20340        requiredArrayOf('commands', toggleOrNormal),
20341        optionOf('launch', choose$1('type', {
20342            contextformbutton: launchButtonFields$1,
20343            contextformtogglebutton: launchToggleButtonFields
20344        })),
20345        defaultedFunction('onInput', noop),
20346        defaultedFunction('onSetup', noop)
20347    ];
20348    const contextFormFields = [
20349        ...contextBarFields,
20350        ...baseContextFormFields,
20351        requiredStringEnum('type', ['contextform']),
20352        defaultedFunction('initValue', constant$1('')),
20353        optionString('placeholder'),
20354    ];
20355    const contextSliderFormFields = [
20356        ...contextBarFields,
20357        ...baseContextFormFields,
20358        requiredStringEnum('type', ['contextsliderform']),
20359        defaultedFunction('initValue', constant$1(0)),
20360        defaultedFunction('min', constant$1(0)),
20361        defaultedFunction('max', constant$1(100))
20362    ];
20363    const contextSizeInputFormFields = [
20364        ...contextBarFields,
20365        ...baseContextFormFields,
20366        requiredStringEnum('type', ['contextsizeinputform']),
20367        defaultedFunction('initValue', constant$1({ width: '', height: '' }))
20368    ];
20369    const contextFormSchema = choose$1('type', {
20370        contextform: contextFormFields,
20371        contextsliderform: contextSliderFormFields,
20372        contextsizeinputform: contextSizeInputFormFields
20373    });
20374    const createContextForm = (spec) => asRaw('ContextForm', contextFormSchema, spec);
20375
20376    const launchButtonFields = baseToolbarButtonFields.concat([
20377        defaultedType('contexttoolbarbutton')
20378    ]);
20379    const contextToolbarSchema = objOf([
20380        defaultedType('contexttoolbar'),
20381        optionObjOf('launch', launchButtonFields),
20382        requiredOf('items', oneOf([
20383            string,
20384            arrOfObj([
20385                optionString('name'),
20386                optionString('label'),
20387                requiredArrayOf('items', string)
20388            ])
20389        ])),
20390    ].concat(contextBarFields));
20391    const toolbarGroupBackToSpec = (toolbarGroup) => ({
20392        name: toolbarGroup.name.getOrUndefined(),
20393        label: toolbarGroup.label.getOrUndefined(),
20394        items: toolbarGroup.items
20395    });
20396    const contextToolbarToSpec = (contextToolbar) => ({
20397        ...contextToolbar,
20398        launch: contextToolbar.launch.getOrUndefined(),
20399        items: isString(contextToolbar.items) ? contextToolbar.items : map$2(contextToolbar.items, toolbarGroupBackToSpec)
20400    });
20401    const createContextToolbar = (spec) => asRaw('ContextToolbar', contextToolbarSchema, spec);
20402
20403    const cardImageFields = [
20404        type,
20405        requiredString('src'),
20406        optionString('alt'),
20407        defaultedArrayOf('classes', [], string)
20408    ];
20409    const cardImageSchema = objOf(cardImageFields);
20410
20411    const cardTextFields = [
20412        type,
20413        text,
20414        optionalName,
20415        defaultedArrayOf('classes', ['tox-collection__item-label'], string)
20416    ];
20417    const cardTextSchema = objOf(cardTextFields);
20418
20419    const itemSchema = valueThunk(() => choose$2('type', {
20420        cardimage: cardImageSchema,
20421        cardtext: cardTextSchema,
20422        cardcontainer: cardContainerSchema
20423    }));
20424    const cardContainerSchema = objOf([
20425        type,
20426        defaultedString('direction', 'horizontal'),
20427        defaultedString('align', 'left'),
20428        defaultedString('valign', 'middle'),
20429        requiredArrayOf('items', itemSchema)
20430    ]);
20431
20432    const cardMenuItemSchema = objOf([
20433        type,
20434        optionalLabel,
20435        requiredArrayOf('items', itemSchema),
20436        onSetup,
20437        defaultedOnAction
20438    ].concat(commonMenuItemFields));
20439    const createCardMenuItem = (spec) => asRaw('cardmenuitem', cardMenuItemSchema, spec);
20440
20441    const choiceMenuItemSchema = objOf([
20442        type,
20443        active,
20444        optionalIcon,
20445        optionalLabel
20446    ].concat(commonMenuItemFields));
20447    const createChoiceMenuItem = (spec) => asRaw('choicemenuitem', choiceMenuItemSchema, spec);
20448
20449    const baseFields = [
20450        type,
20451        requiredString('fancytype'),
20452        defaultedOnAction
20453    ];
20454    const insertTableFields = [
20455        defaulted('initData', {})
20456    ].concat(baseFields);
20457    const colorSwatchFields = [
20458        optionFunction('select'),
20459        defaultedObjOf('initData', {}, [
20460            defaultedBoolean('allowCustomColors', true),
20461            defaultedString('storageKey', 'default'),
20462            // Note: We don't validate the colors as they are instead validated by choiceschema when rendering
20463            optionArrayOf('colors', anyValue())
20464        ])
20465    ].concat(baseFields);
20466    const imageSelectFields = [
20467        optionFunction('select'),
20468        requiredObjOf('initData', [
20469            requiredNumber('columns'),
20470            // Note: We don't validate the items as they are instead validated by imageMenuItemSchema when rendering
20471            defaultedArrayOf('items', [], anyValue())
20472        ])
20473    ].concat(baseFields);
20474    const fancyMenuItemSchema = choose$1('fancytype', {
20475        inserttable: insertTableFields,
20476        colorswatch: colorSwatchFields,
20477        imageselect: imageSelectFields
20478    });
20479    const createFancyMenuItem = (spec) => asRaw('fancymenuitem', fancyMenuItemSchema, spec);
20480
20481    const imageMenuItemSchema = objOf([
20482        type,
20483        active,
20484        url,
20485        optionalLabel,
20486        optionalTooltip
20487    ].concat(commonMenuItemFields));
20488    const resetImageItemSchema = objOf([
20489        type,
20490        active,
20491        icon,
20492        label,
20493        optionalTooltip,
20494        value
20495    ].concat(commonMenuItemFields));
20496    const createImageMenuItem = (spec) => asRaw('imagemenuitem', imageMenuItemSchema, spec);
20497    const createResetImageItem = (spec) => asRaw('resetimageitem', resetImageItemSchema, spec);
20498
20499    const menuItemSchema = objOf([
20500        type,
20501        onSetup,
20502        defaultedOnAction,
20503        optionalIcon
20504    ].concat(commonMenuItemFields));
20505    const createMenuItem = (spec) => asRaw('menuitem', menuItemSchema, spec);
20506
20507    const nestedMenuItemSchema = objOf([
20508        type,
20509        getSubmenuItems,
20510        onSetup,
20511        optionalIcon
20512    ].concat(commonMenuItemFields));
20513    const createNestedMenuItem = (spec) => asRaw('nestedmenuitem', nestedMenuItemSchema, spec);
20514
20515    const toggleMenuItemSchema = objOf([
20516        type,
20517        optionalIcon,
20518        active,
20519        onSetup,
20520        onAction
20521    ].concat(commonMenuItemFields));
20522    const createToggleMenuItem = (spec) => asRaw('togglemenuitem', toggleMenuItemSchema, spec);
20523
20524    const sidebarSchema = objOf([
20525        optionalIcon,
20526        optionalTooltip,
20527        defaultedFunction('onShow', noop),
20528        defaultedFunction('onHide', noop),
20529        onSetup
20530    ]);
20531    const createSidebar = (spec) => asRaw('sidebar', sidebarSchema, spec);
20532
20533    const groupToolbarButtonSchema = objOf([
20534        type,
20535        requiredOf('items', oneOf([
20536            arrOfObj([
20537                name,
20538                requiredArrayOf('items', string)
20539            ]),
20540            string
20541        ]))
20542    ].concat(baseToolbarButtonFields));
20543    const createGroupToolbarButton = (spec) => asRaw('GroupToolbarButton', groupToolbarButtonSchema, spec);
20544
20545    const splitButtonSchema = objOf([
20546        type,
20547        optionalTooltip,
20548        optionalChevronTooltip,
20549        optionalIcon,
20550        optionalText,
20551        optionalSelect,
20552        fetch,
20553        onSetup,
20554        // TODO: Validate the allowed presets
20555        defaultedStringEnum('presets', 'normal', ['normal', 'color', 'listpreview']),
20556        defaultedColumns(1),
20557        onAction,
20558        onItemAction,
20559        defaultedString('context', 'mode:design')
20560    ]);
20561    const createSplitButton = (spec) => asRaw('SplitButton', splitButtonSchema, spec);
20562
20563    const baseButtonFields = [
20564        optionalText,
20565        optionalIcon,
20566        optionString('tooltip'),
20567        defaultedStringEnum('buttonType', 'secondary', ['primary', 'secondary']),
20568        defaultedBoolean('borderless', false),
20569        requiredFunction('onAction'),
20570        defaultedString('context', 'mode:design')
20571    ];
20572    const normalButtonFields = [
20573        ...baseButtonFields,
20574        text,
20575        requiredStringEnum('type', ['button']),
20576    ];
20577    const toggleButtonFields = [
20578        ...baseButtonFields,
20579        defaultedBoolean('active', false),
20580        requiredStringEnum('type', ['togglebutton'])
20581    ];
20582    const schemaWithoutGroupButton = {
20583        button: normalButtonFields,
20584        togglebutton: toggleButtonFields,
20585    };
20586    const groupFields = [
20587        requiredStringEnum('type', ['group']),
20588        defaultedArrayOf('buttons', [], choose$1('type', schemaWithoutGroupButton))
20589    ];
20590    const viewButtonSchema = choose$1('type', {
20591        ...schemaWithoutGroupButton,
20592        group: groupFields
20593    });
20594
20595    const viewSchema = objOf([
20596        defaultedArrayOf('buttons', [], viewButtonSchema),
20597        requiredFunction('onShow'),
20598        requiredFunction('onHide')
20599    ]);
20600    const createView = (spec) => asRaw('view', viewSchema, spec);
20601
20602    const detectSize = (comp, margin, selectorClass) => {
20603        const descendants$1 = descendants(comp.element, '.' + selectorClass);
20604        // TODO: This seems to cause performance issues in the emoji dialog
20605        if (descendants$1.length > 0) {
20606            const columnLength = findIndex$1(descendants$1, (c) => {
20607                const thisTop = c.dom.getBoundingClientRect().top;
20608                const cTop = descendants$1[0].dom.getBoundingClientRect().top;
20609                return Math.abs(thisTop - cTop) > margin;
20610            }).getOr(descendants$1.length);
20611            return Optional.some({
20612                numColumns: columnLength,
20613                numRows: Math.ceil(descendants$1.length / columnLength)
20614            });
20615        }
20616        else {
20617            return Optional.none();
20618        }
20619    };
20620
20621    // Consider moving to alloy once it takes shape.
20622    const namedEvents = (name, handlers) => derive$1([
20623        config(name, handlers)
20624    ]);
20625    const unnamedEvents = (handlers) => namedEvents(generate$6('unnamed-events'), handlers);
20626    const SimpleBehaviours = {
20627        namedEvents,
20628        unnamedEvents
20629    };
20630
20631    const item = (disabled) => Disabling.config({
20632        disabled,
20633        disableClass: 'tox-collection__item--state-disabled'
20634    });
20635    const button = (disabled) => Disabling.config({
20636        disabled
20637    });
20638    const splitButton = (disabled) => Disabling.config({
20639        disabled,
20640        disableClass: 'tox-tbtn--disabled'
20641    });
20642    const toolbarButton = (disabled) => Disabling.config({
20643        disabled,
20644        disableClass: 'tox-tbtn--disabled',
20645        useNative: false
20646    });
20647    const DisablingConfigs = {
20648        item,
20649        button,
20650        splitButton,
20651        toolbarButton
20652    };
20653
20654    const runWithApi = (info, comp) => {
20655        const api = info.getApi(comp);
20656        return (f) => {
20657            f(api);
20658        };
20659    };
20660    // These handlers are used for providing common onAttached and onDetached handlers.
20661    // Essentially, the `editorOffCell` is used store the onDestroy function returned
20662    // by onSetup. The reason onControlAttached doesn't create the cell itself, is because
20663    // it also has to be passed into onControlDetached. We could make this function return
20664    // the cell and the onAttachedHandler, but that would provide too much complexity.
20665    const onControlAttached = (info, editorOffCell) => runOnAttached((comp) => {
20666        if (isFunction(info.onBeforeSetup)) {
20667            info.onBeforeSetup(comp);
20668        }
20669        const run = runWithApi(info, comp);
20670        run((api) => {
20671            const onDestroy = info.onSetup(api);
20672            if (isFunction(onDestroy)) {
20673                editorOffCell.set(onDestroy);
20674            }
20675        });
20676    });
20677    const onControlDetached = (getApi, editorOffCell) => runOnDetached((comp) => runWithApi(getApi, comp)(editorOffCell.get()));
20678    const onContextFormControlDetached = (getApi, editorOffCell, valueState) => runOnDetached((comp) => {
20679        valueState.set(Representing.getValue(comp));
20680        return runWithApi(getApi, comp)(editorOffCell.get());
20681    });
20682
20683    const UiStateChannel = 'silver.uistate';
20684    const messageSetDisabled = 'setDisabled';
20685    const messageSetEnabled = 'setEnabled';
20686    const messageInit = 'init';
20687    const messageSwitchMode = 'switchmode';
20688    const modeContextMessages = [messageSwitchMode, messageInit];
20689    const broadcastEvents = (uiRefs, messageType) => {
20690        const outerContainer = uiRefs.mainUi.outerContainer;
20691        const motherships = [uiRefs.mainUi.mothership, ...uiRefs.uiMotherships];
20692        if (messageType === messageSetDisabled) {
20693            each$1(motherships, (m) => {
20694                m.broadcastOn([dismissPopups()], { target: outerContainer.element });
20695            });
20696        }
20697        each$1(motherships, (m) => {
20698            m.broadcastOn([UiStateChannel], messageType);
20699        });
20700    };
20701    const setupEventsForUi = (editor, uiRefs) => {
20702        editor.on('init SwitchMode', (event) => {
20703            broadcastEvents(uiRefs, event.type);
20704        });
20705        editor.on('DisabledStateChange', (event) => {
20706            if (!event.isDefaultPrevented()) {
20707                // When the event state indicates the editor is **enabled** (`event.state` is false),
20708                // we send an 'init' message instead of 'setEnabled' because the editor might be in read-only mode.
20709                // Sending 'setEnabled' would enable all the toolbar buttons, which is undesirable if the editor is read-only.
20710                const messageType = event.state ? messageSetDisabled : messageInit;
20711                broadcastEvents(uiRefs, messageType);
20712                // After refreshing the state of the buttons, trigger a NodeChange event.
20713                if (!event.state) {
20714                    editor.nodeChanged();
20715                }
20716            }
20717        });
20718        editor.on('NodeChange', (e) => {
20719            const messageType = editor.ui.isEnabled() ? e.type : messageSetDisabled;
20720            broadcastEvents(uiRefs, messageType);
20721        });
20722        if (isReadOnly(editor)) {
20723            editor.mode.set('readonly');
20724        }
20725    };
20726    const toggleOnReceive = (getContext) => Receiving.config({
20727        channels: {
20728            [UiStateChannel]: {
20729                onReceive: (comp, messageType) => {
20730                    if (messageType === messageSetDisabled || messageType === messageSetEnabled) {
20731                        Disabling.set(comp, messageType === messageSetDisabled);
20732                        return;
20733                    }
20734                    const { contextType, shouldDisable } = getContext();
20735                    if (contextType === 'mode' && !contains$2(modeContextMessages, messageType)) {
20736                        return;
20737                    }
20738                    Disabling.set(comp, shouldDisable);
20739                }
20740            }
20741        }
20742    });
20743
20744    // Perform `action` when an item is clicked on, close menus, and stop event
20745    const onMenuItemExecute = (info, itemResponse) => runOnExecute$1((comp, simulatedEvent) => {
20746        // If there is an action, run the action
20747        runWithApi(info, comp)(info.onAction);
20748        if (!info.triggersSubmenu && itemResponse === ItemResponse$1.CLOSE_ON_EXECUTE) {
20749            if (comp.getSystem().isConnected()) {
20750                emit(comp, sandboxClose());
20751            }
20752            simulatedEvent.stop();
20753        }
20754    });
20755    const menuItemEventOrder = {
20756        // TODO: use the constants provided by behaviours.
20757        [execute$5()]: ['disabling', 'alloy.base.behaviour', 'toggling', 'item-events']
20758    };
20759
20760    const componentRenderPipeline = cat;
20761    const renderCommonItem = (spec, structure, itemResponse, providersBackstage) => {
20762        const editorOffCell = Cell(noop);
20763        return {
20764            type: 'item',
20765            dom: structure.dom,
20766            components: componentRenderPipeline(structure.optComponents),
20767            data: spec.data,
20768            eventOrder: menuItemEventOrder,
20769            hasSubmenu: spec.triggersSubmenu,
20770            itemBehaviours: derive$1([
20771                config('item-events', [
20772                    onMenuItemExecute(spec, itemResponse),
20773                    onControlAttached(spec, editorOffCell),
20774                    onControlDetached(spec, editorOffCell)
20775                ]),
20776                DisablingConfigs.item(() => !spec.enabled || providersBackstage.checkUiComponentContext(spec.c
20776ontext).shouldDisable),
20777                toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context)),
20778                Replacing.config({})
20779            ].concat(spec.itemBehaviours))
20780        };
20781    };
20782    const buildData = (source) => ({
20783        value: source.value,
20784        meta: {
20785            text: source.text.getOr(''),
20786            ...source.meta
20787        }
20788    });
20789
20790    const renderImage$1 = (spec, imageUrl) => {
20791        const spinnerElement = SugarElement.fromTag('div');
20792        add$2(spinnerElement, 'tox-image-selector-loading-spinner');
20793        const addSpinnerElement = (loadingElement) => {
20794            add$2(loadingElement, 'tox-image-selector-loading-spinner-wrapper');
20795            append$2(loadingElement, spinnerElement);
20796        };
20797        const removeSpinnerElement = (loadingElement) => {
20798            remove$3(loadingElement, 'tox-image-selector-loading-spinner-wrapper');
20799            remove$7(spinnerElement);
20800        };
20801        return {
20802            dom: {
20803                tag: spec.tag,
20804                attributes: spec.attributes ?? {},
20805                classes: spec.classes,
20806            },
20807            components: [
20808                {
20809                    dom: {
20810                        tag: 'div',
20811                        classes: ['tox-image-selector-image-wrapper']
20812                    },
20813                    components: [
20814                        {
20815                            dom: {
20816                                tag: 'img',
20817                                attributes: { src: imageUrl },
20818                                classes: ['tox-image-selector-image-img']
20819                            }
20820                        },
20821                    ]
20822                },
20823                ...spec.checkMark.toArray()
20824            ],
20825            behaviours: derive$1([
20826                ...spec.behaviours ?? [],
20827                config('render-image-events', [
20828                    runOnAttached((component) => {
20829                        addSpinnerElement(component.element);
20830                        descendant(component.element, 'img').each((image$1) => {
20831                            image(image$1).catch((e) => {
20832                                // eslint-disable-next-line no-console
20833                                console.error(e);
20834                            }).finally(() => {
20835                                removeSpinnerElement(component.element);
20836                            });
20837                        });
20838                    })
20839                ]),
20840            ])
20841        };
20842    };
20843    const render$3 = (imageUrl, spec) => renderImage$1(spec, imageUrl);
20844
20845    // Converts shortcut format to Mac/PC variants
20846    // Note: This is different to the help shortcut converter, as it doesn't padd the + symbol with spaces
20847    //       so as to not take up large amounts of space in the menus
20848    const convertText = (source) => {
20849        const isMac = global$7.os.isMacOS() || global$7.os.isiOS();
20850        const mac = {
20851            alt: '\u2325',
20852            ctrl: '\u2303',
20853            shift: '\u21E7',
20854            meta: '\u2318',
20855            access: '\u2303\u2325'
20856        };
20857        const other = {
20858            meta: 'Ctrl',
20859            access: 'Shift+Alt'
20860        };
20861        const replace = isMac ? mac : other;
20862        const shortcut = source.split('+');
20863        const updated = map$2(shortcut, (segment) => {
20864            // search lowercase, but if not found use the original
20865            const search = segment.toLowerCase().trim();
20866            return has$2(replace, search) ? replace[search] : segment;
20867        });
20868        return isMac ? updated.join('') : updated.join('+');
20869    };
20870
20871    const renderIcon$2 = (name, icons, classes = [iconClass]) => render$4(name, { tag: 'div', classes }, icons);
20872    const renderText = (text) => ({
20873        dom: {
20874            tag: 'div',
20875            classes: [textClass]
20876        },
20877        components: [text$2(global$6.translate(text))]
20878    });
20879    const renderHtml = (html, classes) => ({
20880        dom: {
20881            tag: 'div',
20882            classes,
20883            innerHtml: html
20884        }
20885    });
20886    const renderStyledText = (style, text) => ({
20887        dom: {
20888            tag: 'div',
20889            classes: [textClass]
20890        },
20891        components: [
20892            {
20893                dom: {
20894                    tag: style.tag,
20895                    styles: style.styles
20896                },
20897                components: [text$2(global$6.translate(text))]
20898            }
20899        ]
20900    });
20901    const renderShortcut = (shortcut) => ({
20902        dom: {
20903            tag: 'div',
20904            classes: [accessoryClass]
20905        },
20906        components: [
20907            text$2(convertText(shortcut))
20908        ]
20909    });
20910    const renderCheckmark = (icons) => renderIcon$2('checkmark', icons, [checkmarkClass]);
20911    const renderSubmenuCaret = (icons) => renderIcon$2('chevron-right', icons, [caretClass]);
20912    const renderDownwardsCaret = (icons) => renderIcon$2('chevron-down', icons, [caretClass]);
20913    const renderContainer = (container, components) => {
20914        const directionClass = container.direction === 'vertical' ? containerColumnClass : containerRowClass;
20915        const alignClass = container.align === 'left' ? containerAlignLeftClass : containerAlignRightClass;
20916        const getValignClass = () => {
20917            switch (container.valign) {
20918                case 'top':
20919                    return containerValignTopClass;
20920                case 'middle':
20921                    return containerValignMiddleClass;
20922                case 'bottom':
20923                    return containerValignBottomClass;
20924            }
20925        };
20926        return {
20927            dom: {
20928                tag: 'div',
20929                classes: [
20930                    containerClass,
20931                    directionClass,
20932                    alignClass,
20933                    getValignClass()
20934                ]
20935            },
20936            components
20937        };
20938    };
20939    const renderImage = (src, classes, alt) => ({
20940        dom: {
20941            tag: 'img',
20942            classes,
20943            attributes: {
20944                src,
20945                alt: alt.getOr('')
20946            }
20947        }
20948    });
20949
20950    const renderColorStructure = (item, providerBackstage, fallbackIcon) => {
20951        const colorPickerCommand = 'custom';
20952        const removeColorCommand = 'remove';
20953        const itemValue = item.value;
20954        const iconSvg = item.iconContent.map((name) => getOr(name, providerBackstage.icons, fallbackIcon));
20955        const attributes = item.ariaLabel.map((al) => ({
20956            'aria-label': providerBackstage.translate(al),
20957            'data-mce-name': al
20958        })).getOr({});
20959        const getDom = () => {
20960            const common = colorClass;
20961            const icon = iconSvg.getOr('');
20962            const baseDom = {
20963                tag: 'div',
20964                attributes,
20965                classes: [common]
20966            };
20967            if (itemValue === colorPickerCommand) {
20968                return {
20969                    ...baseDom,
20970                    tag: 'button',
20971                    classes: [...baseDom.classes, 'tox-swatches__picker-btn'],
20972                    innerHtml: icon
20973                };
20974            }
20975            else if (itemValue === removeColorCommand) {
20976                return {
20977                    ...baseDom,
20978                    classes: [...baseDom.classes, 'tox-swatch--remove'],
20979                    innerHtml: icon
20980                };
20981            }
20982            else if (isNonNullable(itemValue)) {
20983                return {
20984                    ...baseDom,
20985                    attributes: {
20986                        ...baseDom.attributes,
20987                        'data-mce-color': itemValue
20988                    },
20989                    styles: {
20990                        'background-color': itemValue
20991                    },
20992                    innerHtml: icon
20993                };
20994            }
20995            else {
20996                return baseDom;
20997            }
20998        };
20999        return {
21000            dom: getDom(),
21001            optComponents: []
21002        };
21003    };
21004    const renderItemDomStructure = (ariaLabel, classes) => {
21005        const domTitle = ariaLabel.map((label) => ({
21006            attributes: {
21007                'id': generate$6('menu-item'),
21008                'aria-label': global$6.translate(label)
21009            }
21010        })).getOr({});
21011        return {
21012            tag: 'div',
21013            classes: [navClass, selectableClass].concat(classes),
21014            ...domTitle
21015        };
21016    };
21017    const createLabel = (label) => {
21018        return {
21019            dom: {
21020                tag: 'label'
21021            },
21022            components: [
21023                text$2(label)
21024            ]
21025        };
21026    };
21027    const renderNormalItemStructure = (info, providersBackstage, renderIcons, fallbackIcon) => {
21028        // TODO: TINY-3036 Work out a better way of dealing with custom icons
21029        const iconSpec = { tag: 'div', classes: [iconClass] };
21030        const renderIcon = (iconName) => render$4(iconName, iconSpec, providersBackstage.icons, fallbackIcon);
21031        const renderEmptyIcon = () => Optional.some({ dom: iconSpec });
21032        // Note: renderIcons indicates if any icons are present in the menu - if false then the icon column will not be present for the whole menu
21033        const leftIcon = renderIcons ? info.iconContent.map(renderIcon).orThunk(renderEmptyIcon) : Optional.none();
21034        // TINY-3345: Dedicated columns for icon and checkmark if applicable
21035        const checkmark = info.checkMark;
21036        // Style items and autocompleter both have meta. Need to branch on 
21036style
21037        // This could probably be more stable...
21038        const textRender = Optional.from(info.meta).fold(() => renderText, (meta) => has$2(meta, 'style') ? curry(renderStyledText, meta.style) : renderText);
21039        const content = info.htmlContent.fold(() => info.textContent.map(textRender), (html) => Optional.some(renderHtml(html, [textClass])));
21040        const menuItem = {
21041            dom: renderItemDomStructure(info.ariaLabel, []),
21042            optComponents: [
21043                leftIcon,
21044                content,
21045                info.shortcutContent.map(renderShortcut),
21046                checkmark,
21047                info.caret,
21048                info.labelContent.map(createLabel)
21049            ]
21050        };
21051        return menuItem;
21052    };
21053    const renderImgItemStructure = (info) => {
21054        const menuItem = {
21055            dom: renderItemDomStructure(info.ariaLabel, [imageSelectorClasll]),
21056            optComponents: [
21057                Optional.some(render$3(info.iconContent.getOrDie(), { tag: 'div', classes: [imageClass], checkMark: info.checkMark })),
21058                info.labelContent.map(createLabel)
21059            ]
21060        };
21061        return menuItem;
21062    };
21063    // TODO: Maybe need aria-label
21064    const renderItemStructure = (info, providersBackstage, renderIcons, fallbackIcon = Optional.none()) => {
21065        if (info.presets === 'color') {
21066            return renderColorStructure(info, providersBackstage, fallbackIcon);
21067        }
21068        else if (info.presets === 'img') {
21069            return renderImgItemStructure(info);
21070        }
21071        else {
21072            return renderNormalItemStructure(info, providersBackstage, renderIcons, fallbackIcon);
21073        }
21074    };
21075
21076    // Use meta to pass through special information about the tooltip
21077    // (yes this is horrible but it is not yet public API)
21078    const tooltipBehaviour = (meta, sharedBackstage, tooltipText) => get$h(meta, 'tooltipWorker')
21079        .map((tooltipWorker) => [
21080        Tooltipping.config({
21081            lazySink: sharedBackstage.getSink,
21082            tooltipDom: {
21083                tag: 'div',
21084                classes: ['tox-tooltip-worker-container']
21085            },
21086            tooltipComponents: [],
21087            anchor: (comp) => ({
21088                type: 'submenu',
21089                item: comp,
21090                overrides: {
21091                    // NOTE: this avoids it setting overflow and max-height.
21092                    maxHeightFunction: expandable$1
21093                }
21094            }),
21095            mode: 'follow-highlight',
21096            onShow: (component, _tooltip) => {
21097                tooltipWorker((elm) => {
21098                    Tooltipping.setComponents(component, [
21099                        external({ element: SugarElement.fromDom(elm) })
21100                    ]);
21101                });
21102            }
21103        })
21104    ])
21105        .getOrThunk(() => {
21106        return tooltipText.map((text) => [
21107            Tooltipping.config({
21108                ...sharedBackstage.providers.tooltips.getConfig({
21109                    tooltipText: text
21110                }),
21111                mode: 'follow-highlight'
21112            })
21113        ]).getOr([]);
21114    });
21115    const encodeText = (text) => global$9.DOM.encode(text);
21116    const replaceText = (text, matchText) => {
21117        const translated = global$6.translate(text);
21118        const encoded = encodeText(translated);
21119        if (matchText.length > 0) {
21120            const escapedMatchRegex = new RegExp(escape(matchText), 'gi');
21121            return encoded.replace(escapedMatchRegex, (match) => `<span class="tox-autocompleter-highlight">${match}</span>`);
21122        }
21123        else {
21124            return encoded;
21125        }
21126    };
21127    const renderAutocompleteItem = (spec, matchText, useText, presets, onItemValueHandler, itemResponse, sharedBackstage, renderIcons = true) => {
21128        const structure = renderItemStructure({
21129            presets,
21130            textContent: Optional.none(),
21131            htmlContent: useText ? spec.text.map((text) => replaceText(text, matchText)) : Optional.none(),
21132            ariaLabel: spec.text,
21133            labelContent: Optional.none(),
21134            iconContent: spec.icon,
21135            shortcutContent: Optional.none(),
21136            checkMark: Optional.none(),
21137            caret: Optional.none(),
21138            value: spec.value
21139        }, sharedBackstage.providers, renderIcons, spec.icon);
21140        const tooltipString = spec.text.filter((text) => !useText && text !== '');
21141        return renderCommonItem({
21142            context: 'mode:design',
21143            data: buildData(spec),
21144            enabled: spec.enabled,
21145            getApi: constant$1({}),
21146            onAction: (_api) => onItemValueHandler(spec.value, spec.meta),
21147            onSetup: constant$1(noop),
21148            triggersSubmenu: false,
21149            itemBehaviours: tooltipBehaviour(spec, sharedBackstage, tooltipString)
21150        }, structure, itemResponse, sharedBackstage.providers);
21151    };
21152
21153    const renderChoiceItem = (spec, useText, presets, onItemValueHandler, isSelected, itemResponse, providersBackstage, renderIcons = true) => {
21154        const getApi = (component) => ({
21155            setActive: (state) => {
21156                Toggling.set(component, state);
21157            },
21158            isActive: () => Toggling.isOn(component),
21159            isEnabled: () => !Disabling.isDisabled(component),
21160            setEnabled: (state) => Disabling.set(component, !state)
21161        });
21162        const structure = renderItemStructure({
21163            presets,
21164            textContent: useText ? spec.text : Optional.none(),
21165            htmlContent: Optional.none(),
21166            labelContent: spec.label,
21167            ariaLabel: spec.text,
21168            iconContent: spec.icon,
21169            shortcutContent: useText ? spec.shortcut : Optional.none(),
21170            // useText essentially says that we have one column. In one column lists, we should show a tick
21171            // The tick is controlled by the tickedClass (via css). It is always present
21172            // but is hidden unless the tickedClass is present.
21173            checkMark: useText ? Optional.some(renderCheckmark(providersBackstage.icons)) : Optional.none(),
21174            caret: Optional.none(),
21175            value: spec.value
21176        }, providersBackstage, renderIcons);
21177        const optTooltipping = spec.text
21178            .filter(constant$1(!useText))
21179            .map((t) => Tooltipping.config(providersBackstage.tooltips.getConfig({
21180            tooltipText: providersBackstage.translate(t)
21181        })));
21182        return deepMerge(renderCommonItem({
21183            context: spec.context,
21184            data: buildData(spec),
21185            enabled: spec.enabled,
21186            getApi,
21187            onAction: (_api) => onItemValueHandler(spec.value),
21188            onSetup: (api) => {
21189                api.setActive(isSelected);
21190                return noop;
21191            },
21192            triggersSubmenu: false,
21193            itemBehaviours: [
21194                ...optTooltipping.toArray()
21195            ]
21196        }, structure, itemResponse, providersBackstage), {
21197            toggling: {
21198                toggleClass: tickedClass,
21199                toggleOnExecute: false,
21200                selected: spec.active,
21201                exclusive: true
21202            }
21203        });
21204    };
21205
21206    const renderSeparatorItem = (spec) => ({
21207        type: 'separator',
21208        dom: {
21209            tag: 'div',
21210            classes: [selectableClass, groupHeadingClass]
21211        },
21212        components: spec.text.map(text$2).toArray()
21213    });
21214
21215    // Note, this does not create a valid SketchSpec.
21216    const renderNormalItem = (spec, itemResponse, providersBackstage, renderIcons = true) => {
21217        const getApi = (component) => ({
21218            isEnabled: () => !Disabling.isDisabled(component),
21219            setEnabled: (state) => Disabling.set(component, !state)
21220        });
21221        const structure = renderItemStructure({
21222            presets: 'normal',
21223            iconContent: spec.icon,
21224            textContent: spec.text,
21225            htmlContent: Optional.none(),
21226            labelContent: Optional.none(),
21227            ariaLabel: spec.text,
21228            caret: Optional.none(),
21229            checkMark: Optional.none(),
21230            shortcutContent: spec.shortcut
21231        }, providersBackstage, renderIcons);
21232        return renderCommonItem({
21233            context: spec.context,
21234            data: buildData(spec),
21235            getApi,
21236            enabled: spec.enabled,
21237            onAction: spec.onAction,
21238            onSetup: spec.onSetup,
21239            triggersSubmenu: false,
21240            itemBehaviours: []
21241        }, structure, itemResponse, providersBackstage);
21242    };
21243
21244    // Note, this does not create a valid SketchSpec.
21245    const renderNestedItem = (spec, itemResponse, providersBackstage, renderIcons = true, downwardsCaret = false) => {
21246        const caret = downwardsCaret ? renderDownwardsCaret(providersBackstage.icons) : renderSubmenuCaret(providersBackstage.icons);
21247        const getApi = (component) => ({
21248            isEnabled: () => !Disabling.isDisabled(component),
21249            setEnabled: (state) => Disabling.set(component, !state),
21250            setIconFill: (id, value) => {
21251                descendant(component.element, `svg path[class="${id}"], rect[class="${id}"]`).each((underlinePath) => {
21252                    set$9(underlinePath, 'fill', value);
21253                });
21254            },
21255            setTooltip: (tooltip) => {
21256                const translatedTooltip = providersBackstage.translate(tooltip);
21257                set$9(component.element, 'aria-label', translatedTooltip);
21258            }
21259        });
21260        const structure = renderItemStructure({
21261            presets: 'normal',
21262            iconContent: spec.icon,
21263            textContent: spec.text,
21264            htmlContent: Optional.none(),
21265            ariaLabel: spec.text,
21266            labelContent: Optional.none(),
21267            caret: Optional.some(caret),
21268            checkMark: Optional.none(),
21269            shortcutContent: spec.shortcut
21270        }, providersBackstage, renderIcons);
21271        return renderCommonItem({
21272            context: spec.context,
21273            data: buildData(spec),
21274            getApi,
21275            enabled: spec.enabled,
21276            onAction: noop,
21277            onSetup: spec.onSetup,
21278            triggersSubmenu: true,
21279            itemBehaviours: []
21280        }, structure, itemResponse, providersBackstage);
21281    };
21282
21283    const renderToggleMenuItem = (spec, itemResponse, providersBackstage, renderIcons = true) => {
21284        const getApi = (component) => ({
vendor: 12,749 bytes, lines 21285-21619
21285            setActive: (state) => {
21286                Toggling.set(component, state);
21287            },
21288            isActive: () => Toggling.isOn(component),
21289            isEnabled: () => !Disabling.isDisabled(component),
21290            setEnabled: (state) => Disabling.set(component, !state)
21291        });
21292        // BespokeSelects use meta to pass through styling information. Bespokes should only
21293        // be togglemenuitems hence meta is only passed through in this MenuItem.
21294        const structure = renderItemStructure({
21295            iconContent: spec.icon,
21296            textContent: spec.text,
21297            htmlContent: Optional.none(),
21298            labelContent: Optional.none(),
21299            ariaLabel: spec.text,
21300            checkMark: Optional.some(renderCheckmark(providersBackstage.icons)),
21301            caret: Optional.none(),
21302            shortcutContent: spec.shortcut,
21303            presets: 'normal',
21304            meta: spec.meta
21305        }, providersBackstage, renderIcons);
21306        return deepMerge(renderCommonItem({
21307            context: spec.context,
21308            data: buildData(spec),
21309            enabled: spec.enabled,
21310            getApi,
21311            onAction: spec.onAction,
21312            onSetup: spec.onSetup,
21313            triggersSubmenu: false,
21314            itemBehaviours: []
21315        }, structure, itemResponse, providersBackstage), {
21316            toggling: {
21317                toggleClass: tickedClass,
21318                toggleOnExecute: false,
21319                selected: spec.active
21320            },
21321            role: spec.role.getOrUndefined()
21322        });
21323    };
21324
21325    const hexColour = (value) => ({
21326        value: normalizeHex(value)
21327    });
21328    const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
21329    const longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
21330    const isHexString = (hex) => shorthandRegex.test(hex) || longformRegex.test(hex);
21331    const normalizeHex = (hex) => removeLeading(hex, '#').toUpperCase();
21332    const fromString$1 = (hex) => isHexString(hex) ? Optional.some({ value: normalizeHex(hex) }) : Optional.none();
21333    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
21334    const getLongForm = (hex) => {
21335        const hexString = hex.value.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
21336        return { value: hexString };
21337    };
21338    const extractValues = (hex) => {
21339        const longForm = getLongForm(hex);
21340        const splitForm = longformRegex.exec(longForm.value);
21341        return splitForm === null ? ['FFFFFF', 'FF', 'FF', 'FF'] : splitForm;
21342    };
21343    const toHex = (component) => {
21344        const hex = component.toString(16);
21345        return (hex.length === 1 ? '0' + hex : hex).toUpperCase();
21346    };
21347    const fromRgba = (rgbaColour) => {
21348        const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);
21349        return hexColour(value);
21350    };
21351
21352    const hsvColour = (hue, saturation, value) => ({
21353        hue,
21354        saturation,
21355        value
21356    });
21357    const fromRgb = (rgbaColour) => {
21358        let h = 0;
21359        let s = 0;
21360        let v = 0;
21361        const r = rgbaColour.red / 255;
21362        const g = rgbaColour.green / 255;
21363        const b = rgbaColour.blue / 255;
21364        const minRGB = Math.min(r, Math.min(g, b));
21365        const maxRGB = Math.max(r, Math.max(g, b));
21366        if (minRGB === maxRGB) {
21367            v = minRGB;
21368            return hsvColour(0, 0, v * 100);
21369        }
21370        /* eslint no-nested-ternary:0 */
21371        const d = (r === minRGB) ? g - b : ((b === minRGB) ? r - g : b - r);
21372        h = (r === minRGB) ? 3 : ((b === minRGB) ? 1 : 5);
21373        h = 60 * (h - d / (maxRGB - minRGB));
21374        s = (maxRGB - minRGB) / maxRGB;
21375        v = maxRGB;
21376        return hsvColour(Math.round(h), Math.round(s * 100), Math.round(v * 100));
21377    };
21378
21379    const min = Math.min;
21380    const max = Math.max;
21381    const round$1 = Math.round;
21382    const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*\)\s*$/i;
21383    // This regex will match rgba(0, 0, 0, 0.5) or rgba(0, 0, 0, 50%) , or without commas
21384    const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*((?:\d?\.\d+|\d+)%?)\s*\)\s*$/i;
21385    const rgbaColour = (red, green, blue, alpha) => ({
21386        red,
21387        green,
21388        blue,
21389        alpha
21390    });
21391    const isRgbaComponent = (value) => {
21392        const num = parseInt(value, 10);
21393        return num.toString() === value && num >= 0 && num <= 255;
21394    };
21395    const fromHsv = (hsv) => {
21396        let r;
21397        let g;
21398        let b;
21399        const hue = (hsv.hue || 0) % 360;
21400        let saturation = hsv.saturation / 100;
21401        let brightness = hsv.value / 100;
21402        saturation = max(0, min(saturation, 1));
21403        brightness = max(0, min(brightness, 1));
21404        if (saturation === 0) {
21405            r = g = b = round$1(255 * brightness);
21406            return rgbaColour(r, g, b, 1);
21407        }
21408        const side = hue / 60;
21409        const chroma = brightness * saturation;
21410        const x = chroma * (1 - Math.abs(side % 2 - 1));
21411        const match = brightness - chroma;
21412        switch (Math.floor(side)) {
21413            case 0:
21414                r = chroma;
21415                g = x;
21416                b = 0;
21417                break;
21418            case 1:
21419                r = x;
21420                g = chroma;
21421                b = 0;
21422                break;
21423            case 2:
21424                r = 0;
21425                g = chroma;
21426                b = x;
21427                break;
21428            case 3:
21429                r = 0;
21430                g = x;
21431                b = chroma;
21432                break;
21433            case 4:
21434                r = x;
21435                g = 0;
21436                b = chroma;
21437                break;
21438            case 5:
21439                r = chroma;
21440                g = 0;
21441                b = x;
21442                break;
21443            default:
21444                r = g = b = 0;
21445        }
21446        r = round$1(255 * (r + match));
21447        g = round$1(255 * (g + match));
21448        b = round$1(255 * (b + match));
21449        return rgbaColour(r, g, b, 1);
21450    };
21451    // Temporarily using: https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
21452    const fromHex = (hexColour) => {
21453        const result = extractValues(hexColour);
21454        const red = parseInt(result[1], 16);
21455        const green = parseInt(result[2], 16);
21456        const blue = parseInt(result[3], 16);
21457        return rgbaColour(red, green, blue, 1);
21458    };
21459    const fromStringValues = (red, green, blue, alpha) => {
21460        const r = parseInt(red, 10);
21461        const g = parseInt(green, 10);
21462        const b = parseInt(blue, 10);
21463        const a = parseFloat(alpha);
21464        return rgbaColour(r, g, b, a);
21465    };
21466    const fromString = (rgbaString) => {
21467        const rgbMatch = rgbRegex.exec(rgbaString);
21468        if (rgbMatch !== null) {
21469            return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], '1'));
21470        }
21471        const rgbaMatch = rgbaRegex.exec(rgbaString);
21472        if (rgbaMatch !== null) {
21473            return Optional.some(fromStringValues(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], rgbaMatch[4]));
21474        }
21475        return Optional.none();
21476    };
21477    const toString = (rgba) => `rgba(${rgba.red},${rgba.green},${rgba.blue},${rgba.alpha})`;
21478    const red = rgbaColour(255, 0, 0, 1);
21479
21480    const hexToHsv = (hex) => fromRgb(fromHex(hex));
21481    const hsvToHex = (hsv) => fromRgba(fromHsv(hsv));
21482    const anyToHex = (color) => fromString$1(color)
21483        .orThunk(() => fromString(color).map(fromRgba))
21484        .getOrThunk(() => {
21485        // Not dealing with Hex or RGBA so use a canvas to parse the color
21486        const canvas = document.createElement('canvas');
21487        canvas.height = 1;
21488        canvas.width = 1;
21489        const canvasContext = canvas.getContext('2d');
21490        // all valid colors after this point
21491        canvasContext.clearRect(0, 0, canvas.width, canvas.height);
21492        // invalid colors will be shown as white - the first assignment will pass and the second may be ignored
21493        canvasContext.fillStyle = '#FFFFFF';
21494        canvasContext.fillStyle = color;
21495        canvasContext.fillRect(0, 0, 1, 1);
21496        const rgba = canvasContext.getImageData(0, 0, 1, 1).data;
21497        const r = rgba[0];
21498        const g = rgba[1];
21499        const b = rgba[2];
21500        const a = rgba[3];
21501        return fromRgba(rgbaColour(r, g, b, a));
21502    });
21503
21504    const fireSkinLoaded$1 = (editor) => {
21505        editor.dispatch('SkinLoaded');
21506    };
21507    const fireSkinLoadError$1 = (editor, error) => {
21508        editor.dispatch('SkinLoadError', error);
21509    };
21510    const fireResizeEditor = (editor) => {
21511        editor.dispatch('ResizeEditor');
21512    };
21513    const fireResizeContent = (editor, e) => {
21514        editor.dispatch('ResizeContent', e);
21515    };
21516    const fireScrollContent = (editor, e) => {
21517        editor.dispatch('ScrollContent', e);
21518    };
21519    const fireTextColorChange = (editor, data) => {
21520        editor.dispatch('TextColorChange', data);
21521    };
21522    const fireAfterProgressState = (editor, state) => {
21523        editor.dispatch('AfterProgressState', { state });
21524    };
21525    const fireResolveName = (editor, node) => editor.dispatch('ResolveName', {
21526        name: node.nodeName.toLowerCase(),
21527        target: node
21528    });
21529    const fireToggleToolbarDrawer = (editor, state) => {
21530        editor.dispatch('ToggleToolbarDrawer', { state });
21531    };
21532    const fireStylesTextUpdate = (editor, data) => {
21533        editor.dispatch('StylesTextUpdate', data);
21534    };
21535    const fireAlignTextUpdate = (editor, data) => {
21536        editor.dispatch('AlignTextUpdate', data);
21537    };
21538    const fireFontSizeTextUpdate = (editor, data) => {
21539        editor.dispatch('FontSizeTextUpdate', data);
21540    };
21541    const fireFontSizeInputTextUpdate = (editor, data) => {
21542        editor.dispatch('FontSizeInputTextUpdate', data);
21543    };
21544    const fireBlocksTextUpdate = (editor, data) => {
21545        editor.dispatch('BlocksTextUpdate', data);
21546    };
21547    const fireFontFamilyTextUpdate = (editor, data) => {
21548        editor.dispatch('FontFamilyTextUpdate', data);
21549    };
21550    const fireToggleSidebar = (editor) => {
21551        editor.dispatch('ToggleSidebar');
21552    };
21553    const fireToggleView = (editor) => {
21554        editor.dispatch('ToggleView');
21555    };
21556    const fireContextToolbarClose = (editor) => {
21557        editor.dispatch('ContextToolbarClose');
21558    };
21559    const fireContextFormSlideBack = (editor) => {
21560        editor.dispatch('ContextFormSlideBack');
21561    };
21562
21563    const composeUnbinders = (f, g) => () => {
21564        f();
21565        g();
21566    };
21567    const onSetupEditableToggle = (editor, enabledPredicate = always) => onSetupEvent(editor, 'NodeChange', (api) => {
21568        api.setEnabled(editor.selection.isEditable() && enabledPredicate());
21569    });
21570    const onSetupFormatToggle = (editor, name) => (api) => {
21571        const boundFormatChangeCallback = unbindable();
21572        const init = () => {
21573            api.setActive(editor.formatter.match(name));
21574            const binding = editor.formatter.formatChanged(name, api.setActive);
21575            boundFormatChangeCallback.set(binding);
21576        };
21577        // The editor may or may not have been setup yet, so check for that
21578        editor.initialized ? init() : editor.once('init', init);
21579        return () => {
21580            editor.off('init', init);
21581            boundFormatChangeCallback.clear();
21582        };
21583    };
21584    const onSetupStateToggle = (editor, name) => (api) => {
21585        const unbindEditableToogle = onSetupEditableToggle(editor)(api);
21586        const unbindFormatToggle = onSetupFormatToggle(editor, name)(api);
21587        return () => {
21588            unbindEditableToogle();
21589            unbindFormatToggle();
21590        };
21591    };
21592    const onSetupEvent = (editor, event, f) => (api) => {
21593        const handleEvent = () => f(api);
21594        const init = () => {
21595            f(api);
21596            editor.on(event, handleEvent);
21597        };
21598        // The editor may or may not have been setup yet, so check for that
21599        editor.initialized ? init() : editor.once('init', init);
21600        return () => {
21601            editor.off('init', init);
21602            editor.off(event, handleEvent);
21603        };
21604    };
21605    const onActionToggleFormat$1 = (editor) => (rawItem) => () => {
21606        editor.undoManager.transact(() => {
21607            editor.focus();
21608            editor.execCommand('mceToggleFormat', false, rawItem.format);
21609        });
21610    };
21611    const onActionExecCommand = (editor, command) => () => editor.execCommand(command);
21612
21613    var global$5 = tinymce.util.Tools.resolve('tinymce.util.LocalStorage');
21614
21615    const cacheStorage = {};
21616    const ColorCache = (storageId, max = 10) => {
21617        const storageString = global$5.getItem(storageId);
21618        const localstorage = isString(storageString) ? JSON.parse(storageString) : [];
21619        const prune = (list) => {
21620            // When the localStorage cache is too big,
21621            // remove the difference from the tail (head is fresh, tail is stale!)
21622            const diff = max - list.length;
21623            return (diff < 0) ? list.slice(0, max) : list;
21624        };
21625        const cache = prune(localstorage);
21626        const add = (key) => {
21627            // Remove duplicates first.
21628            indexOf(cache, key).each(remove);
21629            cache.unshift(key);
21630            // When max size is exceeded, the oldest colors will be removed
21631            if (cache.length > max) {
21632                cache.pop();
21633            }
21634            global$5.setItem(storageId, JSON.stringify(cache));
21635        };
21636        const remove = (idx) => {
21637            cache.splice(idx, 1);
21638        };
21639        const state = () => cache.slice(0);
21640        return {
21641            add,
21642            state
21643        };
21644    };
21645    const getCacheForId = (id) => get$h(cacheStorage, id).getOrThunk(() => {
21646        const storageId = `tinymce-custom-colors-${id}`;
21647        const currentData = global$5.getItem(storageId);
21648        if (isNullable(currentData)) {
21649            const legacyDefault = global$5.getItem('tinymce-custom-colors');
21650            global$5.setItem(storageId, isNonNullable(legacyDefault) ? legacyDefault : '[]');
21651        }
21652        const storage = ColorCache(storageId, 10);
21653        cacheStorage[id] = storage;
21654        return storage;
21655    });
21656    const getCurrentColors = (id) => map$2(getCacheForId(id).state(), (color) => ({
21657        type: 'choiceitem',
21658        text: color,
21659        icon: 'checkmark',
21660        value: color
21661    }));
21662    const addColor = (id, color) => {
21663        getCacheForId(id).add(color);
21664    };
21665
21666    const foregroundId = 'forecolor';
21667    const backgroundId = 'hilitecolor';
21668    const fallbackCols = 5;
21669    const mapColors = (colorMap) => mapColorsRaw(colorMap.map((color, index) => {
21670        if (index % 2 === 0) {
21671            return '#' + anyToHex(color).value;
21672        }
21673        return color;
21674    }));
21675    const mapColorsRaw = (colorMap) => {
21676        const colors = [];
21677        for (let i = 0; i < colorMap.length; i += 2) {
21678            colors.push({
21679                text: colorMap[i + 1],
21680                value: colorMap[i],
21681                icon: 'checkmark',
21682                type: 'choiceitem'
21683            });
21684        }
21685        return colors;
21686    };
21687    const option$1 = (name) => (editor) => editor.options.get(name);
21688    const fallbackColor = '#000000';
21689    const register$e = (editor) => {
21690        const registerOption = editor.options.register;
21691        const colorProcessor = (value) => {
21692            if (isArrayOf(value, isString)) {
21693                return { value: mapColors(value), valid: true };
21694            }
21695            else {
21696                return { valid: false, message: 'Must be an array of strings.' };
21697            }
21698        };
21699        const colorProcessorRaw = (value) => {
21700            if (isArrayOf(value, isString)) {
21701                return { value: mapColorsRaw(value), valid: true };
21702            }
21703            else {
21704                return { valid: false, message: 'Must be an array of strings.' };
21705            }
21706        };
21707        const colorColsProcessor = (value) => {
21708            if (isNumber(value) && value > 0) {
21709                return { value, valid: true };
21710            }
21711            else {
21712                return { valid: false, message: 'Must be a positive number.' };
21713            }
21714        };
21715        registerOption('color_map', {
21716            processor: colorProcessor,
21717            default: [
21718                '#BFEDD2', 'Light Green',
21719                '#FBEEB8', 'Light Yellow',
21720                '#F8CAC6', 'Light Red',
21721                '#ECCAFA', 'Light Purple',
21722                '#C2E0F4', 'Light Blue',
21723                '#2DC26B', 'Green',
21724                '#F1C40F', 'Yellow',
21725                '#E03E2D', 'Red',
21726                '#B96AD9', 'Purple',
21727                '#3598DB', 'Blue',
21728                '#169179', 'Dark Turquoise',
21729                '#E67E23', 'Orange',
21730                '#BA372A', 'Dark Red',
21731                '#843FA1', 'Dark Purple',
21732                '#236FA1', 'Dark Blue',
21733                '#ECF0F1', 'Light Gray',
21734                '#CED4D9', 'Medium Gray',
21735                '#95A5A6', 'Gray',
21736                '#7E8C8D', 'Dark Gray',
21737                '#34495E', 'Navy Blue',
21738                '#000000', 'Black',
21739                '#ffffff', 'White'
21740            ]
21741        });
21742        registerOption('color_map_raw', {
21743            processor: colorProcessorRaw,
21744        });
21745        registerOption('color_map_background', {
21746            processor: colorProcessor
21747        });
21748        registerOption('color_map_foreground', {
21749            processor: colorProcessor
21750        });
21751        registerOption('color_cols', {
21752            processor: colorColsProcessor,
21753            default: calcCols(editor)
21754        });
21755        registerOption('color_cols_foreground', {
21756            processor: colorColsProcessor,
21757            default: defaultCols(editor, foregroundId)
21758        });
21759        registerOption('color_cols_background', {
21760            processor: colorColsProcessor,
21761            default: defaultCols(editor, backgroundId)
21762        });
21763        registerOption('custom_colors', {
21764            processor: 'boolean',
21765            default: true
21766        });
21767        registerOption('color_default_foreground', {
21768            processor: 'string',
21769            default: fallbackColor
21770        });
21771        registerOption('color_default_background', {
21772            processor: 'string',
21773            default: fallbackColor
21774        });
21775    };
21776    const getColors$2 = (editor, id) => {
21777        if (id === foregroundId && editor.options.isSet('color_map_foreground')) {
21778            return option$1('color_map_foreground')(editor);
21779        }
21780        else if (id === backgroundId && editor.options.isSet('color_map_background')) {
21781            return option$1('color_map_background')(editor);
21782        }
21783        else if (editor.options.isSet('color_map_raw')) {
21784            return option$1('color_map_raw')(editor);
21785        }
21786        else {
21787            return option$1('color_map')(editor);
21788        }
21789    };
21790    const calcCols = (editor, id = 'default') => Math.max(fallbackCols, Math.ceil(Math.sqrt(getColors$2(editor, id).length)));
21791    const defaultCols = (editor, id) => {
21792        const defaultCols = option$1('color_cols')(editor);
21793        const calculatedCols = calcCols(editor, id);
21794        if (defaultCols === calcCols(editor)) {
21795            return calculatedCols;
21796        }
21797        else {
21798            return defaultCols;
21799        }
21800    };
21801    const getColorCols$1 = (editor, id = 'default') => {
21802        const getCols = () => {
21803            if (id === foregroundId) {
21804                return option$1('color_cols_foreground')(editor);
21805            }
21806            else if (id === backgroundId) {
21807                return option$1('color_cols_background')(editor);
21808            }
21809            else {
21810                return option$1('color_cols')(editor);
21811            }
21812        };
21813        return Math.round(getCols());
21814    };
21815    const hasCustomColors$1 = option$1('custom_colors');
21816    const getDefaultForegroundColor = option$1('color_default_foreground');
21817    const getDefaultBackgroundColor = option$1('color_default_background');
21818
21819    const defaultBackgroundColor = 'rgba(0, 0, 0, 0)';
21820    const isValidBackgroundColor = (value) => fromString(value).exists((c) => c.alpha !== 0);
21821    // Climb up the tree to find the value of the background until finding a non-transparent value or defaulting.
21822    const getClosestCssBackgroundColorValue = (scope) => {
21823        return closest(scope, (node) => {
21824            if (isElement$1(node)) {
21825                const color = get$e(node, 'background-color');
21826                return someIf(isValidBackgroundColor(color), color);
21827            }
21828            else {
21829                return Optional.none();
21830            }
21831        }).getOr(defaultBackgroundColor);
21832    };
21833    const getCurrentColor = (editor, format) => {
21834        const node = SugarElement.fromDom(editor.selection.getStart());
21835        const cssRgbValue = format === 'hilitecolor'
21836            ? getClosestCssBackgroundColorValue(node)
21837            : get$e(node, 'color');
21838        return fromString(cssRgbValue).map((rgba) => '#' + fromRgba(rgba).value);
21839    };
21840    const applyFormat = (editor, format, value) => {
21841        editor.undoManager.transact(() => {
21842            editor.focus();
21843            editor.formatter.apply(format, { value });
21844            editor.nodeChanged();
21845        });
21846    };
21847    const removeFormat = (editor, format) => {
21848        editor.undoManager.transact(() => {
21849            editor.focus();
21850            editor.formatter.remove(format, { value: null }, undefined, true);
21851            editor.nodeChanged();
21852        });
21853    };
21854    const registerCommands = (editor) => {
21855        editor.addCommand('mceApplyTextcolor', (format, value) => {
21856            applyFormat(editor, format, value);
21857        });
21858        editor.addCommand('mceRemoveTextcolor', (format) => {
21859            removeFormat(editor, format);
21860        });
21861    };
21862    const getAdditionalColors = (hasCustom) => {
21863        const type = 'choiceitem';
21864        const remove = {
21865            type,
21866            text: 'Remove color',
21867            icon: 'color-swatch-remove-color',
21868            value: 'remove'
21869        };
21870        const custom = {
21871            type,
21872            text: 'Custom color',
21873            icon: 'color-picker',
21874            value: 'custom'
21875        };
21876        return hasCustom ? [
21877            remove,
21878            custom
21879        ] : [remove];
21880    };
21881    const applyColor = (editor, format, value, onChoice) => {
21882        if (value === 'custom') {
21883            const dialog = colorPickerDialog(editor);
21884            dialog((colorOpt) => {
21885                colorOpt.each((color) => {
21886                    addColor(format, color);
21887                    editor.execCommand('mceApplyTextcolor', format, color);
21888                    onChoice(color);
21889                });
21890            }, getCurrentColor(editor, format).getOr(fallbackColor));
21891        }
21892        else if (value === 'remove') {
21893            onChoice('');
21894            editor.execCommand('mceRemoveTextcolor', format);
21895        }
21896        else {
21897            onChoice(value);
21898            editor.execCommand('mceApplyTextcolor', format, value);
21899        }
21900    };
21901    const getColors$1 = (colors, id, hasCustom) => colors.concat(getCurrentColors(id).concat(getAdditionalColors(hasCustom)));
21902    const getFetch$1 = (colors, id, hasCustom) => (callback) => {
21903        callback(getColors$1(colors, id, hasCustom));
21904    };
21905    const setIconColor = (splitButtonApi, name, newColor) => {
21906        const id = name === 'forecolor' ? 'tox-icon-text-color__color' : 'tox-icon-highlight-bg-color__color';
21907        splitButtonApi.setIconFill(id, newColor);
21908    };
21909    const setTooltip = (buttonApi, tooltip) => {
21910        buttonApi.setTooltip(tooltip);
21911    };
21912    const select$1 = (editor, format) => (value) => {
21913        const optCurrentHex = getCurrentColor(editor, format);
21914        return is$1(optCurrentHex, value.toUpperCase());
21915    };
21916    // Selecting `Remove Color` would set the lastColor to ''
21917    const getToolTipText = (editor, format, lastColor) => {
21918        if (isEmpty(lastColor)) {
21919            return format === 'forecolor' ? 'Text color' : 'Background color';
21920        }
21921        const tooltipPrefix = format === 'forecolor' ? 'Text color {0}' : 'Background color {0}';
21922        const colors = getColors$1(getColors$2(editor, format), format, false);
21923        const colorText = find$5(colors, (c) => c.value === lastColor).getOr({ text: '' }).text;
21924        return editor.translate([tooltipPrefix, editor.translate(colorText)]);
21925    };
21926    const registerTextColorButton = (editor, name, format, lastColor) => {
21927        editor.ui.registry.addSplitButton(name, {
21928            tooltip: getToolTipText(editor, format, lastColor.get()),
21929            chevronTooltip: name === 'forecolor' ? 'Text color menu' : 'Background color menu',
21930            presets: 'color',
21931            icon: name === 'forecolor' ? 'text-color' : 'highlight-bg-color',
21932            select: select$1(editor, format),
21933            columns: getColorCols$1(editor, format),
21934            fetch: getFetch$1(getColors$2(editor, format), format, hasCustomColors$1(editor)),
21935            onAction: (_splitButtonApi) => {
21936                applyColor(editor, format, lastColor.get(), noop);
21937            },
21938            onItemAction: (_splitButtonApi, value) => {
21939                applyColor(editor, format, value, (newColor) => {
21940                    lastColor.set(newColor);
21941                    fireTextColorChange(editor, {
21942                        name,
21943                        color: newColor
21944                    });
21945                });
21946            },
21947            onSetup: (splitButtonApi) => {
21948                setIconColor(splitButtonApi, name, lastColor.get());
21949                const handler = (e) => {
21950                    if (e.name === name) {
21951                        setIconColor(splitButtonApi, e.name, e.color);
21952                        setTooltip(splitButtonApi, getToolTipText(editor, format, e.color));
21953                    }
21954                };
21955                editor.on('TextColorChange', handler);
21956                return composeUnbinders(onSetupEditableToggle(editor)(splitButtonApi), () => {
21957                    editor.off('TextColorChange', handler);
21958                });
21959            }
21960        });
21961    };
21962    const registerTextColorMenuItem = (editor, name, format, text, lastColor) => {
21963        editor.ui.registry.addNestedMenuItem(name, {
21964            text,
21965            icon: name === 'forecolor' ? 'text-color' : 'highlight-bg-color',
21966            onSetup: (api) => {
21967                setTooltip(api, getToolTipText(editor, format, lastColor.get()));
21968                setIconColor(api, name, lastColor.get());
21969                return onSetupEditableToggle(editor)(api);
21970            },
21971            getSubmenuItems: () => [
21972                {
21973                    type: 'fancymenuitem',
21974                    fancytype: 'colorswatch',
21975                    select: select$1(editor, format),
21976                    initData: {
21977                        storageKey: format,
21978                    },
21979                    onAction: (data) => {
21980                        applyColor(editor, format, data.value, (newColor) => {
21981                            lastColor.set(newColor);
21982                            fireTextColorChange(editor, {
21983                                name,
21984                                color: newColor
21985                            });
21986                        });
21987                    },
21988                }
21989            ]
21990        });
21991    };
21992    const colorPickerDialog = (editor) => (callback, value) => {
21993        let isValid = false;
vendor: 2,415 bytes, lines 21994-22061
21994        const onSubmit = (api) => {
21995            const data = api.getData();
21996            const hex = data.colorpicker;
21997            if (isValid) {
21998                callback(Optional.from(hex));
21999                api.close();
22000            }
22001            else {
22002                editor.windowManager.alert(editor.translate(['Invalid hex color code: {0}', hex]));
22003            }
22004        };
22005        const onAction = (_api, details) => {
22006            if (details.name === 'hex-valid') {
22007                isValid = details.value;
22008            }
22009        };
22010        const initialData = {
22011            colorpicker: value
22012        };
22013        editor.windowManager.open({
22014            title: 'Color Picker',
22015            size: 'normal',
22016            body: {
22017                type: 'panel',
22018                items: [
22019                    {
22020                        type: 'colorpicker',
22021                        name: 'colorpicker',
22022                        label: 'Color'
22023                    }
22024                ]
22025            },
22026            buttons: [
22027                {
22028                    type: 'cancel',
22029                    name: 'cancel',
22030                    text: 'Cancel'
22031                },
22032                {
22033                    type: 'submit',
22034                    name: 'save',
22035                    text: 'Save',
22036                    primary: true
22037                }
22038            ],
22039            initialData,
22040            onAction,
22041            onSubmit,
22042            onClose: noop,
22043            onCancel: () => {
22044                callback(Optional.none());
22045            }
22046        });
22047    };
22048    const register$d = (editor) => {
22049        registerCommands(editor);
22050        const fallbackColorForeground = getDefaultForegroundColor(editor);
22051        const fallbackColorBackground = getDefaultBackgroundColor(editor);
22052        const lastForeColor = Cell(fallbackColorForeground);
22053        const lastBackColor = Cell(fallbackColorBackground);
22054        registerTextColorButton(editor, 'forecolor', 'forecolor', lastForeColor);
22055        registerTextColorButton(editor, 'backcolor', 'hilitecolor', lastBackColor);
22056        registerTextColorMenuItem(editor, 'forecolor', 'forecolor', 'Text color', lastForeColor);
22057        registerTextColorMenuItem(editor, 'backcolor', 'hilitecolor', 'Background color', lastBackColor);
22058    };
22059
22060    const renderImgItem = (spec, onItemValueHandler, isSelected, itemResponse, providersBackstage) => {
22061        const getApi = (component) => ({
22062            setActive: (state) => {
22063                Toggling.set(component, state);
22064            },
22065            isActive: () => Toggling.isOn(component),
22066            isEnabled: () => !Disabling.isDisabled(component),
22067            setEnabled: (state) => Disabling.set(component, !state)
22068        });
22069        const structure = renderItemStructure({
22070            presets: 'img',
22071            textContent: Optional.none(),
22072            htmlContent: Optional.none(),
22073            ariaLabel: spec.tooltip,
22074            iconContent: Optional.some(spec.url),
22075            labelContent: spec.label,
22076            shortcutContent: Optional.none(),
22077            checkMark: Optional.some(renderCheckmark(providersBackstage.icons)),
22078            caret: Optional.none(),
22079            value: spec.value
22080        }, providersBackstage, true);
22081        const optTooltipping = spec.tooltip
22082            .map((t) => Tooltipping.config(providersBackstage.tooltips.getConfig({
22083            tooltipText: providersBackstage.translate(t)
22084        })));
22085        return deepMerge(renderCommonItem({
22086            context: spec.context,
22087            data: buildData(spec),
22088            enabled: spec.enabled,
22089            getApi,
22090            onAction: (api) => {
22091                onItemValueHandler(spec.value);
22092                api.setActive(true);
22093            },
22094            onSetup: (api) => {
22095                api.setActive(isSelected);
22096                return noop;
22097            },
22098            triggersSubmenu: false,
22099            itemBehaviours: [
22100                ...optTooltipping.toArray()
22101            ]
22102        }, structure, itemResponse, providersBackstage), {
22103            toggling: {
22104                toggleClass: tickedClass,
22105                toggleOnExecute: false,
22106                selected: spec.active,
22107                exclusive: true
22108            }
22109        });
22110    };
22111
22112    const createPartialChoiceMenu = (value, items, onItemValueHandler, columns, presets, itemResponse, select, providersBackstage) => {
22113        const hasIcons = menuHasIcons(items);
22114        const presetItemTypes = presets !== 'color' ? 'normal' : 'color';
22115        const alloyItems = createChoiceItems(items, onItemValueHandler, columns, presetItemTypes, itemResponse, select, providersBackstage);
22116        const menuLayout = {
22117            menuType: presets
22118        };
22119        return createPartialMenuWithAlloyItems(value, hasIcons, alloyItems, columns, menuLayout);
22120    };
22121    const createChoiceItems = (items, onItemValueHandler, columns, itemPresets, itemResponse, select, providersBackstage) => cat(map$2(items, (item) => {
22122        if (item.type === 'choiceitem') {
22123            return createChoiceMenuItem(item).fold(handleError, (d) => Optional.some(renderChoiceItem(d, columns === 1, itemPresets, onItemValueHandler, select(d.value), itemResponse, providersBackstage, menuHasIcons(items))));
22124        }
22125        else if (item.type === 'imageitem') {
22126            return createImageMenuItem(item).fold(handleError, (d) => Optional.some(renderImgItem(d, onItemValueHandler, select(d.value), itemResponse, providersBackstage)));
22127        }
22128        else if (item.type === 'resetimage') {
22129            return createResetImageItem(item).fold(handleError, (d) => Optional.some(renderChoiceItem(({
22130                ...d,
22131                type: 'choiceitem',
22132                text: d.tooltip,
22133                icon: Optional.some(d.icon),
22134                label: Optional.some(d.label),
22135            }), columns === 1, itemPresets, onItemValueHandler, select(d.value), itemResponse, providersBackstage, menuHasIcons(items))));
22136        }
22137        else {
22138            return Optional.none();
22139        }
22140    }));
22141
22142    const deriveMenuMovement = (columns, presets) => {
22143        const menuMarkers = markers(presets);
22144        if (columns === 1) {
22145            return { mode: 'menu' };
22146        }
22147        else if (columns === 'auto') {
22148            return {
22149                mode: 'grid',
22150                selector: '.' + menuMarkers.item,
22151                initSize: {
22152                    numColumns: 1,
22153                    numRows: 1
22154                }
22155            };
22156        }
22157        else {
22158            const rowClass = {
22159                color: 'tox-swatches__row',
22160                imageselector: 'tox-image-selector__row',
22161                listpreview: 'tox-collection__group',
22162                normal: 'tox-collection__group'
22163            }[presets];
22164            return {
22165                mode: 'matrix',
22166                rowSelector: '.' + rowClass,
22167                previousSelector: (menu) => {
22168                    // We only want the navigation to start on the selected item if we are in color-mode (The colorswatch)
22169                    return presets === 'color'
22170                        ? descendant(menu.element, '[aria-checked=true]')
22171                        : Optional.none();
22172                }
22173            };
22174        }
22175    };
22176    const deriveCollectionMovement = (columns, presets) => {
22177        if (columns === 1) {
22178            return {
22179                mode: 'menu',
22180                selector: '.tox-collection__item'
22181            };
22182        }
22183        else if (columns === 'auto') {
22184            return {
22185                mode: 'flatgrid',
22186                selector: '.' + 'tox-collection__item',
22187                initSize: {
22188                    numColumns: 1,
22189                    numRows: 1
22190                }
22191            };
22192        }
22193        else {
22194            return {
22195                mode: 'matrix',
22196                selectors: {
22197                    row: presets === 'color' ? '.tox-swatches__row' : '.tox-collection__group',
22198                    cell: presets === 'color' ? `.${colorClass}` : `.${selectableClass}`
22199                }
22200            };
22201        }
22202    };
22203
22204    const renderColorSwatchItem = (spec, backstage) => {
22205        const items = getColorItems(spec, backstage);
22206        const columns = backstage.colorinput.getColorCols(spec.initData.storageKey);
22207        const presets = 'color';
22208        const menuSpec = createPartialChoiceMenu(generate$6('menu-value'), items, (value) => {
22209            spec.onAction({ value });
22210        }, columns, presets, ItemResponse$1.CLOSE_ON_EXECUTE, spec.select.getOr(never), backstage.shared.providers);
22211        const widgetSpec = {
22212            ...menuSpec,
22213            markers: markers(presets),
22214            movement: deriveMenuMovement(columns, presets),
22215            // TINY-10806: Avoid duplication of ARIA role="menu" in the accessibility tree for Color Swatch menu item.
22216            showMenuRole: false
22217        };
22218        return {
22219            type: 'widget',
22220            data: { value: generate$6('widget-id') },
22221            dom: {
22222                tag: 'div',
22223                classes: ['tox-fancymenuitem']
22224            },
22225            autofocus: true,
22226            components: [
22227                parts$7.widget(Menu.sketch(widgetSpec))
22228            ]
22229        };
22230    };
22231    const getColorItems = (spec, backstage) => {
22232        const useCustomColors = spec.initData.allowCustomColors && backstage.colorinput.hasCustomColors();
22233        return spec.initData.colors.fold(() => getColors$1(backstage.colorinput.getColors(spec.initData.storageKey), spec.initData.storageKey, useCustomColors), (colors) => colors.concat(getAdditionalColors(useCustomColors)));
22234    };
22235
22236    const renderImageSelector = (spec, backstage) => {
22237        const presets = 'imageselector';
22238        const columns = spec.initData.columns;
22239        const menuSpec = createPartialChoiceMenu(generate$6('menu-value'), spec.initData.items, (value) => {
22240            spec.onAction({ value });
22241        }, columns, presets, ItemResponse$1.CLOSE_ON_EXECUTE, spec.select.getOr(never), backstage.shared.providers);
22242        const widgetSpec = {
22243            ...menuSpec,
22244            markers: markers(presets),
22245            movement: deriveMenuMovement(columns, presets),
22246            // TINY-10806: Avoid duplication of ARIA role="menu" in the accessibility tree for Image Selector menu item.
22247            showMenuRole: false
22248        };
22249        return {
22250            type: 'widget',
22251            data: { value: generate$6('widget-id') },
22252            dom: {
22253                tag: 'div',
22254                classes: ['tox-fancymenuitem', 'tox-collection--toolbar']
22255            },
22256            autofocus: true,
22257            components: [
22258                parts$7.widget(Menu.sketch(widgetSpec))
22259            ]
22260        };
22261    };
22262
22263    const cellOverEvent = generate$6('cell-over');
22264    const cellExecuteEvent = generate$6('cell-execute');
22265    const makeAnnouncementText = (backstage) => (row, col) => backstage.shared.providers.translate(['{0} columns, {1} rows', col, row]);
22266    const makeCell = (row, col, label) => {
22267        const emitCellOver = (c) => emitWith(c, cellOverEvent, { row, col });
22268        const emitExecute = (c) => emitWith(c, cellExecuteEvent, { row, col });
22269        const onClick = (c, se) => {
22270            se.stop();
22271            emitExecute(c);
22272        };
22273        return build$1({
22274            dom: {
22275                tag: 'div',
22276                attributes: {
22277                    role: 'button',
22278                    ['aria-label']: label
22279                }
22280            },
22281            behaviours: derive$1([
22282                config('insert-table-picker-cell', [
22283                    run$1(mouseover(), Focusing.focus),
22284                    run$1(execute$5(), emitExecute),
22285                    run$1(click(), onClick),
22286                    run$1(tap(), onClick)
22287                ]),
22288                Toggling.config({
22289                    toggleClass: 'tox-insert-table-picker__selected',
22290                    toggleOnExecute: false
22291                }),
22292                Focusing.config({ onFocus: emitCellOver })
22293            ])
22294        });
22295    };
22296    const makeCells = (getCellLabel, numRows, numCols) => {
22297        const cells = [];
22298        for (let i = 0; i < numRows; i++) {
22299            const row = [];
22300            for (let j = 0; j < numCols; j++) {
22301                const label = getCellLabel(i + 1, j + 1);
22302                row.push(makeCell(i, j, label));
22303            }
22304            cells.push(row);
22305        }
22306        return cells;
22307    };
22308    const selectCells = (cells, selectedRow, selectedColumn, numRows, numColumns) => {
22309        for (let i = 0; i < numRows; i++) {
22310            for (let j = 0; j < numColumns; j++) {
22311                Toggling.set(cells[i][j], i <= selectedRow && j <= selectedColumn);
22312            }
22313        }
22314    };
22315    const makeComponents = (cells) =>
vendor: 6,712 bytes, lines 22315-22465
22315 bind$3(cells, (cellRow) => map$2(cellRow, premade));
22316    const makeLabelText = (row, col) => text$2(`${col}x${row}`);
22317    const renderInsertTableMenuItem = (spec, backstage) => {
22318        const numRows = 10;
22319        const numColumns = 10;
22320        const getCellLabel = makeAnnouncementText(backstage);
22321        const cells = makeCells(getCellLabel, numRows, numColumns);
22322        const emptyLabelText = makeLabelText(0, 0);
22323        const memLabel = record({
22324            dom: {
22325                tag: 'span',
22326                classes: ['tox-insert-table-picker__label'],
22327            },
22328            components: [emptyLabelText],
22329            behaviours: derive$1([
22330                Replacing.config({})
22331            ])
22332        });
22333        return {
22334            type: 'widget',
22335            data: { value: generate$6('widget-id') },
22336            dom: {
22337                tag: 'div',
22338                classes: ['tox-fancymenuitem']
22339            },
22340            autofocus: true,
22341            components: [parts$7.widget({
22342                    dom: {
22343                        tag: 'div',
22344                        classes: ['tox-insert-table-picker']
22345                    },
22346                    components: makeComponents(cells).concat(memLabel.asSpec()),
22347                    behaviours: derive$1([
22348                        config('insert-table-picker', [
22349                            runOnAttached((c) => {
22350                                // Restore the empty label when opened, otherwise it may still be using an old label from last time it was opened
22351                                Replacing.set(memLabel.get(c), [emptyLabelText]);
22352                            }),
22353                            runWithTarget(cellOverEvent, (c, t, e) => {
22354                                const { row, col } = e.event;
22355                                selectCells(cells, row, col, numRows, numColumns);
22356                                Replacing.set(memLabel.get(c), [makeLabelText(row + 1, col + 1)]);
22357                            }),
22358                            runWithTarget(cellExecuteEvent, (c, _, e) => {
22359                                const { row, col } = e.event;
22360                                // Close the sandbox before triggering the action
22361                                emit(c, sandboxClose());
22362                                spec.onAction({ numRows: row + 1, numColumns: col + 1 });
22363                            })
22364                        ]),
22365                        Keying.config({
22366                            initSize: {
22367                                numRows,
22368                                numColumns
22369                            },
22370                            mode: 'flatgrid',
22371                            selector: '[role="button"]'
22372                        })
22373                    ])
22374                })]
22375        };
22376    };
22377
22378    const fancyMenuItems = {
22379        inserttable: renderInsertTableMenuItem,
22380        colorswatch: renderColorSwatchItem,
22381        imageselect: renderImageSelector
22382    };
22383    const renderFancyMenuItem = (spec, backstage) => get$h(fancyMenuItems, spec.fancytype).map((render) => render(spec, backstage));
22384
22385    const render$2 = (items, extras) => map$2(items, (item) => {
22386        switch (item.type) {
22387            case 'cardcontainer':
22388                return renderContainer(item, render$2(item.items, extras));
22389            case 'cardimage':
22390                return renderImage(item.src, item.classes, item.alt);
22391            case 'cardtext':
22392                // Only highlight targeted text components
22393                const shouldHighlight = item.name.exists((name) => contains$2(extras.cardText.highlightOn, name));
22394                const matchText = shouldHighlight ? Optional.from(extras.cardText.matchText).getOr('') : '';
22395                return renderHtml(replaceText(item.text, matchText), item.classes);
22396        }
22397    });
22398    const renderCardMenuItem = (spec, itemResponse, sharedBackstage, extras) => {
22399        const getApi = (component) => ({
22400            isEnabled: () => !Disabling.isDisabled(component),
22401            setEnabled: (state) => {
22402                Disabling.set(component, !state);
22403                // Disable sub components
22404                each$1(descendants(component.element, '*'), (elm) => {
22405                    component.getSystem().getByDom(elm).each((comp) => {
22406                        if (comp.hasConfigured(Disabling)) {
22407                            Disabling.set(comp, !state);
22408                        }
22409                    });
22410                });
22411            }
22412        });
22413        const structure = {
22414            dom: renderItemDomStructure(spec.label, []),
22415            optComponents: [
22416                Optional.some({
22417                    dom: {
22418                        tag: 'div',
22419                        classes: [containerClass, containerRowClass]
22420                    },
22421                    components: render$2(spec.items, extras)
22422                })
22423            ]
22424        };
22425        return renderCommonItem({
22426            context: 'mode:design',
22427            data: buildData({ text: Optional.none(), ...spec }),
22428            enabled: spec.enabled,
22429            getApi,
22430            onAction: spec.onAction,
22431            onSetup: spec.onSetup,
22432            triggersSubmenu: false,
22433            itemBehaviours: Optional.from(extras.itemBehaviours).getOr([])
22434        }, structure, itemResponse, sharedBackstage.providers);
22435    };
22436
22437    const identifyMenuLayout = (searchMode) => {
22438        switch (searchMode.searchMode) {
22439            case 'no-search': {
22440                return {
22441                    menuType: 'normal'
22442                };
22443            }
22444            default: {
22445                return {
22446                    menuType: 'searchable',
22447                    searchMode
22448                };
22449            }
22450        }
22451    };
22452    const handleRefetchTrigger = (originalSandboxComp) => {
22453        // At the moment, a Sandbox is "Represented" by its triggering Dropdown.
22454        // We'll want to make this an official API, in case we change it later.
22455        const dropdown = Representing.getValue(originalSandboxComp);
22456        // Because refetch will replace the entire menu, we need to store the
22457        // original version of the searcher state, so that we can reinstate it
22458        // after the fetch completes (which is async)
22459        const optSearcherState = findWithinSandbox(originalSandboxComp).map(saveState);
22460        Dropdown.refetch(dropdown).get(() => {
22461            // It has completed, so now find the searcher and set its value
22462            // again. We can't just use the originalSandbox, because that will
22463            // have been thrown away and recreated by now.
22464            const newSandboxComp = Coupling.getCoupled(dropdown, 'sandbox');
22465            optSearcherState.each((searcherState) =>
22465 findWithinSandbox(newSandboxComp).each((inputComp) => restoreState(inputComp, searcherState)));
22466        });
22467    };
22468    // This event is triggered by the searcher for key events
22469    // that should be handled by the currently selected item
22470    // (that is, the one with *fake* focus, not real focus). So we
22471    // need to redispatch them to the selected item in the sandbox.
22472    const handleRedirectToMenuItem = (sandboxComp, se) => {
22473        getActiveMenuItemFrom(sandboxComp).each((activeItem) => {
22474            retargetAndDispatchWith(sandboxComp, activeItem.element, se.event.eventType, se.event.interactionEvent);
22475        });
22476    };
22477    // This function is useful when you have fakeFocus, so you can't just find the
22478    // currently focused item (or the item that triggered a key event). It relies on
22479    // the following relationships between components
22480    // The Sandbox creates a tieredmenu, so Sandboxing.getState returns the TieredMenu
22481    // The TieredMenu uses Highlighting for managing which menus are active, so
22482    // Highlighting.getHighlighted(tmenu) is the current active menu
22483    // The Menu uses highlighting to manage the active item, so use
22484    // Highlighting.getHighlighted(menu) to get the current item.
22485    const getActiveMenuItemFrom = (sandboxComp) => {
22486        // Consider moving some of these things into shared APIs. For example, make an extra API
22487        // for TieredMenu to get the highlighted item.
22488        return Sandboxing.getState(sandboxComp)
22489            .bind(Highlighting.getHighlighted)
22490            .bind(Highlighting.getHighlighted);
22491    };
22492    const getSearchResults = (activeMenuComp) => {
22493        // Depending on the menu layout, the search results will either be the entire
22494        // menu, or something within the menu.
22495        return has(activeMenuComp.element, searchResultsClass)
22496            ? Optional.some(activeMenuComp.element)
22497            : descendant(activeMenuComp.element, '.' + searchResultsClass);
22498    };
22499    // Model the interaction with ARIA
22500    const updateAriaOnHighlight = (tmenuComp, menuComp, itemComp) => {
22501        // This ARIA behaviour is based on the algolia example documented in TINY-8952
22502        findWithinMenu(tmenuComp).each((inputComp) => {
22503            setActiveDescendant(inputComp, itemComp);
22504            const optActiveResults = getSearchResults(menuComp);
22505            optActiveResults.each((resultsElem) => {
22506                // Link aria-controls of the input to the id of the results container.
22507                getOpt(resultsElem, 'id')
22508                    .each((controlledId) => set$9(inputComp.element, 'aria-controls', controlledId));
22509            });
22510        });
22511        // Update the aria-selected on the item. The removal is handled by onDehighlight
22512        set$9(itemComp.element, 'aria-selected', 'true');
22513    };
22514    const updateAriaOnDehighlight = (tmenuComp, menuComp, itemComp) => {
22515        // This ARIA behaviour is based on the algolia example documented in TINY-8952
22516        set$9(itemComp.element, 'aria-selected', 'false');
22517    };
22518    const focusSearchField = (tmenuComp) => {
22519        findWithinMenu(tmenuComp).each((searcherComp) => Focusing.focus(searcherComp));
22520    };
22521    const getSearchPattern = (dropdownComp) => {
22522        // Dropdowns are "coupled" with their sandbox and generally, create them on demand.
22523        // When using "getExistingCoupled" of Coupling, it only returns the coupled
22524        // component (here: the sandbox) if it already exists ... it won't do any creation.
22525        // So here, we are trying to get possible fetchContext information for our fetch
22526        // callback. If there is no sandbox, then there is no open menu, and we
22527        // don't have any search context, so use an empty string. Otherwise, dive into
22528        // the sandbox, and find the search field's current pattern.
22529        const optSandboxComp = Coupling.getExistingCoupled(dropdownComp, 'sandbox');
22530        return optSandboxComp
22531            .bind(findWithinSandbox)
22532            .map(saveState)
22533            .map((state) => state.fetchPattern)
22534            .getOr('');
22535    };
22536
22537    var FocusMode;
22538    (function (FocusMode) {
22539        FocusMode[FocusMode["ContentFocus"] = 0] = "ContentFocus";
22540        FocusMode[FocusMode["UiFocus"] = 1] = "UiFocus";
22541    })(FocusMode || (FocusMode = {}));
22542    const createMenuItemFromBridge = (item, itemResponse, backstage, menuHasIcons, isHorizontalMenu) => {
22543        const providersBackstage = backstage.shared.providers;
22544        // If we're making a horizontal menu (mobile context menu) we want text OR icons
22545        // to simplify the UI. We also don't want shortcut text.
22546        const parseForHorizontalMenu = (menuitem) => !isHorizontalMenu ? menuitem : ({
22547            ...menuitem,
22548            shortcut: Optional.none(),
22549            icon: menuitem.text.isSome() ? Optional.none() : menuitem.icon
22550        });
22551        switch (item.type) {
22552            case 'menuitem':
22553                return createMenuItem(item).fold(handleError, (d) => Optional.some(renderNormalItem(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons)));
22554            case 'nestedmenuitem':
22555                return createNestedMenuItem(item).fold(handleError, (d) => Optional.some(renderNestedItem(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons, isHorizontalMenu)));
22556            case 'togglemenuitem':
22557                return createToggleMenuItem(item).fold(handleError, (d) => Optional.some(renderToggleMenuItem(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons)));
22558            case 'separator':
22559                return createSeparatorMenuItem(item).fold(handleError, (d) => Optional.some(renderSeparatorItem(d)));
22560            case 'fancymenuitem':
22561                return createFancyMenuItem(item).fold(handleError, 
22562                // Fancy menu items don't have shortcuts or icons
22563                (d) => renderFancyMenuItem(d, backstage));
22564            default: {
22565                // eslint-disable-next-line no-console
22566                console.error('Unknown item in general menu', item);
22567                return Optional.none();
22568            }
22569        }
22570    };
22571    const createAutocompleteItems = (items, matchText, onItemValueHandler, columns, itemResponse, sharedBackstage, highlightOn) => {
22572        // Render text and icons if we're using a single column, otherwise only render icons
22573        const renderText = columns === 1;
22574        const renderIcons = !renderText || menuHasIcons(items);
22575        return cat(map$2(items, (item) => {
22576            switch (item.type) {
22577                case 'separator':
22578                    return createSeparatorItem(item).fold(handleError, (d) => Optional.some(renderSeparatorItem(d)));
22579                case 'cardmenuitem':
22580                    return createCardMenuItem(item).fold(handleError, (d) => Optional.some(renderCardMenuItem({
22581                        ...d,
22582                        // Intercept action
22583                        onAction: (api) => {
22584                            d.onAction(api);
22585                            onItemValueHandler(d.value, d.meta);
22586                        }
22587                    }, itemResponse, sharedBackstage, {
22588                        itemBehaviours: tooltipBehaviour(d.meta, sharedBackstage, Optional.none()),
22589                        cardText: {
22590                            matchText,
22591                            highlightOn
22592                        }
22593                    })));
22594                case 'autocompleteitem':
22595                default:
22596                    return createAutocompleterItem(item).fold(handleError, (d) => Optional.some(renderAutocompleteItem(d, matchText, renderText, 'normal', onItemValueHandler, itemResponse, sharedBackstage, renderIcons)));
22597            }
22598        }));
22599    };
22600    const createPartialMenu = (value, items, itemResponse, backstage, isHorizontalMenu, searchMode) => {
22601        const hasIcons = menuHasIcons(items);
22602        const alloyItems = cat(map$2(items, (item) => {
22603            // Have to check each item for an icon, instead of as part of hasIcons above,
22604            // else in horizontal menus, items with an icon but without text will display
22605            // with neither
22606            const itemHasIcon = (i) => isHorizontalMenu ? !has$2(i, 'text') : hasIcons;
22607            const createItem = (i) => createMenuItemFromBridge(i, itemResponse, backstage, itemHasIcon(i), isHorizontalMenu);
22608            if (item.type === 'nestedmenuitem' && item.getSubmenuItems().length <= 0) {
22609                return createItem({ ...item, enabled: false });
22610            }
22611            else {
22612                return createItem(item);
22613            }
22614        }));
22615        // The menu layout is dependent upon our search mode.
22616        const menuLayout = identifyMenuLayout(searchMode);
22617        const createPartial = isHorizontalMenu ?
22618            createHorizontalPartialMenuWithAlloyItems :
22619            createPartialMenuWithAlloyItems;
22620        return createPartial(value, hasIcons, alloyItems, 1, menuLayout);
22621    };
22622    const createTieredDataFrom = (partialMenu) => tieredMenu.singleData(partialMenu.value, partialMenu);
22623    const createInlineMenuFrom = (partialMenu, columns, focusMode, presets) => {
22624        const movement = deriveMenuMovement(columns, presets);
22625        const menuMarkers = markers(presets);
22626        return {
22627            data: createTieredDataFrom({
22628                ...partialMenu,
22629                movement,
22630                menuBehaviours: SimpleBehaviours.unnamedEvents(columns !== 'auto' ? [] : [
22631                    runOnAttached((comp, _se) => {
22632                        detectSize(comp, 4, menuMarkers.item).each(({ numColumns, numRows }) => {
22633                            Keying.setGridSize(comp, numRows, numColumns);
22634                        });
22635                    })
22636                ])
22637            }),
22638            menu: {
22639                markers: markers(presets),
22640                fakeFocus: focusMode === FocusMode.ContentFocus
22641            }
22642        };
22643    };
22644
22645    const rangeToSimRange = (r) => SimRange.create(SugarElement.fromDom(r.startContainer), r.startOffset, SugarElement.fromDom(r.endContainer), r.endOffset);
22646    const register$c = (editor, sharedBackstage) => {
22647        const autocompleterId = generate$6('autocompleter');
22648        const processingAction = Cell(false);
22649        const activeState = Cell(false);
22650        const activeRange = value$2();
22651        const autocompleter = build$1(InlineView.sketch({
22652            dom: {
22653                tag: 'div',
22654                classes: ['tox-autocompleter'],
22655                attributes: {
22656                    id: autocompleterId
22657                }
22658            },
22659            components: [],
22660            fireDismissalEventInstead: {},
22661            inlineBehaviours: derive$1([
22662                config('dismissAutocompleter', [
22663                    run$1(dismissRequested(), () => cancelIfNecessary()),
22664                    run$1(highlight$1(), (_, se) => {
22665                        getOpt(se.event.target, 'id').each((id) => set$9(SugarElement.fromDom(editor.getBody()), 'aria-activedescendant', id));
22666                    }),
22667                ])
22668            ]),
22669            lazySink: sharedBackstage.getSink
22670        }));
22671        const isMenuOpen = () => InlineView.isOpen(autocompleter);
22672        const isActive = activeState.get;
22673        const hideIfNecessary = () => {
22674            if (isMenuOpen()) {
22675                InlineView.hide(autocompleter);
22676                editor.dom.remove(autocompleterId, false);
22677                const editorBody = SugarElement.fromDom(editor.getBody());
22678                getOpt(editorBody, 'aria-owns')
22679                    .filter((ariaOwnsAttr) => ariaOwnsAttr === autocompleterId)
22680                    .each(() => {
22681                    remove$8(editorBody, 'aria-owns');
22682                    remove$8(editorBody, 'aria-activedescendant');
22683                });
22684            }
22685        };
22686        const getMenu = () => InlineView.getContent(autocompleter).bind((tmenu) => {
22687            // The autocompleter menu will be the first child component of the tiered menu.
22688            // Unfortunately a memento can't be used to do this lookup because the component
22689            // id is changed while generating the tiered menu.
22690            return get$i(tmenu.components(), 0);
22691        });
22692        const cancelIfNecessary = () => editor.execCommand('mceAutocompleterClose');
22693        const getCombinedItems = (matches) => {
22694            const columns = findMap(matches, (m) => Optional.from(m.columns)).getOr(1);
22695            return bind$3(matches, (match) => {
22696                const choices = match.items;
22697                return createAutocompleteItems(choices, match.matchText, (itemValue, itemMeta) => {
22698                    const autocompleterApi = {
22699                        hide: () => cancelIfNecessary(),
22700                        reload: (fetchOptions) => {
22701                            hideIfNecessary();
22702                            editor.execCommand('mceAutocompleterReload', false, { fetchOptions });
22703                        }
22704                    };
22705                    // Asks the editor for a new active range that emits an event that updates
22706                    // the activeRange state not ideal but trying to avoid direct method calls to the core.
22707                    // We need to get a fresh range since when you hit enter the IME commits and the updates the DOM so we then need to rescan.
22708                    editor.execCommand('mceAutocompleterRefreshActiveRange');
22709                    activeRange.get().each((range) => {
22710                        processingAction.set(true);
22711                        match.onAction(autocompleterApi, range, itemValue, itemMeta);
22712                        processingAction.set(false);
22713                    });
22714                }, columns, ItemResponse$1.BUBBLE_TO_SANDBOX, sharedBackstage, match.highlightOn);
22715            });
22716        };
22717        const display = (lookupData, items) => {
22718            // Display the autocompleter menu
22719            const columns = findMap(lookupData, (ld) => Optional.from(ld.columns)).getOr(1);
22720            InlineView.showMenuAt(autocompleter, {
22721                anchor: {
22722                    type: 'selection',
22723                    getSelection: () => activeRange.get().map(rangeToSimRange),
22724                    root: SugarElement.fromDom(editor.getBody()),
22725                }
22726            }
22726, createInlineMenuFrom(createPartialMenuWithAlloyItems('autocompleter-value', true, items, columns, { menuType: 'normal' }), columns, FocusMode.ContentFocus, 
22727            // Use the constant.
22728            'normal'));
22729            getMenu().each(Highlighting.highlightFirst);
22730        };
22731        const updateDisplay = (lookupData) => {
22732            const combinedItems = getCombinedItems(lookupData);
22733            // Open the autocompleter if there are items to show
22734            if (combinedItems.length > 0) {
22735                display(lookupData, combinedItems);
22736                set$9(SugarElement.fromDom(editor.getBody()), 'aria-owns', autocompleterId);
22737                if (!editor.inline) {
22738                    cloneAutocompleterToEditorDoc();
22739                }
22740            }
22741            else {
22742                hideIfNecessary();
22743            }
22744        };
22745        const cloneAutocompleterToEditorDoc = () => {
22746            if (editor.dom.get(autocompleterId)) {
22747                editor.dom.remove(autocompleterId, false);
22748            }
22749            const docElm = editor.getDoc().documentElement;
22750            const selection = editor.selection.getNode();
22751            const newElm = deep(autocompleter.element);
22752            setAll(newElm, {
22753                border: '0',
22754                clip: 'rect(0 0 0 0)',
22755                height: '1px',
22756                margin: '-1px',
22757                overflow: 'hidden',
22758                padding: '0',
22759                position: 'absolute',
22760                width: '1px',
22761                top: `${selection.offsetTop}px`,
22762                left: `${selection.offsetLeft}px`,
22763            });
22764            editor.dom.add(docElm, newElm.dom);
22765            // Clean up positioning styles so that the "hidden" autocompleter is around the selection
22766            descendant(newElm, '[role="menu"]').each((child) => {
22767                remove$6(child, 'position');
22768                remove$6(child, 'max-height');
22769            });
22770        };
22771        editor.on('AutocompleterStart', ({ lookupData }) => {
22772            activeState.set(true);
22773            processingAction.set(false);
22774            updateDisplay(lookupData);
22775        });
22776        editor.on('AutocompleterUpdate', ({ lookupData }) => updateDisplay(lookupData));
22777        editor.on('AutocompleterUpdateActiveRange', ({ range }) => activeRange.set(range));
22778        editor.on('AutocompleterEnd', () => {
22779            // Hide the menu and reset
22780            hideIfNecessary();
22781            activeState.set(false);
22782            processingAction.set(false);
22783            activeRange.clear();
22784        });
22785        const autocompleterUiApi = {
22786            cancelIfNecessary,
22787            isMenuOpen,
22788            isActive,
22789            isProcessingAction: processingAction.get,
22790            getMenu
22791        };
22792        AutocompleterEditorEvents.setup(autocompleterUiApi, editor);
22793    };
22794    const Autocompleter = {
22795        register: register$c
22796    };
22797
22798    const renderBar = (spec, backstage) => ({
22799        dom: {
22800            tag: 'div',
22801            classes: ['tox-bar', 'tox-form__controls-h-stack']
22802        },
22803        components: map$2(spec.items, backstage.interpreter)
22804    });
22805
22806    var global$4 = tinymce.util.Tools.resolve('tinymce.html.Entities');
22807
22808    const renderFormFieldWith = (pLabel, pField, extraClasses, extraBehaviours) => {
22809        const spec = renderFormFieldSpecWith(pLabel, pField, extraClasses, extraBehaviours);
22810        return FormField.sketch(spec);
22811    };
22812    const renderFormField = (pLabel, pField) => renderFormFieldWith(pLabel, pField, [], []);
22813    const renderFormFieldSpecWith = (pLabel, pField, extraClasses, extraBehaviours) => ({
22814        dom: renderFormFieldDomWith(extraClasses),
22815        components: pLabel.toArray().concat([pField]),
22816        fieldBehaviours: derive$1(extraBehaviours)
22817    });
22818    const renderFormFieldDom = () => renderFormFieldDomWith([]);
22819    const renderFormFieldDomWith = (extraClasses) => ({
22820        tag: 'div',
22821        classes: ['tox-form__group'].concat(extraClasses)
22822    });
22823    const renderLabel$3 = (label, providersBackstage) => FormField.parts.label({
22824        dom: {
22825            tag: 'label',
22826            classes: ['tox-label']
22827        },
22828        components: [
22829            text$2(providersBackstage.translate(label))
22830        ]
22831    });
22832
22833    const formChangeEvent = generate$6('form-component-change');
22834    const formInputEvent = generate$6('form-component-input');
22835    const formCloseEvent = generate$6('form-close');
22836    const formCancelEvent = generate$6('form-cancel');
22837    const formActionEvent = generate$6('form-action');
22838    const formSubmitEvent = generate$6('form-submit');
22839    const formBlockEvent = generate$6('form-block');
22840    const formUnblockEvent = generate$6('form-unblock');
22841    const formTabChangeEvent = generate$6('form-tabchange');
22842    const formResizeEvent = generate$6('form-resize');
22843
22844    const renderCollection = (spec, providersBackstage, initialData) => {
22845        // DUPE with TextField.
22846        const pLabel = spec.label.map((label) => renderLabel$3(label, providersBackstage));
22847        const icons = providersBackstage.icons();
22848        // TINY-10174: Icon string is either in icon pack or displayed directly
22849        const getIcon = (icon) => icons[icon] ?? icon;
22850        const runOnItem = (f) => (comp, se) => {
22851            closest$3(se.event.target, '[data-collection-item-value]').each((target) => {
22852                f(comp, se, target, get$g(target, 'data-collection-item-value'));
22853            });
22854        };
22855        const setContents = (comp, items) => {
22856            // Giving it a default `mode:design` context, these shouldn't run at all in mode:readonly
22857            const disabled = providersBackstage.checkUiComponentContext('mode:design').shouldDisable || providersBackstage.isDisabled();
22858            const disabledClass = disabled ? ' tox-collection__item--state-disabled' : '';
22859            const htmlLines = map$2(items, (item) => {
22860                const itemText = global$6.translate(item.text);
22861                const textContent = spec.columns === 1 ? `<div class="tox-collection__item-label">${itemText}</div>` : '';
22862                const iconContent = `<div class="tox-collection__item-icon">${getIcon(item.icon)}</div>`;
22863                // Replacing the hyphens and underscores in collection items with spaces
22864                // to ensure the screen readers pronounce the words correctly.
22865                // This is only for aria purposes. Emoticon and Special Character names will still use _ and - for autocompletion.
22866                const mapItemName = {
22867                    '_': ' ',
22868                    ' - ': ' ',
22869                    '-': ' '
22870                };
22871                // Using aria-label here overrides the Apple description of emojis and special characters in Mac/ MS description in Windows.
22872                // But if only the title attribute is used instead, the names are read out twice. i.e., the description followed by the item.text.
22873                const ariaLabel = itemText.replace(/\_| \- |\-/g, (match) => mapItemName[match]);
22874                return `<div data-mce-tooltip="${ariaLabel}" class="tox-collection__item${disabledClass}" tabindex="-1" data-collection-item-value="${global$4.encodeAllRaw(item.value)}" aria-label="${ariaLabel}">${iconContent}${textContent}</div>`;
22875            });
22876            const chunks = spec.columns !== 'auto' && spec.columns > 1 ? chunk$1(htmlLines, spec.columns) : [htmlLines];
22877            const html = map$2(chunks, (ch) => `<div class="tox-collection__group">${ch.join('')}</div>`);
22878            set$8(comp.element, html.join(''));
22879        };
22880        const onClick = runOnItem((comp, se, tgt, itemValue) => {
22881            se.stop();
22882            if (!(providersBackstage.checkUiComponentContext('mode:design').shouldDisable || providersBackstage.isDisabled())) {
22883                emitWith(comp, formActionEvent, {
22884                    name: spec.name,
22885                    value: itemValue
22886                });
22887            }
22888        });
22889        const collectionEvents = [
22890            run$1(mouseover(), runOnItem((comp, se, tgt) => {
22891                focus$4(tgt, true);
22892            })),
22893            run$1(click(), onClick),
22894            run$1(tap(), onClick),
22895            run$1(focusin(), runOnItem((comp, se, tgt) => {
22896                descendant(comp.element, '.' + activeClass).each((currentActive) => {
22897                    remove$3(currentActive, activeClass);
22898                });
22899                add$2(tgt, activeClass);
22900            })),
22901            run$1(focusout(), runOnItem((comp) => {
22902                descendant(comp.element, '.' + activeClass).each((currentActive) => {
22903                    remove$3(currentActive, activeClass);
22904                    blur$1(currentActive);
22905                });
22906            })),
22907            runOnExecute$1(runOnItem((comp, se, tgt, itemValue) => {
22908                emitWith(comp, formActionEvent, {
22909                    name: spec.name,
22910                    value: itemValue
22911                });
22912            })),
22913        ];
22914        const iterCollectionItems = (comp, applyAttributes) => map$2(descendants(comp.element, '.tox-collection__item'), applyAttributes);
22915        const pField = FormField.parts.field({
22916            dom: {
22917                tag: 'div',
22918                // FIX: Read from columns
22919                classes: ['tox-collection'].concat(spec.columns !== 1 ? ['tox-collection--grid'] : ['tox-collection--list'])
22920            },
22921            components: [],
22922            factory: { sketch: identity },
22923            behaviours: derive$1([
22924                Disabling.config({
22925                    disabled: () => providersBackstage.checkUiComponentContext(spec.context).shouldDisable,
22926                    onDisabled: (comp) => {
22927                        iterCollectionItems(comp, (childElm) => {
22928                            add$2(childElm, 'tox-collection__item--state-disabled');
22929                            set$9(childElm, 'aria-disabled', true);
22930                        });
22931                    },
22932                    onEnabled: (comp) => {
22933                        iterCollectionItems(comp, (childElm) => {
22934                            remove$3(childElm, 'tox-collection__item--state-disabled');
22935                            remove$8(childElm, 'aria-disabled');
22936                        });
22937                    }
22938                }),
22939                toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context)),
22940                Replacing.config({}),
22941                Tooltipping.config({
22942                    ...providersBackstage.tooltips.getConfig({
22943                        tooltipText: '',
22944                        onShow: (comp) => {
22945                            descendant(comp.element, '.' + activeClass + '[data-mce-tooltip]').each((current) => {
22946                                getOpt(current, 'data-mce-tooltip').each((text) => {
22947                                    Tooltipping.setComponents(comp, providersBackstage.tooltips.getComponents({ tooltipText: text }));
22948                                });
22949                            });
22950                        }
22951                    }),
22952                    mode: 'children-keyboard-focus',
22953                    anchor: (comp) => ({
22954                        type: 'node',
22955                        node: descendant(comp.element, '.' + activeClass).orThunk(() => first('.tox-collection__item')),
22956                        root: comp.element,
22957                        layouts: {
22958                            onLtr: constant$1([south$2, north$2, southeast$2, northeast$2, southwest$2, northwest$2]),
22959                            onRtl: constant$1([south$2, north$2, southeast$2, northeast$2, southwest$2, northwest$2])
22960                        },
22961                        bubble: nu$6(0, -2, {}),
22962                    })
22963                }),
22964                Representing.config({
22965                    store: {
22966                        mode: 'memory',
22967                        initialValue: initialData.getOr([])
22968                    },
22969                    onSetValue: (comp, items) => {
22970                        setContents(comp, items);
22971                        if (spec.columns === 'auto') {
22972                            detectSize(comp, 5, 'tox-collection__item').each(({ numRows, numColumns }) => {
22973                                Keying.setGridSize(comp, numRows, numColumns);
22974                            });
22975                        }
22976                        emit(comp, formResizeEvent);
22977                    }
22978                }),
22979                Tabstopping.config({}),
22980                Keying.config(deriveCollectionMovement(spec.columns, 'normal')),
22981                config('collection-events', collectionEvents)
22982            ]),
22983            eventOrder: {
22984                [execute$5()]: ['disabling', 'alloy.base.behaviour', 'collection-events'],
22985                [focusin()]: ['collection-events', 'tooltipping'],
22986            }
22987        });
22988        const extraClasses = ['tox-form__group--collection'];
22989        return renderFormFieldWith(pLabel, pField, extraClasses, []);
22990    };
22991
22992    const renderPanelButton = (spec, sharedBackstage) => Dropdown.sketch({
22993        dom: spec.dom,
22994        components: spec.components,
22995        toggleClass: 'mce-active',
22996        dropdownBehaviours: derive$1([
22997            DisablingConfigs.button(() => sharedBackstage.providers.isDisabled() || sharedBackstage.providers.checkUiComponentContext(spec.context).shouldDisable),
22998            toggleOnReceive(() => sharedBackstage.providers.checkUiComponentContext(spec.context)),
22999            Unselecting.config({}),
23000            Tabstopping.config({})
23001        ]),
23002        layouts: spec.layouts,
23003        sandboxClasses: ['tox-dialog__popups'],
23004        lazySink: sharedBackstage.getSink,
23005        fetch: (comp) => Future.nu((callback) => spec.fetch(callback)).map((items) => Optional.from(createTieredDataFrom(deepMerge(createPartialChoiceMenu(generate$6('menu-value'), items, (value) => {
23006            spec.onItemAction(comp, value);
23007        }, spec.columns, spec.presets, ItemResponse$1.CLOSE_ON_EXECUTE, 
23008        // No colour is ever selected on opening
23009        never, sharedBackstage.providers), {
23010            movement: deriveMenuMovement(spec.columns, spec.presets)
23011        })))),
23012        parts: {
23013            menu: part(false, 1, spec.presets)
23014        }
23015    });
23016
23017    const colorInputChangeEvent = generate$6('color-input-change');
23018    const colorSwatchChangeEvent = generate$6('color-swatch-change');
23019    const colorPickerCancelEvent = generate$6('color-picker-cancel');
23020    const renderColorInput = (spec, sharedBackstage, colorInputBackstage, initialData) => {
23021        const pField = FormField.parts.field({
23022            factory: Input,
23023            inputClasses: ['tox-textfield'],
23024            data: initialData,
23025            onSetValue: (c) => Invalidating.run(c).get(noop),
23026            inputBehaviours: derive$1([
23027                Disabling.config({
23028                    disabled: () => sharedBackstage.providers.isDisabled() || sharedBackstage.providers.checkUiComponentContext(spec.context).shouldDisable
23029                }),
23030                toggleOnReceive(() => sharedBackstage.providers.checkUiComponentContext(spec.context)),
23031                Tabstopping.config({}),
23032                Invalidating.config({
23033                    invalidClass: 'tox-textbox-field-invalid',
23034                    getRoot: (comp) => parentElement(comp.element),
23035                    notify: {
23036                        onValid: (comp) => {
23037                            // onValid should pass through the value here
23038                            // We need a snapshot of the value validated.
23039                            const val = Representing.getValue(comp);
23040                            emitWith(comp, colorInputChangeEvent, {
23041                                color: val
23042                            });
23043                        }
23044                    },
23045                    validator: {
23046                        validateOnLoad: false,
23047                        validate: (input) => {
23048                            const inputValue = Representing.getValue(input);
23049                            // Consider empty strings valid colours
23050                            if (inputValue.length === 0) {
23051                                return Future.pure(Result.value(true));
23052                            }
23053                            else {
23054                                const span = SugarElement.fromTag('span');
23055                                set$7(span, 'background-color', inputValue);
23056                                const res = getRaw(span, 'background-color').fold(
23057                                // TODO: Work out what we want to do here.
23058                                () => Result.error('blah'), (_) => Result.value(inputValue));
23059                                return Future.pure(res);
23060                            }
23061                        }
23062                    }
23063                })
23064            ]),
23065            selectOnFocus: false
23066        });
23067        const pLabel = spec.label.map((label) => renderLabel$3(label, sharedBackstage.providers));
23068        const emitSwatchChange = (colorBit, value) => {
23069            emitWith(colorBit, colorSwatchChangeEvent, {
23070                value
23071            });
23072        };
23073        const onItemAction = (comp, value) => {
23074            memColorButton.getOpt(comp).each((colorBit) => {
23075                if (value === 'custom') {
23076                    colorInputBackstage.colorPicker((valueOpt) => {
23077                        valueOpt.fold(() => emit(colorBit, colorPickerCancelEvent), (value) => {
23078                            emitSwatchChange(colorBit, value);
vendor: 4,985 bytes, lines 23079-23208
23079                            addColor(spec.storageKey, value);
23080                        });
23081                    }, '#ffffff');
23082                }
23083                else if (value === 'remove') {
23084                    emitSwatchChange(colorBit, '');
23085                }
23086                else {
23087                    emitSwatchChange(colorBit, value);
23088                }
23089            });
23090        };
23091        const memColorButton = record(renderPanelButton({
23092            dom: {
23093                tag: 'span',
23094                attributes: {
23095                    'aria-label': sharedBackstage.providers.translate('Color swatch')
23096                }
23097            },
23098            layouts: {
23099                onRtl: () => [southwest$2, southeast$2, south$2],
23100                onLtr: () => [southeast$2, southwest$2, south$2]
23101            },
23102            components: [],
23103            fetch: getFetch$1(colorInputBackstage.getColors(spec.storageKey), spec.storageKey, colorInputBackstage.hasCustomColors()),
23104            columns: colorInputBackstage.getColorCols(spec.storageKey),
23105            presets: 'color',
23106            onItemAction,
23107            context: spec.context
23108        }, sharedBackstage));
23109        return FormField.sketch({
23110            dom: {
23111                tag: 'div',
23112                classes: ['tox-form__group']
23113            },
23114            components: pLabel.toArray().concat([
23115                {
23116                    dom: {
23117                        tag: 'div',
23118                        classes: ['tox-color-input']
23119                    },
23120                    components: [
23121                        pField,
23122                        memColorButton.asSpec()
23123                    ]
23124                }
23125            ]),
23126            fieldBehaviours: derive$1([
23127                config('form-field-events', [
23128                    run$1(colorInputChangeEvent, (comp, se) => {
23129                        memColorButton.getOpt(comp).each((colorButton) => {
23130                            set$7(colorButton.element, 'background-color', se.event.color);
23131                        });
23132                        emitWith(comp, formChangeEvent, { name: spec.name });
23133                    }),
23134                    run$1(colorSwatchChangeEvent, (comp, se) => {
23135                        FormField.getField(comp).each((field) => {
23136                            Representing.setValue(field, se.event.value);
23137                            // Focus the field now that we've set its value
23138                            Composing.getCurrent(comp).each(Focusing.focus);
23139                        });
23140                    }),
23141                    run$1(colorPickerCancelEvent, (comp, _se) => {
23142                        FormField.getField(comp).each((_field) => {
23143                            Composing.getCurrent(comp).each(Focusing.focus);
23144                        });
23145                    })
23146                ])
23147            ])
23148        });
23149    };
23150
23151    // TODO: Move this to alloy if the concept works out
23152    // eslint-disable-next-line consistent-this
23153    const self = () => Composing.config({
23154        find: Optional.some
23155    });
23156    const memento$1 = (mem) => Composing.config({
23157        find: mem.getOpt
23158    });
23159    const childAt = (index) => Composing.config({
23160        find: (comp) => child$2(comp.element, index)
23161            .bind((element) => comp.getSystem().getByDom(element).toOptional())
23162    });
23163    const ComposingConfigs = {
23164        self,
23165        memento: memento$1,
23166        childAt
23167    };
23168
23169    const processors = objOf([
23170        defaulted('preprocess', identity),
23171        defaulted('postprocess', identity)
23172    ]);
23173    const memento = (mem, rawProcessors) => {
23174        const ps = asRawOrDie$1('RepresentingConfigs.memento processors', processors, rawProcessors);
23175        return Representing.config({
23176            store: {
23177                mode: 'manual',
23178                getValue: (comp) => {
23179                    const other = mem.get(comp);
23180                    const rawValue = Representing.getValue(other);
23181                    return ps.postprocess(rawValue);
23182                },
23183                setValue: (comp, rawValue) => {
23184                    const newValue = ps.preprocess(rawValue);
23185                    const other = mem.get(comp);
23186                    Representing.setValue(other, newValue);
23187                }
23188            }
23189        });
23190    };
23191    const withComp = (optInitialValue, getter, setter) => Representing.config({
23192        store: {
23193            mode: 'manual',
23194            ...optInitialValue.map((initialValue) => ({ initialValue })).getOr({}),
23195            getValue: getter,
23196            setValue: setter
23197        }
23198    });
23199    const withElement = (initialValue, getter, setter) => withComp(initialValue, (c) => getter(c.element), (c, v) => setter(c.element, v));
23200    const domHtml = (optInitialValue) => withElement(optInitialValue, get$f, set$8);
23201    const memory = (initialValue) => Representing.config({
23202        store: {
23203            mode: 'memory',
23204            initialValue
23205        }
23206    });
23207
23208    const fieldsUpdate = generate$6('rgb-hex-update');
23209    const sliderUpdate = generate$6('slider-update');
23210    const paletteUpdate = generate$6('palette-update');
23211
23212    const sliderFactory = (translate, getClass) => {
23213        const spectrum = Slider.parts.spectrum({
23214            dom: {
23215                tag: 'div',
23216                classes: [getClass('hue-slider-spectrum')],
23217                attributes: {
23218                    role: 'presentation'
23219                }
23220            }
23221        });
23222        const thumb = Slider.parts.thumb({
23223            dom: {
23224                tag: 'div',
23225                classes: [getClass('hue-slider-thumb')],
23226                attributes: {
23227                    role: 'presentation'
23228                }
23229            }
23230        });
23231        return Slider.sketch({
23232            dom: {
23233                tag: 'div',
23234                classes: [getClass('hue-slider')],
23235                attributes: {
23236                    'role': 'slider',
23237                    'aria-valuemin': 0,
23238                    'aria-valuemax': 360,
23239                    'aria-valuenow': 120,
23240                }
23241            },
23242            rounded: false,
23243            model: {
23244                mode: 'y',
23245                getInitialValue: constant$1(0)
23246            },
23247            components: [
23248                spectrum,
23249                thumb
23250            ],
23251            sliderBehaviours: derive$1([
23252                Focusing.config({})
23253            ]),
23254            onChange: (slider, _thumb, value) => {
23255                set$9(slider.element, 'aria-valuenow', Math.floor(360 - (value * 3.6)));
23256                emitWith(slider, sliderUpdate, {
23257                    value
23258                });
23259            }
23260        });
23261    };
23262
23263    const validInput = generate$6('valid-input');
23264    const invalidInput = generate$6('invalid-input');
23265    const validatingInput = generate$6('validating-input');
23266    const translatePrefix = 'colorcustom.rgb.';
23267    const uninitiatedTooltipApi = {
23268        isEnabled: always,
23269        setEnabled: noop,
23270        immediatelyShow: noop,
23271        immediatelyHide: noop,
23272    };
23273    const rgbFormFactory = (translate, getClass, onValidHexx, onInvalidHexx, tooltipGetConfig, makeIcon) => {
23274        const setTooltipEnabled = (enabled, tooltipApi) => {
23275            const api = tooltipApi.get();
23276            if (enabled === api.isEnabled()) {
23277                return;
23278            }
23279            api.setEnabled(enabled);
23280            if (enabled) {
23281                api.immediatelyShow();
23282            }
23283            else {
23284                api.immediatelyHide();
23285            }
23286        };
23287        const invalidation = (label, isValid, tooltipApi) => Invalidating.config({
23288            invalidClass: getClass('invalid'),
23289            notify: {
23290                onValidate: (comp) => {
23291                    emitWith(comp, validatingInput, {
23292                        type: label
23293                    });
23294                },
23295                onValid: (comp) => {
23296                    setTooltipEnabled(false, tooltipApi);
23297                    emitWith(comp, validInput, {
23298                        type: label,
23299                        value: Representing.getValue(comp)
23300                    });
23301                },
23302                onInvalid: (comp) => {
23303                    setTooltipEnabled(true, tooltipApi);
23304                    emitWith(comp, invalidInput, {
23305                        type: label,
23306                        value: Representing.getValue(comp)
23307                    });
23308                }
23309            },
23310            validator: {
23311                validate: (comp) => {
23312                    const value = Representing.getValue(comp);
23313                    const res = isValid(value) ? Result.value(true) : Result.error(translate('aria.input.invalid'));
23314                    return Future.pure(res);
23315                },
23316                validateOnLoad: false
23317            }
23318        });
23319        const renderTextField = (isValid, name, label, description, data) => {
23320            const tooltipApi = Cell(uninitiatedTooltipApi);
23321            const helptext = translate(translatePrefix + 'range');
23322            const pLabel = FormField.parts.label({
23323                dom: { tag: 'label' },
23324                components: [text$2(label)]
23325            });
23326            const pField = FormField.parts.field({
23327                data,
23328                factory: Input,
23329                inputAttributes: {
23330                    'type': 'text',
23331                    'aria-label': description,
23332                    ...name === 'hex' ? { 'aria-live': 'polite' } : {}
23333                },
23334                inputClasses: [getClass('textfield')],
23335                // Have basic invalidating and tabstopping behaviour.
23336                inputBehaviours: derive$1([
23337                    invalidation(name, isValid, tooltipApi),
23338                    Tabstopping.config({}),
23339                    Tooltipping.config({
23340                        ...tooltipGetConfig({
23341                            tooltipText: '',
23342                            onSetup: (comp) => {
23343                                tooltipApi.set({
23344                                    isEnabled: () => {
23345                                        return Tooltipping.isEnabled(comp);
23346                                    },
23347                                    setEnabled: (enabled) => {
23348                                        return Tooltipping.setEnabled(comp, enabled);
23349                                    },
23350                                    immediatelyShow: () => {
23351                                        return Tooltipping.immediateOpenClose(comp, true);
23352                                    },
23353                                    immediatelyHide: () => {
23354                                        return Tooltipping.immediateOpenClose(comp, false);
23355                                    },
23356                                });
23357                                Tooltipping.setEnabled(comp, false);
23358                            },
23359                            onShow: (component, _tooltip) => {
23360                                Tooltipping.setComponents(component, [
23361                                    {
23362                                        dom: {
23363                                            tag: 'p',
23364                                            classes: [
23365                                                getClass('rgb-warning-note')
23366                                            ]
23367                                        },
23368                                        components: [text$2(translate(name === 'hex' ? 'colorcustom.rgb.invalidHex' : 'colorcustom.rgb.invalid'))]
23369                                    }
23370                                ]);
23371                            },
23372                        })
23373                    })
23374                ]),
23375                // If it was invalid, and the value was set, run validation against it.
23376                onSetValue: (input) => {
23377                    if (Invalidating.isInvalid(input)) {
23378                        const run = Invalidating.run(input);
23379                        run.get(noop);
23380                    }
23381                }
23382            });
23383            const errorId = generate$6('aria-invalid');
23384            const memInvalidIcon = record(makeIcon('invalid', Optional.some(errorId), 'warning'));
23385            const memStatus = record({
23386                dom: {
23387                    tag: 'div',
23388                    classes: [getClass('invalid-icon')]
23389                },
23390                components: [
23391                    memInvalidIcon.asSpec()
23392                ]
23393            });
23394            const comps = [pLabel, pField, memStatus.asSpec()];
23395            const concats = name !== 'hex' ? [FormField.parts['aria-descriptor']({
23396                    text: helptext
23397                })] : [];
23398            const components = comps.concat(concats);
23399            return {
23400                dom: {
23401                    tag: 'div',
23402                    attributes: {
23403                        role: 'presentation'
23404                    },
23405                    classes: [
23406                        getClass('rgb-container'),
23407                    ]
23408                },
23409                components
23410            };
23411        };
23412        const copyRgbToHex = (form, rgba) => {
23413            const hex = fromRgba(rgba);
23414            Form.getField(form, 'hex').each((hexField) => {
23415                // Not amazing, but it turns out that if we have an invalid RGB field, and no hex code
23416                // and then type in a valid three digit hex code, the RGB field will be overriden, then validate and then set
23417                // the hex field to be the six digit version of that same three digit hex code. This is incorrect.
23418                if (!Focusing.isFocused(hexField)) {
23419                    Representing.setValue(form, {
23420                        hex: hex.value
23421                    });
23422                }
23423            });
23424            return hex;
23425        };
vendor: 10,351 bytes, lines 23426-23679
23426        const copyRgbToForm = (form, rgb) => {
23427            const red = rgb.red;
23428            const green = rgb.green;
23429            const blue = rgb.blue;
23430            Representing.setValue(form, { red, green, blue });
23431        };
23432        const memPreview = record({
23433            dom: {
23434                tag: 'div',
23435                classes: [getClass('rgba-preview')],
23436                styles: {
23437                    'background-color': 'white'
23438                },
23439                attributes: {
23440                    role: 'presentation'
23441                }
23442            }
23443        });
23444        const updatePreview = (anyInSystem, hex) => {
23445            memPreview.getOpt(anyInSystem).each((preview) => {
23446                set$7(preview.element, 'background-color', '#' + hex.value);
23447            });
23448        };
23449        const factory = () => {
23450            const state = {
23451                red: Cell(Optional.some(255)),
23452                green: Cell(Optional.some(255)),
23453                blue: Cell(Optional.some(255)),
23454                hex: Cell(Optional.some('ffffff'))
23455            };
23456            const copyHexToRgb = (form, hex) => {
23457                const rgb = fromHex(hex);
23458                copyRgbToForm(form, rgb);
23459                setValueRgb(rgb);
23460            };
23461            const get = (prop) => state[prop].get();
23462            const set = (prop, value) => {
23463                state[prop].set(value);
23464            };
23465            const getValueRgb = () => get('red').bind((red) => get('green').bind((green) => get('blue').map((blue) => rgbaColour(red, green, blue, 1))));
23466            // TODO: Find way to use this for palette and slider updates
23467            const setValueRgb = (rgb) => {
23468                const red = rgb.red;
23469                const green = rgb.green;
23470                const blue = rgb.blue;
23471                set('red', Optional.some(red));
23472                set('green', Optional.some(green));
23473                set('blue', Optional.some(blue));
23474            };
23475            const onInvalidInput = (form, simulatedEvent) => {
23476                const data = simulatedEvent.event;
23477                if (data.type !== 'hex') {
23478                    set(data.type, Optional.none());
23479                }
23480                else {
23481                    onInvalidHexx(form);
23482                }
23483            };
23484            const onValidHex = (form, value) => {
23485                onValidHexx(form);
23486                const hex = hexColour(value);
23487                set('hex', Optional.some(hex.value));
23488                const rgb = fromHex(hex);
23489                copyRgbToForm(form, rgb);
23490                setValueRgb(rgb);
23491                emitWith(form, fieldsUpdate, {
23492                    hex
23493                });
23494                updatePreview(form, hex);
23495            };
23496            const onValidRgb = (form, prop, value) => {
23497                const val = parseInt(value, 10);
23498                set(prop, Optional.some(val));
23499                getValueRgb().each((rgb) => {
23500                    const hex = copyRgbToHex(form, rgb);
23501                    emitWith(form, fieldsUpdate, {
23502                        hex
23503                    });
23504                    updatePreview(form, hex);
23505                });
23506            };
23507            const isHexInputEvent = (data) => data.type === 'hex';
23508            const onValidInput = (form, simulatedEvent) => {
23509                const data = simulatedEvent.event;
23510                if (isHexInputEvent(data)) {
23511                    onValidHex(form, data.value);
23512                }
23513                else {
23514                    onValidRgb(form, data.type, data.value);
23515                }
23516            };
23517            const formPartStrings = (key) => ({
23518                label: translate(translatePrefix + key + '.label'),
23519                description: translate(translatePrefix + key + '.description')
23520            });
23521            const redStrings = formPartStrings('red');
23522            const greenStrings = formPartStrings('green');
23523            const blueStrings = formPartStrings('blue');
23524            const hexStrings = formPartStrings('hex');
23525            // TODO: Provide a nice way of adding APIs to existing sketchers
23526            return deepMerge(Form.sketch((parts) => ({
23527                dom: {
23528                    tag: 'form',
23529                    classes: [getClass('rgb-form')],
23530                    attributes: { 'aria-label': translate('aria.color.picker') }
23531                },
23532                components: [
23533                    parts.field('red', FormField.sketch(renderTextField(isRgbaComponent, 'red', redStrings.label, redStrings.description, 255))),
23534                    parts.field('green', FormField.sketch(renderTextField(isRgbaComponent, 'green', greenStrings.label, greenStrings.description, 255))),
23535                    parts.field('blue', FormField.sketch(renderTextField(isRgbaComponent, 'blue', blueStrings.label, blueStrings.description, 255))),
23536                    parts.field('hex', FormField.sketch(renderTextField(isHexString, 'hex', hexStrings.label, hexStrings.description, 'ffffff'))),
23537                    memPreview.asSpec()
23538                ],
23539                formBehaviours: derive$1([
23540                    Invalidating.config({
23541                        invalidClass: getClass('form-invalid')
23542                    }),
23543                    config('rgb-form-events', [
23544                        run$1(validInput, onValidInput),
23545                        run$1(invalidInput, onInvalidInput),
23546                        run$1(validatingInput, onInvalidInput)
23547                    ])
23548                ])
23549            })), {
23550                apis: {
23551                    updateHex: (form, hex) => {
23552                        Representing.setValue(form, {
23553                            hex: hex.value
23554                        });
23555                        copyHexToRgb(form, hex);
23556                        updatePreview(form, hex);
23557                    }
23558                }
23559            });
23560        };
23561        const rgbFormSketcher = single({
23562            factory,
23563            name: 'RgbForm',
23564            configFields: [],
23565            apis: {
23566                updateHex: (apis, form, hex) => {
23567                    apis.updateHex(form, hex);
23568                }
23569            },
23570            extraApis: {}
23571        });
23572        return rgbFormSketcher;
23573    };
23574
23575    const paletteFactory = (translate, getClass) => {
23576        const spectrumPart = Slider.parts.spectrum({
23577            dom: {
23578                tag: 'canvas',
23579                attributes: {
23580                    role: 'presentation'
23581                },
23582                classes: [getClass('sv-palette-spectrum')]
23583            }
23584        });
23585        const thumbPart = Slider.parts.thumb({
23586            dom: {
23587                tag: 'div',
23588                attributes: {
23589                    role: 'presentation'
23590                },
23591                classes: [getClass('sv-palette-thumb')],
23592                innerHtml: `<div class=${getClass('sv-palette-inner-thumb')} role="presentation"></div>`
23593            }
23594        });
23595        const setColour = (canvas, rgba) => {
23596            const { width, height } = canvas;
23597            const ctx = canvas.getContext('2d');
23598            if (ctx === null) {
23599                return;
23600            }
23601            ctx.fillStyle = rgba;
23602            ctx.fillRect(0, 0, width, height);
23603            const grdWhite = ctx.createLinearGradient(0, 0, width, 0);
23604            grdWhite.addColorStop(0, 'rgba(255,255,255,1)');
23605            grdWhite.addColorStop(1, 'rgba(255,255,255,0)');
23606            ctx.fillStyle = grdWhite;
23607            ctx.fillRect(0, 0, width, height);
23608            const grdBlack = ctx.createLinearGradient(0, 0, 0, height);
23609            grdBlack.addColorStop(0, 'rgba(0,0,0,0)');
23610            grdBlack.addColorStop(1, 'rgba(0,0,0,1)');
23611            ctx.fillStyle = grdBlack;
23612            ctx.fillRect(0, 0, width, height);
23613        };
23614        const setPaletteHue = (slider, hue) => {
23615            const canvas = slider.components()[0].element.dom;
23616            const hsv = hsvColour(hue, 100, 100);
23617            const rgba = fromHsv(hsv);
23618            setColour(canvas, toString(rgba));
23619        };
23620        const setPaletteThumb = (slider, hex) => {
23621            const hsv = fromRgb(fromHex(hex));
23622            Slider.setValue(slider, { x: hsv.saturation, y: 100 - hsv.value });
23623            set$9(slider.element, 'aria-valuetext', translate(['Saturation {0}%, Brightness {1}%', hsv.saturation, hsv.value]));
23624        };
23625        const factory = (_detail) => {
23626            const getInitialValue = constant$1({
23627                x: 0,
23628                y: 0
23629            });
23630            const onChange = (slider, _thumb, value) => {
23631                if (!isNumber(value)) {
23632                    set$9(slider.element, 'aria-valuetext', translate(['Saturation {0}%, Brightness {1}%', Math.floor(value.x), Math.floor(100 - value.y)]));
23633                }
23634                emitWith(slider, paletteUpdate, {
23635                    value
23636                });
23637            };
23638            const onInit = (_slider, _thumb, spectrum, _value) => {
23639                // Maybe make this initial value configurable?
23640                setColour(spectrum.element.dom, toString(red));
23641            };
23642            const sliderBehaviours = derive$1([
23643                Composing.config({
23644                    find: Optional.some
23645                }),
23646                Focusing.config({})
23647            ]);
23648            return Slider.sketch({
23649                dom: {
23650                    tag: 'div',
23651                    attributes: {
23652                        'role': 'slider',
23653                        'aria-valuetext': translate(['Saturation {0}%, Brightness {1}%', 0, 0])
23654                    },
23655                    classes: [getClass('sv-palette')]
23656                },
23657                model: {
23658                    mode: 'xy',
23659                    getInitialValue,
23660                },
23661                rounded: false,
23662                components: [
23663                    spectrumPart,
23664                    thumbPart
23665                ],
23666                onChange,
23667                onInit,
23668                sliderBehaviours
23669            });
23670        };
23671        const saturationBrightnessPaletteSketcher = single({
23672            factory,
23673            name: 'SaturationBrightnessPalette',
23674            configFields: [],
23675            apis: {
23676                setHue: (_apis, slider, hue) => {
23677                    setPaletteHue(slider, hue);
23678                },
23679                setThumb: (_apis, slider, hex) => {
23680                    setPaletteThumb(slider, hex);
23681                }
23682            },
23683            extraApis: {}
23684        });
23685        return saturationBrightnessPaletteSketcher;
23686    };
23687
23688    const makeFactory = (translate, getClass, tooltipConfig, makeIcon) => {
23689        const factory = (detail) => {
23690            const rgbForm = rgbFormFactory(translate, getClass, detail.onValidHex, detail.onInvalidHex, tooltipConfig, makeIcon);
23691            const sbPalette = paletteFactory(translate, getClass);
23692            const hueSliderToDegrees = (hue) => (100 - hue) / 100 * 360;
23693            const hueDegreesToSlider = (hue) => 100 - (hue / 360) * 100;
23694            const state = {
23695                paletteRgba: Cell(red),
23696                paletteHue: Cell(0)
23697            };
23698            const memSlider = record(sliderFactory(translate, getClass));
23699            const memPalette = record(sbPalette.sketch({}));
23700            const memRgb = record(rgbForm.sketch({}));
23701            const updatePalette = (anyInSystem, _hex, hue) => {
23702                memPalette.getOpt(anyInSystem).each((palette) => {
23703                    sbPalette.setHue(palette, hue);
23704                });
23705            };
23706            const updateFields = (anyInSystem, hex) => {
23707                memRgb.getOpt(anyInSystem).each((form) => {
23708                    rgbForm.updateHex(form, hex);
23709                });
23710            };
23711            const updateSlider = (anyInSystem, _hex, hue) => {
23712                memSlider.getOpt(anyInSystem).each((slider) => {
23713                    Slider.setValue(slider, hueDegreesToSlider(hue));
23714                });
23715            };
23716            const updatePaletteThumb = (anyInSystem, hex) => {
23717                memPalette.getOpt(anyInSystem).each((palette) => {
23718                    sbPalette.setThumb(palette, hex);
23719                });
23720            };
23721            const updateState = (hex, hue) => {
23722                const rgba = fromHex(hex);
23723                state.paletteRgba.set(rgba);
23724                state.paletteHue.set(hue);
23725            };
23726            const runUpdates = (anyInSystem, hex, hue, updates) => {
23727                updateState(hex, hue);
23728                each$1(updates, (update) => {
23729                    update(anyInSystem, hex, hue);
23730                });
23731            };
23732            const onPaletteUpdate = () => {
23733                const updates = [updateFields];
23734                return (form, simulatedEvent) => {
23735                    const value = simulatedEvent.event.value;
23736                    const oldHue = state.paletteHue.get();
23737                    const newHsv = hsvColour(oldHue, value.x, (100 - value.y));
23738                    const newHex = hsvToHex(newHsv);
23739                    runUpdates(form, newHex, oldHue, updates);
23740                };
23741            };
23742            const onSliderUpdate = () => {
23743                const updates = [updatePalette, updateFields];
23744                return (form, simulatedEvent) => {
23745                    const hue = hueSliderToDegrees(simulatedEvent.event.value);
23746                    const oldRgb = state.paletteRgba.get();
23747                    const oldHsv = fromRgb(oldRgb);
23748                    const newHsv = hsvColour(hue, oldHsv.saturation, oldHsv.value);
23749                    const newHex = hsvToHex(newHsv);
23750                    runUpdates(form, newHex, hue, updates);
23751                };
23752            };
23753            const onFieldsUpdate = () => {
23754                const updates = [updatePalette, updateSlider, updatePaletteThumb];
23755                return (form, simulatedEvent) => {
23756                    const hex = simulatedEvent.event.hex;
23757                    const hsv = hexToHsv(hex);
23758                    runUpdates(form, hex, hsv.hue, updates);
23759                };
23760            };
23761            return {
23762                uid: detail.uid,
23763                dom: detail.dom,
23764                components: [
23765                    memPalette.asSpec(),
23766                    memSlider.asSpec(),
23767                    memRgb.asSpec()
23768                ],
23769                behaviours: derive$1([
23770                    config('colour-picker-events', [
23771                        run$1(fieldsUpdate, onFieldsUpdate()),
23772                        run$1(paletteUpdate, onPaletteUpdate()),
23773                        run$1(sliderUpdate, onSliderUpdate())
23774                    ]),
23775                    Composing.config({
23776                        find: (comp) => memRgb.getOpt(comp)
23777                    }),
23778                    Keying.config({
23779                        mode: 'acyclic'
23780                    })
23781                ])
23782            };
23783        };
23784        const colourPickerSketcher = single({
23785            name: 'ColourPicker',
23786            configFields: [
23787                required$1('dom'),
23788                defaulted('onValidHex', noop),
23789                defaulted('onInvalidHex', noop)
23790            ],
23791            factory
23792        });
23793        return colourPickerSketcher;
23794    };
23795
23796    const english = {
23797        'colorcustom.rgb.red.label': 'R',
23798        'colorcustom.rgb.red.description': 'Red channel',
23799        'colorcustom.rgb.green.label': 'G',
23800        'colorcustom.rgb.green.description': 'Green channel',
23801        'colorcustom.rgb.blue.label': 'B',
23802        'colorcustom.rgb.blue.description': 'Blue channel',
23803        'colorcustom.rgb.hex.label': '#',
23804        'colorcustom.rgb.hex.description': 'Hex color code',
23805        'colorcustom.rgb.range': 'Range 0 to 255',
23806        'colorcustom.rgb.invalid': 'Numbers only, 0 to 255',
23807        'colorcustom.rgb.invalidHex': 'Hexadecimal only, 000000 to FFFFFF',
23808        'aria.color.picker': 'Color Picker',
23809        'aria.input.invalid': 'Invalid input'
23810    };
23811    const translate = (providerBackstage) => (key) => {
23812        if (isString(key)) {
23813            return providerBackstage.translate(english[key]);
23814        }
23815        else {
23816            return providerBackstage.translate(key);
23817        }
23818    };
23819    const renderColorPicker = (_spec, providerBackstage, initialData) => {
23820        const getClass = (key) => 'tox-' + key;
23821        const renderIcon = (name, errId, icon = name, label = name) => render$4(icon, {
23822            tag: 'div',
23823            classes: ['tox-icon', 'tox-control-wrap__status-icon-' + name],
23824            attributes: {
23825                'title': providerBackstage.translate(label),
23826                'aria-live': 'polite',
23827                ...errId.fold(() => ({}), (id) => ({ id }))
23828            }
23829        }, providerBackstage.icons);
23830        const colourPickerFactory = makeFactory(translate(providerBackstage), getClass, providerBackstage.tooltips.getConfig, renderIcon);
23831        const onValidHex = (form) => {
23832            emitWith(form, formActionEvent, { name: 'hex-valid', value: true });
23833        };
23834        const onInvalidHex = (form) => {
23835            emitWith(form, formActionEvent, { name: 'hex-valid', value: false });
23836        };
23837        const memPicker = record(colourPickerFactory.sketch({
23838            dom: {
23839                tag: 'div',
23840                classes: [getClass('color-picker-container')],
23841                attributes: {
23842                    role: 'presentation'
23843                }
23844            },
23845            onValidHex,
23846            onInvalidHex
23847        }));
23848        return {
23849            dom: {
23850                tag: 'div'
23851            },
23852            components: [
23853                memPicker.asSpec()
23854            ],
23855            behaviours: derive$1([
23856                // We'll allow invalid values
23857                withComp(initialData, (comp) => {
23858                    const picker = memPicker.get(comp);
23859                    const optRgbForm = Composing.getCurrent(picker);
23860                    const optHex = optRgbForm.bind((rgbForm) => {
23861                        const formValues = Representing.getValue(rgbForm);
23862                        return formValues.hex;
23863                    });
23864                    return optHex.map((hex) => '#' + removeLeading(hex, '#')).getOr('');
23865                }, (comp, newValue) => {
23866                    const pattern = /^#([a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?)/;
23867                    const valOpt = Optional.from(pattern.exec(newValue)).bind((matches) => get$i(matches, 1));
23868                    const picker = memPicker.get(comp);
23869                    const optRgbForm = Composing.getCurrent(picker);
23870                    optRgbForm.fold(() => {
23871                        // eslint-disable-next-line no-console
23872                        console.log('Can not find form');
23873                    }, (rgbForm) => {
23874                        Representing.setValue(rgbForm, {
23875                            hex: valOpt.getOr('')
23876                        });
23877                        // So not the way to do this.
23878                        Form.getField(rgbForm, 'hex').each((hexField) => {
23879                            emit(hexField, input());
23880                        });
23881                    });
23882                }),
23883                ComposingConfigs.self()
23884            ])
23885        };
23886    };
23887
23888    var global$3 = tinymce.util.Tools.resolve('tinymce.Resource');
23889
23890    const isOldCustomEditor = (spec) => has$2(spec, 'init');
23891    const renderCustomEditor = (spec) => {
23892        const editorApi = value$2();
23893        const memReplaced = record({
23894            dom: {
23895                tag: spec.tag
23896            }
23897        });
23898        const initialValue = value$2();
23899        const focusBehaviour = !isOldCustomEditor(spec) && spec.onFocus.isSome() ? [
23900            Focusing.config({
23901                onFocus: (comp) => {
23902                    spec.onFocus.each((onFocusFn) => {
23903                        onFocusFn(comp.element.dom);
23904                    });
23905                }
23906            }),
23907            Tabstopping.config({})
23908        ] : [];
23909        return {
23910            dom: {
23911                tag: 'div',
23912                classes: ['tox-custom-editor']
23913            },
23914            behaviours: derive$1([
23915                config('custom-editor-events', [
23916                    runOnAttached((component) => {
23917                        memReplaced.getOpt(component).each((ta) => {
23918                            // eslint-disable-next-line @typescript-eslint/no-floating-promises
23919                            (isOldCustomEditor(spec)
23920                                ? spec.init(ta.element.dom)
23921                                : global$3.load(spec.scriptId, spec.scriptUrl).then((init) => init(ta.element.dom, spec.settings))).then((ea) => {
23922                                initialValue.on((cvalue) => {
23923                                    ea.setValue(cvalue);
23924                                });
23925                                initialValue.clear();
23926                                editorApi.set(ea);
23927                            });
23928                        });
23929                    })
23930                ]),
23931                withComp(Optional.none(), () => editorApi.get().fold(() => initialValue.get().getOr(''), (ed) => ed.getValue()), (_component, value) => {
23932                    editorApi.get().fold(() => initialValue.set(value), (ed) => ed.setValue(value));
23933                }),
23934                ComposingConfigs.self()
23935            ].concat(focusBehaviour)),
23936            components: [memReplaced.asSpec()]
23937        };
23938    };
23939
23940    var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');
23941
23942    const browseFilesEvent = generate$6('browse.files.event');
23943    const filterByExtension = (files, providersBackstage, allowedFileExtensions) => {
23944        const allowedImageFileTypes = global$2.explode(providersBackstage.getOption('images_file_types'));
23945        const isFileInAllowedTypes = (file) => allowedFileExtensions.fold(() => exists(allowedImageFileTypes, (type) => endsWith(file.name.toLowerCase(), `.${type.toLowerCase()}`)), (exts) => exists(exts, (type) => endsWith(file.name.toLowerCase(), `.${type.toLowerCase()}`)));
23946        return filter$2(from(files), isFileInAllowedTypes);
23947    };
23948    const renderDropZone = (spec, providersBackstage, initialData) => {
23949        // TODO: Consider moving to alloy
23950        const stopper = (_, se) => {
23951            se.stop();
23952        };
23953        // TODO: Consider moving to alloy
23954        const sequence = (actions) => (comp, se) => {
23955            each$1(actions, (a) => {
23956                a(comp, se);
23957            });
23958        };
23959        const onDrop = (comp, se) => {
23960            if (!Disabling.isDisabled(comp)) {
23961                const transferEvent = se.event.raw;
23962                emitWith(comp, browseFilesEvent, { files: transferEvent.dataTransfer?.files });
23963            }
23964        };
23965        const onSelect = (component, simulatedEvent) => {
23966            const input = simulatedEvent.event.raw.target;
23967            emitWith(component, browseFilesEvent, { files: input.files });
23968        };
23969        const handleFiles = (component, files) => {
23970            if (files) {
23971                const filteredFiles = filterByExtension(files, providersBackstage, spec.allowedFileExtensions);
23972                Representing.setValue(component, filteredFiles);
23973                emitWith(component, formChangeEvent, { name: spec.name });
23974                if (filteredFiles.length === 0) {
23975                    spec.onInvalidFiles().finally(() => {
23976                        component.element.dom.focus();
23977                    }).catch(noop);
23978                }
23979            }
23980        };
23981        const memInput = record({
23982            dom: {
23983                tag: 'input',
23984                attributes: {
23985                    type: 'file',
23986                    accept: spec.allowedFileTypes.getOr('image/*')
23987                },
23988                styles: {
23989                    display: 'none'
23990                }
23991            },
23992            behaviours: derive$1([
23993                config('input-file-events', [
23994                    cutter(click()),
23995                    cutter(tap())
23996                ])
23997            ])
23998        });
23999        const pLabel = spec.label.map((label) => renderLabel$3(label, providersBackstage));
24000        const pField = FormField.parts.field({
24001            factory: Button,
24002            dom: {
24003                tag: 'button',
24004                styles: {
24005                    position: 'relative'
24006                },
24007                classes: ['tox-button', 'tox-button--secondary']
24008            },
24009            components: [
24010                text$2(providersBackstage.translate(spec.buttonLabel.getOr('Browse for an image'))),
24011                memInput.asSpec()
24012            ],
24013            action: (comp) => {
24014                const inputComp = memInput.get(comp);
24015                inputComp.element.dom.click();
24016            },
24017            buttonBehaviours: derive$1([
24018                ComposingConfigs.self(),
24019                memory(initialData.getOr([])),
24020                Tabstopping.config({}),
24021                DisablingConfigs.button(() =>
24021 providersBackstage.checkUiComponentContext(spec.context).shouldDisable),
24022                toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context))
24023            ])
24024        });
24025        const wrapper = {
24026            dom: {
24027                tag: 'div',
24028                classes: ['tox-dropzone-container']
24029            },
24030            behaviours: derive$1([
24031                Disabling.config({
24032                    disabled: () => providersBackstage.checkUiComponentContext(spec.context).shouldDisable
24033                }),
24034                toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context)),
24035                Toggling.config({
24036                    toggleClass: 'dragenter',
24037                    toggleOnExecute: false
24038                }),
24039                config('dropzone-events', [
24040                    run$1('dragenter', sequence([stopper, Toggling.toggle])),
24041                    run$1('dragleave', sequence([stopper, Toggling.toggle])),
24042                    run$1('dragover', stopper),
24043                    run$1('drop', sequence([stopper, onDrop])),
24044                    run$1(change(), onSelect)
24045                ])
24046            ]),
24047            components: [
24048                {
24049                    dom: {
24050                        tag: 'div',
24051                        classes: ['tox-dropzone'],
24052                        styles: {}
24053                    },
24054                    components: [
24055                        {
24056                            dom: {
24057                                tag: 'p'
24058                            },
24059                            components: [
24060                                text$2(providersBackstage.translate(spec.dropAreaLabel.getOr('Drop an image here')))
24061                            ]
24062                        },
24063                        pField
24064                    ]
24065                }
24066            ]
24067        };
24068        return renderFormFieldWith(pLabel, wrapper, ['tox-form__group--stretched'], [config('handle-files', [
24069                run$1(browseFilesEvent, (comp, se) => {
24070                    FormField.getField(comp).each((field) => {
24071                        handleFiles(field, se.event.files);
24072                    });
24073                })
24074            ])]);
24075    };
24076
24077    const renderGrid = (spec, backstage) => ({
24078        dom: {
24079            tag: 'div',
24080            classes: ['tox-form__grid', `tox-form__grid--${spec.columns}col`]
24081        },
24082        components: map$2(spec.items, backstage.interpreter)
24083    });
24084
24085    const beforeObject = generate$6('alloy-fake-before-tabstop');
24086    const afterObject = generate$6('alloy-fake-after-tabstop');
24087    const craftWithClasses = (classes) => {
24088        return {
24089            dom: {
24090                tag: 'div',
24091                styles: {
24092                    width: '1px',
24093                    height: '1px',
24094                    outline: 'none'
24095                },
24096                attributes: {
24097                    tabindex: '0' // Capture native tabbing in the appropriate order
24098                },
24099                classes
24100            },
24101            behaviours: derive$1([
24102                Focusing.config({ ignore: true }),
24103                Tabstopping.config({})
24104            ])
24105        };
24106    };
24107    const craft = (containerClasses, spec) => {
24108        return {
24109            dom: {
24110                tag: 'div',
24111                classes: ['tox-navobj', ...containerClasses.getOr([])]
24112            },
24113            components: [
24114                craftWithClasses([beforeObject]),
24115                spec,
24116                craftWithClasses([afterObject])
24117            ],
24118            behaviours: derive$1([
24119                ComposingConfigs.childAt(1)
24120            ])
24121        };
24122    };
24123    // TODO: Create an API in alloy to do this.
24124    const triggerTab = (placeholder, shiftKey) => {
24125        emitWith(placeholder, keydown(), {
24126            raw: {
24127                which: 9,
24128                shiftKey
24129            }
24130        });
24131    };
24132    const onFocus = (container, targetComp) => {
24133        const target = targetComp.element;
24134        // If focus has shifted naturally to a before object, the tab direction is backwards.
24135        if (has(target, beforeObject)) {
24136            triggerTab(container, true);
24137        }
24138        else if (has(target, afterObject)) {
24139            triggerTab(container, false);
24140        }
24141    };
24142    const isPseudoStop = (element) => {
24143        return closest$1(element, ['.' + beforeObject, '.' + afterObject].join(','), never);
24144    };
24145
24146    const dialogChannel = generate$6('update-dialog');
24147    const titleChannel = generate$6('update-title');
24148    const bodyChannel = generate$6('update-body');
24149    const footerChannel = generate$6('update-footer');
24150    const bodySendMessageChannel = generate$6('body-send-message');
24151    const dialogFocusShiftedChannel = generate$6('dialog-focus-shifted');
24152
24153    const browser = detect$1().browser;
24154    const isSafari = browser.isSafari();
24155    const isFirefox = browser.isFirefox();
24156    const isSafariOrFirefox = isSafari || isFirefox;
24157    const isChromium = browser.isChromium();
24158    const isElementScrollAtBottom = ({ scrollTop, scrollHeight, clientHeight }) =>
24158 Math.ceil(scrollTop) + clientHeight >= scrollHeight;
24159    const scrollToY = (win, y) => 
24160    // TINY-10128: The iframe body is occasionally null when we attempt to scroll, so instead of using body.scrollHeight, use a
24161    // fallback value of 99999999. To minimise the potential impact of future browser changes, this fallback is significantly smaller
24162    // than the minimum of the maximum value Window.scrollTo would take on supported browsers:
24163    // Chromium: > Number.MAX_SAFE_INTEGER
24164    // Safari: 2^31 - 1 = 2147483647
24165    // Firefox: 2147483583
24166    win.scrollTo(0, y === 'bottom' ? 99999999 : y);
24167    const getScrollingElement = (doc, html) => {
24168        // TINY-10110: The scrolling element can change between body and documentElement depending on whether there
24169        // is a doctype declaration. However, this behavior is inconsistent on Chrome and Safari so checking for
24170        // the scroll properties is the most reliable way to determine which element is the scrolling element, at
24171        // least for the purposes of determining whether scroll is at bottom.
24172        const body = doc.body;
24173        return Optional.from(!/^<!DOCTYPE (html|HTML)/.test(html) &&
24174            (!isChromium && !isSafari || isNonNullable(body) && (body.scrollTop !== 0 || Math.abs(body.scrollHeight - body.clientHeight) > 1))
24175            ? body : doc.documentElement);
24176    };
24177    const writeValue = (iframeElement, html, fallbackFn) => {
24178        const iframe = iframeElement.dom;
24179        Optional.from(iframe.contentDocument).fold(fallbackFn, (doc) => {
24180            let lastScrollTop = 0;
24181            // TINY-10032: If documentElement (or body) is nullable, we assume document is empty and so scroll is at bottom.
24182            const isScrollAtBottom = getScrollingElement(doc, html).map((el) => {
24183                lastScrollTop = el.scrollTop;
24184                return el;
24185            }).forall(isElementScrollAtBottom);
24186            const scrollAfterWrite = () => {
24187                const win = iframe.contentWindow;
24188                if (isNonNullable(win)) {
24189                    if (isScrollAtBottom) {
24190                        scrollToY(win, 'bottom');
24191                    }
24192                    else if (!isScrollAtBottom && isSafariOrFirefox && lastScrollTop !== 0) {
24193                        // TINY-10078: Safari and Firefox reset scroll to top on each document.write(), so we need to restore scroll manually
24194                        scrollToY(win, lastScrollTop);
24195                    }
24196                }
24197            };
24198            // TINY-10109: On Safari, attempting to scroll before the iframe has finished loading will cause scroll to reset to top upon load.
24199            // TINY-10128: We will not wait for the load event on Chrome and Firefox since doing so causes the scroll to jump around erratically,
24200            // especially on Firefox. However, not waiting for load has the trade-off of potentially losing bottom scroll when updating at a very
24201            // rapid rate, as attempting to scroll before the iframe body is loaded will not work.
24202            if (isSafari) {
24203                iframe.addEventListener('load', scrollAfterWrite, { once: true });
24204            }
24205            doc.open();
24206            doc.write(html);
24207            doc.close();
24208            if (!isSafari) {
24209                scrollAfterWrite();
24210            }
24211        });
24212    };
24213    // TINY-10078: On Firefox, throttle to 200ms to improve scrolling experience. Since we are manually maintaining previous scroll position
24214    // on each update, when updating rapidly without a throttle, attempting to scroll around the iframe can feel stuck.
24215    // TINY-10097: On Safari, throttle to 500ms to reduce flickering as the document.write() method still observes significant flickering.
24216    // Also improves scrolling, as scroll positions are maintained manually similar to Firefox.
24217    const throttleInterval = someIf(isSafariOrFirefox, isSafari ? 500 : 200);
24218    // TINY-10078: Use Throttler.adaptable to ensure that any content added during the waiting period is not lost.
24219    const writeValueThrottler = throttleInterval.map((interval) => adaptable(writeValue, interval));
24220    const getDynamicSource = (initialData, stream) => {
24221        const cachedValue = Cell(initialData.getOr(''));
24222        return {
24223            getValue: (_frameComponent) => 
24224            // Ideally we should fetch data from the iframe...innerHtml, this triggers Cors errors
24225            cachedValue.get(),
24226            setValue: (frameComponent, html) => {
24227                if (cachedValue.get() !== html) {
24228                    const iframeElement = frameComponent.element;
24229                    const setSrcdocValue = () => set$9(iframeElement, 'srcdoc', html);
24230                    if (stream) {
24231                        writeValueThrottler.fold(constant$1(writeValue), (throttler) => throttler.throttle)(iframeElement, html, setSrcdocValue);
24232                    }
24233                    else {
24234                        // TINY-3769: We need to use srcdoc here, instead of src with a data URI, otherwise browsers won't retain the Origin.
24235                        // See https://bugs.chromium.org/p/chromium/issues/detail?id=58999#c11
24236                        setSrcdocValue();
24237                    }
24238                }
24239                cachedValue.set(html);
24240            }
24241        };
24242    };
24243    const renderIFrame = (spec, providersBackstage, initialData) => {
24244        const baseClass = 'tox-dialog__iframe';
24245        const opaqueClass = spec.transparent ? [] : [`${baseClass}--opaque`];
24246        const containerBorderedClass = spec.border ? [`tox-navobj-bordered`] : [];
24247        const attributes = {
24248            ...spec.label.map((title) => ({ title })).getOr({}),
24249            ...initialData.map((html) => ({ srcdoc: html })).getOr({}),
24250            ...spec.sandboxed ? { sandbox: 'allow-scripts allow-same-origin' } : {}
24251        };
24252        const sourcing = getDynamicSource(initialData, spec.streamContent);
24253        const pLabel = spec.label.map((label) => renderLabel$3(label, providersBackstage));
24254        const factory = (newSpec) => craft(Optional.from(containerBorderedClass), {
24255            // We need to use the part uid or the label and field won't be linked with ARIA
24256            uid: newSpec.uid,
24257            dom: {
24258                tag: 'iframe',
24259                attributes,
24260                classes: [
24261                    baseClass,
24262                    ...opaqueClass
24263                ]
24264            },
24265            behaviours: derive$1([
24266                Tabstopping.config({}),
24267                Focusing.config({}),
24268                withComp(initialData, sourcing.getValue, sourcing.setValue),
24269                Receiving.config({
24270                    channels: {
24271                        [dialogFocusShiftedChannel]: {
24272                            onReceive: (comp, message) => {
24273                                message.newFocus.each((newFocus) => {
24274                                    parentElement(comp.element).each((parent) => {
24275                                        const f = eq(comp.element, newFocus) ? add$2 : remove$3;
24276                                        f(parent, 'tox-navobj-bordered-focus');
24277                                    });
24278                                });
24279                            }
24280                        }
24281                    }
24282                })
24283            ])
24284        });
24285        // Note, it's not going to handle escape at this point.
24286        const pField = FormField.parts.field({
24287            factory: { sketch: factory }
24288        });
24289        return renderFormFieldWith(pLabel, pField, ['tox-form__group--stretched'], []);
24290    };
24291
24292    const calculateImagePosition = (panelWidth, panelHeight, imageWidth, imageHeight, zoom) => {
24293        const width = imageWidth * zoom;
24294        const height = imageHeight * zoom;
24295        const left = Math.max(0, panelWidth / 2 - width / 2);
24296        const top = Math.max(0, panelHeight / 2 - height / 2);
24297        return {
24298            left: left.toString() + 'px',
24299            top: top.toString() + 'px',
24300            width: width.toString() + 'px',
24301            height: height.toString() + 'px',
24302        };
24303    };
24304    const zoomToFit = (panel, width, height) => {
24305        const panelW = get$c(panel);
24306        const panelH = get$d(panel);
24307        return Math.min(panelW / width, panelH / height, 1);
24308    };
24309    const renderImagePreview = (spec, initialData) => {
24310        const cachedData = Cell(initialData.getOr({ url: '' }));
24311        const memImage = record({
24312            dom: {
24313                tag: 'img',
24314                classes: ['tox-imagepreview__image'],
24315                attributes: initialData.map((data) => ({ src: data.url })).getOr({})
24316            },
24317        });
24318        const memContainer = record({
24319            dom: {
24320                tag: 'div',
24321                classes: ['tox-imagepreview__container'],
24322                attributes: {
24323                    role: 'presentation'
24324                },
24325            },
24326            components: [
24327                memImage.asSpec()
24328            ]
24329        });
24330        const setValue = (frameComponent, data) => {
24331            const translatedData = {
24332                url: data.url
24333            };
24334            // update properties that are set by the data
24335            data.zoom.each((z) => translatedData.zoom = z);
24336            data.cachedWidth.each((z) => translatedData.cachedWidth = z);
24337            data.cachedHeight.each((z) => translatedData.cachedHeight = z);
24338            cachedData.set(translatedData);
24339            const applyFramePositioning = () => {
24340                const { cachedWidth, cachedHeight, zoom } = translatedData;
24341                if (!isUndefined(cachedWidth) && !isUndefined(cachedHeight)) {
24342                    if (isUndefined(zoom)) {
24343                        const z = zoomToFit(frameComponent.element, cachedWidth, cachedHeight);
24344                        // sneaky mutation since we own the object
24345                        translatedData.zoom = z;
24346                    }
24347                    const position = calculateImagePosition(get$c(frameComponent.element), get$d(frameComponent.element), cachedWidth, cachedHeight, translatedData.zoom);
24348                    memContainer.getOpt(frameComponent).each((container) => {
24349                        setAll(container.element, position);
24350                    });
24351                }
24352            };
24353            memImage.getOpt(frameComponent).each((imageComponent) => {
24354                const img = imageComponent.element;
24355                if (data.url !== get$g(img, 'src')) {
24356                    set$9(img, 'src', data.url);
24357                    remove$3(frameComponent.element, 'tox-imagepreview__loaded');
24358                }
24359                applyFramePositioning();
24360                // eslint-disable-next-line @typescript-eslint/no-floating-promises
24361                image(img).then((img) => {
24362                    // Ensure the component hasn't been removed while the image was loading
24363                    // if it is disconnected, just do nothing
24364                    if (frameComponent.getSystem().isConnected()) {
24365                        add$2(frameComponent.element, 'tox-imagepreview__loaded');
24366                        // sneaky mutation since we own the object
24367                        translatedData.cachedWidth = img.dom.naturalWidth;
24368                        translatedData.cachedHeight = img.dom.naturalHeight;
24369                        applyFramePositioning();
24370                    }
24371                });
24372            });
24373        };
24374        const styles = {};
24375        spec.height.each((h) => styles.height = h);
24376        // TODO: TINY-8393 Use the initial data properly once it's validated
24377        const fakeValidatedData = initialData.map((d) => ({
24378            url: d.url,
24379            zoom: Optional.from(d.zoom),
24380            cachedWidth: Optional.from(d.cachedWidth),
24381            cachedHeight: Optional.from(d.cachedHeight),
24382        }));
24383        return {
24384            dom: {
24385                tag: 'div',
24386                classes: ['tox-imagepreview'],
24387                styles,
24388                attributes: {
24389                    role: 'presentation'
24390                }
24391            },
24392            components: [
24393                memContainer.asSpec(),
24394            ],
24395            behaviours: derive$1([
24396                ComposingConfigs.self(),
24397                withComp(fakeValidatedData, () => 
24398                /*
24399                  NOTE: This is intentionally returning the cached image width and height.
24400      
24401                  Including those details in the dialog data helps when `setData` only changes the URL, as
24402                  the old image must continue to be displayed at the old size until the new image has loaded.
24403                */
24404                cachedData.get(), setValue),
24405            ])
24406        };
24407    };
24408
24409    const renderLabel$2 = (spec, backstageShared, getCompByName) => {
24410        const baseClass = 'tox-label';
24411        const centerClass = spec.align === 'center' ? [`${baseClass}--center`] : [];
24412        const endClass = spec.align === 'end' ? [`${baseClass}--end`] : [];
24413        const label = record({
24414            dom: {
24415                tag: 'label',
24416                classes: [baseClass, ...centerClass, ...endClass]
24417            },
24418            components: [
24419                text$2(backstageShared.providers.translate(spec.label))
24420            ]
24421        });
24422        const comps = map$2(spec.items, backstageShared.interpreter);
24423        return {
24424            dom: {
24425                tag: 'div',
24426                classes: ['tox-form__group']
24427            },
24428            components: [
24429                label.asSpec(),
24430                ...comps
24431            ],
24432            behaviours: derive$1([
24433                ComposingConfigs.self(),
24434                Replacing.config({}),
24435                domHtml(Optional.none()),
24436                Keying.config({
24437                    mode: 'acyclic'
24438                }),
24439                config('label', [
24440                    runOnAttached((comp) => {
24441                        spec.for.each((name) => {
24442                            getCompByName(name).each((target) => {
24443                                label.getOpt(comp).each((labelComp) => {
24444                                    const id = get$g(target.element, 'id') ?? generate$6('form-field');
24445                                    set$9(target.element, 'id', id);
24446                                    set$9(labelComp.element, 'for', id);
24447                                });
24448                            });
24449                        });
24450                    })
24451                ]),
24452            ])
24453        };
24454    };
24455
24456    const internalToolbarButtonExecute = generate$6('toolbar.button.execute');
24457    // Perform `action` when an item is clicked on, close menus, and stop event
24458    const onToolbarButtonExecute = (info) => runOnExecute$1((comp, _simulatedEvent) => {
24459        // If there is an action, run the action
24460        runWithApi(info, comp)((itemApi) => {
24461            emitWith(comp, internalToolbarButtonExecute, {
24462                buttonApi: itemApi
24463            });
24464            info.onAction(itemApi);
24465        });
24466    });
24467    const commonButtonDisplayEvent = generate$6('common-button-display-events');
24468    const toolbarButtonEventOrder = {
24469        // TODO: use the constants provided by behaviours.
24470        [execute$5()]: ['disabling', 'alloy.base.behaviour', 'toggling', 'toolbar-button-events', 'tooltipping'],
24471        [attachedToDom()]: [
24472            'toolbar-button-events',
24473            commonButtonDisplayEvent
24474        ],
24475        [detachedFromDom()]: ['toolbar-button-events', 'dropdown-events', 'tooltipping'],
24476        [mousedown()]: [
24477            'focusing',
24478            'alloy.base.behaviour',
24479            commonButtonDisplayEvent
24480        ]
24481    };
24482
24483    const forceInitialSize = (comp) => set$7(comp.element, 'width', get$e(comp.element, 'width'));
24484
24485    const renderIcon$1 = (iconName, iconsProvider, behaviours) => render$4(iconName, {
24486        tag: 'span',
24487        classes: ["tox-icon" /* ToolbarButtonClasses.Icon */, "tox-tbtn__icon-wrap" /* ToolbarButtonClasses.IconWrap */],
24488        behaviours
24489    }, iconsProvider);
24490    const renderIconFromPack$1 = (iconName, iconsProvider) => renderIcon$1(iconName, iconsProvider, []);
24491    const renderReplaceableIconFromPack = (iconName, iconsProvider) => renderIcon$1(iconName, iconsProvider, [Replacing.config({})]);
24492    const renderLabel$1 = (text, prefix, providersBackstage) => ({
24493        dom: {
24494            tag: 'span',
24495            classes: [`${prefix}__select-label`]
24496        },
24497        components: [
24498            text$2(providersBackstage.translate(text))
24499        ],
24500        behaviours: derive$1([
24501            Replacing.config({})
24502        ])
24503    });
24504
24505    const updateMenuText = generate$6('update-menu-text');
24506    const updateMenuIcon = generate$6('update-menu-icon');
24507    const updateTooltiptext = generate$6('update-tooltip-text');
24508    // TODO: Use renderCommonStructure here.
24509    const renderCommonDropdown = (spec, prefix, sharedBackstage, btnName) => {
24510        const editorOffCell = Cell(noop);
24511        const tooltip = Cell(spec.tooltip);
24512        // We need mementos for display text and display icon because on the events
24513        // updateMenuText and updateMenuIcon respectively, their contents are changed
24514        // via Replacing. These events are generally emitted by dropdowns that want the
24515        // main text and icon to match the current selection (e.g. bespokes like font family)
24516        const optMemDisplayText = spec.text.map((text) => record(renderLabel$1(text, prefix, sharedBackstage.providers)));
24517        const optMemDisplayIcon = spec.icon.map((iconName) => record(renderReplaceableIconFromPack(iconName, sharedBackstage.providers.icons)));
24518        /*
24519         * The desired behaviour here is:
24520         *
24521         *   when left or right is pressed, and it isn't associated with expanding or
24522         *   collapsing a submenu, then it should navigate to the next menu item, and
24523         *   expand it (without highlighting any items in the expanded menu).
24524         *   It also needs to close the previous menu
24525         */
24526        const onLeftOrRightInMenu = (comp, se) => {
24527            // The originating dropdown is stored on the sandbox itself. This is just an
24528            // implementation detail of alloy. We really need to make it a fully-fledged API.
24529            // TODO: TINY-9014 Make SandboxAPI have a function that just delegates to Representing
24530            const dropdown = Representing.getValue(comp);
24531            // Focus the dropdown. Current workaround required to make FlowLayout recognise the current focus.
24532            // The triggering keydown is going to try to move the focus left or
24533            // right of the current menu, so it needs to know what the current menu dropdown is. It
24534            // can't work it out by the current focus, because the current focus is *in* the menu, so
24535            // we help it by moving the focus to the button, so it can work out what the next menu to
24536            // the left or right is.
24537            Focusing.focus(dropdown);
24538            emitWith(dropdown, 'keydown', {
24539                raw: se.event.raw
24540            });
24541            // Because we have just navigated off this open menu, we want to close it.
24542            // INVESTIGATE: TINY-9014: Is this handling situations where there were no menus
24543            // to move to? Does it matter if we still close it when there are no other menus?
24544            Dropdown.close(dropdown);
24545            // The Optional.some(true) tells the keyboard handler that this event was handled,
24546            // which will do things like stopPropagation and preventDefault.
24547            return Optional.some(true);
24548        };
24549        const role = spec.role.fold(() => ({}), (role) => ({ role }));
24550        const listRole = Optional.from(spec.listRole).map((listRole) => ({ listRole })).getOr({});
24551        const ariaLabelAttribute = spec.ariaLabel.fold(() => ({}), (ariaLabel) => {
24552            const translatedAriaLabel = sharedBackstage.providers.translate(ariaLabel);
24553            return {
24554                'aria-label': translatedAriaLabel
24555            };
24556        });
24557        const iconSpec = render$4('chevron-down', {
24558            tag: 'div',
24559            classes: [`${prefix}__select-chevron`]
24560        }, sharedBackstage.providers.icons);
24561        const fixWidthBehaviourName = generate$6('common-button-display-events');
24562        // Should we use Id.generate here?
24563        const customEventsName = 'dropdown-events';
24564        const memDropdown = record(Dropdown.sketch({
24565            ...spec.uid ? { uid: spec.uid } : {},
24566            ...role,
24567            ...listRole,
24568            dom: {
24569                tag: 'button',
24570                classes: [prefix, `${prefix}--select`].concat(map$2(spec.classes, (c) => `${prefix}--${c}`)),
24571                attributes: {
24572                    ...ariaLabelAttribute,
24573                    ...(isNonNullable(btnName) ? { 'data-mce-name': btnName } : {})
24574                }
24575            },
24576            components: componentRenderPipeline([
24577                optMemDisplayIcon.map((mem) => mem.asSpec()),
24578                optMemDisplayText.map((mem) => mem.asSpec()),
24579                Optional.some(iconSpec)
24580            ]),
24581            matchWidth: true,
24582            useMinWidth: true,
24583            // When the dropdown opens, if we are in search mode, then we want to
24584            // focus our searcher.
24585            onOpen: (anchor, dropdownComp, tmenuComp) => {
24586                if (spec.searchable) {
24587                    focusSearchField(tmenuComp);
24588                }
24589            },
24590            dropdownBehaviours: derive$1([
24591                ...spec.dropdownBehaviours,
24592                DisablingConfigs.button(() => spec.disabled || sharedBackstage.providers.checkUiComponentContext(spec.c
24592ontext).shouldDisable),
24593                toggleOnReceive(() => sharedBackstage.providers.checkUiComponentContext(spec.context)),
24594                // INVESTIGATE (TINY-9012): There was a old comment here about something not quite working, and that
24595                // we can still get the button focused. It was probably related to Unselecting.
24596                Unselecting.config({}),
24597                Replacing.config({}),
24598                ...(spec.tooltip.map((t) => Tooltipping.config(sharedBackstage.providers.tooltips.getConfig({
24599                    tooltipText: sharedBackstage.providers.translate(t),
24600                    onShow: (comp) => {
24601                        if (lift2(tooltip.get(), spec.tooltip, (tooltipStr, tt) => tt !== tooltipStr).getOr(false)) {
24602                            const translatedTooltip = sharedBackstage.providers.translate(tooltip.get().getOr(''));
24603                            Tooltipping.setComponents(comp, sharedBackstage.providers.tooltips.getComponents({ tooltipText: translatedTooltip }));
24604                        }
24605                    }
24606                })))).toArray(),
24607                // This is the generic way to make onSetup and onDestroy call as the component is attached /
24608                // detached from the page/DOM.
24609                config(customEventsName, [
24610                    onControlAttached(spec, editorOffCell),
24611                    onControlDetached(spec, editorOffCell)
24612                ]),
24613                config(fixWidthBehaviourName, [
24614                    runOnAttached((comp, _se) => {
24615                        if (spec.listRole !== 'listbox') {
24616                            forceInitialSize(comp);
24617                        }
24618                    }),
24619                ]),
24620                config('update-dropdown-width-variable', [
24621                    run$1(windowResize(), (comp, _se) => Dropdown.close(comp)),
24622                ]),
24623                config('menubutton-update-display-text', [
24624                    // These handlers are just using Replacing to replace either the menu
24625                    // text or the icon.
24626                    run$1(updateMenuText, (comp, se) => {
24627                        optMemDisplayText.bind((mem) => mem.getOpt(comp)).each((displayText) => {
24628                            Replacing.set(displayText, [text$2(sharedBackstage.providers.translate(se.event.text))]);
24629                        });
24630                    }),
24631                    run$1(updateMenuIcon, (comp, se) => {
24632                        optMemDisplayIcon.bind((mem) => mem.getOpt(comp)).each((displayIcon) => {
24633                            Replacing.set(displayIcon, [
24634                                renderReplaceableIconFromPack(se.event.icon, sharedBackstage.providers.icons)
24635                            ]);
24636                        });
24637                    }),
24638                    run$1(updateTooltiptext, (comp, se) => {
24639                        const translatedTooltip = sharedBackstage.providers.translate(se.event.text);
24640                        set$9(comp.element, 'aria-label', translatedTooltip);
24641                        tooltip.set(Optional.some(se.event.text));
24642                    })
24643                ])
24644            ]),
24645            eventOrder: deepMerge(toolbarButtonEventOrder, {
24646                // INVESTIGATE (TINY-9014): Explain why we need the events in this order.
24647                // Ideally, have a test that fails when they are in a different order if order
24648                // is important
24649                [mousedown()]: ['focusing', 'alloy.base.behaviour', 'item-type-events', 'normal-dropdown-events'],
24650                [attachedToDom()]: [
24651                    'toolbar-button-events',
24652                    Tooltipping.name(),
24653                    customEventsName,
24654                    fixWidthBehaviourName,
24655                ]
24656            }),
24657            sandboxBehaviours: derive$1([
24658                Keying.config({
24659                    mode: 'special',
24660                    onLeft: onLeftOrRightInMenu,
24661                    onRight: onLeftOrRightInMenu,
24662                }),
24663                config('dropdown-sandbox-events', [
24664                    run$1(refetchTriggerEvent, (originalSandboxComp, se) => {
24665                        handleRefetchTrigger(originalSandboxComp);
24666                        // It's a custom event that no-one else should be listening to, so stop it.
24667                        se.stop();
24668                    }),
24669                    run$1(redirectMenuItemInteractionEvent, (sandboxComp, se) => {
24670                        handleRedirectToMenuItem(sandboxComp, se);
24671                        // It's a custom event that no-one else should be listening to, so stop it.
24672                        se.stop();
24673                    })
24674                ])
24675            ]),
24676            lazySink: sharedBackstage.getSink,
24677            toggleClass: `${prefix}--active`,
24678            parts: {
24679                menu: {
24680                    ...part(false, spec.columns, spec.presets),
24681                    // When the menu is "searchable", use fakeFocus so that keyboard
24682                    // focus stays in the search field
24683                    fakeFocus: spec.searchable,
24684                    // We don't want to update the  `aria-selected` on highlight or dehighlight for the `listbox` role because that is used to indicate the selected item
24685                    ...(spec.listRole === 'listbox' ? {} : {
24686                        onHighlightItem: updateAriaOnHighlight,
24687                        onCollapseMenu: (tmenuComp, itemCompCausingCollapse, nowActiveMenuComp) => {
24688                            // We want to update ARIA on collapsing as well, because it isn't changing
24689                            // the highlights. So what we need to do is get the right parameters to
24690                            // pass to updateAriaOnHighlight
24691                            Highlighting.getHighlighted(nowActiveMenuComp).each((itemComp) => {
24692                                updateAriaOnHighlight(tmenuComp, nowActiveMenuComp, itemComp);
24693                            });
24694                        },
24695                        onDehighlightItem: updateAriaOnDehighlight
24696                    })
24697                }
24698            },
24699            getAnchorOverrides: () => {
24700                return {
24701                    maxHeightFunction: (element, available) => {
24702                        anchored()(element, available - 10);
24703                    },
24704                };
24705            },
24706            fetch: (comp) => Future.nu(curry(spec.fetch, comp))
24707        }));
24708        return memDropdown.asSpec();
24709    };
24710
24711    const isMenuItemReference = (item) => isString(item);
24712    const isSeparator$2 = (item) => item.type === 'separator';
24713    const isExpandingMenuItem = (item) => has$2(item, 'getSubmenuItems');
24714    const separator$2 = {
24715        type: 'separator'
24716    };
24717    const unwrapReferences = (items, menuItems) => {
24718        // Unwrap any string based menu item references
24719        const realItems = foldl(items, (acc, item) => {
24720            if (isMenuItemReference(item)) {
24721                if (item === '') {
24722                    return acc;
24723                }
24724                else if (item === '|') {
24725                    // Ignore the separator if it's at the start or a duplicate
24726                    return acc.length > 0 && !isSeparator$2(acc[acc.length - 1]) ? acc.concat([separator$2]) : acc;
24727                }
24728                else if (has$2(menuItems, item.toLowerCase())) {
24729                    return acc.concat([menuItems[item.toLowerCase()]]);
24730                }
24731                else {
24732                    // TODO: Add back after TINY-3232 is implemented
24733                    // console.error('No representation for menuItem: ' + item);
24734                    return acc;
24735                }
24736            }
24737            else {
24738                return acc.concat([item]);
24739            }
24740        }, []);
24741        // Remove any trailing separators
24742        if (realItems.length > 0 && isSeparator$2(realItems[realItems.length - 1])) {
24743            realItems.pop();
24744        }
24745        return realItems;
24746    };
24747    const getFromExpandingItem = (item, menuItems) => {
24748        const submenuItems = item.getSubmenuItems();
24749        const rest = expand(submenuItems, menuItems);
24750        const newMenus = deepMerge(rest.menus, { [item.value]: rest.items });
24751        const newExpansions = deepMerge(rest.expansions, { [item.value]: item.value });
24752        return {
24753            item,
24754            menus: newMenus,
24755            expansions: newExpansions
24756        };
24757    };
24758    const generateValueIfRequired = (item) => {
24759        // Use the value already in item if it has one.
24760        const itemValue = get$h(item, 'value').getOrThunk(() => generate$6('generated-menu-item'));
24761        return deepMerge({ value: itemValue }, item);
24762    };
24763    // Takes items, and consolidates them into its return value
24764    const expand = (items, menuItems) => {
24765        // Fistly, we do all substitution using the registry for any items referenced by their
24766        // string key.
24767        const realItems = unwrapReferences(isString(items) ? items.split(' ') : items, menuItems);
24768        // Now that we have complete bridge Item specs for all items, we need to collect the
24769        // submenus, items in the primary menu, and triggering menu items all into one
24770        // giant object to from the building blocks on our TieredData
24771        return foldr(realItems, (acc, item) => {
24772            if (isExpandingMenuItem(item)) {
24773                // We generate a random value for item, but only if there isn't an existing value
24774                const itemWithValue = generateValueIfRequired(item);
vendor: 4,234 bytes, lines 24775-24852
24775                // The newData isn't quite in the format you might expect. The list of items
24776                // for an item with nested items is just the single parent item. All of the nested
24777                // items becomes part of '.menus'. Finally, the expansions is just a map from
24778                // the triggering item to the first submenu. Incidentally, they are given the same
24779                // value (triggering item and submenu), for convenience.
24780                const newData = getFromExpandingItem(itemWithValue, menuItems);
24781                return {
24782                    // Combine all of our current submenus and items with the new submenus created by
24783                    // this item with nested subitems
24784                    menus: deepMerge(acc.menus, newData.menus),
24785                    // Add our parent item into the list of items in the *current menu*.
24786                    items: [newData.item, ...acc.items],
24787                    // Merge together our "this item opens this submenu" objects
24788                    expansions: deepMerge(acc.expansions, newData.expansions)
24789                };
24790            }
24791            else {
24792                // If we aren't creating any submenus, then all we need to do is add this item
24793                // to the list of items in the current menu. So this is the same as an expanding
24794                // menu item, except it doesn't add to `menus` or `expansions`.
24795                return {
24796                    ...acc,
24797                    items: [item, ...acc.items]
24798                };
24799            }
24800        }, {
24801            menus: {},
24802            expansions: {},
24803            items: []
24804        });
24805    };
24806
24807    const getSearchModeForField = (settings) => {
24808        return settings.search.fold(() => ({ searchMode: 'no-search' }), (searchSettings) => ({
24809            searchMode: 'search-with-field',
24810            placeholder: searchSettings.placeholder
24811        }));
24812    };
24813    const getSearchModeForResults = (settings) => {
24814        return settings.search.fold(() => ({ searchMode: 'no-search' }), (_) => ({ searchMode: 'search-with-results' }));
24815    };
24816    const build = (items, itemResponse, backstage, settings) => {
24817        const primary = generate$6('primary-menu');
24818        // The expand process identifies all the items, submenus, and triggering items
24819        // defined by the list of items. It substitutes the strings using the values registered
24820        // in the menuItem registry where necessary. It is the building blocks of TieredData,
24821        // but everything is still just in the bridge item format ... nothing has been turned
24822        // into AlloySpecs.
24823        const data = expand(items, backstage.shared.providers.menuItems());
24824        if (data.items.length === 0) {
24825            return Optional.none();
24826        }
24827        // Only the main menu has a searchable widget (if it is enabled)
24828        const mainMenuSearchMode = getSearchModeForField(settings);
24829        const mainMenu = createPartialMenu(primary, data.items, itemResponse, backstage, settings.isHorizontalMenu, mainMenuSearchMode);
24830        // The submenus do not have the search field, but will have search results for
24831        // connecting to the search field via aria-controls
24832        const submenuSearchMode = getSearchModeForResults(settings);
24833        const submenus = map$1(data.menus, (menuItems, menuName) => createPartialMenu(menuName, menuItems, itemResponse, backstage, 
24834        // Currently, submenus cannot be horizontal menus (so always false)
24835        false, submenuSearchMode));
24836        const menus = deepMerge(submenus, wrap(primary, mainMenu));
24837        return Optional.from(tieredMenu.tieredData(primary, menus, data.expansions));
24838    };
24839
24840    const isSingleListItem = (item) => !has$2(item, 'items');
24841    const dataAttribute = 'data-value';
24842    const fetchItems = (dropdownComp, name, items, selectedValue, hasNestedItems) => map$2(items, (item) => {
24843        if (!isSingleListItem(item)) {
24844            return {
24845                type: 'nestedmenuitem',
24846                text: item.text,
24847                getSubmenuItems: () => fetchItems(dropdownComp, name, item.items, selectedValue, hasNestedItems)
24848            };
24849        }
24850        else {
24851            return {
24852                type: 'togglemenuitem',
24853                ...(hasNestedItems ? {} : { role: 'option' }),
24854                text: item.text,
24855                value: item.value,
24856                active: item.value === selectedValue,
24857                onAction: () => {
24858                    Representing.setValue(dropdownComp, item.value);
24859                    emitWith(dropdownComp, formChangeEvent, { name });
24860                    Focusing.focus(dropdownComp);
24861                }
24862            };
24863        }
24864    });
24865    const findItemByValue = (items, value) => findMap(items, (item) => {
24866        if (!isSingleListItem(item)) {
24867            return findItemByValue(item.items, value);
24868        }
24869        else {
24870            return someIf(item.value === value, item);
24871        }
24872    });
24873    const renderListBox = (spec, backstage, initialData) => {
24874        const hasNestedItems = exists(spec.items, (item) => !isSingleListItem(item));
24875        const providersBackstage = backstage.shared.providers;
24876        const initialItem = initialData
24877            .bind((value) => findItemByValue(spec.items, value))
24878            .orThunk(() => head(spec.items).filter(isSingleListItem));
24879        const pLabel = spec.label.map((label) => renderLabel$3(label, providersBackstage));
24880        const pField = FormField.parts.field({
24881            dom: {},
24882            factory: {
24883                sketch: (sketchSpec) => renderCommonDropdown({
24884                    context: spec.context,
24885                    uid: sketchSpec.uid,
24886                    text: initialItem.map((item) => item.text),
24887                    icon: Optional.none(),
24888                    tooltip: Optional.none(),
24889                    role: someIf(!hasNestedItems, 'combobox'),
24890                    ...(hasNestedItems ? {} : { listRole: 'listbox' }),
24891                    ariaLabel: spec.label,
24892                    fetch: (comp, callback) => {
24893                        const items = fetchItems(comp, spec.name, spec.items, Representing.getValue(comp), hasNestedItems);
24894                        callback(build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
24895                            isHorizontalMenu: false,
24896                            search: Optional.none()
24897                        }));
24898                    },
24899                    onSetup: constant$1(noop),
24900                    getApi: constant$1({}),
24901                    columns: 1,
24902                    presets: 'normal',
24903                    classes: [],
24904                    dropdownBehaviours: [
24905                        Tabstopping.config({}),
24906                        withComp(initialItem.map((item) => item.value), (comp) => get$g(comp.element, dataAttribute), (comp, data) => {
24907                            // We only want to update the saved value if the value set is a valid property
24908                            findItemByValue(spec.items, data)
24909                                .each((item) => {
24910                                set$9(comp.element, dataAttribute, item.value);
24911                                emitWith(comp, updateMenuText, { text: item.text });
24912                            });
24913                        })
24914                    ]
24915                }, 'tox-listbox', backstage.shared)
24916            }
24917        });
24918        const listBoxWrap = {
24919            dom: {
24920                tag: 'div',
24921                classes: ['tox-listboxfield']
24922            },
24923            components: [pField]
24924        };
24925        return FormField.sketch({
24926            dom: {
24927                tag: 'div',
24928                classes: ['tox-form__group']
24929            },
24930            components: flatten([pLabel.toArray(), [listBoxWrap]]),
24931            fieldBehaviours: derive$1([
24932                Disabling.config({
24933                    disabled: () => !spec.enabled || providersBackstage.checkUiComponentContext(spec.context).shouldDisable,
24934                    onDisabled: (comp) => {
24935                        FormField.getField(comp).each(Disabling.disable);
24936                    },
24937                    onEnabled: (comp) => {
24938                        FormField.getField(comp).each(Disabling.enable);
24939                    }
24940                })
24941            ])
24942        });
24943    };
24944
24945    const renderPanel = (spec, backstage) => ({
24946        dom: {
24947            tag: 'div',
24948            classes: spec.classes
24949        },
24950        // All of the items passed through the form need to be put through the interpreter
24951        // with their form part preserved.
24952        components: map$2(spec.items, backstage.shared.interpreter)
24953    });
24954
24955    const renderSelectBox = (spec, providersBackstage, initialData) => {
24956        const translatedOptions = map$2(spec.items, (item) => ({
24957            text: providersBackstage.translate(item.text),
24958            value: item.value
24959        }));
24960        // DUPE with TextField.
24961        const pLabel = spec.label.map((label) => renderLabel$3(label, providersBackstage));
24962        const pField = FormField.parts.field({
24963            // TODO: Alloy should not allow dom changing of an HTML select!
24964            dom: {},
24965            ...initialData.map((data) => ({ data })).getOr({}),
24966            selectAttributes: {
24967                size: spec.size
24968            },
24969            options: translatedOptions,
24970            factory: HtmlSelect,
24971            selectBehaviours: derive$1([
24972                Disabling.config({
24973                    disabled: () => !spec.enabled || providersBackstage.checkUiComponentContext(spec.c
24973ontext).shouldDisable
24974                }),
24975                Tabstopping.config({}),
24976                config('selectbox-change', [
24977                    run$1(change(), (component, _) => {
24978                        emitWith(component, formChangeEvent, { name: spec.name });
24979                    })
24980                ])
24981            ])
24982        });
24983        const chevron = spec.size > 1 ? Optional.none() :
24984            Optional.some(render$4('chevron-down', { tag: 'div', classes: ['tox-selectfield__icon-js'] }, providersBackstage.icons));
24985        const selectWrap = {
24986            dom: {
24987                tag: 'div',
24988                classes: ['tox-selectfield']
24989            },
24990            components: flatten([[pField], chevron.toArray()])
24991        };
24992        return FormField.sketch({
24993            dom: {
24994                tag: 'div',
24995                classes: ['tox-form__group']
24996            },
24997            components: flatten([pLabel.toArray(), [selectWrap]]),
24998            fieldBehaviours: derive$1([
24999                Disabling.config({
25000                    disabled: () => !spec.enabled || providersBackstage.checkUiComponentContext(spec.context).shouldDisable,
25001                    onDisabled: (comp) => {
25002                        FormField.getField(comp).each(Disabling.disable);
25003                    },
25004                    onEnabled: (comp) => {
25005                        FormField.getField(comp).each(Disabling.enable);
25006                    }
25007                }),
25008                toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context))
25009            ])
25010        });
25011    };
25012
25013    const formatSize = (size) => {
25014        const unitDec = {
25015            '': 0,
25016            'px': 0,
25017            'pt': 1,
25018            'mm': 1,
25019            'pc': 2,
25020            'ex': 2,
25021            'em': 2,
25022            'ch': 2,
25023            'rem': 2,
25024            'cm': 3,
25025            'in': 4,
25026            '%': 4
25027        };
25028        const maxDecimal = (unit) => unit in unitDec ? unitDec[unit] : 1;
25029        let numText = size.value.toFixed(maxDecimal(size.unit));
25030        if (numText.indexOf('.') !== -1) {
25031            numText = numText.replace(/\.?0*$/, '');
25032        }
25033        return numText + size.unit;
25034    };
25035    const parseSize = (sizeText) => {
25036        const numPattern = /^\s*(\d+(?:\.\d+)?)\s*(|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|%)\s*$/;
25037        const match = numPattern.exec(sizeText);
25038        if (match !== null) {
25039            const value = parseFloat(match[1]);
25040            const unit = match[2];
25041            return Result.value({ value, unit });
25042        }
25043        else {
25044            return Result.error(sizeText);
25045        }
25046    };
25047    const convertUnit = (size, unit) => {
25048        const inInch = {
25049            '': 96,
25050            'px': 96,
25051            'pt': 72,
25052            'cm': 2.54,
25053            'pc': 12,
25054            'mm': 25.4,
25055            'in': 1
25056        };
25057        const supported = (u) => has$2(inInch, u);
25058        if (size.unit === unit) {
25059            return Optional.some(size.value);
25060        }
25061        else if (supported(size.unit) && supported(unit)) {
25062            if (inInch[size.unit] === inInch[unit]) {
25063                return Optional.some(size.value);
25064            }
25065            else {
25066                return Optional.some(size.value / inInch[size.unit] * inInch[unit]);
25067            }
25068        }
25069        else {
25070            return Optional.none();
25071        }
25072    };
25073    const noSizeConversion = (_input) => Optional.none();
25074    const ratioSizeConversion = (scale, unit) => (size) => convertUnit(size, unit).map((value) => ({ value: value * scale, unit }));
25075    const makeRatioConverter = (currentFieldText, otherFieldText) => {
25076        const cValue = parseSize(currentFieldText).toOptional();
25077        const oValue = parseSize(otherFieldText).toOptional();
25078        return lift2(cValue, oValue, (cSize, oSize) => convertUnit(cSize, oSize.unit).map((val) => oSize.value / val).map((r) => ratioSizeConversion(r, oSize.unit)).getOr(noSizeConversion)).getOr(noSizeConversion);
25079    };
25080
25081    const renderSizeInput = (spec, providersBackstage) => {
25082        let converter = noSizeConversion;
25083        const ratioEvent = generate$6('ratio-event');
25084        const makeIcon = (iconName) => render$4(iconName, { tag: 'span', classes: ['tox-icon', 'tox-lock-icon__' + iconName] }, providersBackstage.icons);
25085        const disabled = () => !spec.enabled || providersBackstage.checkUiComponentContext(spec.c
25085ontext).shouldDisable;
25086        const toggleOnReceive$1 = toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context));
25087        const label = spec.label.getOr('Constrain proportions');
25088        const translatedLabel = providersBackstage.translate(label);
25089        const pLock = FormCoupledInputs.parts.lock({
25090            dom: {
25091                tag: 'button',
25092                classes: ['tox-lock', 'tox-button', 'tox-button--naked', 'tox-button--icon'],
25093                attributes: {
25094                    'aria-label': translatedLabel,
25095                    'data-mce-name': label
25096                }
25097            },
25098            components: [
25099                makeIcon('lock'),
25100                makeIcon('unlock')
25101            ],
25102            buttonBehaviours: derive$1([
25103                Disabling.config({ disabled }),
25104                toggleOnReceive$1,
25105                Tabstopping.config({}),
25106                Tooltipping.config(providersBackstage.tooltips.getConfig({
25107                    tooltipText: translatedLabel
25108                }))
25109            ])
25110        });
25111        const formGroup = (components) => ({
25112            dom: {
25113                tag: 'div',
25114                classes: ['tox-form__group']
25115            },
25116            components
25117        });
25118        const getFieldPart = (isField1) => FormField.parts.field({
25119            factory: Input,
25120            inputClasses: ['tox-textfield'],
25121            inputBehaviours: derive$1([
25122                Disabling.config({ disabled }),
25123                toggleOnReceive$1,
25124                Tabstopping.config({}),
25125                config('size-input-events', [
25126                    run$1(focusin(), (component, _simulatedEvent) => {
25127                        emitWith(component, ratioEvent, { isField1 });
25128                    }),
25129                    run$1(change(), (component, _simulatedEvent) => {
25130                        emitWith(component, formChangeEvent, { name: spec.name });
25131                    })
25132                ])
25133            ]),
25134            selectOnFocus: false
25135        });
25136        const getLabel = (label) => ({
25137            dom: {
25138                tag: 'label',
25139                classes: ['tox-label']
25140            },
25141            components: [
25142                text$2(providersBackstage.translate(label))
25143            ]
25144        });
25145        const widthField = FormCoupledInputs.parts.field1(formGroup([FormField.parts.label(getLabel('Width')), getFieldPart(true)]));
25146        const heightField = FormCoupledInputs.parts.field2(formGroup([FormField.parts.label(getLabel('Height')), getFieldPart(false)]));
25147        return FormCoupledInputs.sketch({
25148            dom: {
25149                tag: 'div',
25150                classes: ['tox-form__group']
25151            },
25152            components: [
25153                {
25154                    dom: {
25155                        tag: 'div',
25156                        classes: ['tox-form__controls-h-stack']
25157                    },
25158                    components: [
25159                        // NOTE: Form coupled inputs to the FormField.sketch themselves.
25160                        widthField,
25161                        heightField,
25162                        formGroup([
25163                            getLabel(nbsp),
25164                            pLock
25165                        ])
25166                    ]
25167                }
25168            ],
25169            field1Name: 'width',
25170            field2Name: 'height',
25171            locked: true,
25172            markers: {
25173                lockClass: 'tox-locked'
25174            },
25175            onLockedChange: (current, other, _lock) => {
25176                parseSize(Representing.getValue(current)).each((size) => {
25177                    converter(size).each((newSize) => {
25178                        Representing.setValue(other, formatSize(newSize));
25179                    });
25180                });
25181            },
25182            coupledFieldBehaviours: derive$1([
25183                Disabling.config({
25184                    disabled,
25185                    onDisabled: (comp) => {
25186                        FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.disable);
25187                        FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.disable);
25188                        FormCoupledInputs.getLock(comp).each(Disabling.disable);
25189                    },
25190                    onEnabled: (comp) => {
25191                        FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.enable);
25192                        FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.enable);
25193                        FormCoupledInputs.getLock(comp).each(Disabling.enable);
25194                    }
25195                }),
25196                toggleOnReceive(() => providersBackstage.checkUiComponentContext('mode:design')),
25197                config('size-input-events2', [
25198                    run$1(ratioEvent, (component, simulatedEvent) => {
25199                        const isField1 = simulatedEvent.event.is
vendor: 4,139 bytes, lines 25199-25316
25199Field1;
25200                        const optCurrent = isField1 ? FormCoupledInputs.getField1(component) : FormCoupledInputs.getField2(component);
25201                        const optOther = isField1 ? FormCoupledInputs.getField2(component) : FormCoupledInputs.getField1(component);
25202                        const value1 = optCurrent.map(Representing.getValue).getOr('');
25203                        const value2 = optOther.map(Representing.getValue).getOr('');
25204                        converter = makeRatioConverter(value1, value2);
25205                    })
25206                ])
25207            ])
25208        });
25209    };
25210
25211    const renderSlider = (spec, providerBackstage, initialData) => {
25212        const labelPart = Slider.parts.label({
25213            dom: {
25214                tag: 'label',
25215                classes: ['tox-label']
25216            },
25217            components: [
25218                text$2(providerBackstage.translate(spec.label))
25219            ]
25220        });
25221        const spectrum = Slider.parts.spectrum({
25222            dom: {
25223                tag: 'div',
25224                classes: ['tox-slider__rail'],
25225                attributes: {
25226                    role: 'presentation'
25227                }
25228            }
25229        });
25230        const thumb = Slider.parts.thumb({
25231            dom: {
25232                tag: 'div',
25233                classes: ['tox-slider__handle'],
25234                attributes: {
25235                    role: 'presentation'
25236                }
25237            }
25238        });
25239        return Slider.sketch({
25240            dom: {
25241                tag: 'div',
25242                classes: ['tox-slider'],
25243                attributes: {
25244                    role: 'presentation'
25245                }
25246            },
25247            model: {
25248                mode: 'x',
25249                minX: spec.min,
25250                maxX: spec.max,
25251                getInitialValue: constant$1(initialData.getOrThunk(() => (Math.abs(spec.max) - Math.abs(spec.min)) / 2))
25252            },
25253            components: [
25254                labelPart,
25255                spectrum,
25256                thumb
25257            ],
25258            sliderBehaviours: derive$1([
25259                ComposingConfigs.self(),
25260                Focusing.config({})
25261            ]),
25262            onChoose: (component, thumb, value) => {
25263                emitWith(component, formChangeEvent, { name: spec.name, value });
25264            },
25265            onChange: (component, thumb, value) => {
25266                emitWith(component, formChangeEvent, { name: spec.name, value });
25267            },
25268        });
25269    };
25270
25271    const renderTable = (spec, providersBackstage) => {
25272        const renderTh = (text) => ({
25273            dom: {
25274                tag: 'th',
25275                innerHtml: providersBackstage.translate(text)
25276            }
25277        });
25278        const renderHeader = (header) => ({
25279            dom: {
25280                tag: 'thead'
25281            },
25282            components: [
25283                {
25284                    dom: {
25285                        tag: 'tr'
25286                    },
25287                    components: map$2(header, renderTh)
25288                }
25289            ]
25290        });
25291        const renderTd = (text) => ({ dom: { tag: 'td', innerHtml: providersBackstage.translate(text) } });
25292        const renderTr = (row) => ({ dom: { tag: 'tr' }, components: map$2(row, renderTd) });
25293        const renderRows = (rows) => ({ dom: { tag: 'tbody' }, components: map$2(rows, renderTr) });
25294        return {
25295            dom: {
25296                tag: 'table',
25297                classes: ['tox-dialog__table']
25298            },
25299            components: [
25300                renderHeader(spec.header),
25301                renderRows(spec.cells)
25302            ],
25303            behaviours: derive$1([
25304                Tabstopping.config({}),
25305                Focusing.config({})
25306            ])
25307        };
25308    };
25309
25310    const renderTextField = (spec, providersBackstage) => {
25311        const pLabel = spec.label.map((label) => renderLabel$3(label, providersBackstage));
25312        const baseInputBehaviours = [
25313            Disabling.config({
25314                disabled: () => spec.disabled || providersBackstage.checkUiComponentContext(spec.context).shouldDisable
25315            }),
25316            toggleOnReceive(() =>
25316 providersBackstage.checkUiComponentContext(spec.context)),
25317            Keying.config({
25318                mode: 'execution',
25319                useEnter: spec.multiline !== true,
25320                useControlEnter: spec.multiline === true,
25321                execute: (comp) => {
25322                    emit(comp, formSubmitEvent);
25323                    return Optional.some(true);
25324                }
25325            }),
25326            config('textfield-change', [
25327                run$1(input(), (component, _) => {
25328                    emitWith(component, formChangeEvent, { name: spec.name });
25329                }),
25330                run$1(postPaste(), (component, _) => {
25331                    emitWith(component, formChangeEvent, { name: spec.name });
25332                })
25333            ]),
25334            Tabstopping.config({})
25335        ];
25336        const validatingBehaviours = spec.validation.map((vl) => Invalidating.config({
25337            getRoot: (input) => {
25338                return parentElement(input.element);
25339            },
25340            invalidClass: 'tox-invalid',
25341            validator: {
25342                validate: (input) => {
25343                    const v = Representing.getValue(input);
25344                    const result = vl.validator(v);
25345                    return Future.pure(result === true ? Result.value(v) : Result.error(result));
25346                },
25347                validateOnLoad: vl.validateOnLoad
25348            }
25349        })).toArray();
25350        const placeholder = spec.placeholder.fold(constant$1({}), (p) => ({ placeholder: providersBackstage.translate(p) }));
25351        const inputMode = spec.inputMode.fold(constant$1({}), (mode) => ({ inputmode: mode }));
25352        const spellcheck = spec.spellcheck.fold(constant$1({}), (spellchecker) => ({ spellcheck: spellchecker }));
25353        const inputAttributes = {
25354            ...spellcheck,
25355            ...placeholder,
25356            ...inputMode,
25357            'data-mce-name': spec.name
25358        };
25359        const pField = FormField.parts.field({
25360            tag: spec.multiline === true ? 'textarea' : 'input',
25361            ...spec.data.map((data) => ({ data })).getOr({}),
25362            inputAttributes,
25363            inputClasses: [spec.classname],
25364            inputBehaviours: derive$1(flatten([
25365                baseInputBehaviours,
25366                validatingBehaviours
25367            ])),
25368            selectOnFocus: false,
25369            factory: Input
25370        });
25371        // TINY-9331: This wrapper is needed to avoid border-radius rendering issues when the textarea has a scrollbar
25372        const pTextField = spec.multiline ? {
25373            dom: {
25374                tag: 'div',
25375                classes: ['tox-textarea-wrap']
25376            },
25377            components: [pField]
25378        } : pField;
25379        const extraClasses = spec.flex ? ['tox-form__group--stretched'] : [];
25380        const extraClasses2 = extraClasses.concat(spec.maximized ? ['tox-form-group--maximize'] : []);
25381        const extraBehaviours = [
25382            Disabling.config({
25383                disabled: () => spec.disabled || providersBackstage.checkUiComponentContext(spec.context).shouldDisable,
25384                onDisabled: (comp) => {
25385                    FormField.getField(comp).each(Disabling.disable);
25386                },
25387                onEnabled: (comp) => {
25388                    FormField.getField(comp).each(Disabling.enable);
25389                }
25390            }),
25391            toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context)),
25392        ];
25393        return renderFormFieldWith(pLabel, pTextField, extraClasses2, extraBehaviours);
25394    };
25395    const renderInput = (spec, providersBackstage, initialData) => renderTextField({
25396        name: spec.name,
25397        multiline: false,
25398        label: spec.label,
25399        inputMode: spec.inputMode,
25400        placeholder: spec.placeholder,
25401        flex: false,
25402        disabled: !spec.enabled,
25403        classname: 'tox-textfield',
25404        validation: Optional.none(),
25405        maximized: spec.maximized,
25406        data: initialData,
25407        context: spec.context,
25408        spellcheck: Optional.none(),
25409    }, providersBackstage);
25410    const renderTextarea = (spec, providersBackstage, initialData) => renderTextField({
25411        name: spec.name,
25412        multiline: true,
25413        label: spec.label,
25414        inputMode: Optional.none(), // type attribute is not val
25414id for textareas
25415        placeholder: spec.placeholder,
25416        flex: true,
25417        disabled: !spec.enabled,
25418        classname: 'tox-textarea',
25419        validation: Optional.none(),
25420        maximized: spec.maximized,
25421        data: initialData,
25422        context: spec.context,
25423        spellcheck: spec.spellcheck,
25424    }, providersBackstage);
25425
25426    const getMenuButtonApi = (component) => ({
25427        isEnabled: () => !Disabling.isDisabled(component),
25428        setEnabled: (state) => Disabling.set(component, !state),
25429        setActive: (state) => {
25430            // Note: We can't use the toggling behaviour here, as the dropdown for the menu also relies on it.
25431            // As such, we'll need to do this manually
25432            const elm = component.element;
25433            if (state) {
25434                add$2(elm, "tox-tbtn--enabled" /* ToolbarButtonClasses.Ticked */);
25435                set$9(elm, 'aria-pressed', true);
25436            }
25437            else {
25438                remove$3(elm, "tox-tbtn--enabled" /* ToolbarButtonClasses.Ticked */);
25439                remove$8(elm, 'aria-pressed');
25440            }
25441        },
25442        isActive: () => has(component.element, "tox-tbtn--enabled" /* ToolbarButtonClasses.Ticked */),
25443        setTooltip: (tooltip) => {
25444            emitWith(component, updateTooltiptext, {
25445                text: tooltip
25446            });
25447        },
25448        setText: (text) => {
25449            emitWith(component, updateMenuText, {
25450                text
25451            });
25452        },
25453        setIcon: (icon) => emitWith(component, updateMenuIcon, {
25454            icon
25455        })
25456    });
25457    const renderMenuButton = (spec, prefix, backstage, role, tabstopping = true, btnName) => {
25458        const classes = spec.buttonType === 'bordered' ? ['bordered'] : [];
25459        return renderCommonDropdown({
25460            text: spec.text,
25461            icon: spec.icon,
25462            tooltip: spec.tooltip,
25463            ariaLabel: spec.tooltip,
25464            searchable: spec.search.isSome(),
25465            // https://www.w3.org/TR/wai-aria-practices/examples/menubar/menubar-2/menubar-2.html
25466            role,
25467            fetch: (dropdownComp, callback) => {
25468                const fetchContext = {
25469                    pattern: spec.search.isSome() ? getSearchPattern(dropdownComp) : ''
25470                };
25471                spec.fetch((items) => {
25472                    callback(build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
25473                        isHorizontalMenu: false,
25474                        // MenuButtons are the only dropdowns that support searchable (2022-08-16)
25475                        search: spec.search
25476                    }));
25477                }, fetchContext, getMenuButtonApi(dropdownComp));
25478            },
25479            onSetup: spec.onSetup,
25480            getApi: (comp) => getMenuButtonApi(comp),
25481            columns: 1,
25482            presets: 'normal',
25483            classes,
25484            dropdownBehaviours: [
25485                ...(tabstopping ? [Tabstopping.config({})] : []),
25486            ],
25487            context: spec.context
25488        }, prefix, backstage.shared, btnName);
25489    };
25490    const getFetch = (items, getButton, backstage) => {
25491        const getMenuItemAction = (item) => (api) => {
25492            // Update the menu item state
25493            const newValue = !api.isActive();
25494            api.setActive(newValue);
25495            item.storage.set(newValue);
25496            // Fire the form action event
25497            backstage.shared.getSink().each((sink) => {
25498                getButton().getOpt(sink).each((orig) => {
25499                    focus$4(orig.element);
25500                    emitWith(orig, formActionEvent, {
25501                        name: item.name,
25502                        value: item.storage.get()
25503                    });
25504                });
25505            });
25506        };
25507        const getMenuItemSetup = (item) => (api) => {
25508            api.setActive(item.storage.get());
25509        };
25510        return (success) => {
25511            success(map$2(items, (item) => {
25512                const text = item.text.fold(() => ({}), (text) => ({
25513                    text
25514                }));
25515                return {
25516                    type: item.type,
25517                    active: false,
25518                    ...text,
25519                    context: item.context,
25520                    onAction: getMenuItemAction(item),
25521                    onSetup: getMenuItemSetup(item)
25522                };
25523            }));
25524        };
25525    };
25526
25527    const renderLabel = (text) => ({
25528        dom: {
25529            tag: 'span',
25530            classes: ['tox-tree__label'],
25531            attributes: {
25532                'aria-label': text,
25533            }
25534        },
25535        components: [
25536            text$2(text)
25537        ],
25538    });
25539    const renderCustomStateIcon = (container, components, backstage) => {
25540        container.customStateIcon.each((icon) => components.push(renderIcon(icon, backstage.shared.providers.icons, container.customStateIconTooltip.fold(() => [], (tooltip) => [
25541            Tooltipping.config(backstage.shared.providers.tooltips.getConfig({
25542                tooltipText: tooltip
25543            }))
25544        ]), ['tox-icon-custom-state'])));
25545    };
25546    const leafLabelEventsId = generate$6('leaf-label-event-id');
25547    const renderLeafLabel = ({ leaf, onLeafAction, visible, treeId, selectedId, backstage }) => {
25548        const internalMenuButton = leaf.menu.map((btn) => renderMenuButton(btn, 'tox-mbtn', backstage, Optional.none(), visible));
25549        const components = [renderLabel(leaf.title)];
25550        renderCustomStateIcon(leaf, components, backstage);
25551        internalMenuButton.each((btn) => components.push(btn));
25552        return Button.sketch({
25553            dom: {
25554                tag: 'div',
25555                classes: ['tox-tree--leaf__label', 'tox-trbtn']
25556                    .concat(visible ? ['tox-tree--leaf__label--visible'] : []),
25557            },
25558            components,
25559            role: 'treeitem',
25560            action: (button) => {
25561                onLeafAction(leaf.id);
25562                button.getSystem().broadcastOn([`update-active-item-${treeId}`], {
25563                    value: leaf.id
25564                });
25565            },
25566            eventOrder: {
25567                [keydown()]: [
25568                    leafLabelEventsId,
25569                    'keying',
25570                ]
25571            },
25572            buttonBehaviours: derive$1([
25573                ...(visible ? [Tabstopping.config({})] : []),
25574                Toggling.config({
25575                    toggleClass: 'tox-trbtn--enabled',
25576                    toggleOnExecute: false,
25577                    aria: {
25578                        mode: 'selected'
25579                    }
25580                }),
25581                Receiving.config({
25582                    channels: {
25583                        [`update-active-item-${treeId}`]: {
25584                            onReceive: (comp, message) => {
25585                                (message.value === leaf.id ? Toggling.on : Toggling.off)(comp);
25586                            }
25587                        }
25588                    }
25589                }),
25590                config(leafLabelEventsId, [
25591                    runOnAttached((comp, _se) => {
25592                        selectedId.each((id) => {
25593                            const toggle = id === leaf.id ? Toggling.on : Toggling.off;
25594                            toggle(comp);
25595                        });
25596                    }),
25597                    run$1(keydown(), (comp, se) => {
25598                        const isLeftArrowKey = se.event.raw.code === 'ArrowLeft';
25599                        const isRightArrowKey = se.event.raw.code === 'ArrowRight';
25600                        if (isLeftArrowKey) {
25601                            ancestor$1(comp.element, '.tox-tree--directory').each((dirElement) => {
25602                                comp.getSystem().getByDom(dirElement).each((dirComp) => {
25603                                    child(dirElement, '.tox-tree--directory__label').each((dirLabelElement) => {
25604                                        dirComp.getSystem().getByDom(dirLabelElement).each(Focusing.focus);
25605                                    });
25606                                });
25607                            });
25608                            se.stop();
25609                        }
25610                        else if (isRightArrowKey) {
25611                            se.stop();
25612                        }
25613                    })
25614                ])
25615            ]),
25616        });
25617    };
25618    const renderIcon = (iconName, iconsProvider, behaviours, extraClasses, extraAttributes) => render$4(iconName, {
25619        tag: 'span',
25620        classes: [
25621            'tox-tree__icon-wrap',
25622            'tox-icon',
25623        ].concat(extraClasses || []),
25624        behaviours,
25625        attributes: extraAttributes
25626    }, iconsProvider);
25627    const renderIconFromPack = (iconName, iconsProvider) => renderIcon(iconName, iconsProvider, []);
25628    const directoryLabelEventsId = generate$6('directory-label-event-id');
25629    const renderDirectoryLabel = ({ directory, visible, noChildren, backstage }) => {
25630        const internalMenuButton = directory.menu.map((btn) => renderMenuButton(btn, 'tox-mbtn', backstage, Optional.none()));
25631        const components = [
25632            {
25633                dom: {
25634                    tag: 'div',
25635                    classes: ['tox-chevron'],
25636                },
25637                components: [
25638                    renderIconFromPack('chevron-right', backstage.shared.providers.icons),
25639                ]
25640            },
25641            renderLabel(directory.title)
25642        ];
25643        renderCustomStateIcon(directory, components, backstage);
25644        internalMenuButton.each((btn) => {
25645            components.push(btn);
25646        });
25647        const toggleExpandChildren = (button) => {
25648            ancestor$1(button.element, '.tox-tree--directory').each((directoryEle) => {
25649                button.getSystem().getByDom(directoryEle).each((directoryComp) => {
25650                    const willExpand = !Toggling.isOn(directoryComp);
25651                    Toggling.toggle(directoryComp);
25652                    emitWith(button, 'expand-tree-node', { expanded: willExpand, node: directory.id });
25653                });
25654            });
25655        };
25656        return Button.sketch({
25657            dom: {
25658                tag: 'div',
25659                classes: ['tox-tree--directory__label', 'tox-trbtn'].concat(visible ? ['tox-tree--directory__label--visible'] : []),
25660            },
25661            components,
25662            action: toggleExpandChildren,
25663            eventOrder: {
25664                [keydown()]: [
25665                    directoryLabelEventsId,
25666                    'keying',
25667                ]
25668            },
25669            buttonBehaviours: derive$1([
25670                ...(visible ? [Tabstopping.config({})] : []),
25671                config(directoryLabelEventsId, [
25672                    run$1(keydown(), (comp, se) => {
25673                        const isRightArrowKey = se.event.raw.code === 'ArrowRight';
25674                        const isLeftArrowKey = se.event.raw.code === 'ArrowLeft';
25675                        if (isRightArrowKey && noChildren) {
25676                            se.stop();
25677                        }
25678                        if (isRightArrowKey || isLeftArrowKey) {
25679                            ancestor$1(comp.element, '.tox-tree--directory').each((directoryEle) => {
25680                                comp.getSystem().getByDom(directoryEle).each((directoryComp) => {
25681                                    if (!Toggling.isOn(directoryComp) && isRightArrowKey || Toggling.isOn(directoryComp) && isLeftArrowKey) {
25682                                        toggleExpandChildren(comp);
25683                                        se.stop();
25684                                    }
25685                                    else if (isLeftArrowKey && !Toggling.isOn(directoryComp)) {
25686                                        ancestor$1(directoryComp.element, '.tox-tree--directory').each((parentDirElement) => {
25687                                            child(parentDirElement, '.tox-tree--directory__label').each((parentDirLabelElement) => {
25688                                                directoryComp.getSystem().getByDom(parentDirLabelElement).each(Focusing.focus);
25689                                            });
25690                                        });
25691                                        se.stop();
25692                                    }
25693                                });
25694                            });
25695                        }
25696                    })
25697                ])
25698            ])
25699        });
25700    };
25701    const renderDirectoryChildren = ({ children, onLeafAction, visible, treeId, expandedIds, selectedId, backstage }) => {
25702        return {
25703            dom: {
25704                tag: 'div',
25705                classes: ['tox-tree--directory__children'],
25706            },
25707            components: children.map((item) => {
25708                return item.type === 'leaf' ?
25709                    renderLeafLabel({ leaf: item, selectedId, onLeafAction, visible, treeId, backstage }) :
25710                    renderDirectory({ directory: item, expandedIds, selectedId, onLeafAction, labelTabstopping: visible, treeId, backstage });
25711            }),
25712            behaviours: derive$1([
25713                Sliding.config({
25714                    dimension: {
25715                        property: 'height'
25716                    },
25717                    closedClass: 'tox-tree--directory__children--closed',
25718                    openClass: 'tox-tree--directory__children--open',
25719                    growingClass: 'tox-tree--directory__children--growing',
25720                    shrinkingClass: 'tox-tree--directory__children--shrinking',
25721                    expanded: visible,
25722                }),
25723                Replacing.config({})
25724            ])
25725        };
25726    };
25727    const directoryEventsId = generate$6('directory-event-id');
25728    const renderDirectory = ({ directory, onLeafAction, labelTabstopping, treeId, backstage, expandedIds, selectedId }) => {
25729        const { children } = directory;
25730        const expandedIdsCell = Cell(expandedIds);
25731        const computedChildrenComponents = (visible) => children.map((item) => {
25732            return item.type === 'leaf' ?
25733                renderLeafLabel({ leaf: item, selectedId, onLeafAction, visible, treeId, backstage }) :
25734                renderDirectory({ directory: item, expandedIds: expandedIdsCell.get(), selectedId, onLeafAction, labelTabstopping: visible, treeId, backstage });
25735        });
25736        const childrenVisible = expandedIds.includes(directory.id);
25737        return ({
25738            dom: {
25739                tag: 'div',
25740                classes: ['tox-tree--directory'],
25741                attributes: {
25742                    role: 'treeitem'
25743                }
25744            },
25745            components: [
25746                renderDirectoryLabel({ directory, visible: labelTabstopping, noChildren: directory.children.length === 0, backstage }),
25747                renderDirectoryChildren({ children, expandedIds, selectedId, onLeafAction, visible: childrenVisible, treeId, backstage })
25748            ],
25749            behaviours: derive$1([
25750                config(directoryEventsId, [
25751                    runOnAttached((comp, _se) => {
25752                        Toggling.set(comp, childrenVisible);
25753                    }),
25754                    run$1('expand-tree-node', (_cmp, se) => {
25755                        const { expanded, node } = se.event;
25756                        expandedIdsCell.set(expanded ?
25757                            [...expandedIdsCell.get(), node] :
25758                            expandedIdsCell.get().filter((id) => id !== node));
25759                    }),
25760                ]),
25761                Toggling.config({
25762                    ...(directory.children.length > 0 ? {
25763                        aria: {
25764                            mode: 'expanded',
25765                        },
25766                    } : {}),
25767                    toggleClass: 'tox-tree--directory--expanded',
25768                    onToggled: (comp, childrenVisible) => {
25769                        const childrenComp = comp.components()[1];
25770                        const newChildren = computedChildrenComponents(childrenVisible);
25771                        if (childrenVisible) {
25772                            Sliding.grow(childrenComp);
25773                        }
25774                        else {
25775                            Sliding.shrink(childrenComp);
25776                        }
25777                        Replacing.set(childrenComp, newChildren);
25778                    },
25779                }),
25780            ])
25781        });
25782    };
25783    const treeEventsId = generate$6('tree-event-id');
25784    const renderTree = (spec, backstage) => {
25785        const onLeafAction = spec.onLeafAction.getOr(noop);
25786        const onToggleExpand = spec.onToggleExpand.getOr(noop);
25787        const defaultExpandedIds = spec.defaultExpandedIds;
25788        const expandedIds = Cell(defaultExpandedIds);
25789        const selectedIdCell = Cell(spec.defaultSelectedId);
25790        const treeId = generate$6('tree-id');
25791        const children = (selectedId, expandedIds) => spec.items.map((item) => {
25792            return item.type === 'leaf' ?
25793                renderLeafLabel({ leaf: item, selectedId, onLeafAction, visible: true, treeId, backstage }) :
25794                renderDirectory({ directory: item, selectedId, onLeafAction, expandedIds, labelTabstopping: true, treeId, backstage });
25795        });
25796        return {
25797            dom: {
25798                tag: 'div',
25799                classes: ['tox-tree'],
25800                attributes: {
25801                    role: 'tree'
25802                }
25803            },
25804            components: children(selectedIdCell.get(), expandedIds.get()),
25805            behaviours: derive$1([
25806                Keying.config({
25807                    mode: 'flow',
25808                    selector: '.tox-tree--leaf__label--visible, .tox-tree--directory__label--visible',
25809                    cycles: false,
25810                }),
25811                config(treeEventsId, [
25812                    run$1('expand-tree-node', (_cmp, se) => {
25813                        const { expanded, node } = se.event;
25814                        expandedIds.set(expanded ?
25815                            [...expandedIds.get(), node] :
25816                            expandedIds.get().filter((id) => id !== node));
25817                        onToggleExpand(expandedIds.get(), { expanded, node });
25818                    })
25819                ]),
25820                Receiving.config({
25821                    channels: {
25822                        [`update-active-item-${treeId}`]: {
25823                            onReceive: (comp, message) => {
25824                                selectedIdCell.set(Optional.some(message.value));
25825                                Replacing.set(comp, children(Optional.some(message.value), expandedIds.get()));
25826                            }
25827                        }
25828                    }
25829                }),
25830                Replacing.config({})
25831            ])
25832        };
25833    };
25834
25835    const renderCommonSpec = (spec, actionOpt, extraBehaviours = [], dom, components, tooltip, providersBackstage) => {
25836        const action = actionOpt.fold(() => ({}), (action) => ({
25837            action
25838        }));
25839        const common = {
25840            buttonBehaviours: derive$1([
25841                DisablingConfigs.item(() => !spec.enabled || providersBackstage.checkUiComponentContext(spec.c
25841ontext).shouldDisable),
25842                toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context)),
25843                Tabstopping.config({}),
25844                ...tooltip.map((t) => Tooltipping.config(providersBackstage.tooltips.getConfig({
25845                    tooltipText: providersBackstage.translate(t)
25846                }))).toArray(),
25847                config('button press', [
25848                    preventDefault('click')
25849                ])
25850            ].concat(extraBehaviours)),
25851            eventOrder: {
25852                click: ['button press', 'alloy.base.behaviour'],
25853                mousedown: ['button press', 'alloy.base.behaviour']
25854            },
25855            ...action
25856        };
25857        const domFinal = deepMerge(common, { dom });
25858        return deepMerge(domFinal, { components });
25859    };
25860    // An IconButton just seems to be a button that *cannot* have text, but
25861    // can have a tooltip. It's only used for the More Drawer button at the moment.
25862    const renderIconButtonSpec = (spec, action, providersBackstage, extraBehaviours = [], btnName) => {
25863        const tooltipAttributes = spec.tooltip.map((tooltip) => ({
25864            'aria-label': providersBackstage.translate(tooltip),
25865        })).getOr({});
25866        const dom = {
25867            tag: 'button',
25868            classes: ["tox-tbtn" /* ToolbarButtonClasses.Button */],
25869            attributes: { ...tooltipAttributes, 'data-mce-name': btnName }
25870        };
25871        const icon = spec.icon.map((iconName) => renderIconFromPack$1(iconName, providersBackstage.icons));
25872        const components = componentRenderPipeline([
25873            icon
25874        ]);
25875        return renderCommonSpec(spec, action, extraBehaviours, dom, components, spec.tooltip, providersBackstage);
25876    };
25877    const calculateClassesFromButtonType = (buttonType) => {
25878        switch (buttonType) {
25879            case 'primary':
25880                return ['tox-button'];
25881            case 'toolbar':
25882                return ['tox-tbtn'];
25883            case 'secondary':
25884            default:
25885                return ['tox-button', 'tox-button--secondary'];
25886        }
25887    };
25888    // Maybe the list of extraBehaviours is better than doing a Merger.deepMerge that
25889    // we do elsewhere? Not sure.
25890    const renderButtonSpec = (spec, action, providersBackstage, extraBehaviours = [], extraClasses = []) => {
25891        // It's a bit confusing that this is called text. It seems to be a tooltip. Although I can see
25892        // that it's used if there is no icon
25893        const translatedText = providersBackstage.translate(spec.text);
25894        const icon = spec.icon.map((iconName) => renderIconFromPack$1(iconName, providersBackstage.icons));
25895        const components = [icon.getOrThunk(() => text$2(translatedText))];
25896        // The old default is based on the now-deprecated 'primary' property. `buttonType` takes precedence now.
25897        const buttonType = spec.buttonType.getOr(!spec.primary && !spec.borderless ? 'secondary' : 'primary');
25898        const baseClasses = calculateClassesFromButtonType(buttonType);
25899        const classes = [
25900            ...baseClasses,
25901            ...icon.isSome() ? ['tox-button--icon'] : [],
25902            ...spec.borderless ? ['tox-button--naked'] : [],
25903            ...extraClasses
25904        ];
25905        const dom = {
25906            tag: 'button',
25907            classes,
25908            attributes: {
25909                'aria-label': translatedText,
25910                'data-mce-name': spec.text
25911            }
25912        };
25913        // Only provide a tooltip if we are using an icon. This is because above, a button is only an icon
25914        // or text, and not both.
25915        const optTooltip = spec.icon.map(constant$1(translatedText));
25916        return renderCommonSpec(spec, action, extraBehaviours, dom, components, optTooltip, providersBackstage);
25917    };
25918    // This actually seems to be a button on the dialog for UrlInput only (browse). Interesting.
25919    const renderButton$1 = (spec, action, providersBackstage, extraBehaviours = [], extraClasses = []) => {
25920        const buttonSpec = renderButtonSpec(spec, Optional.some(action), providersBackstage, extraBehaviours, extraClasse
25920s);
25921        return Button.sketch(buttonSpec);
25922    };
25923    const getAction = (name, buttonType) => (comp) => {
25924        if (buttonType === 'custom') {
25925            emitWith(comp, formActionEvent, {
25926                name,
25927                value: {}
25928            });
25929        }
25930        else if (buttonType === 'submit') {
25931            emit(comp, formSubmitEvent);
25932        }
25933        else if (buttonType === 'cancel') {
25934            emit(comp, formCancelEvent);
25935        }
25936        else {
25937            // eslint-disable-next-line no-console
25938            console.error('Unknown button type: ', buttonType);
25939        }
25940    };
25941    const isMenuFooterButtonSpec = (spec, buttonType) => buttonType === 'menu';
25942    const isNormalFooterButtonSpec = (spec, buttonType) => buttonType === 'custom' || buttonType === 'cancel' || buttonType === 'submit';
25943    const isToggleButtonSpec = (spec, buttonType) => buttonType === 'togglebutton';
25944    const renderToggleButton = (spec, providers, btnName) => {
25945        const optMemIcon = spec.icon
25946            .map((memIcon) => renderReplaceableIconFromPack(memIcon, providers.icons))
25947            .map(record);
25948        const action = (comp) => {
25949            emitWith(comp, formActionEvent, {
25950                name: spec.name,
25951                value: {
25952                    setIcon: (newIcon) => {
25953                        optMemIcon.map((memIcon) => memIcon.getOpt(comp).each((displayIcon) => {
25954                            Replacing.set(displayIcon, [
25955                                renderReplaceableIconFromPack(newIcon, providers.icons)
25956                            ]);
25957                        }));
25958                    }
25959                }
25960            });
25961        };
25962        // The old default is based on the now-deprecated 'primary' property. `buttonType` takes precedence now.
25963        const buttonType = spec.buttonType.getOr(!spec.primary ? 'secondary' : 'primary');
25964        const buttonSpec = {
25965            ...spec,
25966            name: spec.name ?? '',
25967            primary: buttonType === 'primary',
25968            tooltip: spec.tooltip,
25969            enabled: spec.enabled ?? false,
25970            borderless: false
25971        };
25972        const tooltipAttributes = buttonSpec.tooltip.or(spec.text).map((tooltip) => ({
25973            'aria-label': providers.translate(tooltip),
25974        })).getOr({});
25975        const buttonTypeClasses = calculateClassesFromButtonType(buttonType ?? 'secondary');
25976        const showIconAndText = spec.icon.isSome() && spec.text.isSome();
25977        const dom = {
25978            tag: 'button',
25979            classes: [
25980                ...buttonTypeClasses.concat(spec.icon.isSome() ? ['tox-button--icon'] : []),
25981                ...(spec.active ? ["tox-button--enabled" /* ViewButtonClasses.Ticked */] : []),
25982                ...(showIconAndText ? ['tox-button--icon-and-text'] : [])
25983            ],
25984            attributes: {
25985                ...tooltipAttributes,
25986                ...(isNonNullable(btnName) ? { 'data-mce-name': btnName } : {})
25987            }
25988        };
25989        const extraBehaviours = [];
25990        const translatedText = providers.translate(spec.text.getOr(''));
25991        const translatedTextComponed = text$2(translatedText);
25992        const iconComp = componentRenderPipeline([optMemIcon.map((memIcon) => memIcon.asSpec())]);
25993        const components = [
25994            ...iconComp,
25995            ...(spec.text.isSome() ? [translatedTextComponed] : [])
25996        ];
25997        const iconButtonSpec = renderCommonSpec(buttonSpec, Optional.some(action), extraBehaviours, dom, components, spec.tooltip, providers);
25998        return Button.sketch(iconButtonSpec);
25999    };
26000    const renderFooterButton = (spec, buttonType, backstage) => {
26001        if (isMenuFooterButtonSpec(spec, buttonType)) {
26002            const getButton = () => memButton;
26003            const menuButtonSpec = spec;
26004            const fixedSpec = {
26005                ...spec,
26006                buttonType: 'default',
26007                type: 'menubutton',
26008                // Currently, dialog-based menu buttons cannot be searchable.
26009                search: Optional.none(),
26010                onSetup: (api) => {
26011                    api.setEnabled(spec.enabled);
26012                    return noop;
26013                },
26014                fetch: getFetch(menuButtonSpec.items, getButton, backstage)
26015            };
26016            const memButton = record(renderMenuButton(fixedSpec, "tox-tbtn" /* ToolbarButtonClasse
26016s.Button */, backstage, Optional.none(), true, spec.text.or(spec.tooltip).getOrUndefined()));
26017            return memButton.asSpec();
26018        }
26019        else if (isNormalFooterButtonSpec(spec, buttonType)) {
26020            const action = getAction(spec.name, buttonType);
26021            const buttonSpec = {
26022                ...spec,
26023                context: buttonType === 'cancel' ? 'any' : spec.context,
26024                borderless: false
26025            };
26026            return renderButton$1(buttonSpec, action, backstage.shared.providers, []);
26027        }
26028        else if (isToggleButtonSpec(spec, buttonType)) {
26029            return renderToggleButton(spec, backstage.shared.providers, spec.text.or(spec.tooltip).getOrUndefined());
26030        }
26031        else {
26032            // eslint-disable-next-line no-console
26033            console.error('Unknown footer button type: ', buttonType);
26034            throw new Error('Unknown footer button type');
26035        }
26036    };
26037    const renderDialogButton = (spec, providersBackstage) => {
26038        const action = getAction(spec.name, 'custom');
26039        return renderFormField(Optional.none(), FormField.parts.field({
26040            factory: Button,
26041            ...renderButtonSpec(spec, Optional.some(action), providersBackstage, [
26042                memory(''),
26043                ComposingConfigs.self()
26044            ])
26045        }));
26046    };
26047
26048    const separator$1 = {
26049        type: 'separator'
26050    };
26051    const toMenuItem = (target) => ({
26052        type: 'menuitem',
26053        value: target.url,
26054        text: target.title,
26055        meta: {
26056            attach: target.attach
26057        },
26058        onAction: noop
26059    });
26060    const staticMenuItem = (title, url) => ({
26061        type: 'menuitem',
26062        value: url,
26063        text: title,
26064        meta: {
26065            attach: undefined
26066        },
26067        onAction: noop
26068    });
26069    const toMenuItems = (targets) => map$2(targets, toMenuItem);
26070    const filterLinkTargets = (type, targets) => filter$2(targets, (target) => target.type === type);
26071    const filteredTargets = (type, targets) => toMenuItems(filterLinkTargets(type, targets));
26072    const headerTargets = (linkInfo) => filteredTargets('header', linkInfo.targets);
26073    const anchorTargets = (linkInfo) => filteredTargets('anchor', linkInfo.targets);
26074    const anchorTargetTop = (linkInfo) =>
26074 Optional.from(linkInfo.anchorTop).map((url) => staticMenuItem('<top>', url)).toArray();
26075    const anchorTargetBottom = (linkInfo) => Optional.from(linkInfo.anchorBottom).map((url) => staticMenuItem('<bottom>', url)).toArray();
26076    const historyTargets = (history) => map$2(history, (url) => staticMenuItem(url, url));
26077    const joinMenuLists = (items) => {
26078        return foldl(items, (a, b) => {
26079            const bothEmpty = a.length === 0 || b.length === 0;
26080            return bothEmpty ? a.concat(b) : a.concat(separator$1, b);
26081        }, []);
26082    };
26083    const filterByQuery = (term, menuItems) => {
26084        const lowerCaseTerm = term.toLowerCase();
26085        return filter$2(menuItems, (item) => {
26086            const text = item.meta !== undefined && item.meta.text !== undefined ? item.meta.text : item.text;
26087            const value = item.value ?? '';
26088            return contains$1(text.toLowerCase(), lowerCaseTerm) || contains$1(value.toLowerCase(), lowerCaseTerm);
26089        });
26090    };
26091
26092    const getItems = (fileType, input, urlBackstage) => {
26093        const urlInputValue = Representing.getValue(input);
26094        const term = urlInputValue?.meta?.text ?? urlInputValue.value;
26095        const info = urlBackstage.getLinkInformation();
26096        return info.fold(() => [], (linkInfo) => {
26097            const history = filterByQuery(term, historyTargets(urlBackstage.getHistory(fileType)));
26098            return fileType === 'file' ? joinMenuLists([
26099                history,
26100                filterByQuery(term, headerTargets(linkInfo)),
26101                filterByQuery(term, flatten([
26102                    anchorTargetTop(linkInfo),
26103                    anchorTargets(linkInfo),
26104                    anchorTargetBottom(linkInfo)
26105                ]))
26106            ])
26107                : history;
26108        });
26109    };
26110    const errorId = generate$6('aria-invalid');
26111    const renderUrlInput = (spec, backstage, urlBackstage, initialData) => {
26112        const providersBackstage = backstage.shared.providers;
26113        const updateHistory = (component) => {
26114            const urlEntry = Representing.getValue(component);
26115            urlBackstage.addToHistory(urlEntry.value, spec.filetype);
26116        };
26117        // TODO: Make alloy's typeahead only swallow enter and escape if menu is open
26118        const typeaheadSpec = {
26119            ...initialData.map((initialData) => ({ initialData })).getOr({}),
26120            dismissOnBlur: true,
26121            inputClasses: ['tox-textfield'],
26122            sandboxClasses: ['tox-dialog__popups'],
26123            inputAttributes: {
26124                type: 'url'
26125            },
26126            minChars: 0,
26127            responseTime: 0,
26128            fetch: (input) => {
26129                const items = getItems(spec.filetype, input, urlBackstage);
26130                const tdata = build(items, ItemResponse$1.BUBBLE_TO_SANDBOX, backstage, {
26131                    isHorizontalMenu: false,
26132                    search: Optional.none()
26133                });
26134                return Future.pure(tdata);
26135            },
26136            getHotspot: (comp) => memUrlBox.getOpt(comp),
26137            onSetValue: (comp, _newValue) => {
26138                if (comp.hasConfigured(Invalidating)) {
26139                    Invalidating.run(comp).get(noop);
26140                }
26141            },
26142            typeaheadBehaviours: derive$1([
26143                ...urlBackstage.getValidationHandler().map((handler) => Invalidating.config({
26144                    getRoot: (comp) => parentElement(comp.element),
26145                    invalidClass: 'tox-control-wrap--status-invalid',
26146                    notify: {
26147                        onInvalid: (comp, err) => {
26148                            memInvalidIcon.getOpt(comp).each((invalidComp) => {
26149                                set$9(invalidComp.element, 'title', providersBackstage.translate(err));
26150                            });
26151                        }
26152                    },
26153                    validator: {
26154                        validate: (input) => {
26155                            const urlEntry = Representing.getValue(input);
26156                            return FutureResult.nu((completer) => {
26157                                handler({ type: spec.filetype, url: urlEntry.value }, (validation) => {
26158                                    if (validation.status === 'invali
vendor: 4,600 bytes, lines 26158-26260
26158d') {
26159                                        set$9(input.element, 'aria-errormessage', errorId);
26160                                        const err = Result.error(validation.message);
26161                                        completer(err);
26162                                    }
26163                                    else {
26164                                        remove$8(input.element, 'aria-errormessage');
26165                                        const val = Result.value(validation.message);
26166                                        completer(val);
26167                                    }
26168                                });
26169                            });
26170                        },
26171                        validateOnLoad: false
26172                    }
26173                })).toArray(),
26174                Disabling.config({
26175                    disabled: () => !spec.enabled || providersBackstage.checkUiComponentContext(spec.context).shouldDisable
26176                }),
26177                Tabstopping.config({}),
26178                config('urlinput-events', 
26179                // We want to get fast feedback for the link dialog, but not sure about others
26180                [
26181                    run$1(input(), (comp) => {
26182                        const currentValue = get$5(comp.element);
26183                        const trimmedValue = currentValue.trim();
26184                        if (trimmedValue !== currentValue) {
26185                            set$4(comp.element, trimmedValue);
26186                        }
26187                        if (spec.filetype === 'file') {
26188                            emitWith(comp, formChangeEvent, { name: spec.name });
26189                        }
26190                    }),
26191                    run$1(change(), (comp) => {
26192                        emitWith(comp, formChangeEvent, { name: spec.name });
26193                        updateHistory(comp);
26194                    }),
26195                    run$1(postPaste(), (comp) => {
26196                        emitWith(comp, formChangeEvent, { name: spec.name });
26197                        updateHistory(comp);
26198                    })
26199                ])
26200            ]),
26201            eventOrder: {
26202                [input()]: ['streaming', 'urlinput-events', 'invalidating']
26203            },
26204            model: {
26205                getDisplayText: (itemData) => itemData.value,
26206                selectsOver: false,
26207                populateFromBrowse: false
26208            },
26209            markers: {
26210                openClass: 'tox-textfield--popup-open'
26211            },
26212            lazySink: backstage.shared.getSink,
26213            parts: {
26214                menu: part(false, 1, 'normal')
26215            },
26216            onExecute: (_menu, component, _entry) => {
26217                emitWith(component, formSubmitEvent, {});
26218            },
26219            onItemExecute: (typeahead, _sandbox, _item, _value) => {
26220                updateHistory(typeahead);
26221                emitWith(typeahead, formChangeEvent, { name: spec.name });
26222            }
26223        };
26224        const pField = FormField.parts.field({
26225            ...typeaheadSpec,
26226            factory: Typeahead
26227        });
26228        const pLabel = spec.label.map((label) => renderLabel$3(label, providersBackstage));
26229        // TODO: Consider a way of merging with Checkbox.
26230        const makeIcon = (name, errId, icon = name, label = name) => render$4(icon, {
26231            tag: 'div',
26232            classes: ['tox-icon', 'tox-control-wrap__status-icon-' + name],
26233            attributes: {
26234                'title': providersBackstage.translate(label),
26235                'aria-live': 'polite',
26236                ...errId.fold(() => ({}), (id) => ({ id }))
26237            }
26238        }, providersBackstage.icons);
26239        const memInvalidIcon = record(makeIcon('invalid', Optional.some(errorId), 'warning'));
26240        const memStatus = record({
26241            dom: {
26242                tag: 'div',
26243                classes: ['tox-control-wrap__status-icon-wrap']
26244            },
26245            components: [
26246                // Include the 'valid' and 'unknown' icons here only if they are to be displayed
26247                memInvalidIcon.asSpec()
26248            ]
26249        });
26250        const optUrlPicker = urlBackstage.getUrlPicker(spec.filetype);
26251        const browseUrlEvent = generate$6('browser.url.event');
26252        const memUrlBox = record({
26253            dom: {
26254                tag: 'div',
26255                classes: ['tox-control-wrap']
26256            },
26257            components: [pField, memStatus.asSpec()],
26258            behaviours: derive$1([
26259                Disabling.config({
26260                    disabled: () => !spec.enabled || providersBackstage.checkUiComponentContext(spec.c
26260ontext).shouldDisable
26261                })
26262            ])
26263        });
26264        const memUrlPickerButton = record(renderButton$1({
26265            context: spec.context,
26266            name: spec.name,
26267            icon: Optional.some('browse'),
26268            text: spec.picker_text.or(spec.label).getOr(''),
26269            enabled: spec.enabled,
26270            primary: false,
26271            buttonType: Optional.none(),
26272            borderless: true
26273        }, (component) => emit(component, browseUrlEvent), providersBackstage, [], ['tox-browse-url']));
26274        const controlHWrapper = () => ({
26275            dom: {
26276                tag: 'div',
26277                classes: ['tox-form__controls-h-stack']
26278            },
26279            components: flatten([
26280                [memUrlBox.asSpec()],
26281                optUrlPicker.map(() => memUrlPickerButton.asSpec()).toArray()
26282            ])
26283        });
26284        const openUrlPicker = (comp) => {
26285            Composing.getCurrent(comp).each((field) => {
26286                const componentData = Representing.getValue(field);
26287                const urlData = {
26288                    fieldname: spec.name,
26289                    ...componentData
26290                };
26291                optUrlPicker.each((picker) => {
26292                    picker(urlData).get((chosenData) => {
26293                        Representing.setValue(field, chosenData);
26294                        emitWith(comp, formChangeEvent, { name: spec.name });
26295                    });
26296                });
26297            });
26298        };
26299        return FormField.sketch({
26300            dom: renderFormFieldDom(),
26301            components: pLabel.toArray().concat([
26302                controlHWrapper()
26303            ]),
26304            fieldBehaviours: derive$1([
26305                Disabling.config({
26306                    disabled: () => !spec.enabled || providersBackstage.checkUiComponentContext(spec.context).shouldDisable,
26307                    onDisabled: (comp) => {
26308                        FormField.getField(comp).each(Disabling.disable);
26309                        memUrlPickerButton.getOpt(comp).each(Disabling.disable);
26310                    },
26311                    onEnabled: (comp) => {
26312                        FormField.getField(comp).each(Disabling.enable);
26313                        memUrlPickerButton.getOpt(comp).each(Disabling.enable);
26314                    }
26315                }),
26316                toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context)),
26317                config('url-input-events', [
26318                    run$1(browseUrlEvent, openUrlPicker)
26319                ])
26320            ])
26321        });
26322    };
26323
26324    const renderAlertBanner = (spec, providersBackstage) => {
26325        const icon = get(spec.icon, providersBackstage.icons);
26326        // For using the alert banner inside a dialog
26327        return Container.sketch({
26328            dom: {
26329                tag: 'div',
26330                attributes: {
26331                    role: 'alert'
26332                },
26333                classes: ['tox-notification', 'tox-notification--in', `tox-notification--${spec.level}`]
26334            },
26335            components: [
26336                {
26337                    dom: {
26338                        tag: 'div',
26339                        classes: ['tox-notification__icon'],
26340                        innerHtml: !spec.url ? icon : undefined
26341                    },
26342                    components: spec.url ? [
26343                        Button.sketch({
26344                            dom: {
26345                                tag: 'button',
26346                                classes: ['tox-button', 'tox-button--naked', 'tox-button--icon'],
26347                                innerHtml: icon,
26348                                attributes: {
26349                                    title: providersBackstage.translate(spec.iconTooltip)
26350                                }
26351                            },
26352                            // TODO: aria label this button!
26353                            action: (comp) => emitWith(comp, formActionEvent, { name: 'alert-banner', value: spec.url }),
26354                            buttonBehaviours: derive$1([
26355                                addFocusableBehaviour()
26356                            ])
26357                        })
26358                    ] : undefined
26359                },
26360                {
26361                    dom: {
26362                        tag: 'div',
26363                        classes: ['tox-notification__body'],
26364                        // TODO: AP-247: Escape this text so that it can't contain script tags
26365                        innerHtml: providersBackstage.translate(spec.text)
26366                    }
26367                }
26368            ]
26369        });
26370    };
26371
26372    const renderCheckbox = (spec, providerBackstage, initialData) => {
26373        const toggleCheckboxHandler = (comp) => {
26374            comp.element.dom.click();
26375            return Optional.some(true);
26376        };
26377        const pField = FormField.parts.field({
26378            factory: { sketch: identity },
26379            dom: {
26380                tag: 'input',
26381                classes: ['tox-checkbox__input'],
26382                attributes: {
26383                    type: 'checkbox'
26384                }
26385            },
26386            behaviours: derive$1([
26387                ComposingConfigs.self(),
26388                Disabling.config({
26389                    disabled: () => !spec.enabled || providerBackstage.checkUiComponentContext(spec.c
26389ontext).shouldDisable,
26390                    onDisabled: (component) => {
26391                        parentElement(component.element).each((element) => add$2(element, 'tox-checkbox--disabled'));
26392                    },
26393                    onEnabled: (component) => {
26394                        parentElement(component.element).each((element) => remove$3(element, 'tox-checkbox--disabled'));
26395                    }
26396                }),
26397                Tabstopping.config({}),
26398                Focusing.config({}),
26399                withElement(initialData, get$9, set$5),
26400                Keying.config({
26401                    mode: 'special',
26402                    onEnter: toggleCheckboxHandler,
26403                    onSpace: toggleCheckboxHandler,
26404                    stopSpaceKeyup: true
26405                }),
26406                config('checkbox-events', [
26407                    run$1(change(), (component, _) => {
26408                        emitWith(component, formChangeEvent, { name: spec.name });
26409                    })
26410                ])
26411            ])
26412        });
26413        const pLabel = FormField.parts.label({
26414            dom: {
26415                tag: 'span',
26416                classes: ['tox-checkbox__label']
26417            },
26418            components: [
26419                text$2(providerBackstage.translate(spec.label))
26420            ],
26421            behaviours: derive$1([
26422                Unselecting.config({})
26423            ])
26424        });
26425        const makeIcon = (className) => {
26426            const iconName = className === 'checked' ? 'selected' : 'unselected';
26427            return render$4(iconName, { tag: 'span', classes: ['tox-icon', 'tox-checkbox-icon__' + className] }, providerBackstage.icons);
26428        };
26429        const memIcons = record({
26430            dom: {
26431                tag: 'div',
26432                classes: ['tox-checkbox__icons']
26433            },
26434            components: [
26435                makeIcon('checked'),
26436                makeIcon('unchecked')
26437            ]
26438        });
26439        return FormField.sketch({
26440            dom: {
26441                tag: 'label',
26442                classes: ['tox-checkbox']
26443            },
26444            components: [
26445                pField,
26446                memIcons.asSpec(),
26447                pLabel
26448            ],
26449            fieldBehaviours: derive$1([
26450                Disabling.config({
26451                    disabled: () => !spec.enabled || providerBackstage.checkUiComponentContext(spec.context).shouldDisable,
26452                }),
26453                toggleOnReceive(() => providerBackstage.checkUiComponentContext(spec.context))
26454            ])
26455        });
26456    };
26457
26458    const renderHtmlPanel = (spec, providersBackstage) => {
26459        const classes = ['tox-form__group', ...(spec.stretched ? ['tox-form__group--stretched'] : [])];
26460        const init = config('htmlpanel', [
26461            runOnAttached((comp) => {
26462                spec.onInit(comp.element.dom);
26463            })
26464        ]);
26465        if (spec.presets === 'presentation') {
26466            return Container.sketch({
26467                dom: {
26468                    tag: 'div',
26469                    classes,
26470                    innerHtml: spec.html
26471                },
26472                containerBehaviours: derive$1([
26473                    Tooltipping.config({
26474                        ...providersBackstage.tooltips.getConfig({
26475                            tooltipText: '',
26476                            onShow: (comp) => {
26477                                descendant(comp.element, '[data-mce-tooltip]:hover').orThunk(() => search(comp.element))
26478                                    .each((current) => {
26479                                    getOpt(current, 'data-mce-tooltip').each((text) => {
26480                                        Tooltipping.setComponents(comp, providersBackstage.tooltips.getComponents({ tooltipText: text }));
26481                                    });
26482                                });
26483                            },
26484                        }),
26485                        mode: 'children-normal',
26486                        anchor: (comp) => ({
26487                            type: 'node',
26488                            node: descendant(comp.element, '[data-mce-tooltip]:hover')
26489                                .orThunk(() => search(comp.element).filter((current) => getOpt(current, 'data-mce-tooltip').isSome())),
26490                            root: comp.element,
26491                            layouts: {
26492                                onLtr: constant$1([south$2, north$2, southeast$2, northeast$2, southwest$2, northwest$2]),
26493                                onRtl: constant$1([south$2, north$2, southeast$2, northeast$2, southwest$2, northwest$2])
26494                            },
26495                            bubble: nu$6(0, -2, {}),
26496                        })
26497                    }),
26498                    init
26499                ])
26500            });
26501        }
26502        else {
26503            return Container.sketch({
26504                dom: {
26505                    tag: 'div',
26506                    classes,
26507                    innerHtml: spec.html,
26508                    attributes: {
26509                        role: 'document'
26510                    }
26511                },
26512                containerBehaviours: derive$1([
26513                    Tabstopping.config({}),
26514                    Focusing.config({}),
26515                    init
26516                ])
26517            });
26518        }
26519    };
26520
26521    const make$1 = (render) => {
26522        return (parts, spec, dialogData, backstage, getCompByName) => get$h(spec, 'name').fold(() => render(spec, backstage, Optional.none(), getCompByName), (fieldName) => parts.field(fieldName, render(spec, backstage, get$h(dialogData, fieldName), getCompByName)));
26523    };
26524    const makeIframe = (render) => (parts, spec, dialogData, backstage, getCompByName) => {
26525        const iframeSpec = deepMerge(spec, {
26526            source: 'dynamic'
26527        });
26528        return make$1(render)(parts, iframeSpec, dialogData, backstage, getCompByName);
26529    };
26530    const factories = {
26531        bar: make$1((spec, backstage) => renderBar(spec, backstage.shared)),
26532        collection: make$1((spec, backstage, data) => renderCollection(spec, backstage.shared.providers, data)),
26533        alertbanner: make$1((spec, backstage) => renderAlertBanner(spec, backstage.shared.providers)),
26534        input: make$1((spec, backstage, data) => renderInput(spec, backstage.shared.providers, data)),
26535        textarea: make$1((spec, backstage, data) => renderTextarea(spec, backstage.shared.providers, data)),
26536        label: make$1((spec, backstage, _data, getCompByName) => renderLabel$2(spec, backstage.shared, getCompByName)),
26537        iframe: makeIframe((spec, backstage, data) => renderIFrame(spec, backstage.shared.providers, data)),
26538        button: make$1((spec, backstage) => renderDialogButton(spec, backstage.shared.providers)),
26539        checkbox: make$1((spec, backstage, data) => renderCheckbox(spec, backstage.shared.providers, data)),
26540        colorinput: make$1((spec, backstage, data) => renderColorInput(spec, backstage.shared, backstage.colorinput, data)),
26541        colorpicker: make$1((spec, backstage, data) => renderColorPicker(spec, backstage.shared.providers, data)), // Not sure if this needs name.
26542        dropzone: make$1((spec, backstage, data) => renderDropZone(spec, backstage.shared.providers, data)),
26543        grid: make$1((spec, backstage) => renderGrid(spec, backstage.shared)),
26544        listbox: make$1((spec, backstage, data) =>
26544 renderListBox(spec, backstage, data)),
26545        selectbox: make$1((spec, backstage, data) => renderSelectBox(spec, backstage.shared.providers, data)),
26546        sizeinput: make$1((spec, backstage) => renderSizeInput(spec, backstage.shared.providers)),
26547        slider: make$1((spec, backstage, data) => renderSlider(spec, backstage.shared.providers, data)),
26548        urlinput: make$1((spec, backstage, data) => renderUrlInput(spec, backstage, backstage.urlinput, data)),
26549        customeditor: make$1(renderCustomEditor),
26550        htmlpanel: make$1((spec, backstage) => renderHtmlPanel(spec, backstage.shared.providers)),
26551        imagepreview: make$1((spec, _, data) => renderImagePreview(spec, data)),
26552        table: make$1((spec, backstage) => renderTable(spec, backstage.shared.providers)),
26553        tree: make$1((spec, backstage) => renderTree(spec, backstage)),
26554        panel: make$1((spec, backstage) => renderPanel(spec, backstage))
26555    };
26556    const noFormParts = {
26557        // This is cast as we only actually want an alloy spec and don't need the actual part here
26558        field: (_name, spec) => spec,
26559        record: constant$1([])
26560    };
26561    const interpretInForm = (parts, spec, dialogData, oldBackstage, getCompByName) => {
26562        // Now, we need to update the backstage to use the parts variant.
26563        const newBackstage = deepMerge(oldBackstage, {
26564            // Add the interpreter based on the form parts.
26565            shared: {
26566                interpreter: (childSpec) => interpretParts(parts, childSpec, dialogData, newBackstage, getCompByName)
26567            }
26568        });
26569        return interpretParts(parts, spec, dialogData, newBackstage, getCompByName);
26570    };
26571    const interpretParts = (parts, spec, dialogData, backstage, getCompByName) => get$h(factories, spec.type).fold(() => {
26572        console.error(`Unknown factory type "${spec.type}", defaulting to container: `, spec);
26573        return spec;
26574    }, (factory) => factory(parts, spec, dialogData, backstage, getCompByName));
26575    const interpretWithoutForm = (spec, dialogData, backstage, getCompByName) => interpretParts(noFormParts, spec, dialogData, backstage, getCompByName);
26576
26577    const bubbleAlignments$2 = {
26578        valignCentre: [],
26579        alignCentre: [],
26580        alignLeft: [],
26581        alignRight: [],
26582        right: [],
26583        left: [],
26584        bottom: [],
26585        top: []
26586    };
26587    const getInlineDialogAnchor = (contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor) => {
26588        const bubbleSize = 12;
26589        const overrides = {
26590            maxHeightFunction: expandable$1()
26591        };
26592        const editableAreaAnchor = () => ({
26593            type: 'node',
26594            root: getContentContainer(getRootNode(contentAreaElement())),
26595            node: Optional.from(contentAreaElement()),
26596            bubble: nu$6(bubbleSize, bubbleSize, bubbleAlignments$2),
26597            layouts: {
26598                onRtl: () => [northeast$1],
26599                onLtr: () => [northwest$1]
26600            },
26601            overrides
26602        });
26603        const standardAnchor = () => ({
26604            type: 'hotspot',
26605            hotspot: lazyAnchorbar(),
26606            bubble: nu$6(-bubbleSize, bubbleSize, bubbleAlignments$2),
26607            layouts: {
26608                onRtl: () => [southeast$2, southwest$2, south$2],
26609                onLtr: () => [southwest$2, southeast$2, south$2]
26610            },
26611            overrides
26612        });
26613        return () => lazyUseEditableAreaAnchor() ? editableAreaAnchor() : standardAnchor();
26614    };
26615    const getInlineBottomDialogAnchor = (inline, contentAreaElement, lazyBottomAnchorBar, lazyUseEditableAreaAnchor) => {
26616        const bubbleSize = 12;
26617        const overrides = {
26618            maxHeightFunction: expandable$1()
26619        };
26620        const editableAreaAnchor = () => ({
26621            type: 'node',
26622            root: getContentContainer(getRootNode(contentAreaElement())),
26623            node: Optional.from(contentAreaElement()),
26624            bubble: nu$6(bubbleSize, bubbleSize, bubbleAlignments$2),
26625            layouts: {
26626                onRtl: () => [north$1],
26627                onLtr: () => [north$1]
26628            },
26629            overrides
26630        });
26631        const standardAnchor = () => inline ?
26632            ({
26633                type: 'node',
26634                root: getContentContainer(getRootNode(contentAreaElement())),
26635                node: Optional.from(contentAreaElement()),
26636                bubble: nu$6(0, -getOuter$1(contentAreaElement()), bubbleAlignments$2),
26637                layouts: {
26638                    onRtl: () => [north$2],
26639                    onLtr: () => [north$2]
26640                },
26641                overrides
26642            })
26643            : ({
26644                type: 'hotspot',
26645                hotspot: lazyBottomAnchorBar(),
26646                bubble: nu$6(0, 0, bubbleAlignments$2),
26647                layouts: {
26648                    onRtl: () => [north$2],
26649                    onLtr: () => [north$2]
26650                },
26651                overrides
26652            });
26653        return () => lazyUseEditableAreaAnchor() ? editableAreaAnchor() : standardAnchor();
26654    };
26655    const getBannerAnchor = (contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor) => {
26656        const editableAreaAnchor = () => ({
26657            type: 'node',
26658            root: getContentContainer(getRootNode(contentAreaElement())),
26659            node: Optional.from(contentAreaElement()),
26660            layouts: {
26661                onRtl: () => [north$1],
26662                onLtr: () => [north$1]
26663            }
26664        });
26665        const standardAnchor = () => ({
26666            type: 'hotspot',
vendor: 7,738 bytes, lines 26667-26833
26667            hotspot: lazyAnchorbar(),
26668            layouts: {
26669                onRtl: () => [south$2],
26670                onLtr: () => [south$2]
26671            }
26672        });
26673        return () => lazyUseEditableAreaAnchor() ? editableAreaAnchor() : standardAnchor();
26674    };
26675    const getCursorAnchor = (editor, bodyElement) => () => ({
26676        type: 'selection',
26677        root: bodyElement(),
26678        getSelection: () => {
26679            const rng = editor.selection.getRng();
26680            // Only return a range if there is a selection of more than one cell.
26681            const selectedCells = editor.model.table.getSelectedCells();
26682            if (selectedCells.length > 1) {
26683                const firstCell = selectedCells[0];
26684                const lastCell = selectedCells[selectedCells.length - 1];
26685                const selectionTableCellRange = {
26686                    firstCell: SugarElement.fromDom(firstCell),
26687                    lastCell: SugarElement.fromDom(lastCell)
26688                };
26689                return Optional.some(selectionTableCellRange);
26690            }
26691            return Optional.some(SimSelection.range(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));
26692        }
26693    });
26694    const getNodeAnchor$1 = (bodyElement) => (element) => ({
26695        type: 'node',
26696        root: bodyElement(),
26697        node: element
26698    });
26699    const getAnchors = (editor, lazyAnchorbar, lazyBottomAnchorBar, isToolbarTop) => {
26700        const useFixedToolbarContainer = useFixedContainer(editor);
26701        const bodyElement = () => SugarElement.fromDom(editor.getBody());
26702        const contentAreaElement = () => SugarElement.fromDom(editor.getContentAreaContainer());
26703        // If using fixed_toolbar_container or if the toolbar is positioned at the bottom
26704        // of the editor, some things should anchor to the top of the editable area.
26705        const lazyUseEditableAreaAnchor = () => useFixedToolbarContainer || !isToolbarTop();
26706        return {
26707            inlineDialog: getInlineDialogAnchor(contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor),
26708            inlineBottomDialog: getInlineBottomDialogAnchor(editor.inline, contentAreaElement, lazyBottomAnchorBar, lazyUseEditableAreaAnchor),
26709            banner: getBannerAnchor(contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor),
26710            cursor: getCursorAnchor(editor, bodyElement),
26711            node: getNodeAnchor$1(bodyElement)
26712        };
26713    };
26714
26715    const colorPicker = (editor) => (callback, value) => {
26716        const dialog = colorPickerDialog(editor);
26717        dialog(callback, value);
26718    };
26719    const hasCustomColors = (editor) => () => hasCustomColors$1(editor);
26720    const getColors = (editor) => (id) => getColors$2(editor, id);
26721    const getColorCols = (editor) => (id) => getColorCols$1(editor, id);
26722    const ColorInputBackstage = (editor) => ({
26723        colorPicker: colorPicker(editor),
26724        hasCustomColors: hasCustomColors(editor),
26725        getColors: getColors(editor),
26726        getColorCols: getColorCols(editor)
26727    });
26728
26729    const isDraggableModal = (editor) => () => isDraggableModal$1(editor);
26730    const DialogBackstage = (editor) => ({
26731        isDraggableModal: isDraggableModal(editor)
26732    });
26733
26734    const HeaderBackstage = (editor) => {
26735        const mode = Cell(isToolbarLocationBottom(editor) ? 'bottom' : 'top');
26736        return {
26737            isPositionedAtTop: () => mode.get() === 'top',
26738            getDockingMode: mode.get,
26739            setDockingMode: mode.set
26740        };
26741    };
26742
26743    const isNestedFormat = (format) => hasNonNullableKey(format, 'items');
26744    const isFormatReference = (format) => hasNonNullableKey(format, 'format');
26745    const defaultStyleFormats = [
26746        {
26747            title: 'Headings', items: [
26748                { title: 'Heading 1', format: 'h1' },
26749                { title: 'Heading 2', format: 'h2' },
26750                { title: 'Heading 3', format: 'h3' },
26751                { title: 'Heading 4', format: 'h4' },
26752                { title: 'Heading 5', format: 'h5' },
26753                { title: 'Heading 6', format: 'h6' }
26754            ]
26755        },
26756        {
26757            title: 'Inline', items: [
26758                { title: 'Bold', format: 'bold' },
26759                { title: 'Italic', format: 'italic' },
26760                { title: 'Underline', format: 'underline' },
26761                { title: 'Strikethrough', format: 'strikethrough' },
26762                { title: 'Superscript', format: 'superscript' },
26763                { title: 'Subscript', format: 'subscript' },
26764                { title: 'Code', format: 'code' }
26765            ]
26766        },
26767        {
26768            title: 'Blocks', items: [
26769                { title: 'Paragraph', format: 'p' },
26770                { title: 'Blockquote', format: 'blockquote' },
26771                { title: 'Div', format: 'div' },
26772                { title: 'Pre', format: 'pre' }
26773            ]
26774        },
26775        {
26776            title: 'Align', items: [
26777                { title: 'Left', format: 'alignleft' },
26778                { title: 'Center', format: 'aligncenter' },
26779                { title: 'Right', format: 'alignright' },
26780                { title: 'Justify', format: 'alignjustify' }
26781            ]
26782        }
26783    ];
26784    // Note: Need to cast format below to expected type, as Obj.has uses "K keyof T", which doesn't work with aliases
26785    const isNestedFormats = (format) => has$2(format, 'items');
26786    const isBlockFormat = (format) => has$2(format, 'block');
26787    const isInlineFormat = (format) => has$2(format, 'inline');
26788    const isSelectorFormat = (format) => has$2(format, 'selector');
26789    const mapFormats = (userFormats) => foldl(userFormats, (acc, fmt) => {
26790        if (isNestedFormats(fmt)) {
26791            // Map the child formats
26792            const result = mapFormats(fmt.items);
26793            return {
26794                customFormats: acc.customFormats.concat(result.customFormats),
26795                formats: acc.formats.concat([{ title: fmt.title, items: result.formats }])
26796            };
26797        }
26798        else if (isInlineFormat(fmt) || isBlockFormat(fmt) || isSelectorFormat(fmt)) {
26799            // Convert the format to a reference and add the original to the custom formats to be registered
26800            const formatName = isString(fmt.name) ? fmt.name : fmt.title.toLowerCase();
26801            const formatNameWithPrefix = `custom-${formatName}`;
26802            return {
26803                customFormats: acc.customFormats.concat([{ name: formatNameWithPrefix, format: fmt }]),
26804                formats: acc.formats.concat([{ title: fmt.title, format: formatNameWithPrefix, icon: fmt.icon }])
26805            };
26806        }
26807        else {
26808            return { ...acc, formats: acc.formats.concat(fmt) };
26809        }
26810    }, { customFormats: [], formats: [] });
26811    const registerCustomFormats = (editor, userFormats) => {
26812        const result = mapFormats(userFormats);
26813        const registerFormats = (customFormats) => {
26814            each$1(customFormats, (fmt) => {
26815                // Only register the custom format with the editor, if it's not already registered
26816                if (!editor.formatter.has(fmt.name)) {
26817                    editor.formatter.register(fmt.name, fmt.format);
26818                }
26819            });
26820        };
26821        // The editor may not yet be initialized, so check for that
26822        if (editor.formatter) {
26823            registerFormats(result.customFormats);
26824        }
26825        else {
26826            editor.on('init', () => {
26827                registerFormats(result.customFormats);
26828            });
26829        }
26830        return result.formats;
26831    };
26832    const getStyleFormats = (editor) => getUserStyleFormats(editor).map((userFormats) => {
26833        // Ensure that any custom formats specified by the user are registered with the editor
26834        const registeredUserFormats = registerCustomFormats(editor, userFormats);
26835        // Merge the default formats with the custom formats if required
26836        return shouldMergeStyleFormats(editor) ? defaultStyleFormats.concat(registeredUserFormats) : registeredUserFormats;
26837    }).getOr(defaultStyleFormats);
26838
26839    const isSeparator$1 = (format) => {
26840        const keys$1 = keys(format);
26841        return keys$1.length === 1 && contains$2(keys$1, 'title');
26842    };
26843    const processBasic = (item, isSelectedFor, getPreviewFor) => ({
26844        ...item,
26845        type: 'formatter',
26846        isSelected: isSelectedFor(item.format),
26847        getStylePreview: getPreviewFor(item.format)
26848    });
26849    // TODO: This is adapted from StyleFormats in the mobile theme. Consolidate.
26850    const register$b = (editor, formats, isSelectedFor, getPreviewFor) => {
26851        const enrichSupported = (item) => processBasic(item, isSelectedFor, getPreviewFor);
26852        // Item that triggers a submenu
26853        const enrichMenu = (item) => {
26854            const newItems = doEnrich(item.items);
26855            return {
26856                ...item,
26857                type: 'submenu',
26858                getStyleItems: constant$1(newItems)
26859            };
26860        };
26861        const enrichCustom = (item) => {
26862            const formatName = isString(item.name) ? item.name : generate$6(item.title);
26863            const formatNameWithPrefix = `custom-${formatName}`;
26864            const newItem = {
26865                ...item,
26866                type: 'formatter',
26867                format: formatNameWithPrefix,
26868                isSelected: isSelectedFor(formatNameWithPrefix),
26869                getStylePreview: getPreviewFor(formatNameWithPrefix)
26870            };
26871            editor.formatter.register(formatName, newItem);
26872            return newItem;
26873        };
26874        const doEnrich = (items) => map$2(items, (item) => {
26875            // If it is a submenu, enrich all the subitems.
26876            if (isNestedFormat(item)) {
26877                return enrichMenu(item);
26878            }
26879            else if (isFormatReference(item)) {
26880                return enrichSupported(item);
26881                // NOTE: This branch is added from the original StyleFormats in mobile
26882            }
26883            else if (isSeparator$1(item)) {
26884                return { ...item, type: 'separator' };
26885            }
26886            else {
26887                return enrichCustom(item);
26888            }
26889        });
26890        return doEnrich(formats);
26891    };
26892
26893    const init$1 = (editor) => {
26894        const isSelectedFor = (format) => () => editor.formatter.match(format);
26895        const getPreviewFor = (format) => () => {
26896            const fmt = editor.formatter.get(format);
26897            return fmt !== undefined ? Optional.some({
26898                tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',
26899                styles: editor.dom.parseStyle(editor.formatter.getCssText(format))
26900            }) : Optional.none();
26901        };
26902        const settingsFormats = Cell([]);
26903        const eventsFormats = Cell([]);
26904        const replaceSettings = Cell(false);
26905        editor.on('PreInit', (_e) => {
26906            const formats = getStyleFormats(editor);
26907            const enriched = register$b(editor, formats, isSelectedFor, getPreviewFor);
26908            settingsFormats.set(enriched);
26909        });
26910        editor.on('addStyleModifications', (e) => {
26911            // Is there going to be an order issue here?
26912            const modifications = register$b(editor, e.items, isSelectedFor, getPreviewFor);
26913            eventsFormats.set(modifications);
26914            replaceSettings.set(e.replace);
26915        });
26916        const getData = () => {
26917            const fromSettings = replaceSettings.get() ? [] : settingsFormats.get();
26918            const fromEvents = eventsFormats.get();
26919            return fromSettings.concat(fromEvents);
26920        };
26921        return {
26922            getData
26923        };
26924    };
26925
26926    const TooltipsBackstage = (getSink) => {
26927        const tooltipDelay = 300;
26928        const intervalDelay = tooltipDelay * 0.2; // Arbitrary value
26929        let numActiveTooltips = 0;
26930        const alreadyShowingTooltips = () => numActiveTooltips > 0;
26931        const getComponents = (spec) => {
26932            return [
26933                {
26934                    dom: {
26935                        tag: 'div',
26936                        classes: ['tox-tooltip__body']
26937                    },
26938                    components: [
26939                        text$2(spec.tooltipText)
26940                    ]
26941                }
26942            ];
26943        };
26944        const getConfig = (spec) => {
26945            return {
26946                delayForShow: () => alreadyShowingTooltips() ? intervalDelay : tooltipDelay,
26947                delayForHide: constant$1(tooltipDelay),
26948                exclusive: true,
26949                lazySink: getSink,
26950                tooltipDom: {
26951                    tag: 'div',
26952                    classes: ['tox-tooltip', 'tox-tooltip--up']
26953                },
26954                tooltipComponents: getComponents(spec),
26955                onShow: (comp, tooltip) => {
26956                    numActiveTooltips++;
26957                    if (spec.onShow) {
26958                        spec.onShow(comp, tooltip);
26959                    }
26960                },
26961                onHide: (comp, tooltip) => {
26962                    numActiveTooltips--;
26963                    if (spec.onHide) {
26964                        spec.onHide(comp, tooltip);
26965                    }
26966                },
26967                onSetup: spec.onSetup,
26968            };
26969        };
26970        return {
26971            getConfig,
26972            getComponents
26973        };
26974    };
26975
26976    const isElement = (node) => isNonNullable(node) && node.nodeType === 1;
26977    const trim = global$2.trim;
26978    const hasContentEditableState = (value) => {
26979        return (node) => {
26980            if (isElement(node)) {
26981                if (node.contentEditable === value) {
26982                    return true;
26983                }
26984                if (node.getAttribute('data-mce-contenteditable') === value) {
26985                    return true;
26986                }
26987            }
26988            return false;
26989        };
26990    };
26991    const isContentEditableTrue = hasContentEditableState('true');
26992    const isContentEditableFalse = hasContentEditableState('false');
26993    const create = (type, title, url, level, attach) => ({
26994        type,
26995        title,
26996        url,
26997        level,
26998        attach
26999    });
27000    const isChildOfContentEditableTrue = (node) => {
27001        let tempNode = node;
27002        while ((tempNode = tempNode.parentNode)) {
27003            const value = tempNode.contentEditable;
27004            if (value && value !== 'inherit') {
27005                return isContentEditableTrue(tempNode);
27006            }
27007        }
27008        return false;
27009    };
27010    const select = (selector, root) => {
27011        return map$2(descendants(SugarElement.fromDom(root), selector), (element) => {
27012            return element.dom;
27013        });
27014    };
27015    const getElementText = (elm) => {
27016        return elm.innerText || elm.textContent;
27017    };
27018    const getOrGenerateId = (elm) => {
27019        return elm.id ? elm.id : generate$6('h');
27020    };
27021    const isAnchor = (elm) => {
27022        return elm && elm.nodeName === 'A' && (elm.id || elm.name) !== undefined;
27023    };
27024    const isValidAnchor = (elm) => {
27025        return isAnchor(elm) && isEditable(elm);
27026    };
27027    const isHeader = (elm) => {
27028        return elm && /^(H[1-6])$/.test(elm.nodeName);
27029    };
27030    const isEditable = (elm) => {
27031        return isChildOfContentEditableTrue(elm) && !isContentEditableFalse(elm);
27032    };
27033    const isValidHeader = (elm) => {
27034        return isHeader(elm) && isEditable(elm);
27035    };
27036    const getLevel = (elm) => {
27037        return isHeader(elm) ? parseInt(elm.nodeName.substr(1), 10) : 0;
27038    };
27039    const headerTarget = (elm) => {
27040        const headerId = getOrGenerateId(elm);
27041        const attach = () => {
27042            elm.id = headerId;
27043        };
27044        return create('header', getElementText(elm) ?? '', '#' + headerId, getLevel(elm), attach);
27045    };
27046    const anchorTarget = (elm) => {
27047        const anchorId = elm.id || elm.name;
27048        const anchorText = getElementText(elm);
27049        return create('anchor', anchorText ? anchorText : '#' + anchorId, '#' + anchorId, 0, noop);
27050    };
27051    const getHeaderTargets = (elms) => {
27052        return map$2(filter$2(elms, isValidHeader), headerTarget);
27053    };
27054    const getAnchorTargets = (elms) => {
27055        return map$2(filter$2(elms, isValidAnchor), anchorTarget);
27056    };
27057    const getTargetElements = (elm) => {
27058        const elms = select('h1,h2,h3,h4,h5,h6,a:not([href])', elm);
27059        return elms;
27060    };
27061    const hasTitle = (target) => {
27062        return trim(target.title).length > 0;
27063    };
27064    const find = (elm) => {
27065        const elms = getTargetElements(elm);
27066        return filter$2(getHeaderTargets(elms).concat(getAnchorTargets(elms)), hasTitle);
27067    };
27068    const LinkTargets = {
27069        find
27070    };
27071
27072    const STORAGE_KEY = 'tinymce-url-history';
27073    const HISTORY_LENGTH = 5;
27074    // validation functions
27075    const isHttpUrl = (url) => isString(url) && /^https?/.test(url);
27076    const isArrayOfUrl = (a) => isArray(a) && a.length <= HISTORY_LENGTH && forall(a, isHttpUrl);
27077    const isRecordOfUrlArray = (r) =>
27077 isObject(r) && find$4(r, (value) => !isArrayOfUrl(value)).isNone();
27078    const getAllHistory = () => {
27079        const unparsedHistory = global$5.getItem(STORAGE_KEY);
27080        if (unparsedHistory === null) {
27081            return {};
27082        }
27083        // parse history
27084        let history;
27085        try {
27086            history = JSON.parse(unparsedHistory);
27087        }
27088        catch (e) {
27089            if (e instanceof SyntaxError) {
27090                // eslint-disable-next-line no-console
27091                console.log('Local storage ' + STORAGE_KEY + ' was not valid JSON', e);
27092                return {};
27093            }
27094            throw e;
27095        }
27096        // validate the parsed value
27097        if (!isRecordOfUrlArray(history)) {
27098            // eslint-disable-next-line no-console
27099            console.log('Local storage ' + STORAGE_KEY + ' was not valid format', history);
27100            return {};
27101        }
27102        return history;
27103    };
27104    const setAllHistory = (history) => {
27105        if (!isRecordOfUrlArray(history)) {
27106            throw new Error('Bad format for history:\n' + JSON.stringify(history));
27107        }
27108        global$5.setItem(STORAGE_KEY, JSON.stringify(history));
27109    };
27110    const getHistory = (fileType) => {
27111        const history = getAllHistory();
27112        return get$h(history, fileType).getOr([]);
27113    };
27114    const addToHistory = (url, fileType) => {
27115        if (!isHttpUrl(url)) {
27116            return;
27117        }
27118        const history = getAllHistory();
27119        const items = get$h(history, fileType).getOr([]);
27120        const itemsWithoutUrl = filter$2(items, (item) => item !== url);
27121        history[fileType] = [url].concat(itemsWithoutUrl).slice(0, HISTORY_LENGTH);
27122        setAllHistory(history);
27123    };
27124
27125    const isTruthy = (value) => !!value;
27126    const makeMap = (value) => map$1(global$2.makeMap(value, /[, ]/), isTruthy);
27127    const getPicker = (editor) => Optional.from(getFilePickerCallback(editor));
27128    const getPickerTypes = (editor) => {
27129        const optFileTypes = Optional.from(getFilePickerTypes(editor)).filter(isTruthy).map(makeMap);
27130        return getPicker(editor).fold(never, (_picker) => optFileTypes.fold(always, (types) => keys(types).length > 0 ? types : false));
27131    };
27132    const getPickerSetting = (editor, filetype) => {
27133        const pickerTypes = getPickerTypes(editor);
27134        if (isBoolean(pickerTypes)) {
27135            return pickerTypes ? getPicker(editor) : Optional.none();
27136        }
27137        else {
27138            return pickerTypes[filetype] ? getPicker(editor) : Optional.none();
27139        }
27140    };
27141    const getUrlPicker = (editor, filetype) => getPickerSetting(editor, filetype).map((picker) => (entry) => Future.nu((completer) => {
27142        const handler = (value, meta) => {
27143            if (!isString(value)) {
27144                throw new Error('Expected value to be string');
27145            }
27146            if (meta !== undefined && !isObject(meta)) {
27147                throw new Error('Expected meta to be a object');
27148            }
27149            const r = { value, meta };
27150            completer(r);
27151        };
27152        const meta = {
27153            filetype,
27154            fieldname: entry.fieldname,
27155            ...Optional.from(entry.meta).getOr({})
27156        };
27157        // file_picker_callback(callback, currentValue, metaData)
27158        picker.call(editor, handler, entry.value, meta);
27159    }));
27160    const getTextSetting = (value) => Optional.from(value).filter(isString).getOrUndefined();
27161    const getLinkInformation = (editor) => {
27162        if (!useTypeaheadUrls(editor)) {
27163            return Optional.none();
27164        }
27165        return Optional.some({
27166            targets: LinkTargets.find(editor.getBody()),
27167            anchorTop: getTextSetting(getAnchorTop(editor)),
27168            anchorBottom: getTextSetting(getAnchorBottom(editor))
27169        });
27170    };
27171    const getValidationHandler = (editor) => Optional.from(getFilePickerValidatorHandler(editor));
27172    const UrlInputBackstage = (editor) => ({
27173        getHistory,
27174        addToHistory,
27175        getLinkInformation: () => getLinkInformation(editor),
27176        getValidationHandler: () => getValidationHandler(editor),
27177        getUrlPicker: (filetype) => getUrlPicker(editor, filetype)
27178    });
27179
27180    const init = (lazySinks, editor, lazyAnchorbar, lazyBottomAnchorBar) => {
27181        const contextMenuState = Cell(false);
27182        const toolbar = HeaderBackstage(editor);
27183        const providers = {
27184            icons: () => editor.ui.registry.getAll().icons,
27185            menuItems: () => editor.ui.registry.getAll().menuItems,
27186            translate: global$6.translate,
27187            isDisabled: () => !editor.ui.isEnabled(),
27188            getOption: editor.options.get,
27189            tooltips: TooltipsBackstage(lazySinks.dialog),
27190            checkUiComponentContext: (specContext) => {
27191                if (isDisabled(editor)) {
27192                    return {
27193                        contextType: 'disabled',
27194                        shouldDisable: true
27195                    };
27196                }
27197                const [key, value = ''] = specContext.split(':');
27198                const contexts = editor.ui.registry.getAll().contexts;
27199                const enabledInContext = get$h(contexts, key)
27200                    .fold(
27201                // Fallback to 'mode:design' if key is not found
27202                () => get$h(contexts, 'mode').map((pred) => pred('design')).getOr(false), (pred) => value.charAt(0) === '!' ? !pred(value.slice(1)) : pred(value));
27203                return {
27204                    contextType: key,
27205                    shouldDisable: !enabledInContext
27206                };
27207            }
27208        };
27209        const urlinput = UrlInputBackstage(editor);
27210        const styles = init$1(editor);
27211        const colorinput = ColorInputBackstage(editor);
27212        const dialogSettings = DialogBackstage(editor);
27213        const isContextMenuOpen = () => contextMenuState.get();
27214        const setContextMenuState = (state) => contextMenuState.set(state);
27215        const commonBackstage = {
27216            shared: {
27217                providers,
27218                anchors: getAnchors(editor, lazyAnchorbar, lazyBottomAnchorBar, toolbar.isPositionedAtTop),
27219                header: toolbar,
27220            },
27221            urlinput,
27222            styles,
27223            colorinput,
27224            dialog: dialogSettings,
27225            isContextMenuOpen,
27226            setContextMenuState
27227        };
27228        const getCompByName = (_name) => Optional.none();
27229        const popupBackstage = {
27230            ...commonBackstage,
27231            shared: {
27232                ...commonBackstage.shared,
27233                interpreter: (s) => interpretWithoutForm(s, {}, popupBackstage, getCompByName),
27234                getSink: lazySinks.popup
27235            }
27236        };
27237        const dialogBackstage = {
27238            ...commonBackstage,
27239            shared: {
27240                ...commonBackstage.shared,
27241                interpreter: (s) => interpretWithoutForm(s, {}, dialogBackstage, getCompByName),
27242                getSink: lazySinks.dialog
27243            }
27244        };
27245        return {
27246            popup: popupBackstage,
27247            dialog: dialogBackstage
27248        };
27249    };
27250
27251    const migrationFrom7x = 'https://www.tiny.cloud/docs/tinymce/latest/migration-from-7x/';
27252    const deprecatedFeatures = {
27253        skipFocus: `ToggleToolbarDrawer skipFocus is deprecated see migration guide: ${migrationFrom7x}`,
27254    };
27255    const logFeatureDeprecationWarning = (feature) => {
27256        // eslint-disable-next-line no-console
27257        console.warn(deprecatedFeatures[feature], new Error().stack);
27258    };
27259
27260    const setup$b = (editor, mothership, uiMotherships) => {
27261        const broadcastEvent = (name, evt) => {
27262            each$1([mothership, ...uiMotherships], (m) => {
27263                m.broadcastEvent(name, evt);
27264            });
27265        };
27266        const broadcastOn = (channel, message) => {
27267            each$1([mothership, ...uiMotherships], (m) => {
27268                m.broadcastOn([channel], message);
27269            });
27270        };
27271        const fireDismissPopups = (evt) => broadcastOn(dismissPopups(), { target: evt.target });
27272        const fireCloseTooltips = (event) => {
27273            broadcastOn(closeTooltips(), {
27274                closedTooltip: () => {
27275                    event.preventDefault();
27276                }
27277            });
27278        };
27279        // Document touch events
27280        const doc = getDocument();
27281        const onTouchstart = bind$1(doc, 'touchstart', fireDismissPopups);
27282        const onTouchmove = bind$1(doc, 'touchmove', (evt) => broadcastEvent(documentTouchmove(), evt));
27283        const onTouchend = bind$1(doc, 'touchend', (evt) => broadcastEvent(documentTouchend(), evt));
27284        // Document mouse events
27285        const onMousedown = bind$1(doc, 'mousedown', fireDismissPopups);
27286        const onMouseup = bind$1(doc, 'mouseup', (evt) => {
27287            if (evt.raw.button === 0) {
27288                broadcastOn(mouseReleased(), { target: evt.target });
27289            }
27290        });
27291        // Editor content events
27292        const onContentClick = (raw) => broadcastOn(dismissPopups(), { target: SugarElement.fromDom(raw.target) });
27293        const onContentMouseup = (raw) => {
27294            if (raw.button === 0) {
27295                broadcastOn(mouseReleased(), { target: SugarElement.fromDom(raw.target) });
27296            }
27297        };
27298        const onContentMousedown = () => {
27299            each$1(editor.editorManager.get(), (loopEditor) => {
27300                if (editor !== loopEditor) {
27301                    loopEditor.dispatch('DismissPopups', { relatedTarget: editor });
27302                }
27303            });
27304        };
27305        // Window events
27306        const onWindowScroll = (evt) => broadcastEvent(windowScroll(), fromRawEvent(evt));
27307        const onWindowResize = (evt) => {
27308            broadcastOn(repositionPopups(), {});
27309            broadcastEvent(windowResize(), fromRawEvent(evt));
27310        };
27311        // TINY-9425: At the moment, we are only supporting situations where the scrolling container
27312        // is *inside* the shadow root - which is why we bind to the root node, instead of just the outer
27313        // document. However, if we needed to support scrolling containers that *contained* the shadow root,
27314        // we would need to listen to the outer document (or at the least, the root node of the scrolling div in
27315        // the case of muliple layers of shadow roots).
27316        const dos = getRootNode(SugarElement.fromDom(editor.getElement()));
27317        const onElementScroll = capture(dos, 'scroll', (evt) => {
27318            requestAnimationFrame(() => {
27319                const c = editor.getContainer();
27320                // Because this can fire before the editor is rendered, we need to stop that from happening.
27321                // Some tests can create this situation, and then we get a Node name null or defined error.
27322                if (c !== undefined && c !== null) {
27323                    const optScrollingContext = detectWhenSplitUiMode(editor, mothership.element);
27324                    const scrollers = optScrollingContext.map((sc) => [sc.element, ...sc.others]).getOr([]);
27325                    if (exists(scrollers, (s) => eq(s, evt.target))) {
27326                        editor.dispatch('ElementScroll', { target: evt.target.dom });
27327                        broadcastEvent(externalElementScroll(), evt);
27328                    }
27329                }
27330            });
27331        });
27332        const onEditorResize = () => broadcastOn(repositionPopups(), {});
27333        const onEditorProgress = (evt) => {
27334            if (evt.state) {
27335                broadcastOn(dismissPopups(), { target: SugarElement.fromDom(editor.getContainer()) });
27336            }
27337        };
27338        const onDismissPopups = (event) => {
27339            broadcastOn(dismissPopups(), { target: SugarElement.fromDom(event.relatedTarget.getContainer()) });
27340        };
27341        const onFocusIn = (event) => editor.dispatch('focusin', event);
27342        const onFocusOut = (event) => editor.dispatch('focusout', event);
27343        // Don't start listening to events until the UI has rendered
27344        editor.on('PostRender', () => {
27345            editor.on('click', onContentClick);
27346            editor.on('tap', onContentClick);
27347            editor.on('mouseup', onContentMouseup);
27348            editor.on('mousedown', onContentMousedown);
27349            editor.on('ScrollWindow', onWindowScroll);
27350            editor.on('ResizeWindow', onWindowResize);
27351            editor.on('ResizeEditor', onEditorResize);
27352            editor.on('AfterProgressState', onEditorProgress);
27353            editor.on('DismissPopups', onDismissPopups);
27354            editor.on('CloseActiveTooltips', fireCloseTooltips);
27355            each$1([mothership, ...uiMotherships], (gui) => {
27356                gui.element.dom.addEventListener('focusin', onFocusIn);
27357                gui.element.dom.addEventListener('focusout', onFocusOut);
27358            });
27359        });
27360        editor.on('remove', () => {
27361            // We probably don't need these unbinds, but it helps to have them if we move this code out.
27362            editor.off('click', onContentClick);
27363            editor.off('tap', onContentClick);
27364            editor.off('mouseup', onContentMouseup);
27365            editor.off('mousedown', onContentMousedown);
27366            editor.off('ScrollWindow', onWindowScroll);
27367            editor.off('ResizeWindow', onWindowResize);
27368            editor.off('ResizeEditor', onEditorResize);
27369            editor.off('AfterProgressState', onEditorProgress);
27370            editor.off('DismissPopups', onDismissPopups);
27371            editor.off('CloseActiveTooltips', fireCloseTooltips);
27372            each$1([mothership, ...uiMotherships], (gui) => {
27373                gui.element.dom.removeEventListener('focusin', onFocusIn);
27374                gui.element.dom.removeEventListener('focusout', onFocusOut);
27375            });
27376            onMousedown.unbind();
27377            onTouchstart.unbind();
27378            onTouchmove.unbind();
27379            onTouchend.unbind();
27380            onMouseup.unbind();
27381            onElementScroll.unbind();
27382        });
27383        editor.on('detach', () => {
27384            each$1([mothership, ...uiMotherships], detachSystem);
27385            each$1([mothership, ...uiMotherships], (m) => m.destroy());
27386        });
27387    };
27388
27389    const setup$a = noop;
27390    const isDocked$1 = never;
27391    const getBehaviours$1 = constant$1([]);
27392
27393    var StaticHeader = /*#__PURE__*/Object.freeze({
27394        __proto__: null,
27395        setup: setup$a,
27396        isDocked: isDocked$1,
27397        getBehaviours: getBehaviours$1
27398    });
27399
27400    const toolbarHeightChange = constant$1(generate$6('toolbar-height-change'));
27401
27402    const visibility = {
27403        fadeInClass: 'tox-editor-dock-fadein',
27404        fadeOutClass: 'tox-editor-dock-fadeout',
27405        transitionClass: 'tox-editor-dock-transition'
27406    };
27407    const editorStickyOnClass = 'tox-tinymce--toolbar-sticky-on';
27408    const editorStickyOffClass = 'tox-tinymce--toolbar-sticky-off';
27409    const scrollFromBehindHeader = (e, containerHeader) => {
27410        const doc = owner$4(containerHeader);
27411        const win = defaultView(containerHeader);
27412        const viewHeight = win.dom.innerHeight;
27413        const scrollPos = get$b(doc);
27414        const markerElement = SugarElement.fromDom(e.elm);
27415        const markerPos = absolute$2(markerElement);
27416        const markerHeight = get$d(markerElement);
27417        const markerTop = markerPos.y;
27418        const markerBottom = markerTop + markerHeight;
27419        const editorHeaderPos = absolute$3(containerHeader);
27420        const editorHeaderHeight = get$d(containerHeader);
27421        const editorHeaderTop = editorHeaderPos.top;
27422        const editorHeaderBottom = editorHeaderTop + editorHeaderHeight;
27423        // Check to see if the header is docked to the top/bottom of the page (eg is floating)
27424        const editorHeaderDockedAtTop = Math.abs(editorHeaderTop - scrollPos.top) < 2;
27425        const editorHeaderDockedAtBottom = Math.abs(editorHeaderBottom - (scrollPos.top + viewHeight)) < 2;
27426        // If the element is behind the header at the top of the page, then
27427        // scroll the element down by the header height
27428        if (editorHeaderDockedAtTop && markerTop < editorHeaderBottom) {
27429            to(scrollPos.left, markerTop - editorHeaderHeight, doc);
27430            // If the element is behind the header at the bottom of the page, then
27431            // scroll the element up by the header height
27432        }
27433        else if (editorHeaderDockedAtBottom && markerBottom > editorHeaderTop) {
27434            const y = (markerTop - viewHeight) + markerHeight + editorHeaderHeight;
27435            to(scrollPos.left, y, doc);
27436        }
27437    };
27438    const isDockedMode = (header, mode) => contains$2(Docking.getModes(header), mode);
27439    const updateIframeContentFlow = (header) => {
27440        const getOccupiedHeight = (elm) => getOuter$1(elm) +
27441            (parseInt(get$e(elm, 'margin-top'), 10) || 0) +
27442            (parseInt(get$e(elm, 'margin-bottom'), 10) || 0);
27443        const elm = header.element;
27444        parentElement(elm).each((parentElem) => {
27445            const padding = 'padding-' + Docking.getModes(header)[0];
27446            if (Docking.isDocked(header)) {
27447                const parentWidth = get$c(parentElem);
27448                set$7(elm, 'width', parentWidth + 'px');
27449                set$7(parentElem, padding, getOccupiedHeight(elm) + 'px');
27450            }
27451            else {
27452                remove$6(elm, 'width');
27453                remove$6(parentElem, padding);
27454            }
27455        });
27456    };
27457    const updateSinkVisibility = (sinkElem, visible) => {
27458        if (visible) {
27459            remove$3(sinkElem, visibility.fadeOutClass);
27460            add$1(sinkElem, [visibility.transitionClass, visibility.fadeInClass]);
27461        }
27462        else {
27463            remove$3(sinkElem, visibility.fadeInClass);
27464            add$1(sinkElem, [visibility.fadeOutClass, visibility.transitionClass]);
27465        }
27466    };
27467    const updateEditorClasses = (editor, docked) => {
27468        const editorContainer = SugarElement.fromDom(editor.getContainer());
27469        if (docked) {
27470            add$2(editorContainer, editorStickyOnClass);
27471            remove$3(editorContainer, editorStickyOffClass);
27472        }
27473        else {
27474            add$2(editorContainer, editorStickyOffClass);
27475            remove$3(editorContainer, editorStickyOnClass);
27476        }
27477    };
27478    const restoreFocus = (headerElem, focusedElem) => {
27479        // When the header is hidden, then the element that was focused will be lost
27480        // so we need to restore it if nothing else has already been focused (eg anything other than the body)
27481        const ownerDoc = owner$4(focusedElem);
27482        active$1(ownerDoc).filter((activeElm) => 
27483        // Don't try to refocus the same element
27484        !eq(focusedElem, activeElm)).filter((activeElm) => 
27485        // Only attempt to refocus if the current focus is the body or is in the header element
27486        eq(activeElm, SugarElement.fromDom(ownerDoc.dom.body)) || contains(headerElem, activeElm)).each(() => focus$4(focusedElem));
27487    };
27488    const findFocusedElem = (rootElm, lazySink) => 
27489    // Check to see if an element is focused inside the header or inside the sink
27490    // and if so store the element so we can restore it later
27491    search(rootElm).orThunk(() => lazySink().toOptional().bind((sink) => search(sink.element)));
27492    const setup$9 = (editor, sharedBackstage, lazyHeader) => {
27493        if (!editor.inline) {
27494            // If using bottom toolbar then when the editor resizes we need to reset docking
27495            // otherwise it won't know the original toolbar position has moved
27496            if (!sharedBackstage.header.isPositionedAtTop()) {
27497                editor.on('ResizeEditor', () => {
27498                    lazyHeader().each(Docking.reset);
27499                });
27500            }
27501            // No need to update the content flow in inline mode as the header always floats
27502            editor.on('ResizeWindow ResizeEditor', () => {
27503                lazyHeader().each(updateIframeContentFlow);
27504            });
27505            // Need to reset the docking position on skin loaded as the original position will have
27506            // changed due the skins styles being applied.
27507            // Note: Inline handles it's own skin loading, as it needs to do other initial positioning
27508            editor.on('SkinLoaded', () => {
27509                lazyHeader().each((comp) => {
27510                    Docking.isDocked(comp) ? Docking.reset(comp) : Docking.refresh(comp);
27511                });
27512            });
27513            // Need to reset when we go fullscreen so that if the header is docked,
27514            // then it'll undock and viceversa
27515            editor.on('FullscreenStateChanged', () => {
27516                lazyHeader().each(Docking.reset);
27517            });
27518        }
27519        // If inline or sticky toolbars is enabled, then when scrolling into view we may still be
27520        // behind the editor header so we need to adjust the scroll position to account for that
27521        editor.on('AfterScrollIntoView', (e) => {
27522            lazyHeader().each((header) => {
27523                // We need to make sure the header docking has refreshed, otherwise if a large scroll occurred
27524                // the header may have gone off page and need to be docked before doing calculations
27525                Docking.refresh(header);
27526                // If the header element is still visible, then adjust the scroll position if required
27527                const headerElem = header.element;
27528                if (isVisible(headerElem)) {
27529                    scrollFromBehindHeader(e, headerElem);
27530                }
27531            });
27532        });
27533        // Update the editor classes once initial rendering has completed
27534        editor.on('PostRender', () => {
27535            updateEditorClasses(editor, false);
27536        });
27537    };
27538    const isDocked = (lazyHeader) => lazyHeader().map(Docking.isDocked).getOr(false);
27539    const getIframeBehaviours = () => [
27540        Receiving.config({
27541            channels: {
27542                [toolbarHeightChange()]: {
27543                    onReceive: updateIframeContentFlow
27544                }
27545            }
27546        })
27547    ];
27548    const getBehaviours = (editor, sharedBackstage) => {
27549        const focusedElm = value$2();
27550        const lazySink = sharedBackstage.getSink;
27551        const runOnSinkElement = (f) => {
27552            lazySink().each((sink) => f(sink.element));
27553        };
27554        const onDockingSwitch = (comp) => {
27555            if (!editor.inline) {
27556                updateIframeContentFlow(comp);
27557            }
27558            updateEditorClasses(editor, Docking.isDocked(comp));
27559            // TINY-9223: This will only reposition the popups in the same mothership as the StickyHeader
27560            // and its sink. If we need to reposition the popups in all motherships (in the two sink
27561            // model) then we'll need a reference to all motherships here.
27562            comp.getSystem().broadcastOn([repositionPopups()], {});
27563            lazySink().each((sink) => sink.getSystem().broadcastOn([repositionPopups()], {}));
27564        };
27565        const additionalBehaviours = editor.inline ? [] : getIframeBehaviours();
27566        return [
27567            Focusing.config({}),
27568            Docking.config({
27569                contextual: {
27570                    lazyContext: (comp) => {
27571                        const headerHeight = getOuter$1(comp.element);
27572                        const container = editor.inline ? editor.getContentAreaContainer() : editor.getContainer();
27573                        return Optional.from(container).map((c) => {
27574                            const box = box$1(SugarElement.fromDom(c));
27575                            const optScrollingContext = detectWhenSplitUiMode(editor, comp.element);
27576                            return optScrollingContext.fold(() => {
27577                                // Force the header to hide before it overflows outside the container
27578                                const boxHeight = box.height - headerHeight;
27579                                const topBound = box.y + (isDockedMode(comp, 'top') ? 0 : headerHeight);
27580                                return bounds(box.x, topBound, box.width, boxHeight);
27581                            }, (scrollEnv) => {
27582                                const constrainedBounds = constrain(box, getBoundsFrom(scrollEnv));
27583                                // When the toolbar location is set to the top, y is the top of the container and height is the available container height minus the header height, as the toolbar will be placed at the top of the container
27584                                // This is so that as you scroll the scrollable container/the page, it will dock at the top and when there's insufficient height/space (that's the reason of deducting the headerHeight for the available height), it will be hidden.
27585                                // When the toolbar location is set to the bottom, y is the top of the container plus the header height, as the toolbar will be placed at the bottom of the container, beyond the container, so that's why we need to add the headerHeight
27586                                // When there's insufficient height/space, it will be hidden, and when you scroll past the editor, it will be hidden
27587                                const constrainedBoundsY = isDockedMode(comp, 'top')
27588                                    ? constrainedBounds.y
27589                                    : constrainedBounds.y + headerHeight;
27590                                return bounds(constrainedBounds.x, 
27591                                // ASSUMPTION: The constrainedBounds removes the need for us to set this to 0px
27592                                // for docked mode. Also, docking in a scrolling environment will often be
27593                                // at the scroller top, not the window top
27594                                constrainedBoundsY, constrainedBounds.width, constrainedBounds.height - headerHeight);
27595                            });
27596                        });
27597                    },
27598                    onShow: () => {
27599                        runOnSinkElement((elem) => updateSinkVisibility(elem, true));
27600                    },
27601                    onShown: (comp) => {
27602                        runOnSinkElement((elem) => remove$2(elem, [visibility.transitionClass, visibility.fadeInClass]));
27603                        // Restore focus and reset the stored focused element
27604                        focusedElm.get().each((elem) => {
27605                            restoreFocus(comp.element, elem);
27606                            focusedElm.clear();
27607                        });
27608                    },
27609                    onHide: (comp) => {
27610                        findFocusedElem(comp.element, lazySink).fold(focusedElm.clear, focusedElm.set);
27611                        runOnSinkElement((elem) => updateSinkVisibility(elem, false));
27612                    },
27613                    onHidden: () => {
27614                        runOnSinkElement((elem) => remove$2(elem, [visibility.transitionClass]));
27615                    },
27616                    ...visibility
27617                },
27618                lazyViewport: (comp) => {
27619                    const optScrollingContext = detectWhenSplitUiMode(editor, comp.element);
27620                    return optScrollingContext.fold(() => {
27621                        const boundsWithoutOffset = win();
27622                        const offset = getStickyToolbarOffset(editor);
27623                        const top = boundsWithoutOffset.y + (isDockedMode(comp, 'top') && !isFullscreen(editor) ? offset : 0);
27624                        const height = boundsWithoutOffset.height - (isDockedMode(comp, 'bottom') ? offset : 0);
27625                        // No scrolling context, so just window
27626                        return {
27627                            bounds: bounds(boundsWithoutOffset.x, top, boundsWithoutOffset.width, height),
27628                            optScrollEnv: Optional.none()
27629                        };
27630                    }, (sc) => {
27631                        // TINY-9411: Implement sticky toolbar offsets in scrollable containers
27632                        const combinedBounds = getBoundsFrom(sc);
27633                        return {
27634                            bounds: combinedBounds,
27635                            optScrollEnv: Optional.some({
27636                                currentScrollTop: sc.element.dom.scrollTop,
27637                                scrollElmTop: absolute$3(sc.element).top
27638                            })
27639                        };
27640                    });
27641                },
27642                modes: [sharedBackstage.header.getDockingMode()],
27643                onDocked: onDockingSwitch,
27644                onUndocked: onDockingSwitch
27645            }),
27646            ...additionalBehaviours
27647        ];
27648    };
27649
27650    var StickyHeader = /*#__PURE__*/Object.freeze({
27651        __proto__: null,
27652        setup: setup$9,
27653        isDocked: isDocked,
27654        getBehaviours: getBehaviours
27655    });
27656
27657    const renderHeader = (spec) => {
27658        const editor = spec.editor;
27659        const getBehaviours$2 = spec.sticky ? getBehaviours : getBehaviours$1;
27660        return {
27661            uid: spec.uid,
27662            dom: spec.dom,
27663            components: spec.components,
27664            behaviours: derive$1(getBehaviours$2(editor, spec.sharedBackstage))
27665        };
27666    };
27667
27668    const factory$3 = (detail, spec) => {
27669        const setMenus = (comp, menus) => {
27670            const newMenus = map$2(menus, (m) => {
27671                const buttonSpec = {
27672                    type: 'menubutton',
27673                    text: m.text,
27674                    fetch: (callback) => {
27675                        callback(m.getItems());
27676                    },
27677                    context: 'any'
27678                };
27679                // Convert to an internal bridge spec
27680                const internal = createMenuButton(buttonSpec).mapError((errInfo) => formatError(errInfo)).getOrDie();
27681                return renderMenuButton(internal, "tox-mbtn" /* MenuButtonClasses.Button */, spec.backstage, 
27682                // https://www.w3.org/TR/wai-aria-practices/examples/menubar/menubar-2/menubar-2.html
27683                Optional.some('menuitem'));
27684            });
27685            Replacing.set(comp, newMenus);
27686        };
27687        const apis = {
27688            focus: Keying.focusIn,
27689            setMenus
27690        };
27691        return {
27692            uid: detail.uid,
27693            dom: detail.dom,
27694            components: [],
27695            behaviours: derive$1([
27696                Replacing.config({}),
27697                config('menubar-events', [
27698                    runOnAttached((component) => {
27699                        detail.onSetup(component);
27700                    }),
27701                    run$1(mouseover(), (comp, se) => {
27702                        // TODO: Use constants
27703                        descendant(comp.element, '.' + "tox-mbtn--active" /* MenuButtonClasses.Active */).each((activeButton) => {
27704                            closest$3(se.event.target, '.' + "tox-mbtn" /* MenuButtonClasses.Button */).each((hoveredButton) => {
27705                                if (!eq(activeButton, hoveredButton)) {
27706                                    // Now, find the components, and expand the hovered one, and close the active one
27707                                    comp.getSystem().getByDom(activeButton).each((activeComp) => {
27708                                        comp.getSystem().getByDom(hoveredButton).each((hoveredComp) => {
27709                                            Dropdown.expand(hoveredComp);
27710                                            Dropdown.close(activeComp);
27711                                            Focusing.focus(hoveredComp);
27712                                        });
27713                                    });
27714                                }
27715                            });
27716                        });
27717                    }),
27718                    run$1(focusShifted(), (comp, se) => {
27719                        se.event.prevFocus.bind((prev) => comp.getSystem().getByDom(prev).toOptional()).each((prev) => {
27720                            se.event.newFocus.bind((nu) => comp.getSystem().getByDom(nu).toOptional()).each((nu) => {
27721                                if (Dropdown.isOpen(prev)) {
27722                                    Dropdown.expand(nu);
27723                                    Dropdown.close(prev);
27724                                }
27725                            });
27726                        });
27727                    })
27728                ]),
27729                Keying.config({
27730                    mode: 'flow',
27731                    selector: '.' + "tox-mbtn" /* MenuButtonClasse
27731s.Button */,
27732                    onEscape: (comp) => {
27733                        detail.onEscape(comp);
27734                        return Optional.some(true);
27735                    }
27736                }),
27737                Tabstopping.config({})
27738            ]),
27739            apis,
27740            domModification: {
27741                attributes: {
27742                    role: 'menubar'
27743                }
27744            }
27745        };
27746    };
27747    var SilverMenubar = single({
27748        factory: factory$3,
27749        name: 'silver.Menubar',
27750        configFields: [
27751            required$1('dom'),
27752            required$1('uid'),
27753            required$1('onEscape'),
27754            required$1('backstage'),
27755            defaulted('onSetup', noop)
27756        ],
27757        apis: {
27758            focus: (apis, comp) => {
27759                apis.focus(comp);
27760            },
27761            setMenus: (apis, comp, menus) => {
27762                apis.setMenus(comp, menus);
27763            }
27764        }
27765    });
27766
27767    const promotionMessage = '💝 Get all features';
27768    const promotionLink = 'https://www.tiny.cloud/tinymce-upgrade-to-cloud/?utm_campaign=self_hosted_upgrade_promo&utm_source=tiny&utm_medium=referral';
27769    const renderPromotion = (spec) => {
27770        const components = spec.promotionLink ? [
27771            {
27772                dom: {
27773                    tag: 'a',
27774                    attributes: {
27775                        'href': promotionLink,
27776                        'rel': 'noopener',
27777                        'target': '_blank',
27778                        'aria-hidden': 'true'
27779                    },
27780                    classes: ['tox-promotion-link'],
27781                    innerHtml: promotionMessage
27782                }
27783            }
27784        ] : [];
27785        return {
27786            uid: spec.uid,
27787            dom: spec.dom,
27788            components
27789        };
27790    };
27791
27792    const setup$8 = (editor) => {
27793        const { sidebars } = editor.ui.registry.getAll();
27794        // Setup each registered sidebar
27795        each$1(keys(sidebars), (name) => {
27796            const spec = sidebars[name];
27797            const isActive = () => is$1(Optional.from(editor.queryCommandValue('ToggleSidebar')), name);
27798            editor.ui.registry.addToggleButton(name, {
27799                icon: spec.icon,
27800                tooltip: spec.tooltip,
27801                onAction: (buttonApi) => {
27802                    editor.execCommand('ToggleSidebar', false, name);
27803                    buttonApi.setActive(isActive());
27804                },
27805                onSetup: (buttonApi) => {
27806                    buttonApi.setActive(isActive());
27807                    const handleToggle = () => buttonApi.setActive(isActive());
27808                    editor.on('ToggleSidebar', handleToggle);
27809                    return () => {
27810                        editor.off('ToggleSidebar', handleToggle);
27811                    };
27812                },
27813                context: 'any'
27814            });
27815        });
27816    };
27817    const getApi = (comp) => ({
27818        element: () => comp.element.dom
27819    });
27820    const makePanels = (parts, panelConfigs) => {
27821        const specs = map$2(keys(panelConfigs), (name) => {
27822            const spec = panelConfigs[name];
27823            const bridged = getOrDie(createSidebar(spec));
27824            return {
27825                name,
27826                getApi,
27827                onSetup: bridged.onSetup,
27828                onShow: bridged.onShow,
27829                onHide: bridged.onHide
27830            };
27831        });
27832        return map$2(specs, (spec) => {
27833            const editorOffCell = Cell(noop);
27834            return parts.slot(spec.name, {
27835                dom: {
27836                    tag: 'div',
27837                    classes: ['tox-sidebar__pane']
27838                },
27839                behaviours: SimpleBehaviours.unnamedEvents([
27840                    onControlAttached(spec, editorOffCell),
27841                    onControlDetached(spec, editorOffCell),
27842                    run$1(slotVisibility(), (sidepanel, se) => {
27843                        const data = se.event;
27844                        const optSidePanelSpec = find$5(specs, (config) => config.name === data.name);
27845                        optSidePanelSpec.each((sidePanelSpec) => {
27846                            const handler = data.visible ? sidePanelSpec.onShow : sidePanelSpec.onHide;
27847                            handler(sidePanelSpec.getApi(sidepanel));
27848                        });
27849                    })
27850                ])
27851            });
27852        });
27853    };
27854    const makeSidebar = (panelConfigs) =>
27854 SlotContainer.sketch((parts) => ({
27855        dom: {
27856            tag: 'div',
27857            classes: ['tox-sidebar__pane-container']
27858        },
27859        components: makePanels(parts, panelConfigs),
27860        slotBehaviours: SimpleBehaviours.unnamedEvents([
27861            runOnAttached((slotContainer) => SlotContainer.hideAllSlots(slotContainer))
27862        ])
27863    }));
27864    const setSidebar = (sidebar, panelConfigs, showSidebar) => {
27865        const optSlider = Composing.getCurrent(sidebar);
27866        optSlider.each((slider) => {
27867            Replacing.set(slider, [makeSidebar(panelConfigs)]);
27868            // Show the default sidebar
27869            const configKey = showSidebar?.toLowerCase();
27870            if (isString(configKey) && has$2(panelConfigs, configKey)) {
27871                Composing.getCurrent(slider).each((slotContainer) => {
27872                    SlotContainer.showSlot(slotContainer, configKey);
27873                    Sliding.immediateGrow(slider);
27874                    // TINY-8710: Remove the width as since the skins/styles won't have loaded yet, so it's going to be incorrect
27875                    remove$6(slider.element, 'width');
27876                    updateSidebarRoleOnToggle(sidebar.element, "region" /* SidebarStateRoleAttr.Grown */);
27877                });
27878            }
27879        });
27880    };
27881    const updateSidebarRoleOnToggle = (sidebar, sidebarState) => {
27882        set$9(sidebar, 'role', sidebarState);
27883    };
27884    const toggleSidebar = (sidebar, name) => {
27885        const optSlider = Composing.getCurrent(sidebar);
27886        optSlider.each((slider) => {
27887            const optSlotContainer = Composing.getCurrent(slider);
27888            optSlotContainer.each((slotContainer) => {
27889                if (Sliding.hasGrown(slider)) {
27890                    if (SlotContainer.isShowing(slotContainer, name)) {
27891                        // close the slider and then hide the slot after the animation finishes
27892                        Sliding.shrink(slider);
27893                        updateSidebarRoleOnToggle(sidebar.element, "presentation" /* SidebarStateRoleAttr.Shrunk */);
27894                    }
27895                    else {
27896                        SlotContainer.hideAllSlots(slotContainer);
27897                        SlotContainer.showSlot(slotContainer, name);
27898                        updateSidebarRoleOnToggle(sidebar.element, "region" /* SidebarStateRoleAttr.Grown */);
27899                    }
27900                }
27901                else {
27902                    // Should already be hidden if the animation has finished but if it has not we hide them
27903                    SlotContainer.hideAllSlots(slotContainer);
27904                    SlotContainer.showSlot(slotContainer, name);
27905                    Sliding.grow(slider);
27906                    updateSidebarRoleOnToggle(sidebar.element, "region" /* SidebarStateRoleAttr.Grown */);
27907                }
27908            });
27909        });
27910    };
27911    const whichSidebar = (sidebar) => {
27912        const optSlider = Composing.getCurrent(sidebar);
27913        return optSlider.bind((slider) => {
27914            const sidebarOpen = Sliding.isGrowing(slider) || Sliding.hasGrown(slider);
27915            if (sidebarOpen) {
27916                const optSlotContainer = Composing.getCurrent(slider);
27917                return optSlotContainer.bind((slotContainer) => find$5(SlotContainer.getSlotNames(slotContainer), (name) => SlotContainer.isShowing(slotContainer, name)));
27918            }
27919            else {
27920                return Optional.none();
27921            }
27922        });
27923    };
27924    const fixSize = generate$6('FixSizeEvent');
27925    const autoSize = generate$6('AutoSizeEvent');
27926    const renderSidebar = (spec) => ({
27927        uid: spec.uid,
27928        dom: {
27929            tag: 'div',
27930            classes: ['tox-sidebar'],
27931            attributes: {
27932                role: "presentation" /* SidebarStateRoleAttr.Shrunk */
27933            }
27934        },
27935        components: [
27936            {
27937                dom: {
27938                    tag: 'div',
27939                    classes: ['tox-sidebar__slider']
27940                },
27941                components: [
27942                // this will be replaced on setSidebar
27943                ],
27944                behaviours: derive$1([
27945                    Tabstopping.config({}),
27946                    Focusing.config({}), // TODO use Keying and use focusIn, but need to handle if sidebar contains 
27946nothing
27947                    Sliding.config({
27948                        dimension: {
27949                            property: 'width'
27950                        },
27951                        closedClass: 'tox-sidebar--sliding-closed',
27952                        openClass: 'tox-sidebar--sliding-open',
27953                        shrinkingClass: 'tox-sidebar--sliding-shrinking',
27954                        growingClass: 'tox-sidebar--sliding-growing',
27955                        onShrunk: (slider) => {
27956                            const optSlotContainer = Composing.getCurrent(slider);
27957                            optSlotContainer.each(SlotContainer.hideAllSlots);
27958                            emit(slider, autoSize);
27959                        },
27960                        onGrown: (slider) => {
27961                            emit(slider, autoSize);
27962                        },
27963                        onStartGrow: (slider) => {
27964                            emitWith(slider, fixSize, { width: getRaw(slider.element, 'width').getOr('') });
27965                        },
27966                        onStartShrink: (slider) => {
27967                            emitWith(slider, fixSize, { width: get$c(slider.element) + 'px' });
27968                        }
27969                    }),
27970                    Replacing.config({}),
27971                    Composing.config({
27972                        find: (comp) => {
27973                            const children = Replacing.contents(comp);
27974                            return head(children);
27975                        }
27976                    })
27977                ])
27978            }
27979        ],
27980        behaviours: derive$1([
27981            ComposingConfigs.childAt(0),
27982            config('sidebar-sliding-events', [
27983                run$1(fixSize, (comp, se) => {
27984                    set$7(comp.element, 'width', se.event.width);
27985                }),
27986                run$1(autoSize, (comp, _se) => {
27987                    remove$6(comp.element, 'width');
27988                })
27989            ])
27990        ])
27991    });
27992
27993    const getBusySpec$1 = (providerBackstage) => (_root, _behaviours) => ({
27994        dom: {
27995            tag: 'div',
27996            attributes: {
27997                'aria-label': providerBackstage.translate('Loading...'),
27998                'tabindex': '0'
27999            },
28000            classes: ['tox-throbber__busy-spinner']
28001        },
28002        components: [
28003            {
28004                dom: fromHtml('<div class="tox-spinner"><div></div><div></div><div></div></div>')
28005            }
28006        ],
28007    });
28008    const focusBusyComponent = (throbber) => Composing.getCurrent(throbber).each((comp) => focus$4(comp.element, true));
28009    // When the throbber is enabled, prevent the iframe from being part of the sequential keyboard navigation when Tabbing
28010    // TODO: TINY-7500 Only works for iframe mode at this stage
28011    const toggleEditorTabIndex = (editor, state) => {
28012        const tabIndexAttr = 'tabindex';
28013        const dataTabIndexAttr = `data-mce-${tabIndexAttr}`;
28014        Optional.from(editor.iframeElement)
28015            .map(SugarElement.fromDom)
28016            .each((iframe) => {
28017            if (state) {
28018                getOpt(iframe, tabIndexAttr).each((tabIndex) => set$9(iframe, dataTabIndexAttr, tabIndex));
28019                set$9(iframe, tabIndexAttr, -1);
28020            }
28021            else {
28022                remove$8(iframe, tabIndexAttr);
28023                getOpt(iframe, dataTabIndexAttr).each((tabIndex) => {
28024                    set$9(iframe, tabIndexAttr, tabIndex);
28025                    remove$8(iframe, dataTabIndexAttr);
28026                });
28027            }
28028        });
28029    };
28030    /*
28031    * If the throbber has been toggled on, only focus the throbber if the editor had focus as we don't to steal focus if it is on an input or dialog
28032    * If the throbber has been toggled off, only put focus back on the editor if the throbber had focus.
28033    * The next logical focus transition from the throbber is to put it back on the editor
28034    */
28035    const toggleThrobber = (editor, comp, state, providerBackstage) => {
28036        const element = comp.element;
28037        toggleEditorTabIndex(editor, state);
28038        if (state) {
28039            Blocking.block(comp, getBusySpec$1(providerBackstage));
28040            remove$6(element, 'display');
28041            remove$8(element, 'aria-hidden');
28042            if (editor.hasFocus()) {
28043                focusBusyComponent(comp);
28044            }
28045        }
28046        else {
28047            // Get the focus of the busy component before it is removed from the DOM
28048            const throbberFocus = Composing.getCurrent(comp).exists((busyComp) => hasFocus(busyComp.element));
28049            Blocking.unblock(comp);
28050            set$7(element, 'display', 'none');
28051            set$9(element, 'aria-hidden', 'true');
28052            if (throbberFocus) {
28053                editor.focus();
28054            }
28055        }
28056    };
28057    const renderThrobber = (spec) => ({
28058        uid: spec.uid,
28059        dom: {
28060            tag: 'div',
28061            attributes: {
28062                'aria-hidden': 'true'
28063            },
28064            classes: ['tox-throbber'],
28065            styles: {
28066                display: 'none'
28067            }
28068        },
28069        behaviours: derive$1([
28070            Replacing.config({}),
28071            Blocking.config({
28072                focus: false
28073            }),
28074            Composing.config({
28075                find: (comp) => head(comp.components())
28076            })
28077        ]),
28078        components: []
28079    });
28080    const isFocusEvent = (event) => event.type === 'focusin';
28081    const isPasteBinTarget = (event) => {
28082        if (isFocusEvent(event)) {
28083            const node = event.composed ? head(event.composedPath()) : Optional.from(event.target);
28084            return node
28085                .map(SugarElement.fromDom)
28086                .filter(isElement$1)
28087                .exists((targetElm) => has(targetElm, 'mce-pastebin'));
28088        }
28089        else {
28090            return false;
28091        }
28092    };
28093    const setup$7 = (editor, lazyThrobber, sharedBackstage) => {
28094        const throbberState = Cell(false);
28095        const timer = value$2();
28096        const stealFocus = (e) => {
28097            if (throbberState.get() && !isPasteBinTarget(e)) {
28098                e.preventDefault();
28099                focusBusyComponent(lazyThrobber());
28100                editor.editorManager.setActive(editor);
28101            }
28102        };
28103        // TODO: TINY-7500 Only worrying about iframe mode at this stage since inline mode has a number of other issues
28104        if (!editor.inline) {
28105            editor.on('PreInit', () => {
28106                // Cover focus when the editor is focused natively
28107                editor.dom.bind(editor.getWin(), 'focusin', stealFocus);
28108                // Cover stealing focus when editor.focus() is called
28109                editor.on('BeforeExecCommand', (e) => {
28110                    // If skipFocus is specified as true in the command, don't focus the Throbber
28111                    if (e.command.toLowerCase() === 'mcefocus' && e.value !== true) {
28112                        stealFocus(e);
28113                    }
28114                });
28115            });
28116        }
28117        const toggle = (state) => {
28118            if (state !== throbberState.get()) {
28119                throbberState.set(state);
28120                toggleThrobber(editor, lazyThrobber(), state, sharedBackstage.providers);
28121                fireAfterProgressState(editor, state);
28122            }
28123        };
28124        editor.on('ProgressState', (e) => {
28125            timer.on(clearTimeout);
28126            if (isNumber(e.time)) {
28127                const timerId = global$a.setEditorTimeout(editor, () => toggle(e.state), e.time);
28128                timer.set(timerId);
28129            }
28130            else {
28131                toggle(e.state);
28132                timer.clear();
28133            }
28134        });
28135    };
28136
28137    const renderToolbarGroupCommon = (toolbarGroup) => {
28138        const attributes = toolbarGroup.label.or(toolbarGroup.title).fold(() => ({}), (label) => ({ attributes: { 'aria-label': label } }));
28139        return {
28140            dom: {
28141                tag: 'div',
28142                classes: ['tox-toolbar__group'].concat(toolbarGroup.label.isSome() ? ['tox-toolbar__group_with_label'] : []),
28143                ...attributes
28144            },
28145            components: [
28146                ...(toolbarGroup.label.map((label) => {
28147                    return {
28148                        dom: {
28149                            tag: 'span',
28150                            classes: ['tox-label', 'tox-label--context-toolbar'],
28151                        },
28152                        components: [text$2(label)]
28153                    };
28154                }).toArray()),
28155                ToolbarGroup.parts.items({})
28156            ],
28157            items: toolbarGroup.items,
28158            markers: {
28159                // nav within a group breaks if disabled buttons are first in their group so skip them
28160                itemSelector: '.tox-tbtn:not([disabled]), ' +
28161                    '.tox-toolbar-nav-item:not([disabled]), ' +
28162                    '.tox-number-input:not([disabled])'
28163            },
28164            tgroupBehaviours: derive$1([
28165                Tabstopping.config({}),
28166                Focusing.config({
28167                    ignore: true
28168                })
28169            ])
28170        };
28171    };
28172    const renderToolbarGroup = (toolbarGroup) => ToolbarGroup.sketch(renderToolbarGroupCommon(toolbarGroup));
28173    const getToolbarBehaviours = (toolbarSpec, modeName) => {
28174        const onAttached = runOnAttached((component) => {
28175            const groups = map$2(toolbarSpec.initGroups, renderToolbarGroup);
28176            Toolbar.setGroups(component, groups);
28177        });
28178        return derive$1([
28179            DisablingConfigs.toolbarButton(() => toolbarSpec.providers.checkUiComponentContext('any').shouldDisable),
28180            toggleOnReceive(() => toolbarSpec.providers.checkUiComponentContext('any')),
28181            Keying.config({
28182                // Tabs between groups
28183                mode: modeName,
28184                onEscape: toolbarSpec.onEscape,
28185                visibilitySelector: '.tox-toolbar__overflow',
28186                selector: '.tox-toolbar__group'
28187            }),
28188            config('toolbar-events', [onAttached])
28189        ]);
28190    };
28191    const renderMoreToolbarCommon = (toolbarSpec) => {
28192        const modeName = toolbarSpec.cyclicKeying ? 'cyclic' : 'acyclic';
28193        return {
28194            uid: toolbarSpec.uid,
28195            dom: {
28196                tag: 'div',
28197                classes: ['tox-toolbar-overlord']
28198            },
28199            parts: {
28200                // This already knows it is a toolbar group
28201                'overflow-group': renderToolbarGroupCommon({
28202                    title: Optional.none(),
28203                    label: Optional.none(),
28204                    items: []
28205                }),
28206                'overflow-button': renderIconButtonSpec({
28207                    context: 'any',
28208                    name: 'more',
28209                    icon: Optional.some('more-drawer'),
28210                    enabled: true,
28211                    tooltip: Optional.some('Reveal or hide additional toolbar items'),
28212                    primary: false,
28213                    buttonType: Optional.none(),
28214                    borderless: false
28215                }, Optional.none(), toolbarSpec.providers, [], 'overflow-button')
28216            },
28217            splitToolbarBehaviours: getToolbarBehaviours(toolbarSpec, modeName)
28218        };
28219    };
28220    const renderFloatingMoreToolbar = (toolbarSpec) => {
28221        const baseSpec = renderMoreToolbarCommon(toolbarSpec);
28222        const overflowXOffset = 4;
28223        const primary = SplitFloatingToolbar.parts.primary({
28224            dom: {
28225                tag: 'div',
28226                classes: ['tox-toolbar__primary']
28227            }
28228        });
28229        return SplitFloatingToolbar.sketch({
28230            ...baseSpec,
28231            lazySink: toolbarSpec.getSink,
28232            getOverflowBounds: () => {
28233                // Restrict the left/right bounds to the editor header width, but don't restrict the top/bottom
28234                const headerElem = toolbarSpec.moreDrawerData.lazyHeader().element;
28235                const headerBounds = absolute$2(headerElem);
28236                const docElem = documentElement(headerElem);
28237                const docBounds = absolute$2(docElem);
28238                const height = Math.max(docElem.dom.scrollHeight, docBounds.height);
28239                return bounds(headerBounds.x + overflowXOffset, docBounds.y, headerBounds.width - overflowXOffset * 2, height);
28240            },
28241            parts: {
28242                ...baseSpec.parts,
28243                overflow: {
28244                    dom: {
28245                        tag: 'div',
28246                        classes: ['tox-toolbar__overflow'],
28247                        attributes: toolbarSpec.attributes
28248                    }
28249                }
28250            },
28251            components: [primary],
28252            markers: {
28253                overflowToggledClass: "tox-tbtn--enabled" /* ToolbarButtonClasse
28253s.Ticked */
28254            },
28255            onOpened: (comp) => toolbarSpec.onToggled(comp, true),
28256            onClosed: (comp) => toolbarSpec.onToggled(comp, false)
28257        });
28258    };
28259    const renderSlidingMoreToolbar = (toolbarSpec) => {
28260        const primary = SplitSlidingToolbar.parts.primary({
28261            dom: {
28262                tag: 'div',
28263                classes: ['tox-toolbar__primary']
28264            }
28265        });
28266        const overflow = SplitSlidingToolbar.parts.overflow({
28267            dom: {
28268                tag: 'div',
28269                classes: ['tox-toolbar__overflow']
28270            }
28271        });
28272        const baseSpec = renderMoreToolbarCommon(toolbarSpec);
28273        return SplitSlidingToolbar.sketch({
28274            ...baseSpec,
28275            components: [primary, overflow],
28276            markers: {
28277                openClass: 'tox-toolbar__overflow--open',
28278                closedClass: 'tox-toolbar__overflow--closed',
28279                growingClass: 'tox-toolbar__overflow--growing',
28280                shrinkingClass: 'tox-toolbar__overflow--shrinking',
28281                overflowToggledClass: "tox-tbtn--enabled" /* ToolbarButtonClasses.Ticked */
28282            },
28283            onOpened: (comp) => {
28284                // TINY-9223: This will only broadcast to the same mothership as the toolbar
28285                comp.getSystem().broadcastOn([toolbarHeightChange()], { type: 'opened' });
28286                toolbarSpec.onToggled(comp, true);
28287            },
28288            onClosed: (comp) => {
28289                // TINY-9223: This will only broadcast to the same mothership as the toolbar
28290                comp.getSystem().broadcastOn([toolbarHeightChange()], { type: 'closed' });
28291                toolbarSpec.onToggled(comp, false);
28292            }
28293        });
28294    };
28295    const renderToolbar = (toolbarSpec) => {
28296        const modeName = toolbarSpec.cyclicKeying ? 'cyclic' : 'acyclic';
28297        return Toolbar.sketch({
28298            uid: toolbarSpec.uid,
28299            dom: {
28300                tag: 'div',
28301                classes: ['tox-toolbar'].concat(toolbarSpec.type === ToolbarMode$1.scrolling ? ['tox-toolbar--scrolling'] : [])
28302            },
28303            components: [
28304                Toolbar.parts.groups({})
28305            ],
28306            toolbarBehaviours: getToolbarBehaviours(toolbarSpec, modeName)
28307        });
28308    };
28309
28310    const renderButton = (spec, providers) => {
28311        const isToggleButton = spec.type === 'togglebutton';
28312        const optMemIcon = spec.icon
28313            .map((memIcon) => renderReplaceableIconFromPack(memIcon, providers.icons))
28314            .map(record);
28315        const getAction = () => (comp) => {
28316            const setIcon = (newIcon) => {
28317                optMemIcon.map((memIcon) => memIcon.getOpt(comp).each((displayIcon) => {
28318                    Replacing.set(displayIcon, [
28319                        renderReplaceableIconFromPack(newIcon, providers.icons)
28320                    ]);
28321                }));
28322            };
28323            const setActive = (state) => {
28324                const elm = comp.element;
28325                if (state) {
28326                    add$2(elm, "tox-button--enabled" /* ViewButtonClasses.Ticked */);
28327                    set$9(elm, 'aria-pressed', true);
28328                }
28329                else {
28330                    remove$3(elm, "tox-button--enabled" /* ViewButtonClasses.Ticked */);
28331                    remove$8(elm, 'aria-pressed');
28332                }
28333            };
28334            const isActive = () => has(comp.element, "tox-button--enabled" /* ViewButtonClasses.Ticked */);
28335            const focus = () => focus$4(comp.element);
28336            if (isToggleButton) {
28337                return spec.onAction({ setIcon, setActive, isActive, focus });
28338            }
28339            if (spec.type === 'button') {
28340                return spec.onAction({ setIcon });
28341            }
28342        };
28343        const action = getAction();
28344        const buttonSpec = {
28345            ...spec,
28346            name: isToggleButton ? spec.text.getOr(spec.icon.getOr('')) : spec.text ?? spec.icon.getOr(''),
28347            primary: spec.buttonType === 'primary',
28348            buttonType: Optional.from(spec.buttonType),
28349            tooltip: spec.tooltip,
28350            icon: spec.icon,
28351            enabled: true,
28352            borderless: spec.borderless
28353        };
28354        const buttonTypeClasses = calculateClassesFromButtonType(spec.buttonType ?? 'secondary');
28355        const optTranslatedText = isToggleButton ? spec.text.map(providers.translate) : Optional.some(prov
28355iders.translate(spec.text));
28356        const optTranslatedTextComponed = optTranslatedText.map(text$2);
28357        const ariaLabelAttributes = buttonSpec.tooltip.or(optTranslatedText).map((al) => ({
28358            'aria-label': providers.translate(al),
28359        })).getOr({});
28360        const optIconSpec = optMemIcon.map((memIcon) => memIcon.asSpec());
28361        const components = componentRenderPipeline([optIconSpec, optTranslatedTextComponed]);
28362        const hasIconAndText = spec.icon.isSome() && optTranslatedTextComponed.isSome();
28363        const dom = {
28364            tag: 'button',
28365            classes: buttonTypeClasses
28366                .concat(...spec.icon.isSome() && !hasIconAndText ? ['tox-button--icon'] : [])
28367                .concat(...hasIconAndText ? ['tox-button--icon-and-text'] : [])
28368                .concat(...spec.borderless ? ['tox-button--naked'] : [])
28369                .concat(...spec.type === 'togglebutton' && spec.active ? ["tox-button--enabled" /* ViewButtonClasses.Ticked */] : []),
28370            attributes: ariaLabelAttributes
28371        };
28372        const extraBehaviours = [];
28373        const iconButtonSpec = renderCommonSpec(buttonSpec, Optional.some(action), extraBehaviours, dom, components, spec.tooltip, providers);
28374        return Button.sketch(iconButtonSpec);
28375    };
28376
28377    const renderViewButton = (spec, providers) => renderButton(spec, providers);
28378    const renderButtonsGroup = (spec, providers) => {
28379        return {
28380            dom: {
28381                tag: 'div',
28382                classes: ['tox-view__toolbar__group'],
28383            },
28384            components: map$2(spec.buttons, (button) => renderViewButton(button, providers))
28385        };
28386    };
28387    const deviceDetection = detect$1().deviceType;
28388    const isPhone = deviceDetection.isPhone();
28389    const isTablet = deviceDetection.isTablet();
28390    const renderViewHeader = (spec) => {
28391        let hasGroups = false;
28392        const endButtons = map$2(spec.buttons, (btnspec) => {
28393            if (btnspec.type === 'group') {
28394                hasGroups = true;
28395                return renderButtonsGroup(btnspec, spec.providers);
28396            }
28397            else {
28398                return renderViewButton(btnspec, spec.providers);
28399            }
28400        });
28401        return {
28402            uid: spec.uid,
28403            dom: {
28404                tag: 'div',
28405                classes: [
28406                    !hasGroups ? 'tox-view__header' : 'tox-view__toolbar',
28407                    ...(isPhone || isTablet ? ['tox-view--mobile', 'tox-view--scrolling'] : [])
28408                ]
28409            },
28410            behaviours: derive$1([
28411                Focusing.config({}),
28412                Keying.config({
28413                    mode: 'flow',
28414                    selector: 'button, .tox-button',
28415                    focusInside: FocusInsideModes.OnEnterOrSpaceMode
28416                })
28417            ]),
28418            components: hasGroups ?
28419                endButtons
28420                : [
28421                    Container.sketch({
28422                        dom: {
28423                            tag: 'div',
28424                            classes: ['tox-view__header-start']
28425                        },
28426                        components: []
28427                    }),
28428                    Container.sketch({
28429                        dom: {
28430                            tag: 'div',
28431                            classes: ['tox-view__header-end']
28432                        },
28433                        components: endButtons
28434                    })
28435                ]
28436        };
28437    };
28438    const renderViewPane = (spec) => {
28439        return {
28440            uid: spec.uid,
28441            behaviours: derive$1([
28442                Focusing.config({}),
28443                Tabstopping.config({})
28444            ]),
28445            dom: {
28446                tag: 'div',
28447                classes: ['tox-view__pane']
28448            }
28449        };
28450    };
28451    const factory$2 = (detail, components, _spec, _externals) => {
28452        const apis = {
28453            getPane: (comp) => parts$g.getPart(comp, detail, 'pane'),
28454            getOnShow: (_comp) => detail.viewConfig.onShow,
28455            getOnHide: (_comp) => detail.viewConfig.onHide,
28456        };
28457        return {
28458            uid: detail.uid,
28459            dom: detail.dom,
28460            components,
28461            behaviours: derive$1([
28462                Focusing.config({}),
28463                Keying.config({
28464                    mode: 'cyclic',
28465                    focusInside: FocusInsideModes.OnEnterOrSpaceMode
28466                })
28467            ]),
28468            apis
28469        };
28470    };
28471    var View = composite({
28472        name: 'silver.View',
28473        configFields: [
28474            required$1('viewConfig'),
28475        ],
28476        partFields: [
28477            optional({
28478                factory: {
28479                    sketch: renderViewHeader
28480                },
28481                schema: [
28482                    required$1('buttons'),
vendor: 4,353 bytes, lines 28483-28589
28483                    required$1('providers')
28484                ],
28485                name: 'header'
28486            }),
28487            optional({
28488                factory: {
28489                    sketch: renderViewPane
28490                },
28491                schema: [],
28492                name: 'pane'
28493            })
28494        ],
28495        factory: factory$2,
28496        apis: {
28497            getPane: (apis, comp) => apis.getPane(comp),
28498            getOnShow: (apis, comp) => apis.getOnShow(comp),
28499            getOnHide: (apis, comp) => apis.getOnHide(comp)
28500        }
28501    });
28502
28503    const makeViews = (parts, viewConfigs, providers) => {
28504        return mapToArray(viewConfigs, (config, name) => {
28505            const internalViewConfig = getOrDie(createView(config));
28506            return parts.slot(name, View.sketch({
28507                dom: {
28508                    tag: 'div',
28509                    classes: ['tox-view']
28510                },
28511                viewConfig: internalViewConfig,
28512                components: [
28513                    ...internalViewConfig.buttons.length > 0 ? [
28514                        View.parts.header({
28515                            buttons: internalViewConfig.buttons,
28516                            providers
28517                        })
28518                    ] : [],
28519                    View.parts.pane({})
28520                ]
28521            }));
28522        });
28523    };
28524    const makeSlotContainer = (viewConfigs, providers) => SlotContainer.sketch((parts) => ({
28525        dom: {
28526            tag: 'div',
28527            classes: ['tox-view-wrap__slot-container']
28528        },
28529        components: makeViews(parts, viewConfigs, providers),
28530        slotBehaviours: SimpleBehaviours.unnamedEvents([
28531            runOnAttached((slotContainer) => SlotContainer.hideAllSlots(slotContainer))
28532        ])
28533    }));
28534    const getCurrentName = (slotContainer) => {
28535        return find$5(SlotContainer.getSlotNames(slotContainer), (name) => SlotContainer.isShowing(slotContainer, name));
28536    };
28537    const hideContainer = (comp) => {
28538        const element = comp.element;
28539        set$7(element, 'display', 'none');
28540        set$9(element, 'aria-hidden', 'true');
28541    };
28542    const showContainer = (comp) => {
28543        const element = comp.element;
28544        remove$6(element, 'display');
28545        remove$8(element, 'aria-hidden');
28546    };
28547    const makeViewInstanceApi = (slot) => ({
28548        getContainer: constant$1(slot)
28549    });
28550    const runOnPaneWithInstanceApi = (slotContainer, name, get) => {
28551        SlotContainer.getSlot(slotContainer, name).each((view) => {
28552            View.getPane(view).each((pane) => {
28553                const onCallback = get(view);
28554                onCallback(makeViewInstanceApi(pane.element.dom));
28555            });
28556        });
28557    };
28558    const runOnShow = (slotContainer, name) => runOnPaneWithInstanceApi(slotContainer, name, View.getOnShow);
28559    const runOnHide = (slotContainer, name) => runOnPaneWithInstanceApi(slotContainer, name, View.getOnHide);
28560    const factory$1 = (detail, spec) => {
28561        const setViews = (comp, viewConfigs) => {
28562            Replacing.set(comp, [makeSlotContainer(viewConfigs, spec.backstage.shared.providers)]);
28563        };
28564        const whichView = (comp) => {
28565            return Composing.getCurrent(comp).bind(getCurrentName);
28566        };
28567        const toggleView = (comp, showMainView, hideMainView, name) => {
28568            return Composing.getCurrent(comp).exists((slotContainer) => {
28569                const optCurrentSlotName = getCurrentName(slotContainer);
28570                const isTogglingCurrentView = optCurrentSlotName.exists((current) => name === current);
28571                const exists = SlotContainer.getSlot(slotContainer, name).isSome();
28572                if (exists) {
28573                    SlotContainer.hideAllSlots(slotContainer);
28574                    if (!isTogglingCurrentView) {
28575                        hideMainView();
28576                        showContainer(comp);
28577                        SlotContainer.showSlot(slotContainer, name);
28578                        runOnShow(slotContainer, name);
28579                    }
28580                    else {
28581                        hideContainer(comp);
28582                        showMainView();
28583                    }
28584                    optCurrentSlotName.each((prevName) => runOnHide(slotContainer, prevName));
28585                }
28586                return exists;
28587            });
28588        };
28589        const apis = {
28590            setViews,
28591            whichView,
28592            toggleView
28593        };
28594        return {
28595            uid: detail.uid,
28596            dom: {
28597                tag: 'div',
28598                classes: ['tox-view-wrap'],
28599                attributes: { 'aria-hidden': 'true' },
28600                styles: { display: 'none' }
28601            },
28602            components: [
28603            // this will be replaced on setViews
28604            ],
28605            behaviours: derive$1([
28606                Replacing.config({}),
28607                Composing.config({
28608                    find: (comp) => {
28609                        const children = Replacing.contents(comp);
28610                        return head(children);
28611                    }
28612                })
28613            ]),
28614            apis
28615        };
28616    };
28617    var ViewWrapper = single({
28618        factory: factory$1,
28619        name: 'silver.ViewWrapper',
28620        configFields: [
28621            required$1('backstage')
28622        ],
28623        apis: {
28624            setViews: (apis, comp, views) => apis.setViews(comp, views),
28625            toggleView: (apis, comp, outerContainer, editorCont, name) => apis.toggleView(comp, outerContainer, editorCont, name),
28626            whichView: (apis, comp) => apis.whichView(comp)
28627        }
28628    });
28629
28630    const factory = (detail, components, _spec) => {
28631        let toolbarDrawerOpenState = false;
28632        const toggleStatusbar = (editorContainer) => {
28633            sibling(editorContainer, '.tox-statusbar').each((statusBar) => {
28634                if (get$e(statusBar, 'display') === 'none' && get$g(statusBar, 'aria-hidden') === 'true') {
28635                    remove$6(statusBar, 'display');
28636                    remove$8(statusBar, 'aria-hidden');
28637                }
28638                else {
28639                    set$7(statusBar, 'display', 'none');
28640                    set$9(statusBar, 'aria-hidden', 'true');
28641                }
28642            });
28643        };
28644        const apis = {
28645            getSocket: (comp) => {
28646                return parts$g.getPart(comp, detail, 'socket');
28647            },
28648            setSidebar: (comp, panelConfigs, showSidebar) => {
28649                parts$g.getPart(comp, detail, 'sidebar').each((sidebar) => setSidebar(sidebar, panelConfigs, showSidebar));
28650            },
28651            toggleSidebar: (comp, name) => {
28652                parts$g.getPart(comp, detail, 'sidebar').each((sidebar) => toggleSidebar(sidebar, name));
28653            },
28654            whichSidebar: (comp) => {
28655                return parts$g.getPart(comp, detail, 'sidebar').bind(w
28655hichSidebar).getOrNull();
28656            },
28657            getHeader: (comp) => {
28658                return parts$g.getPart(comp, detail, 'header');
28659            },
28660            getToolbar: (comp) => {
28661                return parts$g.getPart(comp, detail, 'toolbar');
28662            },
28663            setToolbar: (comp, groups) => {
28664                parts$g.getPart(comp, detail, 'toolbar').each((toolbar) => {
28665                    const renderedGroups = map$2(groups, renderToolbarGroup);
28666                    toolbar.getApis().setGroups(toolbar, renderedGroups);
28667                });
28668            },
28669            setToolbars: (comp, toolbars) => {
28670                parts$g.getPart(comp, detail, 'multiple-toolbar').each((mToolbar) => {
28671                    const renderedToolbars = map$2(toolbars, (g) => map$2(g, renderToolbarGroup));
28672                    CustomList.setItems(mToolbar, renderedToolbars);
28673                });
28674            },
28675            refreshToolbar: (comp) => {
28676                const toolbar = parts$g.getPart(comp, detail, 'toolbar');
28677                toolbar.each((toolbar) => toolbar.getApis().refresh(toolbar));
28678            },
28679            toggleToolbarDrawer: (comp) => {
28680                parts$g.getPart(comp, detail, 'toolbar').each((toolbar) => {
28681                    mapFrom(toolbar.getApis().toggle, (toggle) => toggle(toolbar));
28682                });
28683            },
28684            toggleToolbarDrawerWithoutFocusing: (comp) => {
28685                parts$g.getPart(comp, detail, 'toolbar').each((toolbar) => {
28686                    mapFrom(toolbar.getApis().toggleWithoutFocusing, (toggleWithoutFocusing) => toggleWithoutFocusing(toolbar));
28687                });
28688            },
28689            isToolbarDrawerToggled: (comp) => {
28690                // isOpen may not be defined on all toolbars e.g. 'scrolling' and 'wrap'
28691                return parts$g.getPart(comp, detail, 'toolbar')
28692                    .bind((toolbar) => Optional.from(toolbar.getApis().isOpen).map((isOpen) => isOpen(toolbar)))
28693                    .getOr(false);
28694            },
28695            getThrobber: (comp) => {
28696                return parts$g.getPart(comp, detail, 'throbber');
28697            },
28698            focusToolbar: (comp) => {
28699                const optToolbar = parts$g.getPart(comp, detail, 'toolbar').orThunk(() => parts$g.getPart(comp, detail, 'multiple-toolbar'));
28700                optToolbar.each((toolbar) => {
28701                    Keying.focusIn(toolbar);
28702                });
28703            },
28704            setMenubar: (comp, menus) => {
28705                parts$g.getPart(comp, detail, 'menubar').each((menubar) => {
28706                    SilverMenubar.setMenus(menubar, menus);
28707                });
28708            },
28709            focusMenubar: (comp) => {
28710                parts$g.getPart(comp, detail, 'menubar').each((menubar) => {
28711                    SilverMenubar.focus(menubar);
28712                });
28713            },
28714            setViews: (comp, viewConfigs, showView) => {
28715                parts$g.getPart(comp, detail, 'viewWrapper').each((wrapper) => {
28716                    ViewWrapper.setViews(wrapper, viewConfigs);
28717                });
28718                const configKey = showView?.toLowerCase();
28719                if (isString(configKey) && has$2(viewConfigs, configKey)) {
28720                    apis.toggleView(comp, configKey);
28721                }
28722            },
28723            toggleView: (comp, name) => {
28724                return parts$g.getPart(comp, detail, 'viewWrapper').exists((wrapper) => ViewWrapper.toggleView(wrapper, () => apis.showMainView(comp), () => apis.hideMainView(comp), name));
28725            },
28726            whichView: (comp) => {
28727                return parts$g.getPart(comp, detail, 'viewWrapper').bind(ViewWrapper.whichView).getOrNull();
28728            },
28729            hideMainView: (comp) => {
28730                toolbarDrawerOpenState = apis.isToolbarDrawerToggled(comp);
28731                if (toolbarDrawerOpenState) {
28732                    apis.toggleToolbarDrawer(comp);
28733                }
28734                parts$g.getPart(comp, detail, 'editorContainer').each((editorContainer) => {
28735                    const element = editorContainer.element;
28736                    toggleStatusbar(element);
28737                    set$7(element, 'display', 'none');
28738                    set$9(element, 'aria-hidden', 'true');
28739                });
28740            },
28741            showMainView: (comp) => {
28742                if (toolbarDrawerOpenState) {
28743                    apis.toggleToolbarDrawer(comp);
28744                }
28745                parts$g.getPart(comp, detail, 'editorContainer').each((editorContainer) => {
28746                    const element = editorContainer.element;
28747                    toggleStatusbar(element);
28748                    remove$6(element, 'display');
28749                    remove$8(element, 'aria-hidden');
28750                });
28751            }
28752        };
28753        return {
28754            uid: detail.uid,
28755            dom: detail.dom,
28756            components,
28757            apis,
28758            behaviours: detail.behaviours
28759        };
28760    };
28761    const partMenubar = partType$1.optional({
28762        factory: SilverMenubar,
28763        name: 'menubar',
28764        schema: [
28765            required$1('backstage')
28766        ]
28767    });
28768    const toolbarFactory = (spec) => {
28769        if (spec.type === ToolbarMode$1.sliding) {
28770            return renderSlidingMoreToolbar;
28771        }
28772        else if (spec.type === ToolbarMode$1.floating) {
28773            return renderFloatingMoreToolbar;
28774        }
28775        else {
28776            return renderToolbar;
28777        }
28778    };
28779    const partMultipleToolbar = partType$1.optional({
28780        factory: {
28781            sketch: (spec) => CustomList.sketch({
28782                uid: spec.uid,
28783                dom: spec.dom,
28784                listBehaviours: derive$1([
28785                    Keying.config({
28786                        mode: 'acyclic',
28787                        selector: '.tox-toolbar'
28788                    })
28789                ]),
28790                makeItem: () => renderToolbar({
28791                    type: spec.type,
28792                    uid: generate$6('multiple-toolbar-item'),
28793                    cyclicKeying: false,
28794                    initGroups: [],
28795                    providers: spec.providers,
28796                    onEscape: () => {
28797                        spec.onEscape();
28798                        return Optional.some(true);
28799                    }
28800                }),
28801                setupItem: (_mToolbar, tc, data, _index) => {
28802                    Toolbar.setGroups(tc, data);
28803                },
28804                shell: true
28805            })
28806        },
28807        name: 'multiple-toolbar',
28808        schema: [
28809            required$1('dom'),
28810            required$1('onEscape')
28811        ]
28812    });
28813    const partToolbar = partType$1.optional({
28814        factory: {
28815            sketch: (spec) => {
28816                const renderer = toolbarFactory(spec);
28817                const toolbarSpec = {
28818                    type: spec.type,
28819                    uid: spec.uid,
28820                    onEscape: () => {
28821                        spec.onEscape();
28822                        return Optional.some(true);
28823                    },
28824                    onToggled: (_comp, state) => spec.onToolbarToggled(state),
28825                    cyclicKeying: false,
28826                    initGroups: [],
28827                    getSink: spec.getSink,
28828                    providers: spec.providers,
28829                    moreDrawerData: {
28830                        lazyToolbar: spec.lazyToolbar,
28831                        lazyMoreButton: spec.lazyMoreButton,
28832                        lazyHeader: spec.lazyHeader
28833                    },
28834                    attributes: spec.attributes
28835                };
28836                return renderer(toolbarSpec);
28837            }
28838        },
28839        name: 'toolbar',
28840        schema: [
28841            required$1('dom'),
28842            required$1('onEscape'),
28843            required$1('getSink')
28844        ]
28845    });
28846    const partHeader = partType$1.optional({
28847        factory: {
28848            sketch: renderHeader
28849        },
28850        name: 'header',
28851        schema: [
28852            required$1('dom')
28853        ]
28854    });
28855    const partPromotion = partType$1.optional({
28856        factory: {
28857            sketch: renderPromotion
28858        },
28859        name: 'promotion',
28860        schema: [
28861            required$1('dom'),
28862            required$1('promotionLink')
28863        ]
28864    });
28865    const partSocket = partType$1.optional({
28866        // factory: Fun.identity,
28867        name: 'socket',
28868        schema: [
28869            required$1('dom')
28870        ]
28871    });
28872    const partSidebar = partType$1.optional({
28873        factory: {
vendor: 5,230 bytes, lines 28874-29008
28874            sketch: renderSidebar
28875        },
28876        name: 'sidebar',
28877        schema: [
28878            required$1('dom')
28879        ]
28880    });
28881    const partThrobber = partType$1.optional({
28882        factory: {
28883            sketch: renderThrobber
28884        },
28885        name: 'throbber',
28886        schema: [
28887            required$1('dom')
28888        ]
28889    });
28890    const partViewWrapper = partType$1.optional({
28891        factory: ViewWrapper,
28892        name: 'viewWrapper',
28893        schema: [
28894            required$1('backstage')
28895        ]
28896    });
28897    const renderEditorContainer = (spec) => ({
28898        uid: spec.uid,
28899        dom: {
28900            tag: 'div',
28901            classes: ['tox-editor-container']
28902        },
28903        components: spec.components
28904    });
28905    const partEditorContainer = partType$1.optional({
28906        factory: {
28907            sketch: renderEditorContainer
28908        },
28909        name: 'editorContainer',
28910        schema: []
28911    });
28912    var OuterContainer = composite({
28913        name: 'OuterContainer',
28914        factory,
28915        configFields: [
28916            required$1('dom'),
28917            required$1('behaviours')
28918        ],
28919        partFields: [
28920            partHeader,
28921            partMenubar,
28922            partToolbar,
28923            partMultipleToolbar,
28924            partSocket,
28925            partSidebar,
28926            partPromotion,
28927            partThrobber,
28928            partViewWrapper,
28929            partEditorContainer
28930        ],
28931        apis: {
28932            getSocket: (apis, comp) => {
28933                return apis.getSocket(comp);
28934            },
28935            setSidebar: (apis, comp, panelConfigs, showSidebar) => {
28936                apis.setSidebar(comp, panelConfigs, showSidebar);
28937            },
28938            toggleSidebar: (apis, comp, name) => {
28939                apis.toggleSidebar(comp, name);
28940            },
28941            whichSidebar: (apis, comp) => {
28942                return apis.whichSidebar(comp);
28943            },
28944            getHeader: (apis, comp) => {
28945                return apis.getHeader(comp);
28946            },
28947            getToolbar: (apis, comp) => {
28948                return apis.getToolbar(comp);
28949            },
28950            setToolbar: (apis, comp, groups) => {
28951                apis.setToolbar(comp, groups);
28952            },
28953            setToolbars: (apis, comp, toolbars) => {
28954                apis.setToolbars(comp, toolbars);
28955            },
28956            refreshToolbar: (apis, comp) => {
28957                return apis.refreshToolbar(comp);
28958            },
28959            toggleToolbarDrawer: (apis, comp) => {
28960                apis.toggleToolbarDrawer(comp);
28961            },
28962            toggleToolbarDrawerWithoutFocusing: (apis, comp) => {
28963                apis.toggleToolbarDrawerWithoutFocusing(comp);
28964            },
28965            isToolbarDrawerToggled: (apis, comp) => {
28966                return apis.isToolbarDrawerToggled(comp);
28967            },
28968            getThrobber: (apis, comp) => {
28969                return apis.getThrobber(comp);
28970            },
28971            // FIX: Dupe
28972            setMenubar: (apis, comp, menus) => {
28973                apis.setMenubar(comp, menus);
28974            },
28975            focusMenubar: (apis, comp) => {
28976                apis.focusMenubar(comp);
28977            },
28978            focusToolbar: (apis, comp) => {
28979                apis.focusToolbar(comp);
28980            },
28981            setViews: (apis, comp, views, showView) => {
28982                apis.setViews(comp, views, showView);
28983            },
28984            toggleView: (apis, comp, name) => {
28985                return apis.toggleView(comp, name);
28986            },
28987            whichView: (apis, comp) => {
28988                return apis.whichView(comp);
28989            },
28990        }
28991    });
28992
28993    const defaultMenubar = 'file edit view insert format tools table help';
28994    const defaultMenus = {
28995        file: { title: 'File', items: 'newdocument restoredraft | preview | importword exportpdf exportword | export print | deleteallconversations' },
28996        edit: { title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall | searchreplace' },
28997        view: { title: 'View', items: 'code suggestededits revisionhistory | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments' },
28998        insert: { title: 'Insert', items: 'image video link media addcomment pageembed inserttemplate codesample inserttable accordion math | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents footnotes | mergetags | insertdatetime' },
28999        format: { title: 'Format', items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat' },
29000        tools: { title: 'Tools', items: 'tinymceai-chat tinymceai-review tinymceai-quickactions aidialog aishortcuts | spellchecker spellcheckerlanguage | autocorrect capitalization | a11ycheck code typography wordcount addtemplate' },
29001        table: { title: 'Table', items: 'inserttable | cell row column | advtablesort | tableprops deletetable' },
29002        help: { title: 'Help', items: 'help' }
29003    };
29004    const make = (menu, registry, editor) => {
29005        const removedMenuItems = getRemovedMenuItems(editor).split(/[ ,]/);
29006        return {
29007            text: menu.title,
29008            getItems: () =>
29008 bind$3(menu.items, (i) => {
29009                const itemName = i.toLowerCase();
29010                if (itemName.trim().length === 0) {
29011                    return [];
29012                }
29013                else if (exists(removedMenuItems, (removedMenuItem) => removedMenuItem === itemName)) {
29014                    return [];
29015                }
29016                else if (itemName === 'separator' || itemName === '|') {
29017                    return [{
29018                            type: 'separator'
29019                        }];
29020                }
29021                else if (registry.menuItems[itemName]) {
29022                    return [registry.menuItems[itemName]];
29023                }
29024                else {
29025                    return [];
29026                }
29027            })
29028        };
29029    };
29030    const parseItemsString = (items) => {
29031        return items.split(' ');
29032    };
29033    const identifyMenus = (editor, registry) => {
29034        const rawMenuData = { ...defaultMenus, ...registry.menus };
29035        const userDefinedMenus = keys(registry.menus).length > 0;
29036        const menubar = registry.menubar === undefined || registry.menubar === true ? parseItemsString(defaultMenubar) : parseItemsString(registry.menubar === false ? '' : registry.menubar);
29037        const validMenus = filter$2(menubar, (menuName) => {
29038            const isDefaultMenu = has$2(defaultMenus, menuName);
29039            if (userDefinedMenus) {
29040                return isDefaultMenu || get$h(registry.menus, menuName).exists((menu) => has$2(menu, 'items'));
29041            }
29042            else {
29043                return isDefaultMenu;
29044            }
29045        });
29046        const menus = map$2(validMenus, (menuName) => {
29047            const menuData = rawMenuData[menuName];
29048            return make({ title: menuData.title, items: parseItemsString(menuData.items) }, registry, editor);
29049        });
29050        return filter$2(menus, (menu) => {
29051            // Filter out menus that have no items, or only separators
29052            const isNotSeparator = (item) => isString(item) || item.type !== 'separator';
29053            return menu.getItems().length > 0 && exists(menu.getItems(), isNotSeparator);
29054        });
29055    };
29056
29057    const fireSkinLoaded = (editor) => {
29058        const done = () => {
29059            editor._skinLoaded = true;
29060            fireSkinLoaded$1(editor);
29061        };
29062        return () => {
29063            if (editor.initialized) {
29064                done();
29065            }
29066            else {
29067                editor.on('init', done);
29068            }
29069        };
29070    };
29071    const fireSkinLoadError = (editor, err) => () => fireSkinLoadError$1(editor, { message: err });
29072
29073    const getSkinResourceIdentifier = (editor) => {
29074        const skin = getSkin(editor);
29075        // Use falsy check to cover false, undefined/null and empty string
29076        if (!skin) {
29077            return Optional.none();
29078        }
29079        else {
29080            return Optional.from(skin);
29081        }
29082    };
29083    const loadStylesheet = (editor, stylesheetUrl, styleSheetLoader) => {
29084        // Ensure the stylesheet is cleaned up when the editor is destroyed
29085        editor.on('remove', () => styleSheetLoader.unload(stylesheetUrl));
29086        return styleSheetLoader.load(stylesheetUrl);
29087    };
29088    const loadRawCss = (editor, key, css, styleSheetLoader) => {
29089        // Ensure the stylesheet is cleaned up when the editor is destroyed
29090        editor.on('remove', () => styleSheetLoader.unloadRawCss(key));
29091        return styleSheetLoader.loadRawCss(key, css);
29092    };
29093    const skinIdentifierToResourceKey = (identifier, filename) => 'ui/' + identifier + '/' + filename;
29094    const getResourceValue = (resourceKey) => Optional.from(tinymce.Resource.get(resourceKey)).filter(isString);
29095    const determineCSSDecision = (editor, filenameBase, skinUrl = '') => {
29096        const resourceKey = getSkinResourceIdentifier(editor)
29097            .map((identifier) => skinIdentifierToResourceKey(identifier, `${filenameBase}.css`));
29098        const resourceValue = resourceKey.bind(getResourceValue);
29099        return lift2(resourceKey, resourceValue, (key, css) => {
29100            return { _kind: 'load-raw', key, css };
29101        }).getOrThunk(() => {
29102            const suffix = editor.editorManager.suffix;
29103            const skinUiCssUrl = skinUrl + `/${filenameBase}${suffix}.css`;
29104            return { _kind: 'load-stylesheet', url: skinUiCssUrl };
29105        });
29106    };
29107    const loadUiSkins = (editor, skinUrl) => {
29108        const loader = editor.ui.styleSheetLoader;
29109        const decision = determineCSSDecision(editor, 'skin', skinUrl);
29110        switch (decision._kind) {
29111            case 'load-raw':
29112                const { key, css } = decision;
29113                loadRawCss(editor, key, css, loader);
29114                return Promise.resolve();
29115            case 'load-stylesheet':
29116                const { url } = decision;
29117                return loadStylesheet(editor, url, loader);
29118            default:
29119                return Promise.resolve();
29120        }
29121    };
29122    const loadShadowDomUiSkins = (editor, skinUrl) => {
29123        const isInShadowRoot$1 = isInShadowRoot(SugarElement.fromDom(editor.getElement()));
29124        if (!isInShadowRoot$1) {
29125            return Promise.resolve();
29126        }
29127        else {
29128            const loader = global$9.DOM.styleSheetLoader;
29129            const decision = determineCSSDecision(editor, 'skin.shadowdom', skinUrl);
29130            switch (decision._kind) {
29131                case 'load-raw':
29132                    const { key, css } = decision;
29133                    loadRawCss(editor, key, css, loader);
29134                    return Promise.resolve();
29135                case 'load-stylesheet':
29136                    const { url } = decision;
29137                    return loadStylesheet(editor, url, loader);
29138                default:
29139                    return Promise.resolve();
29140            }
29141        }
29142    };
29143    const loadUiContentCSS = (editor, isInline, skinUrl) => {
29144        const filenameBase = isInline ? 'content.inline' : 'content';
29145        const decision = determineCSSDecision(editor, filenameBase, skinUrl);
29146        if (!skinUrl) {
29147            return Promise.resolve();
29148        }
29149        switch (decision._kind) {
29150            case 'load-raw':
29151                const { key } = decision;
29152                editor.contentCSS.push(key);
29153                return Promise.resolve();
29154            case 'load-stylesheet':
29155                const { url } = decision;
29156                editor.contentCSS.push(url);
29157                return Promise.resolve();
29158            default:
29159                return Promise.resolve();
29160        }
29161    };
29162    const loadUrlSkin = async (isInline, editor) => {
29163        const skinUrl = getSkinUrl(editor);
29164        await loadUiContentCSS(editor, isInline, skinUrl);
29165        // In Modern Inline, this is explicitly called in editor.on('focus', ...) as well as in render().
29166        // Seems to work without, but adding a note in case things break later
29167        if (!isSkinDisabled(editor) && isString(skinUrl)) {
29168            return Promise.all([
29169                loadUiSkins(editor, skinUrl),
29170                loadShadowDomUiSkins(editor, skinUrl)
29171            ]).then();
29172        }
29173    };
29174    const loadSkin = (isInline, editor) => {
29175        return loadUrlSkin(isInline, editor).then(fireSkinLoaded(editor), fireSkinLoadError(editor, 'Skin could not be loaded'));
29176    };
29177    const iframe = curry(loadSkin, false);
29178    const inline = curry(loadSkin, true);
29179
29180    const getButtonApi = (component) => ({
29181        isEnabled: () => !Disabling.isDisabled(component),
29182        setEnabled: (state) => Disabling.set(component, !state),
29183        setText: (text) => emitWith(component, updateMenuText, {
29184            text
29185        }),
29186        setIcon: (icon) => emitWith(component, updateMenuIcon, {
29187            icon
29188        })
29189    });
29190    const getToggleApi = (component) => ({
29191        setActive: (state) => {
29192            Toggling.set(component, state);
29193        },
29194        isActive: () => Toggling.isOn(component),
29195        isEnabled: () => !Disabling.isDisabled(component),
29196        setEnabled: (state) => Disabling.set(component, !state),
29197        setText: (text) => emitWith(component, updateMenuText, {
29198            text
29199        }),
29200        setIcon: (icon) => emitWith(component, updateMenuIcon, {
29201            icon
29202        })
29203    });
29204    const getTooltipAttributes = (tooltip, providersBackstage) => tooltip.map((tooltip) => ({
29205        'aria-label': providersBackstage.translate(tooltip),
29206    })).getOr({});
29207    const focusButtonEvent = generate$6('focus-button');
29208    const renderCommonStructure = (optIcon, optText, tooltip, behaviours, providersBackstage, context, btnName) => {
29209        const optMemDisplayText = optText.map((text) => record(renderLabel$1(text, "tox-tbtn" /* ToolbarButtonClasses.Button */, providersBackstage)));
29210        const optMemDisplayIcon = optIcon.map((icon) => record(renderReplaceableIconFromPack(icon, providersBackstage.icons)));
29211        return {
29212            dom: {
29213                tag: 'button',
29214                classes: ["tox-tbtn" /* ToolbarButtonClasses.Button */].concat(optText.isSome() ? ["tox-tbtn--select" /* ToolbarButtonClasses.MatchWidth */] : []),
29215                attributes: {
29216                    ...getTooltipAttributes(tooltip, providersBackstage),
29217                    ...(isNonNullable(btnName) ? { 'data-mce-name': btnName } : {})
29218                }
29219            },
29220            components: componentRenderPipeline([
29221                optMemDisplayIcon.map((mem) => mem.asSpec()),
29222                optMemDisplayText.map((mem) => mem.asSpec()),
29223            ]),
29224            eventOrder: {
29225                [mousedown()]: [
29226                    'focusing',
29227                    'alloy.base.behaviour',
29228                    commonButtonDisplayEvent
29229                ],
29230                [attachedToDom()]: [commonButtonDisplayEvent, 'toolbar-group-button-events'],
29231                [detachedFromDom()]: [commonButtonDisplayEvent, 'toolbar-group-button-events', 'tooltipping']
29232            },
29233            buttonBehaviours: derive$1([
29234                DisablingConfigs.toolbarButton(() => providersBackstage.checkUiComponentContext(context).shouldDisable),
29235                toggleOnReceive(() => providersBackstage.checkUiComponentContext(context)),
29236                config(commonButtonDisplayEvent, [
29237                    runOnAttached((comp, _se) => forceInitialSize(comp)),
29238                    run$1(updateMenuText, (comp, se) => {
29239                        optMemDisplayText.bind((mem) => mem.getOpt(comp)).each((displayText) => {
29240                            Replacing.set(displayText, [text$2(providersBackstage.translate(se.event.text))]);
29241                        });
29242                    }),
29243                    run$1(updateMenuIcon, (comp, se) => {
29244                        optMemDisplayIcon.bind((mem) => mem.getOpt(comp)).each((displayIcon) => {
29245                            Replacing.set(displayIcon, [renderReplaceableIconFromPack(se.event.icon, providersBackstage.icons)]);
29246                        });
29247                    }),
29248                    run$1(mousedown(), (button, se) => {
29249                        se.event.prevent();
29250                        emit(button, focusButtonEvent);
29251                    })
29252                ])
29253            ].concat(behaviours.getOr([])))
29254        };
29255    };
29256    const renderFloatingToolbarButton = (spec, backstage, identifyButtons, attributes, btnName) => {
29257        const sharedBackstage = backstage.shared;
29258        const editorOffCell = Cell(noop);
29259        const specialisation = {
29260            toolbarButtonBehaviours: [],
29261            getApi: getButtonApi,
29262            onSetup: spec.onSetup
29263        };
29264        const behaviours = [
29265            config('toolbar-group-button-events', [
29266                onControlAttached(specialisation, editorOffCell),
29267                onControlDetached(specialisation, editorOffCell)
29268            ]),
29269            ...(spec.tooltip.map((t) => Tooltipping.config(backstage.shared.providers.tooltips.getConfig({
29270                tooltipText: backstage.shared.providers.translate(t),
29271            })))).toArray()
29272        ];
29273        return FloatingToolbarButton.sketch({
29274            lazySink: sharedBackstage.getSink,
29275            fetch: () => Future.nu((resolve) => {
29276                resolve(map$2(identifyButtons(spec.items), renderToolbarGroup));
29277            }),
29278            markers: {
29279                toggledClass: "tox-tbtn--enabled" /* ToolbarButtonClasses.Ticked */
29280            },
29281            parts: {
29282                button: renderCommonStructure(spec.icon, spec.text, spec.tooltip, Optional.some(behaviours), sharedBackstage.providers, spec.context, btnName),
29283                toolbar: {
29284                    dom: {
29285                        tag: 'div',
29286                        classes: ['tox-toolbar__overflow'],
29287                        attributes
29288                    }
29289                }
29290            }
29291        });
29292    };
29293    const renderCommonToolbarButton = (spec, specialisation, providersBackstage, btnName) => {
29294        const editorOffCell = Cell(noop);
29295        const structure = renderCommonStructure(spec.icon, spec.text, spec.tooltip, Optional.none(), providersBackstage, spec.context, btnName);
29296        return Button.sketch({
29297            dom: structure.dom,
29298            components: structure.components,
29299            eventOrder: toolbarButtonEventOrder,
29300            buttonBehaviours: {
29301                ...derive$1([
29302                    config('toolbar-button-events', [
29303                        onToolbarButtonExecute({
29304                            onAction: spec.onAction,
29305                            getApi: specialisation.getApi
29306                        }),
29307                        onControlAttached(specialisation, editorOffCell),
29308                        onControlDetached(specialisation, editorOffCell)
29309                    ]),
29310                    ...(spec.tooltip.map((t) => Tooltipping.config(providersBackstage.tooltips.getConfig({
29311                        tooltipText: providersBackstage.translate(t) + spec.shortcut.map((shortcut) => ` (${convertText(shortcut)})`).getOr(''),
29312                    })))).toArray(),
29313                    // Enable toolbar buttons by default
29314                    DisablingConfigs.toolbarButton(() => !spec.enabled || providersBackstage.checkUiComponentContext(spec.context).shouldDisable),
29315                    toggleOnReceive(() => providersBackstage.checkUiComponentContext(spec.context))
29316                ].concat(specialisation.toolbarButtonBehaviours)),
29317                // Here we add the commonButtonDisplayEvent behaviour from the structure so we can listen
29318                // to updateMenuIcon and updateMenuText events and run the defined callbacks as they are
29319                // defined in the renderCommonStructure function and fix the size of the button onAttached.
29320                [commonButtonDisplayEvent]: structure.buttonBehaviours?.[commonButtonDisplayEvent],
29321            }
29322        });
29323    };
29324    const renderToolbarButton = (spec, providersBackstage, btnName) => renderToolbarButtonWith(spec, providersBackstage, [], btnName);
29325    const renderToolbarButtonWith = (spec, providersBackstage, bonusEvents, btnName) => renderCommonToolbarButton(spec, {
29326        toolbarButtonBehaviours: (bonusEvents.length > 0 ? [
29327            // TODO: May have to pass through eventOrder if events start clashing
29328            config('toolbarButtonWith', bonusEvents)
29329        ] : []),
29330        getApi: getButtonApi,
29331        onSetup: spec.onSetup
29332    }, providersBackstage, btnName);
29333    const renderToolbarToggleButton = (spec, providersBackstage, btnName) => renderToolbarToggleButtonWith(spec, providersBackstage, [], btnName);
29334    const renderToolbarToggleButtonWith = (spec, providersBackstage, bonusEvents, btnName) => renderCommonToolbarButton(spec, {
29335        toolbarButtonBehaviours: [
29336            Replacing.config({}),
29337            Toggling.config({ toggleClass: "tox-tbtn--enabled" /* ToolbarButtonClasse
29337s.Ticked */, aria: { mode: 'pressed' }, toggleOnExecute: false })
29338        ].concat(bonusEvents.length > 0 ? [
29339            // TODO: May have to pass through eventOrder if events start clashing
29340            config('toolbarToggleButtonWith', bonusEvents)
29341        ] : []),
29342        getApi: getToggleApi,
29343        onSetup: spec.onSetup
29344    }, providersBackstage, btnName);
29345    const fetchChoices = (getApi, spec, providersBackstage) => (comp) => Future.nu((callback) => spec.fetch(callback))
29346        .map((items) => Optional.from(createTieredDataFrom(deepMerge(createPartialChoiceMenu(generate$6('menu-value'), items, (value) => {
29347        spec.onItemAction(getApi(comp), value);
29348    }, spec.columns, spec.presets, ItemResponse$1.CLOSE_ON_EXECUTE, spec.select.getOr(never), providersBackstage), {
29349        movement: deriveMenuMovement(spec.columns, spec.presets),
29350        menuBehaviours: SimpleBehaviours.unnamedEvents(spec.columns !== 'auto' ? [] : [
29351            runOnAttached((comp, _se) => {
29352                detectSize(comp, 4, classForPreset(spec.presets)).each(({ numRows, numColumns }) => {
29353                    Keying.setGridSize(comp, numRows, numColumns);
29354                });
29355            })
29356        ])
29357    }))));
29358    const makeSplitButtonApi = (tooltipString, sharedBackstage, spec) => (component) => {
29359        const system = component.getSystem();
29360        const element = component.element;
29361        const getComponents = () => {
29362            const isChevron = has(element, 'tox-split-button__chevron');
29363            const mainOpt = isChevron ?
29364                prevSibling(element).bind((el) => system.getByDom(el).toOptional()) :
29365                Optional.some(component);
29366            const chevronOpt = isChevron ?
29367                Optional.some(component) :
29368                nextSibling(element).bind((el) => system.getByDom(el).toOptional().filter((comp) => has(comp.element, 'tox-split-button__chevron')));
29369            return { mainOpt, chevronOpt };
29370        };
29371        const applyBoth = (f) => {
29372            const { mainOpt, chevronOpt } = getComponents();
29373            mainOpt.each(f);
29374            chevronOpt.each(f);
29375        };
29376        return {
29377            isEnabled: () => {
29378                const { mainOpt } = getComponents();
29379                return mainOpt.exists((c) => !Disabling.isDisabled(c));
29380            },
29381            setEnabled: (state) => applyBoth((c) => Disabling.set(c, !state)),
29382            setText: (text) => {
29383                const { mainOpt } = getComponents();
29384                mainOpt.each((c) => emitWith(c, updateMenuText, { text }));
29385            },
29386            setIcon: (icon) => {
29387                const { mainOpt } = getComponents();
29388                mainOpt.each((c) => emitWith(c, updateMenuIcon, { icon }));
29389            },
29390            setIconFill: (id, value) => applyBoth((c) => {
29391                descendant(c.element, `svg path[class="${id}"], rect[class="${id}"]`).each((underlinePath) => {
29392                    set$9(underlinePath, 'fill', value);
29393                });
29394            }),
29395            isActive: () => {
29396                const { mainOpt } = getComponents();
29397                return mainOpt.exists((c) => Toggling.isOn(c));
29398            },
29399            setActive: (state) => {
29400                const { mainOpt } = getComponents();
29401                mainOpt.each((c) => Toggling.set(c, state));
29402            },
29403            setTooltip: (tooltip) => {
29404                tooltipString.set(tooltip);
29405                const { mainOpt, chevronOpt } = getComponents();
29406                mainOpt.each((c) => set$9(c.element, 'aria-label', sharedBackstage.providers.translate(tooltip)));
29407                // For chevron, use the explicit chevronTooltip if provided, otherwise fall back to default behavior
29408                const chevronTooltipText = spec.chevronTooltip
29409                    .map((chevronTooltip) => sharedBackstage.providers.translate(chevronTooltip))
29410                    .getOr(sharedBackstage.providers.translate(tooltip));
29411                chevronOpt.each((c) => set$9(c.element, 'aria-label', chevronTooltipText));
29412            }
29413        };
29414    };
29415    const renderSplitButton = (spec, sharedBackstage, btnName) => {
29416        const editorOffCell = Cell(noop);
29417        const tooltipString = Cell(spec.tooltip.getOr(''));
29418        const getApi = makeSplitButtonApi(tooltipString, sharedBackstage, spec);
29419        const menuId = generate$6('tox-split-menu');
29420        const expandedCell = Cell(false);
29421        const getAriaAttributes = () => ({
29422            'aria-haspopup': 'menu',
29423            'aria-expanded': String(expandedCell.get()),
29424            'aria-controls': menuId
29425        });
29426        // Helper to get ARIA label for the main button
29427        const getMainButtonAriaLabel = () => {
29428            return spec.tooltip.map((tooltip) => sharedBackstage.providers.translate(tooltip))
29429                .getOr(sharedBackstage.providers.translate('Text color'));
29430        };
29431        // Helper to get ARIA label and tooltip for the chevron/dropdown button
29432        const getChevronTooltip = () => {
29433            return spec.chevronTooltip
29434                .map((tooltip) => sharedBackstage.providers.translate(tooltip))
29435                .getOrThunk(() => {
29436                const mainLabel = getMainButtonAriaLabel();
29437                return sharedBackstage.providers.translate(['{0} menu', mainLabel]);
29438            });
29439        };
29440        const updateAriaExpanded = (expanded, comp) => {
29441            expandedCell.set(expanded);
29442            set$9(comp.element, 'aria-expanded', String(expanded));
29443        };
29444        const arrow = Dropdown.sketch({
29445            dom: {
29446                tag: 'button',
29447                classes: ["tox-tbtn" /* ToolbarButtonClasses.Button */, 'tox-split-button__chevron'],
29448                innerHtml: get('chevron-down', sharedBackstage.providers.icons),
29449                attributes: {
29450                    'aria-label': getChevronTooltip(),
29451                    ...(isNonNullable(btnName) ? { 'data-mce-name': btnName + '-chevron' } : {}),
29452                    ...getAriaAttributes()
29453                }
29454            },
29455            components: [],
29456            toggleClass: "tox-tbtn--enabled" /* ToolbarButtonClasses.Ticked */,
29457            dropdownBehaviours: derive$1([
29458                config('split-dropdown-events', [
29459                    runOnAttached((comp, _se) => forceInitialSize(comp)),
29460                    onControlAttached({ getApi, onSetup: spec.onSetup }, editorOffCell),
29461                    run$1('alloy-dropdown-open', (comp) => updateAriaExpanded(true, comp)),
29462                    run$1('alloy-dropdown-close', (comp) => updateAriaExpanded(false, comp)),
29463                ]),
29464                DisablingConfigs.toolbarButton(() => sharedBackstage.providers.checkUiComponentContext(spec.context).shouldDisable),
29465                toggleOnReceive(() => sharedBackstage.providers.checkUiComponentContext(spec.context)),
29466                Unselecting.config({}),
29467                Tooltipping.config(sharedBackstage.providers.tooltips.getConfig({
29468                    tooltipText: getChevronTooltip(),
29469                    onShow: (comp) => {
29470                        if (tooltipString.get() !== spec.tooltip.getOr('')) {
29471                            const chevronTooltipText = spec.chevronTooltip
29472                                .map((chevronTooltip) => sharedBackstage.providers.translate(chevronTooltip))
29473                                .getOr(`${sharedBackstage.providers.translate(tooltipString.get())} menu`);
29474                            Tooltipping.setComponents(comp, sharedBackstage.providers.tooltips.getComponents({ tooltipText: chevronTooltipText }));
29475                        }
29476                    }
29477                }))
29478            ]),
29479            lazySink: sharedBackstage.getSink,
29480            fetch: fetchChoices(getApi, spec, sharedBackstage.providers),
29481            getHotspot: (comp) => prevSibling(comp.element).bind((el) => comp.getSystem().getByDom(el).toOptional()),
29482            onOpen: (_anchor, _comp, menu) => {
29483                Highlighting.highlightBy(menu, (item) => has(item.element, 'tox-collection__item--active'));
29484                Highlighting.getHighlighted(menu).each(Keying.focusIn);
29485            },
29486            parts: {
29487                menu: {
29488                    ...part(false, spec.columns, spec.presets),
29489                    dom: {
29490                        ...part(false, spec.columns, spec.presets).dom,
29491                        tag: 'div',
29492                        attributes: {
29493                            id: menuId
29494                        }
29495                    }
29496                }
29497            }
29498        });
29499        const structure = renderCommonStructure(spec.icon, spec.text, Optional.none(), Optional.some([
29500            Toggling.config({
29501                toggleClass: "tox-tbtn--enabled" /* ToolbarButtonClasse
29501s.Ticked */,
29502                aria: spec.presets === 'color' ? { mode: 'none' } : { mode: 'pressed' },
29503                toggleOnExecute: false
29504            }),
29505            ...(spec.tooltip.isSome() ? [
29506                Tooltipping.config(sharedBackstage.providers.tooltips.getConfig({
29507                    tooltipText: sharedBackstage.providers.translate(spec.tooltip.getOr('')),
29508                    onShow: (comp) => {
29509                        if (tooltipString.get() !== spec.tooltip.getOr('')) {
29510                            const translated = sharedBackstage.providers.translate(tooltipString.get());
29511                            Tooltipping.setComponents(comp, sharedBackstage.providers.tooltips.getComponents({ tooltipText: translated }));
29512                        }
29513                    }
29514                }))
29515            ] : [])
29516        ]), sharedBackstage.providers, spec.context, btnName);
29517        const mainButton = Button.sketch({
29518            dom: {
29519                ...structure.dom,
29520                classes: [
29521                    "tox-tbtn" /* ToolbarButtonClasses.Button */,
29522                    'tox-split-button__main'
29523                ].concat(spec.text.isSome() ? ["tox-tbtn--select" /* ToolbarButtonClasses.MatchWidth */] : []),
29524                attributes: {
29525                    'aria-label': getMainButtonAriaLabel(),
29526                    ...(isNonNullable(btnName) ? { 'data-mce-name': btnName } : {})
29527                }
29528            },
29529            components: structure.components,
29530            eventOrder: structure.eventOrder,
29531            buttonBehaviours: structure.buttonBehaviours,
29532            action: (button) => {
29533                if (spec.onAction) {
29534                    const api = getApi(button);
29535                    if (api.isEnabled()) {
29536                        spec.onAction(api);
29537                    }
29538                }
29539            }
29540        });
29541        return [mainButton, arrow];
29542    };
29543
29544    const contextFormInputSelector = '.tox-toolbar-slider__input,.tox-toolbar-textfield';
29545    const focusIn = (contextbar) => {
29546        InlineView.getContent(contextbar).each((comp) => {
29547            descendant(comp.element, contextFormInputSelector).fold(() => Keying.focusIn(comp), focus$4);
29548        });
29549    };
29550    // TODO: Is this really the best way to move focus out of the input when it gets disabled #TINY-11527
29551    const focusParent = (comp) => search(comp.element).each((focus) => {
29552        ancestor$1(focus, '[tabindex="-1"]').each((parent) => {
29553            focus$4(parent);
29554        });
29555    });
29556
29557    const forwardSlideEvent = generate$6('forward-slide');
29558    const backSlideEvent = generate$6('backward-slide');
29559    const changeSlideEvent = generate$6('change-slide-event');
29560    const resizingClass = 'tox-pop--resizing';
29561    const renderContextToolbar = (spec) => {
29562        const stack = Cell([]);
29563        const sketch = InlineView.sketch({
29564            dom: {
29565                tag: 'div',
29566                classes: ['tox-pop']
29567            },
29568            fireDismissalEventInstead: {
29569                event: 'doNotDismissYet'
29570            },
29571            onShow: (comp) => {
29572                stack.set([]);
29573                InlineView.getContent(comp).each((c) => {
29574                    remove$6(c.element, 'visibility');
29575                });
29576                remove$3(comp.element, resizingClass);
29577                remove$6(comp.element, 'width');
29578            },
29579            onHide: () => {
29580                stack.set([]);
29581                spec.onHide();
29582            },
29583            inlineBehaviours: derive$1([
29584                config('context-toolbar-events', [
29585                    runOnSource(transitionend(), (comp, se) => {
29586                        if (se.event.raw.propertyName === 'width') {
29587                            remove$3(comp.element, resizingClass);
29588                            remove$6(comp.element, 'width');
29589                        }
29590                    }),
29591                    run$1(changeSlideEvent, (comp, se) => {
29592                        const elem = comp.element;
29593                        // If it was partially through a slide, clear that and measure afresh
29594                        remove$6(elem, 'width');
29595                        const currentWidth = get$c(elem);
29596                        const hadFocus = search(comp.element).isSome();
29597                        // Remove these so that we can property measure the width of the context form content
29598                        remove$6(elem, 'left');
29599                        remove$6(elem, 'right');
29600                        remove$6(elem, 'max-width');
29601                        InlineView.setContent(comp, se.event.contents);
29602                        add$2(elem, resizingClass);
29603                        const newWidth = get$c(elem);
29604                        // Reposition without transition to avoid it from being animated from previous position
29605                        set$7(elem, 'transition', 'none');
29606                        InlineView.reposition(comp);
29607                        remove$6(elem, 'transition');
29608                        set$7(elem, 'width', currentWidth + 'px');
29609                        se.event.focus.fold(() => {
29610                            if (hadFocus) {
29611                                focusIn(comp);
29612                            }
29613                        }, (f) => {
29614                            active$1(getRootNode(comp.element)).fold(() => focus$4(f), (active) => {
29615                                // We need this extra check since if the focus is aleady on the iframe we don't want to call focus on it again since that closes the context toolbar
29616                                if (!eq(active, f)) {
29617                                    spec.focusElement(f);
29618                                }
29619                            });
29620                        });
29621                        setTimeout(() => {
29622                            set$7(comp.element, 'width', newWidth + 'px');
29623                        }, 0);
29624                    }),
29625                    run$1(forwardSlideEvent, (comp, se) => {
29626                        InlineView.getContent(comp).each((oldContents) => {
29627                            stack.set(stack.get().concat([
29628                                {
29629                                    bar: oldContents,
29630                                    focus: active$1(getRootNode(comp.element))
29631                                }
29632                            ]));
29633                        });
29634                        emitWith(comp, changeSlideEvent, {
29635                            contents: se.event.forwardContents,
29636                            focus: Optional.none()
29637                        });
29638                    }),
29639                    run$1(backSlideEvent, (comp, _se) => {
29640                        spec.onBack();
29641                        last$1(stack.get()).each((last) => {
29642                            stack.set(stack.get().slice(0, stack.get().length - 1));
29643                            emitWith(comp, changeSlideEvent, {
29644                                // Because we are using premade, we should have access to the same element
29645                                // to give focus (although it isn't working)
29646                                contents: premade(last.bar),
29647                                focus: last.focus
29648                            });
29649                        });
29650                    })
29651                ]),
29652                Keying.config({
29653                    mode: 'special',
29654                    onEscape: (comp) => last$1(stack.get()).fold(() => 
29655                    // Escape just focuses the content. It no longer closes the toolbar.
29656                    spec.onEscape(), (_) => {
29657                        emit(comp, backSlideEvent);
29658                        return Optional.some(true);
29659                    })
29660                })
29661            ]),
29662            lazySink: () => Result.value(spec.sink)
29663        });
29664        return {
29665            sketch,
29666            inSubtoolbar: () => stack.get().length > 0
29667        };
29668    };
29669
29670    const createNavigateBackButton = (editor, backstage) => {
29671        const bridged = getOrDie(createToolbarButton({
29672            type: 'button',
29673            icon: 'chevron-left',
29674            tooltip: 'Back',
29675            onAction: noop
29676        }));
29677        return renderToolbarButtonWith(bridged, backstage.shared.providers, [
29678            run$1(internalToolbarButtonExecute, (comp) => {
29679                emit(comp, backSlideEvent);
29680            })
29681        ]);
29682    };
29683
29684    const makeTooltipText = (editor, labelWithPlaceholder, value) => isEmpty(value) ? editor.translate(labelWithPlaceholder) : editor.translate([labelWithPlaceholder, editor.translate(value)]);
29685
29686    const generateSelectItems = (backstage, spec) => {
29687        const generateItem = (rawItem, response, invalid, value) => {
29688            const translatedText = backstage.shared.providers.translate(rawItem.title);
29689            if (rawItem.type === 'separator') {
29690                return Optional.some({
29691                    type: 'separator',
29692                    text: translatedText
29693                });
29694            }
29695            else if (rawItem.type === 'submenu') {
29696                const items = bind$3(rawItem.getStyleItems(), (si) => validate(si, response, value));
29697                if (response === 0 /* IrrelevantStyleItemResponse.Hide */ && items.length <= 0) {
29698                    return Optional.none();
29699                }
29700                else {
29701                    return Optional.some({
29702                        type: 'nestedmenuitem',
29703                        text: translatedText,
29704                        enabled: items.length > 0,
29705                        getSubmenuItems: () =>
29705 bind$3(rawItem.getStyleItems(), (si) => validate(si, response, value))
29706                    });
29707                }
29708            }
29709            else {
29710                return Optional.some({
29711                    // ONLY TOGGLEMENUITEMS HANDLE STYLE META.
29712                    // See ToggleMenuItem and ItemStructure for how it's handled.
29713                    // If this type ever changes, we'll need to change that too
29714                    type: 'togglemenuitem',
29715                    text: translatedText,
29716                    icon: rawItem.icon,
29717                    active: rawItem.isSelected(value),
29718                    enabled: !invalid,
29719                    onAction: spec.onAction(rawItem),
29720                    ...rawItem.getStylePreview().fold(() => ({}), (preview) => ({ meta: { style: preview } }))
29721                });
29722            }
29723        };
29724        const validate = (item, response, value) => {
29725            const invalid = item.type === 'formatter' && spec.isInvalid(item);
29726            // If we are making them disappear based on some setting
29727            if (response === 0 /* IrrelevantStyleItemResponse.Hide */) {
29728                return invalid ? [] : generateItem(item, response, false, value).toArray();
29729            }
29730            else {
29731                return generateItem(item, response, invalid, value).toArray();
29732            }
29733        };
29734        const validateItems = (preItems) => {
29735            const value = spec.getCurrentValue();
29736            const response = spec.shouldHide ? 0 /* IrrelevantStyleItemResponse.Hide */ : 1 /* IrrelevantStyleItemResponse.Disable */;
29737            return bind$3(preItems, (item) => validate(item, response, value));
29738        };
29739        const getFetch = (backstage, getStyleItems) => (comp, callback) => {
29740            const preItems = getStyleItems();
29741            const items = validateItems(preItems);
29742            const menu = build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
29743                isHorizontalMenu: false,
29744                search: Optional.none()
29745            });
29746            callback(menu);
29747        };
29748        return {
29749            validateItems,
29750            getFetch
29751        };
29752    };
29753    const createMenuItems = (backstage, spec) => {
29754        const dataset = spec.dataset; // needs to be a var for tsc to understand the ternary
29755        const getStyleItems = dataset.type === 'basic' ?
29756            () => map$2(dataset.data, (d) => processBasic(d, spec.isSelectedFor, spec.getPreviewFor)) :
29757            dataset.getData;
29758        return {
29759            items: generateSelectItems(backstage, spec),
29760            getStyleItems
29761        };
29762    };
29763    const createSelectButton = (editor, backstage, spec, getTooltip, textUpdateEventName, btnName) => {
29764        const { items, getStyleItems } = createMenuItems(backstage, spec);
29765        const tooltipString = Cell(spec.tooltip);
29766        const getApi = (comp) => ({
29767            getComponent: constant$1(comp),
29768            setTooltip: (tooltip) => {
29769                const translatedTooltip = backstage.shared.providers.translate(tooltip);
29770                set$9(comp.element, 'aria-label', translatedTooltip);
29771                tooltipString.set(tooltip);
29772            }
29773        });
29774        // Set the initial text when the component is attached and then update on node changes
29775        const onSetup = (api) => {
29776            const handler = (e) => api.setTooltip(makeTooltipText(editor, getTooltip(e.value), e.value));
29777            editor.on(textUpdateEventName, handler);
29778            return composeUnbinders(onSetupEvent(editor, 'NodeChange', (api) => {
29779                const comp = api.getComponent();
29780                spec.updateText(comp);
29781                Disabling.set(api.getComponent(), (!editor.selection.isEditable() || getStyleItems().length === 0));
29782            })(api), () => editor.off(textUpdateEventName, handler));
29783        };
29784        return renderCommonDropdown({
29785            context: 'mode:design',
29786            text: spec.icon.isSome() ? Optional.none() : spec.text,
29787            icon: spec.icon,
29788            ariaLabel: Optional.some(spec.tooltip),
29789            tooltip: Optional.none(), // TINY-10474 - Using own tooltip config
29790            role: Optional.none(),
29791            fetch: items.getFetch(backstage, getStyleItems),
29792            onSetup,
29793            getApi,
29794            columns: 1,
29795            presets: 'normal',
29796            classes: spec.icon.isSome() ? [] : ['bespoke'],
29797            dropdownBehaviours: [
29798                Tooltipping.config({
29799                    ...backstage.shared.providers.tooltips.getConfig({
29800                        tooltipText: backstage.shared.providers.translate(spec.tooltip),
29801                        onShow: (comp) => {
29802                            if (spec.tooltip !== tooltipString.get()) {
29803                                const translatedTooltip = backstage.shared.providers.translate(tooltipString.get());
29804                                Tooltipping.setComponents(comp, backstage.shared.providers.tooltips.getComponents({ tooltipText: translatedTooltip }));
29805                            }
29806                        }
29807                    }),
29808                })
29809            ]
29810        }
29810, "tox-tbtn" /* ToolbarButtonClasses.Button */, backstage.shared, btnName);
29811    };
29812
29813    const process = (rawFormats) => map$2(rawFormats, (item) => {
29814        let title = item, format = item;
29815        // Allow text=value block formats
29816        const values = item.split('=');
29817        if (values.length > 1) {
29818            title = values[0];
29819            format = values[1];
29820        }
29821        return { title, format };
29822    });
29823    const buildBasicStaticDataset = (data) => ({
29824        type: 'basic',
29825        data
29826    });
29827    var Delimiter;
29828    (function (Delimiter) {
29829        Delimiter[Delimiter["SemiColon"] = 0] = "SemiColon";
29830        Delimiter[Delimiter["Space"] = 1] = "Space";
29831    })(Delimiter || (Delimiter = {}));
29832    const split = (rawFormats, delimiter) => {
29833        if (delimiter === Delimiter.SemiColon) {
29834            return rawFormats.replace(/;$/, '').split(';');
29835        }
29836        else {
29837            return rawFormats.split(' ');
29838        }
29839    };
29840    const buildBasicSettingsDataset = (editor, settingName, delimiter) => {
29841        // eslint-disable-next-line @tinymce/no-direct-editor-options
29842        const rawFormats = editor.options.get(settingName);
29843        const data = process(split(rawFormats, delimiter));
29844        return {
29845            type: 'basic',
29846            data
29847        };
29848    };
29849
29850    const menuTitle$4 = 'Align';
29851    const getTooltipPlaceholder$4 = constant$1('Alignment {0}');
29852    const fallbackAlignment = 'left';
29853    const alignMenuItems = [
29854        { title: 'Left', icon: 'align-left', format: 'alignleft', command: 'JustifyLeft' },
29855        { title: 'Center', icon: 'align-center', format: 'aligncenter', command: 'JustifyCenter' },
29856        { title: 'Right', icon: 'align-right', format: 'alignright', command: 'JustifyRight' },
29857        { title: 'Justify', icon: 'align-justify', format: 'alignjustify', command: 'JustifyFull' }
29858    ];
29859    const getSpec$4 = (editor) => {
29860        const getMatchingValue = () => find$5(alignMenuItems, (item) => editor.formatter.match(item.format));
29861        const isSelectedFor = (format) => () => editor.formatter.match(format);
29862        const getPreviewFor = (_format) => Optional.none;
29863        const updateSelectMenuIcon = (comp) => {
29864            const match = getMatchingValue();
29865            const alignment = match.fold(constant$1(fallbackAlignment), (item) => item.title.toLowerCase());
29866            emitWith(comp, updateMenuIcon, {
29867                icon: `align-${alignment}`
29868            });
29869            fireAlignTextUpdate(editor, { value: alignment });
29870        };
29871        const dataset = buildBasicStaticDataset(alignMenuItems);
29872        const onAction = (rawItem) => () => find$5(alignMenuItems, (item) => item.format === rawItem.format)
29873            .each((item) => editor.execCommand(item.command));
29874        return {
29875            tooltip: makeTooltipText(editor, getTooltipPlaceholder$4(), fallbackAlignment),
29876            text: Optional.none(),
29877            icon: Optional.some('align-left'),
29878            isSelectedFor,
29879            getCurrentValue: Optional.none,
29880            getPreviewFor,
29881            onAction,
29882            updateText: updateSelectMenuIcon,
29883            dataset,
29884            shouldHide: false,
29885            isInvalid: (item) => !editor.formatter.canApply(item.format)
29886        };
29887    };
29888    const createAlignButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$4(editor), getTooltipPlaceholder$4, 'AlignTextUpdate', 'align');
29889    const createAlignMenu = (editor, backstage) => {
29890        const menuItems = createMenuItems(backstage, getSpec$4(editor));
29891        editor.ui.registry.addNestedMenuItem('align', {
29892            text: backstage.shared.providers.translate(menuTitle$4),
29893            onSetup: onSetupEditableToggle(editor),
29894            getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())
29895        });
29896    };
29897
29898    const findNearest = (editor, getStyles) => {
29899        const styles = getStyles();
29900        const formats = map$2(styles, (style) => style.format);
29901        return Optional.from(editor.formatter.closest(formats)).bind((fmt) => find$5(styles, (data) => data.format === fmt));
29902    };
29903
29904    const menuTitle$3 = 'Blocks';
29905    const getTooltipPlaceholder$3 = constant$1('Block {0}');
29906    const fallbackFormat = 'Paragraph';
29907    const getSpec$3 = (editor) => {
29908        const isSelectedFor = (format) => () => editor.formatter.match(format);
29909        const getPreviewFor = (format) => () => {
29910            const fmt = editor.formatter.get(format);
29911            if (fmt) {
29912                return Optional.some({
29913                    tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',
29914                    styles: editor.dom.parseStyle(editor.formatter.getCssText(format))
29915                });
29916            }
29917            else {
29918                return Optional.none();
29919            }
29920        };
29921        const updateSelectMenuText = (comp) => {
29922            const detectedFormat = findNearest(editor, () => dataset.data);
29923            const text = detectedFormat.fold(constant$1(fallbackFormat), (fmt) => fmt.title);
29924            emitWith(comp, updateMenuText, {
29925                text
29926            });
29927            fireBlocksTextUpdate(editor, { value: text });
29928        };
29929        const dataset = buildBasicSettingsDataset(editor, 'block_formats', Delimiter.SemiColon);
29930        return {
29931            tooltip: makeTooltipText(editor, getTooltipPlaceholder$3(), fallbackFormat),
29932            text: Optional.some(fallbackFormat),
29933            icon: Optional.none(),
29934            isSelectedFor,
29935            getCurrentValue: Optional.none,
29936            getPreviewFor,
29937            onAction: onActionToggleFormat$1(editor),
29938            updateText: updateSelectMenuText,
29939            dataset,
29940            shouldHide: false,
29941            isInvalid: (item) => !editor.formatter.canApply(item.format)
29942        };
29943    };
29944    const createBlocksButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$3(editor), getTooltipPlaceholder$3, 'BlocksTextUpdate', 'blocks');
29945    // FIX: Test this!
29946    const createBlocksMenu = (editor, backstage) => {
29947        const menuItems = createMenuItems(backstage, getSpec$3(editor));
29948        editor.ui.registry.addNestedMenuItem('blocks', {
29949            text: menuTitle$3,
29950            onSetup: onSetupEditableToggle(editor),
29951            getSubmenuItems: () =>
29951 menuItems.items.validateItems(menuItems.getStyleItems())
29952        });
29953    };
29954
29955    const menuTitle$2 = 'Fonts';
29956    const getTooltipPlaceholder$2 = constant$1('Font {0}');
29957    const systemFont = 'System Font';
29958    // A list of fonts that must be in a font family for the font to be recognised as the system stack
29959    // Note: Don't include 'BlinkMacSystemFont', as Chrome on Mac converts it to different names
29960    // The system font stack will be similar to the following. (Note: each has minor variants)
29961    // Oxide: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
29962    // Bootstrap: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
29963    // Wordpress: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
29964    const systemStackFonts = ['-apple-system', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'sans-serif'];
29965    // Split the fonts into an array and strip away any start/end quotes
29966    const splitFonts = (fontFamily) => {
29967        const fonts = fontFamily.split(/\s*,\s*/);
29968        return map$2(fonts, (font) => font.replace(/^['"]+|['"]+$/g, ''));
29969    };
29970    const matchesStack = (fonts, stack) => stack.length > 0 && forall(stack, (font) => fonts.indexOf(font.toLowerCase()) > -1);
29971    const isSystemFontStack = (fontFamily, userStack) => {
29972        if (fontFamily.indexOf('-apple-system') === 0 || userStack.length > 0) {
29973            const fonts = splitFonts(fontFamily.toLowerCase());
29974            return matchesStack(fonts, systemStackFonts) || matchesStack(fonts, userStack);
29975        }
29976        else {
29977            return false;
29978        }
29979    };
29980    const getSpec$2 = (editor) => {
29981        const getMatchingValue = () => {
29982            const getFirstFont = (fontFamily) => fontFamily ? splitFonts(fontFamily)[0] : '';
29983            const fontFamily = editor.queryCommandValue('FontName');
29984            const items = dataset.data;
29985            const font = fontFamily ? fontFamily.toLowerCase() : '';
29986            const userStack = getDefaultFontStack(editor);
29987            const matchOpt = find$5(items, (item) => {
29988                const format = item.format;
29989                return (format.toLowerCase() === font) || (getFirstFont(format).toLowerCase() === getFirstFont(font).toLowerCase());
29990            }).orThunk(() => {
29991                return someIf(isSystemFontStack(font, userStack), {
29992                    title: systemFont,
29993                    format: font
29994                });
29995            });
29996            return { matchOpt, font: fontFamily };
29997        };
29998        const isSelectedFor = (item) => (valueOpt) => valueOpt.exists((value) => value.format === item);
29999        const getCurrentValue = () => {
30000            const { matchOpt } = getMatchingValue();
30001            return matchOpt;
30002        };
30003        const getPreviewFor = (item) => () => Optional.some({
30004            tag: 'div',
30005            styles: item.indexOf('dings') === -1 ? { 'font-family': item } : {}
30006        });
30007        const onAction = (rawItem) => () => {
30008            editor.undoManager.transact(() => {
30009                editor.focus();
30010                editor.execCommand('FontName', false, rawItem.format);
30011            });
30012        };
30013        const updateSelectMenuText = (comp) => {
30014            const { matchOpt, font } = getMatchingValue();
30015            const text = matchOpt.fold(constant$1(font), (item) => item.title);
30016            emitWith(comp, updateMenuText, {
30017                text
30018            });
30019            fireFontFamilyTextUpdate(editor, { value: text });
30020        };
30021        const dataset = buildBasicSettingsDataset(editor, 'font_family_formats', Delimiter.SemiColon);
30022        return {
30023            tooltip: makeTooltipText(editor, getTooltipPlaceholder$2(), systemFont),
30024            text: Optional.some(systemFont),
30025            icon: Optional.none(),
30026            isSelectedFor,
30027            getCurrentValue,
30028            getPreviewFor,
30029            onAction,
30030            updateText: updateSelectMenuText,
30031            dataset,
30032            shouldHide: false,
30033            isInvalid: never
30034        };
30035    };
30036    const createFontFamilyButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$2(editor), getTooltipPlaceholder$2, 'FontFamilyTextUpdate', 'fontfamily');
30037    // TODO: Test this!
30038    const createFontFamilyMenu = (editor, backstage) => {
30039        const menuItems = createMenuItems(backstage, getSpec$2(editor));
30040        editor.ui.registry.addNestedMenuItem('fontfamily', {
30041            text: backstage.shared.providers.translate(menuTitle$2),
30042            onSetup: onSetupEditableToggle(editor),
30043            getSubmenuItems: () =>
30043 menuItems.items.validateItems(menuItems.getStyleItems())
30044        });
30045    };
30046
30047    var global$1 = tinymce.util.Tools.resolve('tinymce.util.VK');
30048
30049    const createBespokeNumberInput = (editor, backstage, spec, btnName) => {
30050        let currentComp = Optional.none();
30051        const getValueFromCurrentComp = (comp) => comp.map((alloyComp) => Representing.getValue(alloyComp)).getOr('');
30052        const onSetup = onSetupEvent(editor, 'NodeChange SwitchMode DisabledStateChange', (api) => {
30053            const comp = api.getComponent();
30054            currentComp = Optional.some(comp);
30055            spec.updateInputValue(comp);
30056            Disabling.set(comp, !editor.selection.isEditable() || isDisabled(editor));
30057        });
30058        const getApi = (comp) => ({ getComponent: constant$1(comp) });
30059        const editorOffCell = Cell(noop);
30060        const customEvents = generate$6('custom-number-input-events');
30061        const changeValue = (f, fromInput, focusBack) => {
30062            const text = getValueFromCurrentComp(currentComp);
30063            const newValue = spec.getNewValue(text, f);
30064            const lenghtDelta = text.length - `${newValue}`.length;
30065            const oldStart = currentComp.map((comp) => comp.element.dom.selectionStart - lenghtDelta);
30066            const oldEnd = currentComp.map((comp) => comp.element.dom.selectionEnd - lenghtDelta);
30067            spec.onAction(newValue, focusBack);
30068            currentComp.each((comp) => {
30069                Representing.setValue(comp, newValue);
30070                if (fromInput) {
30071                    oldStart.each((oldStart) => comp.element.dom.selectionStart = oldStart);
30072                    oldEnd.each((oldEnd) => comp.element.dom.selectionEnd = oldEnd);
30073                }
30074            });
30075        };
30076        const decrease = (fromInput, focusBack) => changeValue((n, s) => n - s, fromInput, focusBack);
30077        const increase = (fromInput, focusBack) => changeValue((n, s) => n + s, fromInput, focusBack);
30078        const goToParent = (comp) => parentElement(comp.element).fold(Optional.none, (parent) => {
30079            focus$4(parent);
30080            return Optional.some(true);
30081        });
30082        const focusInput = (comp) => {
30083            if (hasFocus(comp.element)) {
30084                firstChild(comp.element).each((input) => focus$4(input));
30085                return Optional.some(true);
30086            }
30087            else {
30088                return Optional.none();
30089            }
30090        };
30091        const makeStepperButton = (action, title, tooltip, classes) => {
30092            const editorOffCellStepButton = Cell(noop);
30093            const translatedTooltip = backstage.shared.providers.translate(tooltip);
30094            const altExecuting = generate$6('altExecuting');
30095            const onSetup = onSetupEvent(editor, 'NodeChange SwitchMode DisabledStateChange', (api) => {
30096                Disabling.set(api.getComponent(), !editor.selection.isEditable() || isDisabled(editor));
30097            });
30098            const onClick = (comp) => {
30099                if (!Disabling.isDisabled(comp)) {
30100                    action(true);
30101                }
30102            };
30103            return Button.sketch({
30104                dom: {
30105                    tag: 'button',
30106                    attributes: {
30107                        'aria-label': translatedTooltip,
30108                        'data-mce-name': title
30109                    },
30110                    classes: classes.concat(title)
30111                },
30112                components: [
30113                    renderIconFromPack$1(title, backstage.shared.providers.icons)
30114                ],
30115                buttonBehaviours: derive$1([
30116                    Disabling.config({}),
30117                    Tooltipping.config(backstage.shared.providers.tooltips.getConfig({
30118                        tooltipText: translatedTooltip
30119                    })),
30120                    config(altExecuting, [
30121                        onControlAttached({ onSetup, getApi }, editorOffCellStepButton),
30122                        onControlDetached({ getApi }, editorOffCellStepButton),
30123                        run$1(keydown(), (comp, se) => {
30124                            if (se.event.raw.keyCode === global$1.SPACEBAR || se.event.raw.keyCode === global$1.ENTER) {
30125                                if (!Disabling.isDisabled(comp)) {
30126                                    action(false);
30127                                }
30128                            }
30129                        }),
30130                        run$1(click(), onClick),
30131                        run$1(touchend(), onClick)
30132                    ])
30133                ]),
30134                eventOrder: {
30135                    [keydown()]: [altExecuting, 'keying'],
30136                    [click()]: [altExecuting, 'alloy.base.behaviour'],
30137                    [touchend()]: [altExecuting, 'alloy.base.behaviour'],
30138                    [attachedToDom()]: ['alloy.base.behaviour', altExecuting, 'tooltipping'],
30139                    [detachedFromDom()]: [altExecuting, 'tooltipping']
30140                }
30141            });
30142        };
30143        const memMinus = record(makeStepperButton((focusBack) => decrease(false, focusBack), 'minus', 'Decrease font size', []));
30144        const memPlus = record(makeStepperButton((focusBack) => increase(false, focusBack), 'plus', 'Increase font size', []));
30145        const memInput = record({
30146            dom: {
30147                tag: 'div',
30148                classes: ['tox-input-wrapper']
30149            },
30150            components: [
30151                Input.sketch({
30152                    inputBehaviours: derive$1([
30153                        Disabling.config({}),
30154                        config(customEvents, [
30155                            onControlAttached({ onSetup, getApi }, editorOffCell),
30156                            onControlDetached({ getApi }, editorOffCell)
30157                        ]),
30158                        config('input-update-display-text', [
30159                            run$1(updateMenuText, (comp, se) => {
30160                                Representing.setValue(comp, se.event.text);
30161                            }),
30162                            run$1(focusout(), (comp) => {
30163                                spec.onAction(Representing.getValue(comp));
30164                            }),
30165                            run$1(change(), (comp) => {
30166                                spec.onAction(Representing.getValue(comp));
30167                            })
30168                        ]),
30169                        Keying.config({
30170                            mode: 'special',
30171                            onEnter: (_comp) => {
30172                                changeValue(identity, true, true);
30173                                return Optional.some(true);
30174                            },
30175                            onEscape: goToParent,
30176                            onUp: (_comp) => {
30177                                increase(true, false);
30178                                return Optional.some(true);
30179                            },
30180                            onDown: (_comp) => {
30181                                decrease(true, false);
30182                                return Optional.some(true);
30183                            },
30184                            onLeft: (_comp, se) => {
30185                                se.cut();
30186                                return Optional.none();
30187                            },
30188                            onRight: (_comp, se) => {
30189                                se.cut();
30190                                return Optional.none();
30191                            }
30192                        })
30193                    ])
30194                })
30195            ],
30196            behaviours: derive$1([
30197                Focusing.config({}),
30198                Keying.config({
30199                    mode: 'special',
30200                    onEnter: focusInput,
30201                    onSpace: focusInput,
30202                    onEscape: goToParent
30203                }),
30204                config('input-wrapper-events', [
30205                    run$1(mouseover(), (comp) => {
30206                        each$1([memMinus, memPlus], (button) => {
30207                            const buttonNode = SugarElement.fromDom(button.get(comp).element.dom);
30208                            if (hasFocus(buttonNode)) {
30209                                blur$1(buttonNode);
30210                            }
30211                        });
30212                    })
30213                ])
30214            ])
30215        });
30216        return {
30217            dom: {
30218                tag: 'div',
30219                classes: ['tox-number-input'],
30220                attributes: {
30221                    ...(isNonNullable(btnName) ? { 'data-mce-name': btnName } : {})
30222                }
30223            },
30224            components: [
30225                memMinus.asSpec(),
30226                memInput.asSpec(),
30227                memPlus.asSpec()
30228            ],
30229            behaviours: derive$1([
30230                Focusing.config({}),
30231                Keying.config({
30232                    mode: 'flow',
30233                    focusInside: FocusInsideModes.OnEnterOrSpaceMode,
30234                    cycles: false,
30235                    selector: 'button, .tox-input-wrapper',
30236                    onEscape: (wrapperComp) => {
30237                        if (hasFocus(wrapperComp.element)) {
30238                            return Optional.none();
30239                        }
30240                        else {
30241                            focus$4(wrapperComp.element);
30242                            return Optional.some(true);
30243                        }
30244                    },
30245                })
30246            ])
30247        };
30248    };
30249
30250    const menuTitle$1 = 'Font sizes';
30251    const getTooltipPlaceholder$1 = constant$1('Font size {0}');
30252    const fallbackFontSize = '12pt';
30253    // See https://websemantics.uk/articles/font-size-conversion/ for conversions
30254    const legacyFontSizes = {
30255        '8pt': '1',
30256        '10pt': '2',
30257        '12pt': '3',
30258        '14pt': '4',
30259        '18pt': '5',
30260        '24pt': '6',
30261        '36pt': '7'
30262    };
30263    // Note: 'xx-small', 'x-small' and 'large' are rounded up to nearest whole pt
30264    const keywordFontSizes = {
30265        'xx-small': '7pt',
30266        'x-small': '8pt',
30267        'small': '10pt',
30268        'medium': '12pt',
30269        'large': '14pt',
30270        'x-large': '18pt',
30271        'xx-large': '24pt'
30272    };
30273    const round = (number, precision) => {
30274        const factor = Math.pow(10, precision);
30275        return Math.round(number * factor) / factor;
30276    };
30277    const toPt = (fontSize, precision) => {
30278        if (/[0-9.]+px$/.test(fontSize)) {
30279            // Round to the nearest 0.5
30280            return round(parseInt(fontSize, 10) * 72 / 96, precision || 0) + 'pt';
30281        }
30282        else {
30283            return get$h(keywordFontSizes, fontSize).getOr(fontSize);
30284        }
30285    };
30286    const toLegacy = (fontSize) => get$h(legacyFontSizes, fontSize).getOr('');
30287    const getSpec$1 = (editor) => {
30288        const getMatchingValue = () => {
30289            let matchOpt = Optional.none();
30290            const items = dataset.data;
30291            const fontSize = editor.queryCommandValue('FontSize');
30292            if (fontSize) {
30293                // checking for three digits after decimal point, should be precise enough
30294                for (let precision = 3; matchOpt.isNone() && precision >= 0; precision--) {
30295                    const pt = toPt(fontSize, precision);
30296                    const legacy = toLegacy(pt);
30297                    matchOpt = find$5(items, (item) => item.format === fontSize || item.format === pt || item.format === legacy);
30298                }
30299            }
30300            return { matchOpt, size: fontSize };
30301        };
30302        const isSelectedFor = (item) => (valueOpt) => valueOpt.exists((value) => value.format === item);
30303        const getCurrentValue = () => {
30304            const { matchOpt } = getMatchingValue();
30305            return matchOpt;
30306        };
30307        const getPreviewFor = constant$1(Optional.none);
30308        const onAction = (rawItem) => () => {
30309            editor.undoManager.transact(() => {
30310                editor.focus();
30311                editor.execCommand('FontSize', false, rawItem.format);
30312            });
30313        };
30314        const updateSelectMenuText = (comp) => {
30315            const { matchOpt, size } = getMatchingValue();
30316            const text = matchOpt.fold(constant$1(size), (match) => match.title);
30317            emitWith(comp, updateMenuText, {
30318                text
30319            });
30320            fireFontSizeTextUpdate(editor, { value: text });
30321        };
30322        const dataset = buildBasicSettingsDataset(editor, 'font_size_formats', Delimiter.Space);
30323        return {
30324            tooltip: makeTooltipText(editor, getTooltipPlaceholder$1(), fallbackFontSize),
30325            text: Optional.some(fallbackFontSize),
30326            icon: Optional.none(),
30327            isSelectedFor,
30328            getPreviewFor,
30329            getCurrentValue,
30330            onAction,
30331            updateText: updateSelectMenuText,
30332            dataset,
30333            shouldHide: false,
30334            isInvalid: never
30335        };
30336    };
30337    const createFontSizeButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$1(editor), getTooltipPlaceholder$1, 'FontSizeTextUpdate', 'fontsize');
30338    const getConfigFromUnit = (unit) => {
30339        const baseConfig = { step: 1 };
30340        const configs = {
30341            em: { step: 0.1 },
30342            cm: { step: 0.1 },
30343            in: { step: 0.1 },
30344            pc: { step: 0.1 },
30345            ch: { step: 0.1 },
30346            rem: { step: 0.1 }
30347        };
30348        return configs[unit] ?? baseConfig;
30349    };
30350    const defaultValue = 16;
30351    const isValidValue = (value) => value >= 0;
30352    const getNumberInputSpec = (editor) => {
30353        const getCurrentValue = () => editor.queryCommandValue('FontSize');
30354        const updateInputValue = (comp) => emitWith(comp, updateMenuText, {
30355            text: getCurrentValue()
30356        });
30357        return {
30358            updateInputValue,
30359            onAction: (format, focusBack) => editor.execCommand('FontSize', false, format, { skip_focus: !focusBack }),
30360            getNewValue: (text, updateFunction) => {
30361                parse(text, ['unsupportedLength', 'empty']);
30362                const currentValue = getCurrentValue();
30363                const parsedText = parse(text, ['unsupportedLength', 'empty']).or(parse(currentValue, ['unsupportedLength', 'empty']));
30364                const value = parsedText.map((res) => res.value).getOr(defaultValue);
30365                const defaultUnit = getFontSizeInputDefaultUnit(editor);
30366                const unit = parsedText.map((res) => res.unit).filter((u) => u !== '').getOr(defaultUnit);
30367                const newValue = updateFunction(value, getConfigFromUnit(unit).step);
30368                const res = `${isValidValue(newValue) ? newValue : value}${unit}`;
30369                if (res !== currentValue) {
30370                    fireFontSizeInputTextUpdate(editor, { value: res });
30371                }
30372                return res;
30373            }
30374        };
30375    };
30376    const createFontSizeInputButton = (editor, backstage) => createBespokeNumberInput(editor, backstage, getNumberInputSpec(editor), 'fontsizeinput');
30377    // TODO: Test this!
30378    const createFontSizeMenu = (editor, backstage) => {
30379        const menuItems = createMenuItems(backstage, getSpec$1(editor));
30380        editor.ui.registry.addNestedMenuItem('fontsize', {
30381            text: menuTitle$1,
30382            onSetup: onSetupEditableToggle(editor),
30383            getSubmenuItems: () =>
30383 menuItems.items.validateItems(menuItems.getStyleItems())
30384        });
30385    };
30386
30387    const menuTitle = 'Formats';
30388    const getTooltipPlaceholder = (value) => isEmpty(value) ? 'Formats' : 'Format {0}';
30389    const getSpec = (editor, dataset) => {
30390        const fallbackFormat = 'Formats';
30391        const isSelectedFor = (format) => () => editor.formatter.match(format);
30392        const getPreviewFor = (format) => () => {
30393            const fmt = editor.formatter.get(format);
30394            return fmt !== undefined ? Optional.some({
30395                tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',
30396                styles: editor.dom.parseStyle(editor.formatter.getCssText(format))
30397            }) : Optional.none();
30398        };
30399        const updateSelectMenuText = (comp) => {
30400            const getFormatItems = (fmt) => {
30401                if (isNestedFormat(fmt)) {
30402                    return bind$3(fmt.items, getFormatItems);
30403                }
30404                else if (isFormatReference(fmt)) {
30405                    return [{ title: fmt.title, format: fmt.format }];
30406                }
30407                else {
30408                    return [];
30409                }
30410            };
30411            const flattenedItems = bind$3(getStyleFormats(editor), getFormatItems);
30412            const detectedFormat = findNearest(editor, constant$1(flattenedItems));
30413            const text = detectedFormat.fold(constant$1({
30414                title: fallbackFormat,
30415                tooltipLabel: ''
30416            }), (fmt) => ({
30417                title: fmt.title,
30418                tooltipLabel: fmt.title
30419            }));
30420            emitWith(comp, updateMenuText, {
30421                text: text.title
30422            });
30423            fireStylesTextUpdate(editor, { value: text.tooltipLabel });
30424        };
30425        return {
30426            tooltip: makeTooltipText(editor, getTooltipPlaceholder(''), ''),
30427            text: Optional.some(fallbackFormat),
30428            icon: Optional.none(),
30429            isSelectedFor,
30430            getCurrentValue: Optional.none,
30431            getPreviewFor,
30432            onAction: onActionToggleFormat$1(editor),
30433            updateText: updateSelectMenuText,
30434            shouldHide: shouldAutoHideStyleFormats(editor),
30435            isInvalid: (item) => !editor.formatter.canApply(item.format),
30436            dataset
30437        };
30438    };
30439    const createStylesButton = (editor, backstage) => {
30440        const dataset = { type: 'advanced', ...backstage.styles };
30441        return createSelectButton(editor, backstage, getSpec(editor, dataset), getTooltipPlaceholder, 'StylesTextUpdate', 'styles');
30442    };
30443    const createStylesMenu = (editor, backstage) => {
30444        const dataset = { type: 'advanced', ...backstage.styles };
30445        const menuItems = createMenuItems(backstage, getSpec(editor, dataset));
30446        editor.ui.registry.addNestedMenuItem('styles', {
30447            text: menuTitle,
30448            onSetup: onSetupEditableToggle(editor, () => menuItems.getStyleItems().length > 0),
30449            getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())
30450        });
30451    };
30452
30453    const defaultToolbar = [
30454        {
30455            name: 'history', items: ['undo', 'redo']
30456        },
30457        {
30458            name: 'ai', items: ['tinymceai-chat', 'tinymceai-review', 'tinymceai-quickactions', 'aidialog', 'aishortcuts']
30459        },
30460        {
30461            name: 'styles', items: ['styles']
30462        },
30463        {
30464            name: 'formatting', items: ['bold', 'italic']
30465        },
30466        {
30467            name: 'alignment', items: ['alignleft', 'aligncenter', 'alignright', 'alignjustify']
30468        },
30469        {
30470            name: 'indentation', items: ['outdent', 'indent']
30471        },
30472        {
30473            name: 'permanent pen', items: ['permanentpen']
30474        },
30475        {
30476            name: 'comments', items: ['addcomment']
30477        }
30478    ];
30479    const renderFromBridge = (bridgeBuilder, render) => (spec, backstage, editor, btnName) => {
30480        const internal = bridgeBuilder(spec).mapError((errInfo) => formatError(errInfo)).getOrDie();
30481        return render(internal, backstage, editor, btnName);
30482    };
30483    const types = {
30484        button: renderFromBridge(createToolbarButton, (s, backstage, _, btnName) => renderToolbarButton(s, backstage.shared.providers, btnName)),
30485        togglebutton: renderFromBridge(createToggleButton, (s, backstage, _, btnName) => renderToolbarToggleButton(s, backstage.shared.providers, btnName)),
30486        menubutton: renderFromBridge(createMenuButton, (s, backstage, _, btnName) =>
30486 renderMenuButton(s, "tox-tbtn" /* ToolbarButtonClasses.Button */, backstage, Optional.none(), false, btnName)),
30487        splitbutton: renderFromBridge(createSplitButton, (s, backstage, _, btnName) => renderSplitButton(s, backstage.shared, btnName)),
30488        grouptoolbarbutton: renderFromBridge(createGroupToolbarButton, (s, backstage, editor, btnName) => {
30489            const buttons = editor.ui.registry.getAll().buttons;
30490            const identify = (toolbar) => identifyButtons(editor, { buttons, toolbar, allowToolbarGroups: false }, backstage, Optional.none());
30491            const attributes = {
30492                [Attribute]: backstage.shared.header.isPositionedAtTop() ? AttributeValue.TopToBottom : AttributeValue.BottomToTop
30493            };
30494            switch (getToolbarMode(editor)) {
30495                case ToolbarMode$1.floating:
30496                    return renderFloatingToolbarButton(s, backstage, identify, attributes, btnName);
30497                default:
30498                    // TODO change this message and add a case when sliding is available
30499                    throw new Error('Toolbar groups are only supported when using floating toolbar mode');
30500            }
30501        })
30502    };
30503    const extractFrom = (spec, backstage, editor, btnName) => get$h(types, spec.type).fold(() => {
30504        // eslint-disable-next-line no-console
30505        console.error('skipping button defined by', spec);
30506        return Optional.none();
30507    }, (render) => Optional.some(render(spec, backstage, editor, btnName)));
30508    const bespokeButtons = {
30509        styles: createStylesButton,
30510        fontsize: createFontSizeButton,
30511        fontsizeinput: createFontSizeInputButton,
30512        fontfamily: createFontFamilyButton,
30513        blocks: createBlocksButton,
30514        align: createAlignButton,
30515        navigateback: createNavigateBackButton
30516    };
30517    const removeUnusedDefaults = (buttons) => {
30518        const filteredItemGroups = map$2(defaultToolbar, (group) => {
30519            const items = filter$2(group.items, (subItem) => has$2(buttons, subItem) || has$2(bespokeButtons, subItem));
30520            return {
30521                name: group.name,
30522                items
30523            };
30524        });
30525        return filter$2(filteredItemGroups, (group) => group.items.length > 0);
30526    };
30527    const convertStringToolbar = (strToolbar) => {
30528        const groupsStrings = strToolbar.split('|');
30529        return map$2(groupsStrings, (g) => ({
30530            items: g.trim().split(' ')
30531        }));
30532    };
30533    const isToolbarGroupSettingArray = (toolbar) => isArrayOf(toolbar, (t) => (has$2(t, 'name') || has$2(t, 'label')) && has$2(t, 'items'));
30534    // Toolbar settings
30535    // false = disabled
30536    // undefined or true = default
30537    // string = enabled with specified buttons and groups
30538    // string array = enabled with specified buttons and groups
30539    // object array = enabled with specified buttons, groups and group titles
30540    const createToolbar = (toolbarConfig) => {
30541        const toolbar = toolbarConfig.toolbar;
30542        const buttons = toolbarConfig.buttons;
30543        if (toolbar === false) {
30544            return [];
30545        }
30546        else if (toolbar === undefined || toolbar === true) {
30547            return removeUnusedDefaults(buttons);
30548        }
30549        else if (isString(toolbar)) {
30550            return convertStringToolbar(toolbar);
30551        }
30552        else if (isToolbarGroupSettingArray(toolbar)) {
30553            return toolbar;
30554        }
30555        else {
30556            // eslint-disable-next-line no-console
30557            console.error('Toolbar type should be string, string[], boolean or ToolbarGroup[]');
30558            return [];
30559        }
30560    };
30561    const lookupButton = (editor, buttons, toolbarItem, allowToolbarGroups, backstage, prefixes) => get$h(buttons, toolbarItem.toLowerCase())
30562        .orThunk(() => prefixes.bind((ps) => findMap(ps, (prefix) => get$h(buttons, prefix + toolbarItem.toLowerCase()))))
30563        .fold(() => get$h(bespokeButtons, toolbarItem.toLowerCase()).map((r) => r(editor, backstage)), 
30564    // TODO: Add back after TINY-3232 is implemented
30565    // .orThunk(() => {
30566    //   console.error('No representation for toolbarItem: ' + toolbarItem);
30567    //   return Optional.none();
30568    // ),
30569    (spec) => {
30570        if (spec.type === 'grouptoolbarbutton' && !allowToolbarGroups) {
30571            // TODO change this message when sliding is available
30572            // eslint-disable-next-line no-console
30573            console.warn(`Ignoring the '${toolbarItem}' toolbar button. Group toolbar buttons are only supported when using floating toolbar mode and cannot be nested.`);
30574            return Optional.none();
30575        }
30576        else {
30577            return extractFrom(spec, backstage, editor, toolbarItem.toLowerCase());
30578        }
30579    });
30580    const identifyButtons = (editor, toolbarConfig, backstage, prefixes) => {
30581        const toolbarGroups = createToolbar(toolbarConfig);
30582        const groups = map$2(toolbarGroups, (group) => {
30583            const items = bind$3(group.items, (toolbarItem) => {
30584                if (toolbarItem.trim().length === 0) {
30585                    return [];
30586                }
30587                return lookupButton(editor, toolbarConfig.buttons, toolbarItem, toolbarConfig.allowToolbarGroups, backstage, prefixes)
30588                    .map((spec) => Array.isArray(spec) ? spec : [spec])
30589                    .getOr([]);
30590            });
30591            return {
30592                title: Optional.from(editor.translate(group.name)),
vendor: 8,313 bytes, lines 30593-30746
30593                label: someIf(group.label !== undefined, editor.translate(group.label)),
30594                items
30595            };
30596        });
30597        return filter$2(groups, (group) => group.items.length > 0);
30598    };
30599
30600    // Set toolbar(s) depending on if multiple toolbars is configured or not
30601    const setToolbar = (editor, uiRefs, rawUiConfig, backstage) => {
30602        const outerContainer = uiRefs.mainUi.outerContainer;
30603        const toolbarConfig = rawUiConfig.toolbar;
30604        const toolbarButtonsConfig = rawUiConfig.buttons;
30605        // Check if toolbar type is a non-empty string array
30606        if (isArrayOf(toolbarConfig, isString)) {
30607            const toolbars = toolbarConfig.map((t) => {
30608                const config = { toolbar: t, buttons: toolbarButtonsConfig, allowToolbarGroups: rawUiConfig.allowToolbarGroups };
30609                return identifyButtons(editor, config, backstage, Optional.none());
30610            });
30611            OuterContainer.setToolbars(outerContainer, toolbars);
30612        }
30613        else {
30614            OuterContainer.setToolbar(outerContainer, identifyButtons(editor, rawUiConfig, backstage, Optional.none()));
30615        }
30616    };
30617
30618    const detection = detect$1();
30619    const isiOS12 = detection.os.isiOS() && detection.os.version.major <= 12;
30620    const setupEvents$1 = (editor, uiRefs) => {
30621        const { uiMotherships } = uiRefs;
30622        const dom = editor.dom;
30623        let contentWindow = editor.getWin();
30624        const initialDocEle = editor.getDoc().documentElement;
30625        const lastWindowDimensions = Cell(SugarPosition(contentWindow.innerWidth, contentWindow.innerHeight));
30626        const lastDocumentDimensions = Cell(SugarPosition(initialDocEle.offsetWidth, initialDocEle.offsetHeight));
30627        const resizeWindow = () => {
30628            // Check if the window dimensions have changed and if so then trigger a content resize event
30629            const outer = lastWindowDimensions.get();
30630            if (outer.left !== contentWindow.innerWidth || outer.top !== contentWindow.innerHeight) {
30631                lastWindowDimensions.set(SugarPosition(contentWindow.innerWidth, contentWindow.innerHeight));
30632                fireResizeContent(editor);
30633            }
30634        };
30635        const resizeDocument = () => {
30636            // Don't use the initial doc ele, as there's a small chance it may have changed
30637            const docEle = editor.getDoc().documentElement;
30638            // Check if the document dimensions have changed and if so then trigger a content resize event
30639            const inner = lastDocumentDimensions.get();
30640            if (inner.left !== docEle.offsetWidth || inner.top !== docEle.offsetHeight) {
30641                lastDocumentDimensions.set(SugarPosition(docEle.offsetWidth, docEle.offsetHeight));
30642                fireResizeContent(editor);
30643            }
30644        };
30645        const scroll = (e) => {
30646            fireScrollContent(editor, e);
30647        };
30648        dom.bind(contentWindow, 'resize', resizeWindow);
30649        dom.bind(contentWindow, 'scroll', scroll);
30650        // Bind to async load events and trigger a content resize event if the size has changed
30651        const elementLoad = capture(SugarElement.fromDom(editor.getBody()), 'load', resizeDocument);
30652        // We want to hide ALL UI motherships here.
30653        editor.on('hide', () => {
30654            each$1(uiMotherships, (m) => {
30655                set$7(m.element, 'display', 'none');
30656            });
30657        });
30658        editor.on('show', () => {
30659            each$1(uiMotherships, (m) => {
30660                remove$6(m.element, 'display');
30661            });
30662        });
30663        editor.on('NodeChange', resizeDocument);
30664        editor.on('remove', () => {
30665            elementLoad.unbind();
30666            dom.unbind(contentWindow, 'resize', resizeWindow);
30667            dom.unbind(contentWindow, 'scroll', scroll);
30668            // Clean memory for IE
30669            contentWindow = null;
30670        });
30671    };
30672    // TINY-9226: When set, the `ui_mode: split` option will create two different sinks (one for popups and one for sinks)
30673    // and the popup sink will be placed adjacent to the editor. This will make it having the same scrolling ancestry.
30674    const attachUiMotherships = (editor, uiRoot, uiRefs) => {
30675        if (isSplitUiMode(editor)) {
30676            attachSystemAfter(uiRefs.mainUi.mothership.element, uiRefs.popupUi.mothership);
30677        }
30678        // In UiRefs, dialogUi and popupUi refer to the same thing if ui_mode: combined
30679        attachSystem(uiRoot, uiRefs.dialogUi.mothership);
30680    };
30681    const render$1 = (editor, uiRefs, rawUiConfig, backstage, args) => {
30682        const { mainUi, uiMotherships } = uiRefs;
30683        const lastToolbarWidth = Cell(0);
30684        const outerContainer = mainUi.outerContainer;
30685        // eslint-disable-next-line @typescript-eslint/no-floating-promises
30686        iframe(editor);
30687        const eTargetNode = SugarElement.fromDom(args.targetNode);
30688        const uiRoot = getContentContainer(getRootNode(eTargetNode));
30689        attachSystemAfter(eTargetNode, mainUi.mothership);
30690        attachUiMotherships(editor, uiRoot, uiRefs);
30691        editor.on('PostRender', () => {
30692            OuterContainer.setSidebar(outerContainer, rawUiConfig.sidebar, getSidebarShow(editor));
30693            OuterContainer.setViews(outerContainer, rawUiConfig.views, getViewShow(editor));
30694        }, true);
30695        // TINY-10343: Using `SkinLoaded` instead of `PostRender` because if the skin loading takes too long you run in to rendering problems since things are measured before the CSS is being applied
30696        editor.on('SkinLoaded', () => {
30697            // Set the sidebar before the toolbar and menubar
30698            // - each sidebar has an associated toggle toolbar button that needs to check the
30699            //   sidebar that is set to determine its active state on setup
30700            setToolbar(editor, uiRefs, rawUiConfig, backstage);
30701            lastToolbarWidth.set(editor.getWin().innerWidth);
30702            OuterContainer.setMenubar(outerContainer, identifyMenus(editor, rawUiConfig));
30703            setupEvents$1(editor, uiRefs);
30704        });
30705        const socket = OuterContainer.getSocket(outerContainer).getOrDie('Could not find expected socket element');
30706        if (isiOS12) {
30707            setAll(socket.element, {
30708                'overflow': 'scroll',
30709                '-webkit-overflow-scrolling': 'touch' // required for ios < 13 content scrolling
30710            });
30711            const limit = first$1(() => {
30712                editor.dispatch('ScrollContent');
30713            }, 20);
30714            const unbinder = bind$1(socket.element, 'scroll', limit.throttle);
30715            editor.on('remove', unbinder.unbind);
30716        }
30717        setupEventsForUi(editor, uiRefs);
30718        editor.addCommand('ToggleSidebar', (_ui, value) => {
30719            OuterContainer.toggleSidebar(outerContainer, value);
30720            fireToggleSidebar(editor);
30721        });
30722        editor.addQueryValueHandler('ToggleSidebar', () => OuterContainer.whichSidebar(outerContainer) ?? '');
30723        editor.addCommand('ToggleView', (_ui, value) => {
30724            if (OuterContainer.toggleView(outerContainer, value)) {
30725                const target = outerContainer.element;
30726                mainUi.mothership.broadcastOn([dismissPopups()], { target });
30727                each$1(uiMotherships, (m) => {
30728                    m.broadcastOn([dismissPopups()], { target });
30729                });
30730                // Switching back to main view should focus the editor and update any UIs
30731                if (isNull(OuterContainer.whichView(outerContainer))) {
30732                    editor.focus();
30733                    editor.nodeChanged();
30734                    OuterContainer.refreshToolbar(outerContainer);
30735                }
30736                fireToggleView(editor);
30737            }
30738        });
30739        editor.addQueryValueHandler('ToggleView', () => OuterContainer.whichView(outerContainer) ?? '');
30740        const toolbarMode = getToolbarMode(editor);
30741        const refreshDrawer = () => {
30742            OuterContainer.refreshToolbar(uiRefs.mainUi.outerContainer);
30743        };
30744        if (toolbarMode === ToolbarMode$1.sliding || toolbarMode === ToolbarMode$1.floating) {
30745            editor.on('ResizeWindow ResizeEditor ResizeContent', () => {
30746                // Check if the width has changed, if so then refresh the toolbar drawer. We don't care if height changes.
30747                const width = editor.getWin().innerWidth;
30748                if (width !== lastToolbarWidth.get()) {
30749                    refreshDrawer();
30750                    lastToolbarWidth.set(width);
30751                }
30752            });
30753        }
30754        const api = {
30755            setEnabled: (state) => {
30756                const eventType = state ? 'setEnabled' : 'setDisabled';
30757                broadcastEvents(uiRefs, eventType);
30758            },
30759            isEnabled: () => !Disabling.isDisabled(outerContainer)
30760        };
30761        return {
30762            iframeContainer: socket.element.dom,
30763            editorContainer: outerContainer.element.dom,
30764            api
30765        };
30766    };
30767
30768    var Iframe = /*#__PURE__*/Object.freeze({
30769        __proto__: null,
30770        render: render$1
30771    });
30772
30773    const parseToInt = (val) => {
30774        // if size is a number or '_px', will return the number
30775        const re = /^[0-9\.]+(|px)$/i;
30776        if (re.test('' + val)) {
30777            return Optional.some(parseInt('' + val, 10));
30778        }
30779        return Optional.none();
30780    };
30781    const numToPx = (val) => isNumber(val) ? val + 'px' : val;
30782    const calcCappedSize = (size, minSize, maxSize) => {
30783        const minOverride = minSize.filter((min) => size < min);
30784        const maxOverride = maxSize.filter((max) => size > max);
30785        return minOverride.or(maxOverride).getOr(size);
30786    };
30787    const convertValueToPx = (element, value) => {
30788        if (typeof value === 'number') {
30789            return Optional.from(value);
30790        }
30791        const splitValue = /^([0-9.]+)(pt|em|px)$/.exec(value.trim());
30792        if (splitValue) {
30793            const type = splitValue[2];
30794            const parsed = Number.parseFloat(splitValue[1]);
30795            if (Number.isNaN(parsed) || parsed < 0) {
30796                return Optional.none();
30797            }
30798            else if (type === 'em') {
30799                return Optional.from(parsed * Number.parseFloat(window.getComputedStyle(element.dom).fontSize));
30800            }
30801            else if (type === 'pt') {
30802                return Optional.from(parsed * (72 / 96));
30803            }
30804            else if (type === 'px') {
30805                return Optional.from(parsed);
30806            }
30807        }
30808        return Optional.none();
30809    };
30810
30811    const getHeight = (editor) => {
30812        const baseHeight = convertValueToPx(SugarElement.fromDom(editor.targetElm), getHeightOption(editor));
30813        const minHeight = getMinHeightOption(editor);
30814        const maxHeight = getMaxHeightOption(editor);
30815        return baseHeight.map((height) => calcCappedSize(height, minHeight, maxHeight));
30816    };
30817    const getHeightWithFallback = (editor) => {
30818        return getHeight(editor).getOr(getHeightOption(editor)); // If we can't parse, set the height while ignoring min/max values.
30819    };
30820    const getWidth = (editor) => {
30821        const baseWidth = getWidthOption(editor);
30822        const minWidth = getMinWidthOption(editor);
30823        const maxWidth = getMaxWidthOption(editor);
30824        return parseToInt(baseWidth).map((width) => calcCappedSize(width, minWidth, maxWidth));
30825    };
30826    const getWidthWithFallback = (editor) => {
30827        const width = getWidth(editor);
30828        return width.getOr(getWidthOption(editor));
30829    };
30830
30831    const { ToolbarLocation, ToolbarMode } = Options;
30832    const maximumDistanceToEdge = 40;
30833    const InlineHeader = (editor, targetElm, uiRefs, backstage, floatContainer) => {
30834        const { mainUi, uiMotherships } = uiRefs;
30835        const DOM = global$9.DOM;
30836        const useFixedToolbarContainer = useFixedContainer(editor);
30837        const isSticky = isStickyToolbar(editor);
30838        const editorMaxWidthOpt = getMaxWidthOption(editor).or(getWidth(editor));
30839        const headerBackstage = backstage.shared.header;
30840        const isPositionedAtTop = headerBackstage.isPositionedAtTop;
30841        const minimumToolbarWidth = 150; // Value is arbitrary.
30842        const toolbarMode = getToolbarMode(editor);
30843        const isSplitToolbar = toolbarMode === ToolbarMode.sliding || toolbarMode === ToolbarMode.floating;
30844        const visible = Cell(false);
30845        const isVisible = () => visible.get() && !editor.removed;
30846        // Calculate the toolbar offset when using a split toolbar drawer
30847        const calcToolbarOffset = (toolbar) => isSplitToolbar ?
30848            toolbar.fold(constant$1(0), (tbar) => 
30849            // If we have an overflow toolbar, we need to offset the positioning by the height of the overflow toolbar
30850            tbar.components().length > 1 ? get$d(tbar.components()[1].element) : 0) : 0;
30851        const calcMode = (container) => {
30852            switch (getToolbarLocation(editor)) {
30853                case ToolbarLocation.auto:
30854                    const toolbar = OuterContainer.getToolbar(mainUi.outerContainer);
30855                    const offset = calcToolbarOffset(toolbar);
30856                    const toolbarHeight = get$d(container.element) - offset;
30857                    const targetBounds = box$1(targetElm);
30858                    // Determine if the toolbar has room to render at the top/bottom of the document
30859                    const roomAtTop = targetBounds.y > toolbarHeight;
30860                    if (roomAtTop) {
30861                        return 'top';
30862                    }
30863                    else {
30864                        const doc = documentElement(targetElm);
30865                        const docHeight = Math.max(doc.dom.scrollHeight, get$d(doc));
30866                        const roomAtBottom = targetBounds.bottom < doc
30866Height - toolbarHeight;
30867                        // If there isn't ever room to add the toolbar above the target element, then place the toolbar at the bottom.
30868                        // Likewise if there's no room at the bottom, then we should show at the top. If there's no room at the bottom
30869                        // or top, then prefer the bottom except when it'll prevent accessing the content at the bottom.
30870                        // Make sure to exclude scroll position, as we want to still show at the top if the user can scroll up to undock
30871                        if (roomAtBottom) {
30872                            return 'bottom';
30873                        }
30874                        else {
30875                            const winBounds = win();
30876                            const isRoomAtBottomViewport = winBounds.bottom < targetBounds.bottom - toolbarHeight;
30877                            return isRoomAtBottomViewport ? 'bottom' : 'top';
30878                        }
30879                    }
30880                case ToolbarLocation.bottom:
30881                    return 'bottom';
30882                case ToolbarLocation.top:
30883                default:
30884                    return 'top';
30885            }
30886        };
30887        const setupMode = (mode) => {
30888            // Update the docking mode
30889            floatContainer.on((container) => {
30890                Docking.setModes(container, [mode]);
30891                headerBackstage.setDockingMode(mode);
30892                // Update the vertical menu direction
30893                const verticalDir = isPositionedAtTop() ? AttributeValue.TopToBottom : AttributeValue.BottomToTop;
30894                set$9(container.element, Attribute, verticalDir);
30895            });
30896        };
30897        const updateChromeWidth = () => {
30898            floatContainer.on((container) => {
30899                // Update the max width of the inline toolbar
30900                const maxWidth = editorMaxWidthOpt.getOrThunk(() => {
30901                    // Adding 10px of margin so that the toolbar won't try to wrap
30902                    return getBounds$1().width - viewport$1(targetElm).left - 10;
30903                });
30904                set$7(container.element, 'max-width', maxWidth + 'px');
30905            });
30906        };
30907        const updateChromePosition = (isOuterContainerWidthRestored, prevScroll) => {
30908            floatContainer.on((container) => {
30909                const toolbar = OuterContainer.getToolbar(mainUi.outerContainer);
30910                const offset = calcToolbarOffset(toolbar);
30911                // The float container/editor may not have been rendered yet, which will cause it to have a non integer based positions
30912                // so we need to round this to account for that.
30913                const targetBounds = box$1(targetElm);
30914                const offsetParent = getOffsetParent$1(editor, mainUi.outerContainer.element);
30915                const getLeft = () => offsetParent.fold(() => targetBounds.x, (offsetParent) => {
30916                    // Because for ui_mode: split, the main mothership (which includes the toolbar) is moved and added as a sibling
30917                    // If there's any relative position div set as the parent and the offsetParent is no longer the body,
30918                    // the absolute top/left positions would no longer be correct
30919                    // When there's a relative div and the position is the same as the toolbar container
30920                    // then it would produce a negative top as it needs to be positioned on top of the offsetParent
30921                    const offsetBox = box$1(offsetParent);
30922                    const isOffsetParentBody = eq(offsetParent, body());
30923                    return isOffsetParentBody
30924                        ? targetBounds.x
30925                        : targetBounds.x - offsetBox.x;
30926                });
30927                const getTop = () => offsetParent.fold(() => isPositionedAtTop()
30928                    ? Math.max(targetBounds.y - get$d(container.element) + offset, 0)
30929                    : targetBounds.bottom, (offsetParent) => {
30930                    // Because for ui_mode: split, the main mothership (which includes the toolbar) is moved and added as a sibling
30931                    // If there's any relative position div set as the parent and the offsetParent is no longer the body,
30932                    // the absolute top/left positions would no longer be correct
30933                    // When there's a relative div and the position is the same as the toolbar container
30934                    // then it would produce a negative top as it needs to be positioned on top of the offsetParent
30935                    const offsetBox = box$1(offsetParent);
30936                    const scrollDelta = offsetParent.dom.scrollTop ?? 0;
30937                    const isOffsetParentBody = eq(offsetParent, body());
30938                    const topValue = isOffsetParentBody
30939                        ? Math.max(targetBounds.y - get$d(container.element) + offset, 0)
30940                        : targetBounds.y - offsetBox.y + scrollDelta - get$d(container.element) + offset;
30941                    return isPositionedAtTop()
30942                        ? topValue
30943                        : targetBounds.bottom;
30944                });
30945                const left = getLeft();
30946                const widthProperties = someIf(isOuterContainerWidthRestored, 
30947                // This width can be used for calculating the "width" when resolving issues with flex-wrapping being triggered at the window width, despite scroll space being available to the right.
30948                Math.ceil(mainUi.outerContainer.element.dom.getBoundingClientRect().width))
30949                    // this check is needed because if the toolbar is rendered outside of the `outerContainer` because the toolbar have `position: "fixed"`
30950                    // the calculate width isn't correct
30951                    .filter((w) => w > minimumToolbarWidth).map((toolbarWidth) => {
30952                    const scroll = prevScroll.getOr(get$b());
30953                    /*
30954                  As the editor container can wrap its elements (due to flex-wrap), the width of the container impacts also its height. Adding a minimum width works around two problems:
30955        
30956                  a) The docking behaviour (e.g. lazyContext) does not handle the situation of a very thin component near the edge of the screen very well, and actually has no concept of horizontal scroll - it only checks y values.
30957        
30958                  b) A very small toolbar is essentially unusable. On scrolling of X, we keep updating the width of the toolbar so that it can grow to fit the available space.
30959        
30960                  Note: this is entirely determined on the number of items in the menu and the toolbar, because when they wrap, that's what causes the height. Also, having multiple toolbars can also make it higher.
30961                  */
30962                    const availableWidth = window.innerWidth - (left - scroll.left);
30963                    const width = Math.max(Math.min(toolbarWidth, availableWidth), minimumToolbarWidth);
30964                    if (availableWidth < toolbarWidth) {
30965                        set$7(mainUi.outerContainer.element, 'width', width + 'px');
30966                    }
30967                    return {
30968                        width: width + 'px'
30969                    };
30970                }).getOr({ width: 'max-content' });
30971                const baseProperties = {
30972                    position: 'absolute',
30973                    left: Math.round(left) + 'px',
30974                    top: getTop() + 'px'
30975                };
30976                setAll(mainUi.outerContainer.element, {
30977                    ...baseProperties,
30978                    ...widthProperties
30979                });
30980            });
30981        };
30982        // This would return Optional.none, for ui_mode: combined, which will fallback to the default co
30982de block
30983        // For ui_mode: split, the offsetParent would be the body if there were no relative div set as parent
30984        const getOffsetParent$1 = (editor, element) => isSplitUiMode(editor) ? getOffsetParent(element) : Optional.none();
30985        const repositionPopups$1 = () => {
30986            each$1(uiMotherships, (m) => {
30987                m.broadcastOn([repositionPopups()], {});
30988            });
30989        };
30990        const restoreOuterContainerWidth = () => {
30991            /*
30992            Editors can be placed so far to the right that their left position is beyond the window width. This causes problems with flex-wrap. To solve this, set a width style on the container.
30993            Natural width of the container needs to be calculated first.
30994            */
30995            if (!useFixedToolbarContainer) {
30996                const toolbarCurrentRightsidePosition = absolute$3(mainUi.outerContainer.element).left + getOuter(mainUi.outerContainer.element);
30997                /*
30998                Check the width if we are within X number of pixels to the edge ( or above ). Also check if we have the width-value set.
30999                This helps handling the issue where it goes from having a width set ( because it's too wide ) to going so far from the edge it no longer triggers the problem. Common when the width is changed by test.
31000                */
31001                if (toolbarCurrentRightsidePosition >= window.innerWidth - maximumDistanceToEdge || getRaw(mainUi.outerContainer.element, 'width').isSome()) {
31002                    set$7(mainUi.outerContainer.element, 'position', 'absolute');
31003                    set$7(mainUi.outerContainer.element, 'left', '0px');
31004                    remove$6(mainUi.outerContainer.element, 'width');
31005                    return true;
31006                }
31007            }
31008            return false;
31009        };
31010        const update = (stickyAction) => {
31011            // Skip updating the ui if it's hidden
31012            if (!isVisible()) {
31013                return;
31014            }
31015            // Handles positioning, docking and SplitToolbar (more drawer) behaviour. Modes:
31016            // 1. Basic inline: does positioning and docking
31017            // 2. Inline + more drawer: does positioning, docking and SplitToolbar
31018            // 3. Inline + fixed_toolbar_container: does nothing
31019            // 4. Inline + fixed_toolbar_container + more drawer: does SplitToolbar
31020            // Update the max width, as the body width may have changed
31021            if (!useFixedToolbarContainer) {
31022                updateChromeWidth();
31023            }
31024            const prevScroll = get$b();
31025            const isOuterContainerWidthRestored = useFixedToolbarContainer ? false : restoreOuterContainerWidth();
31026            /*
31027              Refresh split toolbar. Before calling refresh, we need to make sure that we have the full width (through restoreOuterContainerWidth above), otherwise too much will be put in the overflow drawer.
31028              A split toolbar requires a calculation to see what ends up in the "more drawer". When we don't have a split toolbar, then there is no reason to refresh the toolbar when the size changes.
31029            */
31030            if (isSplitToolbar) {
31031                OuterContainer.refreshToolbar(mainUi.outerContainer);
31032            }
31033            // Positioning
31034            if (!useFixedToolbarContainer) {
31035                // Getting the current scroll as the previous step may have reset the scroll,
31036                // We also want calculation based on the previous scroll, then restoring the scroll when everything is set.
31037                const currentScroll = get$b();
31038                const optScroll = someIf(prevScroll.left !== currentScroll.left, prevScroll);
31039                // This will position the container in the right spot.
31040                updateChromePosition(isOuterContainerWidthRestored, optScroll);
31041                // Restore scroll left position only if they are different, keeping the current scroll top, that shouldn't be changed
31042                optScroll.each((scroll) => {
31043                    to(scroll.left, currentScroll.top);
31044                });
31045            }
31046            // Docking
31047            if (isSticky) {
31048                floatContainer.on(stickyAction);
31049            }
31050            // Floating toolbar
31051            repositionPopups$1();
31052        };
31053        const doUpdateMode = () => {
31054            // Skip updating the mode if the toolbar is hidden, is
31055            // using a fixed container or has sticky toolbars disabled
31056            if (useFixedToolbarContainer || !isSticky || !isVisible()) {
31057                return false;
vendor: 10,956 bytes, lines 31058-31257
31058            }
31059            return floatContainer.get().exists((fc) => {
31060                const currentMode = headerBackstage.getDockingMode();
31061                const newMode = calcMode(fc);
31062                // Note: the docking mode will only be able to change when the `toolbar_location`
31063                // is set to "auto".
31064                if (newMode !== currentMode) {
31065                    setupMode(newMode);
31066                    return true;
31067                }
31068                else {
31069                    return false;
31070                }
31071            });
31072        };
31073        const show = () => {
31074            visible.set(true);
31075            set$7(mainUi.outerContainer.element, 'display', 'flex');
31076            DOM.addClass(editor.getBody(), 'mce-edit-focus');
31077            each$1(uiMotherships, (m) => {
31078                // We remove the display style when showing, because when hiding, we set it to "none"
31079                remove$6(m.element, 'display');
31080            });
31081            doUpdateMode();
31082            if (isSplitUiMode(editor)) {
31083                // When the toolbar is shown, then hidden and when the page is then scrolled,
31084                // the toolbar is set to docked, which shouldn't be as it should be static position
31085                // calling reset here, to reset the state.
31086                // Another case would be when the toolbar is shown initially (with location_bottom)
31087                // we don't want to dock the toolbar, calling Docking.refresh
31088                update((elem) => Docking.isDocked(elem) ? Docking.reset(elem) : Docking.refresh(elem));
31089            }
31090            else {
31091                // Even if we aren't updating the docking mode, we still want to reposition
31092                // the Ui. NOTE: We are using Docking.refresh here, rather than Docking.reset. This
31093                // means it should keep whatever its "previous" coordinates were, and will just
31094                // behave like the window was scrolled again, and Docking needs to work out if it
31095                // is going to dock / undock
31096                update(Docking.refresh);
31097            }
31098        };
31099        const hide = () => {
31100            visible.set(false);
31101            set$7(mainUi.outerContainer.element, 'display', 'none');
31102            DOM.removeClass(editor.getBody(), 'mce-edit-focus');
31103            each$1(uiMotherships, (m) => {
31104                set$7(m.element, 'display', 'none');
31105            });
31106        };
31107        const updateMode = () => {
31108            const changedMode = doUpdateMode();
31109            // If the docking mode has changed due to the update, we want to reset
31110            // docking. This will clear any prior stored positions
31111            if (changedMode) {
31112                update(Docking.reset);
31113            }
31114        };
31115        return {
31116            isVisible,
31117            isPositionedAtTop,
31118            show,
31119            hide,
31120            update,
31121            updateMode,
31122            repositionPopups: repositionPopups$1
31123        };
31124    };
31125
31126    const getTargetPosAndBounds = (targetElm, isToolbarTop) => {
31127        const bounds = box$1(targetElm);
31128        return {
31129            pos: isToolbarTop ? bounds.y : bounds.bottom,
31130            bounds
31131        };
31132    };
31133    const setupEvents = (editor, targetElm, ui, toolbarPersist) => {
31134        const prevPosAndBounds = Cell(getTargetPosAndBounds(targetElm, ui.isPositionedAtTop()));
31135        const resizeContent = (e) => {
31136            const { pos, bounds } = getTargetPosAndBounds(targetElm, ui.isPositionedAtTop());
31137            const { pos: prevPos, bounds: prevBounds } = prevPosAndBounds.get();
31138            const hasResized = bounds.height !== prevBounds.height || bounds.width !== prevBounds.width;
31139            prevPosAndBounds.set({ pos, bounds });
31140            if (hasResized) {
31141                fireResizeContent(editor, e);
31142            }
31143            if (ui.isVisible()) {
31144                if (prevPos !== pos) {
31145                    // The proposed toolbar location has moved, so we need to reposition the Ui. This might
31146                    // include things like refreshing any Docking / stickiness for the toolbars
31147                    ui.update(Docking.reset);
31148                }
31149                else if (hasResized) {
31150                    // The proposed toolbar location hasn't moved, but the dimensions of the editor have changed.
31151                    // We use "updateMode" here instead of "update". The primary reason is that "updateMode"
31152                    // only repositions the Ui if it has detected that the docking mode needs to change, which
31153                    // will only happen with `toolbar_location` is set to `auto`.
31154                    ui.updateMode();
31155                    // NOTE: This repositionPopups call is going to be a duplicate if "updateMode" identifies
31156                    // that the mode has changed. We probably need to make it a bit more granular .. so
31157                    // that we can just query if the mode has changed. Otherwise, we're going to end up with
31158                    // situations like this where we are doing a potentially expensive operation
31159                    // (repositionPopups) more than once.
31160                    ui.repositionPopups();
31161                }
31162            }
31163        };
31164        if (!toolbarPersist) {
31165            editor.on('activate', ui.show);
31166            editor.on('deactivate', ui.hide);
31167        }
31168        // For both the initial load (SkinLoaded) and any resizes (ResizeWindow), we want to
31169        // update the positions of the Ui elements (and reset Docking / stickiness)
31170        editor.on('SkinLoaded ResizeWindow', () => ui.update(Docking.reset));
31171        editor.on('NodeChange keydown', (e) => {
31172            requestAnimationFrame(() => resizeContent(e));
31173        });
31174        // When the page has been scrolled, we need to update any docking positions. We also
31175        // want to reposition all the Ui elements if required.
31176        let lastScrollX = 0;
31177        const updateUi = last(() => ui.update(Docking.refresh), 33);
31178        editor.on('ScrollWindow', () => {
31179            const newScrollX = get$b().left;
31180            if (newScrollX !== lastScrollX) {
31181                lastScrollX = newScrollX;
31182                updateUi.throttle();
31183            }
31184            ui.updateMode();
31185        });
31186        if (isSplitUiMode(editor)) {
31187            editor.on('ElementScroll', (_args) => {
31188                // When the scroller containing the editor scrolls, update the Ui positions
31189                ui.update(Docking.refresh);
31190            });
31191        }
31192        // Bind to async load events and trigger a content resize event if the size has changed
31193        // This is handling resizing based on anything loading inside the content (e.g. img tags)
31194        const elementLoad = unbindable();
31195        elementLoad.set(capture(SugarElement.fromDom(editor.getBody()), 'load', (e) => resizeContent(e.raw)));
31196        editor.on('remove', () => {
31197            elementLoad.clear();
31198        });
31199    };
31200    const render = (editor, uiRefs, rawUiConfig, backstage, args) => {
31201        const { mainUi } = uiRefs;
31202        // This is used to store the reference to the header part of OuterContainer, which is
31203        // *not* created by this module. This reference is used to make sure that we only bind
31204        // events for an inline container *once* ... because our show function is just the
31205        // InlineHeader's show function if this reference is already set. We pass it through to
31206        // InlineHeader because InlineHeader will depend on it.
31207        const floatContainer = value$2();
31208        const targetElm = SugarElement.fromDom(args.targetNode);
31209        const ui = InlineHeader(editor, targetElm, uiRefs, backstage, floatContainer);
31210        const toolbarPersist = isToolbarPersist(editor);
31211        // eslint-disable-next-line @typescript-eslint/no-floating-promises
31212        inline(editor);
31213        const render = () => {
31214            // Because we set the floatContainer immediately afterwards, this is just telling us
31215            // if we have already called this code (e.g. show, hide, show) - then don't do anything
31216            // more than show. It's a pretty messy way of ensuring that all the code that follows
31217            // this `if` block is only executed once (setting up events etc.). So the first call
31218            // to `render` will execute it, but the second call won't. This `render` function is
31219            // used for most of the "show" handlers here, so the function can be invoked either
31220            // for the first time, or or just because something is being show again, after being
31221            // toggled to hidden earlier.
31222            if (floatContainer.isSet()) {
31223                ui.show();
31224                return;
31225            }
31226            // Set up the header part of OuterContainer. Once configured, the `InlineHeader` code
31227            // will use it when setting up and updating the Ui. This module uses it mainly just to
31228            // allow us to call `render` multiple times, but only have it execute the setup code once.
31229            floatContainer.set(OuterContainer.getHeader(mainUi.outerContainer).getOrDie());
31230            // `uiContainer` handles *where* the motherhips get added by default. Currently, uiContainer
31231            // will mostly be the <body> of the document (unless it's a ShadowRoot). When using ui_mode: split,
31232            // the main mothership (which includes the toolbar) and popup sinks will be added as siblings of
31233            // the target element, so that they have the same scrolling context / environment
31234            const uiContainer = getUiContainer(editor);
31235            // Position the motherships based on the editor Ui options.
31236            if (isSplitUiMode(editor)) {
31237                attachSystemAfter(targetElm, mainUi.mothership);
31238                // Only in ui_mode: split, do we have a separate popup sink
31239                attachSystemAfter(targetElm, uiRefs.popupUi.mothership);
31240            }
31241            else {
31242                attachSystem(uiContainer, mainUi.mothership);
31243            }
31244            // NOTE: In UiRefs, dialogUi and popupUi refer to the same thing if ui_mode: combined
31245            attachSystem(uiContainer, uiRefs.dialogUi.mothership);
31246            const setup = () => {
31247                // Unlike menubar below which uses OuterContainer directly, this level of abstraction is
31248                // required because of the different types of toolbars available (e.g. multiple vs single)
31249                setToolbar(editor, uiRefs, rawUiConfig, backstage);
31250                OuterContainer.setMenubar(mainUi.outerContainer, identifyMenus(editor, rawUiConfig));
31251                // Initialise the toolbar - set initial positioning then show
31252                ui.show();
31253                setupEvents(editor, targetElm, ui, toolbarPersist);
31254                editor.nodeChanged();
31255            };
31256            if (toolbarPersist) {
31257                // TINY-10482: for `toolbar_persist: true` we need to wait for the skin to be loaded before showing the toolbar/menubar.
31258                // Without this, there's the occasional chance that the toolbar/menubar could be set/shown before the skin has finished
31259                // loading, which causes CSS issues.
31260                editor.once('SkinLoaded', setup);
31261            }
31262            else {
31263                setup();
31264            }
31265        };
31266        editor.on('show', render);
31267        editor.on('hide', ui.hide);
31268        if (!toolbarPersist) {
31269            editor.on('focus', render);
31270            editor.on('blur', ui.hide);
31271        }
31272        editor.on('init', () => {
31273            if (editor.hasFocus() || toolbarPersist) {
31274                render();
31275            }
31276        });
31277        setupEventsForUi(editor, uiRefs);
31278        const api = {
31279            show: render,
31280            hide: ui.hide,
31281            setEnabled: (state) => {
31282                const eventType = state ? 'setEnabled' : 'setDisabled';
31283                broadcastEvents(uiRefs, eventType);
31284            },
31285            isEnabled: () => !Disabling.isDisabled(mainUi.outerContainer)
31286        };
31287        return {
31288            editorContainer: mainUi.outerContainer.element.dom,
31289            api
31290        };
31291    };
31292
31293    var Inline = /*#__PURE__*/Object.freeze({
31294        __proto__: null,
31295        render: render
31296    });
31297
31298    const LazyUiReferences = () => {
31299        const dialogUi = value$2();
31300        const popupUi = value$2();
31301        const mainUi = value$2();
31302        const lazyGetInOuterOrDie = (label, f) => () => mainUi.get().bind((oc) => f(oc.outerContainer)).getOrDie(`Could not find ${label} element in OuterContainer`);
31303        // TINY-9226: If the motherships are the same, return just the dialog Ui of them (ui_mode: combined mode)
31304        const getUiMotherships = () => {
31305            const optDialogMothership = dialogUi.get().map((ui) => ui.mothership);
31306            const optPopupMothership = popupUi.get().map((ui) => ui.mothership);
31307            return optDialogMothership.fold(() => optPopupMothership.toArray(), (dm) => optPopupMothership.fold(() => [dm], (pm) => eq(dm.element, pm.element) ? [dm] : [dm, pm]));
31308        };
31309        return {
31310            dialogUi,
31311            popupUi,
31312            mainUi,
31313            getUiMotherships,
31314            lazyGetInOuterOrDie
31315        };
31316    };
31317
31318    // TODO: Find a better way of doing this. We probably don't want to just listen to
31319    // editor events. Having an API available like WindowManager would be the best option
31320    const showContextToolbarEvent = 'contexttoolbar-show';
31321    const hideContextToolbarEvent = 'contexttoolbar-hide';
31322
31323    const getFormApi = (input, valueState, focusfallbackElement) => {
31324        return ({
31325            setInputEnabled: (state) => {
31326                if (!state && focusfallbackElement) {
31327                    focus$4(focusfallbackElement);
31328                }
31329                Disabling.set(input, !state);
31330            },
31331            isInputEnabled: () => !Disabling.isDisabled(input),
31332            hide: () => {
31333                emit(input, sandboxClose());
31334            },
31335            back: () => {
31336                emit(input, backSlideEvent);
31337            },
31338            getValue: () => {
31339                return valueState.get().getOrThunk(() => Representing.getValue(input));
31340            },
31341            setValue: (value) => {
31342                if (input.getSystem().isConnected()) {
31343                    Representing.setValue(input, value);
31344                }
31345                else {
31346                    valueState.set(value);
31347                }
31348            }
31349        });
31350    };
31351    const getFormParentApi = (comp, valueState, focusfallbackElement) => {
31352        const parent$1 = parent(comp.element);
31353        const parentCompOpt = parent$1.bind((parent) => comp.getSystem().getByDom(parent).toOptional());
31354        return getFormApi(parentCompOpt.getOr(comp), valueState, focusfallbackElement);
31355    };
31356
31357    const runOnExecute = (memInput, original, valueState) => run$1(internalToolbarButtonExecute, (comp, se) => {
31358        const input = memInput.get(comp);
31359        const formApi = getFormApi(input, valueState, comp.element);
31360        original.onAction(formApi, se.event.buttonApi);
31361    });
31362    const renderContextButton = (memInput, button, providers, valueState) => {
31363        const { primary, ...rest } = button.original;
31364        const bridged = getOrDie(createToolbarButton({
31365            ...rest,
31366            type: 'button',
31367            onAction: noop
31368        }));
31369        return renderToolbarButtonWith(bridged, providers, [
31370            runOnExecute(memInput, button, valueState)
31371        ]);
31372    };
31373    const renderContextToggleButton = (memInput, button, providers, valueState) => {
31374        const { primary, ...rest } = button.original;
31375        const bridged = getOrDie(createToggleButton({
31376            ...rest,
31377            type: 'togglebutton',
31378            onAction: noop
31379        }));
31380        return renderToolbarToggleButtonWith(bridged, providers, [
31381            runOnExecute(memInput, button, valueState)
31382        ]);
31383    };
31384    const isToggleButton = (button) => button.type === 'contextformtogglebutton';
31385    const generateOne = (memInput, button, providersBackstage, valueState) => {
31386        if (isToggleButton(button)) {
31387            return renderContextToggleButton(memInput, button, providersBackstage, valueState);
31388        }
31389        else {
31390            return renderContextButton(memInput, button, providersBackstage, valueState);
31391        }
31392    };
31393    const generate = (memInput, buttons, providersBackstage, valueState) => {
31394        const mementos = map$2(buttons, (button) => record(generateOne(memInput, button, providersBackstage, valueState)));
31395        const asSpecs = () => map$2(mementos, (mem) => mem.asSpec());
31396        const findPrimary = (compInSystem) => findMap(buttons, (button, i) => {
31397            if (button.primary) {
31398                return Optional.from(mementos[i]).bind((mem) => mem.getOpt(compInSystem)).filter(not(Disabling.isDisabled));
31399            }
31400            else {
31401                return Optional.none();
31402            }
31403        });
31404        return {
31405            asSpecs,
31406            findPrimary
31407        };
31408    };
31409
31410    const renderContextFormSizeInput = (ctx, providersBackstage, onEnter, valueState) => {
31411        const { width, height } = ctx.initValue();
31412        let converter = noSizeConversion;
31413        const enabled = true;
31414        const ratioEvent = generate$6('ratio-event');
31415        const getApi = (comp) => getFormApi(comp, valueState);
31416        const makeIcon = (iconName) => render$4(iconName, { tag: 'span', classes: ['tox-icon', 'tox-lock-icon__' + iconName] }, providersBackstage.icons);
31417        const disabled = () => !enabled;
31418        const label = ctx.label.getOr('Constrain proportions');
31419        const translatedLabel = providersBackstage.translate(label);
31420        const pLock = FormCoupledInputs.parts.lock({
31421            dom: {
31422                tag: 'button',
31423                classes: ['tox-lock', 'tox-lock-context-form-size-input', 'tox-button', 'tox-button--naked', 'tox-button--icon'],
31424                attributes: {
31425                    'aria-label': translatedLabel,
31426                    'data-mce-name': label
31427                }
31428            },
31429            components: [
31430                makeIcon('lock'),
31431                makeIcon('unlock')
31432            ],
31433            buttonBehaviours: derive$1([
31434                Disabling.config({ disabled }),
31435                Tabstopping.config({}),
31436                Tooltipping.config(providersBackstage.tooltips.getConfig({
31437                    tooltipText: translatedLabel
31438                }))
31439            ])
31440        });
31441        const formGroup = (components) => ({
31442            dom: {
31443                tag: 'div',
31444                classes: ['tox-context-form__group']
31445            },
31446            components
31447        });
31448        const goToParent = (comp) => {
31449            const focussableWrapperOpt = ancestor$1(comp.element, 'div.tox-focusable-wrapper');
31450            return focussableWrapperOpt.fold(Optional.none, (focussableWrapper) => {
31451                focus$4(focussableWrapper);
31452                return Optional.some(true);
31453            });
31454        };
31455        const getFieldPart = (isField1) => FormField.parts.field({
31456            factory: Input,
31457            inputClasses: ['tox-textfield', 'tox-toolbar-textfield', 'tox-textfield-size'],
31458            data: isField1 ? width : height,
31459            inputBehaviours: derive$1([
31460                Disabling.config({ disabled }),
31461                Tabstopping.config({}),
31462                config('size-input-toolbar-events', [
31463                    run$1(focusin(), (component, _simulatedEvent) => {
31464                        emitWith(component, ratioEvent, { isField1 });
31465                    })
31466                ]),
31467                Keying.config({ mode: 'special', onEnter, onEscape: goToParent })
31468            ]),
31469            selectOnFocus: false
31470        });
31471        const getLabel = (label) => ({
31472            dom: {
31473                tag: 'label',
31474                classes: ['tox-label']
31475            },
31476            components: [
31477                text$2(providersBackstage.translate(label))
31478            ]
31479        });
31480        const focusableWrapper = (field) => ({
31481            dom: {
31482                tag: 'div',
31483                classes: ['tox-focusable-wrapper', 'tox-toolbar-nav-item'],
31484            },
31485            components: [field],
31486            behaviours: derive$1([
31487                Tabstopping.config({}),
31488                Focusing.config({}),
31489                Keying.config({
31490                    mode: 'special',
31491                    onEnter: (comp) => {
31492                        const focussableInputOpt = descendant(comp.element, 'input');
31493                        return focussableInputOpt.fold(Optional.none, (focussableInput) => {
31494                            focus$4(focussableInput);
31495                            return Optional.some(true);
31496                        });
31497                    }
31498                })
31499            ])
31500        });
31501        const widthField = focusableWrapper(FormCoupledInputs.parts.field1(formGroup([FormField.parts.label(getLabel('Width:')), getFieldPart(true)])));
31502        const heightField = focusableWrapper(FormCoupledInputs.parts.field2(formGroup([FormField.parts.label(getLabel('Height:')), getFieldPart(false)])));
31503        const editorOffCell = Cell(noop);
31504        const controlLifecycleHandlers = [
31505            onControlAttached({
31506                onBeforeSetup: (comp) => descendant(comp.element, 'input').each(focus$4),
31507                onSetup: ctx.onSetup,
31508                getApi
31509            }, editorOffCell),
31510            onContextFormControlDetached({ getApi }, editorOffCell, valueState),
31511        ];
31512        return FormCoupledInputs.sketch({
31513            dom: {
31514                tag: 'div',
31515                classes: ['tox-context-form__group']
31516            },
31517            components: [
31518                // NOTE: Form coupled inputs to the FormField.sketch themselves.
31519                widthField,
31520                formGroup([
31521                    pLock
31522                ]),
31523                heightField
31524            ],
31525            field1Name: 'width',
31526            field2Name: 'height',
31527            locked: true,
31528            markers: {
31529                lockClass: 'tox-locked'
31530            },
31531            onLockedChange: (current, other, _lock) => {
31532                parseSize(Representing.getValue(current)).each((size) => {
31533                    converter(size).each((newSize) => {
31534                        Representing.setValue(other, formatSize(newSize));
31535                    });
31536                });
31537            },
31538            onInput: (current) => emit(current, formInputEvent),
31539            coupledFieldBehaviours: derive$1([
31540                Focusing.config({}),
31541                Keying.config({
31542                    mode: 'flow',
31543                    focusInside: FocusInsideModes.OnEnterOrSpaceMode,
31544                    cycles: false,
31545                    selector: 'button, .tox-focusable-wrapper',
31546                }),
31547                Disabling.config({
31548                    disabled,
31549                    onDisabled: (comp) => {
31550                        FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.disable);
31551                        FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.disable);
31552                        FormCoupledInputs.getLock(comp).each(Disabling.disable);
31553                    },
31554                    onEnabled: (comp) => {
31555                        FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.enable);
31556                        FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.enable);
31557                        FormCoupledInputs.getLock(comp).each(Disabling.enable);
31558                    }
31559                }),
31560                toggleOnReceive(() => providersBackstage.checkUiComponentContext('mode:design')),
31561                config('size-input-toolbar-events2', [
31562                    run$1(ratioEvent, (component, simulatedEvent) => {
31563                        const isField1 = simulatedEvent.event.isField1;
31564                        const optCurrent = isField1 ? FormCoupledInputs.getField1(component) : FormCoupledInputs.getField2(component);
31565                        const optOther = isField1 ? FormCoupledInputs.getField2(component) : FormCoupledInputs.getField1(component);
31566                        const value1 = optCurrent.map(Representing.getValue).getOr('');
31567                        const value2 = optOther.map(Representing.getValue).getOr('');
31568                        converter = makeRatioConverter(value1, value2);
31569                    }),
31570                    run$1(formInputEvent, (input) => ctx.onInput(getApi(input))),
31571                    ...controlLifecycleHandlers,
31572                ])
31573            ])
31574        });
31575    };
31576
31577    const createContextFormFieldFromParts = (pLabel, pField, providers) => FormField.sketch({
31578        dom: {
31579            tag: 'div',
31580            classes: ['tox-context-form__group']
31581        },
31582        components: [...pLabel.toArray(), pField],
31583        fieldBehaviours: derive$1([
31584            Disabling.config({
31585                disabled: () => providers.checkUiComponentContext('mode:design').shouldDisable,
31586                onDisabled: (comp) => {
31587                    focusParent(comp);
31588                    FormField.getField(comp).each(Disabling.disable);
31589                },
31590                onEnabled: (comp) => {
31591                    FormField.getField(comp).each(Disabling.enable);
31592                }
31593            }),
31594        ])
31595    });
31596
31597    const renderContextFormSliderInput = (ctx, providers, onEnter, valueState) => {
31598        const editorOffCell = Cell(noop);
31599        const getApi = (comp) => getFormParentApi(comp, valueState);
31600        const pLabel = ctx.label.map((label) => FormField.parts.label({
31601            dom: { tag: 'label', classes: ['tox-label'] },
31602            components: [text$2(providers.translate(label))]
31603        }));
31604        const pField = FormField.parts.field({
31605            factory: Input,
31606            type: 'range',
31607            inputClasses: ['tox-toolbar-slider__input', 'tox-toolbar-nav-item'],
31608            inputAttributes: {
31609                min: String(ctx.min()),
31610                max: String(ctx.max())
31611            },
31612            data: ctx.initValue().toString(),
31613            fromInputValue: (value) => toFloat(value).getOr(ctx.min()),
31614            toInputValue: (value) => String(value),
31615            inputBehaviours: derive$1([
31616                Disabling.config({
31617                    disabled: () => providers.checkUiComponentContext('mode:design').shouldDisable
31618                }),
31619                toggleOnReceive(() => providers.checkUiComponentContext('mode:design')),
31620                Keying.config({
31621                    mode: 'special',
31622                    onEnter,
31623                    // These two lines need to be tested. They are about left and right bypassing
31624                    // any keyboard handling, and allowing left and right to be processed by the input
31625                    // Maybe this should go in an alloy sketch for Input?
31626                    onLeft: (comp, se) => {
31627                        se.cut();
31628                        return Optional.none();
31629                    },
31630                    onRight: (comp, se) => {
31631                        se.cut();
31632                        return Optional.none();
31633                    }
31634                }),
31635                config('slider-events', [
31636                    onControlAttached({
31637                        onSetup: ctx.onSetup,
31638                        getApi,
31639                        onBeforeSetup: Keying.focusIn
31640                    }, editorOffCell),
31641                    onContextFormControlDetached({ getApi }, editorOffCell, valueState),
31642                    run$1(input(), (comp) => {
31643                        ctx.onInput(getApi(comp));
31644                    })
31645                ])
31646            ])
31647        });
31648        return createContextFormFieldFromParts(pLabel, pField, providers);
31649    };
31650
31651    const renderContextFormTextInput = (ctx, providers, onEnter, valueState) => {
31652        const editorOffCell = Cell(noop);
31653        const getFormApi = (comp) => getFormParentApi(comp, valueState);
31654        const pLabel = ctx.label.map((label) => FormField.parts.label({
31655            dom: { tag: 'label', classes: ['tox-label'] },
31656            components: [text$2(providers.translate(label))]
31657        }));
31658        const placeholder = ctx.placeholder.map((p) => ({ placeholder: providers.translate(p) })).getOr({});
31659        const inputAttributes = {
31660            ...placeholder,
31661        };
31662        const pField = FormField.parts.field({
31663            factory: Input,
31664            inputClasses: ['tox-toolbar-textfield', 'tox-toolbar-nav-item'],
31665            inputAttributes,
31666            data: ctx.initValue(),
31667            selectOnFocus: true,
31668            inputBehaviours: derive$1([
31669                Disabling.config({
31670                    disabled: () => providers.checkUiComponentContext('mode:design').shouldDisable
31671                }),
31672                toggleOnReceive(() => providers.checkUiComponentContext('mode:design')),
31673                Keying.config({
31674                    mode: 'special',
31675                    onEnter,
31676                    // These two lines need to be tested. They are about left and right bypassing
31677                    // any keyboard handling, and allowing left and right to be processed by the input
31678                    // Maybe this should go in an alloy sketch for Input?
31679                    onLeft: (comp, se) => {
31680                        se.cut();
31681                        return Optional.none();
31682                    },
31683                    onRight: (comp, se) => {
31684                        se.cut();
31685                        return Optional.none();
31686                    }
31687                }),
31688                config('input-events', [
31689                    onControlAttached({
31690                        onSetup: ctx.onSetup,
31691                        getApi: (comp) => {
31692                            const closestFocussableOpt = ancestor$1(comp.element, '.tox-toolbar').bind((toolbar) => descendant(toolbar, 'button:enabled'));
31693                            return closestFocussableOpt.fold(() => getFormParentApi(comp, valueState), (closestFocussable) => getFormParentApi(comp, valueState, closestFocussable));
31694                        },
31695                        onBeforeSetup: Keying.focusIn
31696                    }, editorOffCell),
31697                    onContextFormControlDetached({ getApi: getFormApi }, editorOffCell, valueState),
31698                    run$1(input(), (comp) => {
31699                        ctx.onInput(getFormApi(comp));
31700                    })
31701                ])
31702            ])
31703        });
31704        return createContextFormFieldFromParts(pLabel, pField, providers);
31705    };
31706
31707    const buildInitGroup = (f, ctx, providers) => {
31708        const valueState = value$2();
31709        const onEnter = (input) => {
31710            return startCommands.findPrimary(input).orThunk(() => endCommands.findPrimary(input)).map((primary) => {
31711                emitExecute(primary);
31712                return true;
31713            });
31714        };
31715        const memInput = record(f(providers, onEnter, valueState));
31716        const commandParts = partition$3(ctx.commands, (command) => command.align === 'start');
31717        const startCommands = generate(memInput, commandParts.pass, providers, valueState);
31718        const endCommands = generate(memInput, commandParts.fail, providers, valueState);
31719        return filter$2([
31720            {
31721                title: Optional.none(),
31722                label: Optional.none(),
31723                items: startCommands.asSpecs()
31724            },
31725            {
31726                title: Optional.none(),
31727                label: Optional.none(),
31728                items: [memInput.asSpec()]
31729            },
31730            {
31731                title: Optional.none(),
31732                label: Optional.none(),
31733                items: endCommands.asSpecs()
31734            }
31735        ], (group) => group.items.length > 0);
31736    };
31737    const buildInitGroups = (ctx, providers) => {
31738        switch (ctx.type) {
31739            case 'contextform': return buildInitGroup(curry(renderContextFormTextInput, ctx), ctx, providers);
31740            case 'contextsliderform': return buildInitGroup(curry(renderContextFormSliderInput, ctx), ctx, providers);
31741            case 'contextsizeinputform': return buildInitGroup(curry(renderContextFormSizeInput, ctx), ctx, providers);
31742        }
31743    };
31744    const renderContextForm = (toolbarType, ctx, providers) => renderToolbar({
31745        type: toolbarType,
31746        uid: generate$6('context-toolbar'),
31747        initGroups: buildInitGroups(ctx, providers),
31748        onEscape: Optional.none,
31749        cyclicKeying: true,
31750        providers
31751    });
31752    const ContextForm = {
31753        renderContextForm,
31754        buildInitGroups
31755    };
31756
31757    // The "threshold" here is the amount of overlap. To make the overlap check
31758    // be more permissive (return true for 'almost' an overlap), use a negative
31759    // threshold value
31760    const isVerticalOverlap = (a, b, threshold) => b.bottom - a.y >= threshold && a.bottom - b.y >= threshold;
31761    const getRangeRect = (rng) => {
31762        const rect = rng.getBoundingClientRect();
31763        // Some ranges (eg <td><br></td>) will return a 0x0 rect, so we'll need to calculate it from the leaf instead
31764        if (rect.height <= 0 && rect.width <= 0) {
31765            const leaf$1 = leaf(SugarElement.fromDom(rng.startContainer), rng.startOffset).element;
31766            const elm = isText(leaf$1) ? parent(leaf$1) : Optional.some(leaf$1);
31767            return elm.filter(isElement$1)
31768                .map((e) => e.dom.getBoundingClientRect())
31769                // We have nothing valid, so just fallback to the original rect
31770                .getOr(rect);
31771        }
31772        else {
31773            return rect;
31774        }
31775    };
31776    const getSelectionBounds = (editor) => {
31777        const rng = editor.selection.getRng();
vendor: 5,577 bytes, lines 31778-31882
31778        const rect = getRangeRect(rng);
31779        if (editor.inline) {
31780            const scroll = get$b();
31781            return bounds(scroll.left + rect.left, scroll.top + rect.top, rect.width, rect.height);
31782        }
31783        else {
31784            // Translate to the top level document, as rect is relative to the iframe viewport
31785            const bodyPos = absolute$2(SugarElement.fromDom(editor.getBody()));
31786            return bounds(bodyPos.x + rect.left, bodyPos.y + rect.top, rect.width, rect.height);
31787        }
31788    };
31789    const getAnchorElementBounds = (editor, lastElement) => lastElement
31790        .filter((elem) => inBody(elem) && isHTMLElement(elem))
31791        .map(absolute$2)
31792        .getOrThunk(() => getSelectionBounds(editor));
31793    const getHorizontalBounds = (contentAreaBox, viewportBounds, margin) => {
31794        const x = Math.max(contentAreaBox.x + margin, viewportBounds.x);
31795        const right = Math.min(contentAreaBox.right - margin, viewportBounds.right);
31796        return { x, width: right - x };
31797    };
31798    const getVerticalBounds = (editor, contentAreaBox, viewportBounds, isToolbarLocationTop, toolbarType, margin) => {
31799        const container = SugarElement.fromDom(editor.getContainer());
31800        const header = descendant(container, '.tox-editor-header').getOr(container);
31801        const headerBox = box$1(header);
31802        const isToolbarBelowContentArea = headerBox.y >= contentAreaBox.bottom;
31803        const isToolbarAbove = isToolbarLocationTop && !isToolbarBelowContentArea;
31804        // Scenario toolbar top & inline: Bottom of the header -> Bottom of the viewport
31805        if (editor.inline && isToolbarAbove) {
31806            return {
31807                y: Math.max(headerBox.bottom + margin, viewportBounds.y),
31808                bottom: viewportBounds.bottom
31809            };
31810        }
31811        // Scenario toolbar top & inline: Top of the viewport -> Top of the header
31812        if (editor.inline && !isToolbarAbove) {
31813            return {
31814                y: viewportBounds.y,
31815                bottom: Math.min(headerBox.y - margin, viewportBounds.bottom)
31816            };
31817        }
31818        // Allow line based context toolbar to overlap the statusbar
31819        const containerBounds = toolbarType === 'line' ? box$1(container) : contentAreaBox;
31820        // Scenario toolbar bottom & Iframe: Bottom of the header -> Bottom of the editor container
31821        if (isToolbarAbove) {
31822            return {
31823                y: Math.max(headerBox.bottom + margin, viewportBounds.y),
31824                bottom: Math.min(containerBounds.bottom - margin, viewportBounds.bottom)
31825            };
31826        }
31827        // Scenario toolbar bottom & Iframe: Top of the editor container -> Top of the header
31828        return {
31829            y: Math.max(containerBounds.y + margin, viewportBounds.y),
31830            bottom: Math.min(headerBox.y - margin, viewportBounds.bottom)
31831        };
31832    };
31833    const getContextToolbarBounds = (editor, sharedBackstage, toolbarType, margin = 0) => {
31834        const viewportBounds = getBounds$1(window);
31835        const contentAreaBox = box$1(SugarElement.fromDom(editor.getContentAreaContainer()));
31836        const toolbarOrMenubarEnabled = isMenubarEnabled(editor) || isToolbarEnabled(editor) || isMultipleToolbars(editor);
31837        const { x, width } = getHorizontalBounds(contentAreaBox, viewportBounds, margin);
31838        // Create bounds that lets the context toolbar overflow outside the content area, but remains in the viewport
31839        if (editor.inline && !toolbarOrMenubarEnabled) {
31840            return bounds(x, viewportBounds.y, width, viewportBounds.height);
31841        }
31842        else {
31843            const isToolbarTop = sharedBackstage.header.isPositionedAtTop();
31844            const { y, bottom } = getVerticalBounds(editor, contentAreaBox, viewportBounds, isToolbarTop, toolbarType, margin);
31845            return bounds(x, y, width, bottom - y);
31846        }
31847    };
31848
31849    const bubbleSize$1 = 12;
31850    const bubbleAlignments$1 = {
31851        valignCentre: [],
31852        alignCentre: [],
31853        alignLeft: ['tox-pop--align-left'],
31854        alignRight: ['tox-pop--align-right'],
31855        right: ['tox-pop--right'],
31856        left: ['tox-pop--left'],
31857        bottom: ['tox-pop--bottom'],
31858        top: ['tox-pop--top'],
31859        inset: ['tox-pop--inset']
31860    };
31861    const anchorOverrides = {
31862        maxHeightFunction: expandable$1(),
31863        maxWidthFunction: expandable()
31864    };
31865    const isEntireElementSelected = (editor, elem) => {
31866        const rng = editor.selection.getRng();
31867        const leaf$1 = leaf(SugarElement.fromDom(rng.startContainer), rng.startOffset);
31868        return rng.startContainer === rng.endContainer && rng.startOffset === rng.endOffset - 1 && eq(leaf$1.element, elem);
31869    };
31870    const preservePosition = (elem, position, f) => {
31871        const currentPosition = getRaw(elem, 'position');
31872        set$7(elem, 'position', position);
31873        const result = f(elem);
31874        currentPosition.each((pos) => set$7(elem, 'position', pos));
31875        return result;
31876    };
31877    // Don't use an inset layout when using a selection/line based anchors as it'll cover the content and can't be moved out the way
31878    const shouldUseInsetLayouts = (position) => position === 'node';
31879    /**
31880     * This function is designed to attempt to intelligently detect where the contextbar should be anchored when using an inside
31881     * layout. It will attempt to preserve the previous outside placement when anchoring to the same element. However, when the
31882     * placement is re-triggered (e.g. not triggered by a reposition) and the current editor selection overlaps with the contextbar,
31883     * then the anchoring should flip from the previous position to avoid conflicting with the selection.
31884     */
31885    const determineInsetLayout = (editor, contextbar, elem, data, bounds) => {
31886        const selectionBounds = getSelectionBounds(editor);
31887        const isSameAnchorElement = data.lastElement().exists((prev) => eq(elem, prev));
31888        if (isEntireElementSelected(editor, elem)) {
31889            // The entire anchor element is selected so it'll always overlap with the selection, in which case just
31890            // preserve or show at the top for a new anchor element.
31891            return isSameAnchorElement ? preserve$1 : north$1;
31892        }
31893        else if (isSameAnchorElement) {
31894            // Preserve the position, get the bounds and then see if we have an overlap.
31895            // If overlapping and this wasn't triggered by a reposition then flip the placement
31896            return preservePosition(contextbar, data.getMode(), () => {
31897                // TINY-8890: The negative 20px threshold here was arrived at by considering the use
31898                // case of a table with default heights for the rows. The threshold had to be
31899                // large enough so that the context toolbar would not prevent the user selecting
31900                // in the row containing the context toolbar.
31901                const isOverlapping = isVerticalOverlap(selectionBounds, box$1(contextbar), -20);
31902                return isOverlapping && !data.isReposition() ? flip : preserve$1;
31903            });
31904        }
31905        else {
31906            // Attempt to find the best layout to use that won't cause an overlap for the new anchor element
31907            // Note: In fixed positioning mode we need to translate by adding the scroll pos to get the absolute position
31908            const yBounds = data.getMode() === 'fixed' ? bounds.y + get$b().top : bounds.y;
31909            const contextbarHeight = get$d(contextbar) + bubbleSize$1;
31910            return yBounds + contextbarHeight <= selectionBounds.y ? north$1 : south$1;
31911        }
31912    };
31913    const getAnchorSpec$2 = (editor, mobile, data, position) => {
31914        // IMPORTANT: We lazily determine the layout here so that we only do the calculations if absolutely necessary
31915        const smartInsetLayout = (elem) => (anchor, element, bubbles, placee, bounds) => {
31916            const layout = determineInsetLayout(editor, placee, elem, data, bounds);
31917            // Adjust the anchor box to use the passed y bound coords so that we simulate a "docking" type of behaviour
31918            const newAnchor = {
31919                ...anchor,
31920                y: bounds.y,
31921                height: bounds.height
31922            };
31923            return {
31924                ...layout(newAnchor, element, bubbles, placee, bounds),
31925                // Ensure this is always the preferred option if no outside layouts fit
31926                alwaysFit: true
31927            };
31928        };
31929        const getInsetLayouts = (elem) => shouldUseInsetLayouts(position) ? [smartInsetLayout(elem)] : [];
31930        // On desktop we prioritise north-then-south because it's cleaner, but on mobile we prioritise south to try to avoid overlapping with native context toolbars
31931        const desktopAnchorSpecLayouts = {
31932            onLtr: (elem) => [north$2, south$2, northeast$2, southeast$2, northwest$2, southwest$2].concat(getInsetLayouts(elem)),
31933            onRtl: (elem) => [north$2, south$2, northwest$2, southwest$2, northeast$2, southeast$2].concat(getInsetLayouts(elem))
31934        };
31935        const mobileAnchorSpecLayouts = {
31936            onLtr: (elem) => [south$2, southeast$2, southwest$2, northeast$2, northwest$2, north$2].concat(getInsetLayouts(elem)),
31937            onRtl: (elem) => [south$2, southwest$2, southeast$2, northwest$2, northeast$2, north$2].concat(getInsetLayouts(elem))
31938        };
31939        return mobile ? mobileAnchorSpecLayouts : desktopAnchorSpecLayouts;
31940    };
31941    const getAnchorLayout = (editor, position, isTouch, data) => {
31942        if (position === 'line') {
31943            return {
31944                bubble: nu$6(bubbleSize$1, 0, bubbleAlignments$1),
31945                layouts: {
31946                    onLtr: () => [east$2, west$2],
31947                    onRtl: () => [west$2, east$2]
31948                },
31949                overrides: anchorOverrides
31950            };
31951        }
31952        else {
31953            return {
31954                // Ensure that inset layouts use a 1px bubble since we're hiding the bubble arrow
31955                bubble: nu$6(0, bubbleSize$1, bubbleAlignments$1, 1 / bubbleSize$1),
31956                layouts: getAnchorSpec$2(editor, isTouch, data, position),
31957                overrides: anchorOverrides
31958            };
31959        }
31960    };
31961
31962    const matchTargetWith = (elem, candidates) => {
31963        const ctxs = filter$2(candidates, (toolbarApi) => toolbarApi.predicate(elem.dom));
31964        // TODO: somehow type this properly (Arr.partition can't)
31965        // e.g. here pass is Toolbar.ContextToolbar and fail is Toolbar.ContextForm
31966        const { pass, fail } = partition$3(ctxs, (t) => t.type === 'contexttoolbar');
31967        return {
31968            contextToolbars: pass,
31969            contextForms: fail
31970        };
31971    };
31972    const filterByPositionForStartNode = (toolbars) => {
31973        if (toolbars.length <= 1) {
31974            return toolbars;
31975        }
31976        else {
31977            const doesPositionExist = (value) => exists(toolbars, (t) => t.position === value);
31978            const filterToolbarsByPosition = (value) =>
31978 filter$2(toolbars, (t) => t.position === value);
31979            const hasSelectionToolbars = doesPositionExist('selection');
31980            const hasNodeToolbars = doesPositionExist('node');
31981            if (hasSelectionToolbars || hasNodeToolbars) {
31982                if (hasNodeToolbars && hasSelectionToolbars) {
31983                    // if there's a mix, change the 'selection' toolbars to 'node' so there's no positioning confusion
31984                    const nodeToolbars = filterToolbarsByPosition('node');
31985                    const selectionToolbars = map$2(filterToolbarsByPosition('selection'), (t) => ({ ...t, position: 'node' }));
31986                    return nodeToolbars.concat(selectionToolbars);
31987                }
31988                else {
31989                    return hasSelectionToolbars ? filterToolbarsByPosition('selection') : filterToolbarsByPosition('node');
31990                }
31991            }
31992            else {
31993                return filterToolbarsByPosition('line');
31994            }
31995        }
31996    };
31997    const filterByPositionForAncestorNode = (toolbars) => {
31998        if (toolbars.length <= 1) {
31999            return toolbars;
32000        }
32001        else {
32002            const findPosition = (value) => find$5(toolbars, (t) => t.position === value);
32003            // prioritise position by 'selection' -> 'node' -> 'line'
32004            const basePosition = findPosition('selection')
32005                .orThunk(() => findPosition('node'))
32006                .orThunk(() => findPosition('line'))
32007                .map((t) => t.position);
32008            return basePosition.fold(() => [], (pos) => filter$2(toolbars, (t) => t.position === pos));
32009        }
32010    };
32011    const matchStartNode = (elem, nodeCandidates, editorCandidates) => {
32012        // requirements:
32013        // 1. prioritise context forms over context menus
32014        // 2. prioritise node scoped over editor scoped context forms
32015        // 3. only show max 1 context form
32016        // 4. concatenate all available context toolbars if no context form
32017        const nodeMatches = matchTargetWith(elem, nodeCandidates);
32018        if (nodeMatches.contextForms.length > 0) {
32019            return Optional.some({ elem, toolbars: [nodeMatches.contextForms[0]] });
32020        }
32021        else {
32022            const editorMatches = matchTargetWith(elem, editorCandidates);
32023            if (editorMatches.contextForms.length > 0) {
32024                return Optional.some({ elem, toolbars: [editorMatches.contextForms[0]] });
32025            }
32026            else if (nodeMatches.contextToolbars.length > 0 || editorMatches.contextToolbars.length > 0) {
32027                const toolbars = filterByPositionForStartNode(nodeMatches.contextToolbars.concat(editorMatches.contextToolbars));
32028                return Optional.some({ elem, toolbars });
32029            }
32030            else {
32031                return Optional.none();
32032            }
32033        }
32034    };
32035    const matchAncestor = (isRoot, startNode, scopes) => {
32036        // Don't continue to traverse if the start node is the root node
32037        if (isRoot(startNode)) {
32038            return Optional.none();
32039        }
32040        else {
32041            return ancestor(startNode, (ancestorElem) => {
32042                if (isElement$1(ancestorElem)) {
32043                    const { contextToolbars, contextForms } = matchTargetWith(ancestorElem, scopes.inNodeScope);
32044                    const toolbars = contextForms.length > 0 ? contextForms : filterByPositionForAncestorNode(contextToolbars);
32045                    return toolbars.length > 0 ? Optional.some({ elem: ancestorElem, toolbars }) : Optional.none();
32046                }
32047                else {
32048                    return Optional.none();
32049                }
32050            }, isRoot);
32051        }
32052    };
32053    const lookup$1 = (scopes, editor) => {
32054        const rootElem = SugarElement.fromDom(editor.getBody());
32055        const isRoot = (elem) => eq(elem, rootElem);
32056        const isOutsideRoot = (startNode) => !isRoot(startNode) && !contains(rootElem, startNode);
32057        const startNode = SugarElement.fromDom(editor.selection.getNode());
32058        // Ensure the lookup doesn't start on a parent or sibling element of the root node
32059        if (isOutsideRoot(startNode)) {
32060            return Optional.none();
32061        }
32062        return matchStartNode(startNode, scopes.inNodeScope, scopes.inEditorScope).orThunk(() => matchAncestor(isRoot, startNode, scopes));
32063    };
32064
32065    const categorise = (contextToolbars, navigate) => {
32066        // TODO: Use foldl/foldr and avoid as much mutation.
32067        const forms = {};
32068        const inNodeScope = [];
32069        const inEditorScope = [];
32070        const formNavigators = {};
32071        const lookupTable = {};
32072        const registerForm = (key, toolbarSpec) => {
32073            const contextForm = getOrDie(createContextForm(toolbarSpec));
32074            forms[key] = contextForm;
32075            contextForm.launch.map((launch) => {
32076                // Use the original here (pre-boulder), because using as a the spec for toolbar buttons
32077                formNavigators['form:' + key + ''] = {
32078                    ...toolbarSpec.launch,
32079                    type: (launch.type === 'contextformtogglebutton' ? 'togglebutton' : 'button'),
32080                    onAction: () => {
32081                        navigate(contextForm);
32082                    }
32083                };
32084            });
32085            if (contextForm.scope === 'editor') {
32086                inEditorScope.push(contextForm);
32087            }
32088            else {
32089                inNodeScope.push(contextForm);
32090            }
32091            lookupTable[key] = contextForm;
32092        };
32093        const registerToolbar = (key, toolbarSpec) => {
32094            createContextToolbar(toolbarSpec).each((contextToolbar) => {
32095                if (contextToolbar.launch.isSome()) {
32096                    formNavigators['toolbar:' + key + ''] = {
32097                        ...toolbarSpec.launch,
32098                        type: 'button',
32099                        onAction: () => {
32100                            navigate(contextToolbar);
32101                        }
32102                    };
32103                }
32104                if (toolbarSpec.scope === 'editor') {
32105                    inEditorScope.push(contextToolbar);
32106                }
32107                else {
32108                    inNodeScope.push(contextToolbar);
32109                }
32110                lookupTable[key] = contextToolbar;
32111            });
32112        };
32113        const keys$1 = keys(contextToolbars);
32114        each$1(keys$1, (key) => {
32115            const toolbarApi = contextToolbars[key];
32116            if (toolbarApi.type === 'contextform' || toolbarApi.type === 'contextsliderform' || toolbarApi.type === 'contextsizeinputform') {
32117                registerForm(key, toolbarApi);
32118            }
32119            else if (toolbarApi.type === 'contexttoolbar') {
32120                registerToolbar(key, toolbarApi);
32121            }
32122        });
32123        return {
32124            forms,
32125            inNodeScope,
32126            inEditorScope,
32127            lookupTable,
32128            formNavigators
32129        };
32130    };
32131
32132    const transitionClass = 'tox-pop--transition';
32133    const isToolbarActionKey = (keyCode) => keyCode === global$1.ENTER || keyCode === global$1.SPACEBAR;
32134    const register$a = (editor, registryContextToolbars, sink, extras) => {
32135        const backstage = extras.backstage;
32136        const sharedBackstage = backstage.shared;
32137        const isTouch = detect$1().deviceType.isTouch;
32138        const lastElement = value$2();
32139        const lastTrigger = value$2();
32140        const lastContextPosition = value$2();
32141        const contextToolbarResult = renderContextToolbar({
32142            sink,
32143            onEscape: () => {
32144                editor.focus();
32145                fireContextToolbarClose(editor);
32146                return Optional.some(true);
32147            },
32148            onHide: () => {
32149                fireContextToolbarClose(editor);
32150            },
32151            onBack: () => {
32152                fireContextFormSlideBack(editor);
32153            },
32154            focusElement: (el) => {
32155                if (editor.getBody().contains(el.dom)) {
32156                    editor.focus();
32157                }
32158                else {
32159                    focus$4(el);
32160                }
32161            }
32162        });
32163        const contextbar = build$1(contextToolbarResult.sketch);
32164        const getBounds = () => {
32165            const position = lastContextPosition.get().getOr('node');
32166            // Use a 1px margin for the bounds to keep the context toolbar from butting directly against
32167            // the header, etc... when switching to inset layouts
32168            const margin = shouldUseInsetLayouts(position) ? 1 : 0;
32169            return getContextToolbarBounds(editor, sharedBackstage, position, margin);
32170        };
32171        const canLaunchToolbar = () => {
32172            // If a mobile context menu is open, don't launch else they'll probably overlap. For android, specifically.
32173            return !editor.removed && !(isTouch() && backstage.isContextMenuOpen());
32174        };
32175        const isSameLaunchElement = (elem) => is$1(lift2(elem, lastElement.get(), eq), true);
32176        const shouldContextToolbarHide = () => {
32177            if (!canLaunchToolbar()) {
32178                return true;
32179            }
32180            else {
32181                const contextToolbarBounds = getBounds();
32182                // Get the anchor bounds. For node anchors we should always try to use the last element bounds
32183                const anchorBounds = is$1(lastContextPosition.get(), 'node') ?
32184                    getAnchorElementBounds(editor, lastElement.get()) :
32185                    getSelectionBounds(editor);
32186                // If the anchor bounds aren't overlapping with the context toolbar bounds, then the context toolbar
32187                // should hide. We want the threshold to require some overlap here (+.01), so that as soon as the
32188                // anchor is off-screen, the context toolbar disappers.
32189                return contextToolbarBounds.height <= 0 || !isVerticalOverlap(anchorBounds, contextToolbarBounds, 0.01);
32190            }
32191        };
32192        const close = () => {
32193            lastElement.clear();
32194            lastTrigger.clear();
32195            lastContextPosition.clear();
32196            InlineView.hide(contextbar);
32197        };
32198        const hideOrRepositionIfNecessary = () => {
32199            if (InlineView.isOpen(contextbar)) {
32200                const contextBarEle = contextbar.element;
32201                remove$6(contextBarEle, 'display');
32202                if (shouldContextToolbarHide()) {
32203                    set$7(contextBarEle, 'display', 'none');
32204                }
32205                else {
32206                    lastTrigger.set(0 /* TriggerCause.Reposition */);
32207                    InlineView.reposition(contextbar);
32208                }
32209            }
32210        };
32211        const wrapInPopDialog = (toolbarSpec) => ({
32212            dom: {
32213                tag: 'div',
32214                classes: ['tox-pop__dialog']
32215            },
32216            components: [toolbarSpec],
32217            behaviours: derive$1([
32218                Keying.config({
32219                    mode: 'acyclic'
32220                }),
32221                config('pop-dialog-wrap-events', [
32222                    runOnAttached((comp) => {
32223                        editor.shortcuts.add('ctrl+F9', 'focus statusbar', () => Keying.focusIn(comp));
32224                    }),
32225                    runOnDetached((_comp) => {
32226                        editor.shortcuts.remove('ctrl+F9');
32227                    })
32228                ])
32229            ])
32230        });
32231        const navigate = (toolbarApi) => {
32232            // ASSUMPTION: This should only ever show one context toolbar since it's used for c
vendor: 4,701 bytes, lines 32232-32313
32232ontext forms hence [toolbarApi]
32233            const alloySpec = buildToolbar([toolbarApi]);
32234            emitWith(contextbar, forwardSlideEvent, {
32235                forwardContents: wrapInPopDialog(alloySpec)
32236            });
32237        };
32238        const getScopes = cached(() => categorise(registryContextToolbars, navigate));
32239        const buildContextToolbarGroups = (allButtons, ctx) => {
32240            return identifyButtons(editor, { buttons: allButtons, toolbar: ctx.items, allowToolbarGroups: false }, extras.backstage, Optional.some(['form:', 'toolbar:']));
32241        };
32242        const buildContextFormGroups = (ctx, providers) => ContextForm.buildInitGroups(ctx, providers);
32243        const buildToolbar = (toolbars) => {
32244            const { buttons } = editor.ui.registry.getAll();
32245            const scopes = getScopes();
32246            const allButtons = { ...buttons, ...scopes.formNavigators };
32247            // For context toolbars we don't want to use floating or sliding, so just restrict this
32248            // to scrolling or wrapping (default)
32249            const toolbarType = getToolbarMode(editor) === ToolbarMode$1.scrolling ? ToolbarMode$1.scrolling : ToolbarMode$1.default;
32250            const initGroups = flatten(map$2(toolbars, (ctx) => ctx.type === 'contexttoolbar' ? buildContextToolbarGroups(allButtons, contextToolbarToSpec(ctx)) : buildContextFormGroups(ctx, sharedBackstage.providers)));
32251            return renderToolbar({
32252                type: toolbarType,
32253                uid: generate$6('context-toolbar'),
32254                initGroups,
32255                onEscape: Optional.none,
32256                cyclicKeying: true,
32257                providers: sharedBackstage.providers
32258            });
32259        };
32260        const getAnchor = (position, element) => {
32261            const anchorage = position === 'node' ? sharedBackstage.anchors.node(element) : sharedBackstage.anchors.cursor();
32262            const anchorLayout = getAnchorLayout(editor, position, isTouch(), {
32263                lastElement: lastElement.get,
32264                isReposition: () => is$1(lastTrigger.get(), 0 /* TriggerCause.Reposition */),
32265                getMode: () => Positioning.getMode(sink)
32266            });
32267            return deepMerge(anchorage, anchorLayout);
32268        };
32269        const launchContext = (toolbarApi, elem) => {
32270            launchContextToolbar.cancel();
32271            // Don't launch if the editor has something else open that would conflict
32272            if (!canLaunchToolbar()) {
32273                return;
32274            }
32275            const toolbarSpec = buildToolbar(toolbarApi);
32276            // TINY-4495 ASSUMPTION: Can only do toolbarApi[0].position because ContextToolbarLookup.filterToolbarsByPosition
32277            // ensures all toolbars returned by ContextToolbarLookup have the same position.
32278            // And everything else that gets toolbars from elsewhere only returns maximum 1 toolbar
32279            const position = toolbarApi[0].position;
32280            const anchor = getAnchor(position, elem);
32281            lastContextPosition.set(position);
32282            lastTrigger.set(1 /* TriggerCause.NewAnchor */);
32283            const contextBarEle = contextbar.element;
32284            remove$6(contextBarEle, 'display');
32285            // Reset placement and transitions when moving to different elements
32286            if (!isSameLaunchElement(elem)) {
32287                remove$3(contextBarEle, transitionClass);
32288                Positioning.reset(sink, contextbar);
32289            }
32290            // Place the element
32291            InlineView.showWithinBounds(contextbar, wrapInPopDialog(toolbarSpec), {
32292                anchor,
32293                transition: {
32294                    classes: [transitionClass],
32295                    mode: 'placement'
32296                }
32297            }, () => Optional.some(getBounds()));
32298            // IMPORTANT: This must be stored after the initial render, otherwise the lookup of the last element in the
32299            // anchor placement will be incorrect as it'll reuse the new element as the anchor point.
32300            elem.fold(lastElement.clear, lastElement.set);
32301            // It's possible we may have launched offscreen, if so then hide
32302            if (shouldContextToolbarHide()) {
32303                set$7(contextBarEle, 'display', 'none');
32304            }
32305        };
32306        const instantReposition = () => {
32307            // Sometimes when we reposition the toolbar it might be in a transitioning state and
32308            // if we try to reposition while that happens the computed position/width will be incorrect.
32309            set$7(contextbar.element, 'transition', 'none');
32310            hideOrRepositionIfNecessary();
32311            remove$6(contextbar.element, 'transition');
32312        };
32313        let isDragging = false;
32314        const launchContextToolbar = last(() => {
32315            // Don't launch if the editor doesn't have focus or has been destroyed
32316            if (!editor.hasFocus() || editor.removed || isDragging) {
32317                return;
32318            }
32319            // If currently transitioning then throttle again so we don't interrupt the transition
32320            if (has(contextbar.element, transitionClass)) {
32321                launchContextToolbar.throttle();
32322            }
32323            else {
32324                const scopes = getScopes();
32325                lookup$1(scopes, editor).fold(close, (info) => {
32326                    launchContext(info.toolbars, Optional.some(info.elem));
32327                });
32328            }
32329        }, 17); // 17ms is used as that's about 1 frame at 60fps
32330        editor.on('init', () => {
32331            editor.on('remove', close);
32332            editor.on('ScrollContent ScrollWindow ObjectResized ResizeEditor longpress', hideOrRepositionIfNecessary);
32333            // FIX: Make it go away when the action makes it go away. E.g. deleting a column deletes the table.
32334            editor.on('click focus SetContent', launchContextToolbar.throttle);
32335            editor.on('keyup', (e) => {
32336                // If you use keyboard to press a button in a subtoolbar then the keyup will happen inside the editor and that should not re-render the toolbar
32337                if (!isToolbarActionKey(e.keyCode) || !contextToolbarResult.inSubtoolbar()) {
32338                    launchContextToolbar.throttle();
32339                }
32340            });
32341            editor.on(hideContextToolbarEvent, close);
32342            editor.on(showContextToolbarEvent, (e) => {
32343                const scopes = getScopes();
32344                // TODO: Have this stored in a better structure
32345                get$h(scopes.lookupTable, e.toolbarKey).each((ctx) => {
32346                    // ASSUMPTION: this is only used to open one specific toolbar at a time, hence [ctx]
32347                    launchContext([ctx], someIf(e.target !== editor, e.target));
32348                    focusIn(contextbar);
32349                });
32350            });
32351            editor.on('focusout', (_e) => {
32352                global$a.setEditorTimeout(editor, () => {
32353                    if (search(sink.element).isNone() && search(contextbar.element).isNone() && !editor.hasFocus()) {
32354                        close();
32355                    }
32356                }, 0);
32357            });
32358            editor.on('SwitchMode', () => {
32359                if (editor.mode.isReadOnly()) {
32360                    close();
32361                }
32362            });
32363            editor.on('DisabledStateChange', (e) => {
32364                if (e.state) {
32365                    close();
32366                }
32367            });
32368            // TINY-10640: Firefox was flaking in tests and was not properly dismissing the toolbar could not reproduce it manually but adding this seems to resolve it.
32369            editor.on('ExecCommand', ({ command }) => {
32370                if (command.toLowerCase() === 'toggleview') {
32371                    close();
32372                }
32373            });
32374            editor.on('AfterProgressState', (event) => {
32375                if (event.state) {
32376                    close();
32377                }
32378                else if (editor.hasFocus()) {
32379                    launchContextToolbar.throttle();
32380                }
32381            });
32382            editor.on('dragstart', () => {
32383                isDragging = true;
32384            });
32385            editor.on('dragend drop', () => {
32386                isDragging = false;
32387            });
32388            editor.on('NodeChange', (_e) => {
32389                if (!contextToolbarResult.inSubtoolbar()) {
32390                    search(contextbar.element).fold(launchContextToolbar.throttle, noop);
32391                }
32392                else {
32393                    instantReposition();
32394                }
32395            });
32396        });
32397    };
32398
32399    const register$9 = (editor) => {
32400        const alignToolbarButtons = [
32401            { name: 'alignleft', text: 'Align left', cmd: 'JustifyLeft', icon: 'align-left' },
32402            { name: 'aligncenter', text: 'Align center', cmd: 'JustifyCenter', icon: 'align-center' },
32403            { name: 'alignright', text: 'Align right', cmd: 'JustifyRight', icon: 'align-right' },
32404            { name: 'alignjustify', text: 'Justify', cmd: 'JustifyFull', icon: 'align-justify' }
32405        ];
32406        each$1(alignToolbarButtons, (item) => {
32407            editor.ui.registry.addToggleButton(item.name, {
32408                tooltip: item.text,
32409                icon: item.icon,
32410                onAction: onActionExecCommand(editor, item.cmd),
32411                onSetup: onSetupStateToggle(editor, item.name)
32412            });
32413        });
32414        editor.ui.registry.addButton('alignnone', {
32415            tooltip: 'No alignment',
32416            icon: 'align-none',
32417            onSetup: onSetupEditableToggle(editor),
32418            onAction: onActionExecCommand(editor, 'JustifyNone')
32419        });
32420    };
32421
32422    const registerController = (editor, spec) => {
32423        const getMenuItems = () => {
32424            const options = spec.getOptions(editor);
32425            const initial = spec.getCurrent(editor).map(spec.hash);
32426            const current = value$2();
32427            return map$2(options, (value) => ({
32428                type: 'togglemenuitem',
32429                text: spec.display(value),
32430                onSetup: (api) => {
32431                    const setActive = (active) => {
32432                        if (active) {
32433                            current.on((oldApi) => oldApi.setActive(false));
32434                            current.set(api);
32435                        }
32436                        api.setActive(active);
32437                    };
32438                    setActive(is$1(initial, spec.hash(value)));
32439                    const unbindWatcher = spec.watcher(editor, value, setActive);
32440                    return () => {
32441                        current.clear();
32442                        unbindWatcher();
32443                    };
32444                },
32445                onAction: () => spec.setCurrent(editor, value)
32446            }));
32447        };
32448        editor.ui.registry.addMenuButton(spec.name, {
32449            tooltip: spec.text,
32450            icon: spec.icon,
32451            fetch: (callback) => callback(getMenuItems()),
32452            onSetup: spec.onToolbarSetup
32453        });
32454        editor.ui.registry.addNestedMenuItem(spec.name, {
32455            type: 'nestedmenuitem',
32456            text: spec.text,
32457            getSubmenuItems: getMenuItems,
32458            onSetup: spec.onMenuSetup
32459        });
32460    };
32461    const lineHeightSpec = (editor) => ({
32462        name: 'lineheight',
32463        text: 'Line height',
32464        icon: 'line-height',
32465        getOptions: getLineHeightFormats,
32466        hash: (input) => normalise(input, ['fixed', 'relative', 'empty']).getOr(input),
32467        display: identity,
32468        watcher: (editor, value, callback) => editor.formatter.formatChanged('lineheight', callback, false, { value }).unbind,
32469        getCurrent: (editor) => Optional.from(editor.queryCommandValue('LineHeight')),
32470        setCurrent: (editor, value) => editor.execCommand('LineHeight', false, value),
32471        onToolbarSetup: onSetupEditableToggle(editor),
32472        onMenuSetup: onSetupEditableToggle(editor)
32473    });
32474    const languageSpec = (editor) => {
32475        const settingsOpt = Optional.from(getContentLanguages(editor));
32476        return settingsOpt.map((settings) => ({
32477            name: 'language',
32478            text: 'Language',
32479            icon: 'language',
32480            getOptions: constant$1(settings),
32481            hash: (input) => isUndefined(input.customCode) ? input.code : `${input.code}/${input.customCode}`,
32482            display: (input) => input.title,
32483            watcher: (editor, value, callback) => editor.formatter.formatChanged('lang', callback, false, { value: value.code, customValue: value.customCode ?? null }).unbind,
32484            getCurrent: (editor) => {
32485                const node = SugarElement.fromDom(editor.selection.getNode());
32486                return closest(node, (n) => Optional.some(n)
32487                    .filter(isElement$1)
32488                    .bind((ele) => {
32489                    const codeOpt = getOpt(ele, 'lang');
32490                    return codeOpt.map((code) => {
32491                        const customCode = getOpt(ele, 'data-mce-lang').getOrUndefined();
32492                        return { code, customCode, title: '' };
32493                    });
32494                }));
32495            },
32496            setCurrent: (editor, lang) => editor.execCommand('Lang', false, lang),
32497            onToolbarSetup: (api) => {
32498                const unbinder = unbindable();
32499                api.setActive(editor.formatter.match('lang', {}, undefined, true));
32500                unbinder.set(editor.formatter.formatChanged('lang', api.setActive, true));
32501                return composeUnbinders(unbinder.clear, onSetupEditableToggle(editor)(api));
32502            },
32503            onMenuSetup: onSetupEditableToggle(editor)
32504        }));
32505    };
32506    const register$8 = (editor) => {
32507        registerController(editor, lineHeightSpec(editor));
32508        languageSpec(editor).each((spec) => registerController(editor, spec));
32509    };
32510
32511    const register$7 = (editor, backstage) => {
32512        createAlignMenu(editor, backstage);
32513        createFontFamilyMenu(editor, backstage);
32514        createStylesMenu(editor, backstage);
32515        createBlocksMenu(editor, backstage);
32516        createFontSizeMenu(editor, backstage);
32517    };
32518
32519    const register$6 = (editor) => {
32520        editor.ui.registry.addContext('editable', () => {
32521            return editor.selection.isEditable();
32522        });
32523        editor.ui.registry.addContext('mode', (mode) => {
32524            return editor.mode.get() === mode;
32525        });
32526        editor.ui.registry.addContext('any', always);
32527        editor.ui.registry.addContext('formatting', (format) => {
32528            return editor.formatter.canApply(format);
32529        });
32530        editor.ui.registry.addContext('insert', (child) => {
32531            return editor.schema.isValidChild(editor.selection.getNode().tagName, child);
32532        });
32533    };
32534
32535    const onSetupOutdentState = (editor) => onSetupEvent(editor, 'NodeChange', (api) => {
32536        api.setEnabled(editor.queryCommandState('outdent') && editor.selection.isEditable());
32537    });
32538    const registerButtons$2 = (editor) => {
32539        editor.ui.registry.addButton('outdent', {
32540            tooltip: 'Decrease indent',
32541            icon: 'outdent',
32542            onSetup: onSetupOutdentState(editor),
32543            onAction: onActionExecCommand(editor, 'outdent')
32544        });
32545        editor.ui.registry.addButton('indent', {
32546            tooltip: 'Increase indent',
32547            icon: 'indent',
32548            onSetup: onSetupEditableToggle(editor, () => editor.queryCommandState('indent')),
32549            onAction: onActionExecCommand(editor, 'indent')
32550        });
32551    };
32552    const register$5 = (editor) => {
32553        registerButtons$2(editor);
32554    };
32555
32556    const makeSetupHandler = (editor, pasteAsText) => (api) => {
32557        api.setActive(pasteAsText.get());
32558        const pastePlainTextToggleHandler = (e) => {
32559            pasteAsText.set(e.state);
32560            api.setActive(e.state);
32561        };
32562        editor.on('PastePlainTextToggle', pastePlainTextToggleHandler);
32563        return composeUnbinders(() => editor.off('PastePlainTextToggle', pastePlainTextToggleHandler), onSetupEditableToggle(editor)(api));
32564    };
32565    const register$4 = (editor) => {
32566        const pasteAsText = Cell(getPasteAsText(editor));
32567        const onAction = () => editor.execCommand('mceTogglePlainTextPaste');
32568        editor.ui.registry.addToggleButton('pastetext', {
32569            active: false,
32570            icon: 'paste-text',
32571            tooltip: 'Paste as text',
32572            onAction,
32573            onSetup: makeSetupHandler(editor, pasteAsText)
32574        });
32575        editor.ui.registry.addToggleMenuItem('pastetext', {
32576            text: 'Paste as text',
32577            icon: 'paste-text',
32578            onAction,
32579            onSetup: makeSetupHandler(editor, pasteAsText)
32580        });
32581    };
32582
32583    const onActionToggleFormat = (editor, fmt) => () => {
32584        editor.execCommand('mceToggleFormat', false, fmt);
32585    };
32586    const registerFormatButtons = (editor) => {
32587        global$2.each([
32588            { name: 'bold', text: 'Bold', icon: 'bold', shortcut: 'Meta+B' },
32589            { name: 'italic', text: 'Italic', icon: 'italic', shortcut: 'Meta+I' },
32590            { name: 'underline', text: 'Underline', icon: 'underline', shortcut: 'Meta+U' },
32591            { name: 'strikethrough', text: 'Strikethrough', icon: 'strike-through' },
32592            { name: 'subscript', text: 'Subscript', icon: 'subscript' },
32593            { name: 'superscript', text: 'Superscript', icon: 'superscript' }
32594        ], (btn, _idx) => {
32595            editor.ui.registry.addToggleButton(btn.name, {
32596                tooltip: btn.text,
32597                icon: btn.icon,
32598                onSetup: onSetupStateToggle(editor, btn.name),
32599                onAction: onActionToggleFormat(editor, btn.name),
32600                shortcut: btn.shortcut
32601            });
32602        });
32603        for (let i = 1; i <= 6; i++) {
32604            const name = 'h' + i;
32605            const shortcut = `Access+${i}`;
32606            editor.ui.registry.addToggleButton(name, {
32607                text: name.toUpperCase(),
32608                tooltip: 'Heading ' + i,
32609                onSetup: onSetupStateToggle(editor, name),
32610                onAction: onActionToggleFormat(editor, name),
32611                shortcut
32612            });
32613        }
32614    };
32615    const registerCommandButtons = (editor) => {
32616        global$2.each([
32617            { name: 'copy', text: 'Copy', action: 'Copy', icon: 'copy', context: 'any' },
32618            { name: 'help', text: 'Help', action: 'mceHelp', icon: 'help', shortcut: 'Alt+0', context: 'any' },
32619            { name: 'selectall', text: 'Select all', action: 'SelectAll', icon: 'select-all', shortcut: 'Meta+A', context: 'any' },
32620            { name: 'newdocument', text: 'New document', action: 'mceNewDocument', icon: 'new-document' },
32621            { name: 'print', text: 'Print', action: 'mcePrint', icon: 'print', shortcut: 'Meta+P', context: 'any' },
32622        ], (btn) => {
32623            editor.ui.registry.addButton(btn.name, {
32624                tooltip: btn.text,
32625                icon: btn.icon,
32626                onAction: onActionExecCommand(editor, btn.action),
32627                shortcut: btn.shortcut,
32628                context: btn.context
32629            });
32630        });
32631        global$2.each([
32632            { name: 'cut', text: 'Cut', action: 'Cut', icon: 'cut' },
32633            { name: 'paste', text: 'Paste', action: 'Paste', icon: 'paste' },
32634            // visualaid was here but also exists in VisualAid.ts?
32635            { name: 'removeformat', text: 'Clear formatting', action: 'RemoveFormat', icon: 'remove-formatting' },
32636            { name: 'remove', text: 'Remove', action: 'Delete', icon: 'remove' },
32637            { name: 'hr', text: 'Horizontal line', action: 'InsertHorizontalRule', icon: 'horizontal-rule' }
32638        ], (btn) => {
32639            editor.ui.registry.addButton(btn.name, {
32640                tooltip: btn.text,
32641                icon: btn.icon,
32642                onSetup: onSetupEditableToggle(editor),
32643                onAction: onActionExecCommand(editor, btn.action)
32644            });
32645        });
32646    };
32647    const registerCommandToggleButtons = (editor) => {
32648        global$2.each([
32649            { name: 'blockquote', text: 'Blockquote', action: 'mceBlockQuote', icon: 'quote' }
32650        ], (btn) => {
32651            editor.ui.registry.addToggleButton(btn.name, {
32652                tooltip: btn.text,
32653                icon: btn.icon,
32654                onAction: onActionExecCommand(editor, btn.action),
32655                onSetup: onSetupStateToggle(editor, btn.name)
32656            });
32657        });
32658    };
32659    const registerButtons$1 = (editor) => {
32660        registerFormatButtons(editor);
32661        registerCommandButtons(editor);
32662        registerCommandToggleButtons(editor);
32663    };
32664    const registerMenuItems$2 = (editor) => {
32665        global$2.each([
32666            { name: 'newdocument', text: 'New document', action: 'mceNewDocument', icon: 'new-document' },
32667            { name: 'copy', text: 'Copy', action: 'Copy', icon: 'copy', shortcut: 'Meta+C', context: 'any' },
32668            { name: 'selectall', text: 'Select all', action: 'SelectAll', icon: 'select-all', shortcut: 'Meta+A', context: 'any' },
32669            { name: 'print', text: 'Print...', action: 'mcePrint', icon: 'print', shortcut: 'Meta+P', context: 'any' }
32670        ], (menuitem) => {
32671            editor.ui.registry.addMenuItem(menuitem.name, {
32672                text: menuitem.text,
32673                icon: menuitem.icon,
32674                shortcut: menuitem.shortcut,
32675                onAction: onActionExecCommand(editor, menuitem.action),
32676                context: menuitem.context
32677            });
32678        });
32679        global$2.each([
32680            { name: 'bold', text: 'Bold', action: 'Bold', icon: 'bold', shortcut: 'Meta+B' },
32681            { name: 'italic', text: 'Italic', action: 'Italic', icon: 'italic', shortcut: 'Meta+I' },
32682            { name: 'underline', text: 'Underline', action: 'Underline', icon: 'underline', shortcut: 'Meta+U' },
32683            { name: 'strikethrough', text: 'Strikethrough', action: 'Strikethrough', icon: 'strike-through' },
32684            { name: 'subscript', text: 'Subscript', action: 'Subscript', icon: 'subscript' },
32685            { name: 'superscript', text: 'Superscript', action: 'Superscript', icon: 'superscript' },
32686            { name: 'removeformat', text: 'Clear formatting', action: 'RemoveFormat', icon: 'remove-formatting' },
32687            { name: 'cut', text: 'Cut', action: 'Cut', icon: 'cut', shortcut: 'Meta+X' },
32688            { name: 'paste', text: 'Paste', action: 'Paste', icon: 'paste', shortcut: 'Meta+V' },
32689            { name: 'hr', text: 'Horizontal line', action: 'InsertHorizontalRule', icon: 'horizontal-rule' }
32690        ], (menuitem) => {
32691            editor.ui.registry.addMenuItem(menuitem.name, {
32692                text: menuitem.text,
32693                icon: menuitem.icon,
32694                shortcut: menuitem.shortcut,
32695                onSetup: onSetupEditableToggle(editor),
32696                onAction: onActionExecCommand(editor, menuitem.action)
32697            });
32698        });
32699        editor.ui.registry.addMenuItem('codeformat', {
32700            text: 'Code',
32701            icon: 'sourcecode',
32702            onSetup: onSetupEditableToggle(editor),
32703            onAction: onActionToggleFormat(editor, 'code')
32704        });
32705    };
32706    const register$3 = (editor) => {
32707        registerButtons$1(editor);
32708        registerMenuItems$2(editor);
32709    };
32710
32711    const onSetupUndoRedoState = (editor, type) => onSetupEvent(editor, 'Undo Redo AddUndo TypingUndo ClearUndos SwitchMode', (api) => {
32712        api.setEnabled(!editor.mode.isReadOnly() && editor.undoManager[type]());
32713    });
32714    const registerMenuItems$1 = (editor) => {
32715        editor.ui.registry.addMenuItem('undo', {
32716            text: 'Undo',
32717            icon: 'undo',
32718            shortcut: 'Meta+Z',
32719            onSetup: onSetupUndoRedoState(editor, 'hasUndo'),
32720            onAction: onActionExecCommand(editor, 'undo')
32721        });
32722        editor.ui.registry.addMenuItem('redo', {
32723            text: 'Redo',
32724            icon: 'redo',
32725            shortcut: 'Meta+Y',
32726            onSetup: onSetupUndoRedoState(editor, 'hasRedo'),
32727            onAction: onActionExecCommand(editor, 'redo')
32728        });
32729    };
vendor: 23,232 bytes, lines 32730-33284
32730    // Note: The undo/redo buttons are disabled by default here, as they'll be rendered
32731    // on init generally and it won't have any undo levels at that stage.
32732    const registerButtons = (editor) => {
32733        editor.ui.registry.addButton('undo', {
32734            tooltip: 'Undo',
32735            icon: 'undo',
32736            enabled: false,
32737            onSetup: onSetupUndoRedoState(editor, 'hasUndo'),
32738            onAction: onActionExecCommand(editor, 'undo'),
32739            shortcut: 'Meta+Z'
32740        });
32741        editor.ui.registry.addButton('redo', {
32742            tooltip: 'Redo',
32743            icon: 'redo',
32744            enabled: false,
32745            onSetup: onSetupUndoRedoState(editor, 'hasRedo'),
32746            onAction: onActionExecCommand(editor, 'redo'),
32747            shortcut: 'Meta+Y'
32748        });
32749    };
32750    const register$2 = (editor) => {
32751        registerMenuItems$1(editor);
32752        registerButtons(editor);
32753    };
32754
32755    const onSetupVisualAidState = (editor) => onSetupEvent(editor, 'VisualAid', (api) => {
32756        api.setActive(editor.hasVisual);
32757    });
32758    const registerMenuItems = (editor) => {
32759        editor.ui.registry.addToggleMenuItem('visualaid', {
32760            text: 'Visual aids',
32761            onSetup: onSetupVisualAidState(editor),
32762            onAction: onActionExecCommand(editor, 'mceToggleVisualAid'),
32763            context: 'any'
32764        });
32765    };
32766    const registerToolbarButton = (editor) => {
32767        editor.ui.registry.addButton('visualaid', {
32768            tooltip: 'Visual aids',
32769            text: 'Visual aids',
32770            onAction: onActionExecCommand(editor, 'mceToggleVisualAid'),
32771            context: 'any'
32772        });
32773    };
32774    const register$1 = (editor) => {
32775        registerToolbarButton(editor);
32776        registerMenuItems(editor);
32777    };
32778
32779    const setup$6 = (editor, backstage) => {
32780        register$9(editor);
32781        register$3(editor);
32782        register$7(editor, backstage);
32783        register$2(editor);
32784        register$d(editor);
32785        register$1(editor);
32786        register$5(editor);
32787        register$8(editor);
32788        register$4(editor);
32789        register$6(editor);
32790    };
32791
32792    const patchPipeConfig = (config) => isString(config) ? config.split(/[ ,]/) : config;
32793    const option = (name) => (editor) => editor.options.get(name);
32794    const register = (editor) => {
32795        const registerOption = editor.options.register;
32796        registerOption('contextmenu_avoid_overlap', {
32797            processor: 'string',
32798            default: ''
32799        });
32800        registerOption('contextmenu_never_use_native', {
32801            processor: 'boolean',
32802            default: false
32803        });
32804        registerOption('contextmenu', {
32805            processor: (value) => {
32806                if (value === false) {
32807                    return { value: [], valid: true };
32808                }
32809                else if (isString(value) || isArrayOf(value, isString)) {
32810                    return { value: patchPipeConfig(value), valid: true };
32811                }
32812                else {
32813                    return { valid: false, message: 'Must be false or a string.' };
32814                }
32815            },
32816            default: 'link linkchecker image editimage table spellchecker configurepermanentpen'
32817        });
32818    };
32819    const shouldNeverUseNative = option('contextmenu_never_use_native');
32820    const getAvoidOverlapSelector = option('contextmenu_avoid_overlap');
32821    const isContextMenuDisabled = (editor) => getContextMenu(editor).length === 0;
32822    const getContextMenu = (editor) => {
32823        const contextMenus = editor.ui.registry.getAll().contextMenus;
32824        const contextMenu = editor.options.get('contextmenu');
32825        if (editor.options.isSet('contextmenu')) {
32826            return contextMenu;
32827        }
32828        else {
32829            // Filter default context menu items when they are not in the registry (e.g. when the plugin is not loaded)
32830            return filter$2(contextMenu, (item) => has$2(contextMenus, item));
32831        }
32832    };
32833
32834    const nu = (x, y) => ({
32835        type: 'makeshift',
32836        x,
32837        y
32838    });
32839    const transpose = (pos, dx, dy) => {
32840        return nu(pos.x + dx, pos.y + dy);
32841    };
32842    const isTouchEvent$1 = (e) => e.type === 'longpress' || e.type.indexOf('touch') === 0;
32843    const fromPageXY = (e) => {
32844        if (isTouchEvent$1(e)) {
32845            const touch = e.touches[0];
32846            return nu(touch.pageX, touch.pageY);
32847        }
32848        else {
32849            return nu(e.pageX, e.pageY);
32850        }
32851    };
32852    const fromClientXY = (e) => {
32853        if (isTouchEvent$1(e)) {
32854            const touch = e.touches[0];
32855            return nu(touch.clientX, touch.clientY);
32856        }
32857        else {
32858            return nu(e.clientX, e.clientY);
32859        }
32860    };
32861    const transposeContentAreaContainer = (element, pos) => {
32862        const containerPos = global$9.DOM.getPos(element);
32863        return transpose(pos, containerPos.x, containerPos.y);
32864    };
32865    const getPointAnchor = (editor, e) => {
32866        // If the contextmenu event is fired via the editor.dispatch() API or some other means, fall back to selection anchor
32867        if (e.type === 'contextmenu' || e.type === 'longpress') {
32868            if (editor.inline) {
32869                return fromPageXY(e);
32870            }
32871            else {
32872                return transposeContentAreaContainer(editor.getContentAreaContainer(), fromClientXY(e));
32873            }
32874        }
32875        else {
32876            return getSelectionAnchor(editor);
32877        }
32878    };
32879    const getSelectionAnchor = (editor) => {
32880        return {
32881            type: 'selection',
32882            root: SugarElement.fromDom(editor.selection.getNode())
32883        };
32884    };
32885    const getNodeAnchor = (editor) => ({
32886        type: 'node',
32887        node: Optional.some(SugarElement.fromDom(editor.selection.getNode())),
32888        root: SugarElement.fromDom(editor.getBody())
32889    });
32890    const getAnchorSpec$1 = (editor, e, anchorType) => {
32891        switch (anchorType) {
32892            case 'node':
32893                return getNodeAnchor(editor);
32894            case 'point':
32895                return getPointAnchor(editor, e);
32896            case 'selection':
32897                return getSelectionAnchor(editor);
32898        }
32899    };
32900
32901    const initAndShow$1 = (editor, e, buildMenu, backstage, contextmenu, anchorType) => {
32902        const items = buildMenu();
32903        const anchorSpec = getAnchorSpec$1(editor, e, anchorType);
32904        build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
32905            isHorizontalMenu: false,
32906            search: Optional.none()
32907        }).map((menuData) => {
32908            e.preventDefault();
32909            // show the context menu, with items set to close on click
32910            InlineView.showMenuAt(contextmenu, { anchor: anchorSpec }, {
32911                menu: {
32912                    markers: markers('normal')
32913                },
32914                data: menuData
32915            });
32916        });
32917    };
32918
32919    const layouts = {
32920        onLtr: () => [south$2, southeast$2, southwest$2, northeast$2, northwest$2, north$2,
32921            north$1, south$1, northeast$1, southeast$1, northwest$1, southwest$1],
32922        onRtl: () => [south$2, southwest$2, southeast$2, northwest$2, northeast$2, north$2,
32923            north$1, south$1, northwest$1, southwest$1, northeast$1, southeast$1]
32924    };
32925    const bubbleSize = 12;
32926    const bubbleAlignments = {
32927        valignCentre: [],
32928        alignCentre: [],
32929        alignLeft: ['tox-pop--align-left'],
32930        alignRight: ['tox-pop--align-right'],
32931        right: ['tox-pop--right'],
32932        left: ['tox-pop--left'],
32933        bottom: ['tox-pop--bottom'],
32934        top: ['tox-pop--top']
32935    };
32936    const isTouchWithinSelection = (editor, e) => {
32937        const selection = editor.selection;
32938        if (selection.isCollapsed() || e.touches.length < 1) {
32939            return false;
32940        }
32941        else {
32942            const touch = e.touches[0];
32943            const rng = selection.getRng();
32944            const rngRectOpt = getFirstRect(editor.getWin(), SimSelection.domRange(rng));
32945            return rngRectOpt.exists((rngRect) => rngRect.left <= touch.clientX &&
32946                rngRect.right >= touch.clientX &&
32947                rngRect.top <= touch.clientY &&
32948                rngRect.bottom >= touch.clientY);
32949        }
32950    };
32951    const setupiOSOverrides = (editor) => {
32952        // iOS will change the selection due to longpress also being a range selection gesture. As such we
32953        // need to reset the selection back to the original selection after the touchend event has fired
32954        const originalSelection = editor.selection.getRng();
32955        const selectionReset = () => {
32956            global$a.setEditorTimeout(editor, () => {
32957                editor.selection.setRng(originalSelection);
32958            }, 10);
32959            unbindEventListeners();
32960        };
32961        editor.once('touchend', selectionReset);
32962        // iPadOS will trigger a mousedown after the longpress which will close open context menus
32963        // so we want to prevent that from running
32964        const preventMousedown = (e) => {
32965            e.preventDefault();
32966            e.stopImmediatePropagation();
32967        };
32968        editor.on('mousedown', preventMousedown, true);
32969        // If a longpresscancel is fired, then a touchmove has occurred so we shouldn't do any overrides
32970        const clearSelectionReset = () => unbindEventListeners();
32971        editor.once('longpresscancel', clearSelectionReset);
32972        const unbindEventListeners = () => {
32973            editor.off('touchend', selectionReset);
32974            editor.off('longpresscancel', clearSelectionReset);
32975            editor.off('mousedown', preventMousedown);
32976        };
32977    };
32978    const getAnchorSpec = (editor, e, anchorType) => {
32979        const anchorSpec = getAnchorSpec$1(editor, e, anchorType);
32980        const bubbleYOffset = anchorType === 'point' ? bubbleSize : 0;
32981        return {
32982            bubble: nu$6(0, bubbleYOffset, bubbleAlignments),
32983            layouts,
32984            overrides: {
32985                maxWidthFunction: expandable(),
32986                maxHeightFunction: expandable$1()
32987            },
32988            ...anchorSpec
32989        };
32990    };
32991    const show = (editor, e, items, backstage, contextmenu, anchorType, highlightImmediately) => {
32992        const anchorSpec = getAnchorSpec(editor, e, anchorType);
32993        build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
32994            // MobileContextMenus are the *only* horizontal menus currently (2022-08-16)
32995            isHorizontalMenu: true,
32996            search: Optional.none()
32997        }).map((menuData) => {
32998            e.preventDefault();
32999            // If we are highlighting immediately, then we want to highlight the menu
33000            // and the item. Otherwise, we don't want to highlight anything.
33001            const highlightOnOpen = highlightImmediately
33002                ? HighlightOnOpen.HighlightMenuAndItem
33003                : HighlightOnOpen.HighlightNone;
33004            // Show the context menu, with items set to close on click
33005            InlineView.showMenuWithinBounds(contextmenu, { anchor: anchorSpec }, {
33006                menu: {
33007                    markers: markers('normal'),
33008                    highlightOnOpen
33009                },
33010                data: menuData,
33011                type: 'horizontal'
33012            }, () => Optional.some(getContextToolbarBounds(editor, backstage.shared, anchorType === 'node' ? 'node' : 'selection')));
33013            // Ensure the context toolbar is hidden
33014            editor.dispatch(hideContextToolbarEvent);
33015        });
33016    };
33017    const initAndShow = (editor, e, buildMenu, backstage, contextmenu, anchorType) => {
33018        const detection = detect$1();
33019        const isiOS = detection.os.isiOS();
33020        const isMacOS = detection.os.isMacOS();
33021        const isAndroid = detection.os.isAndroid();
33022        const isTouch = detection.deviceType.isTouch();
33023        const shouldHighlightImmediately = () => !(isAndroid || isiOS || (isMacOS && isTouch));
33024        const open = () => {
33025            const items = buildMenu();
33026            show(editor, e, items, backstage, contextmenu, anchorType, shouldHighlightImmediately());
33027        };
33028        // On iOS/iPadOS if we've long pressed on a ranged selection then we've already selected the content
33029        // and just need to open the menu. Otherwise we need to wait for a selection change to occur as long
33030        // press triggers a ranged selection on iOS.
33031        if ((isMacOS || isiOS) && anchorType !== 'node') {
33032            const openiOS = () => {
33033                setupiOSOverrides(editor);
33034                open();
33035            };
33036            if (isTouchWithinSelection(editor, e)) {
33037                openiOS();
33038            }
33039            else {
33040                editor.once('selectionchange', openiOS);
33041                editor.once('touchend', () => editor.off('selectionchange', openiOS));
33042            }
33043        }
33044        else {
33045            open();
33046        }
33047    };
33048
33049    const isSeparator = (item) => isString(item) ? item === '|' : item.type === 'separator';
33050    const separator = {
33051        type: 'separator'
33052    };
33053    const makeContextItem = (item) => {
33054        const commonMenuItem = (item) => ({
33055            text: item.text,
33056            icon: item.icon,
33057            enabled: item.enabled,
33058            shortcut: item.shortcut,
33059        });
33060        if (isString(item)) {
33061            return item;
33062        }
33063        else {
33064            switch (item.type) {
33065                case 'separator':
33066                    return separator;
33067                case 'submenu':
33068                    return {
33069                        type: 'nestedmenuitem',
33070                        ...commonMenuItem(item),
33071                        getSubmenuItems: () => {
33072                            const items = item.getSubmenuItems();
33073                            if (isString(items)) {
33074                                return items;
33075                            }
33076                            else {
33077                                return map$2(items, makeContextItem);
33078                            }
33079                        }
33080                    };
33081                default:
33082                    // case 'item', or anything else really
33083                    const commonItem = item;
33084                    return {
33085                        type: 'menuitem',
33086                        ...commonMenuItem(commonItem),
33087                        // disconnect the function from the menu item API bridge defines
33088                        onAction: noarg(commonItem.onAction)
33089                    };
33090            }
33091        }
33092    };
33093    const addContextMenuGroup = (xs, groupItems) => {
33094        // Skip if there are no items
33095        if (groupItems.length === 0) {
33096            return xs;
33097        }
33098        // Only add a separator at the beginning if the last item isn't a separator
33099        const lastMenuItem = last$1(xs).filter((item) => !isSeparator(item));
33100        const before = lastMenuItem.fold(() => [], (_) => [separator]);
33101        return xs.concat(before).concat(groupItems).concat([separator]);
33102    };
33103    const generateContextMenu = (contextMenus, menuConfig, selectedElement) => {
33104        const sections = foldl(menuConfig, (acc, name) => {
33105            // Either read and convert the list of items out of the plugin, or assume it's a standard menu item reference
33106            return get$h(contextMenus, name.toLowerCase()).map((menu) => {
33107                const items = menu.update(selectedElement);
33108                if (isString(items) && isNotEmpty(trim$1(items))) {
33109                    return addContextMenuGroup(acc, items.split(' '));
33110                }
33111                else if (isArray(items) && items.length > 0) {
33112                    // TODO: Should we add a StructureSchema check here?
33113                    const allItems = map$2(items, makeContextItem);
33114                    return addContextMenuGroup(acc, allItems);
33115                }
33116                else {
33117                    return acc;
33118                }
33119            }).getOrThunk(() => acc.concat([name]));
33120        }, []);
33121        // Strip off any trailing separator
33122        if (sections.length > 0 && isSeparator(sections[sections.length - 1])) {
33123            sections.pop();
33124        }
33125        return sections;
33126    };
33127    const isNativeOverrideKeyEvent = (editor, e) => e.ctrlKey && !shouldNeverUseNative(editor);
33128    const isTouchEvent = (e) => e.type === 'longpress' || has$2(e, 'touches');
33129    const isTriggeredByKeyboard = (editor, e) => 
33130    // Different browsers trigger the context menu from keyboards differently, so need to check various different things here.
33131    // If a longpress touch event, always treat it as a pointer event
33132    // Chrome: button = 0, pointerType = undefined & target = the selection range node
33133    // Firefox: button = 0, pointerType = undefined & target = body
33134    // Safari: N/A (Mac's don't expose a contextmenu keyboard shortcut)
33135    !isTouchEvent(e) && (e.button !== 2 || e.target === editor.getBody() && e.pointerType === '');
33136    const getSelectedElement = (editor, e) => isTriggeredByKeyboard(editor, e) ? editor.selection.getStart(true) : e.target;
33137    const getAnchorType = (editor, e) => {
33138        const selector = getAvoidOverlapSelector(editor);
33139        const anchorType = isTriggeredByKeyboard(editor, e) ? 'selection' : 'point';
33140        if (isNotEmpty(selector)) {
33141            const target = getSelectedElement(editor, e);
33142            const selectorExists = closest$1(SugarElement.fromDom(target), selector);
33143            return selectorExists ? 'node' : anchorType;
33144        }
33145        else {
33146            return anchorType;
33147        }
33148    };
33149    const setup$5 = (editor, lazySink, backstage) => {
33150        const detection = detect$1();
33151        const isTouch = detection.deviceType.isTouch;
33152        const contextmenu = build$1(InlineView.sketch({
33153            dom: {
33154                tag: 'div'
33155            },
33156            lazySink,
33157            onEscape: () => editor.focus(),
33158            onShow: () => backstage.setContextMenuState(true),
33159            onHide: () => backstage.setContextMenuState(false),
33160            fireDismissalEventInstead: {},
33161            inlineBehaviours: derive$1([
33162                config('dismissContextMenu', [
33163                    run$1(dismissRequested(), (comp, _se) => {
33164                        Sandboxing.close(comp);
33165                        editor.focus();
33166                    })
33167                ])
33168            ])
33169        }));
33170        const hideContextMenu = () => InlineView.hide(contextmenu);
33171        const showContextMenu = (e) => {
33172            // Prevent the default if we should never use native
33173            if (shouldNeverUseNative(editor)) {
33174                e.preventDefault();
33175            }
33176            if (isNativeOverrideKeyEvent(editor, e) || isContextMenuDisabled(editor)) {
33177                return;
33178            }
33179            const anchorType = getAnchorType(editor, e);
33180            const buildMenu = () => {
33181                // Use the event target element for touch events, otherwise fallback to the current selection
33182                const selectedElement = getSelectedElement(editor, e);
33183                const registry = editor.ui.registry.getAll();
33184                const menuConfig = getContextMenu(editor);
33185                return generateContextMenu(registry.contextMenus, menuConfig, selectedElement);
33186            };
33187            const initAndShow$2 = isTouch() ? initAndShow : initAndShow$1;
33188            initAndShow$2(editor, e, buildMenu, backstage, contextmenu, anchorType);
33189        };
33190        editor.on('init', () => {
33191            // Hide the context menu when scrolling or resizing
33192            // Except ResizeWindow on mobile which fires when the keyboard appears/disappears
33193            const hideEvents = 'ResizeEditor ScrollContent ScrollWindow longpresscancel' + (isTouch() ? '' : ' ResizeWindow');
33194            editor.on(hideEvents, hideContextMenu);
33195            editor.on('longpress contextmenu', showContextMenu);
33196        });
33197    };
33198
33199    const snapWidth = 40;
33200    const snapOffset = snapWidth / 2;
33201    // const insertDebugDiv = (left, top, width, height, color, clazz) => {
33202    //   const debugArea = SugarElement.fromHtml(`<div class="${clazz}"></div>`);
33203    //   Css.setAll(debugArea, {
33204    //     'left': left.toString() + 'px',
33205    //     'top': top.toString() + 'px',
33206    //     'background-color': color,
33207    //     'position': 'absolute',
33208    //     'width': width.toString() + 'px',
33209    //     'height': height.toString() + 'px',
33210    //     'opacity': '0.2'
33211    //   });
33212    //   Insert.append(SugarBody.body(), debugArea);
33213    // };
33214    const calcSnap = (selectorOpt, td, x, y, width, height) => selectorOpt.fold(() => Dragging.snap({
33215        sensor: absolute$1(x - snapOffset, y - snapOffset),
33216        range: SugarPosition(width, height),
33217        output: absolute$1(Optional.some(x), Optional.some(y)),
33218        extra: {
33219            td
33220        }
33221    }), (selectorHandle) => {
33222        const sensorLeft = x - snapOffset;
33223        const sensorTop = y - snapOffset;
33224        const sensorWidth = snapWidth; // box.width();
33225        const sensorHeight = snapWidth; // box.height();
33226        const rect = selectorHandle.element.dom.getBoundingClientRect();
33227        // insertDebugDiv(sensorLeft, sensorTop, sensorWidth, sensorHeight, 'green', 'top-left-snap-debug');
33228        return Dragging.snap({
33229            sensor: absolute$1(sensorLeft, sensorTop),
33230            range: SugarPosition(sensorWidth, sensorHeight),
33231            output: absolute$1(Optional.some(x - (rect.width / 2)), Optional.some(y - (rect.height / 2))),
33232            extra: {
33233                td
33234            }
33235        });
33236    });
33237    const getSnapsConfig = (getSnapPoints, cell, onChange) => {
33238        // Can't use Optional.is() here since we need to do a dom compare, not an equality compare
33239        const isSameCell = (cellOpt, td) => cellOpt.exists((currentTd) => eq(currentTd, td));
33240        return {
33241            getSnapPoints,
33242            leftAttr: 'data-drag-left',
33243            topAttr: 'data-drag-top',
33244            onSensor: (component, extra) => {
33245                const td = extra.td;
33246                if (!isSameCell(cell.get(), td)) {
33247                    cell.set(td);
33248                    onChange(td);
33249                }
33250            },
33251            mustSnap: true
33252        };
33253    };
33254    const createSelector = (snaps) => record(Button.sketch({
33255        dom: {
33256            tag: 'div',
33257            classes: ['tox-selector']
33258        },
33259        buttonBehaviours: derive$1([
33260            Dragging.config({
33261                mode: 'mouseOrTouch',
33262                blockerClass: 'blocker',
33263                snaps
33264            }),
33265            Unselecting.config({})
33266        ]),
33267        eventOrder: {
33268            // Because this is a button, allow dragging. It will stop clicking.
33269            mousedown: ['dragging', 'alloy.base.behaviour'],
33270            touchstart: ['dragging', 'alloy.base.behaviour']
33271        }
33272    }));
33273    const setup$4 = (editor, sink) => {
33274        const tlTds = Cell([]);
33275        const brTds = Cell([]);
33276        const isVisible = Cell(false);
33277        const startCell = value$2();
33278        const finishCell = value$2();
33279        const getTopLeftSnap = (td) => {
33280            const box = absolute$2(td);
33281            return calcSnap(memTopLeft.getOpt(sink), td, box.x, box.y, box.width, box.height);
33282        };
33283        const getTopLeftSnaps = () => 
33284        // const body = SugarBody.body();
33285        // const debugs = SelectorFilter.descendants(body, '.top-left-snap-debug');
33286        // Arr.each(debugs, (debugArea) => {
33287        //   Remove.remove(debugArea);
33288        // });
33289        map$2(tlTds.get(), (td) => getTopLeftSnap(td));
33290        const getBottomRightSnap = (td) => {
33291            const box = absolute$2(td);
33292            return calcSnap(memBottomRight.getOpt(sink), td, box.right, box.bottom, box.width, box.height);
33293        };
33294        const getBottomRightSnaps = () => 
33295        // const body = SugarBody.body();
33296        // const debugs = SelectorFilter.descendants(body, '.bottom-right-snap-debug');
33297        // Arr.each(debugs, (debugArea) => {
33298        //   Remove.remove(debugArea);
33299        // });
33300        map$2(brTds.get(), (td) => getBottomRightSnap(td));
33301        const topLeftSnaps = getSnapsConfig(getTopLeftSnaps, startCell, (start) => {
33302            finishCell.get().each((finish) => {
33303                editor.dispatch('TableSelectorChange', { start, finish });
33304            });
33305        });
33306        const bottomRightSnaps = getSnapsConfig(getBottomRightSnaps, finishCell, (finish) => {
33307            startCell.get().each((start) => {
33308                editor.dispatch('TableSelectorChange', { start, finish });
33309            });
33310        });
33311        const memTopLeft = createSelector(topLeftSnaps);
33312        const memBottomRight = createSelector(bottomRightSnaps);
33313        const topLeft = build$1(memTopLeft.asSpec());
33314        const bottomRight = build$1(memBottomRight.asSpec());
33315        const showOrHideHandle = (selector, cell, isAbove, isBelow) => {
33316            const cellRect = cell.dom.getBoundingClientRect();
33317            remove$6(selector.element, 'display');
33318            const viewportHeight = defaultView(SugarElement.fromDom(editor.getBody())).dom.innerHeight;
33319            const aboveViewport = isAbove(cellRect);
33320            const belowViewport = isBelow(cellRect, viewportHeight);
33321            if (aboveViewport || belowViewport) {
33322                set$7(selector.element, 'display', 'none');
33323            }
33324        };
33325        const snapTo = (selector, cell, getSnapConfig, pos) => {
33326            const snap = getSnapConfig(cell);
33327            Dragging.snapTo(selector, snap);
33328            const isAbove = (rect) => rect[pos] < 0;
33329            const isBelow = (rect, viewportHeight) => rect[pos] > viewportHeight;
33330            showOrHideHandle(selector, cell, isAbove, isBelow);
33331        };
33332        const snapTopLeft = (cell) => snapTo(topLeft, cell, getTopLeftSnap, 'top');
33333        const snapLastTopLeft = () => startCell.get().each(snapTopLeft);
33334        const snapBottomRight = (cell) => snapTo(bottomRight, cell, getBottomRightSnap, 'bottom');
33335        const snapLastBottomRight = () => finishCell.get().each(snapBottomRight);
33336        // TODO: Make this work for desktop maybe?
33337        if (detect$1().deviceType.isTouch()) {
33338            const domToSugar = (arr) => map$2(arr, SugarElement.fromDom);
33339            editor.on('TableSelectionChange', (e) => {
33340                if (!isVisible.get()) {
33341                    attach(sink, topLeft);
33342                    attach(sink, bottomRight);
33343                    isVisible.set(true);
33344                }
33345                const start = SugarElement.fromDom(e.start);
33346                const finish = SugarElement.fromDom(e.finish);
33347                startCell.set(start);
33348                finishCell.set(finish);
33349                Optional.from(e.otherCells).each((otherCells) => {
33350                    tlTds.set(domToSugar(otherCells.upOrLeftCells));
33351                    brTds.set(domToSugar(otherCells.downOrRightCells));
33352                    snapTopLeft(start);
33353                    snapBottomRight(finish);
33354                });
33355            });
33356            editor.on('ResizeEditor ResizeWindow ScrollContent', () => {
33357                snapLastTopLeft();
33358                snapLastBottomRight();
33359            });
33360            editor.on('TableSelectionClear', () => {
33361                if (isVisible.get()) {
33362                    detach(topLeft);
33363                    detach(bottomRight);
33364                    isVisible.set(false);
33365                }
33366                startCell.clear();
33367                finishCell.clear();
33368            });
33369        }
33370    };
33371
33372    var Logo = "<svg height=\"16\" viewBox=\"0 0 80 16\" width=\"80\" xmlns=\"http://www.w3.org/2000/svg\"><g opacity=\".8\"><path d=\"m80 3.537v-2.202h-7.976v11.585h7.976v-2.25h-5.474v-2.621h4.812v-2.069h-4.812v-2.443zm-10.647 6.929c-.493.217-1.13.337-1.864.337s-1.276-.156-1.805-.47a3.732 3.732 0 0 1 -1.3-1.298c-.324-.554-.48-1.191-.48-1.877s.156-1.335.48-1.877a3.635 3.635 0 0 1 1.3-1.299 3.466 3.466 0 0 1 1.805-.481c.65 0 .914.06 1.263.18.36.12.698.277.986.47.289.192.578.384.842.6l.12.085v-2.586l-.023-.024c-.385-.35-.855-.614-1.384-.818-.53-.205-1.155-.313-1.877-.313-.721 0-1.6.144-2.333.445a5.773 5.773 0 0 0 -1.937 1.251 5.929 5.929 0 0 0 -1.324 1.9c-.324.735-.48 1.565-.48 2.455s.156 1.72.48 2.454c.325.734.758 1.383 1.324 1.913.553.53 1.215.938 1.937 1.25a6.286 6.286 0 0 0 2.333.434c.819 0 1.384-.108 1.961-.313.59-.216 1.083-.505 1.468-.866l.024-.024v-2.49l-.12.096c-.41.337-.878.626-1.396.866zm-14.869-4.15-4.8-5.04-.024-.025h-.902v11.67h2.502v-6.847l2.827 3.08.385.409.397-.41 2.791-3.067v6.845h2.502v-11.679h-.902l-4.788 5.052z\"/><path clip-rule=\"evenodd\" d=\"m15.543 5.137c0-3.032-2.466-5.113-4.957-5.137-.36 0-.745.024-1.094.096-.157.024-3.85.758-3.85.758-3.032.602-4.62 2.466-4.704 4.788-.024.89-.024 4.27-.024 4.27.036 3.165 2.406 5.138 5.017 5.126.337 0 1.119-.109 1.287-.145.144-.024.385-.084.746-.144.661-.12 1.684-.325 3.067-.602 2.37-.409 4.103-2.009 4.44-4.33.156-1.023.084-4.692.084-4.692zm-3.213 3.308-2.346.457v2.31l-5.859 1.143v-5.75l2.346-.458v3.441l3.513-.686v-3.44l-3.513.685v-2.297l5.859-1.143v5.75zm20.09-3.296-.083-1.023h-2.13v8.794h2.346v-4.884c0-1.107.95-1.985 2.057-1.997 1.095 0 1.901.89 1.901 1.997v4.884h2.346v-5.245c-.012-2.105-1.588-3.777-3.67-3.765a3.764 3.764 0 0 0 -2.778 1.25l.012-.011zm-6.014-4.102 2.346-.458v2.298l-2.346.457z\" fill-rule=\"evenodd\"/><path d=\"m28.752 4.126h-2.346v8.794h2.346z\"/><path clip-rule=\"evenodd\" d=\"m43.777 15.483 4.043-11.357h-2.418l-1.54 4.355-.445 1.324-.36-1.324-1.54-4.355h-2.418l3.151 8.794-1.083 3.08zm-21.028-5.51c0 .722.541 1.034.878 1.034s.638-.048.95-.144l.518 1.708c-.217.145-.879.518-2.13.518a2.565 2.565 0 0 1 -2.562-2.587c-.024-1.082-.024-2.49 0-4.21h-1.54v-2.142h1.54v-1.912l2.346-.458v2.37h2.201v2.142h-2.2v3.693-.012z\" fill-rule=\"evenodd\"/></g></svg>\n";
33373
33374    const isHidden = (elm) => elm.nodeName === 'BR' || !!elm.getAttribute('data-mce-bogus') || elm.getAttribute('data-mce-type') === 'bookmark';
33375    const renderElementPath = (editor, settings, providersBackstage) => {
33376        const delimiter = settings.delimiter ?? '\u203A';
33377        const renderElement = (name, element, index) => Button.sketch({
33378            dom: {
33379                tag: 'div',
33380                classes: ['tox-statusbar__path-item'],
33381                attributes: {
33382                    'data-index': index,
33383                }
33384            },
33385            components: [
33386                text$2(name)
33387            ],
33388            action: (_btn) => {
33389                editor.focus();
33390                editor.selection.select(element);
33391                editor.nodeChanged();
33392            },
33393            buttonBehaviours: derive$1([
33394                Tooltipping.config({
33395                    ...providersBackstage.tooltips.getConfig({
33396                        tooltipText: providersBackstage.translate(['Select the {0} element', element.nodeName.toLowerCase()]),
33397                        onShow: (comp, tooltip) => {
33398                            describedBy(comp.element, tooltip.element);
33399                        },
33400                        onHide: (comp) => {
33401                            remove$1(comp.element);
33402                        }
33403                    }),
33404                }),
33405                DisablingConfigs.button(providersBackstage.isDisabled),
33406                toggleOnReceive(() => providersBackstage.checkUiComponentContext('any'))
33407            ])
33408        });
33409        const renderDivider = () => ({
33410            dom: {
33411                tag: 'div',
33412                classes: ['tox-statusbar__path-divider'],
33413                attributes: {
33414                    'aria-hidden': true
33415                }
33416            },
33417            components: [
33418                text$2(` ${delimiter} `)
33419            ]
33420        });
33421        const renderPathData = (data) => foldl(data, (acc, path, index) => {
33422            const element = renderElement(path.name, path.element, index);
33423            if (index === 0) {
33424                return acc.concat([element]);
33425            }
33426            else {
33427                return acc.concat([renderDivider(), element]);
33428            }
33429        }, []);
33430        const updatePath = (parents) => {
33431            const newPath = [];
33432            let i = parents.length;
33433            while (i-- > 0) {
33434                const parent = parents[i];
33435                if (parent.nodeType === 1 && !isHidden(parent)) {
33436                    const args = fireResolveName(editor, parent);
33437                    if (!args.isDefaultPrevented()) {
33438                        newPath.push({ name: args.name, element: parent });
33439                    }
33440                    if (args.isPropagationStopped()) {
33441                        break;
33442                    }
33443                }
33444            }
33445            return newPath;
33446        };
33447        return {
33448            dom: {
33449                tag: 'div',
33450                classes: ['tox-statusbar__path'],
33451                attributes: {
33452                    'role': 'group',
33453                    'aria-label': providersBackstage.translate('Element Path')
33454                }
33455            },
33456            behaviours: derive$1([
33457                Keying.config({
33458                    mode: 'flow',
33459                    selector: 'div[role=button]'
33460                }),
33461                Disabling.config({
33462                    disabled: providersBackstage.isDisabled
33463                }),
33464                toggleOnReceive(() => providersBackstage.checkUiComponentContext('any')),
33465                Tabstopping.config({}),
33466                Replacing.config({}),
33467                config('elementPathEvents', [
33468                    runOnAttached((comp, _e) => {
33469                        // NOTE: If statusbar ever gets re-rendered, we will need to free this.
33470                        editor.shortcuts.add('alt+F11', 'focus statusbar elementpath', () => Keying.focusIn(comp));
33471                        editor.on('NodeChange', (e) => {
33472                            const newPath = updatePath(e.parents);
33473                            const newChildren = newPath.length > 0 ? renderPathData(newPath) : [];
33474                            Replacing.set(comp, newChildren);
33475                        });
33476                    })
33477                ])
33478            ]),
33479            components: []
33480        };
33481    };
33482
33483    var ResizeTypes;
33484    (function (ResizeTypes) {
33485        ResizeTypes[ResizeTypes["None"] = 0] = "None";
33486        ResizeTypes[ResizeTypes["Both"] = 1] = "Both";
33487        ResizeTypes[ResizeTypes["Vertical"] = 2] = "Vertical";
33488    })(ResizeTypes || (ResizeTypes = {}));
33489    const getOriginalDimensions = (editor) => {
33490        const container = SugarElement.fromDom(editor.getContainer());
33491        const originalHeight = get$d(container);
33492        const originalWidth = get$c(container);
33493        return {
33494            height: originalHeight,
33495            width: originalWidth,
33496        };
33497    };
33498    const getDimensions = (editor, deltas, resizeType, originalDimentions) => {
33499        const height = calcCappedSize(originalDimentions.height + deltas.top, getMinHeightOption(editor), getMaxHeightOption(editor));
33500        if (resizeType === ResizeTypes.Both) {
33501            return {
33502                height,
33503                width: calcCappedSize(originalDimentions.width + deltas.left, getMinWidthOption(editor), getMaxWidthOption(editor))
33504            };
33505        }
33506        return { height };
33507    };
33508    const resize = (editor, deltas, resizeType) => {
33509        const container = SugarElement.fromDom(editor.getContainer());
33510        const originalDimensions = getOriginalDimensions(editor);
33511        const dimensions = getDimensions(editor, deltas, resizeType, originalDimensions);
33512        each(dimensions, (val, dim) => {
33513            if (isNumber(val)) {
33514                set$7(container, dim, numToPx(val));
33515            }
33516        });
33517        fireResizeEditor(editor);
33518        return dimensions;
33519    };
33520
33521    const keyboardResizeStepInPx = 20;
33522    const getResizeType = (editor) => {
33523        const resize = getResize(editor);
33524        if (resize === false) {
33525            return ResizeTypes.None;
33526        }
33527        else if (resize === 'both') {
33528            return ResizeTypes.Both;
33529        }
33530        else {
33531            return ResizeTypes.Vertical;
33532        }
33533    };
33534    const getAriaValuetext = (dimensions, resizeType) => {
33535        return resizeType === ResizeTypes.Both
33536            ? global$6.translate([`Editor's height: {0} pixels, Editor's width: {1} pixels`, dimensions.height, dimensions.width])
33537            : global$6.translate([`Editor's height: {0} pixels`, dimensions.height]);
33538    };
33539    const setAriaDimensions = (comp, dimensions, resizeType, minHeight, maxHeight) => {
33540        set$9(comp.element, 'aria-valuetext', getAriaValuetext(dimensions, resizeType));
33541        // aria-valuenow/valuemin/valuemax are single numeric values, so they can only
33542        // meaningfully represent one dimension. In 'both' mode the handle resizes both
33543        // width and height, which has no sensible single-value representation, so we only
33544        // set these attributes for vertical resizing and rely on aria-valuetext otherwise.
33545        if (resizeType === ResizeTypes.Vertical) {
33546            set$9(comp.element, 'aria-valuenow', dimensions.height);
33547            set$9(comp.element, 'aria-valuemin', minHeight.getOr(0));
33548            set$9(comp.element, 'aria-valuemax', maxHeight.getOr(dimensions.height + keyboardResizeStepInPx));
33549        }
33550    };
33551    const keyboardHandler = (editor, comp, resizeType, x, y, minHeight, maxHeight) => {
33552        const delta = SugarPosition(x * keyboardResizeStepInPx, y * keyboardResizeStepInPx);
33553        const newDimentions = resize(editor, delta, resizeType);
33554        setAriaDimensions(comp, newDimentions, resizeType, minHeight, maxHeight);
33555        return Optional.some(true);
33556    };
33557    const renderResizeHandler = (editor, providersBackstage) => {
33558        const resizeType = getResizeType(editor);
33559        if (resizeType === ResizeTypes.None) {
33560            return Optional.none();
33561        }
33562        const minHeight = getMinHeightOption(editor);
33563        const maxHeight = getMaxHeightOption(editor);
33564        const resizeLabel = resizeType === ResizeTypes.Both
33565            ? global$6.translate('Press the arrow keys to resize the editor.')
33566            : global$6.translate('Press the Up and Down arrow keys to resize the editor.');
33567        const cursorClass = resizeType === ResizeTypes.Both
33568            ? 'tox-statusbar__resize-cursor-both'
33569            : 'tox-statusbar__resize-cursor-default';
33570        return Optional.some(render$4('resize-handle', {
33571            tag: 'div',
33572            classes: ['tox-statusbar__resize-handle', cursorClass],
33573            attributes: {
33574                'aria-label': providersBackstage.translate(resizeLabel),
33575                'data-mce-name': 'resize-handle',
33576                'role': 'separator'
33577            },
33578            behaviours: [
33579                Dragging.config({
33580                    mode: 'pointer',
33581                    repositionTarget: false,
33582                    onDragStart: (comp) => {
33583                        Tooltipping.immediateOpenClose(comp, false);
33584                        Tooltipping.setEnabled(comp, false);
33585                    },
33586                    onDrag: (comp, _target, delta) => {
33587                        const newDimentions = resize(editor, delta, resizeType);
33588                        setAriaDimensions(comp, newDimentions, resizeType, minHeight, maxHeight);
33589                    },
33590                    onDrop: (comp) => {
33591                        Tooltipping.setEnabled(comp, true);
33592                    },
33593                }),
33594                Keying.config({
33595                    mode: 'special',
33596                    onLeft: (comp) => keyboardHandler(editor, comp, resizeType, -1, 0, minHeight, maxHeight),
33597                    onRight: (comp) => keyboardHandler(editor, comp, resizeType, 1, 0, minHeight, maxHeight),
33598                    onUp: (comp) => keyboardHandler(editor, comp, resizeType, 0, -1, minHeight, maxHeight),
33599                    onDown: (comp) => keyboardHandler(editor, comp, resizeType, 0, 1, minHeight, maxHeight),
33600                }),
33601                Tabstopping.config({}),
33602                Focusing.config({}),
33603                Tooltipping.config(providersBackstage.tooltips.getConfig({
33604                    tooltipText: providersBackstage.translate('Resize')
33605                })),
33606                config('set-aria-valuetext', [
33607                    runOnAttached((comp) => {
33608                        const setInitialValuetext = () => {
33609                            setAriaDimensions(comp, getOriginalDimensions(editor), resizeType, minHeight, maxHeight);
33610                        };
33611                        if (editor._skinLoaded) {
33612                            setInitialValuetext();
33613                        }
33614                        else {
33615                            editor.once('SkinLoaded', setInitialValuetext);
33616                        }
33617                    })
33618                ])
33619            ],
33620            eventOrder: {
33621                [attachedToDom()]: ['add-focusable', 'set-aria-valuetext']
33622            }
33623        }, providersBackstage.icons));
33624    };
33625
33626    const renderWordCount = (editor, providersBackstage) => {
33627        const replaceCountText = (comp, count, mode) => Replacing.set(comp, [text$2(providersBackstage.translate(['{0} ' + mode, count[mode]]))]);
33628        return Button.sketch({
33629            dom: {
33630                // The tag for word count was changed to 'button' as Jaws does not read out spans.
33631                // Word count is just a toggle and changes modes between words and characters.
33632                tag: 'button',
33633                classes: ['tox-statusbar__wordcount']
33634            },
33635            components: [],
33636            buttonBehaviours: derive$1([
33637                DisablingConfigs.button(providersBackstage.isDisabled),
33638                toggleOnReceive(() => providersBackstage.checkUiComponentContext('any')),
33639                Tabstopping.config({}),
33640                Replacing.config({}),
33641                Representing.config({
33642                    store: {
33643                        mode: 'memory',
33644                        initialValue: {
33645                            mode: "words" /* WordCountMode.Words */,
33646                            count: { words: 0, characters: 0 }
33647                        }
33648                    }
33649                }),
33650                config('wordcount-events', [
33651                    runOnExecute$1((comp) => {
33652                        const currentVal = Representing.getValue(comp);
33653                        const newMode = currentVal.mode === "words" /* WordCountMode.Words */ ? "characters" /* WordCountMode.Characters */ : "words" /* WordCountMode.Words */;
33654                        Representing.setValue(comp, { mode: newMode, count: currentVal.count });
33655                        replaceCountText(comp, currentVal.count, newMode);
33656                    }),
33657                    runOnAttached((comp) => {
33658                        editor.on('wordCountUpdate', (e) => {
33659                            const { mode } = Representing.getValue(comp);
33660                            Representing.setValue(comp, { mode, count: e.wordCount });
33661                            replaceCountText(comp, e.wordCount, mode);
33662                        });
33663                    })
33664                ])
33665            ]),
33666            eventOrder: {
33667                [execute$5()]: ['disabling', 'alloy.base.behaviour', 'wordcount-events']
33668            }
33669        });
33670    };
33671
33672    const renderStatusbar = (editor, providersBackstage) => {
33673        const renderBranding = () => {
33674            return {
33675                dom: {
33676                    tag: 'span',
33677                    classes: ['tox-statusbar__branding'],
33678                },
33679                components: [
33680                    {
33681                        dom: {
33682                            tag: 'a',
33683                            attributes: {
33684                                'href': 'https://www.tiny.cloud/powered-by-tiny?utm_campaign=p
33684oweredby&utm_source=tiny&utm_medium=referral&utm_content=v7',
33685                                'rel': 'noopener',
33686                                'target': '_blank',
33687                                'aria-label': editor.translate(['Build with {0}', 'TinyMCE'])
33688                            },
33689                            innerHtml: editor.translate(['Build with {0}', Logo.trim()])
33690                        },
33691                        behaviours: derive$1([
33692                            Focusing.config({})
33693                        ])
33694                    }
33695                ]
33696            };
33697        };
33698        const renderHelpAccessibility = () => {
33699            const shortcutText = convertText('Alt+0');
33700            const text = `Press {0} for help`;
33701            return {
33702                dom: {
33703                    tag: 'div',
33704                    classes: ['tox-statusbar__help-text'],
33705                },
33706                components: [
33707                    text$2(global$6.translate([text, shortcutText]))
33708                ]
33709            };
33710        };
33711        const renderRightContainer = () => {
33712            const components = [];
33713            if (editor.hasPlugin('wordcount')) {
33714                components.push(renderWordCount(editor, providersBackstage));
33715            }
33716            if (useBranding(editor)) {
33717                components.push(renderBranding());
33718            }
33719            return {
33720                dom: {
33721                    tag: 'div',
33722                    classes: ['tox-statusbar__right-container']
33723                },
33724                components
33725            };
33726        };
33727        const getTextComponents = () => {
33728            const components = [];
33729            const shouldRenderHelp = useHelpAccessibility(editor);
33730            const shouldRenderElementPath = useElementPath(editor);
33731            const shouldRenderRightContainer = useBranding(editor) || editor.hasPlugin('wordcount');
33732            const getTextComponentClasses = () => {
33733                const flexStart = 'tox-statusbar__text-container--flex-start';
33734                const flexEnd = 'tox-statusbar__text-container--flex-end';
33735                const spaceAround = 'tox-statusbar__text-container--space-around';
33736                if (shouldRenderHelp) {
33737                    const container3Columns = 'tox-statusbar__text-container-3-cols';
33738                    if (!shouldRenderRightContainer && !shouldRenderElementPath) {
33739                        return [container3Columns, spaceAround];
33740                    }
33741                    if (shouldRenderRightContainer && !shouldRenderElementPath) {
33742                        return [container3Columns, flexEnd];
33743                    }
33744                    return [container3Columns, flexStart];
33745                }
33746                return [shouldRenderRightContainer && !shouldRenderElementPath ? flexEnd : flexStart];
33747            };
33748            if (shouldRenderElementPath) {
33749                components.push(renderElementPath(editor, {}, providersBackstage));
33750            }
33751            if (shouldRenderHelp) {
33752                components.push(renderHelpAccessibility());
33753            }
33754            if (shouldRenderRightContainer) {
33755                components.push(renderRightContainer());
33756            }
33757            if (components.length > 0) {
33758                return [{
33759                        dom: {
33760                            tag: 'div',
33761                            classes: ['tox-statusbar__text-container', ...getTextComponentClasses()]
33762                        },
33763                        components
33764                    }];
33765            }
33766            return [];
33767        };
33768        const getComponents = () => {
33769            const components = getTextComponents();
33770            const resizeHandler = renderResizeHandler(editor, providersBackstage);
33771            return components.concat(resizeHandler.toArray());
33772        };
33773        return {
33774            dom: {
33775                tag: 'div',
33776                classes: ['tox-statusbar']
33777            },
33778            components: getComponents()
33779        };
33780    };
33781
33782    const getLazyMothership = (label, singleton) => singleton.get().getOrDie(`UI for ${label} has not been rendered`);
33783    const setup$3 = (editor, setupForTheme) => {
33784        const isInline = editor.inline;
33785        const mode = isInline ? Inline : Iframe;
vendor: 25,447 bytes, lines 33786-34331
33786        // We use a different component for creating the sticky toolbar behaviour. The
33787        // most important difference is it needs "Docking" configured and all of the
33788        // ripple effects that creates.
33789        const header = isStickyToolbar(editor) ? StickyHeader : StaticHeader;
33790        const lazyUiRefs = LazyUiReferences();
33791        // Importantly, this is outside the setup function.
33792        const lazyMothership = value$2();
33793        const lazyDialogMothership = value$2();
33794        const lazyPopupMothership = value$2();
33795        const platform = detect$1();
33796        const isTouch = platform.deviceType.isTouch();
33797        const touchPlatformClass = 'tox-platform-touch';
33798        const deviceClasses = isTouch ? [touchPlatformClass] : [];
33799        const isToolbarBottom = isToolbarLocationBottom(editor);
33800        const toolbarMode = getToolbarMode(editor);
33801        const memAnchorBar = record({
33802            dom: {
33803                tag: 'div',
33804                classes: ['tox-anchorbar']
33805            }
33806        });
33807        const memBottomAnchorBar = record({
33808            dom: {
33809                tag: 'div',
33810                classes: ['tox-bottom-anchorbar']
33811            }
33812        });
33813        const lazyHeader = () => lazyUiRefs.mainUi.get()
33814            .map((ui) => ui.outerContainer)
33815            .bind(OuterContainer.getHeader);
33816        const lazyDialogSinkResult = () => Result.fromOption(lazyUiRefs.dialogUi.get().map((ui) => ui.sink), 'UI has not been rendered');
33817        const lazyPopupSinkResult = () => Result.fromOption(lazyUiRefs.popupUi.get().map((ui) => ui.sink), '(popup) UI has not been rendered');
33818        const lazyAnchorBar = lazyUiRefs.lazyGetInOuterOrDie('anchor bar', memAnchorBar.getOpt);
33819        const lazyBottomAnchorBar = lazyUiRefs.lazyGetInOuterOrDie('bottom anchor bar', memBottomAnchorBar.getOpt);
33820        const lazyToolbar = lazyUiRefs.lazyGetInOuterOrDie('toolbar', OuterContainer.getToolbar);
33821        const lazyThrobber = lazyUiRefs.lazyGetInOuterOrDie('throbber', OuterContainer.getThrobber);
33822        // Here, we build the backstage. The backstage is going to use different sinks for dialog
33823        // vs popup.
33824        const backstages = init({
33825            popup: lazyPopupSinkResult,
33826            dialog: lazyDialogSinkResult
33827        }, editor, lazyAnchorBar, lazyBottomAnchorBar);
33828        const makeHeaderPart = () => {
33829            const verticalDirAttributes = {
33830                attributes: {
33831                    [Attribute]: isToolbarBottom ?
33832                        AttributeValue.BottomToTop :
33833                        AttributeValue.TopToBottom
33834                }
33835            };
33836            const partMenubar = OuterContainer.parts.menubar({
33837                dom: {
33838                    tag: 'div',
33839                    classes: ['tox-menubar']
33840                },
33841                // TINY-9223: The menu bar should scroll with the editor.
33842                backstage: backstages.popup,
33843                onEscape: () => {
33844                    editor.focus();
33845                }
33846            });
33847            const partToolbar = OuterContainer.parts.toolbar({
33848                dom: {
33849                    tag: 'div',
33850                    classes: ['tox-toolbar']
33851                },
33852                getSink: backstages.popup.shared.getSink,
33853                providers: backstages.popup.shared.providers,
33854                onEscape: () => {
33855                    editor.focus();
33856                },
33857                onToolbarToggled: (state) => {
33858                    fireToggleToolbarDrawer(editor, state);
33859                },
33860                type: toolbarMode,
33861                lazyToolbar,
33862                lazyHeader: () => lazyHeader().getOrDie('Could not find header element'),
33863                ...verticalDirAttributes
33864            });
33865            const partMultipleToolbar = OuterContainer.parts['multiple-toolbar']({
33866                dom: {
33867                    tag: 'div',
33868                    classes: ['tox-toolbar-overlord']
33869                },
33870                providers: backstages.popup.shared.providers,
33871                onEscape: () => {
33872                    editor.focus();
33873                },
33874                type: toolbarMode
33875            });
33876            // False should stop the menubar and toolbar rendering altogether
33877            const hasMultipleToolbar = isMultipleToolbars(editor);
33878            const hasToolbar = isToolbarEnabled(editor);
33879            const hasMenubar = isMenubarEnabled(editor);
33880            const shouldHavePromotionLink = promotionEnabled(editor);
33881            const partPromotion = makePromotion(shouldHavePromotionLink);
33882            const hasAnyContents = hasMultipleToolbar || hasToolbar || hasMenubar;
33883            const getPartToolbar = () => {
33884                if (hasMultipleToolbar) {
33885                    return [partMultipleToolbar];
33886                }
33887                else if (hasToolbar) {
33888                    return [partToolbar];
33889                }
33890                else {
33891                    return [];
33892                }
33893            };
33894            const menubarCollection = [partPromotion, partMenubar];
33895            return OuterContainer.parts.header({
33896                dom: {
33897                    tag: 'div',
33898                    classes: ['tox-editor-header']
33899                        .concat(hasAnyContents ? [] : ['tox-editor-header--empty']),
33900                    ...verticalDirAttributes
33901                },
33902                components: flatten([
33903                    hasMenubar ? menubarCollection : [],
33904                    getPartToolbar(),
33905                    // fixed_toolbar_container anchors to the editable area, else add an anchor bar
33906                    useFixedContainer(editor) ? [] : [memAnchorBar.asSpec()]
33907                ]),
33908                sticky: isStickyToolbar(editor),
33909                editor,
33910                // TINY-9223: If using a sticky toolbar, which sink should it really go in?
33911                sharedBackstage: backstages.popup.shared
33912            });
33913        };
33914        const makePromotion = (promotionLink) => {
33915            return OuterContainer.parts.promotion({
33916                dom: {
33917                    tag: 'div',
33918                    classes: ['tox-promotion'],
33919                },
33920                promotionLink
33921            });
33922        };
33923        const makeSidebarDefinition = () => {
33924            const partSocket = OuterContainer.parts.socket({
33925                dom: {
33926                    tag: 'div',
33927                    classes: ['tox-edit-area']
33928                }
33929            });
33930            const partSidebar = OuterContainer.parts.sidebar({
33931                dom: {
33932                    tag: 'div',
33933                    classes: ['tox-sidebar']
33934                }
33935            });
33936            return {
33937                dom: {
33938                    tag: 'div',
33939                    classes: ['tox-sidebar-wrap']
33940                },
33941                components: [
33942                    partSocket,
33943                    partSidebar
33944                ]
33945            };
33946        };
33947        // TINY-14384: we want to restrict the bounds to the host element, rather than the entire window when the sink is attached in a ShadowDOM (ie: webcomponent)
33948        const getSinkBounds = (sinkOpt) => sinkOpt.bind(({ sink }) => {
33949            const rootNode = getRootNode(sink.element);
33950            if (isShadowRoot(rootNode)) {
33951                return Optional.some(box$1(getShadowHost(rootNode)));
33952            }
33953            return Optional.none();
33954        }).getOr(win());
33955        const renderDialogUi = () => {
33956            const uiContainer = getUiContainer(editor);
33957            // TINY-3321: When the body is using a grid layout, we need to ensure the sink width is manually set
33958            const isGridUiContainer = eq(body(), uiContainer) && get$e(uiContainer, 'display') === 'grid';
33959            const sinkSpec = {
33960                dom: {
33961                    tag: 'div',
33962                    classes: ['tox', 'tox-silver-sink', 'tox-tinymce-aux'].concat(deviceClasses),
33963                    attributes: {
33964                        ...global$6.isRtl() ? { dir: 'rtl' } : {}
33965                    }
33966                },
33967                behaviours: derive$1([
33968                    Positioning.config({
33969                        useFixed: () => header.isDocked(lazyHeader),
33970                        getBounds: () => getSinkBounds(lazyUiRefs.dialogUi.get())
33971                    }),
33972                ])
33973            };
33974            const reactiveWidthSpec = {
33975                dom: {
33976                    styles: { width: document.body.clientWidth + 'px' }
33977                },
33978                events: derive$2([
33979                    run$1(windowResize(), (comp) => {
33980                        set$7(comp.element, 'width', document.body.clientWidth + 'px');
33981                    })
33982                ])
33983            };
33984            const sink = build$1(deepMerge(sinkSpec, isGridUiContainer ? reactiveWidthSpec : {}));
33985            const uiMothership = takeover(sink);
33986            lazyDialogMothership.set(uiMothership);
33987            return { sink, mothership: uiMothership };
33988        };
33989        const renderPopupUi = () => {
33990            // TINY-9226: Because the popupUi is going to be placed adjacent to the editor, we aren't currently
33991            // implementing the behaviour to reset widths based on window sizing. It is a workaround that
33992            // is mainly targeted at Ui containers in the root. However, we may need to revisit this
33993            // if the ui_mode: split setting is commonly used when the editor is at the root level, and the
33994            // page has size-unfriendly CSS for sinks (like CSS grid)
33995            const sinkSpec = {
33996                dom: {
33997                    tag: 'div',
33998                    classes: ['tox', 'tox-silver-sink', 'tox-silver-popup-sink', 'tox-tinymce-aux'].concat(deviceClasses),
33999                    attributes: {
34000                        ...global$6.isRtl() ? { dir: 'rtl' } : {}
34001                    }
34002                },
34003                behaviours: derive$1([
34004                    Positioning.config({
34005                        useFixed: () => header.isDocked(lazyHeader),
34006                        // TINY-9226: We want to limit the popup sink's bounds based on its scrolling environment. We don't
34007                        // want it to try to position things outside of its scrolling viewport, because they will
34008                        // just appear offscreen (hidden by the current scroll values)
34009                        getBounds: () => setupForTheme.getPopupSinkBounds()
34010                    })
34011                ])
34012            };
34013            const sink = build$1(sinkSpec);
34014            const uiMothership = takeover(sink);
34015            lazyPopupMothership.set(uiMothership);
34016            return { sink, mothership: uiMothership };
34017        };
34018        const renderMainUi = () => {
34019            const partHeader = makeHeaderPart();
34020            const sidebarContainer = makeSidebarDefinition();
34021            const partThrobber = OuterContainer.parts.throbber({
34022                dom: {
34023                    tag: 'div',
34024                    classes: ['tox-throbber']
34025                },
34026                backstage: backstages.popup
34027            });
34028            const partViewWrapper = OuterContainer.parts.viewWrapper({
34029                backstage: backstages.popup
34030            });
34031            const statusbar = useStatusBar(editor) && !isInline ? Optional.some(renderStatusbar(editor, backstages.popup.shared.providers)) : Optional.none();
34032            // We need the statusbar to be separate to everything else so resizing works properly
34033            const editorComponents = flatten([
34034                isToolbarBottom ? [] : [partHeader],
34035                // Inline mode does not have a socket/sidebar
34036                isInline ? [] : [sidebarContainer],
34037                isToolbarBottom ? [partHeader] : []
34038            ]);
34039            const editorContainer = OuterContainer.parts.editorContainer({
34040                components: flatten([
34041                    editorComponents,
34042                    isInline ? [] : [memBottomAnchorBar.asSpec()]
34043                ])
34044            });
34045            // Hide the outer container if using inline mode and there's no menubar or toolbar
34046            const isHidden = isDistractionFree(editor);
34047            const attributes = {
34048                role: 'application',
34049                ...global$6.isRtl() ? { dir: 'rtl' } : {},
34050                ...isHidden ? { 'aria-hidden': 'true' } : {}
34051            };
34052            const outerContainer = build$1(OuterContainer.sketch({
34053                dom: {
34054                    tag: 'div',
34055                    classes: ['tox', 'tox-tinymce']
34056                        .concat(isInline ? ['tox-tinymce-inline'] : [])
34057                        .concat(isToolbarBottom ? ['tox-tinymce--toolbar-bottom'] : [])
34058                        .concat(deviceClasses),
34059                    styles: {
34060                        // This is overridden by the skin, it helps avoid FOUC
34061                        visibility: 'hidden',
34062                        // Hide the container if needed, but don't use "display: none" so that it still has a position
34063                        ...isHidden ? { opacity: '0', border: '0' } : {}
34064                    },
34065                    attributes
34066                },
34067                components: [
34068                    editorContainer,
34069                    // Inline mode does not have a status bar
34070                    ...isInline ? [] : [partViewWrapper, ...statusbar.toArray()],
34071                    partThrobber,
34072                ],
34073                behaviours: derive$1([
34074                    toggleOnReceive(() => backstages.popup.shared.providers.checkUiComponentContext('any')),
34075                    Disabling.config({
34076                        disableClass: 'tox-tinymce--disabled'
34077                    }),
34078                    Keying.config({
34079                        mode: 'cyclic',
34080                        selector: '.tox-menubar, .tox-toolbar, .tox-toolbar__primary, .tox-toolbar__overflow--open, .tox-sidebar__overflow--open, .tox-statusbar__path, .tox-statusbar__wordcount, .tox-statusbar__branding a, .tox-statusbar__resize-handle'
34081                    })
34082                ])
34083            }));
34084            const mothership = takeover(outerContainer);
34085            lazyMothership.set(mothership);
34086            return { mothership, outerContainer };
34087        };
34088        const setEditorSize = (outerContainer) => {
34089            // Set height and width if they were given, though height only applies to iframe mode
34090            const parsedHeight = numToPx(getHeightWithFallback(editor));
34091            const parsedWidth = numToPx(getWidthWithFallback(editor));
34092            if (!editor.inline) {
34093                // Update the width
34094                if (isValidValue$1('div', 'width', parsedWidth)) {
34095                    set$7(outerContainer.element, 'width', parsedWidth);
34096                }
34097                // Update the height
34098                if (isValidValue$1('div', 'height', parsedHeight)) {
34099                    set$7(outerContainer.element, 'height', parsedHeight);
34100                }
34101                else {
34102                    set$7(outerContainer.element, 'height', '400px');
34103                }
34104            }
34105            return parsedHeight;
34106        };
34107        const setupShortcutsAndCommands = (outerContainer) => {
34108            editor.addShortcut('alt+F9', 'focus menubar', () => {
34109                OuterContainer.focusMenubar(outerContainer);
34110            });
34111            editor.addShortcut('alt+F10', 'focus toolbar', () => {
34112                OuterContainer.focusToolbar(outerContainer);
34113            });
34114            editor.addCommand('ToggleToolbarDrawer', (_ui, options, args) => {
34115                if (options?.skipFocus) {
34116                    logFeatureDeprecationWarning('skipFocus');
34117                    OuterContainer.toggleToolbarDrawerWithoutFocusing(outerContainer);
34118                }
34119                else if (args?.skip_focus) {
34120                    OuterContainer.toggleToolbarDrawerWithoutFocusing(outerContainer);
34121                }
34122                else {
34123                    OuterContainer.toggleToolbarDrawer(outerContainer);
34124                }
34125            });
34126            editor.addQueryStateHandler('ToggleToolbarDrawer', () => OuterContainer.isToolbarDrawerToggled(outerContainer));
34127            editor.on('blur', () => {
34128                if (getToolbarMode(editor) === ToolbarMode$1.floating && OuterContainer.isToolbarDrawerToggled(outerContainer)) {
34129                    OuterContainer.toggleToolbarDrawerWithoutFocusing(outerContainer);
34130                }
34131            });
34132        };
34133        const renderUIWithRefs = (uiRefs) => {
34134            const { mainUi, popupUi, uiMotherships } = uiRefs;
34135            map$1(getToolbarGroups(editor), (toolbarGroupButtonConfig, name) => {
34136                editor.ui.registry.addGroupToolbarButton(name, toolbarGroupButtonConfig);
34137            });
34138            // Apply Bridge types
34139            const { buttons, menuItems, contextToolbars, sidebars, views } = editor.ui.registry.getAll();
34140            const toolbarOpt = getMultipleToolbarsOption(editor);
34141            const rawUiConfig = {
34142                menuItems,
34143                menus: getMenus(editor),
34144                menubar: getMenubar(editor),
34145                toolbar: toolbarOpt.getOrThunk(() => getToolbar(editor)),
34146                allowToolbarGroups: toolbarMode === ToolbarMode$1.floating,
34147                buttons,
34148                sidebar: sidebars,
34149                views
34150            };
34151            setupShortcutsAndCommands(mainUi.outerContainer);
34152            setup$b(editor, mainUi.mothership, uiMotherships);
34153            // This backstage needs to kept in sync with the one passed to the Header part.
34154            header.setup(editor, backstages.popup.shared, lazyHeader);
34155            // This backstage is probably needed for just the bespoke dropdowns
34156            setup$6(editor, backstages.popup);
34157            setup$5(editor, backstages.popup.shared.getSink, backstages.popup);
34158            setup$8(editor);
34159            setup$7(editor, lazyThrobber, backstages.popup.shared);
34160            register$a(editor, contextToolbars, popupUi.sink, { backstage: backstages.popup });
34161            setup$4(editor, popupUi.sink);
34162            const elm = editor.getElement();
34163            const height = setEditorSize(mainUi.outerContainer);
34164            const args = { targetNode: elm, height };
34165            // The only components that use backstages.dialog currently are the Modal dialogs.
34166            return mode.render(editor, uiRefs, rawUiConfig, backstages.popup, args);
34167        };
34168        const reuseDialogUiForPopuUi = (dialogUi) => {
34169            lazyPopupMothership.set(dialogUi.mothership);
34170            return dialogUi;
34171        };
34172        const renderUI = () => {
34173            const mainUi = renderMainUi();
34174            const dialogUi = renderDialogUi();
34175            // If dialogUi and popupUi are the same, LazyUiReferences should handle deduplicating then
34176            // get calling getUiMotherships
34177            const popupUi = isSplitUiMode(editor) ? renderPopupUi() : reuseDialogUiForPopuUi(dialogUi);
34178            lazyUiRefs.dialogUi.set(dialogUi);
34179            lazyUiRefs.popupUi.set(popupUi);
34180            lazyUiRefs.mainUi.set(mainUi);
34181            // From this point on, we shouldn't use LazyReferences any more.
34182            const uiRefs = {
34183                popupUi,
34184                dialogUi,
34185                mainUi,
34186                uiMotherships: lazyUiRefs.getUiMotherships()
34187            };
34188            return renderUIWithRefs(uiRefs);
34189        };
34190        // We don't have uiRefs here, so we have to rely on cells that are set by renderUI unfortunately.
34191        return {
34192            popups: {
34193                backstage: backstages.popup,
34194                getMothership: () => getLazyMothership('popups', lazyPopupMothership)
34195            },
34196            dialogs: {
34197                backstage: backstages.dialog,
34198                getMothership: () => getLazyMothership('dialogs', lazyDialogMothership)
34199            },
34200            renderUI
34201        };
34202    };
34203
34204    const toValidValues = (values) => {
34205        const errors = [];
34206        const result = {};
34207        each(values, (value, name) => {
34208            value.fold(() => {
34209                errors.push(name);
34210            }, (v) => {
34211                result[name] = v;
34212            });
34213        });
34214        return errors.length > 0 ? Result.error(errors) :
34215            Result.value(result);
34216    };
34217
34218    const renderBodyPanel = (spec, dialogData, backstage, getCompByName) => {
34219        const memForm = record(Form.sketch((parts) => ({
34220            dom: {
34221                tag: 'div',
34222                classes: ['tox-form'].concat(spec.classes)
34223            },
34224            // All of the items passed through the form need to be put through the interpreter
34225            // with their form part preserved.
34226            components: map$2(spec.items, (item) => interpretInForm(parts, item, dialogData, backstage, getCompByName))
34227        })));
34228        return {
34229            dom: {
34230                tag: 'div',
34231                classes: ['tox-dialog__body']
34232            },
34233            components: [
34234                {
34235                    dom: {
34236                        tag: 'div',
34237                        classes: ['tox-dialog__body-content']
34238                    },
34239                    components: [
34240                        memForm.asSpec()
34241                    ]
34242                }
34243            ],
34244            behaviours: derive$1([
34245                Keying.config({
34246                    mode: 'acyclic',
34247                    useTabstopAt: not(isPseudoStop)
34248                }),
34249                ComposingConfigs.memento(memForm),
34250                memento(memForm, {
34251                    postprocess: (formValue) => toValidValues(formValue).fold((err) => {
34252                        // eslint-disable-next-line no-console
34253                        console.error(err);
34254                        return {};
34255                    }, identity)
34256                }),
34257                config('dialog-body-panel', [
34258                    // TINY-10101: This is to cater for the case where clicks are made into the dialog instead using keyboard navigation, as FocusShifted would not be triggered in that case.
34259                    run$1(focusin(), (comp, se) => {
34260                        comp.getSystem().broadcastOn([dialogFocusShiftedChannel], {
34261                            newFocus: Optional.some(se.event.target)
34262                        });
34263                    }),
34264                ])
34265            ])
34266        };
34267    };
34268
34269    const measureHeights = (allTabs, tabview, tabviewComp) => map$2(allTabs, (_tab, i) => {
34270        Replacing.set(tabviewComp, allTabs[i].view());
34271        const rect = tabview.dom.getBoundingClientRect();
34272        Replacing.set(tabviewComp, []);
34273        return rect.height;
34274    });
34275    const getMaxHeight = (heights) => head(sort(heights, (a, b) => {
34276        if (a > b) {
34277            return -1;
34278        }
34279        else if (a < b) {
34280            return +1;
34281        }
34282        else {
34283            return 0;
34284        }
34285    }));
34286    const getMaxTabviewHeight = (dialog, tabview, tablist) => {
34287        const documentElement$1 = documentElement(dialog).dom;
34288        const rootElm = ancestor$1(dialog, '.tox-dialog-wrap').getOr(dialog);
34289        const isFixed = get$e(rootElm, 'position') === 'fixed';
34290        // Get the document or window/viewport height
34291        let maxHeight;
34292        if (isFixed) {
34293            maxHeight = Math.max(documentElement$1.clientHeight, window.innerHeight);
34294        }
34295        else {
34296            maxHeight = Math.max(documentElement$1.offsetHeight, documentElement$1.scrollHeight);
34297        }
34298        // Determine the current height taken up by the tabview panel
34299        const tabviewHeight = get$d(tabview);
34300        const isTabListBeside = tabview.dom.offsetLeft >= tablist.dom.offsetLeft + get$c(tablist);
34301        const currentTabHeight = isTabListBeside ? Math.max(get$d(tablist), tabviewHeight) : tabviewHeight;
34302        // Get the dialog height, making sure to account for any margins on the dialog
34303        const dialogTopMargin = parseInt(get$e(dialog, 'margin-top'), 10) || 0;
34304        const dialogBottomMargin = parseInt(get$e(dialog, 'margin-bottom'), 10) || 0;
34305        const dialogHeight = get$d(dialog) + dialogTopMargin + dialogBottomMargin;
34306        const chromeHeight = dialogHeight - currentTabHeight;
34307        return maxHeight - chromeHeight;
34308    };
34309    const showTab = (allTabs, comp) => {
34310        head(allTabs).each((tab) => TabSection.showTab(comp, tab.value));
34311    };
34312    const setTabviewHeight = (tabview, height) => {
34313        // Set both height and flex-basis as some browsers don't support flex-basis.
34314        set$7(tabview, 'height', height + 'px');
34315        set$7(tabview, 'flex-basis', height + 'px');
34316    };
34317    const updateTabviewHeight = (dialogBody, tabview, maxTabHeight) => {
34318        ancestor$1(dialogBody, '[role="dialog"]').each((dialog) => {
34319            descendant(dialog, '[role="tablist"]').each((tablist) => {
34320                maxTabHeight.get().map((height) => {
34321                    // Set the tab view height to 0, so we can calculate the max tabview height, without worrying about overflows
34322                    set$7(tabview, 'height', '0');
34323                    set$7(tabview, 'flex-basis', '0');
34324                    return Math.min(height, getMaxTabviewHeight(dialog, tabview, tablist));
34325                }).each((height) => {
34326                    setTabviewHeight(tabview, height);
34327                });
34328            });
34329        });
34330    };
34331    const getTabview = (dialog) =>
34331 descendant(dialog, '[role="tabpanel"]');
34332    const smartMode = (allTabs) => {
34333        const maxTabHeight = value$2();
34334        const extraEvents = [
34335            runOnAttached((comp) => {
34336                const dialog = comp.element;
34337                getTabview(dialog).each((tabview) => {
34338                    set$7(tabview, 'visibility', 'hidden');
34339                    // Determine the maximum heights of each tab
34340                    comp.getSystem().getByDom(tabview).toOptional().each((tabviewComp) => {
34341                        const heights = measureHeights(allTabs, tabview, tabviewComp);
34342                        // Calculate the maximum tab height and store it
34343                        const maxTabHeightOpt = getMaxHeight(heights);
34344                        maxTabHeightOpt.fold(maxTabHeight.clear, maxTabHeight.set);
34345                    });
34346                    // Set an initial height, based on the current size
34347                    updateTabviewHeight(dialog, tabview, maxTabHeight);
34348                    // Show the tabs
34349                    remove$6(tabview, 'visibility');
34350                    showTab(allTabs, comp);
34351                    // Use a delay here and recalculate the height, as we need all the components attached
34352                    // to be able to properly calculate the max height
34353                    requestAnimationFrame(() => {
34354                        updateTabviewHeight(dialog, tabview, maxTabHeight);
34355                    });
34356                });
34357            }),
34358            run$1(windowResize(), (comp) => {
34359                const dialog = comp.element;
34360                getTabview(dialog).each((tabview) => {
34361                    updateTabviewHeight(dialog, tabview, maxTabHeight);
34362                });
34363            }),
34364            run$1(formResizeEvent, (comp, _se) => {
34365                const dialog = comp.element;
34366                getTabview(dialog).each((tabview) => {
34367                    const oldFocus = active$1(getRootNode(tabview));
34368                    set$7(tabview, 'visibility', 'hidden');
34369                    const oldHeight = getRaw(tabview, 'height').map((h) => parseInt(h, 10));
34370                    remove$6(tabview, 'height');
34371                    remove$6(tabview, 'flex-basis');
34372                    const newHeight = tabview.dom.getBoundingClientRect().height;
34373                    const hasGrown = oldHeight.forall((h) => newHeight > h);
34374                    if (hasGrown) {
34375                        maxTabHeight.set(newHeight);
34376                        updateTabviewHeight(dialog, tabview, maxTabHeight);
34377                    }
34378                    else {
34379                        oldHeight.each((h) => {
34380                            setTabviewHeight(tabview, h);
34381                        });
34382                    }
34383                    remove$6(tabview, 'visibility');
34384                    oldFocus.each(focus$4);
34385                });
34386            })
34387        ];
34388        const selectFirst = false;
34389        return {
34390            extraEvents,
34391            selectFirst
34392        };
34393    };
34394    // Set tab heights within a dialog to vary according to their contents
34395    const naiveMode = (_allTabs) => {
34396        const extraEvents = [];
34397        const selectFirst = true;
34398        return {
34399            extraEvents,
34400            selectFirst
34401        };
34402    };
34403
34404    const SendDataToSectionChannel = 'send-data-to-section';
34405    const SendDataToViewChannel = 'send-data-to-view';
34406    const renderTabPanel = (spec, dialogData, backstage, getCompByName) => {
34407        const storedValue = Cell({});
34408        const updateDataWithForm = (form) => {
34409            const formData = Representing.getValue(form);
34410            const validData = toValidValues(formData).getOr({});
34411            const currentData = storedValue.get();
34412            const newData = deepMerge(currentData, validData);
34413            storedValue.set(newData);
34414        };
34415        const setDataOnForm = (form) => {
34416            const tabData = storedValue.get();
34417            Representing.setValue(form, tabData);
34418        };
34419        const oldTab = Cell(null);
34420        const allTabs = map$2(spec.tabs, (tab) => {
34421            return {
34422                value: tab.name,
34423                dom: {
34424                    tag: 'div',
34425                    classes: ['tox-dialog__body-nav-item']
34426                },
34427                components: [
34428                    text$2(backstage.shared.providers.translate(tab.title))
34429                ],
34430                view: () => {
34431                    return [
34432                        // Dupe with SilverDialog
vendor: 10,858 bytes, lines 34433-34719
34433                        Form.sketch((parts) => ({
34434                            dom: {
34435                                tag: 'div',
34436                                classes: ['tox-form']
34437                            },
34438                            components: map$2(tab.items, (item) => interpretInForm(parts, item, dialogData, backstage, getCompByName)),
34439                            formBehaviours: derive$1([
34440                                Keying.config({
34441                                    mode: 'acyclic',
34442                                    useTabstopAt: not(isPseudoStop)
34443                                }),
34444                                config('TabView.form.events', [
34445                                    runOnAttached(setDataOnForm),
34446                                    runOnDetached(updateDataWithForm)
34447                                ]),
34448                                Receiving.config({
34449                                    channels: wrapAll([
34450                                        {
34451                                            key: SendDataToSectionChannel,
34452                                            value: {
34453                                                onReceive: updateDataWithForm
34454                                            }
34455                                        },
34456                                        {
34457                                            key: SendDataToViewChannel,
34458                                            value: {
34459                                                onReceive: setDataOnForm
34460                                            }
34461                                        }
34462                                    ])
34463                                })
34464                            ])
34465                        }))
34466                    ];
34467                }
34468            };
34469        });
34470        // Assign fixed height or variable height to the tabs
34471        const tabMode = spec.dynamicHeight
34472            ? naiveMode() // Height is determined by the content of the tab, which may cause layout shifts when switching tabs.
34473            : smartMode(allTabs); // Height is determined by the tallest tab, which prevents layout shifts but may cause excessive whitespace in shorter tabs.
34474        return TabSection.sketch({
34475            dom: {
34476                tag: 'div',
34477                classes: ['tox-dialog__body']
34478            },
34479            onChangeTab: (section, button, _viewItems) => {
34480                const name = Representing.getValue(button);
34481                emitWith(section, formTabChangeEvent, {
34482                    name,
34483                    oldName: oldTab.get()
34484                });
34485                oldTab.set(name);
34486            },
34487            tabs: allTabs,
34488            components: [
34489                TabSection.parts.tabbar({
34490                    dom: {
34491                        tag: 'div',
34492                        classes: ['tox-dialog__body-nav']
34493                    },
34494                    components: [
34495                        Tabbar.parts.tabs({})
34496                    ],
34497                    markers: {
34498                        tabClass: 'tox-tab',
34499                        selectedClass: 'tox-dialog__body-nav-item--active'
34500                    },
34501                    tabbarBehaviours: derive$1([
34502                        Tabstopping.config({})
34503                    ])
34504                }),
34505                TabSection.parts.tabview({
34506                    dom: {
34507                        tag: 'div',
34508                        classes: ['tox-dialog__body-content']
34509                    }
34510                })
34511            ],
34512            selectFirst: tabMode.selectFirst,
34513            tabSectionBehaviours: derive$1([
34514                config('tabpanel', tabMode.extraEvents),
34515                Keying.config({
34516                    mode: 'acyclic'
34517                }),
34518                // INVESTIGATE: Is this necessary? Probably used by getCompByName.
34519                Composing.config({
34520                    // TODO: Think about this
34521                    find: (comp) => head(TabSection.getViewItems(comp))
34522                }),
34523                withComp(Optional.none(), (tsection) => {
34524                    // NOTE: Assumes synchronous updating of store.
34525                    tsection.getSystem().broadcastOn([SendDataToSectionChannel], {});
34526                    return storedValue.get();
34527                }, (tsection, value) => {
34528                    storedValue.set(value);
34529                    tsection.getSystem().broadcastOn([SendDataToViewChannel], {});
34530                })
34531            ])
34532        });
34533    };
34534
34535    // ariaAttrs is being passed through to silver inline dialog
34536    // from the WindowManager as a property of 'params'
34537    const renderBody = (spec, dialogId, contentId, backstage, ariaAttrs, getCompByName) => {
34538        const renderComponents = (incoming) => {
34539            const body = incoming.body;
34540            switch (body.type) {
34541                case 'tabpanel': {
34542                    return [
34543                        renderTabPanel(body, incoming.initialData, backstage, getCompByName)
34544                    ];
34545                }
34546                default: {
34547                    return [
34548                        renderBodyPanel(body, incoming.initialData, backstage, getCompByName)
34549                    ];
34550                }
34551            }
34552        };
34553        const updateState = (_comp, incoming) => Optional.some({
34554            isTabPanel: () => incoming.body.type === 'tabpanel'
34555        });
34556        const ariaAttributes = {
34557            'aria-live': 'polite'
34558        };
34559        return {
34560            dom: {
34561                tag: 'div',
34562                classes: ['tox-dialog__content-js'],
34563                attributes: {
34564                    ...contentId.map((x) => ({ id: x })).getOr({}),
34565                    ...ariaAttrs ? ariaAttributes : {}
34566                }
34567            },
34568            components: [],
34569            behaviours: derive$1([
34570                ComposingConfigs.childAt(0),
34571                Reflecting.config({
34572                    channel: `${bodyChannel}-${dialogId}`,
34573                    updateState,
34574                    renderComponents,
34575                    initialData: spec
34576                })
34577            ])
34578        };
34579    };
34580    const renderInlineBody = (spec, dialogId, contentId, backstage, ariaAttrs, getCompByName) => renderBody(spec, dialogId, Optional.some(contentId), backstage, ariaAttrs, getCompByName);
34581    const renderModalBody = (spec, dialogId, backstage, getCompByName) => {
34582        const bodySpec = renderBody(spec, dialogId, Optional.none(), backstage, false, getCompByName);
34583        return ModalDialog.parts.body(bodySpec);
34584    };
34585    const renderIframeBody = (spec) => {
34586        const bodySpec = {
34587            dom: {
34588                tag: 'div',
34589                classes: ['tox-dialog__content-js']
34590            },
34591            components: [
34592                {
34593                    dom: {
34594                        tag: 'div',
34595                        classes: ['tox-dialog__body-iframe']
34596                    },
34597                    components: [
34598                        craft(Optional.none(), {
34599                            dom: {
34600                                tag: 'iframe',
34601                                attributes: {
34602                                    src: spec.url
34603                                }
34604                            },
34605                            behaviours: derive$1([
34606                                Tabstopping.config({}),
34607                                Focusing.config({})
34608                            ])
34609                        })
34610                    ]
34611                }
34612            ],
34613            behaviours: derive$1([
34614                Keying.config({
34615                    mode: 'acyclic',
34616                    useTabstopAt: not(isPseudoStop)
34617                })
34618            ])
34619        };
34620        return ModalDialog.parts.body(bodySpec);
34621    };
34622
34623    const isTouch = global$7.deviceType.isTouch();
34624    const hiddenHeader = (title, close) => ({
34625        dom: {
34626            tag: 'div',
34627            styles: { display: 'none' },
34628            classes: ['tox-dialog__header']
34629        },
34630        components: [
34631            title,
34632            close
34633        ]
34634    });
34635    const pClose = (onClose, providersBackstage) => ModalDialog.parts.close(
34636    // Need to find a way to make it clear in the docs whether parts can be sketches
34637    Button.sketch({
34638        dom: {
34639            tag: 'button',
34640            classes: ['tox-button', 'tox-button--icon', 'tox-button--naked'],
34641            attributes: {
34642                'type': 'button',
34643                'aria-label': providersBackstage.translate('Close')
34644            }
34645        },
34646        action: onClose,
34647        buttonBehaviours: derive$1([
34648            Tabstopping.config({})
34649        ])
34650    }));
34651    const pUntitled = () => ModalDialog.parts.title({
34652        dom: {
34653            tag: 'div',
34654            classes: ['tox-dialog__title'],
34655            innerHtml: '',
34656            styles: {
34657                display: 'none'
34658            }
34659        }
34660    });
34661    const pBodyMessage = (message, providersBackstage) => ModalDialog.parts.body({
34662        dom: {
34663            tag: 'div',
34664            classes: ['tox-dialog__body']
34665        },
34666        components: [
34667            {
34668                dom: {
34669                    tag: 'div',
34670                    classes: ['tox-dialog__body-content']
34671                },
34672                components: [
34673                    {
34674                        dom: fromHtml(`<p>${sanitizeHtmlString(providersBackstage.translate(message))}</p>`)
34675                    }
34676                ]
34677            }
34678        ]
34679    });
34680    const pFooter = (buttons) => ModalDialog.parts.footer({
34681        dom: {
34682            tag: 'div',
34683            classes: ['tox-dialog__footer']
34684        },
34685        components: buttons
34686    });
34687    const pFooterGroup = (startButtons, endButtons) => [
34688        Container.sketch({
34689            dom: {
34690                tag: 'div',
34691                classes: ['tox-dialog__footer-start']
34692            },
34693            components: startButtons
34694        }),
34695        Container.sketch({
34696            dom: {
34697                tag: 'div',
34698                classes: ['tox-dialog__footer-end']
34699            },
34700            components: endButtons
34701        })
34702    ];
34703    const renderDialog$1 = (spec) => {
34704        const dialogClass = 'tox-dialog';
34705        const blockerClass = dialogClass + '-wrap';
34706        const blockerBackdropClass = blockerClass + '__backdrop';
34707        const scrollLockClass = dialogClass + '__disable-scroll';
34708        return ModalDialog.sketch({
34709            role: spec.role,
34710            lazySink: spec.lazySink,
34711            onEscape: (comp) => {
34712                spec.onEscape(comp);
34713                // TODO: Make a strong type for Handled KeyEvent
34714                return Optional.some(true);
34715            },
34716            useTabstopAt: (elem) => !isPseudoStop(elem),
34717            firstTabstop: spec.firstTabstop,
34718            dom: {
34719                tag: 'div',
34720                classes: [dialogClass].concat(spec.extraClasses),
34721                styles: {
34722                    position: 'relative',
34723                    ...spec.extraStyles
34724                }
34725            },
34726            components: [
34727                spec.header,
34728                spec.body,
34729                ...spec.footer.toArray()
34730            ],
34731            parts: {
34732                blocker: {
34733                    dom: fromHtml(`<div class="${blockerClass}"></div>`),
34734                    components: [
34735                        {
34736                            dom: {
34737                                tag: 'div',
34738                                classes: (isTouch ? [blockerBackdropClass, blockerBackdropClass + '--opaque'] : [blockerBackdropClass])
34739                            }
34740                        }
34741                    ]
34742                }
34743            },
34744            dragBlockClass: blockerClass,
34745            modalBehaviours: derive$1([
34746                Focusing.config({}),
34747                config('dialog-events', spec.dialogEvents.concat([
34748                    // Note: `runOnSource` here will only listen to the event at the outer component level.
34749                    // Using just `run` instead will cause an infinite loop as `focusIn` would fire a `focusin` which would then get responded to and so forth.
34750                    runOnSource(focusin(), (comp, _se) => {
34751                        Blocking.isBlocked(comp) ? noop() : Keying.focusIn(comp);
34752                    }),
34753                    run$1(focusShifted(), (comp, se) => {
34754                        comp.getSystem().broadcastOn([dialogFocusShiftedChannel], {
34755                            newFocus: se.event.newFocus
34756                        });
34757                    })
34758                ])),
34759                config('scroll-lock', [
34760                    runOnAttached(() => {
34761                        add$2(body(), scrollLockClass);
34762                    }),
34763                    runOnDetached(() => {
34764                        remove$3(body(), scrollLockClass);
34765                    })
34766                ]),
34767                ...spec.extraBehaviours
34768            ]),
34769            eventOrder: {
34770                [execute$5()]: ['dialog-events'],
34771                [attachedToDom()]: ['scroll-lock', 'dialog-events', 'alloy.base.behaviour'],
34772                [detachedFromDom()]: ['alloy.base.behaviour', 'dialog-events', 'scroll-lock'],
34773                ...spec.eventOrder
34774            }
34775        });
34776    };
34777
34778    const renderClose = (providersBackstage) => Button.sketch({
34779        dom: {
34780            tag: 'button',
34781            classes: ['tox-button', 'tox-button--icon', 'tox-button--naked'],
34782            attributes: {
34783                'type': 'button',
34784                'aria-label': providersBackstage.translate('Close'),
34785                'data-mce-name': 'close'
34786            }
34787        },
34788        buttonBehaviours: derive$1([
34789            Tabstopping.config({}),
34790            Tooltipping.config(providersBackstage.tooltips.getConfig({
34791                tooltipText: providersBackstage.translate('Close')
34792            }))
34793        ]),
34794        components: [
34795            render$4('close', { tag: 'span', classes: ['tox-icon'] }, providersBackstage.icons)
34796        ],
34797        action: (comp) => {
34798            emit(comp, formCancelEvent);
34799        },
34800    });
34801    const renderTitle = (spec, dialogId, titleId, providersBackstage) => {
34802        const renderComponents = (data) => [text$2(providersBackstage.translate(data.title))];
34803        return {
34804            dom: {
34805                tag: 'h1',
34806                classes: ['tox-dialog__title'],
34807                attributes: {
34808                    ...titleId.map((x) => ({ id: x })).getOr({})
34809                }
34810            },
34811            components: [],
34812            behaviours: derive$1([
34813                Reflecting.config({
34814                    channel: `${titleChannel}-${dialogId}`,
34815                    initialData: spec,
34816                    renderComponents
34817                })
34818            ])
34819        };
34820    };
34821    const renderDragHandle = () => ({
34822        dom: fromHtml('<div class="tox-dialog__draghandle"></div>')
34823    });
34824    const renderInlineHeader = (spec, dialogId, titleId, providersBackstage) => Container.sketch({
34825        dom: fromHtml('<div class="tox-dialog__header"></div>'),
34826        components: [
34827            renderTitle(spec, dialogId, Optional.some(titleId), providersBackstage),
34828            renderDragHandle(),
34829            renderClose(providersBackstage)
34830        ],
34831        containerBehaviours: derive$1([
34832            Dragging.config({
34833                mode: 'mouse',
34834                blockerClass: 'blocker',
34835                getTarget: (handle) => {
34836                    return closest$3(handle, '[role="dialog"]').getOrDie();
34837                },
34838                snaps: {
34839                    getSnapPoints: () => [],
34840                    leftAttr: 'data-drag-left',
34841                    topAttr: 'data-drag-top'
34842                },
34843                onDrag: (comp, target) => {
34844                    comp.getSystem().broadcastOn([repositionPopups()], { target });
34845                }
34846            })
34847        ])
34848    });
34849    const renderModalHeader = (spec, dialogId, providersBackstage) => {
34850        const pTitle = ModalDialog.parts.title(renderTitle(spec, dialogId, Optional.none(), providersBackstage));
34851        const pHandle = ModalDialog.parts.draghandle(renderDragHandle());
34852        const pClose = ModalDialog.parts.close(renderClose(providersBackstage));
34853        const components = [pTitle].concat(spec.draggable ? [pHandle] : []).concat([pClose]);
34854        return Container.sketch({
34855            dom: fromHtml('<div class="tox-dialog__header"></div>'),
34856            components
34857        });
34858    };
34859
34860    const getHeader = (title, dialogId, backstage) => renderModalHeader({
34861        title: backstage.shared.providers.translate(title),
34862        draggable: backstage.dialog.isDraggableModal()
34863    }, dialogId, backstage.shared.providers);
34864    const getBusySpec = (message, bs, providers, headerHeight) => ({
34865        dom: {
34866            tag: 'div',
34867            classes: ['tox-dialog__busy-spinner'],
34868            attributes: {
34869                'aria-label': providers.translate(message)
34870            },
34871            styles: {
34872                left: '0px',
34873                right: '0px',
34874                bottom: '0px',
34875                top: `${headerHeight.getOr(0)}px`,
34876                position: 'absolute'
34877            }
34878        },
34879        behaviours: bs,
34880        components: [{
34881                dom: fromHtml('<div class="tox-spinner"><div></div><div></div><div></div></div>')
34882            }]
34883    });
34884    const getEventExtras = (lazyDialog, providers, extra) => ({
34885        onClose: () => extra.closeWindow(),
34886        onBlock: (blockEvent) => {
34887            const headerHeight = descendant(lazyDialog().element, '.tox-dialog__header').map((header) => get$d(header));
34888            ModalDialog.setBusy(lazyDialog(), (_comp, bs) => getBusySpec(blockEvent.message, bs, providers, headerHeight));
34889        },
34890        onUnblock: () => {
34891            ModalDialog.setIdle(lazyDialog());
34892        }
34893    });
34894    const fullscreenClass = 'tox-dialog--fullscreen';
34895    const largeDialogClass = 'tox-dialog--width-lg';
34896    const mediumDialogClass = 'tox-dialog--width-md';
34897    const getDialogSizeClass = (size) => {
34898        switch (size) {
34899            case 'large':
34900                return Optional.some(largeDialogClass);
34901            case 'medium':
34902                return Optional.some(mediumDialogClass);
34903            default:
34904                return Optional.none();
34905        }
34906    };
34907    const updateDialogSizeClass = (size, component) => {
34908        const dialogBody = SugarElement.fromDom(component.element.dom);
34909        if (!has(dialogBody, fullscreenClass)) {
34910            remove$2(dialogBody, [largeDialogClass, mediumDialogClass]);
34911            getDialogSizeClass(size).each((dialogSizeClass) => add$2(dialogBody, dialogSizeClass));
34912        }
34913    };
34914    const toggleFullscreen = (comp, currentSize) => {
34915        const dialogBody = SugarElement.fromDom(comp.element.dom);
34916        const classes = get$7(dialogBody);
34917        const currentSizeClass = find$5(classes, (c) => c === largeDialogClass || c === mediumDialogClass).or(getDialogSizeClass(currentSize));
34918        toggle$2(dialogBody, [fullscreenClass, ...currentSizeClass.toArray()]);
34919    };
34920    const renderModalDialog = (spec, dialogEvents, backstage) => build$1(renderDialog$1({
34921        ...spec,
34922        firstTabstop: 1,
34923        lazySink: backstage.shared.getSink,
34924        extraBehaviours: [
34925            memory({}),
34926            ...spec.extraBehaviours
34927        ],
34928        onEscape: (comp) => {
34929            emit(comp, formCancelEvent);
34930        },
34931        dialogEvents,
34932        eventOrder: {
34933            [receive()]: [Reflecting.name(), Receiving.name()],
34934            [attachedToDom()]: ['scroll-lock', Reflecting.name(), 'messages', 'dialog-events', 'alloy.base.behaviour'],
34935            [detachedFromDom()]: ['alloy.base.behaviour', 'dialog-events', 'messages', Reflecting.name(), 'scroll-lock']
34936        }
34937    }));
34938    const mapMenuButtons = (buttons, menuItemStates = {}) => {
34939        const mapItems = (button) => {
34940            const items = map$2(button.items, (item) => {
34941                const cell = get$h(menuItemStates, item.name).getOr(Cell(false));
34942                return {
34943                    ...item,
34944                    storage: cell
34945                };
34946            });
34947            return {
34948                ...button,
34949                items
34950            };
34951        };
34952        return map$2(buttons, (button) => {
34953            return button.type === 'menu' ? mapItems(button) : button;
34954        });
34955    };
34956    const extractCellsToObject = (buttons) => foldl(buttons, (acc, button) => {
34957        if (button.type === 'menu') {
34958            const menuButton = button;
34959            return foldl(menuButton.items, (innerAcc, item) => {
34960                innerAcc[item.name] = item.storage;
34961                return innerAcc;
34962            }, acc);
34963        }
34964        return acc;
34965    }, {});
34966
34967    const focusFirstTabbable = (getSink, component) => {
34968        // TODO: add a test for focusIn (TINY-10125)
34969        const focusIn = () => component.getSystem().isConnected() ? Keying.focusIn(component) : undefined;
34970        const isDisabled = (focused) => has$1(focused, 'disabled') || getOpt(focused, 'aria-disabled').exists((val) => val === 'true');
34971        const rootNode = getRootNode(component.element);
34972        const current = active$1(rootNode);
34973        active$1(rootNode).fold(focusIn, (focused) => {
34974            // We need to check if the focused element is disabled because apparently firefox likes to leave focus on disabled elements.
34975            if (isDisabled(focused)) {
34976                focusIn();
34977                // And we need the below check for IE, which likes to leave focus on the parent of disabled elements
34978            }
34979            else if (current.exists((cur) => contains(focused, cur) && isDisabled(cur))) {
34980                focusIn();
34981                // Lastly if something outside the sink has focus then return the focus back to the dialog
34982            }
34983            else {
34984                getSink().toOptional()
34985                    .filter((sink) => !contains(sink.element, focused))
34986                    .each(focusIn);
34987            }
34988        });
34989    };
vendor: 7,079 bytes, lines 34990-35145
34990    const initCommonEvents = (fireApiEvent, extras, getSink) => [
34991        // When focus moves onto a tab-placeholder, skip to the next thing in the tab sequence
34992        runWithTarget(focusin(), onFocus),
34993        // TODO: Test if disabled first.
34994        fireApiEvent(formCloseEvent, (_api, spec, _event, self) => {
34995            // TINY-9148: Safari scrolls down to the sink if the dialog is selected before removing,
34996            // so we should blur the currently active element beforehand.
34997            if (hasFocus(self.element)) {
34998                active$1(getRootNode(self.element)).each(blur$1);
34999            }
35000            extras.onClose();
35001            spec.onClose();
35002        }),
35003        // TODO: Test if disabled first.
35004        fireApiEvent(formCancelEvent, (api, spec, _event, self) => {
35005            spec.onCancel(api);
35006            emit(self, formCloseEvent);
35007        }),
35008        run$1(formUnblockEvent, (component, _se) => {
35009            extras.onUnblock();
35010            focusFirstTabbable(getSink, component);
35011        }),
35012        run$1(formBlockEvent, (_c, se) => extras.onBlock(se.event))
35013    ];
35014    const initUrlDialog = (getInstanceApi, extras, getSink) => {
35015        const fireApiEvent = (eventName, f) => run$1(eventName, (c, se) => {
35016            withSpec(c, (spec, _c) => {
35017                f(getInstanceApi(), spec, se.event, c);
35018            });
35019        });
35020        const withSpec = (c, f) => {
35021            Reflecting.getState(c).get().each((currentDialog) => {
35022                f(currentDialog, c);
35023            });
35024        };
35025        return [
35026            ...initCommonEvents(fireApiEvent, extras, getSink),
35027            fireApiEvent(formActionEvent, (api, spec, event) => {
35028                spec.onAction(api, { name: event.name });
35029            })
35030        ];
35031    };
35032    const initDialog = (getInstanceApi, extras, getSink) => {
35033        const fireApiEvent = (eventName, f) => run$1(eventName, (c, se) => {
35034            withSpec(c, (spec, _c) => {
35035                f(getInstanceApi(), spec, se.event, c);
35036            });
35037        });
35038        const withSpec = (c, f) => {
35039            Reflecting.getState(c).get().each((currentDialogInit) => {
35040                f(currentDialogInit.internalDialog, c);
35041            });
35042        };
35043        return [
35044            ...initCommonEvents(fireApiEvent, extras, getSink),
35045            fireApiEvent(formSubmitEvent, (api, spec) => spec.onSubmit(api)),
35046            fireApiEvent(formChangeEvent, (api, spec, event) => {
35047                spec.onChange(api, { name: event.name });
35048            }),
35049            fireApiEvent(formActionEvent, (api, spec, event, component) => {
35050                spec.onAction(api, { name: event.name, value: event.value });
35051                focusFirstTabbable(getSink, component);
35052            }),
35053            fireApiEvent(formTabChangeEvent, (api, spec, event) => {
35054                spec.onTabChange(api, { newTabName: event.name, oldTabName: event.oldName });
35055            }),
35056            // When the dialog is being closed, store the current state of the form
35057            runOnDetached((component) => {
35058                const api = getInstanceApi();
35059                Representing.setValue(component, api.getData());
35060            })
35061        ];
35062    };
35063
35064    const makeButton = (button, backstage) => renderFooterButton(button, button.type, backstage);
35065    const lookup = (compInSystem, footerButtons, buttonName) => find$5(footerButtons, (button) => button.name === buttonName)
35066        .bind((memButton) => memButton.memento.getOpt(compInSystem));
35067    const renderComponents = (_data, state) => {
35068        // default group is 'end'
35069        const footerButtons = state.map((s) => s.footerButtons).getOr([]);
35070        const buttonGroups = partition$3(footerButtons, (button) => button.align === 'start');
35071        const makeGroup = (edge, buttons) => Container.sketch({
35072            dom: {
35073                tag: 'div',
35074                classes: [`tox-dialog__footer-${edge}`]
35075            },
35076            components: map$2(buttons, (button) => button.memento.asSpec())
35077        });
35078        const startButtons = makeGroup('start', buttonGroups.pass);
35079        const endButtons = makeGroup('end', buttonGroups.fail);
35080        return [startButtons, endButtons];
35081    };
35082    const renderFooter = (initSpec, dialogId, backstage) => {
35083        const updateState = (comp, data) => {
35084            const footerButtons = map$2(data.buttons, (button) => {
35085                const memButton = record(makeButton(button, backstage));
35086                return {
35087                    name: button.name,
35088                    align: button.align,
35089                    memento: memButton
35090                };
35091            });
35092            const lookupByName = (buttonName) => lookup(comp, footerButtons, buttonName);
35093            return Optional.some({
35094                lookupByName,
35095                footerButtons
35096            });
35097        };
35098        return {
35099            dom: fromHtml('<div class="tox-dialog__footer"></div>'),
35100            components: [],
35101            behaviours: derive$1([
35102                Reflecting.config({
35103                    channel: `${footerChannel}-${dialogId}`,
35104                    initialData: initSpec,
35105                    updateState,
35106                    renderComponents
35107                })
35108            ])
35109        };
35110    };
35111    const renderInlineFooter = (initSpec, dialogId, backstage) => renderFooter(initSpec, dialogId, backstage);
35112    const renderModalFooter = (initSpec, dialogId, backstage) => ModalDialog.parts.footer(renderFooter(initSpec, dialogId, backstage));
35113
35114    const getCompByName = (access, name) => {
35115        // TODO: Add API to alloy to find the inner most component of a Composing chain.
35116        const root = access.getRoot();
35117        // This is just to avoid throwing errors if the dialog closes before this. We should take it out
35118        // while developing (probably), and put it back in for the real thing.
35119        if (root.getSystem().isConnected()) {
35120            const form = Composing.getCurrent(access.getFormWrapper()).getOr(access.getFormWrapper());
35121            return Form.getField(form, name).orThunk(() => {
35122                const footer = access.getFooter();
35123                const footerState = footer.bind((f) => Reflecting.getState(f).get());
35124                return footerState.bind((f) => f.lookupByName(name));
35125            });
35126        }
35127        else {
35128            return Optional.none();
35129        }
35130    };
35131    const validateData$1 = (access, data) => {
35132        const root = access.getRoot();
35133        return Reflecting.getState(root).get().map((dialogState) => getOrDie(asRaw('data', dialogState.dataValidator, data))).getOr(data);
35134    };
35135    const getDialogApi = (access, doRedial, menuItemStates) => {
35136        const withRoot = (f) => {
35137            const root = access.getRoot();
35138            if (root.getSystem().isConnected()) {
35139                f(root);
35140            }
35141        };
35142        const getData = () => {
35143            const root = access.getRoot();
35144            const valueComp = root.getSystem().isConnected() ? access.getFormWrapper() : root;
35145            const representedValues = Representing.getValue(value
35145Comp);
35146            const menuItemCurrentState = map$1(menuItemStates, (cell) => cell.get());
35147            return {
35148                ...representedValues,
35149                ...menuItemCurrentState
35150            };
35151        };
35152        const setData = (newData) => {
35153            // Currently, the decision is to ignore setData calls that fire after the dialog is closed
35154            withRoot((_) => {
35155                const prevData = instanceApi.getData();
35156                const mergedData = deepMerge(prevData, newData);
35157                const newInternalData = validateData$1(access, mergedData);
35158                const form = access.getFormWrapper();
35159                Representing.setValue(form, newInternalData);
35160                each(menuItemStates, (v, k) => {
35161                    if (has$2(mergedData, k)) {
35162                        v.set(mergedData[k]);
35163                    }
35164                });
35165            });
35166        };
35167        const setEnabled = (name, state) => {
35168            getCompByName(access, name).each(state ? Disabling.enable : Disabling.disable);
35169        };
35170        const focus = (name) => {
35171            getCompByName(access, name).each(Focusing.focus);
35172        };
35173        const block = (message) => {
35174            if (!isString(message)) {
35175                throw new Error('The dialogInstanceAPI.block function should be passed a blocking message of type string as an argument');
35176            }
35177            withRoot((root) => {
35178                emitWith(root, formBlockEvent, { message });
35179            });
35180        };
35181        const unblock = () => {
35182            withRoot((root) => {
35183                emit(root, formUnblockEvent);
35184            });
35185        };
35186        const showTab = (name) => {
35187            withRoot((_) => {
35188                const body = access.getBody();
35189                const bodyState = Reflecting.getState(body);
35190                if (bodyState.get().exists((b) => b.isTabPanel())) {
35191                    Composing.getCurrent(body).each((tabSection) => {
35192                        TabSection.showTab(tabSection, name);
35193                    });
35194                }
35195            });
35196        };
35197        const redial = (d) => {
35198            withRoot((root) => {
35199                const id = access.getId();
35200                const dialogInit = doRedial(d);
35201                const storedMenuButtons = mapMenuButtons(dialogInit.internalDialog.buttons, menuItemStates);
35202                // TINY-9223: We only need to broadcast to the mothership containing the dialog
35203                root.getSystem().broadcastOn([`${dialogChannel}-${id}`], dialogInit);
35204                // NOTE: Reflecting does not have any smart handling of nested reflecting components,
35205                // and the order of receiving a broadcast is non-deterministic. Here we use separate
35206                // channels for each section (title, body, footer), and make those broadcasts *after*
35207                // we've already sent the overall dialog broadcast. The overall dialog broadcast
35208                // doesn't actually change the components ... its Reflecting config just stores state,
35209                // but these Reflecting configs (title, body, footer) do change the components based on
35210                // the received broadcasts.
35211                root.getSystem().broadcastOn([`${titleChannel}-${id}`], dialogInit.internalDialog);
35212                root.getSystem().broadcastOn([`${bodyChannel}-${id}`], dialogInit.internalDialog);
35213                root.getSystem().broadcastOn([`${footerChannel}-${id}`], {
35214                    ...dialogInit.internalDialog,
35215                    buttons: storedMenuButtons
35216                });
35217                instanceApi.setData(dialogInit.initialData);
35218            });
35219        };
35220        const close = () => {
35221            withRoot((root) => {
35222                emit(root, formCloseEvent);
35223            });
35224        };
35225        const instanceApi = {
35226            getData,
35227            setData,
35228            setEnabled,
35229            focus,
35230            block,
35231            unblock,
35232            showTab,
35233            redial,
35234            close,
35235            toggleFullscreen: access.toggleFullscreen
35236        };
35237        return instanceApi;
35238    };
35239
35240    const renderDialog = (dialogInit, extra, backstage) => {
35241        const dialogId = generate$6('dialog');
35242        const internalDialog = dialogInit.internalDialog;
35243        const header = getHeader(internalDialog.title, dialogId, backstage);
35244        const dialogSize = Cell(internalDialog.size);
35245        const getCompByName$1 = (name) => getCompByName(modalAccess, name);
35246        const dialogSizeClasses = getDialogSizeClass(dialogSize.get()).toArray();
35247        const updateState = (comp, incoming) => {
35248            dialogSize.set(incoming.internalDialog.size);
35249            updateDialogSizeClass(incoming.internalDialog.size, comp);
35250            return Optional.some(incoming);
35251        };
35252        const body = renderModalBody({
35253            body: internalDialog.body,
35254            initialData: internalDialog.initialData
35255        }, dialogId, backstage, getCompByName$1);
35256        const storedMenuButtons = mapMenuButtons(internalDialog.buttons);
35257        const objOfCells = extractCellsToObject(storedMenuButtons);
35258        const footer = someIf(storedMenuButtons.length !== 0, renderModalFooter({ buttons: storedMenuButtons }, dialogId, backstage));
35259        const dialogEvents = initDialog(() => instanceApi, getEventExtras(() => dialog, backstage.shared.providers, extra), backstage.shared.getSink);
35260        const spec = {
35261            id: dialogId,
35262            header,
35263            body,
35264            footer,
35265            extraClasses: dialogSizeClasses,
35266            extraBehaviours: [
35267                Reflecting.config({
35268                    channel: `${dialogChannel}-${dialogId}`,
35269                    updateState,
35270                    initialData: dialogInit
35271                }),
35272            ],
35273            extraStyles: {}
35274        };
35275        const dialog = renderModalDialog(spec, dialogEvents, backstage);
35276        const modalAccess = (() => {
35277            const getForm = () => {
35278                const outerForm = ModalDialog.getBody(dialog);
35279                return Composing.getCurrent(outerForm).getOr(outerForm);
35280            };
35281            const toggleFullscreen$1 = () => {
35282                toggleFullscreen(dialog, dialogSize.get());
35283            };
35284            return {
35285                getId: constant$1(dialogId),
35286                getRoot: constant$1(dialog),
35287                getBody: () => ModalDialog.getBody(dialog),
35288                getFooter: () => ModalDialog.getFooter(dialog),
35289                getFormWrapper: getForm,
35290                toggleFullscreen: toggleFullscreen$1
35291            };
35292        })();
35293        // TODO: Get the validator from the dialog state.
35294        const instanceApi = getDialogApi(modalAccess, extra.redial, objOfCells);
35295        return {
35296            dialog,
35297            instanceApi
35298        };
35299    };
35300
35301    // DUPE with SilverDialog. Cleaning up.
35302    const renderInlineDialog = (dialogInit, extra, backstage, ariaAttrs = false, refreshDocking) => {
35303        const dialogId = generate$6('dialog');
35304        const dialogLabelId = generate$6('dialog-label');
35305        const dialogContentId = generate$6('dialog-content');
35306        const internalDialog = dialogInit.internalDialog;
35307        const getCompByName$1 = (name) => getCompByName(modalAccess, name);
35308        const dialogSize = Cell(internalDialog.size);
35309        const dialogSizeClass = getDialogSizeClass(dialogSize.get()).toArray();
35310        // Reflecting behaviour broadcasts on dialog channel only on redial.
35311        const updateState = (comp, incoming) => {
35312            // Update dialog size and position upon redial.
35313            dialogSize.set(incoming.internalDialog.size);
35314            updateDialogSizeClass(incoming.internalDialog.size, comp);
35315            refreshDocking();
35316            return Optional.some(incoming);
35317        };
35318        const memHeader = record(renderInlineHeader({
35319            title: internalDialog.title,
35320            draggable: true
35321        }, dialogId, dialogLabelId, backstage.shared.providers));
35322        const memBody = record(renderInlineBody({
35323            body: internalDialog.body,
35324            initialData: internalDialog.initialData,
35325        }, dialogId, dialogContentId, backstage, ariaAttrs, getCompByName$1));
35326        const storagedMenuButtons = mapMenuButtons(internalDialog.buttons);
35327        const objOfCells = extractCellsToObject(storagedMenuButtons);
35328        const optMemFooter = someIf(storagedMenuButtons.length !== 0, record(renderInlineFooter({
35329            buttons: storagedMenuButtons
35330        }, dialogId, backstage)));
35331        const dialogEvents = initDialog(() => instanceApi, {
35332            onBlock: (event) => {
35333                Blocking.block(dialog, (_comp, bs) => {
35334                    const headerHeight = memHeader.getOpt(dialog).map((dialog) => get$d(dialog.element));
35335                    return getBusySpec(event.message, bs, backstage.shared.providers, headerHeight);
35336                });
35337            },
35338            onUnblock: () => {
35339                Blocking.unblock(dialog);
35340            },
35341            onClose: () => extra.closeWindow()
35342        }, backstage.shared.getSink);
35343        const inlineClass = 'tox-dialog-inline';
35344        const os = detect$1().os;
35345        // TODO: Disable while validating?
35346        const dialog = build$1({
35347            dom: {
35348                tag: 'div',
35349                classes: ['tox-dialog', inlineClass, ...dialogSizeClass],
35350                attributes: {
35351                    role: 'dialog',
35352                    // TINY-10808 - Workaround to address the dialog header not being announced on VoiceOver with aria-labelledby, ideally we should use the aria-labelledby
35353                    ...os.isMacOS() ? { 'aria-label': internalDialog.title } : { 'aria-labelledby': dialogLabelId }
35354                }
35355            },
35356            eventOrder: {
35357                [receive()]: [Reflecting.name(), Receiving.name()],
35358                [execute$5()]: ['execute-on-form'],
35359                [attachedToDom()]: ['reflecting', 'execute-on-form']
35360            },
35361            // Dupe with SilverDialog.
35362            behaviours: derive$1([
35363                Keying.config({
35364                    mode: 'cyclic',
35365                    onEscape: (c) => {
35366                        emit(c, formCloseEvent);
35367                        return Optional.some(true);
35368                    },
35369                    useTabstopAt: (elem) => !isPseudoStop(elem) && (name$3(elem) !== 'button' || get$g(elem, 'disabled') !== 'disabled'),
35370                    firstTabstop: 1
35371                }),
35372                Reflecting.config({
35373                    channel: `${dialogChannel}-${dialogId}`,
35374                    updateState,
35375                    initialData: dialogInit
35376                }),
35377                Focusing.config({}),
35378                config('execute-on-form', dialogEvents.concat([
35379                    // Note: `runOnSource` here will only listen to the event at the outer component level.
35380                    // Using just `run` instead will cause an infinite loop as `focusIn` would fire a `focusin` which would then get responded to and so forth.
35381                    runOnSource(focusin(), (comp, _se) => {
35382                        Keying.focusIn(comp);
35383                    }),
35384                    run$1(focusShifted(), (comp, se) => {
35385                        comp.getSystem().broadcastOn([dialogFocusShiftedChannel], {
35386                            newFocus: se.event.newFocus
35387                        });
35388                    })
35389                ])),
35390                Blocking.config({ getRoot: () => Optional.some(dialog) }),
35391                Replacing.config({}),
35392                memory({})
35393            ]),
35394            components: [
35395                memHeader.asSpec(),
35396                memBody.asSpec(),
35397                ...optMemFooter.map((memFooter) => memFooter.asSpec()).toArray()
35398            ]
35399        });
35400        const toggleFullscreen$1 = () => {
35401            toggleFullscreen(dialog, dialogSize.get());
35402        };
35403        // TODO: Clean up the dupe between this (InlineDialog) and SilverDialog
35404        const modalAccess = {
35405            getId: constant$1(dialogId),
35406            getRoot: constant$1(dialog),
35407            getFooter: () => optMemFooter.map((memFooter) => memFooter.get(dialog)),
35408            getBody: () => memBody.get(dialog),
35409            getFormWrapper: () => {
35410                const body = memBody.get(dialog);
35411                return Composing.getCurrent(body).getOr(body);
35412            },
35413            toggleFullscreen: toggleFullscreen$1
35414        };
35415        const instanceApi = getDialogApi(modalAccess, extra.redial, objOfCells);
35416        return {
35417            dialog,
35418            instanceApi
35419        };
35420    };
35421
35422    var global = tinymce.util.Tools.resolve('tinymce.util.URI');
35423
35424    const getUrlDialogApi = (root) => {
35425        const withRoot = (f) => {
35426            if (root.getSystem().isConnected()) {
35427                f(root);
35428            }
35429        };
35430        const block = (message) => {
35431            if (!isString(message)) {
35432                throw new Error('The urlDialogInstanceAPI.block function should be passed a blocking message of type string as an argument');
35433            }
35434            withRoot((root) => {
35435                emitWith(root, formBlockEvent, { message });
35436            });
35437        };
35438        const unblock = () => {
35439            withRoot((root) => {
35440                emit(root, formUnblockEvent);
35441            });
35442        };
35443        const close = () => {
35444            withRoot((root) => {
35445                emit(root, formCloseEvent);
35446            });
35447        };
35448        const sendMessage = (data) => {
35449            withRoot((root) => {
35450                root.getSystem().broadcastOn([bodySendMessageChannel], data);
35451            });
35452        };
35453        return {
35454            block,
35455            unblock,
35456            close,
35457            sendMessage
35458        };
35459    };
35460
35461    // A list of supported message actions
35462    const SUPPORTED_MESSAGE_ACTIONS = ['insertContent', 'setContent', 'execCommand', 'close', 'block', 'unblock'];
35463    const isSupportedMessage = (data) => isObject(data) && SUPPORTED_MESSAGE_ACTIONS.indexOf(data.mceAction) !== -1;
35464    const isCustomMessage = (data) => !isSupportedMessage(data) && isObject(data) && has$2(data, 'mceAction');
35465    const handleMessage = (editor, api, data) => {
35466        switch (data.mceAction) {
35467            case 'insertContent':
35468                editor.insertContent(data.content);
35469                break;
35470            case 'setContent':
35471                editor.setContent(data.content);
35472                break;
35473            case 'execCommand':
35474                const ui = isBoolean(data.ui) ? data.ui : false;
35475                editor.execCommand(data.cmd, ui, data.value);
35476                break;
35477            case 'close':
35478                api.close();
35479                break;
35480            case 'block':
35481                api.block(data.message);
35482                break;
35483            case 'unblock':
35484                api.unblock();
35485                break;
35486        }
35487    };
35488    const renderUrlDialog = (internalDialog, extra, editor, backstage) => {
35489        const dialogId = generate$6('dialog');
35490        const header = getHeader(internalDialog.title, dialogId, backstage);
35491        const body = renderIframeBody(internalDialog);
35492        const footer = internalDialog.buttons.bind((buttons) => {
35493            // Don't render a footer if no buttons are specified
35494            if (buttons.length === 0) {
35495                return Optional.none();
35496            }
35497            else {
35498                return Optional.some(renderModalFooter({ buttons }, dialogId, backstage));
35499            }
35500        });
35501        const dialogEvents = initUrlDialog(() => instanceApi, getEventExtras(() => dialog, backstage.shared.providers, extra), backstage.shared.getSink);
35502        // Add the styles for the modal width/height
35503        const styles = {
35504            ...internalDialog.height.fold(() => ({}), (height) => ({ 'height': height + 'px', 'max-height': height + 'px' })),
35505            ...internalDialog.width.fold(() => ({}), (width) => ({ 'width': width + 'px', 'max-width': width + 'px' }))
35506        };
35507        // Default back to using a large sized dialog, if no dimensions are specified
35508        const classes = internalDialog.width.isNone() && internalDialog.height.isNone() ? ['tox-dialog--width-lg'] : [];
35509        // Determine the iframe urls domain, so we can target that specifically when sending messages
35510        const iframeUri = new global(internalDialog.url, { base_uri: new global(window.location.href) });
35511        const iframeDomain = `${iframeUri.protocol}://${iframeUri.host}${iframeUri.port ? ':' + iframeUri.port : ''}`;
35512        const messageHandlerUnbinder = unbindable();
35513        const updateState = (_comp, incoming) => Optional.some(incoming);
35514        // Setup the behaviours for dealing with messages between the iframe and current window
35515        const extraBehaviours = [
35516            // Because this doesn't define `renderComponents`, all this does is update the state.
35517            // We use the state for the initialData. The other parts (body etc.) render the
35518            // components based on what reflecting receives.
35519            Reflecting.config({
35520                channel: `${dialogChannel}-${dialogId}`,
35521                updateState,
35522                initialData: internalDialog
35523            }),
35524            config('messages', [
35525                // When the dialog is opened, bind a window message listener for the spec url
35526                runOnAttached(() => {
35527                    const unbind = bind$1(SugarElement.fromDom(window), 'message', (e) => {
35528                        // Validate that the request came from the correct domain
35529                        if (iframeUri.isSameOrigin(new global(e.raw.origin))) {
35530                            const data = e.raw.data;
35531                            // Handle the message if it has the 'mceAction' key, otherwise just ignore it
35532                            if (isSupportedMessage(data)) {
35533                                handleMessage(editor, instanceApi, data);
35534                            }
35535                            else if (isCustomMessage(data)) {
35536                                internalDialog.onMessage(instanceApi, data);
35537                            }
35538                        }
35539                    });
35540                    messageHandlerUnbinder.set(unbind);
35541                }),
35542                // When the dialog is closed, unbind the window message listener
35543                runOnDetached(messageHandlerUnbinder.clear)
35544            ]),
35545            Receiving.config({
35546                channels: {
35547                    [bodySendMessageChannel]: {
35548                        onReceive: (comp, data) => {
35549                            // Send the message to the iframe via postMessage
35550                            descendant(comp.element, 'iframe').each((iframeEle) => {
35551                                const iframeWin = iframeEle.dom.contentWindow;
35552                                if (isNonNullable(iframeWin)) {
35553                                    iframeWin.postMessage(data, iframeDomain);
35554                                }
35555                            });
35556                        }
35557                    }
35558                }
35559            })
35560        ];
35561        const spec = {
35562            id: dialogId,
35563            header,
35564            body,
35565            footer,
vendor: 9,455 bytes, lines 35566-35778
35566            extraClasses: classes,
35567            extraBehaviours,
35568            extraStyles: styles
35569        };
35570        const dialog = renderModalDialog(spec, dialogEvents, backstage);
35571        const instanceApi = getUrlDialogApi(dialog);
35572        return {
35573            dialog,
35574            instanceApi
35575        };
35576    };
35577
35578    const setup$2 = (backstage) => {
35579        const sharedBackstage = backstage.shared;
35580        const open = (message, callback) => {
35581            const closeDialog = () => {
35582                ModalDialog.hide(alertDialog);
35583                callback();
35584            };
35585            const memFooterClose = record(renderFooterButton({
35586                context: 'any',
35587                name: 'close-alert',
35588                text: 'OK',
35589                primary: true,
35590                buttonType: Optional.some('primary'),
35591                align: 'end',
35592                enabled: true,
35593                icon: Optional.none()
35594            }, 'cancel', backstage));
35595            const titleSpec = pUntitled();
35596            const closeSpec = pClose(closeDialog, sharedBackstage.providers);
35597            const alertDialog = build$1(renderDialog$1({
35598                role: 'alertdialog',
35599                lazySink: () => sharedBackstage.getSink(),
35600                header: hiddenHeader(titleSpec, closeSpec),
35601                body: pBodyMessage(message, sharedBackstage.providers),
35602                footer: Optional.some(pFooter(pFooterGroup([], [
35603                    memFooterClose.asSpec()
35604                ]))),
35605                onEscape: closeDialog,
35606                extraClasses: ['tox-alert-dialog'],
35607                extraBehaviours: [],
35608                extraStyles: {},
35609                dialogEvents: [
35610                    run$1(formCancelEvent, closeDialog),
35611                    runOnAttached((c) => {
35612                        const bodyElm = ModalDialog.getBody(c);
35613                        describedBy(c.element, bodyElm.element);
35614                    }),
35615                ],
35616                eventOrder: {}
35617            }));
35618            ModalDialog.show(alertDialog);
35619            const footerCloseButton = memFooterClose.get(alertDialog);
35620            Focusing.focus(footerCloseButton);
35621        };
35622        return {
35623            open
35624        };
35625    };
35626
35627    const setup$1 = (backstage) => {
35628        const sharedBackstage = backstage.shared;
35629        // FIX: Extreme dupe with Alert dialog
35630        const open = (message, callback) => {
35631            const closeDialog = (state) => {
35632                ModalDialog.hide(confirmDialog);
35633                callback(state);
35634            };
35635            const memFooterYes = record(renderFooterButton({
35636                context: 'any',
35637                name: 'yes',
35638                text: 'Yes',
35639                primary: true,
35640                buttonType: Optional.some('primary'),
35641                align: 'end',
35642                enabled: true,
35643                icon: Optional.none()
35644            }, 'submit', backstage));
35645            const footerNo = renderFooterButton({
35646                context: 'any',
35647                name: 'no',
35648                text: 'No',
35649                primary: false,
35650                buttonType: Optional.some('secondary'),
35651                align: 'end',
35652                enabled: true,
35653                icon: Optional.none()
35654            }, 'cancel', backstage);
35655            const titleSpec = pUntitled();
35656            const closeSpec = pClose(() => closeDialog(false), sharedBackstage.providers);
35657            const confirmDialog = build$1(renderDialog$1({
35658                role: 'alertdialog',
35659                lazySink: () => sharedBackstage.getSink(),
35660                header: hiddenHeader(titleSpec, closeSpec),
35661                body: pBodyMessage(message, sharedBackstage.providers),
35662                footer: Optional.some(pFooter(pFooterGroup([], [
35663                    footerNo,
35664                    memFooterYes.asSpec()
35665                ]))),
35666                onEscape: () => closeDialog(false),
35667                extraClasses: ['tox-confirm-dialog'],
35668                extraBehaviours: [],
35669                extraStyles: {},
35670                dialogEvents: [
35671                    run$1(formCancelEvent, () => closeDialog(false)),
35672                    run$1(formSubmitEvent, () => closeDialog(true)),
35673                    runOnAttached((c) => {
35674                        const bodyElm = ModalDialog.getBody(c);
35675                        describedBy(c.element, bodyElm.element);
35676                    }),
35677                ],
35678                eventOrder: {}
35679            }));
35680            ModalDialog.show(confirmDialog);
35681            const footerYesButton = memFooterYes.get(confirmDialog);
35682            Focusing.focus(footerYesButton);
35683        };
35684        return {
35685            open
35686        };
35687    };
35688
35689    const validateData = (data, validator) => getOrDie(asRaw('data', validator, data));
35690    const isAlertOrConfirmDialog = (target) => closest$1(target, '.tox-alert-dialog') || closest$1(target, '.tox-confirm-dialog');
35691    const inlineAdditionalBehaviours = (editor, isStickyToolbar, isToolbarLocationTop, onHide) => {
35692        // When using sticky toolbars it already handles the docking behaviours so applying docking would
35693        // do nothing except add additional processing when scrolling, so we don't want to include it here
35694        // (Except when the toolbar is located at the bottom since the anchor will be at the top)
35695        if (isStickyToolbar && isToolbarLocationTop) {
35696            return [];
35697        }
35698        else {
35699            return [
35700                Docking.config({
35701                    contextual: {
35702                        lazyContext: () => Optional.some(box$1(SugarElement.fromDom(editor.getContentAreaContainer()))),
35703                        fadeInClass: 'tox-dialog-dock-fadein',
35704                        fadeOutClass: 'tox-dialog-dock-fadeout',
35705                        transitionClass: 'tox-dialog-dock-transition',
35706                        onHide
35707                    },
35708                    modes: ['top'],
35709                    lazyViewport: (comp) => {
35710                        // If we don't have a special scrolling environment, then just use the default
35711                        // viewport of (window)
35712                        const optScrollingContext = detectWhenSplitUiMode(editor, comp.element);
35713                        return optScrollingContext
35714                            .map((sc) => {
35715                            const combinedBounds = getBoundsFrom(sc);
35716                            return {
35717                                bounds: combinedBounds,
35718                                optScrollEnv: Optional.some({
35719                                    currentScrollTop: sc.element.dom.scrollTop,
35720                                    scrollElmTop: absolute$3(sc.element).top
35721                                })
35722                            };
35723                        }).getOrThunk(() => ({
35724                            bounds: win(),
35725                            optScrollEnv: Optional.none()
35726                        }));
35727                    }
35728                })
35729            ];
35730        }
35731    };
35732    const setup = (extras) => {
35733        const editor = extras.editor;
35734        const isStickyToolbar$1 = isStickyToolbar(editor);
35735        // Alert and Confirm dialogs are Modal Dialogs
35736        const alertDialog = setup$2(extras.backstages.dialog);
35737        const confirmDialog = setup$1(extras.backstages.dialog);
35738        const open = (config, params, closeWindow) => {
35739            if (!isUndefined(params)) {
35740                if (params.inline === 'toolbar') {
35741                    return openInlineDialog(config, extras.backstages.popup.shared.anchors.inlineDialog(), closeWindow, params);
35742                }
35743                else if (params.inline === 'bottom') {
35744                    return openBottomInlineDialog(config, extras.backstages.popup.shared.anchors.inlineBottomDialog(), closeWindow, params);
35745                }
35746                else if (params.inline === 'cursor') {
35747                    return openInlineDialog(config, extras.backstages.popup.shared.anchors.cursor(), closeWindow, params);
35748                }
35749            }
35750            return openModalDialog(config, closeWindow);
35751        };
35752        const openUrl = (config, closeWindow) => openModalUrlDialog(config, closeWindow);
35753        const openModalUrlDialog = (config, closeWindow) => {
35754            const factory = (contents) => {
35755                const dialog = renderUrlDialog(contents, {
35756                    closeWindow: () => {
35757                        ModalDialog.hide(dialog.dialog);
35758                        closeWindow(dialog.instanceApi);
35759                    }
35760                }, editor, extras.backstages.dialog);
35761                ModalDialog.show(dialog.dialog);
35762                return dialog.instanceApi;
35763            };
35764            return DialogManager.openUrl(factory, config);
35765        };
35766        const openModalDialog = (config, closeWindow) => {
35767            const factory = (contents, internalInitialData, dataValidator) => {
35768                // We used to validate data here, but it's done by the instanceApi.setData call below.
35769                const initialData = internalInitialData;
35770                const dialogInit = {
35771                    dataValidator,
35772                    initialData,
35773                    internalDialog: contents
35774                };
35775                const dialog = renderDialog(dialogInit, {
35776                    redial: DialogManager.redial,
35777                    closeWindow: () => {
35778                        ModalDialog.hide(dialog.dialog);
35779                        closeWindow(dialog.instanceApi);
35780                    }
35781                }, extras.backstages.dialog);
35782                ModalDialog.show(dialog.dialog);
35783                dialog.instanceApi.setData(initialData);
35784                return dialog.instanceApi;
35785            };
35786            return DialogManager.open(factory, config);
35787        };
35788        const openInlineDialog = (config$1, anchor, closeWindow, windowParams) => {
35789            const factory = (contents, internalInitialData, dataValidator) => {
35790                const initialData = validateData(internalInitialData, dataValidator);
35791                const inlineDialog = value$2();
35792                const isToolbarLocationTop = extras.backstages.popup.shared.header.isPositionedAtTop();
35793                const dialogInit = {
35794                    dataValidator,
35795                    initialData,
35796                    internalDialog: contents
35797                };
35798                const refreshDocking = () => inlineDialog.on((dialog) => {
35799                    InlineView.reposition(dialog);
35800                    if (!isStickyToolbar$1 || !isToolbarLocationTop) {
35801                        Docking.refresh(dialog);
35802                    }
35803                });
35804                const dialogUi = renderInlineDialog(dialogInit, {
35805                    redial: DialogManager.redial,
35806                    closeWindow: () => {
35807                        inlineDialog.on(InlineView.hide);
35808                        editor.off('ResizeEditor', refreshDocking);
35809                        editor.off('ScrollWindow', repositionPopups$1);
35810                        inlineDialog.clear();
35811                        closeWindow(dialogUi.instanceApi);
35812                    }
35813                }, extras.backstages.popup, windowParams.ariaAttrs, refreshDocking);
35814                const repositionPopups$1 = () => dialogUi.dialog.getSystem().broadcastOn([repositionPopups()], { target: dialogUi.dialog.element });
35815                const dismissPopups$1 = () => dialogUi.dialog.getSystem().broadcastOn([dismissPopups()], { target: dialogUi.dialog.element });
35816                const inlineDialogComp = build$1(InlineView.sketch({
35817                    lazySink: extras.backstages.popup.shared.getSink,
35818                    dom: {
35819                        tag: 'div',
35820                        classes: []
35821                    },
35822                    // Fires the default dismiss event.
35823                    fireDismissalEventInstead: (windowParams.persistent ? { event: 'doNotDismissYet' } : {}),
35824                    // TINY-9412: The docking behaviour for inline dialogs is inconsistent
35825                    // for toolbar_location: bottom. We need to clarify exactly what the behaviour
35826                    // should be. The intent here might have been that they shouldn't automatically
35827                    // reposition at all because they aren't visually connected to the toolbar
35828                    // (i.e. inline "toolbar" dialogs still display at the top, even when the
35829                    // toolbar_location is bottom), but it's unclear.
35830                    ...isToolbarLocationTop ? {} : { fireRepositionEventInstead: {} },
35831                    inlineBehaviours: derive$1([
35832                        config('window-manager-inline-events', [
35833                            run$1(dismissRequested(), (_comp, _se) => {
35834                                emit(dialogUi.dialog, formCancelEvent);
35835                            })
35836                        ]),
35837                        ...inlineAdditionalBehaviours(editor, isStickyToolbar$1, isToolbarLocationTop, dismissPopups$1)
35838                    ]),
35839                    // Treat alert or confirm dialogs as part of the inline dialog
35840                    isExtraPart: (_comp, target) => isAlertOrConfirmDialog(target)
35841                }));
35842                inlineDialog.set(inlineDialogComp);
35843                const getInlineDialogBounds = () => {
35844                    // At the moment the inline dialog is just put anywhere in the body, and docking is what is used to make
35845                    // sure that it stays onscreen
35846                    const elem = editor.inline ? body() : SugarElement.fromDom(editor.getContainer());
35847                    const bounds = box$1(elem);
35848                    return Optional.some(bounds);
35849                };
35850                // Position the inline dialog
35851                InlineView.showWithinBounds(inlineDialogComp, premade(dialogUi.dialog), { anchor }, getInlineDialogBounds);
35852                // Refresh the docking position if not using a sticky toolbar
35853                if (!isStickyToolbar$1 || !isToolbarLocationTop) {
35854                    Docking.refresh(inlineDialogComp);
35855                    // Bind to the editor resize event and update docking as needed. We don't need to worry about
35856                    // 'ResizeWindow` as that's handled by docking already.
35857                    editor.on('ResizeEditor', refreshDocking);
35858                }
35859                editor.on('ScrollWindow', repositionPopups$1);
35860                // Set the initial data in the dialog and focus the first focusable item
35861                dialogUi.instanceApi.setData(initialData);
35862                Keying.focusIn(dialogUi.dialog);
35863                return dialogUi.instanceApi;
35864            };
35865            return DialogManager.open(factory, config$1);
35866        };
35867        const openBottomInlineDialog = (config$1, anchor, closeWindow, windowParams) => {
35868            const factory = (contents, internalInitialData, dataValidator) => {
35869                const initialData = validateData(internalInitialData, dataValidator);
35870                const inlineDialog = value$2();
35871                const isToolbarLocationTop = extras.backstages.popup.shared.header.isPositionedAtTop();
35872                const dialogInit = {
35873                    dataValidator,
35874                    initialData,
35875                    internalDialog: contents
35876                };
35877                const refreshDocking = () => inlineDialog.on((dialog) => {
35878                    InlineView.reposition(dialog);
35879                    Docking.refresh(dialog);
35880                });
35881                const dialogUi = renderInlineDialog(dialogInit, {
35882                    redial: DialogManager.redial,
35883                    closeWindow: () => {
35884                        inlineDialog.on(InlineView.hide);
35885                        editor.off('ResizeEditor ScrollWindow ElementScroll', refreshDocking);
35886                        inlineDialog.clear();
35887                        closeWindow(dialogUi.instanceApi);
35888                    }
35889                }, extras.backstages.popup, windowParams.ariaAttrs, refreshDocking);
35890                const inlineDialogComp = build$1(InlineView.sketch({
35891                    lazySink: extras.backstages.popup.shared.getSink,
35892                    dom: {
35893                        tag: 'div',
35894                        classes: []
35895                    },
35896                    // Fires the default dismiss event.
35897                    fireDismissalEventInstead: (windowParams.persistent ? { event: 'doNotDismissYet' } : {}),
35898                    ...isToolbarLocationTop ? {} : { fireRepositionEventInstead: {} },
35899                    inlineBehaviours: derive$1([
35900                        config('window-manager-inline-events', [
35901                            run$1(dismissRequested(), (_comp, _se) => {
35902                                emit(dialogUi.dialog, formCancelEvent);
35903                            })
35904                        ]),
35905                        Docking.config({
35906                            contextual: {
35907                                lazyContext: () => Optional.some(box$1(SugarElement.fromDom(editor.getContentAreaContainer()))),
35908                                fadeInClass: 'tox-dialog-dock-fadein',
35909                                fadeOutClass: 'tox-dialog-dock-fadeout',
35910                                transitionClass: 'tox-dialog-dock-transition'
35911                            },
35912                            modes: ['top', 'bottom'],
35913                            lazyViewport: (comp) => {
35914                                const optScrollingContext = detectWhenSplitUiMode(editor, comp.element);
35915                                return optScrollingContext.map((sc) => {
35916                                    const combinedBounds = getBoundsFrom(sc);
35917                                    return {
35918                                        bounds: combinedBounds,
35919                                        optScrollEnv: Optional.some({
35920                                            currentScrollTop: sc.element.dom.scrollTop,
35921                                            scrollElmTop: absolute$3(sc.element).top
35922                                        })
35923                                    };
35924                                }).getOrThunk(() => ({
35925                                    bounds: win(),
35926                                    optScrollEnv: Optional.none()
35927                                }));
35928                            }
35929                        })
35930                    ]),
35931                    // Treat alert or confirm dialogs as part of the inline dialog
35932                    isExtraPart: (_comp, target) => isAlertOrConfirmDialog(target)
35933                }));
35934                inlineDialog.set(inlineDialogComp);
35935                const getInlineDialogBounds = () => {
35936                    return extras.backstages.popup.shared.getSink().toOptional().bind((s) => {
35937                        const optScrollingContext = detectWhenSplitUiMode(editor, s.element);
35938                        // Margin between element and the bottom of the screen or the editor content area container
35939                        const margin = 15;
35940                        const bounds$1 = optScrollingContext.map((sc) => getBoundsFrom(sc)).getOr(win());
35941                        const contentAreaContainer = box$1(SugarElement.fromDom(editor.getContentAreaContainer()));
35942                        const constrainedBounds = constrain(contentAreaContainer, bounds$1);
35943                        return Optional.some(bounds(constrainedBounds.x, constrainedBounds.y, constrainedBounds.width, constrainedBounds.height - margin));
35944                    });
35945                };
35946                // Position the inline dialog
35947                InlineView.showWithinBounds(inlineDialogComp, premade(dialogUi.dialog), { anchor }, getInlineDialogBounds);
35948                Docking.refresh(inlineDialogComp);
35949                editor.on('ResizeEditor ScrollWindow ElementScroll ResizeWindow', refreshDocking);
35950                // Set the initial data in the dialog and focus the first focusable item
35951                dialogUi.instanceApi.setData(initialData);
35952                Keying.focusIn(dialogUi.dialog);
35953                return dialogUi.instanceApi;
35954            };
35955            return DialogManager.open(factory, config$1);
35956        };
35957        const confirm = (message, callback) => {
35958            confirmDialog.open(message, callback);
35959        };
35960        const alert = (message, callback) => {
35961            alertDialog.open(message, callback);
35962        };
35963        const close = (instanceApi) => {
35964            instanceApi.close();
35965        };
35966        return {
35967            open,
35968            openUrl,
35969            alert,
35970            close,
35971            confirm
35972        };
35973    };
35974
35975    const registerOptions = (editor) => {
35976        register$f(editor);
35977        register$e(editor);
35978        register(editor);
35979    };
35980    var Theme = () => {
35981        global$b.add('silver', (editor) => {
35982            registerOptions(editor);
35983            // When using the ui_mode: split, the popup sink is placed as a sibling to the
35984            // editor, which means that it might be subject to any scrolling environments
35985            // that the editor has. Therefore, we want to make the popup sink have an overall
35986            // bounds that is dependent on its scrolling environment. We don't know that ahead
35987            // of time, so we use a mutable variable whose value will change if there is a scrolling context.
35988            let popupSinkBounds = () => win();
35989            const { dialogs, popups, renderUI: renderModeUI } = setup$3(editor, {
35990                // consult the mutable variable to find out the bounds for the popup sink. When renderUI is
35991                // called, this mutable variable might be reassigned
35992                getPopupSinkBounds: () => popupSinkBounds()
35993            });
35994            // We wrap the `renderModeUI` function being returned by Render so that we can update
35995            // the getPopupSinkBounds mutable variable if required.
35996            // DON'T define this function as `async`; otherwise, it will slow down the rendering process and cause flickering if the editor is repeatedly removed and re-initialized.
35997            const renderUI = () => {
35998                const renderResult = renderModeUI();
35999                const optScrollingContext = detectWhenSplitUiMode(editor, popups.getMothership().element);
36000                optScrollingContext.each((sc) => {
36001                    popupSinkBounds = () => {
36002                        // At this stage, it looks like we need to calculate the bounds each time, just in
36003                        // case the scrolling context details have changed since the last time. The bounds considers
36004                        // the Boxes.box sizes, which might change over time.
36005                        return getBoundsFrom(sc);
36006                    };
36007                });
36008                return renderResult;
36009            };
36010            Autocompleter.register(editor, popups.backstage.shared);
36011            const windowMgr = setup({
36012                editor,
36013                backstages: {
36014                    popup: popups.backstage,
36015                    dialog: dialogs.backstage
36016                }
36017            });
36018            const notificationRegion = value$2();
36019            // The NotificationManager uses the popup mothership (and sink)
36020            const getNotificationManagerImpl = () => NotificationManagerImpl(editor, { backstage: popups.backstage }, popups.getMothership(), notificationRegion);
36021            const getPromotionElement = () => {
36022                return descendant(SugarElement.fromDom(editor.getContainer()), '.tox-promotion').map((promotion) => promotion.dom).getOrNull();
36023            };
36024            const getSinkElement = (type) => type === 'dialog' ? dialogs.getMothership().element.dom : popups.getMothership().element.dom;
36025            return {
36026                renderUI,
36027                getWindowManagerImpl: constant$1(windowMgr),
36028                getNotificationManagerImpl,
36029                getPromotionElement,
36030                getSinkElement
36031            };
36032        });
36033    };
36034
36035    Theme();
36036    /** *****
36037     * DO NOT EXPORT ANYTHING
36038     *
36039     * IF YOU DO ROLLUP WILL LEAVE A GLOBAL ON THE PAGE
36040     *******/
36041
36042})();

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.