import {
  __commonJS
} from "./chunk-HKJ2B2AA.js";

// node_modules/tinymce/tinymce.js
var require_tinymce = __commonJS({
  "node_modules/tinymce/tinymce.js"(exports, module) {
    (function() {
      "use strict";
      var typeOf$1 = function(x) {
        if (x === null) {
          return "null";
        }
        if (x === void 0) {
          return "undefined";
        }
        var t = typeof x;
        if (t === "object" && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === "Array")) {
          return "array";
        }
        if (t === "object" && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === "String")) {
          return "string";
        }
        return t;
      };
      var isEquatableType = function(x) {
        return ["undefined", "boolean", "number", "string", "function", "xml", "null"].indexOf(x) !== -1;
      };
      var sort$1 = function(xs, compareFn) {
        var clone2 = Array.prototype.slice.call(xs);
        return clone2.sort(compareFn);
      };
      var contramap = function(eqa, f) {
        return eq$2(function(x, y) {
          return eqa.eq(f(x), f(y));
        });
      };
      var eq$2 = function(f) {
        return { eq: f };
      };
      var tripleEq = eq$2(function(x, y) {
        return x === y;
      });
      var eqString = tripleEq;
      var eqArray = function(eqa) {
        return eq$2(function(x, y) {
          if (x.length !== y.length) {
            return false;
          }
          var len = x.length;
          for (var i = 0; i < len; i++) {
            if (!eqa.eq(x[i], y[i])) {
              return false;
            }
          }
          return true;
        });
      };
      var eqSortedArray = function(eqa, compareFn) {
        return contramap(eqArray(eqa), function(xs) {
          return sort$1(xs, compareFn);
        });
      };
      var eqRecord = function(eqa) {
        return eq$2(function(x, y) {
          var kx = Object.keys(x);
          var ky = Object.keys(y);
          if (!eqSortedArray(eqString).eq(kx, ky)) {
            return false;
          }
          var len = kx.length;
          for (var i = 0; i < len; i++) {
            var q = kx[i];
            if (!eqa.eq(x[q], y[q])) {
              return false;
            }
          }
          return true;
        });
      };
      var eqAny = eq$2(function(x, y) {
        if (x === y) {
          return true;
        }
        var tx = typeOf$1(x);
        var ty = typeOf$1(y);
        if (tx !== ty) {
          return false;
        }
        if (isEquatableType(tx)) {
          return x === y;
        } else if (tx === "array") {
          return eqArray(eqAny).eq(x, y);
        } else if (tx === "object") {
          return eqRecord(eqAny).eq(x, y);
        }
        return false;
      });
      const getPrototypeOf$2 = Object.getPrototypeOf;
      const hasProto = (v, constructor, predicate) => {
        if (predicate(v, constructor.prototype)) {
          return true;
        } else {
          return v.constructor?.name === constructor.name;
        }
      };
      const typeOf = (x) => {
        const t = typeof x;
        if (x === null) {
          return "null";
        } else if (t === "object" && Array.isArray(x)) {
          return "array";
        } else if (t === "object" && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
          return "string";
        } else {
          return t;
        }
      };
      const isType$1 = (type2) => (value2) => typeOf(value2) === type2;
      const isSimpleType = (type2) => (value2) => typeof value2 === type2;
      const eq$1 = (t) => (a) => t === a;
      const is$5 = (value2, constructor) => isObject(value2) && hasProto(value2, constructor, (o, proto) => getPrototypeOf$2(o) === proto);
      const isString = isType$1("string");
      const isObject = isType$1("object");
      const isPlainObject = (value2) => is$5(value2, Object);
      const isArray$1 = isType$1("array");
      const isNull = eq$1(null);
      const isBoolean = isSimpleType("boolean");
      const isUndefined = eq$1(void 0);
      const isNullable = (a) => a === null || a === void 0;
      const isNonNullable = (a) => !isNullable(a);
      const isFunction = isSimpleType("function");
      const isNumber = isSimpleType("number");
      const isArrayOf = (value2, pred) => {
        if (isArray$1(value2)) {
          for (let i = 0, len = value2.length; i < len; ++i) {
            if (!pred(value2[i])) {
              return false;
            }
          }
          return true;
        }
        return false;
      };
      const noop = () => {
      };
      const compose = (fa, fb) => {
        return (...args) => {
          return fa(fb.apply(null, args));
        };
      };
      const compose1 = (fbc, fab) => (a) => fbc(fab(a));
      const constant = (value2) => {
        return () => {
          return value2;
        };
      };
      const identity = (x) => {
        return x;
      };
      const tripleEquals = (a, b) => {
        return a === b;
      };
      function curry(fn, ...initialArgs) {
        return (...restArgs) => {
          const all2 = initialArgs.concat(restArgs);
          return fn.apply(null, all2);
        };
      }
      const not = (f) => (t) => !f(t);
      const die = (msg) => {
        return () => {
          throw new Error(msg);
        };
      };
      const apply$1 = (f) => {
        return f();
      };
      const call = (f) => {
        f();
      };
      const never = constant(false);
      const always = constant(true);
      class Optional {
        tag;
        value;
        // Sneaky optimisation: every instance of Optional.none is identical, so just
        // reuse the same object
        static singletonNone = new Optional(false);
        // The internal representation has a `tag` and a `value`, but both are
        // private: able to be console.logged, but not able to be accessed by code
        constructor(tag, value2) {
          this.tag = tag;
          this.value = value2;
        }
        // --- Identities ---
        /**
         * Creates a new `Optional<T>` that **does** contain a value.
         */
        static some(value2) {
          return new Optional(true, value2);
        }
        /**
         * Create a new `Optional<T>` that **does not** contain a value. `T` can be
         * any type because we don't actually have a `T`.
         */
        static none() {
          return Optional.singletonNone;
        }
        /**
         * Perform a transform on an `Optional` type. Regardless of whether this
         * `Optional` contains a value or not, `fold` will return a value of type `U`.
         * If this `Optional` does not contain a value, the `U` will be created by
         * calling `onNone`. If this `Optional` does contain a value, the `U` will be
         * created by calling `onSome`.
         *
         * For the FP enthusiasts in the room, this function:
         * 1. Could be used to implement all of the functions below
         * 2. Forms a catamorphism
         */
        fold(onNone, onSome) {
          if (this.tag) {
            return onSome(this.value);
          } else {
            return onNone();
          }
        }
        /**
         * Determine if this `Optional` object contains a value.
         */
        isSome() {
          return this.tag;
        }
        /**
         * Determine if this `Optional` object **does not** contain a value.
         */
        isNone() {
          return !this.tag;
        }
        // --- Functor (name stolen from Haskell / maths) ---
        /**
         * Perform a transform on an `Optional` object, **if** there is a value. If
         * you provide a function to turn a T into a U, this is the function you use
         * to turn an `Optional<T>` into an `Optional<U>`. If this **does** contain
         * a value then the output will also contain a value (that value being the
         * output of `mapper(this.value)`), and if this **does not** contain a value
         * then neither will the output.
         */
        map(mapper) {
          if (this.tag) {
            return Optional.some(mapper(this.value));
          } else {
            return Optional.none();
          }
        }
        // --- Monad (name stolen from Haskell / maths) ---
        /**
         * Perform a transform on an `Optional` object, **if** there is a value.
         * Unlike `map`, here the transform itself also returns an `Optional`.
         */
        bind(binder2) {
          if (this.tag) {
            return binder2(this.value);
          } else {
            return Optional.none();
          }
        }
        // --- Traversable (name stolen from Haskell / maths) ---
        /**
         * For a given predicate, this function finds out if there **exists** a value
         * inside this `Optional` object that meets the predicate. In practice, this
         * means that for `Optional`s that do not contain a value it returns false (as
         * no predicate-meeting value exists).
         */
        exists(predicate) {
          return this.tag && predicate(this.value);
        }
        /**
         * For a given predicate, this function finds out if **all** the values inside
         * this `Optional` object meet the predicate. In practice, this means that
         * for `Optional`s that do not contain a value it returns true (as all 0
         * objects do meet the predicate).
         */
        forall(predicate) {
          return !this.tag || predicate(this.value);
        }
        filter(predicate) {
          if (!this.tag || predicate(this.value)) {
            return this;
          } else {
            return Optional.none();
          }
        }
        // --- Getters ---
        /**
         * Get the value out of the inside of the `Optional` object, using a default
         * `replacement` value if the provided `Optional` object does not contain a
         * value.
         */
        getOr(replacement) {
          return this.tag ? this.value : replacement;
        }
        /**
         * Get the value out of the inside of the `Optional` object, using a default
         * `replacement` value if the provided `Optional` object does not contain a
         * value.  Unlike `getOr`, in this method the `replacement` object is also
         * `Optional` - meaning that this method will always return an `Optional`.
         */
        or(replacement) {
          return this.tag ? this : replacement;
        }
        /**
         * Get the value out of the inside of the `Optional` object, using a default
         * `replacement` value if the provided `Optional` object does not contain a
         * value. Unlike `getOr`, in this method the `replacement` value is
         * "thunked" - that is to say that you don't pass a value to `getOrThunk`, you
         * pass a function which (if called) will **return** the `value` you want to
         * use.
         */
        getOrThunk(thunk) {
          return this.tag ? this.value : thunk();
        }
        /**
         * Get the value out of the inside of the `Optional` object, using a default
         * `replacement` value if the provided Optional object does not contain a
         * value.
         *
         * Unlike `or`, in this method the `replacement` value is "thunked" - that is
         * to say that you don't pass a value to `orThunk`, you pass a function which
         * (if called) will **return** the `value` you want to use.
         *
         * Unlike `getOrThunk`, in this method the `replacement` value is also
         * `Optional`, meaning that this method will always return an `Optional`.
         */
        orThunk(thunk) {
          return this.tag ? this : thunk();
        }
        /**
         * Get the value out of the inside of the `Optional` object, throwing an
         * exception if the provided `Optional` object does not contain a value.
         *
         * WARNING:
         * You should only be using this function if you know that the `Optional`
         * object **is not** empty (otherwise you're throwing exceptions in production
         * code, which is bad).
         *
         * In tests this is more acceptable.
         *
         * Prefer other methods to this, such as `.each`.
         */
        getOrDie(message) {
          if (!this.tag) {
            throw new Error(message ?? "Called getOrDie on None");
          } else {
            return this.value;
          }
        }
        // --- Interop with null and undefined ---
        /**
         * Creates an `Optional` value from a nullable (or undefined-able) input.
         * Null, or undefined, is converted to `None`, and anything else is converted
         * to `Some`.
         */
        static from(value2) {
          return isNonNullable(value2) ? Optional.some(value2) : Optional.none();
        }
        /**
         * Converts an `Optional` to a nullable type, by getting the value if it
         * exists, or returning `null` if it does not.
         */
        getOrNull() {
          return this.tag ? this.value : null;
        }
        /**
         * Converts an `Optional` to an undefined-able type, by getting the value if
         * it exists, or returning `undefined` if it does not.
         */
        getOrUndefined() {
          return this.value;
        }
        // --- Utilities ---
        /**
         * If the `Optional` contains a value, perform an action on that value.
         * Unlike the rest of the methods on this type, `.each` has side-effects. If
         * you want to transform an `Optional<T>` **into** something, then this is not
         * the method for you. If you want to use an `Optional<T>` to **do**
         * something, then this is the method for you - provided you're okay with not
         * doing anything in the case where the `Optional` doesn't have a value inside
         * it. If you're not sure whether your use-case fits into transforming
         * **into** something or **doing** something, check whether it has a return
         * value. If it does, you should be performing a transform.
         */
        each(worker) {
          if (this.tag) {
            worker(this.value);
          }
        }
        /**
         * Turn the `Optional` object into an array that contains all of the values
         * stored inside the `Optional`. In practice, this means the output will have
         * either 0 or 1 elements.
         */
        toArray() {
          return this.tag ? [this.value] : [];
        }
        /**
         * Turn the `Optional` object into a string for debugging or printing. Not
         * recommended for production code, but good for debugging. Also note that
         * these days an `Optional` object can be logged to the console directly, and
         * its inner value (if it exists) will be visible.
         */
        toString() {
          return this.tag ? `some(${this.value})` : "none()";
        }
      }
      const nativeSlice = Array.prototype.slice;
      const nativeIndexOf = Array.prototype.indexOf;
      const nativePush = Array.prototype.push;
      const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
      const indexOf$1 = (xs, x) => {
        const r2 = rawIndexOf(xs, x);
        return r2 === -1 ? Optional.none() : Optional.some(r2);
      };
      const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1;
      const exists = (xs, pred) => {
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          if (pred(x, i)) {
            return true;
          }
        }
        return false;
      };
      const map$3 = (xs, f) => {
        const len = xs.length;
        const r2 = new Array(len);
        for (let i = 0; i < len; i++) {
          const x = xs[i];
          r2[i] = f(x, i);
        }
        return r2;
      };
      const each$e = (xs, f) => {
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          f(x, i);
        }
      };
      const eachr = (xs, f) => {
        for (let i = xs.length - 1; i >= 0; i--) {
          const x = xs[i];
          f(x, i);
        }
      };
      const partition$2 = (xs, pred) => {
        const pass = [];
        const fail = [];
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          const arr = pred(x, i) ? pass : fail;
          arr.push(x);
        }
        return { pass, fail };
      };
      const filter$5 = (xs, pred) => {
        const r2 = [];
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          if (pred(x, i)) {
            r2.push(x);
          }
        }
        return r2;
      };
      const groupBy = (xs, f) => {
        if (xs.length === 0) {
          return [];
        } else {
          let wasType = f(xs[0]);
          const r2 = [];
          let group = [];
          for (let i = 0, len = xs.length; i < len; i++) {
            const x = xs[i];
            const type2 = f(x);
            if (type2 !== wasType) {
              r2.push(group);
              group = [];
            }
            wasType = type2;
            group.push(x);
          }
          if (group.length !== 0) {
            r2.push(group);
          }
          return r2;
        }
      };
      const foldr = (xs, f, acc) => {
        eachr(xs, (x, i) => {
          acc = f(acc, x, i);
        });
        return acc;
      };
      const foldl = (xs, f, acc) => {
        each$e(xs, (x, i) => {
          acc = f(acc, x, i);
        });
        return acc;
      };
      const findUntil$1 = (xs, pred, until) => {
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          if (pred(x, i)) {
            return Optional.some(x);
          } else if (until(x, i)) {
            break;
          }
        }
        return Optional.none();
      };
      const find$2 = (xs, pred) => {
        return findUntil$1(xs, pred, never);
      };
      const findIndex$2 = (xs, pred) => {
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          if (pred(x, i)) {
            return Optional.some(i);
          }
        }
        return Optional.none();
      };
      const findLastIndex = (arr, pred) => {
        for (let i = arr.length - 1; i >= 0; i--) {
          if (pred(arr[i], i)) {
            return Optional.some(i);
          }
        }
        return Optional.none();
      };
      const flatten$1 = (xs) => {
        const r2 = [];
        for (let i = 0, len = xs.length; i < len; ++i) {
          if (!isArray$1(xs[i])) {
            throw new Error("Arr.flatten item " + i + " was not an array, input: " + xs);
          }
          nativePush.apply(r2, xs[i]);
        }
        return r2;
      };
      const bind$3 = (xs, f) => flatten$1(map$3(xs, f));
      const forall = (xs, pred) => {
        for (let i = 0, len = xs.length; i < len; ++i) {
          const x = xs[i];
          if (pred(x, i) !== true) {
            return false;
          }
        }
        return true;
      };
      const reverse = (xs) => {
        const r2 = nativeSlice.call(xs, 0);
        r2.reverse();
        return r2;
      };
      const difference = (a1, a2) => filter$5(a1, (x) => !contains$2(a2, x));
      const mapToObject = (xs, f) => {
        const r2 = {};
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          r2[String(x)] = f(x, i);
        }
        return r2;
      };
      const sort = (xs, comparator) => {
        const copy2 = nativeSlice.call(xs, 0);
        copy2.sort(comparator);
        return copy2;
      };
      const get$b = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
      const head = (xs) => get$b(xs, 0);
      const last$2 = (xs) => get$b(xs, xs.length - 1);
      const from = isFunction(Array.from) ? Array.from : (x) => nativeSlice.call(x);
      const findMap = (arr, f) => {
        for (let i = 0; i < arr.length; i++) {
          const r2 = f(arr[i], i);
          if (r2.isSome()) {
            return r2;
          }
        }
        return Optional.none();
      };
      const unique$1 = (xs, comparator) => {
        const r2 = [];
        const isDuplicated2 = isFunction(comparator) ? (x) => exists(r2, (i) => comparator(i, x)) : (x) => contains$2(r2, x);
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          if (!isDuplicated2(x)) {
            r2.push(x);
          }
        }
        return r2;
      };
      const keys = Object.keys;
      const hasOwnProperty$1 = Object.hasOwnProperty;
      const each$d = (obj, f) => {
        const props = keys(obj);
        for (let k = 0, len = props.length; k < len; k++) {
          const i = props[k];
          const x = obj[i];
          f(x, i);
        }
      };
      const map$2 = (obj, f) => {
        return tupleMap(obj, (x, i) => ({
          k: i,
          v: f(x, i)
        }));
      };
      const tupleMap = (obj, f) => {
        const r2 = {};
        each$d(obj, (x, i) => {
          const tuple = f(x, i);
          r2[tuple.k] = tuple.v;
        });
        return r2;
      };
      const objAcc = (r2) => (x, i) => {
        r2[i] = x;
      };
      const internalFilter = (obj, pred, onTrue, onFalse) => {
        each$d(obj, (x, i) => {
          (pred(x, i) ? onTrue : onFalse)(x, i);
        });
      };
      const bifilter = (obj, pred) => {
        const t = {};
        const f = {};
        internalFilter(obj, pred, objAcc(t), objAcc(f));
        return { t, f };
      };
      const filter$4 = (obj, pred) => {
        const t = {};
        internalFilter(obj, pred, objAcc(t), noop);
        return t;
      };
      const mapToArray = (obj, f) => {
        const r2 = [];
        each$d(obj, (value2, name2) => {
          r2.push(f(value2, name2));
        });
        return r2;
      };
      const values = (obj) => {
        return mapToArray(obj, identity);
      };
      const get$a = (obj, key) => {
        return has$2(obj, key) ? Optional.from(obj[key]) : Optional.none();
      };
      const has$2 = (obj, key) => hasOwnProperty$1.call(obj, key);
      const hasNonNullableKey = (obj, key) => has$2(obj, key) && obj[key] !== void 0 && obj[key] !== null;
      const equal$1 = (a1, a2, eq2 = eqAny) => eqRecord(eq2).eq(a1, a2);
      const generate$1 = (cases) => {
        if (!isArray$1(cases)) {
          throw new Error("cases must be an array");
        }
        if (cases.length === 0) {
          throw new Error("there must be at least one case");
        }
        const constructors = [];
        const adt2 = {};
        each$e(cases, (acase, count2) => {
          const keys$1 = keys(acase);
          if (keys$1.length !== 1) {
            throw new Error("one and only one name per case");
          }
          const key = keys$1[0];
          const value2 = acase[key];
          if (adt2[key] !== void 0) {
            throw new Error("duplicate key detected:" + key);
          } else if (key === "cata") {
            throw new Error("cannot have a case named cata (sorry)");
          } else if (!isArray$1(value2)) {
            throw new Error("case arguments must be an array");
          }
          constructors.push(key);
          adt2[key] = (...args) => {
            const argLength = args.length;
            if (argLength !== value2.length) {
              throw new Error("Wrong number of arguments to case " + key + ". Expected " + value2.length + " (" + value2 + "), got " + argLength);
            }
            const match2 = (branches) => {
              const branchKeys = keys(branches);
              if (constructors.length !== branchKeys.length) {
                throw new Error("Wrong number of arguments to match. Expected: " + constructors.join(",") + "\nActual: " + branchKeys.join(","));
              }
              const allReqd = forall(constructors, (reqKey) => {
                return contains$2(branchKeys, reqKey);
              });
              if (!allReqd) {
                throw new Error("Not all branches were specified when using match. Specified: " + branchKeys.join(", ") + "\nRequired: " + constructors.join(", "));
              }
              return branches[key].apply(null, args);
            };
            return {
              fold: (...foldArgs) => {
                if (foldArgs.length !== cases.length) {
                  throw new Error("Wrong number of arguments to fold. Expected " + cases.length + ", got " + foldArgs.length);
                }
                const target = foldArgs[count2];
                return target.apply(null, args);
              },
              match: match2,
              // NOTE: Only for debugging.
              log: (label) => {
                console.log(label, {
                  constructors,
                  constructor: key,
                  params: args
                });
              }
            };
          };
        });
        return adt2;
      };
      const Adt = {
        generate: generate$1
      };
      const Cell = (initial) => {
        let value2 = initial;
        const get2 = () => {
          return value2;
        };
        const set2 = (v) => {
          value2 = v;
        };
        return {
          get: get2,
          set: set2
        };
      };
      const value$2 = (value2) => {
        const applyHelper = (fn) => fn(value2);
        const constHelper = constant(value2);
        const outputHelper = () => output;
        const output = {
          // Debug info
          tag: true,
          inner: value2,
          // Actual Result methods
          fold: (_onError, onValue) => onValue(value2),
          isValue: always,
          isError: never,
          map: (mapper) => Result.value(mapper(value2)),
          mapError: outputHelper,
          bind: applyHelper,
          exists: applyHelper,
          forall: applyHelper,
          getOr: constHelper,
          or: outputHelper,
          getOrThunk: constHelper,
          orThunk: outputHelper,
          getOrDie: constHelper,
          each: (fn) => {
            fn(value2);
          },
          toOptional: () => Optional.some(value2)
        };
        return output;
      };
      const error = (error2) => {
        const outputHelper = () => output;
        const output = {
          // Debug info
          tag: false,
          inner: error2,
          // Actual Result methods
          fold: (onError, _onValue) => onError(error2),
          isValue: never,
          isError: always,
          map: outputHelper,
          mapError: (mapper) => Result.error(mapper(error2)),
          bind: outputHelper,
          exists: never,
          forall: always,
          getOr: identity,
          or: identity,
          getOrThunk: apply$1,
          orThunk: apply$1,
          getOrDie: die(String(error2)),
          each: noop,
          toOptional: Optional.none
        };
        return output;
      };
      const fromOption = (optional, err) => optional.fold(() => error(err), value$2);
      const Result = {
        value: value$2,
        error,
        fromOption
      };
      const Global = typeof window !== "undefined" ? window : Function("return this;")();
      const uuidV4Bytes = () => {
        const bytes = window.crypto.getRandomValues(new Uint8Array(16));
        bytes[6] = bytes[6] & 15 | 64;
        bytes[8] = bytes[8] & 63 | 128;
        return bytes;
      };
      const uuidV4String = () => {
        const uuid2 = uuidV4Bytes();
        const getHexRange = (startIndex, endIndex) => {
          let buff = "";
          for (let i = startIndex; i <= endIndex; ++i) {
            const hexByte = uuid2[i].toString(16).padStart(2, "0");
            buff += hexByte;
          }
          return buff;
        };
        return `${getHexRange(0, 3)}-${getHexRange(4, 5)}-${getHexRange(6, 7)}-${getHexRange(8, 9)}-${getHexRange(10, 15)}`;
      };
      const clamp$2 = (value2, min2, max2) => Math.min(Math.max(value2, min2), max2);
      const random = () => window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;
      let unique = 0;
      const generate = (prefix) => {
        const date = /* @__PURE__ */ new Date();
        const time = date.getTime();
        const random$1 = Math.floor(random() * 1e9);
        unique++;
        return prefix + "_" + random$1 + unique + String(time);
      };
      const uuidV4 = () => {
        if (window.isSecureContext) {
          return window.crypto.randomUUID();
        } else {
          return uuidV4String();
        }
      };
      const shallow$1 = (old, nu2) => {
        return nu2;
      };
      const deep$1 = (old, nu2) => {
        const bothObjects = isPlainObject(old) && isPlainObject(nu2);
        return bothObjects ? deepMerge(old, nu2) : nu2;
      };
      const baseMerge = (merger) => {
        return (...objects) => {
          if (objects.length === 0) {
            throw new Error(`Can't merge zero objects`);
          }
          const ret = {};
          for (let j = 0; j < objects.length; j++) {
            const curObject = objects[j];
            for (const key in curObject) {
              if (has$2(curObject, key)) {
                ret[key] = merger(ret[key], curObject[key]);
              }
            }
          }
          return ret;
        };
      };
      const deepMerge = baseMerge(deep$1);
      const merge$1 = baseMerge(shallow$1);
      const is$4 = (lhs, rhs, comparator = tripleEquals) => lhs.exists((left) => comparator(left, rhs));
      const equals = (lhs, rhs, comparator = tripleEquals) => lift2(lhs, rhs, comparator).getOr(lhs.isNone() && rhs.isNone());
      const cat = (arr) => {
        const r2 = [];
        const push = (x) => {
          r2.push(x);
        };
        for (let i = 0; i < arr.length; i++) {
          arr[i].each(push);
        }
        return r2;
      };
      const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();
      const lift3 = (oa, ob, oc, f) => oa.isSome() && ob.isSome() && oc.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie(), oc.getOrDie())) : Optional.none();
      const flatten = (oot) => oot.bind(identity);
      const someIf = (b, a) => b ? Optional.some(a) : Optional.none();
      const path = (parts, scope) => {
        let o = scope !== void 0 && scope !== null ? scope : Global;
        for (let i = 0; i < parts.length && o !== void 0 && o !== null; ++i) {
          o = o[parts[i]];
        }
        return o;
      };
      const resolve$3 = (p, scope) => {
        const parts = p.split(".");
        return path(parts, scope);
      };
      Adt.generate([
        { bothErrors: ["error1", "error2"] },
        { firstError: ["error1", "value2"] },
        { secondError: ["value1", "error2"] },
        { bothValues: ["value1", "value2"] }
      ]);
      const partition$1 = (results) => {
        const errors = [];
        const values2 = [];
        each$e(results, (result) => {
          result.fold((err) => {
            errors.push(err);
          }, (value2) => {
            values2.push(value2);
          });
        });
        return { errors, values: values2 };
      };
      const singleton = (doRevoke) => {
        const subject = Cell(Optional.none());
        const revoke = () => subject.get().each(doRevoke);
        const clear2 = () => {
          revoke();
          subject.set(Optional.none());
        };
        const isSet = () => subject.get().isSome();
        const get2 = () => subject.get();
        const set2 = (s) => {
          revoke();
          subject.set(Optional.some(s));
        };
        return {
          clear: clear2,
          isSet,
          get: get2,
          set: set2
        };
      };
      const repeatable = (delay) => {
        const intervalId = Cell(Optional.none());
        const revoke = () => intervalId.get().each((id) => clearInterval(id));
        const clear2 = () => {
          revoke();
          intervalId.set(Optional.none());
        };
        const isSet = () => intervalId.get().isSome();
        const get2 = () => intervalId.get();
        const set2 = (functionToRepeat) => {
          revoke();
          intervalId.set(Optional.some(setInterval(functionToRepeat, delay)));
        };
        return {
          clear: clear2,
          isSet,
          get: get2,
          set: set2
        };
      };
      const value$1 = () => {
        const subject = singleton(noop);
        const on2 = (f) => subject.get().each(f);
        return {
          ...subject,
          on: on2
        };
      };
      const removeFromStart = (str, numChars) => {
        return str.substring(numChars);
      };
      const checkRange = (str, substr, start2) => substr === "" || str.length >= substr.length && str.substr(start2, start2 + substr.length) === substr;
      const removeLeading = (str, prefix) => {
        return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
      };
      const contains$1 = (str, substr, start2 = 0, end2) => {
        const idx = str.indexOf(substr, start2);
        if (idx !== -1) {
          return isUndefined(end2) ? true : idx + substr.length <= end2;
        } else {
          return false;
        }
      };
      const startsWith = (str, prefix) => {
        return checkRange(str, prefix, 0);
      };
      const endsWith = (str, suffix) => {
        return checkRange(str, suffix, str.length - suffix.length);
      };
      const blank = (r2) => (s) => s.replace(r2, "");
      const trim$4 = blank(/^\s+|\s+$/g);
      const lTrim = blank(/^\s+/g);
      const rTrim = blank(/\s+$/g);
      const isNotEmpty = (s) => s.length > 0;
      const isEmpty$5 = (s) => !isNotEmpty(s);
      const repeat = (s, count2) => count2 <= 0 ? "" : new Array(count2 + 1).join(s);
      const toInt = (value2, radix = 10) => {
        const num = parseInt(value2, radix);
        return isNaN(num) ? Optional.none() : Optional.some(num);
      };
      const first$1 = (fn, rate) => {
        let timer = null;
        const cancel = () => {
          if (!isNull(timer)) {
            clearTimeout(timer);
            timer = null;
          }
        };
        const throttle = (...args) => {
          if (isNull(timer)) {
            timer = setTimeout(() => {
              timer = null;
              fn.apply(null, args);
            }, rate);
          }
        };
        return {
          cancel,
          throttle
        };
      };
      const last$1 = (fn, rate) => {
        let timer = null;
        const cancel = () => {
          if (!isNull(timer)) {
            clearTimeout(timer);
            timer = null;
          }
        };
        const throttle = (...args) => {
          cancel();
          timer = setTimeout(() => {
            timer = null;
            fn.apply(null, args);
          }, rate);
        };
        return {
          cancel,
          throttle
        };
      };
      const cached = (f) => {
        let called = false;
        let r2;
        return (...args) => {
          if (!called) {
            called = true;
            r2 = f.apply(null, args);
          }
          return r2;
        };
      };
      const zeroWidth = "\uFEFF";
      const nbsp = " ";
      const ellipsis = "…";
      const isZwsp$2 = (char) => char === zeroWidth;
      const removeZwsp = (s) => s.replace(/\uFEFF/g, "");
      const stringArray = (a) => {
        const all2 = {};
        each$e(a, (key) => {
          all2[key] = {};
        });
        return keys(all2);
      };
      const isArrayLike = (o) => o.length !== void 0;
      const isArray = Array.isArray;
      const toArray$1 = (obj) => {
        if (!isArray(obj)) {
          const array = [];
          for (let i = 0, l = obj.length; i < l; i++) {
            array[i] = obj[i];
          }
          return array;
        } else {
          return obj;
        }
      };
      const each$c = (o, cb, s) => {
        if (!o) {
          return false;
        }
        s = s || o;
        if (isArrayLike(o)) {
          for (let n = 0, l = o.length; n < l; n++) {
            if (cb.call(s, o[n], n, o) === false) {
              return false;
            }
          }
        } else {
          for (const n in o) {
            if (has$2(o, n)) {
              if (cb.call(s, o[n], n, o) === false) {
                return false;
              }
            }
          }
        }
        return true;
      };
      const map$1 = (array, callback) => {
        const out = [];
        each$c(array, (item, index) => {
          out.push(callback(item, index, array));
        });
        return out;
      };
      const filter$3 = (a, f) => {
        const o = [];
        each$c(a, (v, index) => {
          if (!f || f(v, index, a)) {
            o.push(v);
          }
        });
        return o;
      };
      const indexOf = (a, v) => {
        if (a) {
          for (let i = 0, l = a.length; i < l; i++) {
            if (a[i] === v) {
              return i;
            }
          }
        }
        return -1;
      };
      const reduce = (collection, iteratee, accumulator, thisArg) => {
        let acc = isUndefined(accumulator) ? collection[0] : accumulator;
        for (let i = 0; i < collection.length; i++) {
          acc = iteratee.call(thisArg, acc, collection[i], i);
        }
        return acc;
      };
      const findIndex$1 = (array, predicate, thisArg) => {
        for (let i = 0, l = array.length; i < l; i++) {
          if (predicate.call(thisArg, array[i], i, array)) {
            return i;
          }
        }
        return -1;
      };
      const last = (collection) => collection[collection.length - 1];
      const DeviceType = (os2, browser2, userAgent2, mediaMatch2) => {
        const isiPad = os2.isiOS() && /ipad/i.test(userAgent2) === true;
        const isiPhone = os2.isiOS() && !isiPad;
        const isMobile = os2.isiOS() || os2.isAndroid();
        const isTouch2 = isMobile || mediaMatch2("(pointer:coarse)");
        const isTablet2 = isiPad || !isiPhone && isMobile && mediaMatch2("(min-device-width:768px)");
        const isPhone2 = isiPhone || isMobile && !isTablet2;
        const iOSwebview = browser2.isSafari() && os2.isiOS() && /safari/i.test(userAgent2) === false;
        const isDesktop = !isPhone2 && !isTablet2 && !iOSwebview;
        return {
          isiPad: constant(isiPad),
          isiPhone: constant(isiPhone),
          isTablet: constant(isTablet2),
          isPhone: constant(isPhone2),
          isTouch: constant(isTouch2),
          isAndroid: os2.isAndroid,
          isiOS: os2.isiOS,
          isWebView: constant(iOSwebview),
          isDesktop: constant(isDesktop)
        };
      };
      const firstMatch = (regexes, s) => {
        for (let i = 0; i < regexes.length; i++) {
          const x = regexes[i];
          if (x.test(s)) {
            return x;
          }
        }
        return void 0;
      };
      const find$1 = (regexes, agent) => {
        const r2 = firstMatch(regexes, agent);
        if (!r2) {
          return { major: 0, minor: 0 };
        }
        const group = (i) => {
          return Number(agent.replace(r2, "$" + i));
        };
        return nu$3(group(1), group(2));
      };
      const detect$4 = (versionRegexes, agent) => {
        const cleanedAgent = String(agent).toLowerCase();
        if (versionRegexes.length === 0) {
          return unknown$2();
        }
        return find$1(versionRegexes, cleanedAgent);
      };
      const unknown$2 = () => {
        return nu$3(0, 0);
      };
      const nu$3 = (major, minor) => {
        return { major, minor };
      };
      const Version = {
        nu: nu$3,
        detect: detect$4,
        unknown: unknown$2
      };
      const detectBrowser$1 = (browsers2, userAgentData) => {
        return findMap(userAgentData.brands, (uaBrand) => {
          const lcBrand = uaBrand.brand.toLowerCase();
          return find$2(browsers2, (browser2) => lcBrand === browser2.brand?.toLowerCase()).map((info) => ({
            current: info.name,
            version: Version.nu(parseInt(uaBrand.version, 10), 0)
          }));
        });
      };
      const detect$3 = (candidates, userAgent2) => {
        const agent = String(userAgent2).toLowerCase();
        return find$2(candidates, (candidate) => {
          return candidate.search(agent);
        });
      };
      const detectBrowser = (browsers2, userAgent2) => {
        return detect$3(browsers2, userAgent2).map((browser2) => {
          const version = Version.detect(browser2.versionRegexes, userAgent2);
          return {
            current: browser2.name,
            version
          };
        });
      };
      const detectOs = (oses2, userAgent2) => {
        return detect$3(oses2, userAgent2).map((os2) => {
          const version = Version.detect(os2.versionRegexes, userAgent2);
          return {
            current: os2.name,
            version
          };
        });
      };
      const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
      const checkContains = (target) => {
        return (uastring) => {
          return contains$1(uastring, target);
        };
      };
      const browsers = [
        // This is legacy Edge
        {
          name: "Edge",
          versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],
          search: (uastring) => {
            return contains$1(uastring, "edge/") && contains$1(uastring, "chrome") && contains$1(uastring, "safari") && contains$1(uastring, "applewebkit");
          }
        },
        // This is Google Chrome and Chromium Edge
        {
          name: "Chromium",
          brand: "Chromium",
          versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/, normalVersionRegex],
          search: (uastring) => {
            return contains$1(uastring, "chrome") && !contains$1(uastring, "chromeframe");
          }
        },
        {
          name: "IE",
          versionRegexes: [/.*?msie\ ?([0-9]+)\.([0-9]+).*/, /.*?rv:([0-9]+)\.([0-9]+).*/],
          search: (uastring) => {
            return contains$1(uastring, "msie") || contains$1(uastring, "trident");
          }
        },
        // INVESTIGATE: Is this still the Opera user agent?
        {
          name: "Opera",
          versionRegexes: [normalVersionRegex, /.*?opera\/([0-9]+)\.([0-9]+).*/],
          search: checkContains("opera")
        },
        {
          name: "Firefox",
          versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],
          search: checkContains("firefox")
        },
        {
          name: "Safari",
          versionRegexes: [normalVersionRegex, /.*?cpu os ([0-9]+)_([0-9]+).*/],
          search: (uastring) => {
            return (contains$1(uastring, "safari") || contains$1(uastring, "mobile/")) && contains$1(uastring, "applewebkit");
          }
        }
      ];
      const oses = [
        {
          name: "Windows",
          search: checkContains("win"),
          versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]
        },
        {
          name: "iOS",
          search: (uastring) => {
            return contains$1(uastring, "iphone") || contains$1(uastring, "ipad");
          },
          versionRegexes: [/.*?version\/\ ?([0-9]+)\.([0-9]+).*/, /.*cpu os ([0-9]+)_([0-9]+).*/, /.*cpu iphone os ([0-9]+)_([0-9]+).*/]
        },
        {
          name: "Android",
          search: checkContains("android"),
          versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/]
        },
        {
          name: "macOS",
          search: checkContains("mac os x"),
          versionRegexes: [/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]
        },
        {
          name: "Linux",
          search: checkContains("linux"),
          versionRegexes: []
        },
        {
          name: "Solaris",
          search: checkContains("sunos"),
          versionRegexes: []
        },
        {
          name: "FreeBSD",
          search: checkContains("freebsd"),
          versionRegexes: []
        },
        {
          name: "ChromeOS",
          search: checkContains("cros"),
          versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/]
        }
      ];
      const PlatformInfo = {
        browsers: constant(browsers),
        oses: constant(oses)
      };
      const edge = "Edge";
      const chromium = "Chromium";
      const ie = "IE";
      const opera = "Opera";
      const firefox = "Firefox";
      const safari = "Safari";
      const unknown$1 = () => {
        return nu$2({
          current: void 0,
          version: Version.unknown()
        });
      };
      const nu$2 = (info) => {
        const current = info.current;
        const version = info.version;
        const isBrowser = (name2) => () => current === name2;
        return {
          current,
          version,
          isEdge: isBrowser(edge),
          isChromium: isBrowser(chromium),
          // NOTE: isIe just looks too weird
          isIE: isBrowser(ie),
          isOpera: isBrowser(opera),
          isFirefox: isBrowser(firefox),
          isSafari: isBrowser(safari)
        };
      };
      const Browser = {
        unknown: unknown$1,
        nu: nu$2,
        edge: constant(edge),
        chromium: constant(chromium),
        ie: constant(ie),
        opera: constant(opera),
        firefox: constant(firefox),
        safari: constant(safari)
      };
      const windows = "Windows";
      const ios = "iOS";
      const android = "Android";
      const linux = "Linux";
      const macos = "macOS";
      const solaris = "Solaris";
      const freebsd = "FreeBSD";
      const chromeos = "ChromeOS";
      const unknown = () => {
        return nu$1({
          current: void 0,
          version: Version.unknown()
        });
      };
      const nu$1 = (info) => {
        const current = info.current;
        const version = info.version;
        const isOS = (name2) => () => current === name2;
        return {
          current,
          version,
          isWindows: isOS(windows),
          // TODO: Fix capitalisation
          isiOS: isOS(ios),
          isAndroid: isOS(android),
          isMacOS: isOS(macos),
          isLinux: isOS(linux),
          isSolaris: isOS(solaris),
          isFreeBSD: isOS(freebsd),
          isChromeOS: isOS(chromeos)
        };
      };
      const OperatingSystem = {
        unknown,
        nu: nu$1,
        windows: constant(windows),
        ios: constant(ios),
        android: constant(android),
        linux: constant(linux),
        macos: constant(macos),
        solaris: constant(solaris),
        freebsd: constant(freebsd),
        chromeos: constant(chromeos)
      };
      const detect$2 = (userAgent2, userAgentDataOpt, mediaMatch2) => {
        const browsers2 = PlatformInfo.browsers();
        const oses2 = PlatformInfo.oses();
        const browser2 = userAgentDataOpt.bind((userAgentData) => detectBrowser$1(browsers2, userAgentData)).orThunk(() => detectBrowser(browsers2, userAgent2)).fold(Browser.unknown, Browser.nu);
        const os2 = detectOs(oses2, userAgent2).fold(OperatingSystem.unknown, OperatingSystem.nu);
        const deviceType2 = DeviceType(os2, browser2, userAgent2, mediaMatch2);
        return {
          browser: browser2,
          os: os2,
          deviceType: deviceType2
        };
      };
      const PlatformDetection = {
        detect: detect$2
      };
      const mediaMatch = (query) => window.matchMedia(query).matches;
      let platform$4 = cached(() => PlatformDetection.detect(window.navigator.userAgent, Optional.from(window.navigator.userAgentData), mediaMatch));
      const detect$1 = () => platform$4();
      const unsafe = (name2, scope) => {
        return resolve$3(name2, scope);
      };
      const getOrDie = (name2, scope) => {
        const actual = unsafe(name2, scope);
        if (actual === void 0 || actual === null) {
          throw new Error(name2 + " not available on this browser");
        }
        return actual;
      };
      const getPrototypeOf$1 = Object.getPrototypeOf;
      const sandHTMLElement = (scope) => {
        return getOrDie("HTMLElement", scope);
      };
      const isPrototypeOf = (x) => {
        const scope = resolve$3("ownerDocument.defaultView", x);
        return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\w*Element$/.test(getPrototypeOf$1(x).constructor.name));
      };
      const userAgent = window.navigator.userAgent;
      const platform$3 = detect$1();
      const browser$3 = platform$3.browser;
      const os$1 = platform$3.os;
      const deviceType = platform$3.deviceType;
      const windowsPhone = userAgent.indexOf("Windows Phone") !== -1;
      const Env = {
        /**
         * Transparent image data url.
         *
         * @property transparentSrc
         * @type Boolean
         * @final
         */
        transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
        /**
         * Returns the IE document mode. For non IE browsers, this will fake IE 10 document mode.
         *
         * @property documentMode
         * @type Number
         */
        documentMode: browser$3.isIE() ? document.documentMode || 7 : 10,
        cacheSuffix: null,
        container: null,
        /**
         * Constant if CSP mode is possible or not. Meaning we can't use script urls for the iframe.
         */
        canHaveCSP: !browser$3.isIE(),
        windowsPhone,
        /**
         * @include ../../../../../tools/docs/tinymce.Env.js
         */
        browser: {
          current: browser$3.current,
          version: browser$3.version,
          isChromium: browser$3.isChromium,
          isEdge: browser$3.isEdge,
          isFirefox: browser$3.isFirefox,
          isIE: browser$3.isIE,
          isOpera: browser$3.isOpera,
          isSafari: browser$3.isSafari
        },
        os: {
          current: os$1.current,
          version: os$1.version,
          isAndroid: os$1.isAndroid,
          isChromeOS: os$1.isChromeOS,
          isFreeBSD: os$1.isFreeBSD,
          isiOS: os$1.isiOS,
          isLinux: os$1.isLinux,
          isMacOS: os$1.isMacOS,
          isSolaris: os$1.isSolaris,
          isWindows: os$1.isWindows
        },
        deviceType: {
          isDesktop: deviceType.isDesktop,
          isiPad: deviceType.isiPad,
          isiPhone: deviceType.isiPhone,
          isPhone: deviceType.isPhone,
          isTablet: deviceType.isTablet,
          isTouch: deviceType.isTouch,
          isWebView: deviceType.isWebView
        }
      };
      const whiteSpaceRegExp$1 = /^\s*|\s*$/g;
      const trim$3 = (str) => {
        return isNullable(str) ? "" : ("" + str).replace(whiteSpaceRegExp$1, "");
      };
      const is$3 = (obj, type2) => {
        if (!type2) {
          return obj !== void 0;
        }
        if (type2 === "array" && isArray(obj)) {
          return true;
        }
        return typeof obj === type2;
      };
      const makeMap$4 = (items, delim, map2 = {}) => {
        const resolvedItems = isString(items) ? items.split(delim || ",") : items || [];
        let i = resolvedItems.length;
        while (i--) {
          map2[resolvedItems[i]] = {};
        }
        return map2;
      };
      const hasOwnProperty = has$2;
      const extend$3 = (obj, ...exts) => {
        for (let i = 0; i < exts.length; i++) {
          const ext = exts[i];
          for (const name2 in ext) {
            if (has$2(ext, name2)) {
              const value2 = ext[name2];
              if (value2 !== void 0) {
                obj[name2] = value2;
              }
            }
          }
        }
        return obj;
      };
      const walk$4 = function(o, f, n, s) {
        s = s || this;
        if (o) {
          if (n) {
            o = o[n];
          }
          each$c(o, (o2, i) => {
            if (f.call(s, o2, i, n) === false) {
              return false;
            } else {
              walk$4(o2, f, n, s);
              return true;
            }
          });
        }
      };
      const resolve$2 = (n, o = window) => {
        const path2 = n.split(".");
        for (let i = 0, l = path2.length; i < l; i++) {
          o = o[path2[i]];
          if (!o) {
            break;
          }
        }
        return o;
      };
      const explode$3 = (s, d) => {
        if (isArray$1(s)) {
          return s;
        } else if (s === "") {
          return [];
        } else {
          return map$1(s.split(d || ","), trim$3);
        }
      };
      const _addCacheSuffix = (url) => {
        const cacheSuffix = Env.cacheSuffix;
        if (cacheSuffix) {
          url += (url.indexOf("?") === -1 ? "?" : "&") + cacheSuffix;
        }
        return url;
      };
      const Tools = {
        trim: trim$3,
        /**
         * Returns true/false if the object is an array or not.
         *
         * @method isArray
         * @param {Object} obj Object to check.
         * @return {Boolean} true/false state if the object is an array or not.
         */
        isArray,
        is: is$3,
        /**
         * Converts the specified object into a real JavaScript array.
         *
         * @method toArray
         * @param {Object} obj Object to convert into array.
         * @return {Array} Array object based in input.
         */
        toArray: toArray$1,
        makeMap: makeMap$4,
        /**
         * Performs an iteration of all items in a collection such as an object or array. This method will execute the
         * callback function for each item in the collection, if the callback returns false the iteration will terminate.
         * The callback has the following format: `cb(value, key_or_index)`.
         *
         * @method each
         * @param {Object} o Collection to iterate.
         * @param {Function} cb Callback function to execute for each item.
         * @param {Object} s Optional scope to execute the callback in.
         * @example
         * // Iterate an array
         * tinymce.each([ 1,2,3 ], (v, i) => {
         *   console.debug("Value: " + v + ", Index: " + i);
         * });
         *
         * // Iterate an object
         * tinymce.each({ a: 1, b: 2, c: 3 }, (v, k) => {
         *   console.debug("Value: " + v + ", Key: " + k);
         * });
         */
        each: each$c,
        /**
         * Creates a new array by the return value of each iteration function call. This enables you to convert
         * one array list into another.
         *
         * @method map
         * @param {Array} array Array of items to iterate.
         * @param {Function} callback Function to call for each item. It's return value will be the new value.
         * @return {Array} Array with new values based on function return values.
         */
        map: map$1,
        /**
         * Filters out items from the input array by calling the specified function for each item.
         * If the function returns false the item will be excluded if it returns true it will be included.
         *
         * @method grep
         * @param {Array} a Array of items to loop though.
         * @param {Function} f Function to call for each item. Include/exclude depends on it's return value.
         * @return {Array} New array with values imported and filtered based in input.
         * @example
         * // Filter out some items, this will return an array with 4 and 5
         * const items = tinymce.grep([ 1,2,3,4,5 ], (v) => v > 3);
         */
        grep: filter$3,
        /**
         * Returns an index of the item or -1 if item is not present in the array.
         *
         * @method inArray
         * @param {any} item Item to search for.
         * @param {Array} arr Array to search in.
         * @return {Number} index of the item or -1 if item was not found.
         */
        inArray: indexOf,
        hasOwn: hasOwnProperty,
        extend: extend$3,
        walk: walk$4,
        resolve: resolve$2,
        explode: explode$3,
        _addCacheSuffix
      };
      const fromHtml$1 = (html2, scope) => {
        const doc = scope || document;
        const div = doc.createElement("div");
        div.innerHTML = html2;
        if (!div.hasChildNodes() || div.childNodes.length > 1) {
          const message = "HTML does not have a single root node";
          console.error(message, html2);
          throw new Error(message);
        }
        return fromDom$2(div.childNodes[0]);
      };
      const fromTag = (tag, scope) => {
        const doc = scope || document;
        const node = doc.createElement(tag);
        return fromDom$2(node);
      };
      const fromText = (text2, scope) => {
        const doc = scope || document;
        const node = doc.createTextNode(text2);
        return fromDom$2(node);
      };
      const fromDom$2 = (node) => {
        if (node === null || node === void 0) {
          throw new Error("Node cannot be null or undefined");
        }
        return {
          dom: node
        };
      };
      const fromPoint$2 = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$2);
      const SugarElement = {
        fromHtml: fromHtml$1,
        fromTag,
        fromText,
        fromDom: fromDom$2,
        fromPoint: fromPoint$2
      };
      const setStart = (rng, situ) => {
        situ.fold((e) => {
          rng.setStartBefore(e.dom);
        }, (e, o) => {
          rng.setStart(e.dom, o);
        }, (e) => {
          rng.setStartAfter(e.dom);
        });
      };
      const setFinish = (rng, situ) => {
        situ.fold((e) => {
          rng.setEndBefore(e.dom);
        }, (e, o) => {
          rng.setEnd(e.dom, o);
        }, (e) => {
          rng.setEndAfter(e.dom);
        });
      };
      const relativeToNative = (win, startSitu, finishSitu) => {
        const range2 = win.document.createRange();
        setStart(range2, startSitu);
        setFinish(range2, finishSitu);
        return range2;
      };
      const exactToNative = (win, start2, soffset, finish, foffset) => {
        const rng = win.document.createRange();
        rng.setStart(start2.dom, soffset);
        rng.setEnd(finish.dom, foffset);
        return rng;
      };
      const adt$3 = Adt.generate([
        { ltr: ["start", "soffset", "finish", "foffset"] },
        { rtl: ["start", "soffset", "finish", "foffset"] }
      ]);
      const fromRange = (win, type2, range2) => type2(SugarElement.fromDom(range2.startContainer), range2.startOffset, SugarElement.fromDom(range2.endContainer), range2.endOffset);
      const getRanges$1 = (win, selection) => selection.match({
        domRange: (rng) => {
          return {
            ltr: constant(rng),
            rtl: Optional.none
          };
        },
        relative: (startSitu, finishSitu) => {
          return {
            ltr: cached(() => relativeToNative(win, startSitu, finishSitu)),
            rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu)))
          };
        },
        exact: (start2, soffset, finish, foffset) => {
          return {
            ltr: cached(() => exactToNative(win, start2, soffset, finish, foffset)),
            rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start2, soffset)))
          };
        }
      });
      const doDiagnose = (win, ranges) => {
        const rng = ranges.ltr();
        if (rng.collapsed) {
          const reversed = ranges.rtl().filter((rev) => rev.collapsed === false);
          return reversed.map((rev) => (
            // We need to use "reversed" here, because the original only has one point (collapsed)
            adt$3.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)
          )).getOrThunk(() => fromRange(win, adt$3.ltr, rng));
        } else {
          return fromRange(win, adt$3.ltr, rng);
        }
      };
      const diagnose = (win, selection) => {
        const ranges = getRanges$1(win, selection);
        return doDiagnose(win, ranges);
      };
      adt$3.ltr;
      adt$3.rtl;
      const COMMENT = 8;
      const DOCUMENT = 9;
      const DOCUMENT_FRAGMENT = 11;
      const ELEMENT = 1;
      const TEXT = 3;
      const is$2 = (element, selector) => {
        const dom2 = element.dom;
        if (dom2.nodeType !== ELEMENT) {
          return false;
        } else {
          const elem = dom2;
          if (elem.matches !== void 0) {
            return elem.matches(selector);
          } else if (elem.msMatchesSelector !== void 0) {
            return elem.msMatchesSelector(selector);
          } else if (elem.webkitMatchesSelector !== void 0) {
            return elem.webkitMatchesSelector(selector);
          } else if (elem.mozMatchesSelector !== void 0) {
            return elem.mozMatchesSelector(selector);
          } else {
            throw new Error("Browser lacks native selectors");
          }
        }
      };
      const bypassSelector = (dom2) => (
        // Only elements, documents and shadow roots support querySelector
        // shadow root element type is DOCUMENT_FRAGMENT
        dom2.nodeType !== ELEMENT && dom2.nodeType !== DOCUMENT && dom2.nodeType !== DOCUMENT_FRAGMENT || // IE fix for complex queries on empty nodes: http://jsfiddle.net/spyder/fv9ptr5L/
        dom2.childElementCount === 0
      );
      const all = (selector, scope) => {
        const base = scope === void 0 ? document : scope.dom;
        return bypassSelector(base) ? [] : map$3(base.querySelectorAll(selector), SugarElement.fromDom);
      };
      const one = (selector, scope) => {
        const base = scope === void 0 ? document : scope.dom;
        return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);
      };
      const eq = (e1, e2) => e1.dom === e2.dom;
      const contains = (e1, e2) => {
        const d1 = e1.dom;
        const d2 = e2.dom;
        return d1 === d2 ? false : d1.contains(d2);
      };
      const is$1 = is$2;
      const toArray = (target, f) => {
        const r2 = [];
        const recurse = (e) => {
          r2.push(e);
          return f(e);
        };
        let cur = f(target);
        do {
          cur = cur.bind(recurse);
        } while (cur.isSome());
        return r2;
      };
      const name = (element) => {
        const r2 = element.dom.nodeName;
        return r2.toLowerCase();
      };
      const type$1 = (element) => element.dom.nodeType;
      const isType = (t) => (element) => type$1(element) === t;
      const isComment$1 = (element) => type$1(element) === COMMENT || name(element) === "#comment";
      const isHTMLElement$1 = (element) => isElement$8(element) && isPrototypeOf(element.dom);
      const isElement$8 = isType(ELEMENT);
      const isText$c = isType(TEXT);
      const isDocument$2 = isType(DOCUMENT);
      const isDocumentFragment$1 = isType(DOCUMENT_FRAGMENT);
      const isTag = (tag) => (e) => isElement$8(e) && name(e) === tag;
      const owner$1 = (element) => SugarElement.fromDom(element.dom.ownerDocument);
      const documentOrOwner = (dos) => isDocument$2(dos) ? dos : owner$1(dos);
      const documentElement = (element) => SugarElement.fromDom(documentOrOwner(element).dom.documentElement);
      const defaultView = (element) => SugarElement.fromDom(documentOrOwner(element).dom.defaultView);
      const parent = (element) => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
      const parentElement = (element) => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);
      const parents$1 = (element, isRoot2) => {
        const stop2 = isFunction(isRoot2) ? isRoot2 : never;
        let dom2 = element.dom;
        const ret = [];
        while (dom2.parentNode !== null && dom2.parentNode !== void 0) {
          const rawParent = dom2.parentNode;
          const p = SugarElement.fromDom(rawParent);
          ret.push(p);
          if (stop2(p) === true) {
            break;
          } else {
            dom2 = rawParent;
          }
        }
        return ret;
      };
      const siblings = (element) => {
        const filterSelf = (elements) => filter$5(elements, (x) => !eq(element, x));
        return parent(element).map(children$1).map(filterSelf).getOr([]);
      };
      const prevSibling = (element) => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
      const nextSibling = (element) => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
      const prevSiblings = (element) => reverse(toArray(element, prevSibling));
      const nextSiblings = (element) => toArray(element, nextSibling);
      const children$1 = (element) => map$3(element.dom.childNodes, SugarElement.fromDom);
      const child$1 = (element, index) => {
        const cs = element.dom.childNodes;
        return Optional.from(cs[index]).map(SugarElement.fromDom);
      };
      const firstChild = (element) => child$1(element, 0);
      const lastChild = (element) => child$1(element, element.dom.childNodes.length - 1);
      const childNodesCount = (element) => element.dom.childNodes.length;
      const getHead = (doc) => {
        const b = doc.dom.head;
        if (b === null || b === void 0) {
          throw new Error("Head is not available yet");
        }
        return SugarElement.fromDom(b);
      };
      const isShadowRoot = (dos) => isDocumentFragment$1(dos) && isNonNullable(dos.dom.host);
      const getRootNode = (e) => SugarElement.fromDom(e.dom.getRootNode());
      const getStyleContainer = (dos) => isShadowRoot(dos) ? dos : getHead(documentOrOwner(dos));
      const getContentContainer = (dos) => (
        // Can't use SugarBody.body without causing a circular module reference (since SugarBody.inBody uses SugarShadowDom)
        isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body)
      );
      const getShadowRoot = (e) => {
        const r2 = getRootNode(e);
        return isShadowRoot(r2) ? Optional.some(r2) : Optional.none();
      };
      const getShadowHost = (e) => SugarElement.fromDom(e.dom.host);
      const getOriginalEventTarget = (event) => {
        if (isNonNullable(event.target)) {
          const el = SugarElement.fromDom(event.target);
          if (isElement$8(el) && isOpenShadowHost(el)) {
            if (event.composed && event.composedPath) {
              const composedPath = event.composedPath();
              if (composedPath) {
                return head(composedPath);
              }
            }
          }
        }
        return Optional.from(event.target);
      };
      const isOpenShadowHost = (element) => isNonNullable(element.dom.shadowRoot);
      const mkEvent = (target, x, y, stop2, prevent, kill, raw) => ({
        target,
        x,
        y,
        stop: stop2,
        prevent,
        kill,
        raw
      });
      const fromRawEvent = (rawEvent) => {
        const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));
        const stop2 = () => rawEvent.stopPropagation();
        const prevent = () => rawEvent.preventDefault();
        const kill = compose(prevent, stop2);
        return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop2, prevent, kill, rawEvent);
      };
      const handle$1 = (filter2, handler) => (rawEvent) => {
        if (filter2(rawEvent)) {
          handler(fromRawEvent(rawEvent));
        }
      };
      const binder = (element, event, filter2, handler, useCapture) => {
        const wrapped = handle$1(filter2, handler);
        element.dom.addEventListener(event, wrapped, useCapture);
        return {
          unbind: curry(unbind, element, event, wrapped, useCapture)
        };
      };
      const bind$2 = (element, event, filter2, handler) => binder(element, event, filter2, handler, false);
      const unbind = (element, event, handler, useCapture) => {
        element.dom.removeEventListener(event, handler, useCapture);
      };
      const filter$2 = always;
      const bind$1 = (element, event, handler) => bind$2(element, event, filter$2, handler);
      const getDocument = () => SugarElement.fromDom(document);
      const focus$1 = (element, preventScroll = false) => element.dom.focus({ preventScroll });
      const hasFocus$1 = (element) => {
        const root = getRootNode(element).dom;
        return element.dom === root.activeElement;
      };
      const active = (root = getDocument()) => Optional.from(root.dom.activeElement).map(SugarElement.fromDom);
      const search = (element) => active(getRootNode(element)).filter((e) => element.dom.contains(e.dom));
      const before$4 = (marker, element) => {
        const parent$1 = parent(marker);
        parent$1.each((v) => {
          v.dom.insertBefore(element.dom, marker.dom);
        });
      };
      const after$4 = (marker, element) => {
        const sibling2 = nextSibling(marker);
        sibling2.fold(() => {
          const parent$1 = parent(marker);
          parent$1.each((v) => {
            append$1(v, element);
          });
        }, (v) => {
          before$4(v, element);
        });
      };
      const prepend = (parent2, element) => {
        const firstChild$1 = firstChild(parent2);
        firstChild$1.fold(() => {
          append$1(parent2, element);
        }, (v) => {
          parent2.dom.insertBefore(element.dom, v.dom);
        });
      };
      const append$1 = (parent2, element) => {
        parent2.dom.appendChild(element.dom);
      };
      const wrap$2 = (element, wrapper) => {
        before$4(element, wrapper);
        append$1(wrapper, element);
      };
      const before$3 = (marker, elements) => {
        each$e(elements, (x) => {
          before$4(marker, x);
        });
      };
      const after$3 = (marker, elements) => {
        each$e(elements, (x, i) => {
          const e = i === 0 ? marker : elements[i - 1];
          after$4(e, x);
        });
      };
      const append = (parent2, elements) => {
        each$e(elements, (x) => {
          append$1(parent2, x);
        });
      };
      const rawSet = (dom2, key, value2) => {
        if (isString(value2) || isBoolean(value2) || isNumber(value2)) {
          dom2.setAttribute(key, value2 + "");
        } else {
          console.error("Invalid call to Attribute.set. Key ", key, ":: Value ", value2, ":: Element ", dom2);
          throw new Error("Attribute value was not simple");
        }
      };
      const set$4 = (element, key, value2) => {
        rawSet(element.dom, key, value2);
      };
      const setAll$1 = (element, attrs) => {
        const dom2 = element.dom;
        each$d(attrs, (v, k) => {
          rawSet(dom2, k, v);
        });
      };
      const get$9 = (element, key) => {
        const v = element.dom.getAttribute(key);
        return v === null ? void 0 : v;
      };
      const getOpt = (element, key) => Optional.from(get$9(element, key));
      const has$1 = (element, key) => {
        const dom2 = element.dom;
        return dom2 && dom2.hasAttribute ? dom2.hasAttribute(key) : false;
      };
      const remove$9 = (element, key) => {
        element.dom.removeAttribute(key);
      };
      const hasNone = (element) => {
        const attrs = element.dom.attributes;
        return attrs === void 0 || attrs === null || attrs.length === 0;
      };
      const clone$4 = (element) => foldl(element.dom.attributes, (acc, attr) => {
        acc[attr.name] = attr.value;
        return acc;
      }, {});
      const empty = (element) => {
        element.dom.textContent = "";
        each$e(children$1(element), (rogue) => {
          remove$8(rogue);
        });
      };
      const remove$8 = (element) => {
        const dom2 = element.dom;
        if (dom2.parentNode !== null) {
          dom2.parentNode.removeChild(dom2);
        }
      };
      const unwrap = (wrapper) => {
        const children2 = children$1(wrapper);
        if (children2.length > 0) {
          after$3(wrapper, children2);
        }
        remove$8(wrapper);
      };
      const clone$3 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));
      const shallow = (original) => clone$3(original, false);
      const deep = (original) => clone$3(original, true);
      const shallowAs = (original, tag) => {
        const nu2 = SugarElement.fromTag(tag);
        const attributes = clone$4(original);
        setAll$1(nu2, attributes);
        return nu2;
      };
      const mutate = (original, tag) => {
        const nu2 = shallowAs(original, tag);
        after$4(original, nu2);
        const children2 = children$1(original);
        append(nu2, children2);
        remove$8(original);
        return nu2;
      };
      const fromHtml = (html2, scope) => {
        const doc = scope || document;
        const div = doc.createElement("div");
        div.innerHTML = html2;
        return children$1(SugarElement.fromDom(div));
      };
      const fromDom$1 = (nodes) => map$3(nodes, SugarElement.fromDom);
      const get$8 = (element) => element.dom.innerHTML;
      const set$3 = (element, content) => {
        const owner2 = owner$1(element);
        const docDom = owner2.dom;
        const fragment = SugarElement.fromDom(docDom.createDocumentFragment());
        const contentElements = fromHtml(content, docDom);
        append(fragment, contentElements);
        empty(element);
        append$1(element, fragment);
      };
      const getOuter = (element) => {
        const container = SugarElement.fromTag("div");
        const clone2 = SugarElement.fromDom(element.dom.cloneNode(true));
        append$1(container, clone2);
        return get$8(container);
      };
      const isSupported = (dom2) => dom2.style !== void 0 && isFunction(dom2.style.getPropertyValue);
      const inBody = (element) => {
        const dom2 = isText$c(element) ? element.dom.parentNode : element.dom;
        if (dom2 === void 0 || dom2 === null || dom2.ownerDocument === null) {
          return false;
        }
        const doc = dom2.ownerDocument;
        return getShadowRoot(SugarElement.fromDom(dom2)).fold(() => doc.body.contains(dom2), compose1(inBody, getShadowHost));
      };
      const internalSet = (dom2, property, value2) => {
        if (!isString(value2)) {
          console.error("Invalid call to CSS.set. Property ", property, ":: Value ", value2, ":: Element ", dom2);
          throw new Error("CSS value must be a string: " + value2);
        }
        if (isSupported(dom2)) {
          dom2.style.setProperty(property, value2);
        }
      };
      const internalRemove = (dom2, property) => {
        if (isSupported(dom2)) {
          dom2.style.removeProperty(property);
        }
      };
      const set$2 = (element, property, value2) => {
        const dom2 = element.dom;
        internalSet(dom2, property, value2);
      };
      const setAll = (element, css) => {
        const dom2 = element.dom;
        each$d(css, (v, k) => {
          internalSet(dom2, k, v);
        });
      };
      const get$7 = (element, property) => {
        const dom2 = element.dom;
        const styles = window.getComputedStyle(dom2);
        const r2 = styles.getPropertyValue(property);
        return r2 === "" && !inBody(element) ? getUnsafeProperty(dom2, property) : r2;
      };
      const getUnsafeProperty = (dom2, property) => isSupported(dom2) ? dom2.style.getPropertyValue(property) : "";
      const getRaw$1 = (element, property) => {
        const dom2 = element.dom;
        const raw = getUnsafeProperty(dom2, property);
        return Optional.from(raw).filter((r2) => r2.length > 0);
      };
      const getAllRaw = (element) => {
        const css = {};
        const dom2 = element.dom;
        if (isSupported(dom2)) {
          for (let i = 0; i < dom2.style.length; i++) {
            const ruleName = dom2.style.item(i);
            css[ruleName] = dom2.style[ruleName];
          }
        }
        return css;
      };
      const remove$7 = (element, property) => {
        const dom2 = element.dom;
        internalRemove(dom2, property);
        if (is$4(getOpt(element, "style").map(trim$4), "")) {
          remove$9(element, "style");
        }
      };
      const reflow = (e) => e.dom.offsetWidth;
      const Dimension = (name2, getOffset) => {
        const set2 = (element, h) => {
          if (!isNumber(h) && !h.match(/^[0-9]+$/)) {
            throw new Error(name2 + ".set accepts only positive integer values. Value was " + h);
          }
          const dom2 = element.dom;
          if (isSupported(dom2)) {
            dom2.style[name2] = h + "px";
          }
        };
        const get2 = (element) => {
          const r2 = getOffset(element);
          if (r2 <= 0 || r2 === null) {
            const css = get$7(element, name2);
            return parseFloat(css) || 0;
          }
          return r2;
        };
        const getOuter2 = get2;
        const aggregate = (element, properties) => foldl(properties, (acc, property) => {
          const val = get$7(element, property);
          const value2 = val === void 0 ? 0 : parseInt(val, 10);
          return isNaN(value2) ? acc : acc + value2;
        }, 0);
        const max2 = (element, value2, properties) => {
          const cumulativeInclusions = aggregate(element, properties);
          const absoluteMax = value2 > cumulativeInclusions ? value2 - cumulativeInclusions : 0;
          return absoluteMax;
        };
        return {
          set: set2,
          get: get2,
          getOuter: getOuter2,
          aggregate,
          max: max2
        };
      };
      const api$1 = Dimension("height", (element) => {
        const dom2 = element.dom;
        return inBody(element) ? dom2.getBoundingClientRect().height : dom2.offsetHeight;
      });
      const get$6 = (element) => api$1.get(element);
      const r = (left, top) => {
        const translate2 = (x, y) => r(left + x, top + y);
        return {
          left,
          top,
          translate: translate2
        };
      };
      const SugarPosition = r;
      const boxPosition = (dom2) => {
        const box = dom2.getBoundingClientRect();
        return SugarPosition(box.left, box.top);
      };
      const firstDefinedOrZero = (a, b) => {
        if (a !== void 0) {
          return a;
        } else {
          return b !== void 0 ? b : 0;
        }
      };
      const absolute = (element) => {
        const doc = element.dom.ownerDocument;
        const body = doc.body;
        const win = doc.defaultView;
        const html2 = doc.documentElement;
        if (body === element.dom) {
          return SugarPosition(body.offsetLeft, body.offsetTop);
        }
        const scrollTop = firstDefinedOrZero(win?.pageYOffset, html2.scrollTop);
        const scrollLeft2 = firstDefinedOrZero(win?.pageXOffset, html2.scrollLeft);
        const clientTop = firstDefinedOrZero(html2.clientTop, body.clientTop);
        const clientLeft = firstDefinedOrZero(html2.clientLeft, body.clientLeft);
        return viewport(element).translate(scrollLeft2 - clientLeft, scrollTop - clientTop);
      };
      const viewport = (element) => {
        const dom2 = element.dom;
        const doc = dom2.ownerDocument;
        const body = doc.body;
        if (body === dom2) {
          return SugarPosition(body.offsetLeft, body.offsetTop);
        }
        if (!inBody(element)) {
          return SugarPosition(0, 0);
        }
        return boxPosition(dom2);
      };
      const get$5 = (_DOC) => {
        const doc = _DOC !== void 0 ? _DOC.dom : document;
        const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;
        const y = doc.body.scrollTop || doc.documentElement.scrollTop;
        return SugarPosition(x, y);
      };
      const to = (x, y, _DOC) => {
        const doc = _DOC !== void 0 ? _DOC.dom : document;
        const win = doc.defaultView;
        if (win) {
          win.scrollTo(x, y);
        }
      };
      const intoView = (element, alignToTop) => {
        const isSafari2 = detect$1().browser.isSafari();
        if (isSafari2 && isFunction(element.dom.scrollIntoViewIfNeeded)) {
          element.dom.scrollIntoViewIfNeeded(false);
        } else {
          element.dom.scrollIntoView(alignToTop);
        }
      };
      const NodeValue = (is2, name2) => {
        const get2 = (element) => {
          if (!is2(element)) {
            throw new Error("Can only get " + name2 + " value of a " + name2 + " node");
          }
          return getOption2(element).getOr("");
        };
        const getOption2 = (element) => is2(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
        const set2 = (element, value2) => {
          if (!is2(element)) {
            throw new Error("Can only set raw " + name2 + " value of a " + name2 + " node");
          }
          element.dom.nodeValue = value2;
        };
        return {
          get: get2,
          getOption: getOption2,
          set: set2
        };
      };
      const fromElements = (elements, scope) => {
        const doc = scope || document;
        const fragment = doc.createDocumentFragment();
        each$e(elements, (element) => {
          fragment.appendChild(element.dom);
        });
        return SugarElement.fromDom(fragment);
      };
      const api = NodeValue(isText$c, "text");
      const get$4 = (element) => api.get(element);
      const getOption = (element) => api.getOption(element);
      const set$1 = (element, value2) => api.set(element, value2);
      const read$4 = (element, attr) => {
        const value2 = get$9(element, attr);
        return value2 === void 0 || value2 === "" ? [] : value2.split(" ");
      };
      const add$4 = (element, attr, id) => {
        const old = read$4(element, attr);
        const nu2 = old.concat([id]);
        set$4(element, attr, nu2.join(" "));
        return true;
      };
      const remove$6 = (element, attr, id) => {
        const nu2 = filter$5(read$4(element, attr), (v) => v !== id);
        if (nu2.length > 0) {
          set$4(element, attr, nu2.join(" "));
        } else {
          remove$9(element, attr);
        }
        return false;
      };
      var ClosestOrAncestor = (is2, ancestor2, scope, a, isRoot2) => {
        if (is2(scope, a)) {
          return Optional.some(scope);
        } else if (isFunction(isRoot2) && isRoot2(scope)) {
          return Optional.none();
        } else {
          return ancestor2(scope, a, isRoot2);
        }
      };
      const ancestor$5 = (scope, predicate, isRoot2) => {
        let element = scope.dom;
        const stop2 = isFunction(isRoot2) ? isRoot2 : never;
        while (element.parentNode) {
          element = element.parentNode;
          const el = SugarElement.fromDom(element);
          if (predicate(el)) {
            return Optional.some(el);
          } else if (stop2(el)) {
            break;
          }
        }
        return Optional.none();
      };
      const closest$4 = (scope, predicate, isRoot2) => {
        const is2 = (s, test) => test(s);
        return ClosestOrAncestor(is2, ancestor$5, scope, predicate, isRoot2);
      };
      const sibling$1 = (scope, predicate) => {
        const element = scope.dom;
        if (!element.parentNode) {
          return Optional.none();
        }
        return child(SugarElement.fromDom(element.parentNode), (x) => !eq(scope, x) && predicate(x));
      };
      const child = (scope, predicate) => {
        const pred = (node) => predicate(SugarElement.fromDom(node));
        const result = find$2(scope.dom.childNodes, pred);
        return result.map(SugarElement.fromDom);
      };
      const descendant$2 = (scope, predicate) => {
        const descend2 = (node) => {
          for (let i = 0; i < node.childNodes.length; i++) {
            const child2 = SugarElement.fromDom(node.childNodes[i]);
            if (predicate(child2)) {
              return Optional.some(child2);
            }
            const res = descend2(node.childNodes[i]);
            if (res.isSome()) {
              return res;
            }
          }
          return Optional.none();
        };
        return descend2(scope.dom);
      };
      const ancestor$4 = (scope, selector, isRoot2) => ancestor$5(scope, (e) => is$2(e, selector), isRoot2);
      const descendant$1 = (scope, selector) => one(selector, scope);
      const closest$3 = (scope, selector, isRoot2) => {
        const is2 = (element, selector2) => is$2(element, selector2);
        return ClosestOrAncestor(is2, ancestor$4, scope, selector, isRoot2);
      };
      const supports = (element) => element.dom.classList !== void 0;
      const get$3 = (element) => read$4(element, "class");
      const add$3 = (element, clazz) => add$4(element, "class", clazz);
      const remove$5 = (element, clazz) => remove$6(element, "class", clazz);
      const toggle$2 = (element, clazz) => {
        if (contains$2(get$3(element), clazz)) {
          return remove$5(element, clazz);
        } else {
          return add$3(element, clazz);
        }
      };
      const add$2 = (element, clazz) => {
        if (supports(element)) {
          element.dom.classList.add(clazz);
        } else {
          add$3(element, clazz);
        }
      };
      const cleanClass = (element) => {
        const classList = supports(element) ? element.dom.classList : get$3(element);
        if (classList.length === 0) {
          remove$9(element, "class");
        }
      };
      const remove$4 = (element, clazz) => {
        if (supports(element)) {
          const classList = element.dom.classList;
          classList.remove(clazz);
        } else {
          remove$5(element, clazz);
        }
        cleanClass(element);
      };
      const toggle$1 = (element, clazz) => {
        const result = supports(element) ? element.dom.classList.toggle(clazz) : toggle$2(element, clazz);
        cleanClass(element);
        return result;
      };
      const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);
      const add$1 = (element, classes) => {
        each$e(classes, (x) => {
          add$2(element, x);
        });
      };
      const remove$3 = (element, classes) => {
        each$e(classes, (x) => {
          remove$4(element, x);
        });
      };
      const closest$2 = (target) => closest$3(target, "[contenteditable]");
      const isEditable$2 = (element, assumeEditable = false) => {
        if (inBody(element)) {
          return element.dom.isContentEditable;
        } else {
          return closest$2(element).fold(constant(assumeEditable), (editable) => getRaw(editable) === "true");
        }
      };
      const getRaw = (element) => element.dom.contentEditable;
      const set = (element, editable) => {
        element.dom.contentEditable = editable ? "true" : "false";
      };
      const ancestors$1 = (scope, predicate, isRoot2) => filter$5(parents$1(scope, isRoot2), predicate);
      const children = (scope, predicate) => filter$5(children$1(scope), predicate);
      const descendants$1 = (scope, predicate) => {
        let result = [];
        each$e(children$1(scope), (x) => {
          if (predicate(x)) {
            result = result.concat([x]);
          }
          result = result.concat(descendants$1(x, predicate));
        });
        return result;
      };
      const ancestors = (scope, selector, isRoot2) => (
        // It may surprise you to learn this is exactly what JQuery does
        // TODO: Avoid all this wrapping and unwrapping
        ancestors$1(scope, (e) => is$2(e, selector), isRoot2)
      );
      const descendants = (scope, selector) => all(selector, scope);
      const ancestor$3 = (scope, predicate, isRoot2) => ancestor$5(scope, predicate, isRoot2).isSome();
      const sibling = (scope, predicate) => sibling$1(scope, predicate).isSome();
      const descendant = (scope, predicate) => descendant$2(scope, predicate).isSome();
      const ancestor$2 = (element, target) => ancestor$3(element, curry(eq, target));
      const ancestor$1 = (scope, selector, isRoot2) => ancestor$4(scope, selector, isRoot2).isSome();
      const ensureIsRoot = (isRoot2) => isFunction(isRoot2) ? isRoot2 : never;
      const ancestor = (scope, transform, isRoot2) => {
        let element = scope.dom;
        const stop2 = ensureIsRoot(isRoot2);
        while (element.parentNode) {
          element = element.parentNode;
          const el = SugarElement.fromDom(element);
          const transformed = transform(el);
          if (transformed.isSome()) {
            return transformed;
          } else if (stop2(el)) {
            break;
          }
        }
        return Optional.none();
      };
      const closest$1 = (scope, transform, isRoot2) => {
        const current = transform(scope);
        const stop2 = ensureIsRoot(isRoot2);
        return current.orThunk(() => stop2(scope) ? Optional.none() : ancestor(scope, transform, stop2));
      };
      const isTextNodeWithCursorPosition = (el) => getOption(el).filter((text2) => (
        // For the purposes of finding cursor positions only allow text nodes with content,
        // but trim removes &nbsp; and that's allowed
        text2.trim().length !== 0 || text2.indexOf(nbsp) > -1
      )).isSome();
      const isContentEditableFalse$b = (elem) => isHTMLElement$1(elem) && get$9(elem, "contenteditable") === "false";
      const elementsWithCursorPosition = ["img", "br"];
      const isCursorPosition = (elem) => {
        const hasCursorPosition = isTextNodeWithCursorPosition(elem);
        return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem)) || isContentEditableFalse$b(elem);
      };
      const first = (element) => descendant$2(element, isCursorPosition);
      const create$c = (start2, soffset, finish, foffset) => ({
        start: start2,
        soffset,
        finish,
        foffset
      });
      const SimRange = {
        create: create$c
      };
      const adt$2 = Adt.generate([
        { before: ["element"] },
        { on: ["element", "offset"] },
        { after: ["element"] }
      ]);
      const cata = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);
      const getStart$2 = (situ) => situ.fold(identity, identity, identity);
      const before$2 = adt$2.before;
      const on = adt$2.on;
      const after$2 = adt$2.after;
      const Situ = {
        before: before$2,
        on,
        after: after$2,
        cata,
        getStart: getStart$2
      };
      const adt$1 = Adt.generate([
        { domRange: ["rng"] },
        { relative: ["startSitu", "finishSitu"] },
        { exact: ["start", "soffset", "finish", "foffset"] }
      ]);
      const exactFromRange = (simRange) => adt$1.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);
      const getStart$1 = (selection) => selection.match({
        domRange: (rng) => SugarElement.fromDom(rng.startContainer),
        relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),
        exact: (start2, _soffset, _finish, _foffset) => start2
      });
      const domRange = adt$1.domRange;
      const relative = adt$1.relative;
      const exact = adt$1.exact;
      const getWin = (selection) => {
        const start2 = getStart$1(selection);
        return defaultView(start2);
      };
      const range = SimRange.create;
      const SimSelection = {
        domRange,
        relative,
        exact,
        exactFromRange,
        getWin,
        range
      };
      const caretPositionFromPoint = (doc, x, y) => Optional.from(doc.caretPositionFromPoint?.(x, y)).bind((pos) => {
        if (pos.offsetNode === null) {
          return Optional.none();
        }
        const r2 = doc.createRange();
        r2.setStart(pos.offsetNode, pos.offset);
        r2.collapse();
        return Optional.some(r2);
      });
      const caretRangeFromPoint = (doc, x, y) => Optional.from(doc.caretRangeFromPoint?.(x, y));
      const availableSearch = (doc, x, y) => {
        if (doc.caretPositionFromPoint) {
          return caretPositionFromPoint(doc, x, y);
        } else if (doc.caretRangeFromPoint) {
          return caretRangeFromPoint(doc, x, y);
        } else {
          return Optional.none();
        }
      };
      const fromPoint$1 = (win, x, y) => {
        const doc = win.document;
        return availableSearch(doc, x, y).map((rng) => SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));
      };
      const beforeSpecial = (element, offset) => {
        const name$1 = name(element);
        if ("input" === name$1) {
          return Situ.after(element);
        } else if (!contains$2(["br", "img"], name$1)) {
          return Situ.on(element, offset);
        } else {
          return offset === 0 ? Situ.before(element) : Situ.after(element);
        }
      };
      const preprocessRelative = (startSitu, finishSitu) => {
        const start2 = startSitu.fold(Situ.before, beforeSpecial, Situ.after);
        const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after);
        return SimSelection.relative(start2, finish);
      };
      const preprocessExact = (start2, soffset, finish, foffset) => {
        const startSitu = beforeSpecial(start2, soffset);
        const finishSitu = beforeSpecial(finish, foffset);
        return SimSelection.relative(startSitu, finishSitu);
      };
      const preprocess = (selection) => selection.match({
        domRange: (rng) => {
          const start2 = SugarElement.fromDom(rng.startContainer);
          const finish = SugarElement.fromDom(rng.endContainer);
          return preprocessExact(start2, rng.startOffset, finish, rng.endOffset);
        },
        relative: preprocessRelative,
        exact: preprocessExact
      });
      const toNative = (selection) => {
        const win = SimSelection.getWin(selection).dom;
        const getDomRange = (start2, soffset, finish, foffset) => exactToNative(win, start2, soffset, finish, foffset);
        const filtered = preprocess(selection);
        return diagnose(win, filtered).match({
          ltr: getDomRange,
          rtl: getDomRange
        });
      };
      const getAtPoint = (win, x, y) => fromPoint$1(win, x, y);
      const get$2 = (_win) => {
        const win = _win === void 0 ? window : _win;
        if (detect$1().browser.isFirefox()) {
          return Optional.none();
        } else {
          return Optional.from(win.visualViewport);
        }
      };
      const bounds = (x, y, width, height) => ({
        x,
        y,
        width,
        height,
        right: x + width,
        bottom: y + height
      });
      const getBounds = (_win) => {
        const win = _win === void 0 ? window : _win;
        const doc = win.document;
        const scroll = get$5(SugarElement.fromDom(doc));
        return get$2(win).fold(() => {
          const html2 = win.document.documentElement;
          const width = html2.clientWidth;
          const height = html2.clientHeight;
          return bounds(scroll.left, scroll.top, width, height);
        }, (visualViewport) => (
          // iOS doesn't update the pageTop/pageLeft when element.scrollIntoView() is called, so we need to fallback to the
          // scroll position which will always be less than the page top/left values when page top/left are accurate/correct.
          bounds(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height)
        ));
      };
      class DomTreeWalker {
        rootNode;
        node;
        constructor(startNode, rootNode) {
          this.node = startNode;
          this.rootNode = rootNode;
          this.current = this.current.bind(this);
          this.next = this.next.bind(this);
          this.prev = this.prev.bind(this);
          this.prev2 = this.prev2.bind(this);
        }
        /**
         * Returns the current node.
         *
         * @method current
         * @return {Node/undefined} Current node where the walker is, or undefined if the walker has reached the end.
         */
        current() {
          return this.node;
        }
        /**
         * Walks to the next node in tree.
         *
         * @method next
         * @return {Node/undefined} Current node where the walker is after moving to the next node, or undefined if the walker has reached the end.
         */
        next(shallow2) {
          this.node = this.findSibling(this.node, "firstChild", "nextSibling", shallow2);
          return this.node;
        }
        /**
         * Walks to the previous node in tree.
         *
         * @method prev
         * @return {Node/undefined} Current node where the walker is after moving to the previous node, or undefined if the walker has reached the end.
         */
        prev(shallow2) {
          this.node = this.findSibling(this.node, "lastChild", "previousSibling", shallow2);
          return this.node;
        }
        prev2(shallow2) {
          this.node = this.findPreviousNode(this.node, shallow2);
          return this.node;
        }
        findSibling(node, startName, siblingName, shallow2) {
          if (node) {
            if (!shallow2 && node[startName]) {
              return node[startName];
            }
            if (node !== this.rootNode) {
              let sibling2 = node[siblingName];
              if (sibling2) {
                return sibling2;
              }
              for (let parent2 = node.parentNode; parent2 && parent2 !== this.rootNode; parent2 = parent2.parentNode) {
                sibling2 = parent2[siblingName];
                if (sibling2) {
                  return sibling2;
                }
              }
            }
          }
          return void 0;
        }
        findPreviousNode(node, shallow2) {
          if (node) {
            const sibling2 = node.previousSibling;
            if (this.rootNode && sibling2 === this.rootNode) {
              return;
            }
            if (sibling2) {
              if (!shallow2) {
                for (let child2 = sibling2.lastChild; child2; child2 = child2.lastChild) {
                  if (!child2.lastChild) {
                    return child2;
                  }
                }
              }
              return sibling2;
            }
            const parent2 = node.parentNode;
            if (parent2 && parent2 !== this.rootNode) {
              return parent2;
            }
          }
          return void 0;
        }
      }
      const whiteSpaceRegExp = /^[ \t\r\n]*$/;
      const isWhitespaceText = (text2) => whiteSpaceRegExp.test(text2);
      const isZwsp$1 = (text2) => {
        for (const c of text2) {
          if (!isZwsp$2(c)) {
            return false;
          }
        }
        return true;
      };
      const isCollapsibleWhitespace$1 = (c) => " \f	\v".indexOf(c) !== -1;
      const isNewLineChar = (c) => c === "\n" || c === "\r";
      const isNewline = (text2, idx) => idx < text2.length && idx >= 0 ? isNewLineChar(text2[idx]) : false;
      const normalize$4 = (text2, tabSpaces = 4, isStartOfContent = true, isEndOfContent = true) => {
        const tabSpace = repeat(" ", tabSpaces);
        const normalizedText = text2.replace(/\t/g, tabSpace);
        const result = foldl(normalizedText, (acc, c) => {
          if (isCollapsibleWhitespace$1(c) || c === nbsp) {
            if (acc.pcIsSpace || acc.str === "" && isStartOfContent || acc.str.length === normalizedText.length - 1 && isEndOfContent || isNewline(normalizedText, acc.str.length + 1)) {
              return { pcIsSpace: false, str: acc.str + nbsp };
            } else {
              return { pcIsSpace: true, str: acc.str + " " };
            }
          } else {
            return { pcIsSpace: isNewLineChar(c), str: acc.str + c };
          }
        }, { pcIsSpace: false, str: "" });
        return result.str;
      };
      const isNodeType = (type2) => {
        return (node) => {
          return !!node && node.nodeType === type2;
        };
      };
      const isRestrictedNode = (node) => !!node && !Object.getPrototypeOf(node);
      const isElement$7 = isNodeType(1);
      const isHTMLElement = (node) => isElement$7(node) && isHTMLElement$1(SugarElement.fromDom(node));
      const isSVGElement = (node) => isElement$7(node) && node.namespaceURI === "http://www.w3.org/2000/svg";
      const matchNodeName$1 = (name2) => {
        const lowerCasedName = name2.toLowerCase();
        return (node) => isNonNullable(node) && node.nodeName.toLowerCase() === lowerCasedName;
      };
      const matchNodeNames$1 = (names) => {
        const lowerCasedNames = names.map((s) => s.toLowerCase());
        return (node) => {
          if (node && node.nodeName) {
            const nodeName = node.nodeName.toLowerCase();
            return contains$2(lowerCasedNames, nodeName);
          }
          return false;
        };
      };
      const matchStyleValues = (name2, values2) => {
        const items = values2.toLowerCase().split(" ");
        return (node) => {
          if (isElement$7(node)) {
            const win = node.ownerDocument.defaultView;
            if (win) {
              for (let i = 0; i < items.length; i++) {
                const computed = win.getComputedStyle(node, null);
                const cssValue = computed ? computed.getPropertyValue(name2) : null;
                if (cssValue === items[i]) {
                  return true;
                }
              }
            }
          }
          return false;
        };
      };
      const hasAttribute = (attrName) => {
        return (node) => {
          return isElement$7(node) && node.hasAttribute(attrName);
        };
      };
      const isBogus$1 = (node) => isElement$7(node) && node.hasAttribute("data-mce-bogus");
      const isBogusAll = (node) => isElement$7(node) && node.getAttribute("data-mce-bogus") === "all";
      const isTable$2 = (node) => isElement$7(node) && node.tagName === "TABLE";
      const hasContentEditableState = (value2) => {
        return (node) => {
          if (isHTMLElement(node)) {
            if (node.contentEditable === value2) {
              return true;
            }
            if (node.getAttribute("data-mce-contenteditable") === value2) {
              return true;
            }
          }
          return false;
        };
      };
      const isTextareaOrInput = matchNodeNames$1(["textarea", "input"]);
      const isText$b = isNodeType(3);
      const isCData = isNodeType(4);
      const isPi = isNodeType(7);
      const isComment = isNodeType(8);
      const isDocument$1 = isNodeType(9);
      const isDocumentFragment = isNodeType(11);
      const isBr$7 = matchNodeName$1("br");
      const isImg = matchNodeName$1("img");
      const isAnchor = matchNodeName$1("a");
      const isContentEditableTrue$3 = hasContentEditableState("true");
      const isContentEditableFalse$a = hasContentEditableState("false");
      const isEditingHost = (node) => isHTMLElement(node) && node.isContentEditable && isNonNullable(node.parentElement) && !node.parentElement.isContentEditable;
      const isTableCell$3 = matchNodeNames$1(["td", "th"]);
      const isTableCellOrCaption = matchNodeNames$1(["td", "th", "caption"]);
      const isTemplate = matchNodeName$1("template");
      const isMedia$2 = matchNodeNames$1(["video", "audio", "object", "embed"]);
      const isListItem$3 = matchNodeName$1("li");
      const isDetails = matchNodeName$1("details");
      const isSummary$1 = matchNodeName$1("summary");
      const defaultOptionValues = {
        skipBogus: true,
        includeZwsp: false,
        checkRootAsContent: false
      };
      const hasWhitespacePreserveParent = (node, rootNode, schema) => {
        const rootElement = SugarElement.fromDom(rootNode);
        const startNode = SugarElement.fromDom(node);
        const whitespaceElements = schema.getWhitespaceElements();
        const predicate = (node2) => has$2(whitespaceElements, name(node2));
        return ancestor$3(startNode, predicate, curry(eq, rootElement));
      };
      const isNamedAnchor = (node) => {
        return isElement$7(node) && node.nodeName === "A" && !node.hasAttribute("href") && (node.hasAttribute("name") || node.hasAttribute("id"));
      };
      const isNonEmptyElement$1 = (node, schema) => {
        return isElement$7(node) && has$2(schema.getNonEmptyElements(), node.nodeName);
      };
      const isBookmark = hasAttribute("data-mce-bookmark");
      const hasNonEditableParent = (node) => parentElement(SugarElement.fromDom(node)).exists((parent2) => !isEditable$2(parent2));
      const isWhitespace$1 = (node, rootNode, schema) => isWhitespaceText(node.data) && !hasWhitespacePreserveParent(node, rootNode, schema);
      const isText$a = (node, rootNode, schema, options) => isText$b(node) && !isWhitespace$1(node, rootNode, schema) && (!options.includeZwsp || !isZwsp$1(node.data));
      const isContentNode = (schema, node, rootNode, options) => {
        return isFunction(options.isContent) && options.isContent(node) || isNonEmptyElement$1(node, schema) || isBookmark(node) || isNamedAnchor(node) || isText$a(node, rootNode, schema, options) || isContentEditableFalse$a(node) || isContentEditableTrue$3(node) && hasNonEditableParent(node);
      };
      const isEmptyNode = (schema, targetNode, opts) => {
        const options = { ...defaultOptionValues, ...opts };
        if (options.checkRootAsContent) {
          if (isContentNode(schema, targetNode, targetNode, options)) {
            return false;
          }
        }
        let node = targetNode.firstChild;
        let brCount = 0;
        if (!node) {
          return true;
        }
        const walker = new DomTreeWalker(node, targetNode);
        do {
          if (options.skipBogus && isElement$7(node)) {
            const bogusValue = node.getAttribute("data-mce-bogus");
            if (bogusValue) {
              node = walker.next(bogusValue === "all");
              continue;
            }
          }
          if (isComment(node)) {
            node = walker.next(true);
            continue;
          }
          if (isBr$7(node)) {
            brCount++;
            node = walker.next();
            continue;
          }
          if (isContentNode(schema, node, targetNode, options)) {
            return false;
          }
          node = walker.next();
        } while (node);
        return brCount <= 1;
      };
      const isEmpty$4 = (schema, elm, options) => {
        return isEmptyNode(schema, elm.dom, { checkRootAsContent: true, ...options });
      };
      const isContent$1 = (schema, node, options) => {
        return isContentNode(schema, node, node, { includeZwsp: defaultOptionValues.includeZwsp, ...options });
      };
      const nodeNameToNamespaceType = (name2) => {
        const lowerCaseName = name2.toLowerCase();
        if (lowerCaseName === "svg") {
          return "svg";
        } else if (lowerCaseName === "math") {
          return "math";
        } else {
          return "html";
        }
      };
      const isNonHtmlElementRootName = (name2) => nodeNameToNamespaceType(name2) !== "html";
      const isNonHtmlElementRoot = (node) => isNonHtmlElementRootName(node.nodeName);
      const toScopeType = (node) => nodeNameToNamespaceType(node.nodeName);
      const namespaceElements = ["svg", "math"];
      const createNamespaceTracker = () => {
        const currentScope = value$1();
        const current = () => currentScope.get().map(toScopeType).getOr("html");
        const track = (node) => {
          if (isNonHtmlElementRoot(node)) {
            currentScope.set(node);
          } else if (currentScope.get().exists((scopeNode) => !scopeNode.contains(node))) {
            currentScope.clear();
          }
          return current();
        };
        const reset2 = () => {
          currentScope.clear();
        };
        return {
          track,
          current,
          reset: reset2
        };
      };
      const transparentBlockAttr = "data-mce-block";
      const elementNames = (map2) => filter$5(keys(map2), (key) => !/[A-Z]/.test(key));
      const makeSelectorFromSchemaMap = (map2) => map$3(elementNames(map2), (name2) => {
        const escapedName = CSS.escape(name2);
        return `${escapedName}:` + map$3(namespaceElements, (ns) => `not(${ns} ${escapedName})`).join(":");
      }).join(",");
      const updateTransparent = (blocksSelector, transparent) => {
        if (isNonNullable(transparent.querySelector(blocksSelector))) {
          transparent.setAttribute(transparentBlockAttr, "true");
          if (transparent.getAttribute("data-mce-selected") === "inline-boundary") {
            transparent.removeAttribute("data-mce-selected");
          }
          return true;
        } else {
          transparent.removeAttribute(transparentBlockAttr);
          return false;
        }
      };
      const updateBlockStateOnChildren = (schema, scope) => {
        const transparentSelector = makeSelectorFromSchemaMap(schema.getTransparentElements());
        const blocksSelector = makeSelectorFromSchemaMap(schema.getBlockElements());
        return filter$5(scope.querySelectorAll(transparentSelector), (transparent) => updateTransparent(blocksSelector, transparent));
      };
      const trimEdge = (schema, el, leftSide) => {
        const childPropertyName = leftSide ? "lastChild" : "firstChild";
        for (let child2 = el[childPropertyName]; child2; child2 = child2[childPropertyName]) {
          if (isEmptyNode(schema, child2, { checkRootAsContent: true })) {
            child2.parentNode?.removeChild(child2);
            return;
          }
        }
      };
      const split$2 = (schema, parentElm, splitElm) => {
        const range2 = document.createRange();
        const parentNode = parentElm.parentNode;
        if (parentNode) {
          range2.setStartBefore(parentElm);
          range2.setEndBefore(splitElm);
          const beforeFragment = range2.extractContents();
          trimEdge(schema, beforeFragment, true);
          range2.setStartAfter(splitElm);
          range2.setEndAfter(parentElm);
          const afterFragment = range2.extractContents();
          trimEdge(schema, afterFragment, false);
          if (!isEmptyNode(schema, beforeFragment, { checkRootAsContent: true })) {
            parentNode.insertBefore(beforeFragment, parentElm);
          }
          if (!isEmptyNode(schema, splitElm, { checkRootAsContent: true })) {
            parentNode.insertBefore(splitElm, parentElm);
          }
          if (!isEmptyNode(schema, afterFragment, { checkRootAsContent: true })) {
            parentNode.insertBefore(afterFragment, parentElm);
          }
          parentNode.removeChild(parentElm);
        }
      };
      const splitInvalidChildren = (schema, scope, transparentBlocks) => {
        const blocksElements = schema.getBlockElements();
        const rootNode = SugarElement.fromDom(scope);
        const isBlock2 = (el) => name(el) in blocksElements;
        const isRoot2 = (el) => eq(el, rootNode);
        each$e(fromDom$1(transparentBlocks), (transparentBlock) => {
          ancestor$5(transparentBlock, isBlock2, isRoot2).each((parentBlock) => {
            const invalidChildren = children(transparentBlock, (el) => isBlock2(el) && !schema.isValidChild(name(parentBlock), name(el)));
            if (invalidChildren.length > 0) {
              const stateScope = parentElement(parentBlock);
              each$e(invalidChildren, (child2) => {
                ancestor$5(child2, isBlock2, isRoot2).each((parentBlock2) => {
                  split$2(schema, parentBlock2.dom, child2.dom);
                });
              });
              stateScope.each((scope2) => updateBlockStateOnChildren(schema, scope2.dom));
            }
          });
        });
      };
      const unwrapInvalidChildren = (schema, scope, transparentBlocks) => {
        each$e([...transparentBlocks, ...isTransparentBlock(schema, scope) ? [scope] : []], (block) => each$e(descendants(SugarElement.fromDom(block), block.nodeName.toLowerCase()), (elm) => {
          if (isTransparentInline(schema, elm.dom)) {
            unwrap(elm);
          }
        }));
      };
      const updateChildren = (schema, scope) => {
        const transparentBlocks = updateBlockStateOnChildren(schema, scope);
        splitInvalidChildren(schema, scope, transparentBlocks);
        unwrapInvalidChildren(schema, scope, transparentBlocks);
      };
      const updateElement = (schema, target) => {
        if (isTransparentElement(schema, target)) {
          const blocksSelector = makeSelectorFromSchemaMap(schema.getBlockElements());
          updateTransparent(blocksSelector, target);
        }
      };
      const updateCaret = (schema, root, caretParent) => {
        const isRoot2 = (el) => eq(el, SugarElement.fromDom(root));
        const parents2 = parents$1(SugarElement.fromDom(caretParent), isRoot2);
        get$b(parents2, parents2.length - 2).filter(isElement$8).fold(() => updateChildren(schema, root), (scope) => updateChildren(schema, scope.dom));
      };
      const hasBlockAttr = (el) => el.hasAttribute(transparentBlockAttr);
      const isTransparentElementName = (schema, name2) => has$2(schema.getTransparentElements(), name2);
      const isTransparentElement = (schema, node) => isElement$7(node) && isTransparentElementName(schema, node.nodeName);
      const isTransparentBlock = (schema, node) => isTransparentElement(schema, node) && hasBlockAttr(node);
      const isTransparentInline = (schema, node) => isTransparentElement(schema, node) && !hasBlockAttr(node);
      const isTransparentAstBlock = (schema, node) => node.type === 1 && isTransparentElementName(schema, node.name) && isString(node.attr(transparentBlockAttr));
      const browser$2 = detect$1().browser;
      const firstElement = (nodes) => find$2(nodes, isElement$8);
      const getTableCaptionDeltaY = (elm) => {
        if (browser$2.isFirefox() && name(elm) === "table") {
          return firstElement(children$1(elm)).filter((elm2) => {
            return name(elm2) === "caption";
          }).bind((caption) => {
            return firstElement(nextSiblings(caption)).map((body) => {
              const bodyTop = body.dom.offsetTop;
              const captionTop = caption.dom.offsetTop;
              const captionHeight = caption.dom.offsetHeight;
              return bodyTop <= captionTop ? -captionHeight : 0;
            });
          }).getOr(0);
        } else {
          return 0;
        }
      };
      const hasChild = (elm, child2) => elm.children && contains$2(elm.children, child2);
      const getPos = (body, elm, rootElm) => {
        let x = 0, y = 0;
        const doc = body.ownerDocument;
        rootElm = rootElm ? rootElm : body;
        if (elm) {
          if (rootElm === body && elm.getBoundingClientRect && get$7(SugarElement.fromDom(body), "position") === "static") {
            const pos = elm.getBoundingClientRect();
            x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - doc.documentElement.clientLeft;
            y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - doc.documentElement.clientTop;
            return { x, y };
          }
          let offsetParent = elm;
          while (offsetParent && offsetParent !== rootElm && offsetParent.nodeType && !hasChild(offsetParent, rootElm)) {
            const castOffsetParent = offsetParent;
            x += castOffsetParent.offsetLeft || 0;
            y += castOffsetParent.offsetTop || 0;
            offsetParent = castOffsetParent.offsetParent;
          }
          offsetParent = elm.parentNode;
          while (offsetParent && offsetParent !== rootElm && offsetParent.nodeType && !hasChild(offsetParent, rootElm)) {
            x -= offsetParent.scrollLeft || 0;
            y -= offsetParent.scrollTop || 0;
            offsetParent = offsetParent.parentNode;
          }
          y += getTableCaptionDeltaY(SugarElement.fromDom(elm));
        }
        return { x, y };
      };
      const getCrossOrigin$1 = (url, settings) => {
        const crossOriginFn = settings.crossOrigin;
        if (settings.contentCssCors) {
          return "anonymous";
        } else if (isFunction(crossOriginFn)) {
          return crossOriginFn(url);
        } else {
          return void 0;
        }
      };
      const StyleSheetLoader = (documentOrShadowRoot, settings = {}) => {
        let idCount = 0;
        const loadedStates = {};
        const edos = SugarElement.fromDom(documentOrShadowRoot);
        const doc = documentOrOwner(edos);
        const _setReferrerPolicy = (referrerPolicy) => {
          settings.referrerPolicy = referrerPolicy;
        };
        const _setContentCssCors = (contentCssCors) => {
          settings.contentCssCors = contentCssCors;
        };
        const _setCrossOrigin = (crossOrigin) => {
          settings.crossOrigin = crossOrigin;
        };
        const addStyle = (element) => {
          append$1(getStyleContainer(edos), element);
        };
        const removeStyle = (id) => {
          const styleContainer = getStyleContainer(edos);
          descendant$1(styleContainer, "#" + id).each(remove$8);
        };
        const getOrCreateState = (url) => get$a(loadedStates, url).getOrThunk(() => ({
          id: "mce-u" + idCount++,
          passed: [],
          failed: [],
          count: 0
        }));
        const load = (url) => new Promise((success, failure) => {
          let link;
          const urlWithSuffix = Tools._addCacheSuffix(url);
          const state = getOrCreateState(urlWithSuffix);
          loadedStates[urlWithSuffix] = state;
          state.count++;
          const resolve2 = (callbacks, status) => {
            each$e(callbacks, call);
            state.status = status;
            state.passed = [];
            state.failed = [];
            if (link) {
              link.onload = null;
              link.onerror = null;
              link = null;
            }
          };
          const passed = () => resolve2(state.passed, 2);
          const failed = () => resolve2(state.failed, 3);
          if (success) {
            state.passed.push(success);
          }
          if (failure) {
            state.failed.push(failure);
          }
          if (state.status === 1) {
            return;
          }
          if (state.status === 2) {
            passed();
            return;
          }
          if (state.status === 3) {
            failed();
            return;
          }
          state.status = 1;
          const linkElem = SugarElement.fromTag("link", doc.dom);
          setAll$1(linkElem, {
            rel: "stylesheet",
            type: "text/css",
            id: state.id
          });
          const crossorigin = getCrossOrigin$1(url, settings);
          if (crossorigin !== void 0) {
            set$4(linkElem, "crossOrigin", crossorigin);
          }
          if (settings.referrerPolicy) {
            set$4(linkElem, "referrerpolicy", settings.referrerPolicy);
          }
          link = linkElem.dom;
          link.onload = passed;
          link.onerror = failed;
          addStyle(linkElem);
          set$4(linkElem, "href", urlWithSuffix);
        });
        const loadRawCss = (key, css) => {
          const state = getOrCreateState(key);
          loadedStates[key] = state;
          state.count++;
          const styleElem = SugarElement.fromTag("style", doc.dom);
          setAll$1(styleElem, {
            "rel": "stylesheet",
            "type": "text/css",
            "id": state.id,
            "data-mce-key": key
          });
          styleElem.dom.innerHTML = css;
          addStyle(styleElem);
        };
        const loadAll = (urls) => {
          const loadedUrls = Promise.allSettled(map$3(urls, (url) => load(url).then(constant(url))));
          return loadedUrls.then((results) => {
            const parts = partition$2(results, (r2) => r2.status === "fulfilled");
            if (parts.fail.length > 0) {
              return Promise.reject(map$3(parts.fail, (result) => result.reason));
            } else {
              return map$3(parts.pass, (result) => result.value);
            }
          });
        };
        const unload = (url) => {
          const urlWithSuffix = Tools._addCacheSuffix(url);
          get$a(loadedStates, urlWithSuffix).each((state) => {
            const count2 = --state.count;
            if (count2 === 0) {
              delete loadedStates[urlWithSuffix];
              removeStyle(state.id);
            }
          });
        };
        const unloadRawCss = (key) => {
          get$a(loadedStates, key).each((state) => {
            const count2 = --state.count;
            if (count2 === 0) {
              delete loadedStates[key];
              removeStyle(state.id);
            }
          });
        };
        const unloadAll = (urls) => {
          each$e(urls, (url) => {
            unload(url);
          });
        };
        return {
          load,
          loadRawCss,
          loadAll,
          unload,
          unloadRawCss,
          unloadAll,
          _setReferrerPolicy,
          _setContentCssCors,
          _setCrossOrigin
        };
      };
      const create$b = () => {
        const map2 = /* @__PURE__ */ new WeakMap();
        const forElement = (referenceElement, settings) => {
          const root = getRootNode(referenceElement);
          const rootDom = root.dom;
          return Optional.from(map2.get(rootDom)).getOrThunk(() => {
            const sl = StyleSheetLoader(rootDom, settings);
            map2.set(rootDom, sl);
            return sl;
          });
        };
        return {
          forElement
        };
      };
      const instance = create$b();
      const isSpan = (node) => node.nodeName.toLowerCase() === "span";
      const isInlineContent = (node, schema) => isNonNullable(node) && (isContent$1(schema, node) || schema.isInline(node.nodeName.toLowerCase()));
      const surroundedByInlineContent = (node, root, schema) => {
        const prev2 = new DomTreeWalker(node, root).prev(false);
        const next2 = new DomTreeWalker(node, root).next(false);
        const prevIsInline = isUndefined(prev2) || isInlineContent(prev2, schema);
        const nextIsInline = isUndefined(next2) || isInlineContent(next2, schema);
        return prevIsInline && nextIsInline;
      };
      const isBookmarkNode$2 = (node) => isSpan(node) && node.getAttribute("data-mce-type") === "bookmark";
      const isKeepTextNode = (node, root, schema) => isText$b(node) && node.data.length > 0 && surroundedByInlineContent(node, root, schema);
      const isKeepElement = (node) => isElement$7(node) ? node.childNodes.length > 0 : false;
      const isDocument = (node) => isDocumentFragment(node) || isDocument$1(node);
      const trimNode = (dom2, node, schema, root) => {
        const rootNode = root || node;
        if (isElement$7(node) && isBookmarkNode$2(node)) {
          return node;
        }
        const children2 = node.childNodes;
        for (let i = children2.length - 1; i >= 0; i--) {
          trimNode(dom2, children2[i], schema, rootNode);
        }
        if (isElement$7(node)) {
          const currentChildren = node.childNodes;
          if (currentChildren.length === 1 && isBookmarkNode$2(currentChildren[0])) {
            node.parentNode?.insertBefore(currentChildren[0], node);
          }
        }
        if (!isDocument(node) && !isContent$1(schema, node) && !isKeepElement(node) && !isKeepTextNode(node, rootNode, schema)) {
          dom2.remove(node);
        }
        return node;
      };
      const makeMap$3 = Tools.makeMap;
      const attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
      const textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
      const rawCharsRegExp = /[<>&\"\']/g;
      const entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi;
      const asciiMap = {
        128: "€",
        130: "‚",
        131: "ƒ",
        132: "„",
        133: "…",
        134: "†",
        135: "‡",
        136: "ˆ",
        137: "‰",
        138: "Š",
        139: "‹",
        140: "Œ",
        142: "Ž",
        145: "‘",
        146: "’",
        147: "“",
        148: "”",
        149: "•",
        150: "–",
        151: "—",
        152: "˜",
        153: "™",
        154: "š",
        155: "›",
        156: "œ",
        158: "ž",
        159: "Ÿ"
      };
      const baseEntities = {
        '"': "&quot;",
        // Needs to be escaped since the YUI compressor would otherwise break the code
        "'": "&#39;",
        "<": "&lt;",
        ">": "&gt;",
        "&": "&amp;",
        "`": "&#96;"
      };
      const reverseEntities = {
        "&lt;": "<",
        "&gt;": ">",
        "&amp;": "&",
        "&quot;": '"',
        "&apos;": `'`
      };
      const nativeDecode = (text2) => {
        const elm = SugarElement.fromTag("div").dom;
        elm.innerHTML = text2;
        return elm.textContent || elm.innerText || text2;
      };
      const buildEntitiesLookup = (items, radix) => {
        const lookup2 = {};
        if (items) {
          const itemList = items.split(",");
          radix = radix || 10;
          for (let i = 0; i < itemList.length; i += 2) {
            const chr = String.fromCharCode(parseInt(itemList[i], radix));
            if (!baseEntities[chr]) {
              const entity = "&" + itemList[i + 1] + ";";
              lookup2[chr] = entity;
              lookup2[entity] = chr;
            }
          }
          return lookup2;
        } else {
          return void 0;
        }
      };
      const namedEntities = buildEntitiesLookup("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro", 32);
      const encodeRaw = (text2, attr) => text2.replace(attr ? attrsCharsRegExp : textCharsRegExp, (chr) => {
        return baseEntities[chr] || chr;
      });
      const encodeAllRaw = (text2) => ("" + text2).replace(rawCharsRegExp, (chr) => {
        return baseEntities[chr] || chr;
      });
      const encodeNumeric = (text2, attr) => text2.replace(attr ? attrsCharsRegExp : textCharsRegExp, (chr) => {
        if (chr.length > 1) {
          return "&#" + ((chr.charCodeAt(0) - 55296) * 1024 + (chr.charCodeAt(1) - 56320) + 65536) + ";";
        }
        return baseEntities[chr] || "&#" + chr.charCodeAt(0) + ";";
      });
      const encodeNamed = (text2, attr, entities) => {
        const resolveEntities = entities || namedEntities;
        return text2.replace(attr ? attrsCharsRegExp : textCharsRegExp, (chr) => {
          return baseEntities[chr] || resolveEntities[chr] || chr;
        });
      };
      const getEncodeFunc = (name2, entities) => {
        const entitiesMap = buildEntitiesLookup(entities) || namedEntities;
        const encodeNamedAndNumeric = (text2, attr) => text2.replace(attr ? attrsCharsRegExp : textCharsRegExp, (chr) => {
          if (baseEntities[chr] !== void 0) {
            return baseEntities[chr];
          }
          if (entitiesMap[chr] !== void 0) {
            return entitiesMap[chr];
          }
          if (chr.length > 1) {
            return "&#" + ((chr.charCodeAt(0) - 55296) * 1024 + (chr.charCodeAt(1) - 56320) + 65536) + ";";
          }
          return "&#" + chr.charCodeAt(0) + ";";
        });
        const encodeCustomNamed = (text2, attr) => {
          return encodeNamed(text2, attr, entitiesMap);
        };
        const nameMap = makeMap$3(name2.replace(/\+/g, ","));
        if (nameMap.named && nameMap.numeric) {
          return encodeNamedAndNumeric;
        }
        if (nameMap.named) {
          if (entities) {
            return encodeCustomNamed;
          }
          return encodeNamed;
        }
        if (nameMap.numeric) {
          return encodeNumeric;
        }
        return encodeRaw;
      };
      const decode = (text2) => text2.replace(entityRegExp, (all2, numeric) => {
        if (numeric) {
          if (numeric.charAt(0).toLowerCase() === "x") {
            numeric = parseInt(numeric.substr(1), 16);
          } else {
            numeric = parseInt(numeric, 10);
          }
          if (numeric > 65535) {
            numeric -= 65536;
            return String.fromCharCode(55296 + (numeric >> 10), 56320 + (numeric & 1023));
          }
          return asciiMap[numeric] || String.fromCharCode(numeric);
        }
        return reverseEntities[all2] || namedEntities[all2] || nativeDecode(all2);
      });
      const Entities = {
        encodeRaw,
        encodeAllRaw,
        encodeNumeric,
        encodeNamed,
        getEncodeFunc,
        decode
      };
      const split$1 = (items, delim) => {
        items = Tools.trim(items);
        return items ? items.split(delim || " ") : [];
      };
      const patternToRegExp = (str) => new RegExp("^" + str.replace(/([?+*])/g, ".$1") + "$");
      const isRegExp$1 = (obj) => isObject(obj) && obj.source && Object.prototype.toString.call(obj) === "[object RegExp]";
      const deepCloneElementRule = (obj) => {
        const helper = (value2) => {
          if (isArray$1(value2)) {
            return map$3(value2, helper);
          } else if (isRegExp$1(value2)) {
            return new RegExp(value2.source, value2.flags);
          } else if (isObject(value2)) {
            return map$2(value2, helper);
          } else {
            return value2;
          }
        };
        return helper(obj);
      };
      const parseCustomElementsRules = (value2) => {
        const customElementRegExp = /^(~)?(.+)$/;
        return bind$3(split$1(value2, ","), (rule) => {
          const matches = customElementRegExp.exec(rule);
          if (matches) {
            const inline = matches[1] === "~";
            const cloneName = inline ? "span" : "div";
            const name2 = matches[2];
            return [{ cloneName, name: name2 }];
          } else {
            return [];
          }
        });
      };
      const getGlobalAttributeSet = (type2) => {
        return Object.freeze([
          // Present on all schema types
          "id",
          "accesskey",
          "class",
          "dir",
          "lang",
          "style",
          "tabindex",
          "title",
          "role",
          // html5 and html5-strict extra attributes
          ...type2 !== "html4" ? ["contenteditable", "contextmenu", "draggable", "dropzone", "hidden", "spellcheck", "translate", "itemprop", "itemscope", "itemtype"] : [],
          // html4 and html5 extra attributes
          ...type2 !== "html5-strict" ? ["xml:lang"] : []
        ]);
      };
      const getElementSetsAsStrings = (type2) => {
        let blockContent;
        let phrasingContent;
        blockContent = "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul";
        phrasingContent = "a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment";
        if (type2 !== "html4") {
          const transparentContent = "a ins del canvas map";
          blockContent += " article aside details dialog figure main header footer hgroup section nav " + transparentContent;
          phrasingContent += " audio canvas command data datalist mark meter output picture progress template time wbr video ruby bdi keygen svg";
        }
        if (type2 !== "html5-strict") {
          const html4PhrasingContent = "acronym applet basefont big font strike tt";
          phrasingContent = [phrasingContent, html4PhrasingContent].join(" ");
          const html4BlockContent = "center dir isindex noframes";
          blockContent = [blockContent, html4BlockContent].join(" ");
        }
        const flowContent = [blockContent, phrasingContent].join(" ");
        return { blockContent, phrasingContent, flowContent };
      };
      const getElementSets = (type2) => {
        const { blockContent, phrasingContent, flowContent } = getElementSetsAsStrings(type2);
        const toArr = (value2) => {
          return Object.freeze(value2.split(" "));
        };
        return Object.freeze({
          blockContent: toArr(blockContent),
          phrasingContent: toArr(phrasingContent),
          flowContent: toArr(flowContent)
        });
      };
      const cachedSets = {
        "html4": cached(() => getElementSets("html4")),
        "html5": cached(() => getElementSets("html5")),
        "html5-strict": cached(() => getElementSets("html5-strict"))
      };
      const getElementsPreset = (type2, name2) => {
        const { blockContent, phrasingContent, flowContent } = cachedSets[type2]();
        if (name2 === "blocks") {
          return Optional.some(blockContent);
        } else if (name2 === "phrasing") {
          return Optional.some(phrasingContent);
        } else if (name2 === "flow") {
          return Optional.some(flowContent);
        } else {
          return Optional.none();
        }
      };
      const makeSchema = (type2) => {
        const globalAttributes = getGlobalAttributeSet(type2);
        const { phrasingContent, flowContent } = getElementSetsAsStrings(type2);
        const schema = {};
        const addElement = (name2, attributes, children2) => {
          schema[name2] = {
            attributes: mapToObject(attributes, constant({})),
            attributesOrder: attributes,
            children: mapToObject(children2, constant({}))
          };
        };
        const add2 = (name2, attributes = "", children2 = "") => {
          const childNames = split$1(children2);
          const names = split$1(name2);
          let ni = names.length;
          const allAttributes = [...globalAttributes, ...split$1(attributes)];
          while (ni--) {
            addElement(names[ni], allAttributes.slice(), childNames);
          }
        };
        const addAttrs = (name2, attributes) => {
          const names = split$1(name2);
          const attrs = split$1(attributes);
          let ni = names.length;
          while (ni--) {
            const schemaItem = schema[names[ni]];
            for (let i = 0, l = attrs.length; i < l; i++) {
              schemaItem.attributes[attrs[i]] = {};
              schemaItem.attributesOrder.push(attrs[i]);
            }
          }
        };
        if (type2 !== "html5-strict") {
          const html4PhrasingContent = "acronym applet basefont big font strike tt";
          each$e(split$1(html4PhrasingContent), (name2) => {
            add2(name2, "", phrasingContent);
          });
          const html4BlockContent = "center dir isindex noframes";
          each$e(split$1(html4BlockContent), (name2) => {
            add2(name2, "", flowContent);
          });
        }
        add2("html", "manifest", "head body");
        add2("head", "", "base command link meta noscript script style title");
        add2("title hr noscript br");
        add2("base", "href target");
        add2("link", "href rel media hreflang type sizes hreflang");
        add2("meta", "name http-equiv content charset property");
        add2("style", "media type scoped");
        add2("script", "src async defer type charset");
        add2("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload", flowContent);
        add2("dd div", "", flowContent);
        add2("address dt caption", "", type2 === "html4" ? phrasingContent : flowContent);
        add2("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
        add2("blockquote", "cite", flowContent);
        add2("ol", "reversed start type", "li");
        add2("ul", "", "li");
        add2("li", "value", flowContent);
        add2("dl", "", "dt dd");
        add2("a", "href target rel media hreflang type", type2 === "html4" ? phrasingContent : flowContent);
        add2("q", "cite", phrasingContent);
        add2("ins del", "cite datetime", flowContent);
        add2("img", "src sizes srcset alt usemap ismap width height");
        add2("iframe", "src name width height", flowContent);
        add2("embed", "src type width height");
        add2("object", "data type typemustmatch name usemap form width height", [flowContent, "param"].join(" "));
        add2("param", "name value");
        add2("map", "name", [flowContent, "area"].join(" "));
        add2("area", "alt coords shape href target rel media hreflang type");
        add2("table", "border", "caption colgroup thead tfoot tbody tr" + (type2 === "html4" ? " col" : ""));
        add2("colgroup", "span", "col");
        add2("col", "span");
        add2("tbody thead tfoot", "", "tr");
        add2("tr", "", "td th");
        add2("td", "colspan rowspan headers", flowContent);
        add2("th", "colspan rowspan headers scope abbr", flowContent);
        add2("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
        add2("fieldset", "disabled form name", [flowContent, "legend"].join(" "));
        add2("label", "form for", phrasingContent);
        add2("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width");
        add2("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value", type2 === "html4" ? flowContent : phrasingContent);
        add2("select", "disabled form multiple name required size", "option optgroup");
        add2("optgroup", "disabled label", "option");
        add2("option", "disabled label selected value");
        add2("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
        add2("menu", "type label", [flowContent, "li"].join(" "));
        add2("noscript", "", flowContent);
        if (type2 !== "html4") {
          add2("wbr");
          add2("ruby", "", [phrasingContent, "rt rp"].join(" "));
          add2("figcaption", "", flowContent);
          add2("mark rt rp bdi", "", phrasingContent);
          add2("summary", "", [phrasingContent, "h1 h2 h3 h4 h5 h6"].join(" "));
          add2("canvas", "width height", flowContent);
          add2("data", "value", phrasingContent);
          add2("video", "src crossorigin poster preload autoplay mediagroup loop controlslist disablepictureinpicture disableremoteplayback playsinline muted controls width height buffered", [flowContent, "track source"].join(" "));
          add2("audio", "src crossorigin preload autoplay mediagroup loop muted controls buffered volume", [flowContent, "track source"].join(" "));
          add2("picture", "", "img source");
          add2("source", "src srcset type media sizes");
          add2("track", "kind src srclang label default");
          add2("datalist", "", [phrasingContent, "option"].join(" "));
          add2("article section nav aside main header footer", "", flowContent);
          add2("hgroup", "", "h1 h2 h3 h4 h5 h6");
          add2("figure", "", [flowContent, "figcaption"].join(" "));
          add2("time", "datetime", phrasingContent);
          add2("dialog", "open", flowContent);
          add2("command", "type label icon disabled checked radiogroup command");
          add2("output", "for form name", phrasingContent);
          add2("progress", "value max", phrasingContent);
          add2("meter", "value min max low high optimum", phrasingContent);
          add2("details", "open", [flowContent, "summary"].join(" "));
          add2("keygen", "autofocus challenge disabled form keytype name");
          addElement("svg", "id tabindex lang xml:space class style x y width height viewBox preserveAspectRatio zoomAndPan transform".split(" "), []);
        }
        if (type2 !== "html5-strict") {
          addAttrs("script", "language xml:space");
          addAttrs("style", "xml:space");
          addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace");
          addAttrs("embed", "align name hspace vspace");
          addAttrs("param", "valuetype type");
          addAttrs("a", "charset name rev shape coords");
          addAttrs("br", "clear");
          addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
          addAttrs("img", "name longdesc align border hspace vspace");
          addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
          addAttrs("font basefont", "size color face");
          addAttrs("input", "usemap align");
          addAttrs("select");
          addAttrs("textarea");
          addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
          addAttrs("ul", "type compact");
          addAttrs("li", "type");
          addAttrs("ol dl menu dir", "compact");
          addAttrs("pre", "width xml:space");
          addAttrs("hr", "align noshade size width");
          addAttrs("isindex", "prompt");
          addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
          addAttrs("col", "width align char charoff valign");
          addAttrs("colgroup", "width align char charoff valign");
          addAttrs("thead", "align char charoff valign");
          addAttrs("tr", "align char charoff valign bgcolor");
          addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
          addAttrs("form", "accept");
          addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
          addAttrs("tfoot", "align char charoff valign");
          addAttrs("tbody", "align char charoff valign");
          addAttrs("area", "nohref");
          addAttrs("body", "background bgcolor text link vlink alink");
        }
        if (type2 !== "html4") {
          addAttrs("input button select textarea", "autofocus");
          addAttrs("input textarea", "placeholder");
          addAttrs("a", "download");
          addAttrs("link script img", "crossorigin");
          addAttrs("img", "loading");
          addAttrs("iframe", "sandbox seamless allow allowfullscreen loading referrerpolicy");
        }
        if (type2 !== "html4") {
          each$e([schema.video, schema.audio], (item) => {
            delete item.children.audio;
            delete item.children.video;
          });
        }
        each$e(split$1("a form meter progress dfn"), (name2) => {
          if (schema[name2]) {
            delete schema[name2].children[name2];
          }
        });
        delete schema.caption.children.table;
        delete schema.script;
        return schema;
      };
      const prefixToOperation = (prefix) => prefix === "-" ? "remove" : "add";
      const parseValidChild = (name2) => {
        const validChildRegExp = /^(@?)([A-Za-z0-9_\-.\u00b7\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u037d\u037f-\u1fff\u200c-\u200d\u203f-\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]+)$/;
        return Optional.from(validChildRegExp.exec(name2)).map((matches) => ({
          preset: matches[1] === "@",
          name: matches[2]
        }));
      };
      const parseValidChildrenRules = (value2) => {
        const childRuleRegExp = /^([+\-]?)([A-Za-z0-9_\-.\u00b7\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u037d\u037f-\u1fff\u200c-\u200d\u203f-\u2040\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd]+)\[([^\]]+)]$/;
        return bind$3(split$1(value2, ","), (rule) => {
          const matches = childRuleRegExp.exec(rule);
          if (matches) {
            const prefix = matches[1];
            const operation = prefix ? prefixToOperation(prefix) : "replace";
            const name2 = matches[2];
            const validChildren = bind$3(split$1(matches[3], "|"), (validChild) => parseValidChild(validChild).toArray());
            return [{ operation, name: name2, validChildren }];
          } else {
            return [];
          }
        });
      };
      const parseValidElementsAttrDataIntoElement = (attrData, targetElement) => {
        const attrRuleRegExp = /^([!\-])?(\w+[\\:]:\w+|[^=~<]+)?(?:([=~<])(.*))?$/;
        const hasPatternsRegExp = /[*?+]/;
        const { attributes, attributesOrder } = targetElement;
        return each$e(split$1(attrData, "|"), (rule) => {
          const matches = attrRuleRegExp.exec(rule);
          if (matches) {
            const attr = {};
            const attrType = matches[1];
            const attrName = matches[2].replace(/[\\:]:/g, ":");
            const attrPrefix = matches[3];
            const value2 = matches[4];
            if (attrType === "!") {
              targetElement.attributesRequired = targetElement.attributesRequired || [];
              targetElement.attributesRequired.push(attrName);
              attr.required = true;
            }
            if (attrType === "-") {
              delete attributes[attrName];
              attributesOrder.splice(Tools.inArray(attributesOrder, attrName), 1);
              return;
            }
            if (attrPrefix) {
              if (attrPrefix === "=") {
                targetElement.attributesDefault = targetElement.attributesDefault || [];
                targetElement.attributesDefault.push({ name: attrName, value: value2 });
                attr.defaultValue = value2;
              } else if (attrPrefix === "~") {
                targetElement.attributesForced = targetElement.attributesForced || [];
                targetElement.attributesForced.push({ name: attrName, value: value2 });
                attr.forcedValue = value2;
              } else if (attrPrefix === "<") {
                attr.validValues = Tools.makeMap(value2, "?");
              }
            }
            if (hasPatternsRegExp.test(attrName)) {
              const attrPattern = attr;
              targetElement.attributePatterns = targetElement.attributePatterns || [];
              attrPattern.pattern = patternToRegExp(attrName);
              targetElement.attributePatterns.push(attrPattern);
            } else {
              if (!attributes[attrName]) {
                attributesOrder.push(attrName);
              }
              attributes[attrName] = attr;
            }
          }
        });
      };
      const cloneAttributesInto = (from2, to2) => {
        each$d(from2.attributes, (value2, key) => {
          to2.attributes[key] = value2;
        });
        to2.attributesOrder.push(...from2.attributesOrder);
      };
      const parseValidElementsRules = (globalElement, validElements) => {
        const elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)])?$/;
        return bind$3(split$1(validElements, ","), (rule) => {
          const matches = elementRuleRegExp.exec(rule);
          if (matches) {
            const prefix = matches[1];
            const elementName = matches[2];
            const outputName = matches[3];
            const attrsPrefix = matches[4];
            const attrData = matches[5];
            const element = {
              attributes: {},
              attributesOrder: []
            };
            globalElement.each((el) => cloneAttributesInto(el, element));
            if (prefix === "#") {
              element.paddEmpty = true;
            } else if (prefix === "-") {
              element.removeEmpty = true;
            }
            if (attrsPrefix === "!") {
              element.removeEmptyAttrs = true;
            }
            if (attrData) {
              parseValidElementsAttrDataIntoElement(attrData, element);
            }
            if (outputName) {
              element.outputName = elementName;
            }
            if (elementName === "@") {
              if (globalElement.isNone()) {
                globalElement = Optional.some(element);
              } else {
                return [];
              }
            }
            return [outputName ? { name: elementName, element, aliasName: outputName } : { name: elementName, element }];
          } else {
            return [];
          }
        });
      };
      const mapCache = {};
      const makeMap$2 = Tools.makeMap, each$b = Tools.each, extend$2 = Tools.extend, explode$2 = Tools.explode;
      const createMap = (defaultValue, extendWith = {}) => {
        const value2 = makeMap$2(defaultValue, " ", makeMap$2(defaultValue.toUpperCase(), " "));
        return extend$2(value2, extendWith);
      };
      const getTextRootBlockElements = (schema) => createMap("td th li dt dd figcaption caption details summary", schema.getTextBlockElements());
      const compileElementMap = (value2, mode) => {
        if (value2) {
          const styles = {};
          if (isString(value2)) {
            value2 = {
              "*": value2
            };
          }
          each$b(value2, (value3, key) => {
            styles[key] = styles[key.toUpperCase()] = mode === "map" ? makeMap$2(value3, /[, ]/) : explode$2(value3, /[, ]/);
          });
          return styles;
        } else {
          return void 0;
        }
      };
      const Schema = (settings = {}) => {
        const elements = {};
        const children2 = {};
        let patternElements = [];
        const customElementsMap = {};
        const specialElements = {};
        const componentUrls = {};
        const createLookupTable = (option2, defaultValue, extendWith) => {
          const value2 = settings[option2];
          if (!value2) {
            let newValue = mapCache[option2];
            if (!newValue) {
              newValue = createMap(defaultValue, extendWith);
              mapCache[option2] = newValue;
            }
            return newValue;
          } else {
            return makeMap$2(value2, /[, ]/, makeMap$2(value2.toUpperCase(), /[, ]/));
          }
        };
        const schemaType = settings.schema ?? "html5";
        const schemaItems = makeSchema(schemaType);
        if (settings.verify_html === false) {
          settings.valid_elements = "*[*]";
        }
        const validStyles = compileElementMap(settings.valid_styles);
        const invalidStyles = compileElementMap(settings.invalid_styles, "map");
        const validClasses = compileElementMap(settings.valid_classes, "map");
        const whitespaceElementsMap = createLookupTable("whitespace_elements", "pre script noscript style textarea video audio iframe object code");
        const selfClosingElementsMap = createLookupTable("self_closing_elements", "colgroup dd dt li option p td tfoot th thead tr");
        const voidElementsMap = createLookupTable("void_elements", "area base basefont br col frame hr img input isindex link meta param embed source wbr track");
        const boolAttrMap = createLookupTable("boolean_attributes", "checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls allowfullscreen");
        const nonEmptyOrMoveCaretBeforeOnEnter = "td th iframe video audio object script code";
        const nonEmptyElementsMap = createLookupTable("non_empty_elements", nonEmptyOrMoveCaretBeforeOnEnter + " pre svg textarea summary", voidElementsMap);
        const moveCaretBeforeOnEnterElementsMap = createLookupTable("move_caret_before_on_enter_elements", nonEmptyOrMoveCaretBeforeOnEnter + " table", voidElementsMap);
        const headings = "h1 h2 h3 h4 h5 h6";
        const textBlockElementsMap = createLookupTable("text_block_elements", headings + " p div address pre form blockquote center dir fieldset header footer article section hgroup aside main nav figure");
        const blockElementsMap = createLookupTable("block_elements", "hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup figcaption details summary html body multicol listing colgroup col", textBlockElementsMap);
        const textInlineElementsMap = createLookupTable("text_inline_elements", "span strong b em i font s strike u var cite dfn code mark q sup sub samp");
        const transparentElementsMap = createLookupTable("transparent_elements", "a ins del canvas map");
        const wrapBlockElementsMap = createLookupTable("wrap_block_elements", "pre " + headings);
        each$b("script noscript iframe noframes noembed title style textarea xmp plaintext".split(" "), (name2) => {
          specialElements[name2] = new RegExp("</" + name2 + "[^>]*>", "gi");
        });
        const addValidElements = (validElements) => {
          const globalElement = Optional.from(elements["@"]);
          const hasPatternsRegExp = /[*?+]/;
          each$e(parseValidElementsRules(globalElement, validElements ?? ""), ({ name: name2, element, aliasName }) => {
            if (aliasName) {
              elements[aliasName] = element;
            }
            if (hasPatternsRegExp.test(name2)) {
              const patternElement = element;
              patternElement.pattern = patternToRegExp(name2);
              patternElements.push(patternElement);
            } else {
              elements[name2] = element;
            }
          });
        };
        const setValidElements = (validElements) => {
          patternElements = [];
          each$e(keys(elements), (name2) => {
            delete elements[name2];
          });
          addValidElements(validElements);
        };
        const addCustomElement = (name2, spec) => {
          delete mapCache.text_block_elements;
          delete mapCache.block_elements;
          const inline = spec.extends ? !isBlock2(spec.extends) : false;
          const cloneName = spec.extends;
          children2[name2] = cloneName ? children2[cloneName] : {};
          customElementsMap[name2] = cloneName ?? name2;
          nonEmptyElementsMap[name2.toUpperCase()] = {};
          nonEmptyElementsMap[name2] = {};
          if (!inline) {
            blockElementsMap[name2.toUpperCase()] = {};
            blockElementsMap[name2] = {};
          }
          if (cloneName && !elements[name2] && elements[cloneName]) {
            const customRule = deepCloneElementRule(elements[cloneName]);
            delete customRule.removeEmptyAttrs;
            delete customRule.removeEmpty;
            elements[name2] = customRule;
          } else {
            elements[name2] = { attributesOrder: [], attributes: {} };
          }
          if (isArray$1(spec.attributes)) {
            const processAttrName = (name3) => {
              customRule.attributesOrder.push(name3);
              customRule.attributes[name3] = {};
            };
            const customRule = elements[name2] ?? {};
            delete customRule.attributesDefault;
            delete customRule.attributesForced;
            delete customRule.attributePatterns;
            delete customRule.attributesRequired;
            customRule.attributesOrder = [];
            customRule.attributes = {};
            each$e(spec.attributes, (attrName) => {
              const globalAttrs = getGlobalAttributeSet(schemaType);
              parseValidChild(attrName).each(({ preset, name: name3 }) => {
                if (preset) {
                  if (name3 === "global") {
                    each$e(globalAttrs, processAttrName);
                  }
                } else {
                  processAttrName(name3);
                }
              });
            });
            elements[name2] = customRule;
          }
          if (isBoolean(spec.padEmpty)) {
            const customRule = elements[name2] ?? {};
            customRule.paddEmpty = spec.padEmpty;
            elements[name2] = customRule;
          }
          if (isArray$1(spec.children)) {
            const customElementChildren = {};
            const processNodeName = (name3) => {
              customElementChildren[name3] = {};
            };
            const processPreset = (name3) => {
              getElementsPreset(schemaType, name3).each((names) => {
                each$e(names, processNodeName);
              });
            };
            each$e(spec.children, (child2) => {
              parseValidChild(child2).each(({ preset, name: name3 }) => {
                if (preset) {
                  processPreset(name3);
                } else {
                  processNodeName(name3);
                }
              });
            });
            children2[name2] = customElementChildren;
          }
          if (cloneName) {
            each$d(children2, (element, elmName) => {
              if (element[cloneName]) {
                children2[elmName] = element = extend$2({}, children2[elmName]);
                element[name2] = element[cloneName];
              }
            });
          }
        };
        const addCustomElementsFromString = (customElements) => {
          each$e(parseCustomElementsRules(customElements ?? ""), ({ name: name2, cloneName }) => {
            addCustomElement(name2, { extends: cloneName });
          });
        };
        const addComponentUrl = (elementName, componentUrl) => {
          componentUrls[elementName] = componentUrl;
        };
        const addCustomElements = (customElements) => {
          if (isObject(customElements)) {
            each$d(customElements, (spec, name2) => {
              const componentUrl = spec.componentUrl;
              if (isString(componentUrl)) {
                addComponentUrl(name2, componentUrl);
              }
              addCustomElement(name2, spec);
            });
          } else if (isString(customElements)) {
            addCustomElementsFromString(customElements);
          }
        };
        const addValidChildren = (validChildren) => {
          each$e(parseValidChildrenRules(validChildren ?? ""), ({ operation, name: name2, validChildren: validChildren2 }) => {
            const parent2 = operation === "replace" ? { "#comment": {} } : children2[name2];
            const processNodeName = (name3) => {
              if (operation === "remove") {
                delete parent2[name3];
              } else {
                parent2[name3] = {};
              }
            };
            const processPreset = (name3) => {
              getElementsPreset(schemaType, name3).each((names) => {
                each$e(names, processNodeName);
              });
            };
            each$e(validChildren2, ({ preset, name: name3 }) => {
              if (preset) {
                processPreset(name3);
              } else {
                processNodeName(name3);
              }
            });
            children2[name2] = parent2;
          });
        };
        const getElementRule = (name2) => {
          const element = elements[name2];
          if (element) {
            return element;
          }
          let i = patternElements.length;
          while (i--) {
            const patternElement = patternElements[i];
            if (patternElement.pattern.test(name2)) {
              return patternElement;
            }
          }
          return void 0;
        };
        const setup2 = () => {
          if (!settings.valid_elements) {
            each$b(schemaItems, (element, name2) => {
              elements[name2] = {
                attributes: element.attributes,
                attributesOrder: element.attributesOrder
              };
              children2[name2] = element.children;
            });
            each$b(split$1("strong/b em/i"), (item) => {
              const items = split$1(item, "/");
              elements[items[1]].outputName = items[0];
            });
            each$b(textInlineElementsMap, (_val, name2) => {
              if (elements[name2]) {
                if (settings.padd_empty_block_inline_children) {
                  elements[name2].paddInEmptyBlock = true;
                }
                elements[name2].removeEmpty = true;
              }
            });
            each$b(split$1("ol ul blockquote a table tbody"), (name2) => {
              if (elements[name2]) {
                elements[name2].removeEmpty = true;
              }
            });
            each$b(split$1("p h1 h2 h3 h4 h5 h6 th td pre div address caption li summary"), (name2) => {
              if (elements[name2]) {
                elements[name2].paddEmpty = true;
              }
            });
            each$b(split$1("span"), (name2) => {
              elements[name2].removeEmptyAttrs = true;
            });
          } else {
            setValidElements(settings.valid_elements);
            each$b(schemaItems, (element, name2) => {
              children2[name2] = element.children;
            });
          }
          delete elements.svg;
          addCustomElements(settings.custom_elements);
          addValidChildren(settings.valid_children);
          addValidElements(settings.extended_valid_elements);
          addValidChildren("+ol[ul|ol],+ul[ul|ol]");
          each$b({
            dd: "dl",
            dt: "dl",
            li: "ul ol",
            td: "tr",
            th: "tr",
            tr: "tbody thead tfoot",
            tbody: "table",
            thead: "table",
            tfoot: "table",
            legend: "fieldset",
            area: "map",
            param: "video audio object"
          }, (parents2, item) => {
            if (elements[item]) {
              elements[item].parentsRequired = split$1(parents2);
            }
          });
          if (settings.invalid_elements) {
            each$b(explode$2(settings.invalid_elements), (item) => {
              if (elements[item]) {
                delete elements[item];
              }
            });
          }
          if (!getElementRule("span")) {
            addValidElements("span[!data-mce-type|*]");
          }
        };
        const getValidStyles = constant(validStyles);
        const getInvalidStyles = constant(invalidStyles);
        const getValidClasses = constant(validClasses);
        const getBoolAttrs = constant(boolAttrMap);
        const getBlockElements = constant(blockElementsMap);
        const getTextBlockElements = constant(textBlockElementsMap);
        const getTextInlineElements = constant(textInlineElementsMap);
        const getVoidElements = constant(Object.seal(voidElementsMap));
        const getSelfClosingElements = constant(selfClosingElementsMap);
        const getNonEmptyElements = constant(nonEmptyElementsMap);
        const getMoveCaretBeforeOnEnterElements = constant(moveCaretBeforeOnEnterElementsMap);
        const getWhitespaceElements = constant(whitespaceElementsMap);
        const getTransparentElements = constant(transparentElementsMap);
        const getWrapBlockElements = constant(wrapBlockElementsMap);
        const getSpecialElements = constant(Object.seal(specialElements));
        const isValidChild = (name2, child2) => {
          const parent2 = children2[name2.toLowerCase()];
          return !!(parent2 && parent2[child2.toLowerCase()]);
        };
        const isValid2 = (name2, attr) => {
          const rule = getElementRule(name2);
          if (rule) {
            if (attr) {
              if (rule.attributes[attr]) {
                return true;
              }
              const attrPatterns = rule.attributePatterns;
              if (attrPatterns) {
                let i = attrPatterns.length;
                while (i--) {
                  if (attrPatterns[i].pattern.test(attr)) {
                    return true;
                  }
                }
              }
            } else {
              return true;
            }
          }
          return false;
        };
        const isBlock2 = (name2) => has$2(getBlockElements(), name2);
        const isInline2 = (name2) => !startsWith(name2, "#") && isValid2(name2) && !isBlock2(name2);
        const isWrapper = (name2) => has$2(getWrapBlockElements(), name2) || isInline2(name2);
        const getCustomElements = constant(customElementsMap);
        const getComponentUrls = constant(componentUrls);
        setup2();
        return {
          type: schemaType,
          children: children2,
          elements,
          getValidStyles,
          getValidClasses,
          getBlockElements,
          getInvalidStyles,
          getVoidElements,
          getTextBlockElements,
          getTextInlineElements,
          getBoolAttrs,
          getElementRule,
          getSelfClosingElements,
          getNonEmptyElements,
          getMoveCaretBeforeOnEnterElements,
          getWhitespaceElements,
          getTransparentElements,
          getSpecialElements,
          getComponentUrls,
          isValidChild,
          isValid: isValid2,
          isBlock: isBlock2,
          isInline: isInline2,
          isWrapper,
          getCustomElements,
          addValidElements,
          setValidElements,
          addCustomElements,
          addValidChildren
        };
      };
      const hexColour = (value2) => ({
        value: normalizeHex(value2)
      });
      const normalizeHex = (hex) => removeLeading(hex, "#").toUpperCase();
      const toHex = (component) => {
        const hex = component.toString(16);
        return (hex.length === 1 ? "0" + hex : hex).toUpperCase();
      };
      const fromRgba = (rgbaColour2) => {
        const value2 = toHex(rgbaColour2.red) + toHex(rgbaColour2.green) + toHex(rgbaColour2.blue);
        return hexColour(value2);
      };
      const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*\)\s*$/i;
      const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*((?:\d?\.\d+|\d+)%?)\s*\)\s*$/i;
      const rgbaColour = (red, green, blue, alpha) => ({
        red,
        green,
        blue,
        alpha
      });
      const fromStringValues = (red, green, blue, alpha) => {
        const r2 = parseInt(red, 10);
        const g = parseInt(green, 10);
        const b = parseInt(blue, 10);
        const a = parseFloat(alpha);
        return rgbaColour(r2, g, b, a);
      };
      const getColorFormat = (colorString) => {
        if (rgbRegex.test(colorString)) {
          return "rgb";
        } else if (rgbaRegex.test(colorString)) {
          return "rgba";
        }
        return "other";
      };
      const fromString = (rgbaString) => {
        const rgbMatch = rgbRegex.exec(rgbaString);
        if (rgbMatch !== null) {
          return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], "1"));
        }
        const rgbaMatch = rgbaRegex.exec(rgbaString);
        if (rgbaMatch !== null) {
          return Optional.some(fromStringValues(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], rgbaMatch[4]));
        }
        return Optional.none();
      };
      const toString = (rgba) => `rgba(${rgba.red},${rgba.green},${rgba.blue},${rgba.alpha})`;
      const rgbaToHexString = (color) => fromString(color).map(fromRgba).map((h) => "#" + h.value).getOr(color);
      const Styles = (settings = {}, schema) => {
        const urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi;
        const styleRegExp = /\s*([^:]+):\s*([^;]+);?/g;
        const trimRightRegExp = /\s+$/;
        const encodingLookup = {};
        let validStyles;
        let invalidStyles;
        const invisibleChar = zeroWidth;
        if (schema) {
          validStyles = schema.getValidStyles();
          invalidStyles = schema.getInvalidStyles();
        }
        const encodingItems = (`\\" \\' \\; \\: ; : ` + invisibleChar).split(" ");
        for (let i = 0; i < encodingItems.length; i++) {
          encodingLookup[encodingItems[i]] = invisibleChar + i;
          encodingLookup[invisibleChar + i] = encodingItems[i];
        }
        const self = {
          /**
           * Parses the specified style value into an object collection. This parser will also
           * merge and remove any redundant items that browsers might have added. URLs inside
           * the styles will also be converted to absolute/relative based on the settings.
           *
           * @method parse
           * @param {String} css Style value to parse. For example: `border:1px solid red;`
           * @return {Object} Object representation of that style. For example: `{ border: '1px solid red' }`
           */
          parse: (css) => {
            const styles = {};
            let isEncoded = false;
            const urlConverter = settings.url_converter;
            const urlConverterScope = settings.url_converter_scope || self;
            const compress = (prefix, suffix, noJoin) => {
              const top = styles[prefix + "-top" + suffix];
              if (!top) {
                return;
              }
              const right = styles[prefix + "-right" + suffix];
              if (!right) {
                return;
              }
              const bottom = styles[prefix + "-bottom" + suffix];
              if (!bottom) {
                return;
              }
              const left = styles[prefix + "-left" + suffix];
              if (!left) {
                return;
              }
              const box = [top, right, bottom, left];
              let i = box.length - 1;
              while (i--) {
                if (box[i] !== box[i + 1]) {
                  break;
                }
              }
              if (i > -1 && noJoin) {
                return;
              }
              styles[prefix + suffix] = i === -1 ? box[0] : box.join(" ");
              delete styles[prefix + "-top" + suffix];
              delete styles[prefix + "-right" + suffix];
              delete styles[prefix + "-bottom" + suffix];
              delete styles[prefix + "-left" + suffix];
            };
            const canCompress = (key) => {
              const value2 = styles[key];
              if (!value2) {
                return;
              }
              const values2 = value2.indexOf(",") > -1 ? [value2] : value2.split(" ");
              let i = values2.length;
              while (i--) {
                if (values2[i] !== values2[0]) {
                  return false;
                }
              }
              styles[key] = values2[0];
              return true;
            };
            const compress2 = (target, a, b, c) => {
              if (!canCompress(a)) {
                return;
              }
              if (!canCompress(b)) {
                return;
              }
              if (!canCompress(c)) {
                return;
              }
              styles[target] = styles[a] + " " + styles[b] + " " + styles[c];
              delete styles[a];
              delete styles[b];
              delete styles[c];
            };
            const encode = (str) => {
              isEncoded = true;
              return encodingLookup[str];
            };
            const decode2 = (str, keepSlashes) => {
              if (isEncoded) {
                str = str.replace(/\uFEFF[0-9]/g, (str2) => {
                  return encodingLookup[str2];
                });
              }
              if (!keepSlashes) {
                str = str.replace(/\\([\'\";:])/g, "$1");
              }
              return str;
            };
            const decodeSingleHexSequence = (escSeq) => {
              return String.fromCharCode(parseInt(escSeq.slice(1), 16));
            };
            const decodeHexSequences = (value2) => {
              return value2.replace(/\\[0-9a-f]+/gi, decodeSingleHexSequence);
            };
            const processUrl = (match2, url, url2, url3, str, str2) => {
              str = str || str2;
              if (str) {
                str = decode2(str);
                return `'` + str.replace(/\'/g, `\\'`) + `'`;
              }
              url = decode2(url || url2 || url3 || "");
              if (!settings.allow_script_urls) {
                const scriptUrl = url.replace(/[\s\r\n]+/g, "");
                if (/(java|vb)script:/i.test(scriptUrl)) {
                  return "";
                }
                if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) {
                  return "";
                }
              }
              if (urlConverter) {
                url = urlConverter.call(urlConverterScope, url, "style");
              }
              return `url('` + url.replace(/\'/g, `\\'`) + `')`;
            };
            if (css) {
              css = css.replace(/[\u0000-\u001F]/g, "");
              css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, (str) => {
                return str.replace(/[;:]/g, encode);
              });
              let matches;
              while (matches = styleRegExp.exec(css)) {
                styleRegExp.lastIndex = matches.index + matches[0].length;
                let name2 = matches[1].replace(trimRightRegExp, "").toLowerCase();
                let value2 = matches[2].replace(trimRightRegExp, "");
                if (name2 && value2) {
                  name2 = decodeHexSequences(name2);
                  value2 = decodeHexSequences(value2);
                  if (name2.indexOf(invisibleChar) !== -1 || name2.indexOf('"') !== -1) {
                    continue;
                  }
                  if (!settings.allow_script_urls && (name2 === "behavior" || /expression\s*\(|\/\*|\*\//.test(value2))) {
                    continue;
                  }
                  if (name2 === "font-weight" && value2 === "700") {
                    value2 = "bold";
                  } else if (name2 === "color" || name2 === "background-color") {
                    value2 = value2.toLowerCase();
                  }
                  if (getColorFormat(value2) === "rgb") {
                    fromString(value2).each((rgba) => {
                      value2 = rgbaToHexString(toString(rgba)).toLowerCase();
                    });
                  }
                  value2 = value2.replace(urlOrStrRegExp, processUrl);
                  styles[name2] = isEncoded ? decode2(value2, true) : value2;
                }
              }
              compress("border", "", true);
              compress("border", "-width");
              compress("border", "-color");
              compress("border", "-style");
              compress("padding", "");
              compress("margin", "");
              compress2("border", "border-width", "border-style", "border-color");
              if (styles.border === "medium none") {
                delete styles.border;
              }
              if (styles["border-image"] === "none") {
                delete styles["border-image"];
              }
            }
            return styles;
          },
          /**
           * Serializes the specified style object into a string.
           *
           * @method serialize
           * @param {Object} styles Object to serialize as string. For example: `{ border: '1px solid red' }`
           * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized.
           * @return {String} String representation of the style object. For example: `border: 1px solid red`
           */
          serialize: (styles, elementName) => {
            let css = "";
            const serializeStyles = (elemName, validStyleList) => {
              const styleList = validStyleList[elemName];
              if (styleList) {
                for (let i = 0, l = styleList.length; i < l; i++) {
                  const name2 = styleList[i];
                  const value2 = styles[name2];
                  if (value2) {
                    css += (css.length > 0 ? " " : "") + name2 + ": " + value2 + ";";
                  }
                }
              }
            };
            const isValid2 = (name2, elemName) => {
              if (!invalidStyles || !elemName) {
                return true;
              }
              let styleMap = invalidStyles["*"];
              if (styleMap && styleMap[name2]) {
                return false;
              }
              styleMap = invalidStyles[elemName];
              return !(styleMap && styleMap[name2]);
            };
            if (elementName && validStyles) {
              serializeStyles("*", validStyles);
              serializeStyles(elementName, validStyles);
            } else {
              each$d(styles, (value2, name2) => {
                if (value2 && isValid2(name2, elementName)) {
                  css += (css.length > 0 ? " " : "") + name2 + ": " + value2 + ";";
                }
              });
            }
            return css;
          }
        };
        return self;
      };
      const deprecated = {
        keyLocation: true,
        layerX: true,
        layerY: true,
        returnValue: true,
        webkitMovementX: true,
        webkitMovementY: true,
        keyIdentifier: true,
        mozPressure: true
      };
      const isNativeEvent = (event) => event instanceof Event || isFunction(event.initEvent);
      const hasIsDefaultPrevented = (event) => event.isDefaultPrevented === always || event.isDefaultPrevented === never;
      const needsNormalizing = (event) => isNullable(event.preventDefault) || isNativeEvent(event);
      const clone$2 = (originalEvent, data2) => {
        const event = data2 ?? {};
        for (const name2 in originalEvent) {
          if (!has$2(deprecated, name2)) {
            event[name2] = originalEvent[name2];
          }
        }
        if (isNonNullable(originalEvent.composedPath)) {
          event.composedPath = () => originalEvent.composedPath();
        }
        if (isNonNullable(originalEvent.getModifierState)) {
          event.getModifierState = (keyArg) => originalEvent.getModifierState(keyArg);
        }
        if (isNonNullable(originalEvent.getTargetRanges)) {
          event.getTargetRanges = () => originalEvent.getTargetRanges();
        }
        return event;
      };
      const normalize$3 = (type2, originalEvent, fallbackTarget, data2) => {
        const event = clone$2(originalEvent, data2);
        event.type = type2;
        if (isNullable(event.target)) {
          event.target = event.srcElement ?? fallbackTarget;
        }
        if (needsNormalizing(originalEvent)) {
          event.preventDefault = () => {
            event.defaultPrevented = true;
            event.isDefaultPrevented = always;
            if (isFunction(originalEvent.preventDefault)) {
              originalEvent.preventDefault();
            }
          };
          event.stopPropagation = () => {
            event.cancelBubble = true;
            event.isPropagationStopped = always;
            if (isFunction(originalEvent.stopPropagation)) {
              originalEvent.stopPropagation();
            }
          };
          event.stopImmediatePropagation = () => {
            event.isImmediatePropagationStopped = always;
            event.stopPropagation();
          };
          if (!hasIsDefaultPrevented(event)) {
            event.isDefaultPrevented = event.defaultPrevented === true ? always : never;
            event.isPropagationStopped = event.cancelBubble === true ? always : never;
            event.isImmediatePropagationStopped = never;
          }
        }
        return event;
      };
      const eventExpandoPrefix = "mce-data-";
      const mouseEventRe = /^(?:mouse|contextmenu)|click/;
      const addEvent = (target, name2, callback, capture) => {
        target.addEventListener(name2, callback, capture || false);
      };
      const removeEvent = (target, name2, callback, capture) => {
        target.removeEventListener(name2, callback, capture || false);
      };
      const isMouseEvent = (event) => isNonNullable(event) && mouseEventRe.test(event.type);
      const fix = (originalEvent, data2) => {
        const event = normalize$3(originalEvent.type, originalEvent, document, data2);
        if (isMouseEvent(originalEvent) && isUndefined(originalEvent.pageX) && !isUndefined(originalEvent.clientX)) {
          const eventDoc = event.target.ownerDocument || document;
          const doc = eventDoc.documentElement;
          const body = eventDoc.body;
          const mouseEvent = event;
          mouseEvent.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
          mouseEvent.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
        }
        return event;
      };
      const bindOnReady = (win, callback, eventUtils) => {
        const doc = win.document, event = { type: "ready" };
        if (eventUtils.domLoaded) {
          callback(event);
          return;
        }
        const isDocReady = () => {
          return doc.readyState === "complete" || doc.readyState === "interactive" && doc.body;
        };
        const readyHandler = () => {
          removeEvent(win, "DOMContentLoaded", readyHandler);
          removeEvent(win, "load", readyHandler);
          if (!eventUtils.domLoaded) {
            eventUtils.domLoaded = true;
            callback(event);
          }
          win = null;
        };
        if (isDocReady()) {
          readyHandler();
        } else {
          addEvent(win, "DOMContentLoaded", readyHandler);
        }
        if (!eventUtils.domLoaded) {
          addEvent(win, "load", readyHandler);
        }
      };
      class EventUtils {
        static Event = new EventUtils();
        // State if the DOMContentLoaded was executed or not
        domLoaded = false;
        events = {};
        expando;
        hasFocusIn;
        count = 1;
        constructor() {
          this.expando = eventExpandoPrefix + (+/* @__PURE__ */ new Date()).toString(32);
          this.hasFocusIn = "onfocusin" in document.documentElement;
          this.count = 1;
        }
        bind(target, names, callback, scope) {
          const self = this;
          let callbackList;
          const win = window;
          const defaultNativeHandler = (evt) => {
            self.executeHandlers(fix(evt || win.event), id);
          };
          if (!target || isText$b(target) || isComment(target)) {
            return callback;
          }
          let id;
          if (!target[self.expando]) {
            id = self.count++;
            target[self.expando] = id;
            self.events[id] = {};
          } else {
            id = target[self.expando];
          }
          scope = scope || target;
          const namesList = names.split(" ");
          let i = namesList.length;
          while (i--) {
            let name2 = namesList[i];
            let nativeHandler = defaultNativeHandler;
            let capture = false;
            let fakeName = false;
            if (name2 === "DOMContentLoaded") {
              name2 = "ready";
            }
            if (self.domLoaded && name2 === "ready" && target.readyState === "complete") {
              callback.call(scope, fix({ type: name2 }));
              continue;
            }
            if (!self.hasFocusIn && (name2 === "focusin" || name2 === "focusout")) {
              capture = true;
              fakeName = name2 === "focusin" ? "focus" : "blur";
              nativeHandler = (evt) => {
                const event = fix(evt || win.event);
                event.type = event.type === "focus" ? "focusin" : "focusout";
                self.executeHandlers(event, id);
              };
            }
            callbackList = self.events[id][name2];
            if (!callbackList) {
              self.events[id][name2] = callbackList = [{ func: callback, scope }];
              callbackList.fakeName = fakeName;
              callbackList.capture = capture;
              callbackList.nativeHandler = nativeHandler;
              if (name2 === "ready") {
                bindOnReady(target, nativeHandler, self);
              } else {
                addEvent(target, fakeName || name2, nativeHandler, capture);
              }
            } else {
              if (name2 === "ready" && self.domLoaded) {
                callback(fix({ type: name2 }));
              } else {
                callbackList.push({ func: callback, scope });
              }
            }
          }
          target = callbackList = null;
          return callback;
        }
        unbind(target, names, callback) {
          if (!target || isText$b(target) || isComment(target)) {
            return this;
          }
          const id = target[this.expando];
          if (id) {
            let eventMap = this.events[id];
            if (names) {
              const namesList = names.split(" ");
              let i = namesList.length;
              while (i--) {
                const name2 = namesList[i];
                const callbackList = eventMap[name2];
                if (callbackList) {
                  if (callback) {
                    let ci = callbackList.length;
                    while (ci--) {
                      if (callbackList[ci].func === callback) {
                        const nativeHandler = callbackList.nativeHandler;
                        const fakeName = callbackList.fakeName, capture = callbackList.capture;
                        const newCallbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
                        newCallbackList.nativeHandler = nativeHandler;
                        newCallbackList.fakeName = fakeName;
                        newCallbackList.capture = capture;
                        eventMap[name2] = newCallbackList;
                      }
                    }
                  }
                  if (!callback || callbackList.length === 0) {
                    delete eventMap[name2];
                    removeEvent(target, callbackList.fakeName || name2, callbackList.nativeHandler, callbackList.capture);
                  }
                }
              }
            } else {
              each$d(eventMap, (callbackList, name2) => {
                removeEvent(target, callbackList.fakeName || name2, callbackList.nativeHandler, callbackList.capture);
              });
              eventMap = {};
            }
            for (const name2 in eventMap) {
              if (has$2(eventMap, name2)) {
                return this;
              }
            }
            delete this.events[id];
            try {
              delete target[this.expando];
            } catch {
              target[this.expando] = null;
            }
          }
          return this;
        }
        /**
         * Fires the specified event on the specified target.
         * <br>
         * <em>Deprecated in TinyMCE 6.0 and has been marked for removal in TinyMCE 7.0. Use <code>dispatch</code> instead.</em>
         *
         * @method fire
         * @param {Object} target Target node/window or custom object.
         * @param {String} name Event name to fire.
         * @param {Object} args Optional arguments to send to the observers.
         * @return {EventUtils} Event utils instance.
         * @deprecated Use dispatch() instead
         */
        fire(target, name2, args) {
          return this.dispatch(target, name2, args);
        }
        /**
         * Dispatches the specified event on the specified target.
         *
         * @method dispatch
         * @param {Node/window} target Target node/window or custom object.
         * @param {String} name Event name to dispatch.
         * @param {Object} args Optional arguments to send to the observers.
         * @return {EventUtils} Event utils instance.
         */
        dispatch(target, name2, args) {
          if (!target || isText$b(target) || isComment(target)) {
            return this;
          }
          const event = fix({ type: name2, target }, args);
          do {
            const id = target[this.expando];
            if (id) {
              this.executeHandlers(event, id);
            }
            target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
          } while (target && !event.isPropagationStopped());
          return this;
        }
        /**
         * Removes all bound event listeners for the specified target. This will also remove any bound
         * listeners to child nodes within that target.
         *
         * @method clean
         * @param {Object} target Target node/window object.
         * @return {EventUtils} Event utils instance.
         */
        clean(target) {
          if (!target || isText$b(target) || isComment(target)) {
            return this;
          }
          if (target[this.expando]) {
            this.unbind(target);
          }
          if (!target.getElementsByTagName) {
            target = target.document;
          }
          if (target && target.getElementsByTagName) {
            this.unbind(target);
            const children2 = target.getElementsByTagName("*");
            let i = children2.length;
            while (i--) {
              target = children2[i];
              if (target[this.expando]) {
                this.unbind(target);
              }
            }
          }
          return this;
        }
        /**
         * Destroys the event object. Call this to remove memory leaks.
         */
        destroy() {
          this.events = {};
        }
        // Legacy function for canceling events
        cancel(e) {
          if (e) {
            e.preventDefault();
            e.stopImmediatePropagation();
          }
          return false;
        }
        /**
         * Executes all event handler callbacks for a specific event.
         *
         * @private
         * @param {Event} evt Event object.
         * @param {String} id Expando id value to look for.
         */
        executeHandlers(evt, id) {
          const container = this.events[id];
          const callbackList = container && container[evt.type];
          if (callbackList) {
            for (let i = 0, l = callbackList.length; i < l; i++) {
              const callback = callbackList[i];
              if (callback && callback.func.call(callback.scope, evt) === false) {
                evt.preventDefault();
              }
              if (evt.isImmediatePropagationStopped()) {
                return;
              }
            }
          }
        }
      }
      const each$a = Tools.each;
      const grep = Tools.grep;
      const internalStyleName = "data-mce-style";
      const numericalCssMap = Tools.makeMap("fill-opacity font-weight line-height opacity orphans widows z-index zoom", " ");
      const legacySetAttribute = (elm, name2, value2) => {
        if (isNullable(value2) || value2 === "") {
          remove$9(elm, name2);
        } else {
          set$4(elm, name2, value2);
        }
      };
      const camelCaseToHyphens = (name2) => name2.replace(/[A-Z]/g, (v) => "-" + v.toLowerCase());
      const findNodeIndex = (node, normalized) => {
        let idx = 0;
        if (node) {
          for (let lastNodeType = node.nodeType, tempNode = node.previousSibling; tempNode; tempNode = tempNode.previousSibling) {
            const nodeType = tempNode.nodeType;
            if (normalized && isText$b(tempNode)) {
              if (nodeType === lastNodeType || !tempNode.data.length) {
                continue;
              }
            }
            idx++;
            lastNodeType = nodeType;
          }
        }
        return idx;
      };
      const updateInternalStyleAttr = (styles, elm) => {
        const rawValue = get$9(elm, "style");
        const value2 = styles.serialize(styles.parse(rawValue), name(elm));
        legacySetAttribute(elm, internalStyleName, value2);
      };
      const convertStyleToString = (cssValue, cssName) => {
        if (isNumber(cssValue)) {
          return has$2(numericalCssMap, cssName) ? cssValue + "" : cssValue + "px";
        } else {
          return cssValue;
        }
      };
      const applyStyle$1 = ($elm, cssName, cssValue) => {
        const normalizedName = camelCaseToHyphens(cssName);
        if (isNullable(cssValue) || cssValue === "") {
          remove$7($elm, normalizedName);
        } else {
          set$2($elm, normalizedName, convertStyleToString(cssValue, normalizedName));
        }
      };
      const setupAttrHooks = (styles, settings, getContext2) => {
        const keepValues = settings.keep_values;
        const keepUrlHook = {
          set: (elm, value2, name2) => {
            const sugarElm = SugarElement.fromDom(elm);
            if (isFunction(settings.url_converter) && isNonNullable(value2)) {
              value2 = settings.url_converter.call(settings.url_converter_scope || getContext2(), String(value2), name2, elm);
            }
            const internalName = "data-mce-" + name2;
            legacySetAttribute(sugarElm, internalName, value2);
            legacySetAttribute(sugarElm, name2, value2);
          },
          get: (elm, name2) => {
            const sugarElm = SugarElement.fromDom(elm);
            return get$9(sugarElm, "data-mce-" + name2) || get$9(sugarElm, name2);
          }
        };
        const attrHooks = {
          style: {
            set: (elm, value2) => {
              const sugarElm = SugarElement.fromDom(elm);
              if (keepValues) {
                legacySetAttribute(sugarElm, internalStyleName, value2);
              }
              remove$9(sugarElm, "style");
              if (isString(value2)) {
                setAll(sugarElm, styles.parse(value2));
              }
            },
            get: (elm) => {
              const sugarElm = SugarElement.fromDom(elm);
              const value2 = get$9(sugarElm, internalStyleName) || get$9(sugarElm, "style");
              return styles.serialize(styles.parse(value2), name(sugarElm));
            }
          }
        };
        if (keepValues) {
          attrHooks.href = attrHooks.src = keepUrlHook;
        }
        return attrHooks;
      };
      const DOMUtils = (doc, settings = {}) => {
        const addedStyles = {};
        const win = window;
        const files = {};
        let counter = 0;
        const stdMode = true;
        const boxModel = true;
        const styleSheetLoader = instance.forElement(SugarElement.fromDom(doc), {
          contentCssCors: settings.contentCssCors,
          referrerPolicy: settings.referrerPolicy,
          crossOrigin: (url) => {
            const crossOrigin = settings.crossOrigin;
            if (isFunction(crossOrigin)) {
              return crossOrigin(url, "stylesheet");
            } else {
              return void 0;
            }
          }
        });
        const boundEvents = [];
        const schema = settings.schema ? settings.schema : Schema({});
        const styles = Styles({
          url_converter: settings.url_converter,
          url_converter_scope: settings.url_converter_scope
        }, settings.schema);
        const events = settings.ownEvents ? new EventUtils() : EventUtils.Event;
        const blockElementsMap = schema.getBlockElements();
        const isBlock2 = (node) => {
          if (isString(node)) {
            return has$2(blockElementsMap, node);
          } else {
            return isElement$7(node) && (has$2(blockElementsMap, node.nodeName) || isTransparentBlock(schema, node));
          }
        };
        const get2 = (elm) => elm && doc && isString(elm) ? doc.getElementById(elm) : elm;
        const _get = (elm) => {
          const value2 = get2(elm);
          return isNonNullable(value2) ? SugarElement.fromDom(value2) : null;
        };
        const getAttrib = (elm, name2, defaultVal = "") => {
          let value2;
          const $elm = _get(elm);
          if (isNonNullable($elm) && isElement$8($elm)) {
            const hook = attrHooks[name2];
            if (hook && hook.get) {
              value2 = hook.get($elm.dom, name2);
            } else {
              value2 = get$9($elm, name2);
            }
          }
          return isNonNullable(value2) ? value2 : defaultVal;
        };
        const getAttribs = (elm) => {
          const node = get2(elm);
          return isNullable(node) ? [] : node.attributes;
        };
        const setAttrib = (elm, name2, value2) => {
          run(elm, (e) => {
            if (isElement$7(e)) {
              const $elm = SugarElement.fromDom(e);
              const val = value2 === "" ? null : value2;
              const originalValue = get$9($elm, name2);
              const hook = attrHooks[name2];
              if (hook && hook.set) {
                hook.set($elm.dom, val, name2);
              } else {
                legacySetAttribute($elm, name2, val);
              }
              if (originalValue !== val && settings.onSetAttrib) {
                settings.onSetAttrib({
                  attrElm: $elm.dom,
                  // We lie here to not break backwards compatibility
                  attrName: name2,
                  attrValue: val
                });
              }
            }
          });
        };
        const clone2 = (node, deep2) => {
          return node.cloneNode(deep2);
        };
        const getRoot = () => settings.root_element || doc.body;
        const getViewPort = (argWin) => {
          const vp = getBounds(argWin);
          return {
            x: vp.x,
            y: vp.y,
            w: vp.width,
            h: vp.height
          };
        };
        const getPos$1 = (elm, rootElm) => getPos(doc.body, get2(elm), rootElm);
        const setStyle = (elm, name2, value2) => {
          run(elm, (e) => {
            const $elm = SugarElement.fromDom(e);
            applyStyle$1($elm, name2, value2);
            if (settings.update_styles) {
              updateInternalStyleAttr(styles, $elm);
            }
          });
        };
        const setStyles = (elm, stylesArg) => {
          run(elm, (e) => {
            const $elm = SugarElement.fromDom(e);
            each$d(stylesArg, (v, n) => {
              applyStyle$1($elm, n, v);
            });
            if (settings.update_styles) {
              updateInternalStyleAttr(styles, $elm);
            }
          });
        };
        const getStyle2 = (elm, name2, computed) => {
          const $elm = get2(elm);
          if (isNullable($elm) || !isHTMLElement($elm) && !isSVGElement($elm)) {
            return void 0;
          }
          if (computed) {
            return get$7(SugarElement.fromDom($elm), camelCaseToHyphens(name2));
          } else {
            name2 = name2.replace(/-(\D)/g, (a, b) => b.toUpperCase());
            if (name2 === "float") {
              name2 = "cssFloat";
            }
            return $elm.style ? $elm.style[name2] : void 0;
          }
        };
        const getSize = (elm) => {
          const $elm = get2(elm);
          if (!$elm) {
            return { w: 0, h: 0 };
          }
          let w = getStyle2($elm, "width");
          let h = getStyle2($elm, "height");
          if (!w || w.indexOf("px") === -1) {
            w = "0";
          }
          if (!h || h.indexOf("px") === -1) {
            h = "0";
          }
          return {
            w: parseInt(w, 10) || $elm.offsetWidth || $elm.clientWidth,
            h: parseInt(h, 10) || $elm.offsetHeight || $elm.clientHeight
          };
        };
        const getRect = (elm) => {
          const $elm = get2(elm);
          const pos = getPos$1($elm);
          const size = getSize($elm);
          return {
            x: pos.x,
            y: pos.y,
            w: size.w,
            h: size.h
          };
        };
        const is2 = (elm, selector) => {
          if (!elm) {
            return false;
          }
          const elms = isArray$1(elm) ? elm : [elm];
          return exists(elms, (e) => {
            return is$2(SugarElement.fromDom(e), selector);
          });
        };
        const getParents2 = (elm, selector, root, collect) => {
          const result = [];
          let node = get2(elm);
          collect = collect === void 0;
          const resolvedRoot = root || (getRoot().nodeName !== "BODY" ? getRoot().parentNode : null);
          if (isString(selector)) {
            if (selector === "*") {
              selector = isElement$7;
            } else {
              const selectorVal = selector;
              selector = (node2) => is2(node2, selectorVal);
            }
          }
          while (node) {
            if (node === resolvedRoot || isNullable(node.nodeType) || isDocument$1(node) || isDocumentFragment(node)) {
              break;
            }
            if (!selector || selector(node)) {
              if (collect) {
                result.push(node);
              } else {
                return [node];
              }
            }
            node = node.parentNode;
          }
          return collect ? result : null;
        };
        const getParent = (node, selector, root) => {
          const parents2 = getParents2(node, selector, root, false);
          return parents2 && parents2.length > 0 ? parents2[0] : null;
        };
        const _findSib = (node, selector, name2) => {
          let func = selector;
          if (node) {
            if (isString(selector)) {
              func = (node2) => {
                return is2(node2, selector);
              };
            }
            for (let tempNode = node[name2]; tempNode; tempNode = tempNode[name2]) {
              if (isFunction(func) && func(tempNode)) {
                return tempNode;
              }
            }
          }
          return null;
        };
        const getNext = (node, selector) => _findSib(node, selector, "nextSibling");
        const getPrev = (node, selector) => _findSib(node, selector, "previousSibling");
        const isParentNode = (node) => isFunction(node.querySelectorAll);
        const select2 = (selector, scope) => {
          const elm = get2(scope) ?? settings.root_element ?? doc;
          return isParentNode(elm) ? from(elm.querySelectorAll(selector)) : [];
        };
        const run = function(elm, func, scope) {
          const context2 = scope ?? this;
          if (isArray$1(elm)) {
            const result = [];
            each$a(elm, (e, i) => {
              const node = get2(e);
              if (node) {
                result.push(func.call(context2, node, i));
              }
            });
            return result;
          } else {
            const node = get2(elm);
            return !node ? false : func.call(context2, node);
          }
        };
        const setAttribs2 = (elm, attrs) => {
          run(elm, ($elm) => {
            each$d(attrs, (value2, name2) => {
              setAttrib($elm, name2, value2);
            });
          });
        };
        const setHTML = (elm, html2) => {
          run(elm, (e) => {
            const $elm = SugarElement.fromDom(e);
            set$3($elm, html2);
          });
        };
        const add2 = (parentElm, name2, attrs, html2, create3) => run(parentElm, (parentElm2) => {
          const newElm = isString(name2) ? doc.createElement(name2) : name2;
          if (isNonNullable(attrs)) {
            setAttribs2(newElm, attrs);
          }
          if (html2) {
            if (!isString(html2) && html2.nodeType) {
              newElm.appendChild(html2);
            } else if (isString(html2)) {
              setHTML(newElm, html2);
            }
          }
          return !create3 ? parentElm2.appendChild(newElm) : newElm;
        });
        const create2 = (name2, attrs, html2) => add2(doc.createElement(name2), name2, attrs, html2, true);
        const decode2 = Entities.decode;
        const encode = Entities.encodeAllRaw;
        const createHTML = (name2, attrs, html2 = "") => {
          let outHtml = "<" + name2;
          for (const key in attrs) {
            if (hasNonNullableKey(attrs, key)) {
              outHtml += " " + key + '="' + encode(attrs[key]) + '"';
            }
          }
          if (isEmpty$5(html2) && has$2(schema.getVoidElements(), name2)) {
            return outHtml + " />";
          } else {
            return outHtml + ">" + html2 + "</" + name2 + ">";
          }
        };
        const createFragment2 = (html2) => {
          const container = doc.createElement("div");
          const frag = doc.createDocumentFragment();
          frag.appendChild(container);
          if (html2) {
            container.innerHTML = html2;
          }
          let node;
          while (node = container.firstChild) {
            frag.appendChild(node);
          }
          frag.removeChild(container);
          return frag;
        };
        const remove2 = (node, keepChildren) => {
          return run(node, (n) => {
            const $node = SugarElement.fromDom(n);
            if (keepChildren) {
              each$e(children$1($node), (child2) => {
                if (isText$c(child2) && child2.dom.length === 0) {
                  remove$8(child2);
                } else {
                  before$4($node, child2);
                }
              });
            }
            remove$8($node);
            return $node.dom;
          });
        };
        const removeAllAttribs = (e) => run(e, (e2) => {
          const attrs = e2.attributes;
          for (let i = attrs.length - 1; i >= 0; i--) {
            e2.removeAttributeNode(attrs.item(i));
          }
        });
        const parseStyle = (cssText) => styles.parse(cssText);
        const serializeStyle = (stylesArg, name2) => styles.serialize(stylesArg, name2);
        const addStyle = (cssText) => {
          if (self !== DOMUtils.DOM && doc === document) {
            if (addedStyles[cssText]) {
              return;
            }
            addedStyles[cssText] = true;
          }
          let styleElm = doc.getElementById("mceDefaultStyles");
          if (!styleElm) {
            styleElm = doc.createElement("style");
            styleElm.id = "mceDefaultStyles";
            styleElm.type = "text/css";
            const head2 = doc.head;
            if (head2.firstChild) {
              head2.insertBefore(styleElm, head2.firstChild);
            } else {
              head2.appendChild(styleElm);
            }
          }
          if (styleElm.styleSheet) {
            styleElm.styleSheet.cssText += cssText;
          } else {
            styleElm.appendChild(doc.createTextNode(cssText));
          }
        };
        const loadCSS = (urls) => {
          if (!urls) {
            urls = "";
          }
          each$e(urls.split(","), (url) => {
            files[url] = true;
            styleSheetLoader.load(url).catch(noop);
          });
        };
        const toggleClass2 = (elm, cls, state) => {
          run(elm, (e) => {
            if (isElement$7(e)) {
              const $elm = SugarElement.fromDom(e);
              const classes = cls.split(" ");
              each$e(classes, (c) => {
                if (isNonNullable(state)) {
                  const fn = state ? add$2 : remove$4;
                  fn($elm, c);
                } else {
                  toggle$1($elm, c);
                }
              });
            }
          });
        };
        const addClass = (elm, cls) => {
          toggleClass2(elm, cls, true);
        };
        const removeClass = (elm, cls) => {
          toggleClass2(elm, cls, false);
        };
        const hasClass2 = (elm, cls) => {
          const $elm = _get(elm);
          const classes = cls.split(" ");
          return isNonNullable($elm) && forall(classes, (c) => has($elm, c));
        };
        const show = (elm) => {
          run(elm, (e) => remove$7(SugarElement.fromDom(e), "display"));
        };
        const hide = (elm) => {
          run(elm, (e) => set$2(SugarElement.fromDom(e), "display", "none"));
        };
        const isHidden = (elm) => {
          const $elm = _get(elm);
          return isNonNullable($elm) && is$4(getRaw$1($elm, "display"), "none");
        };
        const uniqueId2 = (prefix) => (!prefix ? "mce_" : prefix) + counter++;
        const getOuterHTML = (elm) => {
          const $elm = _get(elm);
          if (isNonNullable($elm)) {
            return isElement$7($elm.dom) ? $elm.dom.outerHTML : getOuter($elm);
          } else {
            return "";
          }
        };
        const setOuterHTML = (elm, html2) => {
          run(elm, ($elm) => {
            if (isElement$7($elm)) {
              $elm.outerHTML = html2;
            }
          });
        };
        const insertAfter2 = (node, reference) => {
          const referenceNode = get2(reference);
          return run(node, (node2) => {
            const parent2 = referenceNode?.parentNode;
            const nextSibling2 = referenceNode?.nextSibling;
            if (parent2) {
              if (nextSibling2) {
                parent2.insertBefore(node2, nextSibling2);
              } else {
                parent2.appendChild(node2);
              }
            }
            return node2;
          });
        };
        const replace = (newElm, oldElm, keepChildren) => run(oldElm, (elm) => {
          const replacee = isArray$1(oldElm) ? newElm.cloneNode(true) : newElm;
          if (keepChildren) {
            each$a(grep(elm.childNodes), (node) => {
              replacee.appendChild(node);
            });
          }
          elm.parentNode?.replaceChild(replacee, elm);
          return elm;
        });
        const rename = (elm, name2) => {
          if (elm.nodeName !== name2.toUpperCase()) {
            const newElm = create2(name2);
            each$a(getAttribs(elm), (attrNode) => {
              setAttrib(newElm, attrNode.nodeName, getAttrib(elm, attrNode.nodeName));
            });
            replace(newElm, elm, true);
            return newElm;
          } else {
            return elm;
          }
        };
        const findCommonAncestor = (a, b) => {
          let ps = a;
          while (ps) {
            let pe = b;
            while (pe && ps !== pe) {
              pe = pe.parentNode;
            }
            if (ps === pe) {
              break;
            }
            ps = ps.parentNode;
          }
          if (!ps && a.ownerDocument) {
            return a.ownerDocument.documentElement;
          } else {
            return ps;
          }
        };
        const isEmpty2 = (node, elements, options) => {
          if (isPlainObject(elements)) {
            const isContent2 = (node2) => {
              const name2 = node2.nodeName.toLowerCase();
              return Boolean(elements[name2]);
            };
            return isEmptyNode(schema, node, { ...options, isContent: isContent2 });
          } else {
            return isEmptyNode(schema, node, options);
          }
        };
        const createRng = () => doc.createRange();
        const split2 = (parentElm, splitElm, replacementElm) => {
          let range2 = createRng();
          let beforeFragment;
          let afterFragment;
          if (parentElm && splitElm && parentElm.parentNode && splitElm.parentNode) {
            const parentNode = parentElm.parentNode;
            range2.setStart(parentNode, findNodeIndex(parentElm));
            range2.setEnd(splitElm.parentNode, findNodeIndex(splitElm));
            beforeFragment = range2.extractContents();
            range2 = createRng();
            range2.setStart(splitElm.parentNode, findNodeIndex(splitElm) + 1);
            range2.setEnd(parentNode, findNodeIndex(parentElm) + 1);
            afterFragment = range2.extractContents();
            parentNode.insertBefore(trimNode(self, beforeFragment, schema), parentElm);
            if (replacementElm) {
              parentNode.insertBefore(replacementElm, parentElm);
            } else {
              parentNode.insertBefore(splitElm, parentElm);
            }
            parentNode.insertBefore(trimNode(self, afterFragment, schema), parentElm);
            remove2(parentElm);
            return replacementElm || splitElm;
          } else {
            return void 0;
          }
        };
        const bind2 = (target, name2, func, scope) => {
          if (isArray$1(target)) {
            let i = target.length;
            const rv = [];
            while (i--) {
              rv[i] = bind2(target[i], name2, func, scope);
            }
            return rv;
          } else {
            if (settings.collect && (target === doc || target === win)) {
              boundEvents.push([target, name2, func, scope]);
            }
            return events.bind(target, name2, func, scope || self);
          }
        };
        const unbind2 = (target, name2, func) => {
          if (isArray$1(target)) {
            let i = target.length;
            const rv = [];
            while (i--) {
              rv[i] = unbind2(target[i], name2, func);
            }
            return rv;
          } else {
            if (boundEvents.length > 0 && (target === doc || target === win)) {
              let i = boundEvents.length;
              while (i--) {
                const [boundTarget, boundName, boundFunc] = boundEvents[i];
                if (target === boundTarget && (!name2 || name2 === boundName) && (!func || func === boundFunc)) {
                  events.unbind(boundTarget, boundName, boundFunc);
                }
              }
            }
            return events.unbind(target, name2, func);
          }
        };
        const dispatch = (target, name2, evt) => events.dispatch(target, name2, evt);
        const fire = (target, name2, evt) => events.dispatch(target, name2, evt);
        const getContentEditable = (node) => {
          if (node && isHTMLElement(node)) {
            const contentEditable = node.getAttribute("data-mce-contenteditable");
            if (contentEditable && contentEditable !== "inherit") {
              return contentEditable;
            }
            return node.contentEditable !== "inherit" ? node.contentEditable : null;
          } else {
            return null;
          }
        };
        const getContentEditableParent = (node) => {
          const root = getRoot();
          let state = null;
          for (let tempNode = node; tempNode && tempNode !== root; tempNode = tempNode.parentNode) {
            state = getContentEditable(tempNode);
            if (state !== null) {
              break;
            }
          }
          return state;
        };
        const isEditable2 = (node) => {
          if (isNonNullable(node)) {
            const scope = isElement$7(node) ? node : node.parentElement;
            return isNonNullable(scope) && isHTMLElement(scope) && isEditable$2(SugarElement.fromDom(scope));
          } else {
            return false;
          }
        };
        const destroy2 = () => {
          if (boundEvents.length > 0) {
            let i = boundEvents.length;
            while (i--) {
              const [boundTarget, boundName, boundFunc] = boundEvents[i];
              events.unbind(boundTarget, boundName, boundFunc);
            }
          }
          each$d(files, (_, url) => {
            styleSheetLoader.unload(url);
            delete files[url];
          });
        };
        const isChildOf = (node, parent2) => {
          return node === parent2 || parent2.contains(node);
        };
        const dumpRng = (r2) => "startContainer: " + r2.startContainer.nodeName + ", startOffset: " + r2.startOffset + ", endContainer: " + r2.endContainer.nodeName + ", endOffset: " + r2.endOffset;
        const self = {
          doc,
          settings,
          win,
          files,
          stdMode,
          boxModel,
          styleSheetLoader,
          boundEvents,
          styles,
          schema,
          events,
          isBlock: isBlock2,
          root: null,
          clone: clone2,
          /**
           * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
           * go above the point of this root node.
           *
           * @method getRoot
           * @return {Element} Root element for the utility class.
           */
          getRoot,
          /**
           * Returns the viewport of the window.
           *
           * @method getViewPort
           * @param {Window} win Optional window to get viewport of.
           * @return {Object} Viewport object with fields x, y, w and h.
           */
          getViewPort,
          /**
           * Returns the rectangle for a specific element.
           *
           * @method getRect
           * @param {Element/String} elm Element object or element ID to get rectangle from.
           * @return {Object} Rectangle for specified element object with x, y, w, h fields.
           */
          getRect,
          /**
           * Returns the size dimensions of the specified element.
           *
           * @method getSize
           * @param {Element/String} elm Element object or element ID to get rectangle from.
           * @return {Object} Rectangle for specified element object with w, h fields.
           */
          getSize,
          /**
           * Returns a node by the specified selector function. This function will
           * loop through all parent nodes and call the specified function for each node.
           * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
           * and the node it found will be returned.
           *
           * @method getParent
           * @param {Node/String} node DOM node to search parents on or ID string.
           * @param {Function} selector Selection function or CSS selector to execute on each node.
           * @param {Node} root Optional root element, never go beyond this point.
           * @return {Node} DOM Node or null if it wasn't found.
           */
          getParent,
          /**
           * Returns a node list of all parents matching the specified selector function or pattern.
           * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
           *
           * @method getParents
           * @param {Node/String} node DOM node to search parents on or ID string.
           * @param {Function} selector Selection function to execute on each node or CSS pattern.
           * @param {Node} root Optional root element, never go beyond this point.
           * @return {Array} Array of nodes or null if it wasn't found.
           */
          getParents: getParents2,
          /**
           * Returns the specified element by ID or the input element if it isn't a string.
           *
           * @method get
           * @param {String/Element} n Element id to look for or element to just pass though.
           * @return {Element} Element matching the specified id or null if it wasn't found.
           */
          get: get2,
          /**
           * Returns the next node that matches selector or function
           *
           * @method getNext
           * @param {Node} node Node to find siblings from.
           * @param {String/function} selector Selector CSS expression or function.
           * @return {Node} Next node item matching the selector or null if it wasn't found.
           */
          getNext,
          /**
           * Returns the previous node that matches selector or function
           *
           * @method getPrev
           * @param {Node} node Node to find siblings from.
           * @param {String/function} selector Selector CSS expression or function.
           * @return {Node} Previous node item matching the selector or null if it wasn't found.
           */
          getPrev,
          // #ifndef jquery
          /**
           * Returns a list of the elements specified by the given CSS selector. For example: `div#a1 p.test`
           *
           * @method select
           * @param {String} selector Target CSS selector.
           * @param {Object} scope Optional root element/scope element to search in.
           * @return {Array} Array with all matched elements.
           * @example
           * // Adds a class to all paragraphs in the currently active editor
           * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
           *
           * // Adds a class to all spans that have the test class in the currently active editor
           * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
           */
          select: select2,
          /**
           * Returns true/false if the specified element matches the specified css pattern.
           *
           * @method is
           * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
           * @param {String} selector CSS pattern to match the element against.
           */
          is: is2,
          // #endif
          /**
           * Adds the specified element to another element or elements.
           *
           * @method add
           * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
           * @param {String/Element} name Name of new element to add or existing element to add.
           * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
           * @param {String} html Optional inner HTML contents to add for each element.
           * @param {Boolean} create Optional flag if the element should be created or added.
           * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
           * were passed in.
           * @example
           * // Adds a new paragraph to the end of the active editor
           * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', { title: 'my title' }, 'Some content');
           */
          add: add2,
          /**
           * Creates a new element.
           *
           * @method create
           * @param {String} name Name of new element.
           * @param {Object} attrs Optional object name/value collection with element attributes.
           * @param {String} html Optional HTML string to set as inner HTML of the element.
           * @return {Element} HTML DOM node element that got created.
           * @example
           * // Adds an element where the caret/selection is in the active editor
           * var el = tinymce.activeEditor.dom.create('div', { id: 'test', 'class': 'myclass' }, 'some content');
           * tinymce.activeEditor.selection.setNode(el);
           */
          create: create2,
          /**
           * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
           *
           * @method createHTML
           * @param {String} name Name of new element.
           * @param {Object} attrs Optional object name/value collection with element attributes.
           * @param {String} html Optional HTML string to set as inner HTML of the element.
           * @return {String} String with new HTML element, for example: <a href="#">test</a>.
           * @example
           * // Creates a html chunk and inserts it at the current selection/caret location
           * tinymce.activeEditor.insertContent(tinymce.activeEditor.dom.createHTML('a', { href: 'test.html' }, 'some line'));
           */
          createHTML,
          /**
           * Creates a document fragment out of the specified HTML string.
           *
           * @method createFragment
           * @param {String} html Html string to create fragment from.
           * @return {DocumentFragment} Document fragment node.
           */
          createFragment: createFragment2,
          /**
           * Removes/deletes the specified element(s) from the DOM.
           *
           * @method remove
           * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
           * @param {Boolean} keepChildren Optional state to keep children or not. If set to true all children will be
           * placed at the location of the removed element.
           * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
           * were passed in.
           * @example
           * // Removes all paragraphs in the active editor
           * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
           *
           * // Removes an element by id in the document
           * tinymce.DOM.remove('mydiv');
           */
          remove: remove2,
          /**
           * Sets the CSS style value on a HTML element. The name can be a camelcase string
           * or the CSS style name like background-color.
           *
           * @method setStyle
           * @param {String/Element/Array} elm HTML element/Array of elements to set CSS style value on.
           * @param {String} name Name of the style value to set.
           * @param {String} value Value to set on the style.
           * @example
           * // Sets a style value on all paragraphs in the currently active editor
           * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
           *
           * // Sets a style value to an element by id in the current document
           * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
           */
          setStyle,
          /**
           * Returns the current style or runtime/computed value of an element.
           *
           * @method getStyle
           * @param {String/Element} elm HTML element or element id string to get style from.
           * @param {String} name Style name to return.
           * @param {Boolean} computed Computed style.
           * @return {String} Current style or computed style value of an element.
           */
          getStyle: getStyle2,
          /**
           * Sets multiple styles on the specified element(s).
           *
           * @method setStyles
           * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set styles on.
           * @param {Object} styles Name/Value collection of style items to add to the element(s).
           * @example
           * // Sets styles on all paragraphs in the currently active editor
           * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), { 'background-color': 'red', 'color': 'green' });
           *
           * // Sets styles to an element by id in the current document
           * tinymce.DOM.setStyles('mydiv', { 'background-color': 'red', 'color': 'green' });
           */
          setStyles,
          /**
           * Removes all attributes from an element or elements.
           *
           * @method removeAllAttribs
           * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
           */
          removeAllAttribs,
          /**
           * Sets the specified attribute of an element or elements.
           *
           * @method setAttrib
           * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attribute on.
           * @param {String} name Name of attribute to set.
           * @param {String} value Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove
           * the attribute instead.
           * @example
           * // Sets class attribute on all paragraphs in the active editor
           * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
           *
           * // Sets class attribute on a specific element in the current page
           * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
           */
          setAttrib,
          /**
           * Sets two or more specified attributes of an element or elements.
           *
           * @method setAttribs
           * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
           * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
           * @example
           * // Sets class and title attributes on all paragraphs in the active editor
           * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), { 'class': 'myclass', title: 'some title' });
           *
           * // Sets class and title attributes on a specific element in the current page
           * tinymce.DOM.setAttribs('mydiv', { 'class': 'myclass', title: 'some title' });
           */
          setAttribs: setAttribs2,
          /**
           * Returns the specified attribute by name.
           *
           * @method getAttrib
           * @param {String/Element} elm Element string id or DOM element to get attribute from.
           * @param {String} name Name of attribute to get.
           * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
           * @return {String} Attribute value string, default value or null if the attribute wasn't found.
           */
          getAttrib,
          /**
           * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
           *
           * @method getPos
           * @param {Element/String} elm HTML element or element id to get x, y position from.
           * @param {Element} rootElm Optional root element to stop calculations at.
           * @return {Object} Absolute position of the specified element object with x, y fields.
           */
          getPos: getPos$1,
          /**
           * Parses the specified style value into an object collection. This parser will also
           * merge and remove any redundant items that browsers might have added. It will also convert non-hex
           * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
           *
           * @method parseStyle
           * @param {String} cssText Style value to parse, for example: border:1px solid red;.
           * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
           */
          parseStyle,
          /**
           * Serializes the specified style object into a string.
           *
           * @method serializeStyle
           * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
           * @param {String} name Optional element name.
           * @return {String} String representation of the style object, for example: border: 1px solid red.
           */
          serializeStyle,
          /**
           * Adds a style element at the top of the document with the specified cssText content.
           *
           * @method addStyle
           * @param {String} cssText CSS Text style to add to top of head of document.
           */
          addStyle,
          /**
           * Imports/loads the specified CSS file into the document bound to the class.
           *
           * @method loadCSS
           * @param {String} url URL to CSS file to load.
           * @example
           * // Loads a CSS file dynamically into the current document
           * tinymce.DOM.loadCSS('somepath/some.css');
           *
           * // Loads a CSS file into the currently active editor instance
           * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
           *
           * // Loads a CSS file into an editor instance by id
           * tinymce.get('someid').dom.loadCSS('somepath/some.css');
           *
           * // Loads multiple CSS files into the current document
           * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
           */
          loadCSS,
          /**
           * Adds a class to the specified element or elements.
           *
           * @method addClass
           * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
           * @param {String} cls Class name to add to each element.
           * @return {String/Array} String with new class value or array with new class values for all elements.
           * @example
           * // Adds a class to all paragraphs in the active editor
           * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
           *
           * // Adds a class to a specific element in the current page
           * tinymce.DOM.addClass('mydiv', 'myclass');
           */
          addClass,
          /**
           * Removes a class from the specified element or elements.
           *
           * @method removeClass
           * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
           * @param {String} cls Class name to remove from each element.
           * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
           * were passed in.
           * @example
           * // Removes a class from all paragraphs in the active editor
           * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
           *
           * // Removes a class from a specific element in the current page
           * tinymce.DOM.removeClass('mydiv', 'myclass');
           */
          removeClass,
          /**
           * Returns true if the specified element has the specified class.
           *
           * @method hasClass
           * @param {String/Element} elm HTML element or element id string to check CSS class on.
           * @param {String} cls CSS class to check for.
           * @return {Boolean} true/false if the specified element has the specified class.
           */
          hasClass: hasClass2,
          /**
           * Toggles the specified class on/off.
           *
           * @method toggleClass
           * @param {Element} elm Element to toggle class on.
           * @param {String} cls Class to toggle on/off.
           * @param {Boolean} state Optional state to set.
           */
          toggleClass: toggleClass2,
          /**
           * Shows the specified element(s) by ID by setting the "display" style.
           *
           * @method show
           * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
           */
          show,
          /**
           * Hides the specified element(s) by ID by setting the "display" style.
           *
           * @method hide
           * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to hide.
           * @example
           * // Hides an element by id in the document
           * tinymce.DOM.hide('myid');
           */
          hide,
          /**
           * Returns true/false if the element is hidden or not by checking the "display" style.
           *
           * @method isHidden
           * @param {String/Element} elm Id or element to check display state on.
           * @return {Boolean} true/false if the element is hidden or not.
           */
          isHidden,
          /**
           * Returns a unique id. This can be useful when generating elements on the fly.
           * This method will not check if the element already exists.
           *
           * @method uniqueId
           * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
           * @return {String} Unique id.
           */
          uniqueId: uniqueId2,
          /**
           * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
           * URLs will get converted, hex color values fixed etc. Check processHTML for details.
           *
           * @method setHTML
           * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set HTML inside of.
           * @param {String} html HTML content to set as inner HTML of the element.
           * @example
           * // Sets the inner HTML of all paragraphs in the active editor
           * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
           *
           * // Sets the inner HTML of an element by id in the document
           * tinymce.DOM.setHTML('mydiv', 'some inner html');
           */
          setHTML,
          /**
           * Returns the outer HTML of an element.
           *
           * @method getOuterHTML
           * @param {String/Element} elm Element ID or element object to get outer HTML from.
           * @return {String} Outer HTML string.
           * @example
           * tinymce.DOM.getOuterHTML(editorElement);
           * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
           */
          getOuterHTML,
          /**
           * Sets the specified outer HTML on an element or elements.
           *
           * @method setOuterHTML
           * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
           * @param {Object} html HTML code to set as outer value for the element.
           * @example
           * // Sets the outer HTML of all paragraphs in the active editor
           * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
           *
           * // Sets the outer HTML of an element by id in the document
           * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
           */
          setOuterHTML,
          /**
           * Entity decodes a string. This method decodes any HTML entities, such as `&amp;aring;`.
           *
           * @method decode
           * @param {String} s String to decode entities on.
           * @return {String} Entity decoded string.
           */
          decode: decode2,
          /**
           * Entity encodes a string. This method encodes the most common entities, such as `<`, `>`, `"` and `&`.
           *
           * @method encode
           * @param {String} text String to encode with entities.
           * @return {String} Entity encoded string.
           */
          encode,
          /**
           * Inserts an element after the reference element.
           *
           * @method insertAfter
           * @param {Element} node Element to insert after the reference.
           * @param {Element/String/Array} referenceNode Reference element, element id or array of elements to insert after.
           * @return {Element/Array} Element that got added or an array with elements.
           */
          insertAfter: insertAfter2,
          /**
           * Replaces the specified element or elements with the new element specified. The new element will
           * be cloned if multiple input elements are passed in.
           *
           * @method replace
           * @param {Element} newElm New element to replace old ones with.
           * @param {Element/String/Array} oldElm Element DOM node, element id or array of elements or ids to replace.
           * @param {Boolean} keepChildren Optional keep children state, if set to true child nodes from the old object will be added
           * to new ones.
           */
          replace,
          /**
           * Renames the specified element and keeps its attributes and children.
           *
           * @method rename
           * @param {Element} elm Element to rename.
           * @param {String} name Name of the new element.
           * @return {Element} New element or the old element if it needed renaming.
           */
          rename,
          /**
           * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
           *
           * @method findCommonAncestor
           * @param {Element} a Element to find common ancestor of.
           * @param {Element} b Element to find common ancestor of.
           * @return {Element} Common ancestor element of the two input elements.
           */
          findCommonAncestor,
          /**
           * Executes the specified function on the element by id or dom element node or array of elements/id.
           *
           * @method run
           * @param {String/Element/Array} elm ID or DOM element object or array with ids or elements.
           * @param {Function} func Function to execute for each item.
           * @param {Object} scope Optional scope to execute the function in.
           * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
           */
          run,
          /**
           * Returns a NodeList with attributes for the element.
           *
           * @method getAttribs
           * @param {HTMLElement/string} elm Element node or string id to get attributes from.
           * @return {NodeList} NodeList with attributes.
           */
          getAttribs,
          /**
           * Returns true/false if the specified node is to be considered empty or not.
           *
           * @method isEmpty
           * @param {Node} node The target node to check if it's empty.
           * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
           * @return {Boolean} true/false if the node is empty or not.
           * @example
           * tinymce.DOM.isEmpty(node, { img: true });
           */
          isEmpty: isEmpty2,
          /**
           * Creates a new DOM Range object. This will use the native DOM Range API if it's
           * available. If it's not, it will fall back to the custom TinyMCE implementation.
           *
           * @method createRng
           * @return {DOMRange} DOM Range object.
           * @example
           * const rng = tinymce.DOM.createRng();
           * alert(rng.startContainer + "," + rng.startOffset);
           */
          createRng,
          /**
           * Returns the index of the specified node within its parent.
           *
           * @method nodeIndex
           * @param {Node} node Node to look for.
           * @param {Boolean} normalized Optional true/false state if the index is what it would be after a normalization.
           * @return {Number} Index of the specified node.
           */
          nodeIndex: findNodeIndex,
          /**
           * Splits an element into two new elements and places the specified split
           * element or elements between the new ones. For example splitting the paragraph at the bold element in
           * this example `<p>abc<b>abc</b>123</p>` would produce `<p>abc</p><b>abc</b><p>123</p>`.
           *
           * @method split
           * @param {Element} parentElm Parent element to split.
           * @param {Element} splitElm Element to split at.
           * @param {Element} replacementElm Optional replacement element to replace the split element with.
           * @return {Element} Returns the split element or the replacement element if that is specified.
           */
          split: split2,
          /**
           * Adds an event handler to the specified object.
           *
           * @method bind
           * @param {Element/Document/Window/Array} target Target element to bind events to.
           * handler to or an array of elements/ids/documents.
           * @param {String} name Name of event handler to add, for example: click.
           * @param {Function} func Function to execute when the event occurs.
           * @param {Object} scope Optional scope to execute the function in.
           * @return {Function} Function callback handler the same as the one passed in.
           */
          bind: bind2,
          /**
           * Removes the specified event handler by name and function from an element or collection of elements.
           *
           * @method unbind
           * @param {Element/Document/Window/Array} target Target element to unbind events on.
           * @param {String} name Event handler name, for example: "click"
           * @param {Function} func Function to remove.
           * @return {Boolean/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
           * were passed in.
           */
          unbind: unbind2,
          /**
           * Fires the specified event name and optional object on the specified target.
           * <br>
           * <em>Deprecated in TinyMCE 6.0 and has been marked for removal in TinyMCE 7.0. Use <code>dispatch</code> instead.</em>
           *
           * @method fire
           * @param {Node/Document/Window} target Target element or object to fire event on.
           * @param {String} name Event name to fire.
           * @param {Object} evt Event object to send.
           * @return {Event} Event object.
           * @deprecated Use dispatch() instead
           */
          fire,
          /**
           * Dispatches the specified event name and optional object on the specified target.
           *
           * @method dispatch
           * @param {Node/Document/Window} target Target element or object to dispatch event on.
           * @param {String} name Name of the event to fire.
           * @param {Object} evt Event object to send.
           * @return {Event} Event object.
           */
          dispatch,
          // Returns the content editable state of a node
          getContentEditable,
          getContentEditableParent,
          /**
           * Checks if the specified node is editable within the given context of its parents.
           *
           * @method isEditable
           * @param {Node} node Node to check if it's editable.
           * @return {Boolean} Will be true if the node is editable and false if it's not editable.
           */
          isEditable: isEditable2,
          /**
           * Destroys all internal references to the DOM to solve memory leak issues.
           *
           * @method destroy
           */
          destroy: destroy2,
          isChildOf,
          dumpRng
        };
        const attrHooks = setupAttrHooks(styles, settings, constant(self));
        return self;
      };
      DOMUtils.DOM = DOMUtils(document);
      DOMUtils.nodeIndex = findNodeIndex;
      const DOM$e = DOMUtils.DOM;
      const QUEUED = 0;
      const LOADING = 1;
      const LOADED = 2;
      const FAILED = 3;
      class ScriptLoader {
        static ScriptLoader = new ScriptLoader();
        settings;
        states = {};
        queue = [];
        scriptLoadedCallbacks = {};
        queueLoadedCallbacks = [];
        loading = false;
        constructor(settings = {}) {
          this.settings = settings;
        }
        _setReferrerPolicy(referrerPolicy) {
          this.settings.referrerPolicy = referrerPolicy;
        }
        _setCrossOrigin(crossOrigin) {
          this.settings.crossOrigin = crossOrigin;
        }
        /**
         * Loads a specific script directly without adding it to the load queue.
         *
         * @method loadScript
         * @param {String} url Absolute URL to script to add.
         * @return {Promise} A promise that will resolve when the script loaded successfully or reject if it failed to load.
         */
        loadScript(url) {
          return new Promise((resolve2, reject) => {
            const dom2 = DOM$e;
            const doc = document;
            let elm;
            const cleanup = () => {
              dom2.remove(id);
              if (elm) {
                elm.onerror = elm.onload = elm = null;
              }
            };
            const done = () => {
              cleanup();
              resolve2();
            };
            const error2 = () => {
              cleanup();
              reject("Failed to load script: " + url);
            };
            const id = dom2.uniqueId();
            elm = doc.createElement("script");
            elm.id = id;
            elm.type = "text/javascript";
            elm.src = Tools._addCacheSuffix(url);
            if (this.settings.referrerPolicy) {
              dom2.setAttrib(elm, "referrerpolicy", this.settings.referrerPolicy);
            }
            const crossOrigin = this.settings.crossOrigin;
            if (isFunction(crossOrigin)) {
              const resultCrossOrigin = crossOrigin(url);
              if (resultCrossOrigin !== void 0) {
                dom2.setAttrib(elm, "crossorigin", resultCrossOrigin);
              }
            }
            elm.onload = done;
            elm.onerror = error2;
            (doc.head || doc.body).appendChild(elm);
          });
        }
        /**
         * Returns true/false if a script has been loaded or not.
         *
         * @method isDone
         * @param {String} url URL to check for.
         * @return {Boolean} true/false if the URL is loaded.
         */
        isDone(url) {
          return this.states[url] === LOADED;
        }
        /**
         * Marks a specific script to be loaded. This can be useful if a script got loaded outside
         * the script loader or to skip it from loading some script.
         *
         * @method markDone
         * @param {String} url Absolute URL to the script to mark as loaded.
         */
        markDone(url) {
          this.states[url] = LOADED;
        }
        /**
         * Adds a specific script to the load queue of the script loader.
         *
         * @method add
         * @param {String} url Absolute URL to script to add.
         * @return {Promise} A promise that will resolve when the script loaded successfully or reject if it failed to load.
         */
        add(url) {
          const self = this;
          self.queue.push(url);
          const state = self.states[url];
          if (state === void 0) {
            self.states[url] = QUEUED;
          }
          return new Promise((resolve2, reject) => {
            if (!self.scriptLoadedCallbacks[url]) {
              self.scriptLoadedCallbacks[url] = [];
            }
            self.scriptLoadedCallbacks[url].push({
              resolve: resolve2,
              reject
            });
          });
        }
        load(url) {
          return this.add(url);
        }
        remove(url) {
          delete this.states[url];
          delete this.scriptLoadedCallbacks[url];
        }
        /**
         * Starts the loading of the queue.
         *
         * @method loadQueue
         * @return {Promise} A promise that is resolved when all queued items are loaded or rejected with the script urls that failed to load.
         */
        loadQueue() {
          const queue = this.queue;
          this.queue = [];
          return this.loadScripts(queue);
        }
        /**
         * Loads the specified queue of files and executes the callback ones they are loaded.
         * This method is generally not used outside this class but it might be useful in some scenarios.
         *
         * @method loadScripts
         * @param {Array} scripts Array of queue items to load.
         * @return {Promise} A promise that is resolved when all scripts are loaded or rejected with the script urls that failed to load.
         */
        loadScripts(scripts) {
          const self = this;
          const execCallbacks = (name2, url) => {
            get$a(self.scriptLoadedCallbacks, url).each((callbacks) => {
              each$e(callbacks, (callback) => callback[name2](url));
            });
            delete self.scriptLoadedCallbacks[url];
          };
          const processResults = (results) => {
            const failures = filter$5(results, (result) => result.status === "rejected");
            if (failures.length > 0) {
              return Promise.reject(bind$3(failures, ({ reason }) => isArray$1(reason) ? reason : [reason]));
            } else {
              return Promise.resolve();
            }
          };
          const load = (urls) => Promise.allSettled(map$3(urls, (url) => {
            if (self.states[url] === LOADED) {
              execCallbacks("resolve", url);
              return Promise.resolve();
            } else if (self.states[url] === FAILED) {
              execCallbacks("reject", url);
              return Promise.reject(url);
            } else {
              self.states[url] = LOADING;
              return self.loadScript(url).then(() => {
                self.states[url] = LOADED;
                execCallbacks("resolve", url);
                const queue = self.queue;
                if (queue.length > 0) {
                  self.queue = [];
                  return load(queue).then(processResults);
                } else {
                  return Promise.resolve();
                }
              }, () => {
                self.states[url] = FAILED;
                execCallbacks("reject", url);
                return Promise.reject(url);
              });
            }
          }));
          const processQueue = (urls) => {
            self.loading = true;
            return load(urls).then((results) => {
              self.loading = false;
              const nextQueuedItem = self.queueLoadedCallbacks.shift();
              Optional.from(nextQueuedItem).each(call);
              return processResults(results);
            });
          };
          const uniqueScripts = stringArray(scripts);
          if (self.loading) {
            return new Promise((resolve2, reject) => {
              self.queueLoadedCallbacks.push(() => {
                processQueue(uniqueScripts).then(resolve2, reject);
              });
            });
          } else {
            return processQueue(uniqueScripts);
          }
        }
        /**
         * Returns the attributes that should be added to a script tag when loading the specified URL.
         *
         * @method getScriptAttributes
         * @param {String} url Url to get attributes for.
         * @return {Object} Object with attributes to add to the script tag.
         */
        getScriptAttributes(url) {
          const attrs = {};
          if (this.settings.referrerPolicy) {
            attrs.referrerpolicy = this.settings.referrerPolicy;
          }
          const crossOrigin = this.settings.crossOrigin;
          if (isFunction(crossOrigin)) {
            const resultCrossOrigin = crossOrigin(url);
            if (isString(resultCrossOrigin)) {
              attrs.crossorigin = resultCrossOrigin;
            }
          }
          return attrs;
        }
      }
      const isDuplicated = (items, item) => {
        const firstIndex = items.indexOf(item);
        return firstIndex !== -1 && items.indexOf(item, firstIndex + 1) > firstIndex;
      };
      const isRaw = (str) => isObject(str) && has$2(str, "raw");
      const isTokenised = (str) => isArray$1(str) && str.length > 1;
      const data = {};
      const currentCode = Cell("en");
      const getLanguageData = () => get$a(data, currentCode.get());
      const getData$1 = () => map$2(data, (value2) => ({ ...value2 }));
      const setCode = (newCode) => {
        if (newCode) {
          currentCode.set(newCode);
        }
      };
      const getCode = () => currentCode.get();
      const add = (code, items) => {
        let langData = data[code];
        if (!langData) {
          data[code] = langData = {};
        }
        const lcNames = map$3(keys(items), (name2) => name2.toLowerCase());
        each$d(items, (translation, name2) => {
          const lcName = name2.toLowerCase();
          if (lcName !== name2 && isDuplicated(lcNames, lcName)) {
            if (!has$2(items, lcName)) {
              langData[lcName] = translation;
            }
            langData[name2] = translation;
          } else {
            langData[lcName] = translation;
          }
        });
      };
      const translate = (text2) => {
        const langData = getLanguageData().getOr({});
        const toString2 = (obj) => {
          if (isFunction(obj)) {
            return Object.prototype.toString.call(obj);
          }
          return !isEmpty2(obj) ? "" + obj : "";
        };
        const isEmpty2 = (text3) => text3 === "" || text3 === null || text3 === void 0;
        const getLangData = (text3) => {
          const textStr = toString2(text3);
          return has$2(langData, textStr) ? toString2(langData[textStr]) : get$a(langData, textStr.toLowerCase()).map(toString2).getOr(textStr);
        };
        const removeContext = (str) => str.replace(/{context:\w+}$/, "");
        const replaceWithEllipsisChar = (text3) => text3.replaceAll("...", ellipsis);
        if (isEmpty2(text2)) {
          return "";
        }
        if (isRaw(text2)) {
          return replaceWithEllipsisChar(toString2(text2.raw));
        }
        if (isTokenised(text2)) {
          const values2 = text2.slice(1);
          const substitued = getLangData(text2[0]).replace(/\{([0-9]+)\}/g, ($1, $2) => has$2(values2, $2) ? toString2(values2[$2]) : $1);
          return replaceWithEllipsisChar(removeContext(substitued));
        }
        return replaceWithEllipsisChar(removeContext(getLangData(text2)));
      };
      const isRtl$1 = () => getLanguageData().bind((items) => get$a(items, "_dir")).exists((dir) => dir === "rtl");
      const hasCode = (code) => has$2(data, code);
      const I18n = {
        getData: getData$1,
        setCode,
        getCode,
        add,
        translate,
        isRtl: isRtl$1,
        hasCode
      };
      const AddOnManager = () => {
        const items = [];
        const urls = {};
        const lookup2 = {};
        const _listeners = [];
        const runListeners = (name2, state) => {
          const matchedListeners = filter$5(_listeners, (listener) => listener.name === name2 && listener.state === state);
          each$e(matchedListeners, (listener) => listener.resolve());
        };
        const isLoaded = (name2) => has$2(urls, name2);
        const isAdded = (name2) => has$2(lookup2, name2);
        const get2 = (name2) => {
          if (lookup2[name2]) {
            return lookup2[name2].instance;
          }
          return void 0;
        };
        const loadLanguagePack = (name2, languages) => {
          const language = I18n.getCode();
          const wrappedLanguages = "," + (languages || "") + ",";
          if (!language || languages && wrappedLanguages.indexOf("," + language + ",") === -1) {
            return;
          }
          ScriptLoader.ScriptLoader.add(urls[name2] + "/langs/" + language + ".js");
        };
        const requireLangPack = (name2, languages) => {
          if (AddOnManager.languageLoad !== false) {
            if (isLoaded(name2)) {
              loadLanguagePack(name2, languages);
            } else {
              waitFor(name2, "loaded").then(() => loadLanguagePack(name2, languages));
            }
          }
        };
        const add2 = (id, addOn) => {
          items.push(addOn);
          lookup2[id] = { instance: addOn };
          runListeners(id, "added");
          return addOn;
        };
        const remove2 = (name2) => {
          delete urls[name2];
          delete lookup2[name2];
        };
        const createUrl = (baseUrl, dep) => {
          if (isString(dep)) {
            return isString(baseUrl) ? { prefix: "", resource: dep, suffix: "" } : { prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix };
          } else {
            return dep;
          }
        };
        const load = (name2, addOnUrl) => {
          if (urls[name2]) {
            return Promise.resolve();
          }
          let urlString = isString(addOnUrl) ? addOnUrl : addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;
          if (urlString.indexOf("/") !== 0 && urlString.indexOf("://") === -1) {
            urlString = AddOnManager.baseURL + "/" + urlString;
          }
          urls[name2] = urlString.substring(0, urlString.lastIndexOf("/"));
          const done = () => {
            runListeners(name2, "loaded");
            return Promise.resolve();
          };
          if (lookup2[name2]) {
            return done();
          } else {
            return ScriptLoader.ScriptLoader.add(urlString).then(done);
          }
        };
        const waitFor = (name2, state = "added") => {
          if (state === "added" && isAdded(name2)) {
            return Promise.resolve();
          } else if (state === "loaded" && isLoaded(name2)) {
            return Promise.resolve();
          } else {
            return new Promise((resolve2) => {
              _listeners.push({ name: name2, state, resolve: resolve2 });
            });
          }
        };
        return {
          items,
          urls,
          lookup: lookup2,
          /**
           * Returns the specified add on by the short name.
           *
           * @method get
           * @param {String} name Add-on to look for.
           * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
           */
          get: get2,
          /**
           * Loads a language pack for the specified add-on.
           *
           * @method requireLangPack
           * @param {String} name Short name of the add-on.
           * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
           */
          requireLangPack,
          /**
           * Adds a instance of the add-on by it's short name.
           *
           * @method add
           * @param {String} id Short name/id for the add-on.
           * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
           * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
           * @example
           * // Create a simple plugin
           * const TestPlugin = (ed, url) => {
           *   ed.on('click', (e) => {
           *     ed.windowManager.alert('Hello World!');
           *   });
           * };
           *
           * // Register plugin using the add method
           * tinymce.PluginManager.add('test', TestPlugin);
           *
           * // Initialize TinyMCE
           * tinymce.init({
           *   ...
           *   plugins: '-test' // Init the plugin but don't try to load it
           * });
           */
          add: add2,
          remove: remove2,
          createUrl,
          /**
           * Loads an add-on from a specific url.
           *
           * @method load
           * @param {String} name Short name of the add-on that gets loaded.
           * @param {String} addOnUrl URL to the add-on that will get loaded.
           * @return {Promise} A promise that will resolve when the add-on is loaded successfully or reject if it failed to load.
           * @example
           * // Loads a plugin from an external URL
           * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
           *
           * // Initialize TinyMCE
           * tinymce.init({
           *   ...
           *   plugins: '-myplugin' // Don't try to load it again
           * });
           */
          load,
          waitFor
        };
      };
      AddOnManager.languageLoad = true;
      AddOnManager.baseURL = "";
      AddOnManager.PluginManager = AddOnManager();
      AddOnManager.ThemeManager = AddOnManager();
      AddOnManager.ModelManager = AddOnManager();
      const annotation = constant("mce-annotation");
      const dataAnnotation = constant("data-mce-annotation");
      const dataAnnotationId = constant("data-mce-annotation-uid");
      const dataAnnotationActive = constant("data-mce-annotation-active");
      const dataAnnotationClasses = constant("data-mce-annotation-classes");
      const dataAnnotationAttributes = constant("data-mce-annotation-attrs");
      const isRoot$1 = (root) => (node) => eq(node, root);
      const identify = (editor, annotationName) => {
        const rng = editor.selection.getRng();
        const start2 = SugarElement.fromDom(rng.startContainer);
        const root = SugarElement.fromDom(editor.getBody());
        const selector = annotationName.fold(() => "." + annotation(), (an) => `[${dataAnnotation()}="${an}"]`);
        const newStart = child$1(start2, rng.startOffset).getOr(start2);
        const closest2 = closest$3(newStart, selector, isRoot$1(root));
        return closest2.bind((c) => getOpt(c, `${dataAnnotationId()}`).bind((uid2) => getOpt(c, `${dataAnnotation()}`).map((name2) => {
          const elements = findMarkers(editor, uid2);
          return {
            uid: uid2,
            name: name2,
            elements
          };
        })));
      };
      const isAnnotation = (elem) => isElement$8(elem) && has(elem, annotation());
      const isBogusElement = (elem, root) => has$1(elem, "data-mce-bogus") || ancestor$1(elem, '[data-mce-bogus="all"]', isRoot$1(root));
      const findMarkers = (editor, uid2) => {
        const body = SugarElement.fromDom(editor.getBody());
        const descendants$12 = descendants(body, `[${dataAnnotationId()}="${uid2}"]`);
        return filter$5(descendants$12, (descendant2) => !isBogusElement(descendant2, body));
      };
      const findAll = (editor, name2) => {
        const body = SugarElement.fromDom(editor.getBody());
        const markers = descendants(body, `[${dataAnnotation()}="${name2}"]`);
        const directory = {};
        each$e(markers, (m) => {
          if (!isBogusElement(m, body)) {
            const uid2 = get$9(m, dataAnnotationId());
            const nodesAlready = get$a(directory, uid2).getOr([]);
            directory[uid2] = nodesAlready.concat([m]);
          }
        });
        return directory;
      };
      const setup$E = (editor, registry2) => {
        const changeCallbacks = Cell({});
        const initData = () => ({
          listeners: [],
          previous: value$1()
        });
        const withCallbacks = (name2, f) => {
          updateCallbacks(name2, (data2) => {
            f(data2);
            return data2;
          });
        };
        const updateCallbacks = (name2, f) => {
          const callbackMap = changeCallbacks.get();
          const data2 = get$a(callbackMap, name2).getOrThunk(initData);
          const outputData = f(data2);
          callbackMap[name2] = outputData;
          changeCallbacks.set(callbackMap);
        };
        const fireCallbacks = (name2, uid2, elements) => {
          withCallbacks(name2, (data2) => {
            each$e(data2.listeners, (f) => f(true, name2, {
              uid: uid2,
              nodes: map$3(elements, (elem) => elem.dom)
            }));
          });
        };
        const fireNoAnnotation = (name2) => {
          withCallbacks(name2, (data2) => {
            each$e(data2.listeners, (f) => f(false, name2));
          });
        };
        const toggleActiveAttr = (uid2, state) => {
          each$e(findMarkers(editor, uid2), (elem) => {
            if (state) {
              set$4(elem, dataAnnotationActive(), "true");
            } else {
              remove$9(elem, dataAnnotationActive());
            }
          });
        };
        const onNodeChange = last$1(() => {
          const annotations = sort(registry2.getNames());
          each$e(annotations, (name2) => {
            updateCallbacks(name2, (data2) => {
              const prev2 = data2.previous.get();
              identify(editor, Optional.some(name2)).fold(() => {
                prev2.each((uid2) => {
                  fireNoAnnotation(name2);
                  data2.previous.clear();
                  toggleActiveAttr(uid2, false);
                });
              }, ({ uid: uid2, name: name3, elements }) => {
                if (!is$4(prev2, uid2)) {
                  prev2.each((uid3) => toggleActiveAttr(uid3, false));
                  fireCallbacks(name3, uid2, elements);
                  data2.previous.set(uid2);
                  toggleActiveAttr(uid2, true);
                }
              });
              return {
                previous: data2.previous,
                listeners: data2.listeners
              };
            });
          });
        }, 30);
        editor.on("remove", () => {
          onNodeChange.cancel();
        });
        editor.on("NodeChange", () => {
          onNodeChange.throttle();
        });
        const addListener = (name2, f) => {
          updateCallbacks(name2, (data2) => ({
            previous: data2.previous,
            listeners: data2.listeners.concat([f])
          }));
        };
        return {
          addListener
        };
      };
      const setup$D = (editor, registry2) => {
        const dataAnnotation$1 = dataAnnotation();
        const identifyParserNode = (node) => Optional.from(node.attr(dataAnnotation$1)).bind(registry2.lookup);
        const removeDirectAnnotation2 = (node) => {
          node.attr(dataAnnotationId(), null);
          node.attr(dataAnnotation(), null);
          node.attr(dataAnnotationActive(), null);
          const customAttrNames = Optional.from(node.attr(dataAnnotationAttributes())).map((names) => names.split(",")).getOr([]);
          const customClasses = Optional.from(node.attr(dataAnnotationClasses())).map((names) => names.split(",")).getOr([]);
          each$e(customAttrNames, (name2) => node.attr(name2, null));
          const classList = node.attr("class")?.split(" ") ?? [];
          const newClassList = difference(classList, [annotation()].concat(customClasses));
          node.attr("class", newClassList.length > 0 ? newClassList.join(" ") : null);
          node.attr(dataAnnotationClasses(), null);
          node.attr(dataAnnotationAttributes(), null);
        };
        editor.serializer.addTempAttr(dataAnnotationActive());
        editor.serializer.addAttributeFilter(dataAnnotation$1, (nodes) => {
          for (const node of nodes) {
            identifyParserNode(node).each((settings) => {
              if (settings.persistent === false) {
                if (node.name === "span") {
                  node.unwrap();
                } else {
                  removeDirectAnnotation2(node);
                }
              }
            });
          }
        });
      };
      const create$a = () => {
        const annotations = {};
        const register2 = (name2, settings) => {
          annotations[name2] = {
            name: name2,
            settings
          };
        };
        const lookup2 = (name2) => get$a(annotations, name2).map((a) => a.settings);
        const getNames = () => keys(annotations);
        return {
          register: register2,
          lookup: lookup2,
          getNames
        };
      };
      const TextWalker = (startNode, rootNode, isBoundary2 = never) => {
        const walker = new DomTreeWalker(startNode, rootNode);
        const walk2 = (direction) => {
          let next2;
          do {
            next2 = walker[direction]();
          } while (next2 && !isText$b(next2) && !isBoundary2(next2));
          return Optional.from(next2).filter(isText$b);
        };
        return {
          current: () => Optional.from(walker.current()).filter(isText$b),
          next: () => walk2("next"),
          prev: () => walk2("prev"),
          prev2: () => walk2("prev2")
        };
      };
      const TextSeeker = (dom2, isBoundary2) => {
        const isBlockBoundary = isBoundary2 ? isBoundary2 : (node) => dom2.isBlock(node) || isBr$7(node) || isContentEditableFalse$a(node);
        const walk2 = (node, offset, walker, process2) => {
          if (isText$b(node)) {
            const newOffset = process2(node, offset, node.data);
            if (newOffset !== -1) {
              return Optional.some({ container: node, offset: newOffset });
            }
          }
          return walker().bind((next2) => walk2(next2.container, next2.offset, walker, process2));
        };
        const backwards = (node, offset, process2, root) => {
          const walker = TextWalker(node, root ?? dom2.getRoot(), isBlockBoundary);
          return walk2(node, offset, () => walker.prev().map((prev2) => ({ container: prev2, offset: prev2.length })), process2).getOrNull();
        };
        const forwards = (node, offset, process2, root) => {
          const walker = TextWalker(node, root ?? dom2.getRoot(), isBlockBoundary);
          return walk2(node, offset, () => walker.next().map((next2) => ({ container: next2, offset: 0 })), process2).getOrNull();
        };
        return {
          backwards,
          forwards
        };
      };
      const tableCells = ["td", "th"];
      const tableSections = ["thead", "tbody", "tfoot"];
      const textBlocks = [
        "h1",
        "h2",
        "h3",
        "h4",
        "h5",
        "h6",
        "p",
        "div",
        "address",
        "pre",
        "form",
        "blockquote",
        "center",
        "dir",
        "fieldset",
        "header",
        "footer",
        "article",
        "section",
        "hgroup",
        "aside",
        "nav",
        "figure"
      ];
      const listItems$1 = ["li", "dd", "dt"];
      const lists = ["ul", "ol", "dl"];
      const wsElements = ["pre", "script", "textarea", "style"];
      const lazyLookup = (items) => {
        let lookup2;
        return (node) => {
          lookup2 = lookup2 ? lookup2 : mapToObject(items, always);
          return has$2(lookup2, name(node));
        };
      };
      const isTable$1 = (node) => name(node) === "table";
      const isBr$6 = (node) => isElement$8(node) && name(node) === "br";
      const isTextBlock$3 = lazyLookup(textBlocks);
      const isList$1 = lazyLookup(lists);
      const isListItem$2 = lazyLookup(listItems$1);
      const isTableSection = lazyLookup(tableSections);
      const isTableCell$2 = lazyLookup(tableCells);
      const isWsPreserveElement = lazyLookup(wsElements);
      const getLastChildren$1 = (elm) => {
        const children2 = [];
        let rawNode = elm.dom;
        while (rawNode) {
          children2.push(SugarElement.fromDom(rawNode));
          rawNode = rawNode.lastChild;
        }
        return children2;
      };
      const removeTrailingBr = (elm) => {
        const allBrs = descendants(elm, "br");
        const brs = filter$5(getLastChildren$1(elm).slice(-1), isBr$6);
        if (allBrs.length === brs.length) {
          each$e(brs, remove$8);
        }
      };
      const createPaddingBr = () => {
        const br = SugarElement.fromTag("br");
        set$4(br, "data-mce-bogus", "1");
        return br;
      };
      const fillWithPaddingBr = (elm) => {
        empty(elm);
        append$1(elm, createPaddingBr());
      };
      const trimBlockTrailingBr = (elm, schema) => {
        lastChild(elm).each((lastChild2) => {
          prevSibling(lastChild2).each((lastChildPrevSibling) => {
            if (schema.isBlock(name(elm)) && isBr$6(lastChild2) && schema.isBlock(name(lastChildPrevSibling))) {
              remove$8(lastChild2);
            }
          });
        });
      };
      const ZWSP$1 = zeroWidth;
      const isZwsp = isZwsp$2;
      const trim$2 = removeZwsp;
      const insert$5 = (editor) => editor.insertContent(ZWSP$1, { preserve_zwsp: true });
      const isElement$6 = isElement$7;
      const isText$9 = isText$b;
      const isCaretContainerBlock$1 = (node) => {
        if (isText$9(node)) {
          node = node.parentNode;
        }
        return isElement$6(node) && node.hasAttribute("data-mce-caret");
      };
      const isCaretContainerInline = (node) => isText$9(node) && isZwsp(node.data);
      const isCaretContainer$2 = (node) => isCaretContainerBlock$1(node) || isCaretContainerInline(node);
      const hasContent = (node) => node.firstChild !== node.lastChild || !isBr$7(node.firstChild);
      const insertInline$1 = (node, before2) => {
        const doc = node.ownerDocument ?? document;
        const textNode = doc.createTextNode(ZWSP$1);
        const parentNode = node.parentNode;
        if (!before2) {
          const sibling2 = node.nextSibling;
          if (isText$9(sibling2)) {
            if (isCaretContainer$2(sibling2)) {
              return sibling2;
            }
            if (startsWithCaretContainer$1(sibling2)) {
              sibling2.splitText(1);
              return sibling2;
            }
          }
          if (node.nextSibling) {
            parentNode?.insertBefore(textNode, node.nextSibling);
          } else {
            parentNode?.appendChild(textNode);
          }
        } else {
          const sibling2 = node.previousSibling;
          if (isText$9(sibling2)) {
            if (isCaretContainer$2(sibling2)) {
              return sibling2;
            }
            if (endsWithCaretContainer$1(sibling2)) {
              return sibling2.splitText(sibling2.data.length - 1);
            }
          }
          parentNode?.insertBefore(textNode, node);
        }
        return textNode;
      };
      const isBeforeInline = (pos) => {
        const container = pos.container();
        if (!isText$b(container)) {
          return false;
        }
        return container.data.charAt(pos.offset()) === ZWSP$1 || pos.isAtStart() && isCaretContainerInline(container.previousSibling);
      };
      const isAfterInline = (pos) => {
        const container = pos.container();
        if (!isText$b(container)) {
          return false;
        }
        return container.data.charAt(pos.offset() - 1) === ZWSP$1 || pos.isAtEnd() && isCaretContainerInline(container.nextSibling);
      };
      const insertBlock = (blockName, node, before2) => {
        const doc = node.ownerDocument ?? document;
        const blockNode = doc.createElement(blockName);
        blockNode.setAttribute("data-mce-caret", before2 ? "before" : "after");
        blockNode.setAttribute("data-mce-bogus", "all");
        blockNode.appendChild(createPaddingBr().dom);
        const parentNode = node.parentNode;
        if (!before2) {
          if (node.nextSibling) {
            parentNode?.insertBefore(blockNode, node.nextSibling);
          } else {
            parentNode?.appendChild(blockNode);
          }
        } else {
          parentNode?.insertBefore(blockNode, node);
        }
        return blockNode;
      };
      const startsWithCaretContainer$1 = (node) => isText$9(node) && node.data[0] === ZWSP$1;
      const endsWithCaretContainer$1 = (node) => isText$9(node) && node.data[node.data.length - 1] === ZWSP$1;
      const trimBogusBr = (elm) => {
        const brs = elm.getElementsByTagName("br");
        const lastBr = brs[brs.length - 1];
        if (isBogus$1(lastBr)) {
          lastBr.parentNode?.removeChild(lastBr);
        }
      };
      const showCaretContainerBlock = (caretContainer) => {
        if (caretContainer && caretContainer.hasAttribute("data-mce-caret")) {
          trimBogusBr(caretContainer);
          caretContainer.removeAttribute("data-mce-caret");
          caretContainer.removeAttribute("data-mce-bogus");
          caretContainer.removeAttribute("style");
          caretContainer.removeAttribute("data-mce-style");
          caretContainer.removeAttribute("_moz_abspos");
          return caretContainer;
        }
        return null;
      };
      const isRangeInCaretContainerBlock = (range2) => isCaretContainerBlock$1(range2.startContainer);
      const round$2 = Math.round;
      const clone$1 = (rect) => {
        if (!rect) {
          return { left: 0, top: 0, bottom: 0, right: 0, width: 0, height: 0 };
        }
        return {
          left: round$2(rect.left),
          top: round$2(rect.top),
          bottom: round$2(rect.bottom),
          right: round$2(rect.right),
          width: round$2(rect.width),
          height: round$2(rect.height)
        };
      };
      const collapse = (rect, toStart) => {
        rect = clone$1(rect);
        if (toStart) {
          rect.right = rect.left;
        } else {
          rect.left = rect.left + rect.width;
          rect.right = rect.left;
        }
        rect.width = 0;
        return rect;
      };
      const isEqual = (rect1, rect2) => rect1.left === rect2.left && rect1.top === rect2.top && rect1.bottom === rect2.bottom && rect1.right === rect2.right;
      const isValidOverflow = (overflowY, rect1, rect2) => overflowY >= 0 && overflowY <= Math.min(rect1.height, rect2.height) / 2;
      const isAbove$1 = (rect1, rect2) => {
        const halfHeight = Math.min(rect2.height / 2, rect1.height / 2);
        if (rect1.bottom - halfHeight < rect2.top) {
          return true;
        }
        if (rect1.top > rect2.bottom) {
          return false;
        }
        return isValidOverflow(rect2.top - rect1.bottom, rect1, rect2);
      };
      const isBelow$1 = (rect1, rect2) => {
        if (rect1.top > rect2.bottom) {
          return true;
        }
        if (rect1.bottom < rect2.top) {
          return false;
        }
        return isValidOverflow(rect2.bottom - rect1.top, rect1, rect2);
      };
      const containsXY = (rect, clientX, clientY) => clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
      const boundingClientRectFromRects = (rects) => {
        return foldl(rects, (acc, rect) => {
          return acc.fold(() => Optional.some(rect), (prevRect) => {
            const left = Math.min(rect.left, prevRect.left);
            const top = Math.min(rect.top, prevRect.top);
            const right = Math.max(rect.right, prevRect.right);
            const bottom = Math.max(rect.bottom, prevRect.bottom);
            return Optional.some({
              top,
              right,
              bottom,
              left,
              width: right - left,
              height: bottom - top
            });
          });
        }, Optional.none());
      };
      const distanceToRectEdgeFromXY = (rect, x, y) => {
        const cx = Math.max(Math.min(x, rect.left + rect.width), rect.left);
        const cy = Math.max(Math.min(y, rect.top + rect.height), rect.top);
        return Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
      };
      const overlapY = (r1, r2) => Math.max(0, Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top));
      const getSelectedNode = (range2) => {
        const startContainer = range2.startContainer, startOffset = range2.startOffset;
        if (startContainer === range2.endContainer && startContainer.hasChildNodes() && range2.endOffset === startOffset + 1) {
          return startContainer.childNodes[startOffset];
        }
        return null;
      };
      const getNode$1 = (container, offset) => {
        if (isElement$7(container) && container.hasChildNodes()) {
          const childNodes = container.childNodes;
          const safeOffset = clamp$2(offset, 0, childNodes.length - 1);
          return childNodes[safeOffset];
        } else {
          return container;
        }
      };
      const getNodeUnsafe = (container, offset) => {
        if (offset < 0 && isElement$7(container) && container.hasChildNodes()) {
          return void 0;
        } else {
          return getNode$1(container, offset);
        }
      };
      const extendingChars = new RegExp("[̀-ͯ҃-҇҈-҉֑-ֽֿׁ-ׂׄ-ׇׅؐ-ًؚ-ٰٟۖ-ۜ۟-ۤۧ-۪ۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣣ-ंऺ़ु-ै्॑-ॗॢ-ॣঁ়াু-ৄ্ৗৢ-ৣਁ-ਂ਼ੁ-ੂੇ-ੈੋ-੍ੑੰ-ੱੵઁ-ં઼ુ-ૅે-ૈ્ૢ-ૣଁ଼ାିୁ-ୄ୍ୖୗୢ-ୣஂாீ்ௗఀా-ీె-ైొ-్ౕ-ౖౢ-ౣಁ಼ಿೂೆೌ-್ೕ-ೖೢ-ೣഁാു-ൄ്ൗൢ-ൣ්ාි-ුූෟัิ-ฺ็-๎ັິ-ູົ-ຼ່-ໍ༘-ཱ༹༙༵༷-ཾྀ-྄྆-྇ྍ-ྗྙ-ྼ࿆ိ-ူဲ-့္-်ွ-ှၘ-ၙၞ-ၠၱ-ၴႂႅ-ႆႍႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒ-ᝓᝲ-ᝳ឴-឵ិ-ួំ៉-៓៝᠋-᠍ᢩᤠ-ᤢᤧ-ᤨᤲ᤹-᤻ᨗ-ᨘᨛᩖᩘ-ᩞ᩠ᩢᩥ-ᩬᩳ-᩿᩼᪰-᪽᪾ᬀ-ᬃ᬴ᬶ-ᬺᬼᭂ᭫-᭳ᮀ-ᮁᮢ-ᮥᮨ-ᮩ᮫-ᮭ᯦ᯨ-ᯩᯭᯯ-ᯱᰬ-ᰳᰶ-᰷᳐-᳔᳒-᳢᳠-᳨᳭᳴᳸-᳹᷀-᷵᷼-᷿‌-‍⃐-⃜⃝-⃠⃡⃢-⃤⃥-⃰⳯-⵿⳱ⷠ-〪ⷿ-〭〮-゙〯-゚꙯꙰-꙲ꙴ-꙽ꚞ-ꚟ꛰-꛱ꠂ꠆ꠋꠥ-ꠦ꣄꣠-꣱ꤦ-꤭ꥇ-ꥑꦀ-ꦂ꦳ꦶ-ꦹꦼꧥꨩ-ꨮꨱ-ꨲꨵ-ꨶꩃꩌꩼꪰꪲ-ꪴꪷ-ꪸꪾ-꪿꫁ꫬ-ꫭ꫶ꯥꯨ꯭ﬞ︀-️︠-︯ﾞ-ﾟ]");
      const isExtendingChar = (ch) => isString(ch) && ch.charCodeAt(0) >= 768 && extendingChars.test(ch);
      const or = (...args) => {
        return (x) => {
          for (let i = 0; i < args.length; i++) {
            if (args[i](x)) {
              return true;
            }
          }
          return false;
        };
      };
      const and = (...args) => {
        return (x) => {
          for (let i = 0; i < args.length; i++) {
            if (!args[i](x)) {
              return false;
            }
          }
          return true;
        };
      };
      const isContentEditableTrue$2 = isContentEditableTrue$3;
      const isContentEditableFalse$9 = isContentEditableFalse$a;
      const isBr$5 = isBr$7;
      const isText$8 = isText$b;
      const isInvalidTextElement = matchNodeNames$1(["script", "style", "textarea"]);
      const isAtomicInline = matchNodeNames$1(["img", "input", "textarea", "hr", "iframe", "video", "audio", "object", "embed"]);
      const isTable = matchNodeNames$1(["table"]);
      const isCaretContainer$1 = isCaretContainer$2;
      const isCaretCandidate$3 = (node) => {
        if (isCaretContainer$1(node)) {
          return false;
        }
        if (isText$8(node)) {
          return !isInvalidTextElement(node.parentNode);
        }
        return isAtomicInline(node) || isBr$5(node) || isTable(node) || isNonUiContentEditableFalse(node);
      };
      const isUnselectable = (node) => isElement$7(node) && node.getAttribute("unselectable") === "true";
      const isNonUiContentEditableFalse = (node) => !isUnselectable(node) && isContentEditableFalse$9(node);
      const isInEditable = (node, root) => {
        for (let tempNode = node.parentNode; tempNode && tempNode !== root; tempNode = tempNode.parentNode) {
          if (isNonUiContentEditableFalse(tempNode)) {
            return false;
          }
          if (isContentEditableTrue$2(tempNode)) {
            return true;
          }
        }
        return true;
      };
      const isAtomicContentEditableFalse = (node) => {
        if (!isNonUiContentEditableFalse(node)) {
          return false;
        }
        return !foldl(from(node.getElementsByTagName("*")), (result, elm) => {
          return result || isContentEditableTrue$2(elm);
        }, false);
      };
      const isAtomic$1 = (node) => isAtomicInline(node) || isAtomicContentEditableFalse(node);
      const isEditableCaretCandidate$1 = (node, root) => isCaretCandidate$3(node) && isInEditable(node, root);
      const isElement$5 = isElement$7;
      const isCaretCandidate$2 = isCaretCandidate$3;
      const isBlock$3 = matchStyleValues("display", "block table");
      const isFloated = matchStyleValues("float", "left right");
      const isValidElementCaretCandidate = and(isElement$5, isCaretCandidate$2, not(isFloated));
      const isNotPre = not(matchStyleValues("white-space", "pre pre-line pre-wrap"));
      const isText$7 = isText$b;
      const isBr$4 = isBr$7;
      const nodeIndex$1 = DOMUtils.nodeIndex;
      const resolveIndex$1 = getNodeUnsafe;
      const createRange$1 = (doc) => doc ? doc.createRange() : DOMUtils.DOM.createRng();
      const isWhiteSpace$1 = (chr) => isString(chr) && /[\r\n\t ]/.test(chr);
      const isRange = (rng) => !!rng.setStart && !!rng.setEnd;
      const isHiddenWhiteSpaceRange = (range2) => {
        const container = range2.startContainer;
        const offset = range2.startOffset;
        if (isWhiteSpace$1(range2.toString()) && isNotPre(container.parentNode) && isText$b(container)) {
          const text2 = container.data;
          if (isWhiteSpace$1(text2[offset - 1]) || isWhiteSpace$1(text2[offset + 1])) {
            return true;
          }
        }
        return false;
      };
      const getBrClientRect = (brNode) => {
        const doc = brNode.ownerDocument;
        const rng = createRange$1(doc);
        const nbsp$1 = doc.createTextNode(nbsp);
        const parentNode = brNode.parentNode;
        parentNode.insertBefore(nbsp$1, brNode);
        rng.setStart(nbsp$1, 0);
        rng.setEnd(nbsp$1, 1);
        const clientRect = clone$1(rng.getBoundingClientRect());
        parentNode.removeChild(nbsp$1);
        return clientRect;
      };
      const getBoundingClientRectWebKitText = (rng) => {
        const sc = rng.startContainer;
        const ec = rng.endContainer;
        const so = rng.startOffset;
        const eo = rng.endOffset;
        if (sc === ec && isText$b(ec) && so === 0 && eo === 1) {
          const newRng = rng.cloneRange();
          newRng.setEndAfter(ec);
          return getBoundingClientRect$1(newRng);
        } else {
          return null;
        }
      };
      const isZeroRect = (r2) => r2.left === 0 && r2.right === 0 && r2.top === 0 && r2.bottom === 0;
      const getBoundingClientRect$1 = (item) => {
        let clientRect;
        const clientRects = item.getClientRects();
        if (clientRects.length > 0) {
          clientRect = clone$1(clientRects[0]);
        } else {
          clientRect = clone$1(item.getBoundingClientRect());
        }
        if (!isRange(item) && isBr$4(item) && isZeroRect(clientRect)) {
          return getBrClientRect(item);
        }
        if (isZeroRect(clientRect) && isRange(item)) {
          return getBoundingClientRectWebKitText(item) ?? clientRect;
        }
        return clientRect;
      };
      const collapseAndInflateWidth = (clientRect, toStart) => {
        const newClientRect = collapse(clientRect, toStart);
        newClientRect.width = 1;
        newClientRect.right = newClientRect.left + 1;
        return newClientRect;
      };
      const getCaretPositionClientRects = (caretPosition) => {
        const clientRects = [];
        const addUniqueAndValidRect = (clientRect) => {
          if (clientRect.height === 0) {
            return;
          }
          if (clientRects.length > 0) {
            if (isEqual(clientRect, clientRects[clientRects.length - 1])) {
              return;
            }
          }
          clientRects.push(clientRect);
        };
        const addCharacterOffset = (container2, offset2) => {
          const range2 = createRange$1(container2.ownerDocument);
          if (offset2 < container2.data.length) {
            if (isExtendingChar(container2.data[offset2])) {
              return;
            }
            if (isExtendingChar(container2.data[offset2 - 1])) {
              range2.setStart(container2, offset2);
              range2.setEnd(container2, offset2 + 1);
              if (!isHiddenWhiteSpaceRange(range2)) {
                addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range2), false));
                return;
              }
            }
          }
          if (offset2 > 0) {
            range2.setStart(container2, offset2 - 1);
            range2.setEnd(container2, offset2);
            if (!isHiddenWhiteSpaceRange(range2)) {
              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range2), false));
            }
          }
          if (offset2 < container2.data.length) {
            range2.setStart(container2, offset2);
            range2.setEnd(container2, offset2 + 1);
            if (!isHiddenWhiteSpaceRange(range2)) {
              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range2), true));
            }
          }
        };
        const container = caretPosition.container();
        const offset = caretPosition.offset();
        if (isText$7(container)) {
          addCharacterOffset(container, offset);
          return clientRects;
        }
        if (isElement$5(container)) {
          if (caretPosition.isAtEnd()) {
            const node = resolveIndex$1(container, offset);
            if (isText$7(node)) {
              addCharacterOffset(node, node.data.length);
            }
            if (isValidElementCaretCandidate(node) && !isBr$4(node)) {
              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), false));
            }
          } else {
            const node = resolveIndex$1(container, offset);
            if (isText$7(node)) {
              addCharacterOffset(node, 0);
            }
            if (isValidElementCaretCandidate(node) && caretPosition.isAtEnd()) {
              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), false));
              return clientRects;
            }
            const beforeNode = resolveIndex$1(caretPosition.container(), caretPosition.offset() - 1);
            if (isValidElementCaretCandidate(beforeNode) && !isBr$4(beforeNode)) {
              if (isBlock$3(beforeNode) || isBlock$3(node) || !isValidElementCaretCandidate(node)) {
                addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(beforeNode), false));
              }
            }
            if (isValidElementCaretCandidate(node)) {
              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), true));
            }
          }
        }
        return clientRects;
      };
      const CaretPosition = (container, offset, clientRects) => {
        const isAtStart = () => {
          if (isText$7(container)) {
            return offset === 0;
          }
          return offset === 0;
        };
        const isAtEnd = () => {
          if (isText$7(container)) {
            return offset >= container.data.length;
          }
          return offset >= container.childNodes.length;
        };
        const toRange = () => {
          const range2 = createRange$1(container.ownerDocument);
          range2.setStart(container, offset);
          range2.setEnd(container, offset);
          return range2;
        };
        const getClientRects2 = () => {
          if (!clientRects) {
            clientRects = getCaretPositionClientRects(CaretPosition(container, offset));
          }
          return clientRects;
        };
        const isVisible = () => getClientRects2().length > 0;
        const isEqual2 = (caretPosition) => caretPosition && container === caretPosition.container() && offset === caretPosition.offset();
        const getNode2 = (before2) => resolveIndex$1(container, before2 ? offset - 1 : offset);
        return {
          /**
           * Returns the container node.
           *
           * @method container
           * @return {Node} Container node.
           */
          container: constant(container),
          /**
           * Returns the offset within the container node.
           *
           * @method offset
           * @return {Number} Offset within the container node.
           */
          offset: constant(offset),
          /**
           * Returns a range out of a the caret position.
           *
           * @method toRange
           * @return {DOMRange} range for the caret position.
           */
          toRange,
          /**
           * Returns the client rects for the caret position. Might be multiple rects between
           * block elements.
           *
           * @method getClientRects
           * @return {Array} Array of client rects.
           */
          getClientRects: getClientRects2,
          /**
           * Returns true if the caret location is visible/displayed on screen.
           *
           * @method isVisible
           * @return {Boolean} true/false if the position is visible or not.
           */
          isVisible,
          /**
           * Returns true if the caret location is at the beginning of text node or container.
           *
           * @method isVisible
           * @return {Boolean} true/false if the position is at the beginning.
           */
          isAtStart,
          /**
           * Returns true if the caret location is at the end of text node or container.
           *
           * @method isVisible
           * @return {Boolean} true/false if the position is at the end.
           */
          isAtEnd,
          /**
           * Compares the caret position to another caret position. This will only compare the
           * container and offset not it's visual position.
           *
           * @method isEqual
           * @param {tinymce.caret.CaretPosition} caretPosition Caret position to compare with.
           * @return {Boolean} true if the caret positions are equal.
           */
          isEqual: isEqual2,
          /**
           * Returns the closest resolved node from a node index. That means if you have an offset after the
           * last node in a container it will return that last node.
           *
           * @method getNode
           * @return {Node} Node that is closest to the index.
           */
          getNode: getNode2
        };
      };
      CaretPosition.fromRangeStart = (range2) => CaretPosition(range2.startContainer, range2.startOffset);
      CaretPosition.fromRangeEnd = (range2) => CaretPosition(range2.endContainer, range2.endOffset);
      CaretPosition.after = (node) => CaretPosition(node.parentNode, nodeIndex$1(node) + 1);
      CaretPosition.before = (node) => CaretPosition(node.parentNode, nodeIndex$1(node));
      CaretPosition.isAbove = (pos1, pos2) => lift2(head(pos2.getClientRects()), last$2(pos1.getClientRects()), isAbove$1).getOr(false);
      CaretPosition.isBelow = (pos1, pos2) => lift2(last$2(pos2.getClientRects()), head(pos1.getClientRects()), isBelow$1).getOr(false);
      CaretPosition.isAtStart = (pos) => pos ? pos.isAtStart() : false;
      CaretPosition.isAtEnd = (pos) => pos ? pos.isAtEnd() : false;
      CaretPosition.isTextPosition = (pos) => pos ? isText$b(pos.container()) : false;
      CaretPosition.isElementPosition = (pos) => !CaretPosition.isTextPosition(pos);
      const trimEmptyTextNode$1 = (dom2, node) => {
        if (isText$b(node) && node.data.length === 0) {
          dom2.remove(node);
        }
      };
      const insertNode = (dom2, rng, node) => {
        rng.insertNode(node);
        trimEmptyTextNode$1(dom2, node.previousSibling);
        trimEmptyTextNode$1(dom2, node.nextSibling);
      };
      const insertFragment = (dom2, rng, frag) => {
        const firstChild2 = Optional.from(frag.firstChild);
        const lastChild2 = Optional.from(frag.lastChild);
        rng.insertNode(frag);
        firstChild2.each((child2) => trimEmptyTextNode$1(dom2, child2.previousSibling));
        lastChild2.each((child2) => trimEmptyTextNode$1(dom2, child2.nextSibling));
      };
      const rangeInsertNode = (dom2, rng, node) => {
        if (isDocumentFragment(node)) {
          insertFragment(dom2, rng, node);
        } else {
          insertNode(dom2, rng, node);
        }
      };
      const isText$6 = isText$b;
      const isBogus = isBogus$1;
      const nodeIndex = DOMUtils.nodeIndex;
      const normalizedParent = (node) => {
        const parentNode = node.parentNode;
        if (isBogus(parentNode)) {
          return normalizedParent(parentNode);
        }
        return parentNode;
      };
      const getChildNodes = (node) => {
        if (!node) {
          return [];
        }
        return reduce(node.childNodes, (result, node2) => {
          if (isBogus(node2) && node2.nodeName !== "BR") {
            result = result.concat(getChildNodes(node2));
          } else {
            result.push(node2);
          }
          return result;
        }, []);
      };
      const normalizedTextOffset = (node, offset) => {
        let tempNode = node;
        while (tempNode = tempNode.previousSibling) {
          if (!isText$6(tempNode)) {
            break;
          }
          offset += tempNode.data.length;
        }
        return offset;
      };
      const equal = (a) => (b) => a === b;
      const normalizedNodeIndex = (node) => {
        let nodes, index;
        nodes = getChildNodes(normalizedParent(node));
        index = findIndex$1(nodes, equal(node), node);
        nodes = nodes.slice(0, index + 1);
        const numTextFragments = reduce(nodes, (result, node2, i) => {
          if (isText$6(node2) && isText$6(nodes[i - 1])) {
            result++;
          }
          return result;
        }, 0);
        nodes = filter$3(nodes, matchNodeNames$1([node.nodeName]));
        index = findIndex$1(nodes, equal(node), node);
        return index - numTextFragments;
      };
      const createPathItem = (node) => {
        const name2 = isText$6(node) ? "text()" : node.nodeName.toLowerCase();
        return name2 + "[" + normalizedNodeIndex(node) + "]";
      };
      const parentsUntil$1 = (root, node, predicate) => {
        const parents2 = [];
        for (let tempNode = node.parentNode; tempNode && tempNode !== root; tempNode = tempNode.parentNode) {
          if (predicate && predicate(tempNode)) {
            break;
          }
          parents2.push(tempNode);
        }
        return parents2;
      };
      const create$9 = (root, caretPosition) => {
        let path2 = [];
        let container = caretPosition.container();
        let offset = caretPosition.offset();
        let outputOffset;
        if (isText$6(container)) {
          outputOffset = normalizedTextOffset(container, offset);
        } else {
          const childNodes = container.childNodes;
          if (offset >= childNodes.length) {
            outputOffset = "after";
            offset = childNodes.length - 1;
          } else {
            outputOffset = "before";
          }
          container = childNodes[offset];
        }
        path2.push(createPathItem(container));
        let parents2 = parentsUntil$1(root, container);
        parents2 = filter$3(parents2, not(isBogus$1));
        path2 = path2.concat(map$1(parents2, (node) => {
          return createPathItem(node);
        }));
        return path2.reverse().join("/") + "," + outputOffset;
      };
      const resolvePathItem = (node, name2, index) => {
        let nodes = getChildNodes(node);
        nodes = filter$3(nodes, (node2, index2) => {
          return !isText$6(node2) || !isText$6(nodes[index2 - 1]);
        });
        nodes = filter$3(nodes, matchNodeNames$1([name2]));
        return nodes[index];
      };
      const findTextPosition = (container, offset) => {
        let node = container;
        let targetOffset = 0;
        while (isText$6(node)) {
          const dataLen = node.data.length;
          if (offset >= targetOffset && offset <= targetOffset + dataLen) {
            container = node;
            offset = offset - targetOffset;
            break;
          }
          if (!isText$6(node.nextSibling)) {
            container = node;
            offset = dataLen;
            break;
          }
          targetOffset += dataLen;
          node = node.nextSibling;
        }
        if (isText$6(container) && offset > container.data.length) {
          offset = container.data.length;
        }
        return CaretPosition(container, offset);
      };
      const resolve$1 = (root, path2) => {
        if (!path2) {
          return null;
        }
        const parts = path2.split(",");
        const paths = parts[0].split("/");
        const offset = parts.length > 1 ? parts[1] : "before";
        const container = reduce(paths, (result, value2) => {
          const match2 = /([\w\-\(\)]+)\[([0-9]+)\]/.exec(value2);
          if (!match2) {
            return null;
          }
          if (match2[1] === "text()") {
            match2[1] = "#text";
          }
          return resolvePathItem(result, match2[1], parseInt(match2[2], 10));
        }, root);
        if (!container) {
          return null;
        }
        if (!isText$6(container) && container.parentNode) {
          let nodeOffset;
          if (offset === "after") {
            nodeOffset = nodeIndex(container) + 1;
          } else {
            nodeOffset = nodeIndex(container);
          }
          return CaretPosition(container.parentNode, nodeOffset);
        }
        return findTextPosition(container, parseInt(offset, 10));
      };
      const isContentEditableFalse$8 = isContentEditableFalse$a;
      const getNormalizedTextOffset$1 = (trim2, container, offset) => {
        let trimmedOffset = trim2(container.data.slice(0, offset)).length;
        for (let node = container.previousSibling; node && isText$b(node); node = node.previousSibling) {
          trimmedOffset += trim2(node.data).length;
        }
        return trimmedOffset;
      };
      const getPoint = (dom2, trim2, normalized, rng, start2) => {
        const container = start2 ? rng.startContainer : rng.endContainer;
        let offset = start2 ? rng.startOffset : rng.endOffset;
        const point2 = [];
        const root = dom2.getRoot();
        if (isText$b(container)) {
          point2.push(normalized ? getNormalizedTextOffset$1(trim2, container, offset) : offset);
        } else {
          let after2 = 0;
          const childNodes = container.childNodes;
          if (offset >= childNodes.length && childNodes.length) {
            after2 = 1;
            offset = Math.max(0, childNodes.length - 1);
          }
          point2.push(dom2.nodeIndex(childNodes[offset], normalized) + after2);
        }
        for (let node = container; node && node !== root; node = node.parentNode) {
          point2.push(dom2.nodeIndex(node, normalized));
        }
        return point2;
      };
      const getLocation = (trim2, selection, normalized, rng) => {
        const dom2 = selection.dom;
        const start2 = getPoint(dom2, trim2, normalized, rng, true);
        const forward = selection.isForward();
        const fakeCaret = isRangeInCaretContainerBlock(rng) ? { isFakeCaret: true } : {};
        if (!selection.isCollapsed()) {
          const end2 = getPoint(dom2, trim2, normalized, rng, false);
          return { start: start2, end: end2, forward, ...fakeCaret };
        } else {
          return { start: start2, forward, ...fakeCaret };
        }
      };
      const findIndex = (dom2, name2, element) => {
        let count2 = 0;
        Tools.each(dom2.select(name2), (node) => {
          if (node.getAttribute("data-mce-bogus") === "all") {
            return;
          } else if (node === element) {
            return false;
          } else {
            count2++;
            return;
          }
        });
        return count2;
      };
      const moveEndPoint$1 = (rng, start2) => {
        let container = start2 ? rng.startContainer : rng.endContainer;
        let offset = start2 ? rng.startOffset : rng.endOffset;
        if (isElement$7(container) && container.nodeName === "TR") {
          const childNodes = container.childNodes;
          container = childNodes[Math.min(start2 ? offset : offset - 1, childNodes.length - 1)];
          if (container) {
            offset = start2 ? 0 : container.childNodes.length;
            if (start2) {
              rng.setStart(container, offset);
            } else {
              rng.setEnd(container, offset);
            }
          }
        }
      };
      const normalizeTableCellSelection = (rng) => {
        moveEndPoint$1(rng, true);
        moveEndPoint$1(rng, false);
        return rng;
      };
      const findSibling = (node, offset) => {
        if (isElement$7(node)) {
          node = getNode$1(node, offset);
          if (isContentEditableFalse$8(node)) {
            return node;
          }
        }
        if (isCaretContainer$2(node)) {
          if (isText$b(node) && isCaretContainerBlock$1(node)) {
            node = node.parentNode;
          }
          let sibling2 = node.previousSibling;
          if (isContentEditableFalse$8(sibling2)) {
            return sibling2;
          }
          sibling2 = node.nextSibling;
          if (isContentEditableFalse$8(sibling2)) {
            return sibling2;
          }
        }
        return void 0;
      };
      const findAdjacentContentEditableFalseElm = (rng) => {
        return findSibling(rng.startContainer, rng.startOffset) || findSibling(rng.endContainer, rng.endOffset);
      };
      const getOffsetBookmark = (trim2, normalized, selection) => {
        const element = selection.getNode();
        const rng = selection.getRng();
        if (element.nodeName === "IMG" || isContentEditableFalse$8(element)) {
          const name2 = element.nodeName;
          return { name: name2, index: findIndex(selection.dom, name2, element) };
        }
        const sibling2 = findAdjacentContentEditableFalseElm(rng);
        if (sibling2) {
          const name2 = sibling2.tagName;
          return { name: name2, index: findIndex(selection.dom, name2, sibling2) };
        }
        return getLocation(trim2, selection, normalized, rng);
      };
      const getCaretBookmark = (selection) => {
        const rng = selection.getRng();
        return {
          start: create$9(selection.dom.getRoot(), CaretPosition.fromRangeStart(rng)),
          end: create$9(selection.dom.getRoot(), CaretPosition.fromRangeEnd(rng)),
          forward: selection.isForward()
        };
      };
      const getRangeBookmark = (selection) => {
        return { rng: selection.getRng(), forward: selection.isForward() };
      };
      const createBookmarkSpan = (dom2, id, filled) => {
        const args = { "data-mce-type": "bookmark", id, "style": "overflow:hidden;line-height:0px" };
        return filled ? dom2.create("span", args, "&#xFEFF;") : dom2.create("span", args);
      };
      const getPersistentBookmark = (selection, filled) => {
        const dom2 = selection.dom;
        let rng = selection.getRng();
        const id = dom2.uniqueId();
        const collapsed = selection.isCollapsed();
        const element = selection.getNode();
        const name2 = element.nodeName;
        const forward = selection.isForward();
        if (name2 === "IMG") {
          return { name: name2, index: findIndex(dom2, name2, element) };
        }
        const rng2 = normalizeTableCellSelection(rng.cloneRange());
        if (!collapsed) {
          rng2.collapse(false);
          const endBookmarkNode = createBookmarkSpan(dom2, id + "_end", filled);
          rangeInsertNode(dom2, rng2, endBookmarkNode);
        }
        rng = normalizeTableCellSelection(rng);
        rng.collapse(true);
        const startBookmarkNode = createBookmarkSpan(dom2, id + "_start", filled);
        rangeInsertNode(dom2, rng, startBookmarkNode);
        selection.moveToBookmark({ id, keep: true, forward });
        return { id, forward };
      };
      const getBookmark$2 = (selection, type2, normalized = false) => {
        if (type2 === 2) {
          return getOffsetBookmark(trim$2, normalized, selection);
        } else if (type2 === 3) {
          return getCaretBookmark(selection);
        } else if (type2) {
          return getRangeBookmark(selection);
        } else {
          return getPersistentBookmark(selection, false);
        }
      };
      const getUndoBookmark = curry(getOffsetBookmark, identity, true);
      const isForwards = (direction) => direction === 1;
      const isBackwards = (direction) => direction === -1;
      const isInlinePattern = (pattern) => pattern.type === "inline-command" || pattern.type === "inline-format";
      const isBlockPattern = (pattern) => pattern.type === "block-command" || pattern.type === "block-format";
      const hasBlockTrigger = (pattern, trigger) => (pattern.type === "block-command" || pattern.type === "block-format") && pattern.trigger === trigger;
      const normalizePattern = (pattern) => {
        const err = (message) => Result.error({ message, pattern });
        const formatOrCmd = (name2, onFormat, onCommand) => {
          if (pattern.format !== void 0) {
            let formats;
            if (isArray$1(pattern.format)) {
              if (!forall(pattern.format, isString)) {
                return err(name2 + " pattern has non-string items in the `format` array");
              }
              formats = pattern.format;
            } else if (isString(pattern.format)) {
              formats = [pattern.format];
            } else {
              return err(name2 + " pattern has non-string `format` parameter");
            }
            return Result.value(onFormat(formats));
          } else if (pattern.cmd !== void 0) {
            if (!isString(pattern.cmd)) {
              return err(name2 + " pattern has non-string `cmd` parameter");
            }
            return Result.value(onCommand(pattern.cmd, pattern.value));
          } else {
            return err(name2 + " pattern is missing both `format` and `cmd` parameters");
          }
        };
        if (!isObject(pattern)) {
          return err("Raw pattern is not an object");
        }
        if (!isString(pattern.start)) {
          return err("Raw pattern is missing `start` parameter");
        }
        if (pattern.end !== void 0) {
          if (!isString(pattern.end)) {
            return err("Inline pattern has non-string `end` parameter");
          }
          if (pattern.start.length === 0 && pattern.end.length === 0) {
            return err("Inline pattern has empty `start` and `end` parameters");
          }
          let start2 = pattern.start;
          let end2 = pattern.end;
          if (end2.length === 0) {
            end2 = start2;
            start2 = "";
          }
          return formatOrCmd("Inline", (format) => ({ type: "inline-format", start: start2, end: end2, format }), (cmd, value2) => ({ type: "inline-command", start: start2, end: end2, cmd, value: value2 }));
        } else if (pattern.replacement !== void 0) {
          if (!isString(pattern.replacement)) {
            return err("Replacement pattern has non-string `replacement` parameter");
          }
          if (pattern.start.length === 0) {
            return err("Replacement pattern has empty `start` parameter");
          }
          return Result.value({
            type: "inline-command",
            start: "",
            end: pattern.start,
            cmd: "mceInsertContent",
            value: pattern.replacement
          });
        } else {
          const trigger = pattern.trigger ?? "space";
          if (pattern.start.length === 0) {
            return err("Block pattern has empty `start` parameter");
          }
          return formatOrCmd("Block", (formats) => ({
            type: "block-format",
            start: pattern.start,
            format: formats[0],
            trigger
          }), (command, commandValue) => ({
            type: "block-command",
            start: pattern.start,
            cmd: command,
            value: commandValue,
            trigger
          }));
        }
      };
      const getBlockPatterns = (patterns) => filter$5(patterns, isBlockPattern);
      const getInlinePatterns = (patterns) => filter$5(patterns, isInlinePattern);
      const createPatternSet = (patterns, dynamicPatternsLookup) => ({
        inlinePatterns: getInlinePatterns(patterns),
        blockPatterns: getBlockPatterns(patterns),
        dynamicPatternsLookup
      });
      const filterByTrigger = (patterns, trigger) => {
        return {
          ...patterns,
          blockPatterns: filter$5(patterns.blockPatterns, (pattern) => hasBlockTrigger(pattern, trigger))
        };
      };
      const fromRawPatterns = (patterns) => {
        const normalized = partition$1(map$3(patterns, normalizePattern));
        each$e(normalized.errors, (err) => console.error(err.message, err.pattern));
        return normalized.values;
      };
      const fromRawPatternsLookup = (lookupFn) => {
        return (ctx) => {
          const rawPatterns = lookupFn(ctx);
          return fromRawPatterns(rawPatterns);
        };
      };
      const firePreProcess = (editor, args) => editor.dispatch("PreProcess", args);
      const firePostProcess = (editor, args) => editor.dispatch("PostProcess", args);
      const fireRemove = (editor) => {
        editor.dispatch("remove");
      };
      const fireDetach = (editor) => {
        editor.dispatch("detach");
      };
      const fireSwitchMode = (editor, mode) => {
        editor.dispatch("SwitchMode", { mode });
      };
      const fireObjectResizeStart = (editor, target, width, height, origin) => {
        editor.dispatch("ObjectResizeStart", { target, width, height, origin });
      };
      const fireObjectResized = (editor, target, width, height, origin) => {
        editor.dispatch("ObjectResized", { target, width, height, origin });
      };
      const firePreInit = (editor) => {
        editor.dispatch("PreInit");
      };
      const firePostRender = (editor) => {
        editor.dispatch("PostRender");
      };
      const fireInit = (editor) => {
        editor.dispatch("Init");
      };
      const firePlaceholderToggle = (editor, state) => {
        editor.dispatch("PlaceholderToggle", { state });
      };
      const fireError = (editor, errorType, error2) => {
        editor.dispatch(errorType, error2);
      };
      const fireFormatApply = (editor, format, node, vars) => {
        editor.dispatch("FormatApply", { format, node, vars });
      };
      const fireFormatRemove = (editor, format, node, vars) => {
        editor.dispatch("FormatRemove", { format, node, vars });
      };
      const fireBeforeSetContent = (editor, args) => editor.dispatch("BeforeSetContent", args);
      const fireSetContent = (editor, args) => editor.dispatch("SetContent", args);
      const fireBeforeGetContent = (editor, args) => editor.dispatch("BeforeGetContent", args);
      const fireGetContent = (editor, args) => editor.dispatch("GetContent", args);
      const fireAutocompleterStart = (editor, args) => {
        editor.dispatch("AutocompleterStart", args);
      };
      const fireAutocompleterUpdate = (editor, args) => {
        editor.dispatch("AutocompleterUpdate", args);
      };
      const fireAutocompleterUpdateActiveRange = (editor, args) => {
        editor.dispatch("AutocompleterUpdateActiveRange", args);
      };
      const fireAutocompleterEnd = (editor) => {
        editor.dispatch("AutocompleterEnd");
      };
      const firePastePreProcess = (editor, html2, internal) => editor.dispatch("PastePreProcess", { content: html2, internal });
      const firePastePostProcess = (editor, node, internal) => editor.dispatch("PastePostProcess", { node, internal });
      const firePastePlainTextToggle = (editor, state) => editor.dispatch("PastePlainTextToggle", { state });
      const fireEditableRootStateChange = (editor, state) => editor.dispatch("EditableRootStateChange", { state });
      const fireDisabledStateChange = (editor, state) => editor.dispatch("DisabledStateChange", { state });
      const fireCloseTooltips = (editor) => editor.dispatch("CloseActiveTooltips");
      const deviceDetection$1 = detect$1().deviceType;
      const isTouch = deviceDetection$1.isTouch();
      const DOM$d = DOMUtils.DOM;
      const getHash = (value2) => {
        const items = value2.indexOf("=") > 0 ? value2.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value2.split(",");
        return foldl(items, (output, item) => {
          const arr = item.split("=");
          const key = arr[0];
          const val = arr.length > 1 ? arr[1] : key;
          output[trim$4(key)] = trim$4(val);
          return output;
        }, {});
      };
      const isRegExp = (x) => is$5(x, RegExp);
      const option$1 = (name2) => (editor) => editor.options.get(name2);
      const stringOrObjectProcessor = (value2) => isString(value2) || isObject(value2);
      const bodyOptionProcessor = (editor, defaultValue = "") => (value2) => {
        const valid = isString(value2);
        if (valid) {
          if (value2.indexOf("=") !== -1) {
            const bodyObj = getHash(value2);
            return { value: get$a(bodyObj, editor.id).getOr(defaultValue), valid };
          } else {
            return { value: value2, valid };
          }
        } else {
          return { valid: false, message: "Must be a string." };
        }
      };
      const register$7 = (editor) => {
        const registerOption = editor.options.register;
        registerOption("id", {
          processor: "string",
          default: editor.id
        });
        registerOption("selector", {
          processor: "string"
        });
        registerOption("target", {
          processor: "object"
        });
        registerOption("suffix", {
          processor: "string"
        });
        registerOption("cache_suffix", {
          processor: "string"
        });
        registerOption("base_url", {
          processor: "string"
        });
        registerOption("referrer_policy", {
          processor: "string",
          default: ""
        });
        registerOption("crossorigin", {
          processor: "function",
          default: constant(void 0)
        });
        registerOption("language_load", {
          processor: "boolean",
          default: true
        });
        registerOption("inline", {
          processor: "boolean",
          default: false
        });
        registerOption("iframe_attrs", {
          processor: "object",
          default: {}
        });
        registerOption("doctype", {
          processor: "string",
          default: "<!DOCTYPE html>"
        });
        registerOption("document_base_url", {
          processor: "string",
          default: editor.editorManager.documentBaseURL
        });
        registerOption("body_id", {
          processor: bodyOptionProcessor(editor, "tinymce"),
          default: "tinymce"
        });
        registerOption("body_class", {
          processor: bodyOptionProcessor(editor),
          default: ""
        });
        registerOption("content_security_policy", {
          processor: "string",
          default: ""
        });
        registerOption("br_in_pre", {
          processor: "boolean",
          default: true
        });
        registerOption("forced_root_block", {
          processor: (value2) => {
            const valid = isString(value2) && isNotEmpty(value2);
            if (valid) {
              return { value: value2, valid };
            } else {
              return { valid: false, message: "Must be a non-empty string." };
            }
          },
          default: "p"
        });
        registerOption("forced_root_block_attrs", {
          processor: "object",
          default: {}
        });
        registerOption("newline_behavior", {
          processor: (value2) => {
            const valid = contains$2(["block", "linebreak", "invert", "default"], value2);
            return valid ? { value: value2, valid } : { valid: false, message: "Must be one of: block, linebreak, invert or default." };
          },
          default: "default"
        });
        registerOption("br_newline_selector", {
          processor: "string",
          default: ".mce-toc h2,figcaption,caption"
        });
        registerOption("no_newline_selector", {
          processor: "string",
          default: ""
        });
        registerOption("keep_styles", {
          processor: "boolean",
          default: true
        });
        registerOption("end_container_on_empty_block", {
          processor: (value2) => {
            if (isBoolean(value2)) {
              return { valid: true, value: value2 };
            } else if (isString(value2)) {
              return { valid: true, value: value2 };
            } else {
              return { valid: false, message: "Must be boolean or a string" };
            }
          },
          default: "blockquote"
        });
        registerOption("font_size_style_values", {
          processor: "string",
          default: "xx-small,x-small,small,medium,large,x-large,xx-large"
        });
        registerOption("font_size_legacy_values", {
          processor: "string",
          // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
          default: "xx-small,small,medium,large,x-large,xx-large,300%"
        });
        registerOption("font_size_classes", {
          processor: "string",
          default: ""
        });
        registerOption("automatic_uploads", {
          processor: "boolean",
          default: true
        });
        registerOption("images_reuse_filename", {
          processor: "boolean",
          default: false
        });
        registerOption("images_replace_blob_uris", {
          processor: "boolean",
          default: true
        });
        registerOption("icons", {
          processor: "string",
          default: ""
        });
        registerOption("icons_url", {
          processor: "string",
          default: ""
        });
        registerOption("images_upload_url", {
          processor: "string",
          default: ""
        });
        registerOption("images_upload_base_path", {
          processor: "string",
          default: ""
        });
        registerOption("images_upload_credentials", {
          processor: "boolean",
          default: false
        });
        registerOption("images_upload_handler", {
          processor: "function"
        });
        registerOption("language", {
          processor: "string",
          default: "en"
        });
        registerOption("language_url", {
          processor: "string",
          default: ""
        });
        registerOption("entity_encoding", {
          processor: "string",
          default: "named"
        });
        registerOption("indent", {
          processor: "boolean",
          default: true
        });
        registerOption("indent_before", {
          processor: "string",
          default: "p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,details,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist"
        });
        registerOption("indent_after", {
          processor: "string",
          default: "p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,details,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist"
        });
        registerOption("indent_use_margin", {
          processor: "boolean",
          default: false
        });
        registerOption("indentation", {
          processor: "string",
          default: "40px"
        });
        registerOption("content_css", {
          processor: (value2) => {
            const valid = value2 === false || isString(value2) || isArrayOf(value2, isString);
            if (valid) {
              if (isString(value2)) {
                return { value: map$3(value2.split(","), trim$4), valid };
              } else if (isArray$1(value2)) {
                return { value: value2, valid };
              } else if (value2 === false) {
                return { value: [], valid };
              } else {
                return { value: value2, valid };
              }
            } else {
              return { valid: false, message: "Must be false, a string or an array of strings." };
            }
          },
          default: isInline$2(editor) ? [] : ["default"]
        });
        registerOption("content_style", {
          processor: "string"
        });
        registerOption("content_css_cors", {
          processor: "boolean",
          default: false
        });
        registerOption("font_css", {
          processor: (value2) => {
            const valid = isString(value2) || isArrayOf(value2, isString);
            if (valid) {
              const newValue = isArray$1(value2) ? value2 : map$3(value2.split(","), trim$4);
              return { value: newValue, valid };
            } else {
              return { valid: false, message: "Must be a string or an array of strings." };
            }
          },
          default: []
        });
        registerOption("extended_mathml_attributes", {
          processor: "string[]"
        });
        registerOption("extended_mathml_elements", {
          processor: "string[]"
        });
        registerOption("inline_boundaries", {
          processor: "boolean",
          default: true
        });
        registerOption("inline_boundaries_selector", {
          processor: "string",
          default: "a[href],code,span.mce-annotation"
        });
        registerOption("object_resizing", {
          processor: (value2) => {
            const valid = isBoolean(value2) || isString(value2);
            if (valid) {
              if (value2 === false || deviceDetection$1.isiPhone() || deviceDetection$1.isiPad()) {
                return { value: "", valid };
              } else {
                return { value: value2 === true ? "table,img,figure.image,div,video,iframe" : value2, valid };
              }
            } else {
              return { valid: false, message: "Must be boolean or a string" };
            }
          },
          // No nice way to do object resizing on touch devices at this stage
          default: !isTouch
        });
        registerOption("resize_img_proportional", {
          processor: "boolean",
          default: true
        });
        registerOption("event_root", {
          processor: "string"
        });
        registerOption("service_message", {
          processor: "string"
        });
        registerOption("onboarding", {
          processor: "boolean",
          default: true
        });
        registerOption("tiny_cloud_entry_url", {
          processor: "string"
        });
        registerOption("theme", {
          processor: (value2) => value2 === false || isString(value2) || isFunction(value2),
          default: "silver"
        });
        registerOption("theme_url", {
          processor: "string"
        });
        registerOption("formats", {
          processor: "object"
        });
        registerOption("format_empty_lines", {
          processor: "boolean",
          default: false
        });
        registerOption("format_noneditable_selector", {
          processor: "string",
          default: ""
        });
        registerOption("preview_styles", {
          processor: (value2) => {
            const valid = value2 === false || isString(value2);
            if (valid) {
              return { value: value2 === false ? "" : value2, valid };
            } else {
              return { valid: false, message: "Must be false or a string" };
            }
          },
          default: "font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow"
        });
        registerOption("custom_ui_selector", {
          processor: "string",
          default: ""
        });
        registerOption("hidden_input", {
          processor: "boolean",
          default: true
        });
        registerOption("submit_patch", {
          processor: "boolean",
          default: true
        });
        registerOption("encoding", {
          processor: "string"
        });
        registerOption("add_form_submit_trigger", {
          processor: "boolean",
          default: true
        });
        registerOption("add_unload_trigger", {
          processor: "boolean",
          default: true
        });
        registerOption("custom_undo_redo_levels", {
          processor: "number",
          default: 0
        });
        registerOption("disable_nodechange", {
          processor: "boolean",
          default: false
        });
        registerOption("disabled", {
          processor: (value2) => {
            if (isBoolean(value2)) {
              if (editor.initialized && isDisabled$1(editor) !== value2) {
                Promise.resolve().then(() => {
                  fireDisabledStateChange(editor, value2);
                });
              }
              return { valid: true, value: value2 };
            }
            return { valid: false, message: "The value must be a boolean." };
          },
          default: false
        });
        registerOption("readonly", {
          processor: "boolean",
          default: false
        });
        registerOption("editable_root", {
          processor: "boolean",
          default: true
        });
        registerOption("plugins", {
          processor: "string[]",
          default: []
        });
        registerOption("external_plugins", {
          processor: "object"
        });
        registerOption("forced_plugins", {
          processor: "string[]"
        });
        registerOption("model", {
          processor: "string",
          default: editor.hasPlugin("rtc") ? "plugin" : "dom"
        });
        registerOption("model_url", {
          processor: "string"
        });
        registerOption("block_unsupported_drop", {
          processor: "boolean",
          default: true
        });
        registerOption("visual", {
          processor: "boolean",
          default: true
        });
        registerOption("visual_table_class", {
          processor: "string",
          default: "mce-item-table"
        });
        registerOption("visual_anchor_class", {
          processor: "string",
          default: "mce-item-anchor"
        });
        registerOption("iframe_aria_text", {
          processor: "string",
          default: "Rich Text Area".concat(editor.hasPlugin("help") ? ". Press ALT-0 for help." : "")
        });
        registerOption("setup", {
          processor: "function"
        });
        registerOption("init_instance_callback", {
          processor: "function"
        });
        registerOption("url_converter", {
          processor: "function",
          // Note: Don't bind here, as the binding is handled via the `url_converter_scope`
          // eslint-disable-next-line @typescript-eslint/unbound-method
          default: editor.convertURL
        });
        registerOption("url_converter_scope", {
          processor: "object",
          default: editor
        });
        registerOption("urlconverter_callback", {
          processor: "function"
        });
        registerOption("allow_conditional_comments", {
          processor: "boolean",
          default: false
        });
        registerOption("allow_html_data_urls", {
          processor: "boolean",
          default: false
        });
        registerOption("allow_svg_data_urls", {
          processor: "boolean"
        });
        registerOption("allow_html_in_named_anchor", {
          processor: "boolean",
          default: false
        });
        registerOption("allow_html_in_comments", {
          processor: "boolean",
          default: false
        });
        registerOption("allow_script_urls", {
          processor: "boolean",
          default: false
        });
        registerOption("allow_unsafe_link_target", {
          processor: "boolean",
          default: false
        });
        registerOption("allow_mathml_annotation_encodings", {
          processor: (value2) => {
            const valid = isArrayOf(value2, isString);
            return valid ? { value: value2, valid } : { valid: false, message: "Must be an array of strings." };
          },
          default: []
        });
        registerOption("convert_fonts_to_spans", {
          processor: "boolean",
          default: true,
          deprecated: true
        });
        registerOption("fix_list_elements", {
          processor: "boolean",
          default: false
        });
        registerOption("preserve_cdata", {
          processor: "boolean",
          default: false
        });
        registerOption("remove_trailing_brs", {
          processor: "boolean",
          default: true
        });
        registerOption("pad_empty_with_br", {
          processor: "boolean",
          default: false
        });
        registerOption("inline_styles", {
          processor: "boolean",
          default: true,
          deprecated: true
        });
        registerOption("element_format", {
          processor: "string",
          default: "html"
        });
        registerOption("entities", {
          processor: "string"
        });
        registerOption("schema", {
          processor: "string",
          default: "html5"
        });
        registerOption("convert_urls", {
          processor: "boolean",
          default: true
        });
        registerOption("relative_urls", {
          processor: "boolean",
          default: true
        });
        registerOption("remove_script_host", {
          processor: "boolean",
          default: true
        });
        registerOption("custom_elements", {
          processor: stringOrObjectProcessor
        });
        registerOption("extended_valid_elements", {
          processor: "string"
        });
        registerOption("invalid_elements", {
          processor: "string"
        });
        registerOption("invalid_styles", {
          processor: stringOrObjectProcessor
        });
        registerOption("valid_children", {
          processor: "string"
        });
        registerOption("valid_classes", {
          processor: stringOrObjectProcessor
        });
        registerOption("valid_elements", {
          processor: "string"
        });
        registerOption("valid_styles", {
          processor: stringOrObjectProcessor
        });
        registerOption("verify_html", {
          processor: "boolean",
          default: true
        });
        registerOption("auto_focus", {
          processor: (value2) => isString(value2) || value2 === true
        });
        registerOption("browser_spellcheck", {
          processor: "boolean",
          default: false
        });
        registerOption("protect", {
          processor: "array"
        });
        registerOption("images_file_types", {
          processor: "string",
          default: "jpeg,jpg,jpe,jfi,jif,jfif,png,gif,bmp,webp"
        });
        registerOption("deprecation_warnings", {
          processor: "boolean",
          default: true
        });
        registerOption("a11y_advanced_options", {
          processor: "boolean",
          default: false
        });
        registerOption("api_key", {
          processor: "string"
        });
        registerOption("license_key", {
          processor: "string"
        });
        registerOption("paste_block_drop", {
          processor: "boolean",
          default: false
        });
        registerOption("paste_data_images", {
          processor: "boolean",
          default: true
        });
        registerOption("paste_preprocess", {
          processor: "function"
        });
        registerOption("paste_postprocess", {
          processor: "function"
        });
        registerOption("paste_webkit_styles", {
          processor: "string",
          default: "none"
        });
        registerOption("paste_remove_styles_if_webkit", {
          processor: "boolean",
          default: true
        });
        registerOption("paste_merge_formats", {
          processor: "boolean",
          default: true
        });
        registerOption("smart_paste", {
          processor: "boolean",
          default: true
        });
        registerOption("paste_as_text", {
          processor: "boolean",
          default: false
        });
        registerOption("paste_tab_spaces", {
          processor: "number",
          default: 4
        });
        registerOption("text_patterns", {
          processor: (value2) => {
            if (isArrayOf(value2, isObject) || value2 === false) {
              const patterns = value2 === false ? [] : value2;
              return { value: fromRawPatterns(patterns), valid: true };
            } else {
              return { valid: false, message: "Must be an array of objects or false." };
            }
          },
          default: [
            { start: "*", end: "*", format: "italic" },
            { start: "**", end: "**", format: "bold" },
            { start: "#", format: "h1", trigger: "space" },
            { start: "##", format: "h2", trigger: "space" },
            { start: "###", format: "h3", trigger: "space" },
            { start: "####", format: "h4", trigger: "space" },
            { start: "#####", format: "h5", trigger: "space" },
            { start: "######", format: "h6", trigger: "space" },
            { start: "1.", cmd: "InsertOrderedList", trigger: "space" },
            { start: "*", cmd: "InsertUnorderedList", trigger: "space" },
            { start: "-", cmd: "InsertUnorderedList", trigger: "space" },
            { start: ">", cmd: "mceBlockQuote", trigger: "space" },
            { start: "---", cmd: "InsertHorizontalRule", trigger: "space" }
          ]
        });
        registerOption("text_patterns_lookup", {
          processor: (value2) => {
            if (isFunction(value2)) {
              return {
                value: fromRawPatternsLookup(value2),
                valid: true
              };
            } else {
              return { valid: false, message: "Must be a single function" };
            }
          },
          default: (_ctx) => []
        });
        registerOption("noneditable_class", {
          processor: "string",
          default: "mceNonEditable"
        });
        registerOption("editable_class", {
          processor: "string",
          default: "mceEditable"
        });
        registerOption("noneditable_regexp", {
          processor: (value2) => {
            if (isArrayOf(value2, isRegExp)) {
              return { value: value2, valid: true };
            } else if (isRegExp(value2)) {
              return { value: [value2], valid: true };
            } else {
              return { valid: false, message: "Must be a RegExp or an array of RegExp." };
            }
          },
          default: []
        });
        registerOption("table_tab_navigation", {
          processor: "boolean",
          default: true
        });
        registerOption("highlight_on_focus", {
          processor: "boolean",
          default: true
        });
        registerOption("xss_sanitization", {
          processor: "boolean",
          default: true
        });
        registerOption("details_initial_state", {
          processor: (value2) => {
            const valid = contains$2(["inherited", "collapsed", "expanded"], value2);
            return valid ? { value: value2, valid } : { valid: false, message: "Must be one of: inherited, collapsed, or expanded." };
          },
          default: "inherited"
        });
        registerOption("details_serialized_state", {
          processor: (value2) => {
            const valid = contains$2(["inherited", "collapsed", "expanded"], value2);
            return valid ? { value: value2, valid } : { valid: false, message: "Must be one of: inherited, collapsed, or expanded." };
          },
          default: "inherited"
        });
        registerOption("init_content_sync", {
          processor: "boolean",
          default: false
        });
        registerOption("newdocument_content", {
          processor: "string",
          default: ""
        });
        registerOption("sandbox_iframes", {
          processor: "boolean",
          default: true
        });
        registerOption("sandbox_iframes_exclusions", {
          processor: "string[]",
          default: [
            "youtube.com",
            "youtu.be",
            "vimeo.com",
            "player.vimeo.com",
            "dailymotion.com",
            "embed.music.apple.com",
            "open.spotify.com",
            "giphy.com",
            "dai.ly",
            "codepen.io"
          ]
        });
        registerOption("convert_unsafe_embeds", {
          processor: "boolean",
          default: true
        });
        registerOption("user_id", {
          processor: "string",
          default: "Anonymous"
        });
        registerOption("fetch_users", {
          processor: (value2) => {
            if (value2 === void 0) {
              return { valid: true, value: void 0 };
            }
            if (isFunction(value2)) {
              return { valid: true, value: value2 };
            }
            return {
              valid: false,
              message: "fetch_users must be a function that returns a Promise<ExpectedUser[]>"
            };
          }
        });
        editor.on("ScriptsLoaded", () => {
          registerOption("directionality", {
            processor: "string",
            default: I18n.isRtl() ? "rtl" : void 0
          });
          registerOption("placeholder", {
            processor: "string",
            // Fallback to the original elements placeholder if not set in the settings
            default: DOM$d.getAttrib(editor.getElement(), "placeholder")
          });
        });
        registerOption("lists_indent_on_tab", {
          processor: "boolean",
          default: true
        });
        registerOption("list_max_depth", {
          processor: (value2) => {
            const valid = isNumber(value2);
            if (valid) {
              if (value2 < 0) {
                throw new Error("list_max_depth cannot be set to lower than 0");
              }
              return { value: value2, valid };
            } else {
              return { valid: false, message: "Must be a number" };
            }
          }
        });
      };
      const getIframeAttrs = option$1("iframe_attrs");
      const getDocType = option$1("doctype");
      const getDocumentBaseUrl = option$1("document_base_url");
      const getBodyId = option$1("body_id");
      const getBodyClass = option$1("body_class");
      const getContentSecurityPolicy = option$1("content_security_policy");
      const shouldPutBrInPre$1 = option$1("br_in_pre");
      const getForcedRootBlock = option$1("forced_root_block");
      const getForcedRootBlockAttrs = option$1("forced_root_block_attrs");
      const getNewlineBehavior = option$1("newline_behavior");
      const getBrNewLineSelector = option$1("br_newline_selector");
      const getNoNewLineSelector = option$1("no_newline_selector");
      const shouldKeepStyles = option$1("keep_styles");
      const shouldEndContainerOnEmptyBlock = option$1("end_container_on_empty_block");
      const isAutomaticUploadsEnabled = option$1("automatic_uploads");
      const shouldReuseFileName = option$1("images_reuse_filename");
      const shouldReplaceBlobUris = option$1("images_replace_blob_uris");
      const getIconPackName = option$1("icons");
      const getIconsUrl = option$1("icons_url");
      const getImageUploadUrl = option$1("images_upload_url");
      const getImageUploadBasePath = option$1("images_upload_base_path");
      const getImagesUploadCredentials = option$1("images_upload_credentials");
      const getImagesUploadHandler = option$1("images_upload_handler");
      const shouldUseContentCssCors = option$1("content_css_cors");
      const getReferrerPolicy = option$1("referrer_policy");
      const getCrossOrigin = option$1("crossorigin");
      const getLanguageCode = option$1("language");
      const getLanguageUrl = option$1("language_url");
      const shouldIndentUseMargin = option$1("indent_use_margin");
      const getIndentation = option$1("indentation");
      const getContentCss = option$1("content_css");
      const getContentStyle = option$1("content_style");
      const getFontCss = option$1("font_css");
      const getDirectionality = option$1("directionality");
      const getInlineBoundarySelector = option$1("inline_boundaries_selector");
      const getObjectResizing = option$1("object_resizing");
      const getResizeImgProportional = option$1("resize_img_proportional");
      const getPlaceholder = option$1("placeholder");
      const getEventRoot = option$1("event_root");
      const getServiceMessage = option$1("service_message");
      const getTheme = option$1("theme");
      const getThemeUrl = option$1("theme_url");
      const getModel = option$1("model");
      const getModelUrl = option$1("model_url");
      const isInlineBoundariesEnabled = option$1("inline_boundaries");
      const getFormats = option$1("formats");
      const getPreviewStyles = option$1("preview_styles");
      const canFormatEmptyLines = option$1("format_empty_lines");
      const getFormatNoneditableSelector = option$1("format_noneditable_selector");
      const getCustomUiSelector = option$1("custom_ui_selector");
      const isInline$2 = option$1("inline");
      const hasHiddenInput = option$1("hidden_input");
      const shouldPatchSubmit = option$1("submit_patch");
      const shouldAddFormSubmitTrigger = option$1("add_form_submit_trigger");
      const shouldAddUnloadTrigger = option$1("add_unload_trigger");
      const getCustomUndoRedoLevels = option$1("custom_undo_redo_levels");
      const shouldDisableNodeChange = option$1("disable_nodechange");
      const isReadOnly$1 = option$1("readonly");
      const hasEditableRoot$1 = option$1("editable_root");
      const hasContentCssCors = option$1("content_css_cors");
      const getPlugins = option$1("plugins");
      const getExternalPlugins$1 = option$1("external_plugins");
      const shouldBlockUnsupportedDrop = option$1("block_unsupported_drop");
      const isVisualAidsEnabled = option$1("visual");
      const getVisualAidsTableClass = option$1("visual_table_class");
      const getVisualAidsAnchorClass = option$1("visual_anchor_class");
      const getIframeAriaText = option$1("iframe_aria_text");
      const getSetupCallback = option$1("setup");
      const getInitInstanceCallback = option$1("init_instance_callback");
      const getUrlConverterCallback = option$1("urlconverter_callback");
      const getAutoFocus = option$1("auto_focus");
      const shouldBrowserSpellcheck = option$1("browser_spellcheck");
      const getProtect = option$1("protect");
      const shouldPasteBlockDrop = option$1("paste_block_drop");
      const shouldPasteDataImages = option$1("paste_data_images");
      const getPastePreProcess = option$1("paste_preprocess");
      const getPastePostProcess = option$1("paste_postprocess");
      const getNewDocumentContent = option$1("newdocument_content");
      const getPasteWebkitStyles = option$1("paste_webkit_styles");
      const shouldPasteRemoveWebKitStyles = option$1("paste_remove_styles_if_webkit");
      const shouldPasteMergeFormats = option$1("paste_merge_formats");
      const isSmartPasteEnabled = option$1("smart_paste");
      const isPasteAsTextEnabled = option$1("paste_as_text");
      const getPasteTabSpaces = option$1("paste_tab_spaces");
      const shouldAllowHtmlDataUrls = option$1("allow_html_data_urls");
      const getTextPatterns = option$1("text_patterns");
      const getTextPatternsLookup = option$1("text_patterns_lookup");
      const getNonEditableClass = option$1("noneditable_class");
      const getEditableClass = option$1("editable_class");
      const getNonEditableRegExps = option$1("noneditable_regexp");
      const shouldPreserveCData = option$1("preserve_cdata");
      const shouldHighlightOnFocus = option$1("highlight_on_focus");
      const shouldSanitizeXss = option$1("xss_sanitization");
      const shouldUseDocumentWrite = option$1("init_content_sync");
      const hasTextPatternsLookup = (editor) => editor.options.isSet("text_patterns_lookup");
      const getFontStyleValues = (editor) => Tools.explode(editor.options.get("font_size_style_values"));
      const getFontSizeClasses = (editor) => Tools.explode(editor.options.get("font_size_classes"));
      const isEncodingXml = (editor) => editor.options.get("encoding") === "xml";
      const getAllowedImageFileTypes = (editor) => Tools.explode(editor.options.get("images_file_types"));
      const hasTableTabNavigation = option$1("table_tab_navigation");
      const getDetailsInitialState = option$1("details_initial_state");
      const getDetailsSerializedState = option$1("details_serialized_state");
      const shouldSandboxIframes = option$1("sandbox_iframes");
      const getSandboxIframesExclusions = (editor) => editor.options.get("sandbox_iframes_exclusions");
      const shouldConvertUnsafeEmbeds = option$1("convert_unsafe_embeds");
      const getLicenseKey = option$1("license_key");
      const getApiKey = option$1("api_key");
      const isDisabled$1 = option$1("disabled");
      const getUserId = option$1("user_id");
      const getFetchUsers = option$1("fetch_users");
      const shouldIndentOnTab = option$1("lists_indent_on_tab");
      const getListMaxDepth = (editor) => Optional.from(editor.options.get("list_max_depth"));
      const isElement$4 = isElement$7;
      const isText$5 = isText$b;
      const removeNode$1 = (node) => {
        const parentNode = node.parentNode;
        if (parentNode) {
          parentNode.removeChild(node);
        }
      };
      const trimCount = (text2) => {
        const trimmedText = trim$2(text2);
        return {
          count: text2.length - trimmedText.length,
          text: trimmedText
        };
      };
      const deleteZwspChars = (caretContainer) => {
        let idx;
        while ((idx = caretContainer.data.lastIndexOf(ZWSP$1)) !== -1) {
          caretContainer.deleteData(idx, 1);
        }
      };
      const removeUnchanged = (caretContainer, pos) => {
        remove$2(caretContainer);
        return pos;
      };
      const removeTextAndReposition = (caretContainer, pos) => {
        const before2 = trimCount(caretContainer.data.substr(0, pos.offset()));
        const after2 = trimCount(caretContainer.data.substr(pos.offset()));
        const text2 = before2.text + after2.text;
        if (text2.length > 0) {
          deleteZwspChars(caretContainer);
          return CaretPosition(caretContainer, pos.offset() - before2.count);
        } else {
          return pos;
        }
      };
      const removeElementAndReposition = (caretContainer, pos) => {
        const parentNode = pos.container();
        const newPosition = indexOf$1(from(parentNode.childNodes), caretContainer).map((index) => {
          return index < pos.offset() ? CaretPosition(parentNode, pos.offset() - 1) : pos;
        }).getOr(pos);
        remove$2(caretContainer);
        return newPosition;
      };
      const removeTextCaretContainer = (caretContainer, pos) => isText$5(caretContainer) && pos.container() === caretContainer ? removeTextAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos);
      const removeElementCaretContainer = (caretContainer, pos) => pos.container() === caretContainer.parentNode ? removeElementAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos);
      const removeAndReposition = (container, pos) => CaretPosition.isTextPosition(pos) ? removeTextCaretContainer(container, pos) : removeElementCaretContainer(container, pos);
      const remove$2 = (caretContainerNode) => {
        if (isElement$4(caretContainerNode) && isCaretContainer$2(caretContainerNode)) {
          if (hasContent(caretContainerNode)) {
            caretContainerNode.removeAttribute("data-mce-caret");
          } else {
            removeNode$1(caretContainerNode);
          }
        }
        if (isText$5(caretContainerNode)) {
          deleteZwspChars(caretContainerNode);
          if (caretContainerNode.data.length === 0) {
            removeNode$1(caretContainerNode);
          }
        }
      };
      const isContentEditableFalse$7 = isContentEditableFalse$a;
      const isMedia$1 = isMedia$2;
      const isTableCell$1 = isTableCell$3;
      const inlineFakeCaretSelector = "*[contentEditable=false],video,audio,embed,object";
      const getAbsoluteClientRect = (root, element, before2) => {
        const clientRect = collapse(element.getBoundingClientRect(), before2);
        let scrollX;
        let scrollY;
        if (root.tagName === "BODY") {
          const docElm = root.ownerDocument.documentElement;
          scrollX = root.scrollLeft || docElm.scrollLeft;
          scrollY = root.scrollTop || docElm.scrollTop;
        } else {
          const rootRect = root.getBoundingClientRect();
          scrollX = root.scrollLeft - rootRect.left;
          scrollY = root.scrollTop - rootRect.top;
        }
        clientRect.left += scrollX;
        clientRect.right += scrollX;
        clientRect.top += scrollY;
        clientRect.bottom += scrollY;
        clientRect.width = 1;
        let margin = element.offsetWidth - element.clientWidth;
        if (margin > 0) {
          if (before2) {
            margin *= -1;
          }
          clientRect.left += margin;
          clientRect.right += margin;
        }
        return clientRect;
      };
      const trimInlineCaretContainers = (root) => {
        const fakeCaretTargetNodes = descendants(SugarElement.fromDom(root), inlineFakeCaretSelector);
        for (let i = 0; i < fakeCaretTargetNodes.length; i++) {
          const node = fakeCaretTargetNodes[i].dom;
          let sibling2 = node.previousSibling;
          if (endsWithCaretContainer$1(sibling2)) {
            const data2 = sibling2.data;
            if (data2.length === 1) {
              sibling2.parentNode?.removeChild(sibling2);
            } else {
              sibling2.deleteData(data2.length - 1, 1);
            }
          }
          sibling2 = node.nextSibling;
          if (startsWithCaretContainer$1(sibling2)) {
            const data2 = sibling2.data;
            if (data2.length === 1) {
              sibling2.parentNode?.removeChild(sibling2);
            } else {
              sibling2.deleteData(0, 1);
            }
          }
        }
      };
      const FakeCaret = (editor, root, isBlock2, hasFocus2) => {
        const lastVisualCaret = value$1();
        let cursorInterval;
        let caretContainerNode;
        const caretBlock = getForcedRootBlock(editor);
        const dom2 = editor.dom;
        const show = (before2, element) => {
          let rng;
          hide();
          if (isTableCell$1(element)) {
            return null;
          }
          if (isBlock2(element)) {
            const caretContainer = insertBlock(caretBlock, element, before2);
            const clientRect = getAbsoluteClientRect(root, element, before2);
            dom2.setStyle(caretContainer, "top", clientRect.top);
            dom2.setStyle(caretContainer, "caret-color", "transparent");
            caretContainerNode = caretContainer;
            const caret = dom2.create("div", { "class": "mce-visual-caret", "data-mce-bogus": "all" });
            dom2.setStyles(caret, { ...clientRect });
            dom2.add(root, caret);
            lastVisualCaret.set({ caret, element, before: before2 });
            if (before2) {
              dom2.addClass(caret, "mce-visual-caret-before");
            }
            startBlink();
            rng = element.ownerDocument.createRange();
            rng.setStart(caretContainer, 0);
            rng.setEnd(caretContainer, 0);
          } else {
            caretContainerNode = insertInline$1(element, before2);
            rng = element.ownerDocument.createRange();
            if (isInlineFakeCaretTarget(caretContainerNode.nextSibling)) {
              rng.setStart(caretContainerNode, 0);
              rng.setEnd(caretContainerNode, 0);
            } else {
              rng.setStart(caretContainerNode, 1);
              rng.setEnd(caretContainerNode, 1);
            }
            return rng;
          }
          return rng;
        };
        const hide = () => {
          trimInlineCaretContainers(root);
          if (caretContainerNode) {
            remove$2(caretContainerNode);
            caretContainerNode = null;
          }
          lastVisualCaret.on((caretState) => {
            dom2.remove(caretState.caret);
            lastVisualCaret.clear();
          });
          if (cursorInterval) {
            clearInterval(cursorInterval);
            cursorInterval = void 0;
          }
        };
        const startBlink = () => {
          cursorInterval = window.setInterval(() => {
            lastVisualCaret.on((caretState) => {
              if (hasFocus2()) {
                dom2.toggleClass(caretState.caret, "mce-visual-caret-hidden");
              } else {
                dom2.addClass(caretState.caret, "mce-visual-caret-hidden");
              }
            });
          }, 500);
        };
        const reposition2 = () => {
          lastVisualCaret.on((caretState) => {
            const clientRect = getAbsoluteClientRect(root, caretState.element, caretState.before);
            dom2.setStyles(caretState.caret, { ...clientRect });
          });
        };
        const destroy2 = () => clearInterval(cursorInterval);
        const getCss = () => ".mce-visual-caret {position: absolute;background-color: black;background-color: currentcolor;}.mce-visual-caret-hidden {display: none;}*[data-mce-caret] {position: absolute;left: -1000px;right: auto;top: 0;margin: 0;padding: 0;}";
        return {
          isShowing: lastVisualCaret.isSet,
          show,
          hide,
          getCss,
          reposition: reposition2,
          destroy: destroy2
        };
      };
      const isFakeCaretTableBrowser = () => Env.browser.isFirefox();
      const isInlineFakeCaretTarget = (node) => isContentEditableFalse$7(node) || isMedia$1(node);
      const isFakeCaretTarget = (node) => {
        const isTarget2 = isInlineFakeCaretTarget(node) || isTable$2(node) && isFakeCaretTableBrowser();
        return isTarget2 && parentElement(SugarElement.fromDom(node)).exists(isEditable$2);
      };
      const isContentEditableTrue$1 = isContentEditableTrue$3;
      const isContentEditableFalse$6 = isContentEditableFalse$a;
      const isMedia = isMedia$2;
      const isBlockLike = matchStyleValues("display", "block table table-cell table-row table-caption list-item");
      const isCaretContainer = isCaretContainer$2;
      const isCaretContainerBlock = isCaretContainerBlock$1;
      const isElement$3 = isElement$7;
      const isText$4 = isText$b;
      const isCaretCandidate$1 = isCaretCandidate$3;
      const skipCaretContainers = (walk2, shallow2) => {
        let node;
        while (node = walk2(shallow2)) {
          if (!isCaretContainerBlock(node)) {
            return node;
          }
        }
        return null;
      };
      const findNode = (node, direction, predicateFn, rootNode, shallow2) => {
        const walker = new DomTreeWalker(node, rootNode);
        const isCefOrCaretContainer = isContentEditableFalse$6(node) || isCaretContainerBlock(node);
        let tempNode;
        if (isBackwards(direction)) {
          if (isCefOrCaretContainer) {
            tempNode = skipCaretContainers(walker.prev.bind(walker), true);
            if (predicateFn(tempNode)) {
              return tempNode;
            }
          }
          while (tempNode = skipCaretContainers(walker.prev.bind(walker), shallow2)) {
            if (predicateFn(tempNode)) {
              return tempNode;
            }
          }
        }
        if (isForwards(direction)) {
          if (isCefOrCaretContainer) {
            tempNode = skipCaretContainers(walker.next.bind(walker), true);
            if (predicateFn(tempNode)) {
              return tempNode;
            }
          }
          while (tempNode = skipCaretContainers(walker.next.bind(walker), shallow2)) {
            if (predicateFn(tempNode)) {
              return tempNode;
            }
          }
        }
        return null;
      };
      const getEditingHost = (node, rootNode) => {
        const isCETrue = (node2) => isContentEditableTrue$1(node2.dom);
        const isRoot2 = (node2) => node2.dom === rootNode;
        return ancestor$5(SugarElement.fromDom(node), isCETrue, isRoot2).map((elm) => elm.dom).getOr(rootNode);
      };
      const isAbsPositionedElement = (node) => isElement$7(node) && get$7(SugarElement.fromDom(node), "position") === "absolute";
      const isInlineBlock = (node, rootNode) => node.parentNode !== rootNode;
      const isInlineAbsPositionedCEF = (node, rootNode) => isContentEditableFalse$6(node) && isAbsPositionedElement(node) && isInlineBlock(node, rootNode);
      const getParentBlock$3 = (node, rootNode) => {
        while (node && node !== rootNode) {
          if (isBlockLike(node) && !isInlineAbsPositionedCEF(node, rootNode)) {
            return node;
          }
          node = node.parentNode;
        }
        return null;
      };
      const isInSameBlock = (caretPosition1, caretPosition2, rootNode) => getParentBlock$3(caretPosition1.container(), rootNode) === getParentBlock$3(caretPosition2.container(), rootNode);
      const getChildNodeAtRelativeOffset = (relativeOffset, caretPosition) => {
        if (!caretPosition) {
          return Optional.none();
        }
        const container = caretPosition.container();
        const offset = caretPosition.offset();
        if (!isElement$3(container)) {
          return Optional.none();
        }
        return Optional.from(container.childNodes[offset + relativeOffset]);
      };
      const beforeAfter = (before2, node) => {
        const doc = node.ownerDocument ?? document;
        const range2 = doc.createRange();
        if (before2) {
          range2.setStartBefore(node);
          range2.setEndBefore(node);
        } else {
          range2.setStartAfter(node);
          range2.setEndAfter(node);
        }
        return range2;
      };
      const isNodesInSameBlock = (root, node1, node2) => getParentBlock$3(node1, root) === getParentBlock$3(node2, root);
      const lean = (left, root, node) => {
        const siblingName = left ? "previousSibling" : "nextSibling";
        let tempNode = node;
        while (tempNode && tempNode !== root) {
          let sibling2 = tempNode[siblingName];
          if (sibling2 && isCaretContainer(sibling2)) {
            sibling2 = sibling2[siblingName];
          }
          if (isContentEditableFalse$6(sibling2) || isMedia(sibling2)) {
            if (isNodesInSameBlock(root, sibling2, tempNode)) {
              return sibling2;
            }
            break;
          }
          if (isCaretCandidate$1(sibling2)) {
            break;
          }
          tempNode = tempNode.parentNode;
        }
        return null;
      };
      const before$1 = curry(beforeAfter, true);
      const after$1 = curry(beforeAfter, false);
      const normalizeRange$2 = (direction, root, range2) => {
        let node;
        const leanLeft = curry(lean, true, root);
        const leanRight2 = curry(lean, false, root);
        const container = range2.startContainer;
        const offset = range2.startOffset;
        if (isCaretContainerBlock$1(container)) {
          const block = isText$4(container) ? container.parentNode : container;
          const location = block.getAttribute("data-mce-caret");
          if (location === "before") {
            node = block.nextSibling;
            if (isFakeCaretTarget(node)) {
              return before$1(node);
            }
          }
          if (location === "after") {
            node = block.previousSibling;
            if (isFakeCaretTarget(node)) {
              return after$1(node);
            }
          }
        }
        if (!range2.collapsed) {
          return range2;
        }
        if (isText$b(container)) {
          if (isCaretContainer(container)) {
            if (direction === 1) {
              node = leanRight2(container);
              if (node) {
                return before$1(node);
              }
              node = leanLeft(container);
              if (node) {
                return after$1(node);
              }
            }
            if (direction === -1) {
              node = leanLeft(container);
              if (node) {
                return after$1(node);
              }
              node = leanRight2(container);
              if (node) {
                return before$1(node);
              }
            }
            return range2;
          }
          if (endsWithCaretContainer$1(container) && offset >= container.data.length - 1) {
            if (direction === 1) {
              node = leanRight2(container);
              if (node) {
                return before$1(node);
              }
            }
            return range2;
          }
          if (startsWithCaretContainer$1(container) && offset <= 1) {
            if (direction === -1) {
              node = leanLeft(container);
              if (node) {
                return after$1(node);
              }
            }
            return range2;
          }
          if (offset === container.data.length) {
            node = leanRight2(container);
            if (node) {
              return before$1(node);
            }
            return range2;
          }
          if (offset === 0) {
            node = leanLeft(container);
            if (node) {
              return after$1(node);
            }
            return range2;
          }
        }
        return range2;
      };
      const getRelativeCefElm = (forward, caretPosition) => getChildNodeAtRelativeOffset(forward ? 0 : -1, caretPosition).filter(isContentEditableFalse$6);
      const getNormalizedRangeEndPoint = (direction, root, range2) => {
        const normalizedRange = normalizeRange$2(direction, root, range2);
        return direction === -1 ? CaretPosition.fromRangeStart(normalizedRange) : CaretPosition.fromRangeEnd(normalizedRange);
      };
      const getElementFromPosition = (pos) => Optional.from(pos.getNode()).map(SugarElement.fromDom);
      const getElementFromPrevPosition = (pos) => Optional.from(pos.getNode(true)).map(SugarElement.fromDom);
      const getVisualCaretPosition = (walkFn, caretPosition) => {
        let pos = caretPosition;
        while (pos = walkFn(pos)) {
          if (pos.isVisible()) {
            return pos;
          }
        }
        return pos;
      };
      const isMoveInsideSameBlock = (from2, to2) => {
        const inSameBlock = isInSameBlock(from2, to2);
        if (!inSameBlock && isBr$7(from2.getNode())) {
          return true;
        }
        return inSameBlock;
      };
      const isContentEditableFalse$5 = isContentEditableFalse$a;
      const isText$3 = isText$b;
      const isElement$2 = isElement$7;
      const isBr$3 = isBr$7;
      const isCaretCandidate = isCaretCandidate$3;
      const isAtomic = isAtomic$1;
      const isEditableCaretCandidate = isEditableCaretCandidate$1;
      const getParents$3 = (node, root) => {
        const parents2 = [];
        let tempNode = node;
        while (tempNode && tempNode !== root) {
          parents2.push(tempNode);
          tempNode = tempNode.parentNode;
        }
        return parents2;
      };
      const nodeAtIndex = (container, offset) => {
        if (container.hasChildNodes() && offset < container.childNodes.length) {
          return container.childNodes[offset];
        }
        return null;
      };
      const getCaretCandidatePosition = (direction, node) => {
        if (isForwards(direction)) {
          if (isCaretCandidate(node.previousSibling) && !isText$3(node.previousSibling)) {
            return CaretPosition.before(node);
          }
          if (isText$3(node)) {
            return CaretPosition(node, 0);
          }
        }
        if (isBackwards(direction)) {
          if (isCaretCandidate(node.nextSibling) && !isText$3(node.nextSibling)) {
            return CaretPosition.after(node);
          }
          if (isText$3(node)) {
            return CaretPosition(node, node.data.length);
          }
        }
        if (isBackwards(direction)) {
          if (isBr$3(node)) {
            return CaretPosition.before(node);
          }
          return CaretPosition.after(node);
        }
        return CaretPosition.before(node);
      };
      const moveForwardFromBr = (root, nextNode) => {
        const nextSibling2 = nextNode.nextSibling;
        if (nextSibling2 && isCaretCandidate(nextSibling2)) {
          if (isText$3(nextSibling2)) {
            return CaretPosition(nextSibling2, 0);
          } else {
            return CaretPosition.before(nextSibling2);
          }
        } else {
          return findCaretPosition$1(1, CaretPosition.after(nextNode), root);
        }
      };
      const findCaretPosition$1 = (direction, startPos, root) => {
        let node;
        let nextNode;
        let innerNode;
        let caretPosition;
        if (!isElement$2(root) || !startPos) {
          return null;
        }
        if (startPos.isEqual(CaretPosition.after(root)) && root.lastChild) {
          caretPosition = CaretPosition.after(root.lastChild);
          if (isBackwards(direction) && isCaretCandidate(root.lastChild) && isElement$2(root.lastChild)) {
            return isBr$3(root.lastChild) ? CaretPosition.before(root.lastChild) : caretPosition;
          }
        } else {
          caretPosition = startPos;
        }
        const container = caretPosition.container();
        let offset = caretPosition.offset();
        if (isText$3(container)) {
          if (isBackwards(direction) && offset > 0) {
            return CaretPosition(container, --offset);
          }
          if (isForwards(direction) && offset < container.length) {
            return CaretPosition(container, ++offset);
          }
          node = container;
        } else {
          if (isBackwards(direction) && offset > 0) {
            nextNode = nodeAtIndex(container, offset - 1);
            if (isCaretCandidate(nextNode)) {
              if (!isAtomic(nextNode)) {
                innerNode = findNode(nextNode, direction, isEditableCaretCandidate, nextNode);
                if (innerNode) {
                  if (isText$3(innerNode)) {
                    return CaretPosition(innerNode, innerNode.data.length);
                  }
                  return CaretPosition.after(innerNode);
                }
              }
              if (isText$3(nextNode)) {
                return CaretPosition(nextNode, nextNode.data.length);
              }
              return CaretPosition.before(nextNode);
            }
          }
          if (isForwards(direction) && offset < container.childNodes.length) {
            nextNode = nodeAtIndex(container, offset);
            if (isCaretCandidate(nextNode)) {
              if (isBr$3(nextNode)) {
                return moveForwardFromBr(root, nextNode);
              }
              if (!isAtomic(nextNode)) {
                innerNode = findNode(nextNode, direction, isEditableCaretCandidate, nextNode);
                if (innerNode) {
                  if (isText$3(innerNode)) {
                    return CaretPosition(innerNode, 0);
                  }
                  return CaretPosition.before(innerNode);
                }
              }
              if (isText$3(nextNode)) {
                return CaretPosition(nextNode, 0);
              }
              return CaretPosition.after(nextNode);
            }
          }
          node = nextNode ? nextNode : caretPosition.getNode();
        }
        if (node && (isForwards(direction) && caretPosition.isAtEnd() || isBackwards(direction) && caretPosition.isAtStart())) {
          node = findNode(node, direction, always, root, true);
          if (isEditableCaretCandidate(node, root)) {
            return getCaretCandidatePosition(direction, node);
          }
        }
        nextNode = node ? findNode(node, direction, isEditableCaretCandidate, root) : node;
        const rootContentEditableFalseElm = last(filter$5(getParents$3(container, root), isContentEditableFalse$5));
        if (rootContentEditableFalseElm && (!nextNode || !rootContentEditableFalseElm.contains(nextNode))) {
          if (isForwards(direction)) {
            caretPosition = CaretPosition.after(rootContentEditableFalseElm);
          } else {
            caretPosition = CaretPosition.before(rootContentEditableFalseElm);
          }
          return caretPosition;
        }
        if (nextNode) {
          return getCaretCandidatePosition(direction, nextNode);
        }
        return null;
      };
      const CaretWalker = (root) => ({
        /**
           * Returns the next logical caret position from the specified input
           * caretPosition or null if there isn't any more positions left for example
           * at the end specified root element.
           *
           * @method next
           * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from.
           * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found.
           */
        next: (caretPosition) => {
          return findCaretPosition$1(1, caretPosition, root);
        },
        /**
           * Returns the previous logical caret position from the specified input
           * caretPosition or null if there isn't any more positions left for example
           * at the end specified root element.
           *
           * @method prev
           * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from.
           * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found.
           */
        prev: (caretPosition) => {
          return findCaretPosition$1(-1, caretPosition, root);
        }
      });
      const walkToPositionIn = (forward, root, start2) => {
        const position = forward ? CaretPosition.before(start2) : CaretPosition.after(start2);
        return fromPosition(forward, root, position);
      };
      const afterElement = (node) => isBr$7(node) ? CaretPosition.before(node) : CaretPosition.after(node);
      const isBeforeOrStart = (position) => {
        if (CaretPosition.isTextPosition(position)) {
          return position.offset() === 0;
        } else {
          return isCaretCandidate$3(position.getNode());
        }
      };
      const isAfterOrEnd = (position) => {
        if (CaretPosition.isTextPosition(position)) {
          const container = position.container();
          return position.offset() === container.data.length;
        } else {
          return isCaretCandidate$3(position.getNode(true));
        }
      };
      const isBeforeAfterSameElement = (from2, to2) => !CaretPosition.isTextPosition(from2) && !CaretPosition.isTextPosition(to2) && from2.getNode() === to2.getNode(true);
      const isAtBr = (position) => !CaretPosition.isTextPosition(position) && isBr$7(position.getNode());
      const shouldSkipPosition = (forward, from2, to2) => {
        if (forward) {
          return !isBeforeAfterSameElement(from2, to2) && !isAtBr(from2) && isAfterOrEnd(from2) && isBeforeOrStart(to2);
        } else {
          return !isBeforeAfterSameElement(to2, from2) && isBeforeOrStart(from2) && isAfterOrEnd(to2);
        }
      };
      const fromPosition = (forward, root, pos) => {
        const walker = CaretWalker(root);
        return Optional.from(forward ? walker.next(pos) : walker.prev(pos));
      };
      const navigate = (forward, root, from2) => fromPosition(forward, root, from2).bind((to2) => {
        if (isInSameBlock(from2, to2, root) && shouldSkipPosition(forward, from2, to2)) {
          return fromPosition(forward, root, to2);
        } else {
          return Optional.some(to2);
        }
      });
      const navigateIgnore = (forward, root, from2, ignoreFilter) => navigate(forward, root, from2).bind((pos) => ignoreFilter(pos) ? navigateIgnore(forward, root, pos, ignoreFilter) : Optional.some(pos));
      const positionIn = (forward, element) => {
        const startNode = forward ? element.firstChild : element.lastChild;
        if (isText$b(startNode)) {
          return Optional.some(CaretPosition(startNode, forward ? 0 : startNode.data.length));
        } else if (startNode) {
          if (isCaretCandidate$3(startNode)) {
            return Optional.some(forward ? CaretPosition.before(startNode) : afterElement(startNode));
          } else {
            return walkToPositionIn(forward, element, startNode);
          }
        } else {
          return Optional.none();
        }
      };
      const nextPosition = curry(fromPosition, true);
      const prevPosition = curry(fromPosition, false);
      const firstPositionIn = curry(positionIn, true);
      const lastPositionIn = curry(positionIn, false);
      const CARET_ID = "_mce_caret";
      const isCaretNode = (node) => isElement$7(node) && node.id === CARET_ID;
      const getParentCaretContainer = (body, node) => {
        let currentNode = node;
        while (currentNode && currentNode !== body) {
          if (isCaretNode(currentNode)) {
            return currentNode;
          }
          currentNode = currentNode.parentNode;
        }
        return null;
      };
      const isStringPathBookmark = (bookmark) => isString(bookmark.start);
      const isRangeBookmark = (bookmark) => has$2(bookmark, "rng");
      const isIdBookmark = (bookmark) => has$2(bookmark, "id");
      const isIndexBookmark = (bookmark) => has$2(bookmark, "name");
      const isPathBookmark = (bookmark) => Tools.isArray(bookmark.start);
      const isForwardBookmark = (bookmark) => !isIndexBookmark(bookmark) && isBoolean(bookmark.forward) ? bookmark.forward : true;
      const addBogus = (dom2, node) => {
        if (isElement$7(node) && dom2.isBlock(node) && !node.innerHTML) {
          node.innerHTML = '<br data-mce-bogus="1" />';
        }
        return node;
      };
      const resolveCaretPositionBookmark = (dom2, bookmark) => {
        const startPos = Optional.from(resolve$1(dom2.getRoot(), bookmark.start));
        const endPos = Optional.from(resolve$1(dom2.getRoot(), bookmark.end));
        return lift2(startPos, endPos, (start2, end2) => {
          const range2 = dom2.createRng();
          range2.setStart(start2.container(), start2.offset());
          range2.setEnd(end2.container(), end2.offset());
          return { range: range2, forward: isForwardBookmark(bookmark) };
        });
      };
      const insertZwsp = (node, rng) => {
        const doc = node.ownerDocument ?? document;
        const textNode = doc.createTextNode(ZWSP$1);
        node.appendChild(textNode);
        rng.setStart(textNode, 0);
        rng.setEnd(textNode, 0);
      };
      const isEmpty$3 = (node) => !node.hasChildNodes();
      const tryFindRangePosition = (node, rng) => lastPositionIn(node).fold(never, (pos) => {
        rng.setStart(pos.container(), pos.offset());
        rng.setEnd(pos.container(), pos.offset());
        return true;
      });
      const padEmptyCaretContainer = (root, node, rng) => {
        if (isEmpty$3(node) && getParentCaretContainer(root, node)) {
          insertZwsp(node, rng);
          return true;
        } else {
          return false;
        }
      };
      const setEndPoint = (dom2, start2, bookmark, rng) => {
        const point2 = bookmark[start2 ? "start" : "end"];
        const root = dom2.getRoot();
        if (point2) {
          let node = root;
          let offset = point2[0];
          for (let i = point2.length - 1; node && i >= 1; i--) {
            const children2 = node.childNodes;
            if (padEmptyCaretContainer(root, node, rng)) {
              return true;
            }
            if (point2[i] > children2.length - 1) {
              if (padEmptyCaretContainer(root, node, rng)) {
                return true;
              }
              return tryFindRangePosition(node, rng);
            }
            node = children2[point2[i]];
          }
          if (isText$b(node)) {
            offset = Math.min(point2[0], node.data.length);
          }
          if (isElement$7(node)) {
            offset = Math.min(point2[0], node.childNodes.length);
          }
          if (start2) {
            rng.setStart(node, offset);
          } else {
            rng.setEnd(node, offset);
          }
        }
        return true;
      };
      const isValidTextNode = (node) => isText$b(node) && node.data.length > 0;
      const restoreEndPoint$1 = (dom2, suffix, bookmark) => {
        const marker = dom2.get(bookmark.id + "_" + suffix);
        const markerParent = marker?.parentNode;
        const keep = bookmark.keep;
        if (marker && markerParent) {
          let container;
          let offset;
          if (suffix === "start") {
            if (!keep) {
              container = markerParent;
              offset = dom2.nodeIndex(marker);
            } else {
              if (marker.hasChildNodes()) {
                container = marker.firstChild;
                offset = 1;
              } else if (isValidTextNode(marker.nextSibling)) {
                container = marker.nextSibling;
                offset = 0;
              } else if (isValidTextNode(marker.previousSibling)) {
                container = marker.previousSibling;
                offset = marker.previousSibling.data.length;
              } else {
                container = markerParent;
                offset = dom2.nodeIndex(marker) + 1;
              }
            }
          } else {
            if (!keep) {
              container = markerParent;
              offset = dom2.nodeIndex(marker);
            } else {
              if (marker.hasChildNodes()) {
                container = marker.firstChild;
                offset = 1;
              } else if (isValidTextNode(marker.previousSibling)) {
                container = marker.previousSibling;
                offset = marker.previousSibling.data.length;
              } else {
                container = markerParent;
                offset = dom2.nodeIndex(marker);
              }
            }
          }
          if (!keep) {
            const prev2 = marker.previousSibling;
            const next2 = marker.nextSibling;
            Tools.each(Tools.grep(marker.childNodes), (node) => {
              if (isText$b(node)) {
                node.data = node.data.replace(/\uFEFF/g, "");
              }
            });
            let otherMarker;
            while (otherMarker = dom2.get(bookmark.id + "_" + suffix)) {
              dom2.remove(otherMarker, true);
            }
            if (isText$b(next2) && isText$b(prev2) && !Env.browser.isOpera()) {
              const idx = prev2.data.length;
              prev2.appendData(next2.data);
              dom2.remove(next2);
              container = prev2;
              offset = idx;
            }
          }
          return Optional.some(CaretPosition(container, offset));
        } else {
          return Optional.none();
        }
      };
      const resolvePaths = (dom2, bookmark) => {
        const range2 = dom2.createRng();
        if (setEndPoint(dom2, true, bookmark, range2) && setEndPoint(dom2, false, bookmark, range2)) {
          return Optional.some({ range: range2, forward: isForwardBookmark(bookmark) });
        } else {
          return Optional.none();
        }
      };
      const resolveId = (dom2, bookmark) => {
        const startPos = restoreEndPoint$1(dom2, "start", bookmark);
        const endPos = restoreEndPoint$1(dom2, "end", bookmark);
        return lift2(startPos, endPos.or(startPos), (spos, epos) => {
          const range2 = dom2.createRng();
          range2.setStart(addBogus(dom2, spos.container()), spos.offset());
          range2.setEnd(addBogus(dom2, epos.container()), epos.offset());
          return { range: range2, forward: isForwardBookmark(bookmark) };
        });
      };
      const resolveIndex = (dom2, bookmark) => Optional.from(dom2.select(bookmark.name)[bookmark.index]).map((elm) => {
        const range2 = dom2.createRng();
        range2.selectNode(elm);
        return { range: range2, forward: true };
      });
      const resolve = (selection, bookmark) => {
        const dom2 = selection.dom;
        if (bookmark) {
          if (isPathBookmark(bookmark)) {
            return resolvePaths(dom2, bookmark);
          } else if (isStringPathBookmark(bookmark)) {
            return resolveCaretPositionBookmark(dom2, bookmark);
          } else if (isIdBookmark(bookmark)) {
            return resolveId(dom2, bookmark);
          } else if (isIndexBookmark(bookmark)) {
            return resolveIndex(dom2, bookmark);
          } else if (isRangeBookmark(bookmark)) {
            return Optional.some({ range: bookmark.rng, forward: isForwardBookmark(bookmark) });
          }
        }
        return Optional.none();
      };
      const getBookmark$1 = (selection, type2, normalized) => {
        return getBookmark$2(selection, type2, normalized);
      };
      const moveToBookmark = (selection, bookmark) => {
        resolve(selection, bookmark).each(({ range: range2, forward }) => {
          selection.setRng(range2, forward);
        });
      };
      const isBookmarkNode$1 = (node) => {
        return isElement$7(node) && node.tagName === "SPAN" && node.getAttribute("data-mce-type") === "bookmark";
      };
      const is = (expected) => (actual) => expected === actual;
      const isNbsp = is(nbsp);
      const isWhiteSpace = (chr) => chr !== "" && " \f\n\r	\v".indexOf(chr) !== -1;
      const isContent = (chr) => !isWhiteSpace(chr) && !isNbsp(chr) && !isZwsp$2(chr);
      const getRanges = (selection) => {
        const ranges = [];
        if (selection) {
          for (let i = 0; i < selection.rangeCount; i++) {
            ranges.push(selection.getRangeAt(i));
          }
        }
        return ranges;
      };
      const getSelectedNodes = (ranges) => {
        return bind$3(ranges, (range2) => {
          const node = getSelectedNode(range2);
          return node ? [SugarElement.fromDom(node)] : [];
        });
      };
      const hasMultipleRanges = (selection) => {
        return getRanges(selection).length > 1;
      };
      const getCellsFromRanges = (ranges) => filter$5(getSelectedNodes(ranges), isTableCell$2);
      const getCellsFromElement = (elm) => descendants(elm, "td[data-mce-selected],th[data-mce-selected]");
      const getCellsFromElementOrRanges = (ranges, element) => {
        const selectedCells = getCellsFromElement(element);
        return selectedCells.length > 0 ? selectedCells : getCellsFromRanges(ranges);
      };
      const getCellsFromEditor = (editor) => getCellsFromElementOrRanges(getRanges(editor.selection.getSel()), SugarElement.fromDom(editor.getBody()));
      const getClosestTable = (cell2, isRoot2) => ancestor$4(cell2, "table", isRoot2);
      const getStartNode = (rng) => {
        const sc = rng.startContainer, so = rng.startOffset;
        if (isText$b(sc)) {
          return so === 0 ? Optional.some(SugarElement.fromDom(sc)) : Optional.none();
        } else {
          return Optional.from(sc.childNodes[so]).map(SugarElement.fromDom);
        }
      };
      const getEndNode = (rng) => {
        const ec = rng.endContainer, eo = rng.endOffset;
        if (isText$b(ec)) {
          return eo === ec.data.length ? Optional.some(SugarElement.fromDom(ec)) : Optional.none();
        } else {
          return Optional.from(ec.childNodes[eo - 1]).map(SugarElement.fromDom);
        }
      };
      const getFirstChildren = (node) => {
        return firstChild(node).fold(constant([node]), (child2) => {
          return [node].concat(getFirstChildren(child2));
        });
      };
      const getLastChildren = (node) => {
        return lastChild(node).fold(constant([node]), (child2) => {
          if (name(child2) === "br") {
            return prevSibling(child2).map((sibling2) => {
              return [node].concat(getLastChildren(sibling2));
            }).getOr([]);
          } else {
            return [node].concat(getLastChildren(child2));
          }
        });
      };
      const hasAllContentsSelected = (elm, rng) => {
        return lift2(getStartNode(rng), getEndNode(rng), (startNode, endNode) => {
          const start2 = find$2(getFirstChildren(elm), curry(eq, startNode));
          const end2 = find$2(getLastChildren(elm), curry(eq, endNode));
          return start2.isSome() && end2.isSome();
        }).getOr(false);
      };
      const moveEndPoint = (dom2, rng, node, start2) => {
        const root = node;
        const walker = new DomTreeWalker(node, root);
        const moveCaretBeforeOnEnterElementsMap = filter$4(dom2.schema.getMoveCaretBeforeOnEnterElements(), (_, name2) => !contains$2(["td", "th", "table"], name2.toLowerCase()));
        let currentNode = node;
        do {
          if (isText$b(currentNode) && Tools.trim(currentNode.data).length !== 0) {
            if (start2) {
              rng.setStart(currentNode, 0);
            } else {
              rng.setEnd(currentNode, currentNode.data.length);
            }
            return;
          }
          if (moveCaretBeforeOnEnterElementsMap[currentNode.nodeName]) {
            if (start2) {
              rng.setStartBefore(currentNode);
            } else {
              if (currentNode.nodeName === "BR") {
                rng.setEndBefore(currentNode);
              } else {
                rng.setEndAfter(currentNode);
              }
            }
            return;
          }
        } while (currentNode = start2 ? walker.next() : walker.prev());
        if (root.nodeName === "BODY") {
          if (start2) {
            rng.setStart(root, 0);
          } else {
            rng.setEnd(root, root.childNodes.length);
          }
        }
      };
      const hasAnyRanges = (editor) => {
        const sel = editor.selection.getSel();
        return isNonNullable(sel) && sel.rangeCount > 0;
      };
      const runOnRanges = (editor, executor) => {
        const fakeSelectionNodes = getCellsFromEditor(editor);
        if (fakeSelectionNodes.length > 0) {
          each$e(fakeSelectionNodes, (elem) => {
            const node = elem.dom;
            const fakeNodeRng = editor.dom.createRng();
            fakeNodeRng.setStartBefore(node);
            fakeNodeRng.setEndAfter(node);
            executor(fakeNodeRng, true);
          });
        } else {
          executor(editor.selection.getRng(), false);
        }
      };
      const preserve = (selection, fillBookmark, executor) => {
        const bookmark = getPersistentBookmark(selection, fillBookmark);
        executor(bookmark);
        selection.moveToBookmark(bookmark);
      };
      const isSelectionOverWholeNode = (range2, nodeTypePredicate) => range2.startContainer === range2.endContainer && range2.endOffset - range2.startOffset === 1 && nodeTypePredicate(range2.startContainer.childNodes[range2.startOffset]);
      const isSelectionOverWholeHTMLElement = (range2) => isSelectionOverWholeNode(range2, isHTMLElement);
      const isSelectionOverWholeTextNode = (range2) => isSelectionOverWholeNode(range2, isText$b);
      const isSelectionOverWholeAnchor = (range2) => isSelectionOverWholeNode(range2, isAnchor);
      const isNode = (node) => isNumber(node?.nodeType);
      const isElementNode$1 = (node) => isElement$7(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$1(node);
      const isElementDirectlySelected = (dom2, node) => {
        if (isElementNode$1(node) && !/^(TD|TH)$/.test(node.nodeName)) {
          const selectedAttr = dom2.getAttrib(node, "data-mce-selected");
          const value2 = parseInt(selectedAttr, 10);
          return !isNaN(value2) && value2 > 0;
        } else {
          return false;
        }
      };
      const preserveSelection = (editor, action2, shouldMoveStart) => {
        const { selection, dom: dom2 } = editor;
        const selectedNodeBeforeAction = selection.getNode();
        const isSelectedBeforeNodeNoneditable = isContentEditableFalse$a(selectedNodeBeforeAction);
        preserve(selection, true, () => {
          action2();
        });
        const isBeforeNodeStillNoneditable = isSelectedBeforeNodeNoneditable && isContentEditableFalse$a(selectedNodeBeforeAction);
        if (isBeforeNodeStillNoneditable && dom2.isChildOf(selectedNodeBeforeAction, editor.getBody())) {
          editor.selection.select(selectedNodeBeforeAction);
        } else if (shouldMoveStart(selection.getStart())) {
          moveStartToNearestText(dom2, selection);
        }
      };
      const moveStartToNearestText = (dom2, selection) => {
        const rng = selection.getRng();
        const { startContainer, startOffset } = rng;
        const selectedNode = selection.getNode();
        if (isElementDirectlySelected(dom2, selectedNode)) {
          return;
        }
        if (isElement$7(startContainer)) {
          const nodes = startContainer.childNodes;
          const root = dom2.getRoot();
          let walker;
          if (startOffset < nodes.length) {
            const startNode = nodes[startOffset];
            walker = new DomTreeWalker(startNode, dom2.getParent(startNode, dom2.isBlock) ?? root);
          } else {
            const startNode = nodes[nodes.length - 1];
            walker = new DomTreeWalker(startNode, dom2.getParent(startNode, dom2.isBlock) ?? root);
            walker.next(true);
          }
          for (let node = walker.current(); node; node = walker.next()) {
            if (dom2.getContentEditable(node) === "false") {
              return;
            } else if (isText$b(node) && !isWhiteSpaceNode$1(node)) {
              rng.setStart(node, 0);
              selection.setRng(rng);
              return;
            }
          }
        }
      };
      const getNonWhiteSpaceSibling = (node, next2, inc) => {
        if (node) {
          const nextName = next2 ? "nextSibling" : "previousSibling";
          for (node = inc ? node : node[nextName]; node; node = node[nextName]) {
            if (isElement$7(node) || !isWhiteSpaceNode$1(node)) {
              return node;
            }
          }
        }
        return void 0;
      };
      const isTextBlock$2 = (schema, node) => !!schema.getTextBlockElements()[node.nodeName.toLowerCase()] || isTransparentBlock(schema, node);
      const isValid = (ed, parent2, child2) => {
        return ed.schema.isValidChild(parent2, child2);
      };
      const isWhiteSpaceNode$1 = (node, allowSpaces = false) => {
        if (isNonNullable(node) && isText$b(node)) {
          const data2 = allowSpaces ? node.data.replace(/ /g, " ") : node.data;
          return isWhitespaceText(data2);
        } else {
          return false;
        }
      };
      const isEmptyTextNode$1 = (node) => {
        return isNonNullable(node) && isText$b(node) && node.length === 0;
      };
      const isWrapNoneditableTarget = (editor, node) => {
        const baseDataSelector = "[data-mce-cef-wrappable]";
        const formatNoneditableSelector = getFormatNoneditableSelector(editor);
        const selector = isEmpty$5(formatNoneditableSelector) ? baseDataSelector : `${baseDataSelector},${formatNoneditableSelector}`;
        return is$2(SugarElement.fromDom(node), selector);
      };
      const isWrappableNoneditable = (editor, node) => {
        const dom2 = editor.dom;
        return isElementNode$1(node) && dom2.getContentEditable(node) === "false" && isWrapNoneditableTarget(editor, node) && dom2.select('[contenteditable="true"]', node).length === 0;
      };
      const replaceVars = (value2, vars) => {
        if (isFunction(value2)) {
          return value2(vars);
        } else if (isNonNullable(vars)) {
          value2 = value2.replace(/%(\w+)/g, (str, name2) => {
            return vars[name2] || str;
          });
        }
        return value2;
      };
      const isEq$5 = (str1, str2) => {
        str1 = str1 || "";
        str2 = str2 || "";
        str1 = "" + (str1.nodeName || str1);
        str2 = "" + (str2.nodeName || str2);
        return str1.toLowerCase() === str2.toLowerCase();
      };
      const normalizeStyleValue = (value2, name2) => {
        if (isNullable(value2)) {
          return null;
        } else {
          let strValue = String(value2);
          if (name2 === "color" || name2 === "backgroundColor") {
            strValue = rgbaToHexString(strValue);
          }
          if (name2 === "fontWeight" && value2 === 700) {
            strValue = "bold";
          }
          if (name2 === "fontFamily") {
            strValue = strValue.replace(/[\'\"]/g, "").replace(/,\s+/g, ",");
          }
          return strValue;
        }
      };
      const getStyle = (dom2, node, name2) => {
        const style = dom2.getStyle(node, name2);
        return normalizeStyleValue(style, name2);
      };
      const getTextDecoration = (dom2, node) => {
        let decoration;
        dom2.getParent(node, (n) => {
          if (isElement$7(n)) {
            decoration = dom2.getStyle(n, "text-decoration");
            return !!decoration && decoration !== "none";
          } else {
            return false;
          }
        });
        return decoration;
      };
      const getParents$2 = (dom2, node, selector) => {
        return dom2.getParents(node, selector, dom2.getRoot());
      };
      const isFormatPredicate = (editor, formatName, predicate) => {
        const formats = editor.formatter.get(formatName);
        return isNonNullable(formats) && exists(formats, predicate);
      };
      const isVariableFormatName = (editor, formatName) => {
        const hasVariableValues = (format) => {
          const isVariableValue = (val) => isFunction(val) || val.length > 1 && val.charAt(0) === "%";
          return exists(["styles", "attributes"], (key) => get$a(format, key).exists((field2) => {
            const fieldValues = isArray$1(field2) ? field2 : values(field2);
            return exists(fieldValues, isVariableValue);
          }));
        };
        return isFormatPredicate(editor, formatName, hasVariableValues);
      };
      const areSimilarFormats = (editor, formatName, otherFormatName) => {
        const validKeys = ["inline", "block", "selector", "attributes", "styles", "classes"];
        const filterObj = (format) => filter$4(format, (_, key) => exists(validKeys, (validKey) => validKey === key));
        return isFormatPredicate(editor, formatName, (fmt1) => {
          const filteredFmt1 = filterObj(fmt1);
          return isFormatPredicate(editor, otherFormatName, (fmt2) => {
            const filteredFmt2 = filterObj(fmt2);
            return equal$1(filteredFmt1, filteredFmt2);
          });
        });
      };
      const isBlockFormat = (format) => hasNonNullableKey(format, "block");
      const isWrappingBlockFormat = (format) => isBlockFormat(format) && format.wrapper === true;
      const isNonWrappingBlockFormat = (format) => isBlockFormat(format) && format.wrapper !== true;
      const isSelectorFormat = (format) => hasNonNullableKey(format, "selector");
      const isInlineFormat = (format) => hasNonNullableKey(format, "inline");
      const isMixedFormat = (format) => isSelectorFormat(format) && isInlineFormat(format) && is$4(get$a(format, "mixed"), true);
      const shouldExpandToSelector = (format) => isSelectorFormat(format) && format.expand !== false && !isInlineFormat(format);
      const getEmptyCaretContainers = (node) => {
        const nodes = [];
        let tempNode = node;
        while (tempNode) {
          if (isText$b(tempNode) && tempNode.data !== ZWSP$1 || tempNode.childNodes.length > 1) {
            return [];
          }
          if (isElement$7(tempNode)) {
            nodes.push(tempNode);
          }
          tempNode = tempNode.firstChild;
        }
        return nodes;
      };
      const isCaretContainerEmpty = (node) => {
        return getEmptyCaretContainers(node).length > 0;
      };
      const isEmptyCaretFormatElement = (element) => {
        return isCaretNode(element.dom) && isCaretContainerEmpty(element.dom);
      };
      const isBookmarkNode = isBookmarkNode$1;
      const getParents$1 = getParents$2;
      const isWhiteSpaceNode = isWhiteSpaceNode$1;
      const isTextBlock$1 = isTextBlock$2;
      const isBogusBr$1 = (node) => {
        return isBr$7(node) && node.getAttribute("data-mce-bogus") && !node.nextSibling;
      };
      const findParentContentEditable = (dom2, node) => {
        let parent2 = node;
        while (parent2) {
          if (isElement$7(parent2) && dom2.getContentEditable(parent2)) {
            return dom2.getContentEditable(parent2) === "false" ? parent2 : node;
          }
          parent2 = parent2.parentNode;
        }
        return node;
      };
      const walkText = (start2, node, offset, predicate) => {
        const str = node.data;
        if (start2) {
          for (let i = offset; i > 0; i--) {
            if (predicate(str.charAt(i - 1))) {
              return i;
            }
          }
        } else {
          for (let i = offset; i < str.length; i++) {
            if (predicate(str.charAt(i))) {
              return i;
            }
          }
        }
        return -1;
      };
      const findSpace = (start2, node, offset) => walkText(start2, node, offset, (c) => isNbsp(c) || isWhiteSpace(c));
      const findContent = (start2, node, offset) => walkText(start2, node, offset, isContent);
      const findWordEndPoint = (dom2, body, container, offset, start2, includeTrailingSpaces) => {
        let lastTextNode;
        const closestRoot = dom2.getParent(container, (node) => isEditingHost(node) || dom2.isBlock(node));
        const rootNode = isNonNullable(closestRoot) ? closestRoot : body;
        const walk2 = (container2, offset2, pred) => {
          const textSeeker = TextSeeker(dom2);
          const walker = start2 ? textSeeker.backwards : textSeeker.forwards;
          return Optional.from(walker(container2, offset2, (text2, textOffset) => {
            if (isBookmarkNode(text2.parentNode)) {
              return -1;
            } else {
              lastTextNode = text2;
              return pred(start2, text2, textOffset);
            }
          }, rootNode));
        };
        const spaceResult = walk2(container, offset, findSpace);
        return spaceResult.bind((result) => includeTrailingSpaces ? walk2(result.container, result.offset + (start2 ? -1 : 0), findContent) : Optional.some(result)).orThunk(() => lastTextNode ? Optional.some({ container: lastTextNode, offset: start2 ? 0 : lastTextNode.length }) : Optional.none());
      };
      const findSelectorEndPoint = (dom2, formatList, rng, container, siblingName) => {
        const sibling2 = container[siblingName];
        if (isText$b(container) && isEmpty$5(container.data) && sibling2) {
          container = sibling2;
        }
        const parents2 = getParents$1(dom2, container);
        for (let i = 0; i < parents2.length; i++) {
          for (let y = 0; y < formatList.length; y++) {
            const curFormat = formatList[y];
            if (isNonNullable(curFormat.collapsed) && curFormat.collapsed !== rng.collapsed) {
              continue;
            }
            if (isSelectorFormat(curFormat) && dom2.is(parents2[i], curFormat.selector)) {
              return parents2[i];
            }
          }
        }
        return container;
      };
      const findBlockEndPoint = (dom2, formatList, container, siblingName) => {
        let node = container;
        const root = dom2.getRoot();
        const format = formatList[0];
        if (isBlockFormat(format)) {
          node = format.wrapper ? null : dom2.getParent(container, format.block, root);
        }
        if (!node) {
          const scopeRoot = dom2.getParent(container, "LI,TD,TH,SUMMARY") ?? root;
          node = dom2.getParent(
            isText$b(container) ? container.parentNode : container,
            // Fixes #6183 where it would expand to editable parent element in inline mode
            (node2) => node2 !== root && isTextBlock$1(dom2.schema, node2),
            scopeRoot
          );
        }
        if (node && isBlockFormat(format) && format.wrapper) {
          node = getParents$1(dom2, node, "ul,ol").reverse()[0] || node;
        }
        if (!node) {
          node = container;
          while (node && node[siblingName] && !dom2.isBlock(node[siblingName])) {
            node = node[siblingName];
            if (isEq$5(node, "br")) {
              break;
            }
          }
        }
        return node || container;
      };
      const isAtBlockBoundary$1 = (dom2, root, container, siblingName) => {
        const parent2 = container.parentNode;
        if (isNonNullable(container[siblingName])) {
          return false;
        } else if (parent2 === root || isNullable(parent2) || dom2.isBlock(parent2)) {
          return true;
        } else {
          return isAtBlockBoundary$1(dom2, root, parent2, siblingName);
        }
      };
      const findParentContainer = (dom2, formatList, container, offset, start2, expandToBlock) => {
        let parent2 = container;
        const siblingName = start2 ? "previousSibling" : "nextSibling";
        const root = dom2.getRoot();
        if (isText$b(container) && !isWhiteSpaceNode(container)) {
          if (start2 ? offset > 0 : offset < container.data.length) {
            return container;
          }
        }
        while (parent2) {
          if (isEditingHost(parent2)) {
            return container;
          }
          if (!formatList[0].block_expand && dom2.isBlock(parent2)) {
            return expandToBlock ? parent2 : container;
          }
          for (let sibling2 = parent2[siblingName]; sibling2; sibling2 = sibling2[siblingName]) {
            const allowSpaces = isText$b(sibling2) && !isAtBlockBoundary$1(dom2, root, sibling2, siblingName);
            if (!isBookmarkNode(sibling2) && !isBogusBr$1(sibling2) && !isWhiteSpaceNode(sibling2, allowSpaces)) {
              return parent2;
            }
          }
          if (parent2 === root || parent2.parentNode === root) {
            container = parent2;
            break;
          }
          parent2 = parent2.parentNode;
        }
        return container;
      };
      const isSelfOrParentBookmark = (container) => isBookmarkNode(container.parentNode) || isBookmarkNode(container);
      const expandRng = (dom2, rng, formatList, expandOptions = {}) => {
        const { includeTrailingSpace = false, expandToBlock = true } = expandOptions;
        const editableHost = dom2.getParent(rng.commonAncestorContainer, (node) => isEditingHost(node));
        const root = isNonNullable(editableHost) ? editableHost : dom2.getRoot();
        let { startContainer, startOffset, endContainer, endOffset } = rng;
        const format = formatList[0];
        if (isElement$7(startContainer) && startContainer.hasChildNodes()) {
          startContainer = getNode$1(startContainer, startOffset);
          if (isText$b(startContainer)) {
            startOffset = 0;
          }
        }
        if (isElement$7(endContainer) && endContainer.hasChildNodes()) {
          endContainer = getNode$1(endContainer, rng.collapsed ? endOffset : endOffset - 1);
          if (isText$b(endContainer)) {
            endOffset = endContainer.data.length;
          }
        }
        startContainer = findParentContentEditable(dom2, startContainer);
        endContainer = findParentContentEditable(dom2, endContainer);
        if (isSelfOrParentBookmark(startContainer)) {
          startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
          if (rng.collapsed) {
            startContainer = startContainer.previousSibling || startContainer;
          } else {
            startContainer = startContainer.nextSibling || startContainer;
          }
          if (isText$b(startContainer)) {
            startOffset = rng.collapsed ? startContainer.length : 0;
          }
        }
        if (isSelfOrParentBookmark(endContainer)) {
          endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
          if (rng.collapsed) {
            endContainer = endContainer.nextSibling || endContainer;
          } else {
            endContainer = endContainer.previousSibling || endContainer;
          }
          if (isText$b(endContainer)) {
            endOffset = rng.collapsed ? 0 : endContainer.length;
          }
        }
        if (rng.collapsed) {
          const startPoint = findWordEndPoint(dom2, root, startContainer, startOffset, true, includeTrailingSpace);
          startPoint.each(({ container, offset }) => {
            startContainer = container;
            startOffset = offset;
          });
          const endPoint = findWordEndPoint(dom2, root, endContainer, endOffset, false, includeTrailingSpace);
          endPoint.each(({ container, offset }) => {
            endContainer = container;
            endOffset = offset;
          });
        }
        if (isInlineFormat(format) || format.block_expand) {
          if (!isInlineFormat(format) || (!isText$b(startContainer) || startOffset === 0)) {
            startContainer = findParentContainer(dom2, formatList, startContainer, startOffset, true, expandToBlock);
          }
          if (!isInlineFormat(format) || (!isText$b(endContainer) || endOffset === endContainer.data.length)) {
            endContainer = findParentContainer(dom2, formatList, endContainer, endOffset, false, expandToBlock);
          }
        }
        if (shouldExpandToSelector(format)) {
          startContainer = findSelectorEndPoint(dom2, formatList, rng, startContainer, "previousSibling");
          endContainer = findSelectorEndPoint(dom2, formatList, rng, endContainer, "nextSibling");
        }
        if (isBlockFormat(format) || isSelectorFormat(format)) {
          startContainer = findBlockEndPoint(dom2, formatList, startContainer, "previousSibling");
          endContainer = findBlockEndPoint(dom2, formatList, endContainer, "nextSibling");
          if (isBlockFormat(format)) {
            if (!dom2.isBlock(startContainer)) {
              startContainer = findParentContainer(dom2, formatList, startContainer, startOffset, true, expandToBlock);
              if (isText$b(startContainer)) {
                startOffset = 0;
              }
            }
            if (!dom2.isBlock(endContainer)) {
              endContainer = findParentContainer(dom2, formatList, endContainer, endOffset, false, expandToBlock);
              if (isText$b(endContainer)) {
                endOffset = endContainer.data.length;
              }
            }
          }
        }
        if (isElement$7(startContainer) && startContainer.parentNode) {
          startOffset = dom2.nodeIndex(startContainer);
          startContainer = startContainer.parentNode;
        }
        if (isElement$7(endContainer) && endContainer.parentNode) {
          endOffset = dom2.nodeIndex(endContainer) + 1;
          endContainer = endContainer.parentNode;
        }
        return {
          startContainer,
          startOffset,
          endContainer,
          endOffset
        };
      };
      const walk$3 = (dom2, rng, callback) => {
        const startOffset = rng.startOffset;
        const startContainer = getNode$1(rng.startContainer, startOffset);
        const endOffset = rng.endOffset;
        const endContainer = getNode$1(rng.endContainer, endOffset - 1);
        const exclude = (nodes) => {
          const firstNode = nodes[0];
          if (isText$b(firstNode) && firstNode === startContainer && startOffset >= firstNode.data.length) {
            nodes.splice(0, 1);
          }
          const lastNode = nodes[nodes.length - 1];
          if (endOffset === 0 && nodes.length > 0 && lastNode === endContainer && isText$b(lastNode)) {
            nodes.splice(nodes.length - 1, 1);
          }
          return nodes;
        };
        const collectSiblings = (node, name2, endNode) => {
          const siblings3 = [];
          for (; node && node !== endNode; node = node[name2]) {
            siblings3.push(node);
          }
          return siblings3;
        };
        const findEndPoint = (node, root) => dom2.getParent(node, (node2) => node2.parentNode === root, root);
        const walkBoundary = (startNode, endNode, next2) => {
          const siblingName = next2 ? "nextSibling" : "previousSibling";
          for (let node = startNode, parent2 = node.parentNode; node && node !== endNode; node = parent2) {
            parent2 = node.parentNode;
            const siblings3 = collectSiblings(node === startNode ? node : node[siblingName], siblingName);
            if (siblings3.length) {
              if (!next2) {
                siblings3.reverse();
              }
              callback(exclude(siblings3));
            }
          }
        };
        if (startContainer === endContainer) {
          return callback(exclude([startContainer]));
        }
        const ancestor2 = dom2.findCommonAncestor(startContainer, endContainer) ?? dom2.getRoot();
        if (dom2.isChildOf(startContainer, endContainer)) {
          return walkBoundary(startContainer, ancestor2, true);
        }
        if (dom2.isChildOf(endContainer, startContainer)) {
          return walkBoundary(endContainer, ancestor2);
        }
        const startPoint = findEndPoint(startContainer, ancestor2) || startContainer;
        const endPoint = findEndPoint(endContainer, ancestor2) || endContainer;
        walkBoundary(startContainer, startPoint, true);
        const siblings2 = collectSiblings(startPoint === startContainer ? startPoint : startPoint.nextSibling, "nextSibling", endPoint === endContainer ? endPoint.nextSibling : endPoint);
        if (siblings2.length) {
          callback(exclude(siblings2));
        }
        walkBoundary(endContainer, endPoint);
      };
      const validBlocks = [
        // Codesample plugin
        'pre[class*=language-][contenteditable="false"]',
        // Image plugin - captioned image
        "figure.image",
        // Mediaembed plugin
        "div[data-ephox-embed-iri]",
        // Pageembed plugin
        "div.tiny-pageembed",
        // Tableofcontents plugin
        "div.mce-toc",
        "div[data-mce-toc]",
        // Footnootes plugin
        "div.mce-footnotes"
      ];
      const isZeroWidth = (elem) => isText$c(elem) && get$4(elem) === ZWSP$1;
      const context = (editor, elem, wrapName, nodeName) => parentElement(elem).fold(() => "skipping", (parent2) => {
        if (nodeName === "br" || isZeroWidth(elem)) {
          return "valid";
        } else if (isAnnotation(elem)) {
          return "existing";
        } else if (isCaretNode(elem.dom)) {
          return "caret";
        } else if (exists(validBlocks, (selector) => is$2(elem, selector))) {
          return "valid-block";
        } else if (!isValid(editor, wrapName, nodeName) || !isValid(editor, name(parent2), wrapName)) {
          return "invalid-child";
        } else {
          return "valid";
        }
      });
      const applyWordGrab = (editor, rng) => {
        const r2 = expandRng(editor.dom, rng, [{ inline: "span" }]);
        rng.setStart(r2.startContainer, r2.startOffset);
        rng.setEnd(r2.endContainer, r2.endOffset);
        editor.selection.setRng(rng);
      };
      const applyAnnotation = (elem, masterUId, data2, annotationName, decorate, directAnnotation) => {
        const { uid: uid2 = masterUId, ...otherData } = data2;
        add$2(elem, annotation());
        set$4(elem, `${dataAnnotationId()}`, uid2);
        set$4(elem, `${dataAnnotation()}`, annotationName);
        const { attributes = {}, classes = [] } = decorate(uid2, otherData);
        setAll$1(elem, attributes);
        add$1(elem, classes);
        if (directAnnotation) {
          if (classes.length > 0) {
            set$4(elem, `${dataAnnotationClasses()}`, classes.join(","));
          }
          const attributeNames = keys(attributes);
          if (attributeNames.length > 0) {
            set$4(elem, `${dataAnnotationAttributes()}`, attributeNames.join(","));
          }
        }
      };
      const removeDirectAnnotation = (elem) => {
        remove$4(elem, annotation());
        remove$9(elem, `${dataAnnotationId()}`);
        remove$9(elem, `${dataAnnotation()}`);
        remove$9(elem, `${dataAnnotationActive()}`);
        const customAttrNames = getOpt(elem, `${dataAnnotationAttributes()}`).map((names) => names.split(",")).getOr([]);
        const customClasses = getOpt(elem, `${dataAnnotationClasses()}`).map((names) => names.split(",")).getOr([]);
        each$e(customAttrNames, (name2) => remove$9(elem, name2));
        remove$3(elem, customClasses);
        remove$9(elem, `${dataAnnotationClasses()}`);
        remove$9(elem, `${dataAnnotationAttributes()}`);
      };
      const makeAnnotation = (eDoc, uid2, data2, annotationName, decorate) => {
        const master = SugarElement.fromTag("span", eDoc);
        applyAnnotation(master, uid2, data2, annotationName, decorate, false);
        return master;
      };
      const annotate = (editor, rng, uid2, annotationName, decorate, data2) => {
        const newWrappers = [];
        const master = makeAnnotation(editor.getDoc(), uid2, data2, annotationName, decorate);
        const wrapper = value$1();
        const finishWrapper = () => {
          wrapper.clear();
        };
        const getOrOpenWrapper = () => wrapper.get().getOrThunk(() => {
          const nu2 = shallow(master);
          newWrappers.push(nu2);
          wrapper.set(nu2);
          return nu2;
        });
        const processElements = (elems) => {
          each$e(elems, processElement);
        };
        const processElement = (elem) => {
          const ctx = context(editor, elem, "span", name(elem));
          switch (ctx) {
            case "invalid-child": {
              finishWrapper();
              const children2 = children$1(elem);
              processElements(children2);
              finishWrapper();
              break;
            }
            case "valid-block": {
              finishWrapper();
              applyAnnotation(elem, uid2, data2, annotationName, decorate, true);
              break;
            }
            case "valid": {
              const w = getOrOpenWrapper();
              wrap$2(elem, w);
              break;
            }
          }
        };
        const processNodes = (nodes) => {
          const elems = map$3(nodes, SugarElement.fromDom);
          processElements(elems);
        };
        walk$3(editor.dom, rng, (nodes) => {
          finishWrapper();
          processNodes(nodes);
        });
        return newWrappers;
      };
      const annotateWithBookmark = (editor, name2, settings, data2) => {
        editor.undoManager.transact(() => {
          const selection = editor.selection;
          const initialRng = selection.getRng();
          const hasFakeSelection = getCellsFromEditor(editor).length > 0;
          const masterUid = generate("mce-annotation");
          if (initialRng.collapsed && !hasFakeSelection) {
            applyWordGrab(editor, initialRng);
          }
          if (selection.getRng().collapsed && !hasFakeSelection) {
            const wrapper = makeAnnotation(editor.getDoc(), masterUid, data2, name2, settings.decorate);
            set$3(wrapper, nbsp);
            selection.getRng().insertNode(wrapper.dom);
            selection.select(wrapper.dom);
          } else {
            preserve(selection, false, () => {
              runOnRanges(editor, (selectionRng) => {
                annotate(editor, selectionRng, masterUid, name2, settings.decorate, data2);
              });
            });
          }
        });
      };
      const Annotator = (editor) => {
        const registry2 = create$a();
        setup$D(editor, registry2);
        const changes = setup$E(editor, registry2);
        const isSpan2 = isTag("span");
        const removeAnnotations = (elements) => {
          each$e(elements, (element) => {
            if (isSpan2(element)) {
              unwrap(element);
            } else {
              removeDirectAnnotation(element);
            }
          });
        };
        return {
          /**
           * Registers a specific annotator by name
           *
           * @method register
           * @param {String} name the name of the annotation
           * @param {Object} settings settings for the annotation (e.g. decorate)
           */
          register: (name2, settings) => {
            registry2.register(name2, settings);
          },
          /**
           * Applies the annotation at the current selection using data
           *
           * @method annotate
           * @param {String} name the name of the annotation to apply
           * @param {Object} data information to pass through to this particular
           * annotation
           */
          annotate: (name2, data2) => {
            registry2.lookup(name2).each((settings) => {
              annotateWithBookmark(editor, name2, settings, data2);
            });
          },
          /**
           * Executes the specified callback when the current selection matches the annotation or not.
           *
           * @method annotationChanged
           * @param {String} name Name of annotation to listen for
           * @param {Function} callback Callback with (state, name, and data) fired when the annotation
           * at the cursor changes. If state if false, data will not be provided.
           */
          annotationChanged: (name2, callback) => {
            changes.addListener(name2, callback);
          },
          /**
           * Removes any annotations from the current selection that match
           * the name
           *
           * @method remove
           * @param {String} name the name of the annotation to remove
           */
          remove: (name2) => {
            identify(editor, Optional.some(name2)).each(({ elements }) => {
              const bookmark = editor.selection.getBookmark();
              removeAnnotations(elements);
              editor.selection.moveToBookmark(bookmark);
            });
          },
          /**
           * Removes all annotations that match the specified name from the entire document.
           *
           * @method removeAll
           * @param {String} name the name of the annotation to remove
           */
          removeAll: (name2) => {
            const bookmark = editor.selection.getBookmark();
            each$d(findAll(editor, name2), (elements, _) => {
              removeAnnotations(elements);
            });
            editor.selection.moveToBookmark(bookmark);
          },
          /**
           * Retrieve all the annotations for a given name
           *
           * @method getAll
           * @param {String} name the name of the annotations to retrieve
           * @return {Object} an index of annotations from uid => DOM nodes
           */
          getAll: (name2) => {
            const directory = findAll(editor, name2);
            return map$2(directory, (elems) => map$3(elems, (elem) => elem.dom));
          }
        };
      };
      const BookmarkManager = (selection) => {
        return {
          /**
           * Returns a bookmark location for the current selection. This bookmark object
           * can then be used to restore the selection after some content modification to the document.
           *
           * @method getBookmark
           * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
           * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
           * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
           * @example
           * // Stores a bookmark of the current selection
           * const bm = tinymce.activeEditor.selection.getBookmark();
           *
           * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
           *
           * // Restore the selection bookmark
           * tinymce.activeEditor.selection.moveToBookmark(bm);
           */
          getBookmark: curry(getBookmark$1, selection),
          /**
           * Restores the selection to the specified bookmark.
           *
           * @method moveToBookmark
           * @param {Object} bookmark Bookmark to restore selection from.
           * @example
           * // Stores a bookmark of the current selection
           * const bm = tinymce.activeEditor.selection.getBookmark();
           *
           * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
           *
           * // Restore the selection bookmark
           * tinymce.activeEditor.selection.moveToBookmark(bm);
           */
          moveToBookmark: curry(moveToBookmark, selection)
        };
      };
      BookmarkManager.isBookmarkNode = isBookmarkNode$1;
      const isXYWithinRange = (clientX, clientY, range2) => {
        if (range2.collapsed) {
          return false;
        } else {
          return exists(range2.getClientRects(), (rect) => containsXY(rect, clientX, clientY));
        }
      };
      const clamp$1 = (offset, element) => {
        const max2 = isText$c(element) ? get$4(element).length : children$1(element).length + 1;
        if (offset > max2) {
          return max2;
        } else if (offset < 0) {
          return 0;
        }
        return offset;
      };
      const normalizeRng = (rng) => SimSelection.range(rng.start, clamp$1(rng.soffset, rng.start), rng.finish, clamp$1(rng.foffset, rng.finish));
      const isOrContains = (root, elm) => !isRestrictedNode(elm.dom) && (contains(root, elm) || eq(root, elm));
      const isRngInRoot = (root) => (rng) => isOrContains(root, rng.start) && isOrContains(root, rng.finish);
      const shouldStore = (editor) => editor.inline || Env.browser.isFirefox();
      const nativeRangeToSelectionRange = (r2) => SimSelection.range(SugarElement.fromDom(r2.startContainer), r2.startOffset, SugarElement.fromDom(r2.endContainer), r2.endOffset);
      const readRange = (win) => {
        const selection = win.getSelection();
        const rng = !selection || selection.rangeCount === 0 ? Optional.none() : Optional.from(selection.getRangeAt(0));
        return rng.map(nativeRangeToSelectionRange);
      };
      const getBookmark = (root) => {
        const win = defaultView(root);
        return readRange(win.dom).filter(isRngInRoot(root));
      };
      const validate = (root, bookmark) => Optional.from(bookmark).filter(isRngInRoot(root)).map(normalizeRng);
      const bookmarkToNativeRng = (bookmark) => {
        const rng = document.createRange();
        try {
          rng.setStart(bookmark.start.dom, bookmark.soffset);
          rng.setEnd(bookmark.finish.dom, bookmark.foffset);
          return Optional.some(rng);
        } catch {
          return Optional.none();
        }
      };
      const store = (editor) => {
        const newBookmark = shouldStore(editor) ? getBookmark(SugarElement.fromDom(editor.getBody())) : Optional.none();
        editor.bookmark = newBookmark.isSome() ? newBookmark : editor.bookmark;
      };
      const getRng = (editor) => {
        const bookmark = editor.bookmark ? editor.bookmark : Optional.none();
        return bookmark.bind((x) => validate(SugarElement.fromDom(editor.getBody()), x)).bind(bookmarkToNativeRng);
      };
      const restore = (editor) => {
        getRng(editor).each((rng) => editor.selection.setRng(rng));
      };
      const isEditorUIElement$1 = (elm) => {
        const className = elm.className.toString();
        return className.indexOf("tox-") !== -1 || className.indexOf("mce-") !== -1;
      };
      const FocusManager = {
        isEditorUIElement: isEditorUIElement$1
      };
      const wrappedSetTimeout = (callback, time) => {
        if (!isNumber(time)) {
          time = 0;
        }
        return window.setTimeout(callback, time);
      };
      const wrappedSetInterval = (callback, time) => {
        if (!isNumber(time)) {
          time = 0;
        }
        return window.setInterval(callback, time);
      };
      const Delay = {
        /**
         * Sets a timeout that's similar to the native browser <a href="https://developer.mozilla.org/en-US/docs/Web/API/setTimeout">setTimeout</a>
         * API, except that it checks if the editor instance is still alive when the callback gets executed.
         *
         * @method setEditorTimeout
         * @param {tinymce.Editor} editor Editor instance to check the removed state on.
         * @param {Function} callback Callback to execute when timer runs out.
         * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
         * @return {Number} Timeout id number.
         */
        setEditorTimeout: (editor, callback, time) => {
          return wrappedSetTimeout(() => {
            if (!editor.removed) {
              callback();
            }
          }, time);
        },
        /**
         * Sets an interval timer that's similar to native browser <a href="https://developer.mozilla.org/en-US/docs/Web/API/setInterval">setInterval</a>
         * API, except that it checks if the editor instance is still alive when the callback gets executed.
         *
         * @method setEditorInterval
         * @param {Function} callback Callback to execute when interval time runs out.
         * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
         * @return {Number} Timeout id number.
         */
        setEditorInterval: (editor, callback, time) => {
          const timer = wrappedSetInterval(() => {
            if (!editor.removed) {
              callback();
            } else {
              window.clearInterval(timer);
            }
          }, time);
          return timer;
        }
      };
      const isManualNodeChange = (e) => {
        return e.type === "nodechange" && e.selectionChange;
      };
      const registerPageMouseUp = (editor, throttledStore) => {
        const mouseUpPage = () => {
          throttledStore.throttle();
        };
        DOMUtils.DOM.bind(document, "mouseup", mouseUpPage);
        editor.on("remove", () => {
          DOMUtils.DOM.unbind(document, "mouseup", mouseUpPage);
        });
      };
      const registerMouseUp = (editor, throttledStore) => {
        editor.on("mouseup touchend", (_e) => {
          throttledStore.throttle();
        });
      };
      const registerEditorEvents = (editor, throttledStore) => {
        registerMouseUp(editor, throttledStore);
        editor.on("keyup NodeChange AfterSetSelectionRange", (e) => {
          if (!isManualNodeChange(e)) {
            store(editor);
          }
        });
      };
      const register$6 = (editor) => {
        const throttledStore = first$1(() => {
          store(editor);
        }, 0);
        editor.on("init", () => {
          if (editor.inline) {
            registerPageMouseUp(editor, throttledStore);
          }
          registerEditorEvents(editor, throttledStore);
        });
        editor.on("remove", () => {
          throttledStore.cancel();
        });
      };
      let documentFocusInHandler;
      const DOM$c = DOMUtils.DOM;
      const isEditorUIElement = (elm) => {
        return isElement$7(elm) && FocusManager.isEditorUIElement(elm);
      };
      const isEditorContentAreaElement = (elm) => {
        const classList = elm.classList;
        if (classList !== void 0) {
          return classList.contains("tox-edit-area") || classList.contains("tox-edit-area__iframe") || classList.contains("mce-content-body");
        } else {
          return false;
        }
      };
      const isUIElement = (editor, elm) => {
        const customSelector = getCustomUiSelector(editor);
        const parent2 = DOM$c.getParent(elm, (elm2) => {
          return isEditorUIElement(elm2) || (customSelector ? editor.dom.is(elm2, customSelector) : false);
        });
        return parent2 !== null;
      };
      const getActiveElement = (editor) => {
        try {
          const root = getRootNode(SugarElement.fromDom(editor.getElement()));
          return active(root).fold(() => document.body, (x) => x.dom);
        } catch {
          return document.body;
        }
      };
      const registerEvents$1 = (editorManager, e) => {
        const editor = e.editor;
        register$6(editor);
        const toggleContentAreaOnFocus = (editor2, fn) => {
          if (shouldHighlightOnFocus(editor2) && editor2.inline !== true) {
            const contentArea = SugarElement.fromDom(editor2.getContainer());
            fn(contentArea, "tox-edit-focus");
          }
        };
        editor.on("focusin", () => {
          const focusedEditor = editorManager.focusedEditor;
          if (isEditorContentAreaElement(getActiveElement(editor))) {
            toggleContentAreaOnFocus(editor, add$2);
          }
          if (focusedEditor !== editor) {
            if (focusedEditor) {
              focusedEditor.dispatch("blur", { focusedEditor: editor });
            }
            editorManager.setActive(editor);
            editorManager.focusedEditor = editor;
            editor.dispatch("focus", { blurredEditor: focusedEditor });
            editor.focus(true);
          }
        });
        editor.on("focusout", () => {
          Delay.setEditorTimeout(editor, () => {
            const focusedEditor = editorManager.focusedEditor;
            if (!isEditorContentAreaElement(getActiveElement(editor)) || focusedEditor !== editor) {
              toggleContentAreaOnFocus(editor, remove$4);
            }
            if (!isUIElement(editor, getActiveElement(editor)) && focusedEditor === editor) {
              editor.dispatch("blur", { focusedEditor: null });
              editorManager.focusedEditor = null;
            }
          });
        });
        if (!documentFocusInHandler) {
          documentFocusInHandler = (e2) => {
            const activeEditor = editorManager.activeEditor;
            if (activeEditor) {
              getOriginalEventTarget(e2).each((target) => {
                const elem = target;
                if (elem.ownerDocument === document) {
                  if (elem !== document.body && !isUIElement(activeEditor, elem) && editorManager.focusedEditor === activeEditor) {
                    activeEditor.dispatch("blur", { focusedEditor: null });
                    editorManager.focusedEditor = null;
                  }
                }
              });
            }
          };
          DOM$c.bind(document, "focusin", documentFocusInHandler);
        }
      };
      const unregisterDocumentEvents = (editorManager, e) => {
        if (editorManager.focusedEditor === e.editor) {
          editorManager.focusedEditor = null;
        }
        if (!editorManager.activeEditor && documentFocusInHandler) {
          DOM$c.unbind(document, "focusin", documentFocusInHandler);
          documentFocusInHandler = null;
        }
      };
      const setup$C = (editorManager) => {
        editorManager.on("AddEditor", curry(registerEvents$1, editorManager));
        editorManager.on("RemoveEditor", curry(unregisterDocumentEvents, editorManager));
      };
      const getContentEditableHost = (editor, node) => editor.dom.getParent(node, (node2) => editor.dom.getContentEditable(node2) === "true");
      const hasContentEditableFalseParent$1 = (editor, node) => editor.dom.getParent(node, (node2) => editor.dom.getContentEditable(node2) === "false") !== null;
      const getCollapsedNode = (rng) => rng.collapsed ? Optional.from(getNode$1(rng.startContainer, rng.startOffset)).map(SugarElement.fromDom) : Optional.none();
      const getFocusInElement = (root, rng) => getCollapsedNode(rng).bind((node) => {
        if (isTableSection(node)) {
          return Optional.some(node);
        } else if (!contains(root, node)) {
          return Optional.some(root);
        } else {
          return Optional.none();
        }
      });
      const normalizeSelection = (editor, rng) => {
        getFocusInElement(SugarElement.fromDom(editor.getBody()), rng).bind((elm) => {
          return firstPositionIn(elm.dom);
        }).fold(() => {
          editor.selection.normalize();
        }, (caretPos) => editor.selection.setRng(caretPos.toRange()));
      };
      const focusBody = (body) => {
        if (body.setActive) {
          try {
            body.setActive();
          } catch {
            body.focus();
          }
        } else {
          body.focus();
        }
      };
      const hasElementFocus = (elm) => hasFocus$1(elm) || search(elm).isSome();
      const hasIframeFocus = (editor) => isNonNullable(editor.iframeElement) && hasFocus$1(SugarElement.fromDom(editor.iframeElement));
      const hasInlineFocus = (editor) => {
        const rawBody = editor.getBody();
        return rawBody && hasElementFocus(SugarElement.fromDom(rawBody));
      };
      const hasUiFocus = (editor) => {
        const dos = getRootNode(SugarElement.fromDom(editor.getElement()));
        return active(dos).filter((elem) => !isEditorContentAreaElement(elem.dom) && isUIElement(editor, elem.dom)).isSome();
      };
      const hasFocus = (editor) => editor.inline ? hasInlineFocus(editor) : hasIframeFocus(editor);
      const hasEditorOrUiFocus = (editor) => hasFocus(editor) || hasUiFocus(editor);
      const focusEditor = (editor) => {
        const selection = editor.selection;
        const body = editor.getBody();
        let rng = selection.getRng();
        editor.quirks.refreshContentEditable();
        const restoreBookmark = (editor2) => {
          getRng(editor2).each((bookmarkRng) => {
            editor2.selection.setRng(bookmarkRng);
            rng = bookmarkRng;
          });
        };
        if (!hasFocus(editor) && editor.hasEditableRoot()) {
          restoreBookmark(editor);
        }
        const contentEditableHost = getContentEditableHost(editor, selection.getNode());
        if (contentEditableHost && editor.dom.isChildOf(contentEditableHost, body)) {
          if (!hasContentEditableFalseParent$1(editor, contentEditableHost)) {
            focusBody(body);
          }
          focusBody(contentEditableHost);
          if (!editor.hasEditableRoot()) {
            restoreBookmark(editor);
          }
          normalizeSelection(editor, rng);
          activateEditor(editor);
          return;
        }
        if (!editor.inline) {
          if (!Env.browser.isOpera()) {
            focusBody(body);
          }
          editor.getWin().focus();
        }
        if (Env.browser.isFirefox() || editor.inline) {
          focusBody(body);
          normalizeSelection(editor, rng);
        }
        activateEditor(editor);
      };
      const activateEditor = (editor) => editor.editorManager.setActive(editor);
      const focus = (editor, skipFocus) => {
        if (editor.removed) {
          return;
        }
        if (skipFocus) {
          activateEditor(editor);
        } else {
          focusEditor(editor);
        }
      };
      const VK = {
        BACKSPACE: 8,
        DELETE: 46,
        DOWN: 40,
        ENTER: 13,
        ESC: 27,
        LEFT: 37,
        RIGHT: 39,
        SPACEBAR: 32,
        TAB: 9,
        UP: 38,
        PAGE_UP: 33,
        PAGE_DOWN: 34,
        END: 35,
        HOME: 36,
        modifierPressed: (e) => {
          return e.shiftKey || e.ctrlKey || e.altKey || VK.metaKeyPressed(e);
        },
        metaKeyPressed: (e) => {
          return Env.os.isMacOS() || Env.os.isiOS() ? e.metaKey : e.ctrlKey && !e.altKey;
        }
      };
      const ucVideoNodeName = "uc-video";
      const isUcVideo = (el) => el.nodeName.toLowerCase() === ucVideoNodeName;
      const elementSelectionAttr = "data-mce-selected";
      const controlElmSelector = `table,img,figure.image,hr,video,span.mce-preview-object,details,${ucVideoNodeName}`;
      const abs = Math.abs;
      const round$1 = Math.round;
      const resizeHandles = {
        // Name: x multiplier, y multiplier, delta size x, delta size y
        nw: [0, 0, -1, -1],
        ne: [1, 0, 1, -1],
        se: [1, 1, 1, 1],
        sw: [0, 1, -1, 1]
      };
      const isTouchEvent = (evt) => evt.type === "longpress" || evt.type.indexOf("touch") === 0;
      const ControlSelection = (selection, editor) => {
        const dom2 = editor.dom;
        const editableDoc = editor.getDoc();
        const rootDocument = document;
        const rootElement = editor.getBody();
        let selectedElm, selectedElmGhost, resizeHelper, selectedHandle, resizeBackdrop;
        let startX, startY, startW, startH, ratio, resizeStarted;
        let width;
        let height;
        let startScrollWidth;
        let startScrollHeight;
        const isImage2 = (elm) => isNonNullable(elm) && (isImg(elm) || dom2.is(elm, "figure.image"));
        const isMedia2 = (elm) => isMedia$2(elm) || dom2.hasClass(elm, "mce-preview-object");
        const isEventOnImageOutsideRange = (evt, range2) => {
          if (isTouchEvent(evt)) {
            const touch = evt.touches[0];
            return isImage2(evt.target) && !isXYWithinRange(touch.clientX, touch.clientY, range2);
          } else {
            return isImage2(evt.target) && !isXYWithinRange(evt.clientX, evt.clientY, range2);
          }
        };
        const contextMenuSelectImage = (evt) => {
          const target = evt.target;
          if (isEventOnImageOutsideRange(evt, editor.selection.getRng()) && !evt.isDefaultPrevented()) {
            editor.selection.select(target);
          }
        };
        const getResizeTargets = (elm) => {
          if (dom2.hasClass(elm, "mce-preview-object") && isNonNullable(elm.firstElementChild)) {
            return [elm, elm.firstElementChild];
          } else if (dom2.is(elm, "figure.image")) {
            return [elm.querySelector("img")];
          } else {
            return [elm];
          }
        };
        const isResizable = (elm) => {
          const selector = getObjectResizing(editor);
          if (!selector || editor.mode.isReadOnly()) {
            return false;
          }
          if (elm.getAttribute("data-mce-resize") === "false") {
            return false;
          }
          if (elm === editor.getBody()) {
            return false;
          }
          if (dom2.hasClass(elm, "mce-preview-object") && isNonNullable(elm.firstElementChild)) {
            return is$2(SugarElement.fromDom(elm.firstElementChild), selector);
          } else {
            return is$2(SugarElement.fromDom(elm), selector);
          }
        };
        const createGhostElement = (dom3, elm) => {
          if (isMedia2(elm)) {
            return dom3.create("img", { src: Env.transparentSrc });
          } else if (isTable$2(elm)) {
            const isNorth = startsWith(selectedHandle.name, "n");
            const rowSelect = isNorth ? head : last$2;
            const tableElm = elm.cloneNode(true);
            rowSelect(dom3.select("tr", tableElm)).each((tr) => {
              const cells2 = dom3.select("td,th", tr);
              dom3.setStyle(tr, "height", null);
              each$e(cells2, (cell2) => dom3.setStyle(cell2, "height", null));
            });
            return tableElm;
          } else {
            return elm.cloneNode(true);
          }
        };
        const setSizeProp = (element, name2, value2) => {
          if (isNonNullable(value2)) {
            const targets = getResizeTargets(element);
            each$e(targets, (target) => {
              if (target.style[name2] || !editor.schema.isValid(target.nodeName.toLowerCase(), name2)) {
                dom2.setStyle(target, name2, value2);
              } else {
                if (isUcVideo(target)) {
                  target[name2] = value2;
                  const minimumWidth = 400;
                  if (target.width > minimumWidth && !(name2 === "width" && value2 < minimumWidth)) {
                    target[name2] = value2;
                    dom2.setAttrib(target, name2, "" + value2);
                  } else {
                    const value3 = name2 === "height" ? minimumWidth * ratio : minimumWidth;
                    target[name2] = value3;
                    dom2.setAttrib(target, name2, "" + value3);
                  }
                } else {
                  dom2.setAttrib(target, name2, "" + value2);
                }
              }
            });
          }
        };
        const setGhostElmSize = (ghostElm, width2, height2) => {
          setSizeProp(ghostElm, "width", width2);
          setSizeProp(ghostElm, "height", height2);
        };
        const resizeGhostElement = (e) => {
          let deltaX, deltaY, proportional;
          let resizeHelperX, resizeHelperY;
          deltaX = e.screenX - startX;
          deltaY = e.screenY - startY;
          width = deltaX * selectedHandle[2] + startW;
          height = deltaY * selectedHandle[3] + startH;
          width = width < 5 ? 5 : width;
          height = height < 5 ? 5 : height;
          if ((isImage2(selectedElm) || isMedia2(selectedElm) || isUcVideo(selectedElm)) && getResizeImgProportional(editor) !== false) {
            proportional = !VK.modifierPressed(e);
          } else {
            proportional = VK.modifierPressed(e);
          }
          if (proportional) {
            if (abs(deltaX) > abs(deltaY)) {
              height = round$1(width * ratio);
              width = round$1(height / ratio);
            } else {
              width = round$1(height / ratio);
              height = round$1(width * ratio);
            }
          }
          setGhostElmSize(selectedElmGhost, width, height);
          resizeHelperX = selectedHandle.startPos.x + deltaX;
          resizeHelperY = selectedHandle.startPos.y + deltaY;
          resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0;
          resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0;
          dom2.setStyles(resizeHelper, {
            left: resizeHelperX,
            top: resizeHelperY,
            display: "block"
          });
          resizeHelper.innerHTML = width + " &times; " + height;
          deltaX = rootElement.scrollWidth - startScrollWidth;
          deltaY = rootElement.scrollHeight - startScrollHeight;
          if (deltaX + deltaY !== 0) {
            dom2.setStyles(resizeHelper, {
              left: resizeHelperX - deltaX,
              top: resizeHelperY - deltaY
            });
          }
          if (!resizeStarted) {
            fireObjectResizeStart(editor, selectedElm, startW, startH, "corner-" + selectedHandle.name);
            resizeStarted = true;
          }
        };
        const endGhostResize = () => {
          const wasResizeStarted = resizeStarted;
          resizeStarted = false;
          if (wasResizeStarted) {
            setSizeProp(selectedElm, "width", width);
            setSizeProp(selectedElm, "height", height);
          }
          dom2.unbind(editableDoc, "mousemove", resizeGhostElement);
          dom2.unbind(editableDoc, "mouseup", endGhostResize);
          if (rootDocument !== editableDoc) {
            dom2.unbind(rootDocument, "mousemove", resizeGhostElement);
            dom2.unbind(rootDocument, "mouseup", endGhostResize);
          }
          dom2.remove(selectedElmGhost);
          dom2.remove(resizeHelper);
          dom2.remove(resizeBackdrop);
          showResizeRect(selectedElm);
          if (wasResizeStarted) {
            fireObjectResized(editor, selectedElm, width, height, "corner-" + selectedHandle.name);
            dom2.setAttrib(selectedElm, "style", dom2.getAttrib(selectedElm, "style"));
          }
          editor.nodeChanged();
        };
        const showResizeRect = (targetElm) => {
          unbindResizeHandleEvents();
          const position = dom2.getPos(targetElm, rootElement);
          const selectedElmX = position.x;
          const selectedElmY = position.y;
          const rect = targetElm.getBoundingClientRect();
          const targetWidth = rect.width || rect.right - rect.left;
          const targetHeight = rect.height || rect.bottom - rect.top;
          if (selectedElm !== targetElm) {
            hideResizeRect();
            selectedElm = targetElm;
            width = height = 0;
          }
          const e = editor.dispatch("ObjectSelected", { target: targetElm });
          if (isResizable(targetElm) && !e.isDefaultPrevented()) {
            each$d(resizeHandles, (handle2, name2) => {
              const startDrag = (e2) => {
                const target = getResizeTargets(selectedElm)[0];
                startX = e2.screenX;
                startY = e2.screenY;
                startW = target.clientWidth;
                startH = target.clientHeight;
                ratio = startH / startW;
                selectedHandle = handle2;
                selectedHandle.name = name2;
                selectedHandle.startPos = {
                  x: targetWidth * handle2[0] + selectedElmX,
                  y: targetHeight * handle2[1] + selectedElmY
                };
                startScrollWidth = rootElement.scrollWidth;
                startScrollHeight = rootElement.scrollHeight;
                resizeBackdrop = dom2.add(rootElement, "div", {
                  "class": "mce-resize-backdrop",
                  "data-mce-bogus": "all"
                });
                dom2.setStyles(resizeBackdrop, {
                  position: "fixed",
                  left: "0",
                  top: "0",
                  width: "100%",
                  height: "100%"
                });
                selectedElmGhost = createGhostElement(dom2, selectedElm);
                dom2.addClass(selectedElmGhost, "mce-clonedresizable");
                dom2.setAttrib(selectedElmGhost, "data-mce-bogus", "all");
                selectedElmGhost.contentEditable = "false";
                dom2.setStyles(selectedElmGhost, {
                  left: selectedElmX,
                  top: selectedElmY,
                  margin: 0
                });
                setGhostElmSize(selectedElmGhost, targetWidth, targetHeight);
                selectedElmGhost.removeAttribute(elementSelectionAttr);
                rootElement.appendChild(selectedElmGhost);
                dom2.bind(editableDoc, "mousemove", resizeGhostElement);
                dom2.bind(editableDoc, "mouseup", endGhostResize);
                if (rootDocument !== editableDoc) {
                  dom2.bind(rootDocument, "mousemove", resizeGhostElement);
                  dom2.bind(rootDocument, "mouseup", endGhostResize);
                }
                resizeHelper = dom2.add(rootElement, "div", {
                  "class": "mce-resize-helper",
                  "data-mce-bogus": "all"
                }, startW + " &times; " + startH);
              };
              let handleElm = dom2.get("mceResizeHandle" + name2);
              if (handleElm) {
                dom2.remove(handleElm);
              }
              handleElm = dom2.add(rootElement, "div", {
                "id": "mceResizeHandle" + name2,
                "data-mce-bogus": "all",
                "class": "mce-resizehandle",
                "unselectable": true,
                "style": "cursor:" + name2 + "-resize; margin:0; padding:0"
              });
              dom2.bind(handleElm, "mousedown", (e2) => {
                e2.stopImmediatePropagation();
                e2.preventDefault();
                startDrag(e2);
              });
              handle2.elm = handleElm;
              dom2.setStyles(handleElm, {
                left: targetWidth * handle2[0] + selectedElmX - handleElm.offsetWidth / 2,
                top: targetHeight * handle2[1] + selectedElmY - handleElm.offsetHeight / 2
              });
            });
          } else {
            hideResizeRect(false);
          }
        };
        const throttledShowResizeRect = first$1(showResizeRect, 0);
        const hideResizeRect = (removeSelected = true) => {
          throttledShowResizeRect.cancel();
          unbindResizeHandleEvents();
          if (selectedElm && removeSelected) {
            selectedElm.removeAttribute(elementSelectionAttr);
          }
          each$d(resizeHandles, (value2, name2) => {
            const handleElm = dom2.get("mceResizeHandle" + name2);
            if (handleElm) {
              dom2.unbind(handleElm);
              dom2.remove(handleElm);
            }
          });
        };
        const isChildOrEqual = (node, parent2) => dom2.isChildOf(node, parent2);
        const updateResizeRect = (e) => {
          if (resizeStarted || editor.removed || editor.composing) {
            return;
          }
          const targetElm = e.type === "mousedown" ? e.target : selection.getNode();
          const controlElm = closest$3(SugarElement.fromDom(targetElm), controlElmSelector).map((e2) => e2.dom).filter((e2) => dom2.isEditable(e2.parentElement) || e2.nodeName === "IMG" && dom2.isEditable(e2)).getOrUndefined();
          const selectedValue = isNonNullable(controlElm) ? dom2.getAttrib(controlElm, elementSelectionAttr, "1") : "1";
          each$e(dom2.select(`img[${elementSelectionAttr}],hr[${elementSelectionAttr}]`), (img) => {
            img.removeAttribute(elementSelectionAttr);
          });
          if (isNonNullable(controlElm) && isChildOrEqual(controlElm, rootElement) && hasEditorOrUiFocus(editor)) {
            disableGeckoResize();
            const startElm = selection.getStart(true);
            if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {
              dom2.setAttrib(controlElm, elementSelectionAttr, selectedValue);
              throttledShowResizeRect.throttle(controlElm);
              return;
            }
          }
          hideResizeRect();
        };
        const unbindResizeHandleEvents = () => {
          each$d(resizeHandles, (handle2) => {
            if (handle2.elm) {
              dom2.unbind(handle2.elm);
              delete handle2.elm;
            }
          });
        };
        const disableGeckoResize = () => {
          try {
            editor.getDoc().execCommand("enableObjectResizing", false, "false");
          } catch {
          }
        };
        editor.on("init", () => {
          disableGeckoResize();
          editor.on("NodeChange ResizeEditor ResizeWindow ResizeContent drop", updateResizeRect);
          editor.on("keyup compositionend", (e) => {
            if (selectedElm && selectedElm.nodeName === "TABLE") {
              updateResizeRect(e);
            }
          });
          editor.on("hide blur", hideResizeRect);
          editor.on("contextmenu longpress", contextMenuSelectImage, true);
        });
        editor.on("remove", unbindResizeHandleEvents);
        const destroy2 = () => {
          throttledShowResizeRect.cancel();
          selectedElm = selectedElmGhost = resizeBackdrop = null;
        };
        return {
          isResizable,
          showResizeRect,
          hideResizeRect,
          updateResizeRect,
          destroy: destroy2
        };
      };
      const fromPoint = (clientX, clientY, doc) => {
        const win = defaultView(SugarElement.fromDom(doc));
        return getAtPoint(win.dom, clientX, clientY).map((simRange) => {
          const rng = doc.createRange();
          rng.setStart(simRange.start.dom, simRange.soffset);
          rng.setEnd(simRange.finish.dom, simRange.foffset);
          return rng;
        }).getOrUndefined();
      };
      const isEq$4 = (rng1, rng2) => {
        return isNonNullable(rng1) && isNonNullable(rng2) && (rng1.startContainer === rng2.startContainer && rng1.startOffset === rng2.startOffset) && (rng1.endContainer === rng2.endContainer && rng1.endOffset === rng2.endOffset);
      };
      const findParent = (node, rootNode, predicate) => {
        let currentNode = node;
        while (currentNode && currentNode !== rootNode) {
          if (predicate(currentNode)) {
            return currentNode;
          }
          currentNode = currentNode.parentNode;
        }
        return null;
      };
      const hasParent$1 = (node, rootNode, predicate) => findParent(node, rootNode, predicate) !== null;
      const hasParentWithName = (node, rootNode, name2) => hasParent$1(node, rootNode, (node2) => node2.nodeName === name2);
      const isCeFalseCaretContainer = (node, rootNode) => isCaretContainer$2(node) && !hasParent$1(node, rootNode, isCaretNode);
      const hasBrBeforeAfter = (dom2, node, left) => {
        const parentNode = node.parentNode;
        if (parentNode) {
          const walker = new DomTreeWalker(node, dom2.getParent(parentNode, dom2.isBlock) || dom2.getRoot());
          let currentNode;
          while (currentNode = walker[left ? "prev" : "next"]()) {
            if (isBr$7(currentNode)) {
              return true;
            }
          }
        }
        return false;
      };
      const isPrevNode = (node, name2) => node.previousSibling?.nodeName === name2;
      const hasContentEditableFalseParent = (root, node) => {
        let currentNode = node;
        while (currentNode && currentNode !== root) {
          if (isContentEditableFalse$a(currentNode)) {
            return true;
          }
          currentNode = currentNode.parentNode;
        }
        return false;
      };
      const findTextNodeRelative = (dom2, isAfterNode, collapsed, left, startNode) => {
        const body = dom2.getRoot();
        const nonEmptyElementsMap = dom2.schema.getNonEmptyElements();
        const parentNode = startNode.parentNode;
        let lastInlineElement;
        let node;
        if (!parentNode) {
          return Optional.none();
        }
        const parentBlockContainer = dom2.getParent(parentNode, dom2.isBlock) || body;
        if (left && isBr$7(startNode) && isAfterNode && dom2.isEmpty(parentBlockContainer)) {
          return Optional.some(CaretPosition(parentNode, dom2.nodeIndex(startNode)));
        }
        const walker = new DomTreeWalker(startNode, parentBlockContainer);
        while (node = walker[left ? "prev" : "next"]()) {
          if (dom2.getContentEditableParent(node) === "false" || isCeFalseCaretContainer(node, body)) {
            return Optional.none();
          }
          if (isText$b(node) && node.data.length > 0) {
            if (!hasParentWithName(node, body, "A")) {
              return Optional.some(CaretPosition(node, left ? node.data.length : 0));
            }
            return Optional.none();
          }
          if (dom2.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
            return Optional.none();
          }
          lastInlineElement = node;
        }
        if (isComment(lastInlineElement)) {
          return Optional.none();
        }
        if (collapsed && lastInlineElement) {
          return Optional.some(CaretPosition(lastInlineElement, 0));
        }
        return Optional.none();
      };
      const normalizeEndPoint = (dom2, collapsed, start2, rng) => {
        const body = dom2.getRoot();
        let node;
        let normalized = false;
        let container = start2 ? rng.startContainer : rng.endContainer;
        let offset = start2 ? rng.startOffset : rng.endOffset;
        const isAfterNode = isElement$7(container) && offset === container.childNodes.length;
        const nonEmptyElementsMap = dom2.schema.getNonEmptyElements();
        let directionLeft = start2;
        if (isCaretContainer$2(container)) {
          return Optional.none();
        }
        if (isElement$7(container) && offset > container.childNodes.length - 1) {
          directionLeft = false;
        }
        if (isDocument$1(container)) {
          container = body;
          offset = 0;
        }
        if (container === body) {
          if (directionLeft) {
            node = container.childNodes[offset > 0 ? offset - 1 : 0];
            if (node) {
              if (isCaretContainer$2(node)) {
                return Optional.none();
              }
              if (nonEmptyElementsMap[node.nodeName] || isTable$2(node)) {
                return Optional.none();
              }
            }
          }
          if (container.hasChildNodes()) {
            offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
            container = container.childNodes[offset];
            offset = isText$b(container) && isAfterNode ? container.data.length : 0;
            if (!collapsed && container === body.lastChild && isTable$2(container)) {
              return Optional.none();
            }
            if (hasContentEditableFalseParent(body, container) || isCaretContainer$2(container)) {
              return Optional.none();
            }
            if (isDetails(container)) {
              return Optional.none();
            }
            if (container.hasChildNodes() && !isTable$2(container)) {
              node = container;
              const walker = new DomTreeWalker(container, body);
              do {
                if (isContentEditableFalse$a(node) || isCaretContainer$2(node)) {
                  normalized = false;
                  break;
                }
                if (isText$b(node) && node.data.length > 0) {
                  offset = directionLeft ? 0 : node.data.length;
                  container = node;
                  normalized = true;
                  break;
                }
                if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCellOrCaption(node)) {
                  offset = dom2.nodeIndex(node);
                  container = node.parentNode;
                  if (!directionLeft) {
                    offset++;
                  }
                  normalized = true;
                  break;
                }
              } while (node = directionLeft ? walker.next() : walker.prev());
            }
          }
        }
        if (collapsed) {
          if (isText$b(container) && offset === 0) {
            findTextNodeRelative(dom2, isAfterNode, collapsed, true, container).each((pos) => {
              container = pos.container();
              offset = pos.offset();
              normalized = true;
            });
          }
          if (isElement$7(container)) {
            node = container.childNodes[offset];
            if (!node) {
              node = container.childNodes[offset - 1];
            }
            if (node && isBr$7(node) && !isPrevNode(node, "A") && !hasBrBeforeAfter(dom2, node, false) && !hasBrBeforeAfter(dom2, node, true)) {
              findTextNodeRelative(dom2, isAfterNode, collapsed, true, node).each((pos) => {
                container = pos.container();
                offset = pos.offset();
                normalized = true;
              });
            }
          }
        }
        if (directionLeft && !collapsed && isText$b(container) && offset === container.data.length) {
          findTextNodeRelative(dom2, isAfterNode, collapsed, false, container).each((pos) => {
            container = pos.container();
            offset = pos.offset();
            normalized = true;
          });
        }
        return normalized && container ? Optional.some(CaretPosition(container, offset)) : Optional.none();
      };
      const normalize$2 = (dom2, rng) => {
        const collapsed = rng.collapsed, normRng = rng.cloneRange();
        const startPos = CaretPosition.fromRangeStart(rng);
        normalizeEndPoint(dom2, collapsed, true, normRng).each((pos) => {
          if (!collapsed || !CaretPosition.isAbove(startPos, pos)) {
            normRng.setStart(pos.container(), pos.offset());
          }
        });
        if (!collapsed) {
          normalizeEndPoint(dom2, collapsed, false, normRng).each((pos) => {
            normRng.setEnd(pos.container(), pos.offset());
          });
        }
        if (collapsed) {
          normRng.collapse(true);
        }
        return isEq$4(rng, normRng) ? Optional.none() : Optional.some(normRng);
      };
      const splitText = (node, offset) => {
        return node.splitText(offset);
      };
      const split = (rng) => {
        let startContainer = rng.startContainer, startOffset = rng.startOffset, endContainer = rng.endContainer, endOffset = rng.endOffset;
        if (startContainer === endContainer && isText$b(startContainer)) {
          if (startOffset > 0 && startOffset < startContainer.data.length) {
            endContainer = splitText(startContainer, startOffset);
            startContainer = endContainer.previousSibling;
            if (endOffset > startOffset) {
              endOffset = endOffset - startOffset;
              const newContainer = splitText(endContainer, endOffset).previousSibling;
              startContainer = endContainer = newContainer;
              endOffset = newContainer.data.length;
              startOffset = 0;
            } else {
              endOffset = 0;
            }
          }
        } else {
          if (isText$b(startContainer) && startOffset > 0 && startOffset < startContainer.data.length) {
            startContainer = splitText(startContainer, startOffset);
            startOffset = 0;
          }
          if (isText$b(endContainer) && endOffset > 0 && endOffset < endContainer.data.length) {
            const newContainer = splitText(endContainer, endOffset).previousSibling;
            endContainer = newContainer;
            endOffset = newContainer.data.length;
          }
        }
        return {
          startContainer,
          startOffset,
          endContainer,
          endOffset
        };
      };
      const RangeUtils = (dom2) => {
        const walk2 = (rng, callback) => {
          return walk$3(dom2, rng, callback);
        };
        const split$12 = split;
        const normalize2 = (rng) => {
          return normalize$2(dom2, rng).fold(never, (normalizedRng) => {
            rng.setStart(normalizedRng.startContainer, normalizedRng.startOffset);
            rng.setEnd(normalizedRng.endContainer, normalizedRng.endOffset);
            return true;
          });
        };
        const expand = (rng, options = { type: "word" }) => {
          if (options.type === "word") {
            const rangeLike = expandRng(dom2, rng, [{ inline: "span" }], { includeTrailingSpace: false, expandToBlock: false });
            const newRange = dom2.createRng();
            newRange.setStart(rangeLike.startContainer, rangeLike.startOffset);
            newRange.setEnd(rangeLike.endContainer, rangeLike.endOffset);
            return newRange;
          }
          return rng;
        };
        return {
          walk: walk2,
          split: split$12,
          expand,
          normalize: normalize2
        };
      };
      RangeUtils.compareRanges = isEq$4;
      RangeUtils.getCaretRangeFromPoint = fromPoint;
      RangeUtils.getSelectedNode = getSelectedNode;
      RangeUtils.getNode = getNode$1;
      const walkUp = (navigation, doc) => {
        const frame = navigation.view(doc);
        return frame.fold(constant([]), (f) => {
          const parent2 = navigation.owner(f);
          const rest = walkUp(navigation, parent2);
          return [f].concat(rest);
        });
      };
      const pathTo = (element, navigation) => {
        const d = navigation.owner(element);
        return walkUp(navigation, d);
      };
      const view = (doc) => {
        const element = doc.dom === document ? Optional.none() : Optional.from(doc.dom.defaultView?.frameElement);
        return element.map(SugarElement.fromDom);
      };
      const owner = (element) => documentOrOwner(element);
      var Navigation = Object.freeze({
        __proto__: null,
        view,
        owner
      });
      const find = (element) => {
        const doc = getDocument();
        const scroll = get$5(doc);
        const frames = pathTo(element, Navigation);
        const offset = viewport(element);
        const r2 = foldr(frames, (b, a) => {
          const loc = viewport(a);
          return {
            left: b.left + loc.left,
            top: b.top + loc.top
          };
        }, { left: 0, top: 0 });
        return SugarPosition(r2.left + offset.left + scroll.left, r2.top + offset.top + scroll.top);
      };
      const excludeFromDescend = (element) => name(element) === "textarea";
      const fireScrollIntoViewEvent = (editor, data2) => {
        const scrollEvent = editor.dispatch("ScrollIntoView", data2);
        return scrollEvent.isDefaultPrevented();
      };
      const fireAfterScrollIntoViewEvent = (editor, data2) => {
        editor.dispatch("AfterScrollIntoView", data2);
      };
      const descend = (element, offset) => {
        const children2 = children$1(element);
        if (children2.length === 0 || excludeFromDescend(element)) {
          return { element, offset };
        } else if (offset < children2.length && !excludeFromDescend(children2[offset])) {
          return { element: children2[offset], offset: 0 };
        } else {
          const last2 = children2[children2.length - 1];
          if (excludeFromDescend(last2)) {
            return { element, offset };
          } else {
            if (name(last2) === "img") {
              return { element: last2, offset: 1 };
            } else if (isText$c(last2)) {
              return { element: last2, offset: get$4(last2).length };
            } else {
              return { element: last2, offset: children$1(last2).length };
            }
          }
        }
      };
      const markerInfo = (element, cleanupFun) => {
        const pos = absolute(element);
        const height = get$6(element);
        return {
          element,
          bottom: pos.top + height,
          height,
          pos,
          cleanup: cleanupFun
        };
      };
      const createMarker$1 = (element, offset) => {
        const startPoint = descend(element, offset);
        const span = SugarElement.fromHtml('<span data-mce-bogus="all" style="display: inline-block;">' + ZWSP$1 + "</span>");
        before$4(startPoint.element, span);
        return markerInfo(span, () => remove$8(span));
      };
      const elementMarker = (element) => markerInfo(SugarElement.fromDom(element), noop);
      const withMarker = (editor, f, rng, alignToTop) => {
        preserveWith(editor, (_s, _e) => applyWithMarker(editor, f, rng, alignToTop), rng);
      };
      const withScrollEvents = (editor, doc, f, marker, alignToTop) => {
        const data2 = { elm: marker.element.dom, alignToTop };
        if (fireScrollIntoViewEvent(editor, data2)) {
          return;
        }
        const scrollTop = get$5(doc).top;
        f(editor, doc, scrollTop, marker, alignToTop);
        fireAfterScrollIntoViewEvent(editor, data2);
      };
      const applyWithMarker = (editor, f, rng, alignToTop) => {
        const body = SugarElement.fromDom(editor.getBody());
        const doc = SugarElement.fromDom(editor.getDoc());
        reflow(body);
        const marker = createMarker$1(SugarElement.fromDom(rng.startContainer), rng.startOffset);
        withScrollEvents(editor, doc, f, marker, alignToTop);
        marker.cleanup();
      };
      const withElement = (editor, element, f, alignToTop) => {
        const doc = SugarElement.fromDom(editor.getDoc());
        withScrollEvents(editor, doc, f, elementMarker(element), alignToTop);
      };
      const preserveWith = (editor, f, rng) => {
        const startElement = rng.startContainer;
        const startOffset = rng.startOffset;
        const endElement = rng.endContainer;
        const endOffset = rng.endOffset;
        f(SugarElement.fromDom(startElement), SugarElement.fromDom(endElement));
        const newRng = editor.dom.createRng();
        newRng.setStart(startElement, startOffset);
        newRng.setEnd(endElement, endOffset);
        editor.selection.setRng(rng);
      };
      const scrollToMarker = (editor, marker, viewHeight, alignToTop, doc) => {
        const pos = marker.pos;
        const scrollMargin = 30;
        if (alignToTop) {
          to(pos.left, Math.max(0, pos.top - scrollMargin), doc);
        } else {
          const y = pos.top - viewHeight + marker.height + scrollMargin;
          to(-editor.getBody().getBoundingClientRect().left, y, doc);
        }
      };
      const intoWindowIfNeeded = (editor, doc, scrollTop, viewHeight, marker, alignToTop) => {
        const viewportBottom = viewHeight + scrollTop;
        const markerTop = marker.pos.top;
        const markerBottom = marker.bottom;
        const largerThanViewport = markerBottom - markerTop >= viewHeight;
        if (markerTop < scrollTop) {
          scrollToMarker(editor, marker, viewHeight, alignToTop !== false, doc);
        } else if (markerTop > viewportBottom) {
          const align = largerThanViewport ? alignToTop !== false : alignToTop === true;
          scrollToMarker(editor, marker, viewHeight, align, doc);
        } else if (markerBottom > viewportBottom && !largerThanViewport) {
          scrollToMarker(editor, marker, viewHeight, alignToTop === true, doc);
        }
      };
      const intoWindow = (editor, doc, scrollTop, marker, alignToTop) => {
        const viewHeight = defaultView(doc).dom.innerHeight;
        intoWindowIfNeeded(editor, doc, scrollTop, viewHeight, marker, alignToTop);
      };
      const intoFrame = (editor, doc, scrollTop, marker, alignToTop) => {
        const frameViewHeight = defaultView(doc).dom.innerHeight;
        intoWindowIfNeeded(editor, doc, scrollTop, frameViewHeight, marker, alignToTop);
        const op = find(marker.element);
        const viewportBounds = getBounds(window);
        if (op.top < viewportBounds.y) {
          intoView(marker.element, alignToTop !== false);
        } else if (op.top > viewportBounds.bottom) {
          intoView(marker.element, alignToTop === true);
        }
      };
      const rangeIntoWindow = (editor, rng, alignToTop) => withMarker(editor, intoWindow, rng, alignToTop);
      const elementIntoWindow = (editor, element, alignToTop) => withElement(editor, element, intoWindow, alignToTop);
      const rangeIntoFrame = (editor, rng, alignToTop) => withMarker(editor, intoFrame, rng, alignToTop);
      const elementIntoFrame = (editor, element, alignToTop) => withElement(editor, element, intoFrame, alignToTop);
      const scrollElementIntoView = (editor, element, alignToTop) => {
        const scroller = editor.inline ? elementIntoWindow : elementIntoFrame;
        scroller(editor, element, alignToTop);
      };
      const scrollRangeIntoView = (editor, rng, alignToTop) => {
        const scroller = editor.inline ? rangeIntoWindow : rangeIntoFrame;
        scroller(editor, rng, alignToTop);
      };
      const isEditableRange = (dom2, rng) => {
        if (rng.collapsed) {
          return dom2.isEditable(rng.startContainer);
        } else {
          return dom2.isEditable(rng.startContainer) && dom2.isEditable(rng.endContainer);
        }
      };
      const getEndpointElement = (root, rng, start2, real, resolve2) => {
        const container = start2 ? rng.startContainer : rng.endContainer;
        const offset = start2 ? rng.startOffset : rng.endOffset;
        return Optional.from(container).map(SugarElement.fromDom).map((elm) => !real || !rng.collapsed ? child$1(elm, resolve2(elm, offset)).getOr(elm) : elm).bind((elm) => isElement$8(elm) ? Optional.some(elm) : parent(elm).filter(isElement$8)).map((elm) => elm.dom).getOr(root);
      };
      const getStart = (root, rng, real = false) => getEndpointElement(root, rng, true, real, (elm, offset) => Math.min(childNodesCount(elm), offset));
      const getEnd = (root, rng, real = false) => getEndpointElement(root, rng, false, real, (elm, offset) => offset > 0 ? offset - 1 : offset);
      const skipEmptyTextNodes = (node, forwards) => {
        const orig = node;
        while (node && isText$b(node) && node.length === 0) {
          node = forwards ? node.nextSibling : node.previousSibling;
        }
        return node || orig;
      };
      const getNode = (root, rng) => {
        if (!rng) {
          return root;
        }
        let startContainer = rng.startContainer;
        let endContainer = rng.endContainer;
        const startOffset = rng.startOffset;
        const endOffset = rng.endOffset;
        let node = rng.commonAncestorContainer;
        if (!rng.collapsed) {
          if (startContainer === endContainer) {
            if (endOffset - startOffset < 2) {
              if (startContainer.hasChildNodes()) {
                node = startContainer.childNodes[startOffset];
              }
            }
          }
          if (isText$b(startContainer) && isText$b(endContainer)) {
            if (startContainer.length === startOffset) {
              startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);
            } else {
              startContainer = startContainer.parentNode;
            }
            if (endOffset === 0) {
              endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);
            } else {
              endContainer = endContainer.parentNode;
            }
            if (startContainer && startContainer === endContainer) {
              node = startContainer;
            }
          }
        }
        const elm = isText$b(node) ? node.parentNode : node;
        return isHTMLElement(elm) ? elm : root;
      };
      const getSelectedBlocks = (dom2, rng, startElm, endElm) => {
        const selectedBlocks = [];
        const root = dom2.getRoot();
        const start2 = dom2.getParent(startElm || getStart(root, rng, rng.collapsed), dom2.isBlock);
        const end2 = dom2.getParent(endElm || getEnd(root, rng, rng.collapsed), dom2.isBlock);
        if (start2 && start2 !== root) {
          selectedBlocks.push(start2);
        }
        if (start2 && end2 && start2 !== end2) {
          let node;
          const walker = new DomTreeWalker(start2, root);
          while ((node = walker.next()) && node !== end2) {
            if (dom2.isBlock(node)) {
              selectedBlocks.push(node);
            }
          }
        }
        if (end2 && start2 !== end2 && end2 !== root) {
          selectedBlocks.push(end2);
        }
        return selectedBlocks;
      };
      const select = (dom2, node, content) => Optional.from(node).bind((node2) => Optional.from(node2.parentNode).map((parent2) => {
        const idx = dom2.nodeIndex(node2);
        const rng = dom2.createRng();
        rng.setStart(parent2, idx);
        rng.setEnd(parent2, idx + 1);
        if (content) {
          moveEndPoint(dom2, rng, node2, true);
          moveEndPoint(dom2, rng, node2, false);
        }
        return rng;
      }));
      const processRanges = (editor, ranges) => map$3(ranges, (range2) => {
        const evt = editor.dispatch("GetSelectionRange", { range: range2 });
        return evt.range !== range2 ? evt.range : range2;
      });
      const typeLookup = {
        "#text": 3,
        "#comment": 8,
        "#cdata": 4,
        "#pi": 7,
        "#doctype": 10,
        "#document-fragment": 11
      };
      const walk$2 = (node, root, prev2) => {
        const startName = prev2 ? "lastChild" : "firstChild";
        const siblingName = prev2 ? "prev" : "next";
        if (node[startName]) {
          return node[startName];
        }
        if (node !== root) {
          let sibling2 = node[siblingName];
          if (sibling2) {
            return sibling2;
          }
          for (let parent2 = node.parent; parent2 && parent2 !== root; parent2 = parent2.parent) {
            sibling2 = parent2[siblingName];
            if (sibling2) {
              return sibling2;
            }
          }
        }
        return void 0;
      };
      const isEmptyTextNode = (node) => {
        const text2 = node.value ?? "";
        if (!isWhitespaceText(text2)) {
          return false;
        }
        const parentNode = node.parent;
        if (parentNode && (parentNode.name !== "span" || parentNode.attr("style")) && /^[ ]+$/.test(text2)) {
          return false;
        }
        return true;
      };
      const isNonEmptyElement = (node) => {
        const isNamedAnchor2 = node.name === "a" && !node.attr("href") && node.attr("id");
        return node.attr("name") || node.attr("id") && !node.firstChild || node.attr("data-mce-bookmark") || isNamedAnchor2;
      };
      class AstNode {
        /**
         * Creates a node of a specific type.
         *
         * @static
         * @method create
         * @param {String} name Name of the node type to create for example "b" or "#text".
         * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
         */
        static create(name2, attrs) {
          const node = new AstNode(name2, typeLookup[name2] || 1);
          if (attrs) {
            each$d(attrs, (value2, attrName) => {
              node.attr(attrName, value2);
            });
          }
          return node;
        }
        name;
        type;
        attributes;
        value;
        parent;
        firstChild;
        lastChild;
        next;
        prev;
        raw;
        /**
         * Constructs a new Node instance.
         *
         * @constructor
         * @method Node
         * @param {String} name Name of the node type.
         * @param {Number} type Numeric type representing the node.
         */
        constructor(name2, type2) {
          this.name = name2;
          this.type = type2;
          if (type2 === 1) {
            this.attributes = [];
            this.attributes.map = {};
          }
        }
        /**
         * Replaces the current node with the specified one.
         *
         * @method replace
         * @param {tinymce.html.Node} node Node to replace the current node with.
         * @return {tinymce.html.Node} The old node that got replaced.
         * @example
         * someNode.replace(someNewNode);
         */
        replace(node) {
          const self = this;
          if (node.parent) {
            node.remove();
          }
          self.insert(node, self);
          self.remove();
          return self;
        }
        attr(name2, value2) {
          const self = this;
          if (!isString(name2)) {
            if (isNonNullable(name2)) {
              each$d(name2, (value3, key) => {
                self.attr(key, value3);
              });
            }
            return self;
          }
          const attrs = self.attributes;
          if (attrs) {
            if (value2 !== void 0) {
              if (value2 === null) {
                if (name2 in attrs.map) {
                  delete attrs.map[name2];
                  let i = attrs.length;
                  while (i--) {
                    if (attrs[i].name === name2) {
                      attrs.splice(i, 1);
                      return self;
                    }
                  }
                }
                return self;
              }
              if (name2 in attrs.map) {
                let i = attrs.length;
                while (i--) {
                  if (attrs[i].name === name2) {
                    attrs[i].value = value2;
                    break;
                  }
                }
              } else {
                attrs.push({ name: name2, value: value2 });
              }
              attrs.map[name2] = value2;
              return self;
            }
            return attrs.map[name2];
          }
          return void 0;
        }
        /**
         * Does a shallow clones the node into a new node. It will also exclude id attributes since
         * there should only be one id per document.
         *
         * @method clone
         * @return {tinymce.html.Node} New copy of the original node.
         * @example
         * const clonedNode = node.clone();
         */
        clone() {
          const self = this;
          const clone2 = new AstNode(self.name, self.type);
          const selfAttrs = self.attributes;
          if (selfAttrs) {
            const cloneAttrs = [];
            cloneAttrs.map = {};
            for (let i = 0, l = selfAttrs.length; i < l; i++) {
              const selfAttr = selfAttrs[i];
              if (selfAttr.name !== "id") {
                cloneAttrs[cloneAttrs.length] = { name: selfAttr.name, value: selfAttr.value };
                cloneAttrs.map[selfAttr.name] = selfAttr.value;
              }
            }
            clone2.attributes = cloneAttrs;
          }
          clone2.value = self.value;
          return clone2;
        }
        /**
         * Wraps the node in in another node.
         *
         * @method wrap
         * @example
         * node.wrap(wrapperNode);
         */
        wrap(wrapper) {
          const self = this;
          if (self.parent) {
            self.parent.insert(wrapper, self);
            wrapper.append(self);
          }
          return self;
        }
        /**
         * Unwraps the node in other words it removes the node but keeps the children.
         *
         * @method unwrap
         * @example
         * node.unwrap();
         */
        unwrap() {
          const self = this;
          for (let node = self.firstChild; node; ) {
            const next2 = node.next;
            self.insert(node, self, true);
            node = next2;
          }
          self.remove();
        }
        /**
         * Removes the node from it's parent.
         *
         * @method remove
         * @return {tinymce.html.Node} Current node that got removed.
         * @example
         * node.remove();
         */
        remove() {
          const self = this, parent2 = self.parent, next2 = self.next, prev2 = self.prev;
          if (parent2) {
            if (parent2.firstChild === self) {
              parent2.firstChild = next2;
              if (next2) {
                next2.prev = null;
              }
            } else if (prev2) {
              prev2.next = next2;
            }
            if (parent2.lastChild === self) {
              parent2.lastChild = prev2;
              if (prev2) {
                prev2.next = null;
              }
            } else if (next2) {
              next2.prev = prev2;
            }
            self.parent = self.next = self.prev = null;
          }
          return self;
        }
        /**
         * Appends a new node as a child of the current node.
         *
         * @method append
         * @param {tinymce.html.Node} node Node to append as a child of the current one.
         * @return {tinymce.html.Node} The node that got appended.
         * @example
         * node.append(someNode);
         */
        append(node) {
          const self = this;
          if (node.parent) {
            node.remove();
          }
          const last2 = self.lastChild;
          if (last2) {
            last2.next = node;
            node.prev = last2;
            self.lastChild = node;
          } else {
            self.lastChild = self.firstChild = node;
          }
          node.parent = self;
          return node;
        }
        /**
         * Inserts a node at a specific position as a child of this node.
         *
         * @method insert
         * @param {tinymce.html.Node} node Node to insert as a child of this node.
         * @param {tinymce.html.Node} refNode Reference node to set node before/after.
         * @param {Boolean} before Optional state to insert the node before the reference node.
         * @return {tinymce.html.Node} The node that got inserted.
         * @example
         * parentNode.insert(newChildNode, oldChildNode);
         */
        insert(node, refNode, before2) {
          if (node.parent) {
            node.remove();
          }
          const parent2 = refNode.parent || this;
          if (before2) {
            if (refNode === parent2.firstChild) {
              parent2.firstChild = node;
            } else if (refNode.prev) {
              refNode.prev.next = node;
            }
            node.prev = refNode.prev;
            node.next = refNode;
            refNode.prev = node;
          } else {
            if (refNode === parent2.lastChild) {
              parent2.lastChild = node;
            } else if (refNode.next) {
              refNode.next.prev = node;
            }
            node.next = refNode.next;
            node.prev = refNode;
            refNode.next = node;
          }
          node.parent = parent2;
          return node;
        }
        /**
         * Get all descendants by name.
         *
         * @method getAll
         * @param {String} name Name of the descendant nodes to collect.
         * @return {Array} Array with descendant nodes matching the specified name.
         */
        getAll(name2) {
          const self = this;
          const collection = [];
          for (let node = self.firstChild; node; node = walk$2(node, self)) {
            if (node.name === name2) {
              collection.push(node);
            }
          }
          return collection;
        }
        /**
         * Get all children of this node.
         *
         * @method children
         * @return {Array} Array containing child nodes.
         */
        children() {
          const self = this;
          const collection = [];
          for (let node = self.firstChild; node; node = node.next) {
            collection.push(node);
          }
          return collection;
        }
        /**
         * Removes all children of the current node.
         *
         * @method empty
         * @return {tinymce.html.Node} The current node that got cleared.
         */
        empty() {
          const self = this;
          if (self.firstChild) {
            const nodes = [];
            for (let node = self.firstChild; node; node = walk$2(node, self)) {
              nodes.push(node);
            }
            let i = nodes.length;
            while (i--) {
              const node = nodes[i];
              node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
            }
          }
          self.firstChild = self.lastChild = null;
          return self;
        }
        /**
         * Returns true/false if the node is to be considered empty or not.
         *
         * @method isEmpty
         * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
         * @param {Object} whitespace Name/value object with elements that are automatically treated whitespace preservables.
         * @param {Function} predicate Optional predicate that gets called after the other rules determine that the node is empty. Should return true if the node is a content node.
         * @return {Boolean} true/false if the node is empty or not.
         * @example
         * node.isEmpty({ img: true });
         */
        isEmpty(elements, whitespace = {}, predicate) {
          const self = this;
          let node = self.firstChild;
          if (isNonEmptyElement(self)) {
            return false;
          }
          if (node) {
            do {
              if (node.type === 1) {
                if (node.attr("data-mce-bogus")) {
                  continue;
                }
                if (elements[node.name]) {
                  return false;
                }
                if (isNonEmptyElement(node)) {
                  return false;
                }
              }
              if (node.type === 8) {
                return false;
              }
              if (node.type === 3 && !isEmptyTextNode(node)) {
                return false;
              }
              if (node.type === 3 && node.parent && whitespace[node.parent.name] && isWhitespaceText(node.value ?? "")) {
                return false;
              }
              if (predicate && predicate(node)) {
                return false;
              }
            } while (node = walk$2(node, self));
          }
          return true;
        }
        /**
         * Walks to the next or previous node and returns that node or null if it wasn't found.
         *
         * @method walk
         * @param {Boolean} prev Optional previous node state defaults to false.
         * @return {tinymce.html.Node} Node that is next to or previous of the current node.
         */
        walk(prev2) {
          return walk$2(this, null, prev2);
        }
      }
      const unescapedTextParents = Tools.makeMap("NOSCRIPT STYLE SCRIPT XMP IFRAME NOEMBED NOFRAMES PLAINTEXT", " ");
      const containsZwsp = (node) => isString(node.nodeValue) && node.nodeValue.includes(ZWSP$1);
      const getTemporaryNodeSelector = (tempAttrs) => `${tempAttrs.length === 0 ? "" : `${map$3(tempAttrs, (attr) => `[${attr}]`).join(",")},`}[data-mce-bogus="all"]`;
      const getTemporaryNodes = (tempAttrs, body) => body.querySelectorAll(getTemporaryNodeSelector(tempAttrs));
      const createZwspCommentWalker = (body) => document.createTreeWalker(body, NodeFilter.SHOW_COMMENT, (node) => containsZwsp(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP);
      const createUnescapedZwspTextWalker = (body) => document.createTreeWalker(body, NodeFilter.SHOW_TEXT, (node) => {
        if (containsZwsp(node)) {
          const parent2 = node.parentNode;
          return parent2 && has$2(unescapedTextParents, parent2.nodeName) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
        } else {
          return NodeFilter.FILTER_SKIP;
        }
      });
      const hasZwspComment = (body) => createZwspCommentWalker(body).nextNode() !== null;
      const hasUnescapedZwspText = (body) => createUnescapedZwspTextWalker(body).nextNode() !== null;
      const hasTemporaryNode = (tempAttrs, body) => body.querySelector(getTemporaryNodeSelector(tempAttrs)) !== null;
      const trimTemporaryNodes = (tempAttrs, body) => {
        each$e(getTemporaryNodes(tempAttrs, body), (elm) => {
          const element = SugarElement.fromDom(elm);
          if (get$9(element, "data-mce-bogus") === "all") {
            remove$8(element);
          } else {
            each$e(tempAttrs, (attr) => {
              if (has$1(element, attr)) {
                remove$9(element, attr);
              }
            });
          }
        });
      };
      const emptyAllNodeValuesInWalker = (walker) => {
        let curr = walker.nextNode();
        while (curr !== null) {
          curr.nodeValue = null;
          curr = walker.nextNode();
        }
      };
      const emptyZwspComments = compose(emptyAllNodeValuesInWalker, createZwspCommentWalker);
      const emptyUnescapedZwspTexts = compose(emptyAllNodeValuesInWalker, createUnescapedZwspTextWalker);
      const trim$1 = (body, tempAttrs) => {
        const conditionalTrims = [
          {
            condition: curry(hasTemporaryNode, tempAttrs),
            action: curry(trimTemporaryNodes, tempAttrs)
          },
          {
            condition: hasZwspComment,
            action: emptyZwspComments
          },
          {
            condition: hasUnescapedZwspText,
            action: emptyUnescapedZwspTexts
          }
        ];
        let trimmed = body;
        let cloned = false;
        each$e(conditionalTrims, ({ condition, action: action2 }) => {
          if (condition(trimmed)) {
            if (!cloned) {
              trimmed = body.cloneNode(true);
              cloned = true;
            }
            action2(trimmed);
          }
        });
        return trimmed;
      };
      const cleanupBogusElements = (parent2) => {
        const bogusElements = descendants(parent2, "[data-mce-bogus]");
        each$e(bogusElements, (elem) => {
          const bogusValue = get$9(elem, "data-mce-bogus");
          if (bogusValue === "all") {
            remove$8(elem);
          } else if (isBr$6(elem)) {
            before$4(elem, SugarElement.fromText(zeroWidth));
            remove$8(elem);
          } else {
            unwrap(elem);
          }
        });
      };
      const cleanupInputNames = (parent2) => {
        const inputs = descendants(parent2, "input");
        each$e(inputs, (input) => {
          remove$9(input, "name");
        });
      };
      const trimEmptyContents = (editor, html2) => {
        const blockName = getForcedRootBlock(editor);
        const emptyRegExp = new RegExp(`^(<${blockName}[^>]*>(&nbsp;|&#160;|\\s| |<br \\/>|)<\\/${blockName}>[\r
]*|<br \\/>[\r
]*)$`);
        return html2.replace(emptyRegExp, "");
      };
      const getPlainTextContent = (editor, body) => {
        const doc = editor.getDoc();
        const dos = getRootNode(SugarElement.fromDom(editor.getBody()));
        const offscreenDiv = SugarElement.fromTag("div", doc);
        set$4(offscreenDiv, "data-mce-bogus", "all");
        setAll(offscreenDiv, {
          position: "fixed",
          left: "-9999999px",
          top: "0"
        });
        set$3(offscreenDiv, body.innerHTML);
        cleanupBogusElements(offscreenDiv);
        cleanupInputNames(offscreenDiv);
        const root = getContentContainer(dos);
        append$1(root, offscreenDiv);
        const content = trim$2(offscreenDiv.dom.innerText);
        remove$8(offscreenDiv);
        return content;
      };
      const getContentFromBody = (editor, args, body) => {
        let content;
        if (args.format === "raw") {
          content = Tools.trim(trim$2(trim$1(body, editor.serializer.getTempAttrs()).innerHTML));
        } else if (args.format === "text") {
          content = getPlainTextContent(editor, body);
        } else if (args.format === "tree") {
          content = editor.serializer.serialize(body, args);
        } else {
          content = trimEmptyContents(editor, editor.serializer.serialize(body, args));
        }
        const shouldTrim = args.format !== "text" && !isWsPreserveElement(SugarElement.fromDom(body));
        return shouldTrim && isString(content) ? Tools.trim(content) : content;
      };
      const getContentInternal = (editor, args) => Optional.from(editor.getBody()).fold(constant(args.format === "tree" ? new AstNode("body", 11) : ""), (body) => getContentFromBody(editor, args, body));
      const makeMap$1 = Tools.makeMap;
      const Writer = (settings) => {
        const html2 = [];
        settings = settings || {};
        const indent2 = settings.indent;
        const indentBefore = makeMap$1(settings.indent_before || "");
        const indentAfter = makeMap$1(settings.indent_after || "");
        const encode = Entities.getEncodeFunc(settings.entity_encoding || "raw", settings.entities);
        const htmlOutput = settings.element_format !== "xhtml";
        return {
          /**
           * Writes a start element, such as `<p id="a">`.
           *
           * @method start
           * @param {String} name Name of the element.
           * @param {Array} attrs Optional array of objects containing an attribute name and value, or undefined if the element has no attributes.
           * @param {Boolean} empty Optional empty state if the tag should serialize as a void element. For example: `<img />`
           */
          start: (name2, attrs, empty2) => {
            if (indent2 && indentBefore[name2] && html2.length > 0) {
              const value2 = html2[html2.length - 1];
              if (value2.length > 0 && value2 !== "\n") {
                html2.push("\n");
              }
            }
            html2.push("<", name2);
            if (attrs) {
              for (let i = 0, l = attrs.length; i < l; i++) {
                const attr = attrs[i];
                html2.push(" ", attr.name, '="', encode(attr.value, true), '"');
              }
            }
            if (!empty2 || htmlOutput) {
              html2[html2.length] = ">";
            } else {
              html2[html2.length] = " />";
            }
            if (empty2 && indent2 && indentAfter[name2] && html2.length > 0) {
              const value2 = html2[html2.length - 1];
              if (value2.length > 0 && value2 !== "\n") {
                html2.push("\n");
              }
            }
          },
          /**
           * Writes an end element, such as `</p>`.
           *
           * @method end
           * @param {String} name Name of the element.
           */
          end: (name2) => {
            let value2;
            html2.push("</", name2, ">");
            if (indent2 && indentAfter[name2] && html2.length > 0) {
              value2 = html2[html2.length - 1];
              if (value2.length > 0 && value2 !== "\n") {
                html2.push("\n");
              }
            }
          },
          /**
           * Writes a text node.
           *
           * @method text
           * @param {String} text String to write out.
           * @param {Boolean} raw Optional raw state. If true, the contents won't get encoded.
           */
          text: (text2, raw) => {
            if (text2.length > 0) {
              html2[html2.length] = raw ? text2 : encode(text2);
            }
          },
          /**
           * Writes a cdata node, such as `<![CDATA[data]]>`.
           *
           * @method cdata
           * @param {String} text String to write out inside the cdata.
           */
          cdata: (text2) => {
            html2.push("<![CDATA[", text2, "]]>");
          },
          /**
           * Writes a comment node, such as `<!-- Comment -->`.
           *
           * @method comment
           * @param {String} text String to write out inside the comment.
           */
          comment: (text2) => {
            html2.push("<!--", text2, "-->");
          },
          /**
           * Writes a processing instruction (PI) node, such as `<?xml attr="value" ?>`.
           *
           * @method pi
           * @param {String} name Name of the pi.
           * @param {String} text String to write out inside the pi.
           */
          pi: (name2, text2) => {
            if (text2) {
              html2.push("<?", name2, " ", encode(text2), "?>");
            } else {
              html2.push("<?", name2, "?>");
            }
            if (indent2) {
              html2.push("\n");
            }
          },
          /**
           * Writes a doctype node, such as `<!DOCTYPE data>`.
           *
           * @method doctype
           * @param {String} text String to write out inside the doctype.
           */
          doctype: (text2) => {
            html2.push("<!DOCTYPE", text2, ">", indent2 ? "\n" : "");
          },
          /**
           * Resets the internal buffer. For example, if one wants to reuse the writer.
           *
           * @method reset
           */
          reset: () => {
            html2.length = 0;
          },
          /**
           * Returns the contents that was serialized.
           *
           * @method getContent
           * @return {String} HTML contents that got written down.
           */
          getContent: () => {
            return html2.join("").replace(/\n$/, "");
          }
        };
      };
      const HtmlSerializer = (settings = {}, schema = Schema()) => {
        const writer = Writer(settings);
        settings.validate = "validate" in settings ? settings.validate : true;
        const serialize = (node) => {
          const validate2 = settings.validate;
          const handlers = {
            // #text
            3: (node2) => {
              writer.text(node2.value ?? "", node2.raw);
            },
            // #comment
            8: (node2) => {
              writer.comment(node2.value ?? "");
            },
            // Processing instruction
            7: (node2) => {
              writer.pi(node2.name, node2.value);
            },
            // Doctype
            10: (node2) => {
              writer.doctype(node2.value ?? "");
            },
            // CDATA
            4: (node2) => {
              writer.cdata(node2.value ?? "");
            },
            // Document fragment
            11: (node2) => {
              let tempNode = node2;
              if (tempNode = tempNode.firstChild) {
                do {
                  walk2(tempNode);
                } while (tempNode = tempNode.next);
              }
            }
          };
          writer.reset();
          const walk2 = (node2) => {
            const handler = handlers[node2.type];
            if (!handler) {
              const name2 = node2.name;
              const isEmpty2 = name2 in schema.getVoidElements();
              let attrs = node2.attributes;
              if (validate2 && attrs && attrs.length > 1) {
                const sortedAttrs = [];
                sortedAttrs.map = {};
                const elementRule = schema.getElementRule(node2.name);
                if (elementRule) {
                  for (let i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
                    const attrName = elementRule.attributesOrder[i];
                    if (attrName in attrs.map) {
                      const attrValue = attrs.map[attrName];
                      sortedAttrs.map[attrName] = attrValue;
                      sortedAttrs.push({ name: attrName, value: attrValue });
                    }
                  }
                  for (let i = 0, l = attrs.length; i < l; i++) {
                    const attrName = attrs[i].name;
                    if (!(attrName in sortedAttrs.map)) {
                      const attrValue = attrs.map[attrName];
                      sortedAttrs.map[attrName] = attrValue;
                      sortedAttrs.push({ name: attrName, value: attrValue });
                    }
                  }
                  attrs = sortedAttrs;
                }
              }
              writer.start(name2, attrs, isEmpty2);
              if (isNonHtmlElementRootName(name2)) {
                if (isString(node2.value)) {
                  writer.text(node2.value, true);
                }
                writer.end(name2);
              } else {
                if (!isEmpty2) {
                  let child2 = node2.firstChild;
                  if (child2) {
                    if ((name2 === "pre" || name2 === "textarea") && child2.type === 3 && child2.value?.[0] === "\n") {
                      writer.text("\n", true);
                    }
                    do {
                      walk2(child2);
                    } while (child2 = child2.next);
                  }
                  writer.end(name2);
                }
              }
            } else {
              handler(node2);
            }
          };
          if (node.type === 1 && !settings.inner) {
            walk2(node);
          } else if (node.type === 3) {
            handlers[3](node);
          } else {
            handlers[11](node);
          }
          return writer.getContent();
        };
        return {
          serialize
        };
      };
      const nonInheritableStyles = /* @__PURE__ */ new Set();
      (() => {
        const nonInheritableStylesArr = [
          "margin",
          "margin-left",
          "margin-right",
          "margin-top",
          "margin-bottom",
          "padding",
          "padding-left",
          "padding-right",
          "padding-top",
          "padding-bottom",
          "border",
          "border-width",
          "border-style",
          "border-color",
          "background",
          "background-attachment",
          "background-clip",
          "background-image",
          "background-origin",
          "background-position",
          "background-repeat",
          "background-size",
          "float",
          "position",
          "left",
          "right",
          "top",
          "bottom",
          "z-index",
          "display",
          "transform",
          "width",
          "max-width",
          "min-width",
          "height",
          "max-height",
          "min-height",
          "overflow",
          "overflow-x",
          "overflow-y",
          "text-overflow",
          "vertical-align",
          "transition",
          "transition-delay",
          "transition-duration",
          "transition-property",
          "transition-timing-function"
        ];
        each$e(nonInheritableStylesArr, (style) => {
          nonInheritableStyles.add(style);
        });
      })();
      const conditionalNonInheritableStyles = /* @__PURE__ */ new Set();
      (() => {
        const conditionalNonInheritableStylesArr = [
          "background-color"
        ];
        each$e(conditionalNonInheritableStylesArr, (style) => {
          conditionalNonInheritableStyles.add(style);
        });
      })();
      const shorthandStyleProps = ["font", "text-decoration", "text-emphasis"];
      const getStyles$1 = (dom2, node) => dom2.parseStyle(dom2.getAttrib(node, "style"));
      const getStyleProps = (dom2, node) => keys(getStyles$1(dom2, node));
      const isNonInheritableStyle = (style) => nonInheritableStyles.has(style);
      const isConditionalNonInheritableStyle = (style) => conditionalNonInheritableStyles.has(style);
      const hasNonInheritableStyles = (dom2, node) => exists(getStyleProps(dom2, node), (style) => isNonInheritableStyle(style));
      const hasConditionalNonInheritableStyles = (dom2, node) => hasNonInheritableStyles(dom2, node) && exists(getStyleProps(dom2, node), (style) => isConditionalNonInheritableStyle(style));
      const getLonghandStyleProps = (styles) => filter$5(styles, (style) => exists(shorthandStyleProps, (prop) => startsWith(style, prop)));
      const hasStyleConflict = (dom2, node, parentNode) => {
        const nodeStyleProps = getStyleProps(dom2, node);
        const parentNodeStyleProps = getStyleProps(dom2, parentNode);
        const valueMismatch = (prop) => {
          const nodeValue = dom2.getStyle(node, prop) ?? "";
          const parentValue = dom2.getStyle(parentNode, prop) ?? "";
          return isNotEmpty(nodeValue) && isNotEmpty(parentValue) && nodeValue !== parentValue;
        };
        return exists(nodeStyleProps, (nodeStyleProp) => {
          const propExists = (props) => exists(props, (prop) => prop === nodeStyleProp);
          if (!propExists(parentNodeStyleProps) && propExists(shorthandStyleProps)) {
            const longhandProps = getLonghandStyleProps(parentNodeStyleProps);
            return exists(longhandProps, valueMismatch);
          } else {
            return valueMismatch(nodeStyleProp);
          }
        });
      };
      const isChar = (forward, predicate, pos) => Optional.from(pos.container()).filter(isText$b).exists((text2) => {
        const delta = forward ? 0 : -1;
        return predicate(text2.data.charAt(pos.offset() + delta));
      });
      const isBeforeSpace = curry(isChar, true, isWhiteSpace);
      const isAfterSpace = curry(isChar, false, isWhiteSpace);
      const isEmptyText = (pos) => {
        const container = pos.container();
        return isText$b(container) && (container.data.length === 0 || isZwsp(container.data) && BookmarkManager.isBookmarkNode(container.parentNode));
      };
      const matchesElementPosition = (before2, predicate) => (pos) => getChildNodeAtRelativeOffset(before2 ? 0 : -1, pos).filter(predicate).isSome();
      const isImageBlock = (node) => isImg(node) && get$7(SugarElement.fromDom(node), "display") === "block";
      const isCefNode = (node) => isContentEditableFalse$a(node) && !isBogusAll(node);
      const isBeforeImageBlock = matchesElementPosition(true, isImageBlock);
      const isAfterImageBlock = matchesElementPosition(false, isImageBlock);
      const isBeforeMedia = matchesElementPosition(true, isMedia$2);
      const isAfterMedia = matchesElementPosition(false, isMedia$2);
      const isBeforeTable = matchesElementPosition(true, isTable$2);
      const isAfterTable = matchesElementPosition(false, isTable$2);
      const isBeforeContentEditableFalse = matchesElementPosition(true, isCefNode);
      const isAfterContentEditableFalse = matchesElementPosition(false, isCefNode);
      const dropLast = (xs) => xs.slice(0, -1);
      const parentsUntil = (start2, root, predicate) => {
        if (contains(root, start2)) {
          return dropLast(parents$1(start2, (elm) => {
            return predicate(elm) || eq(elm, root);
          }));
        } else {
          return [];
        }
      };
      const parents = (start2, root) => parentsUntil(start2, root, never);
      const parentsAndSelf = (start2, root) => [start2].concat(parents(start2, root));
      const navigateIgnoreEmptyTextNodes = (forward, root, from2) => navigateIgnore(forward, root, from2, isEmptyText);
      const isBlock$2 = (schema) => (el) => schema.isBlock(name(el));
      const getClosestBlock$1 = (root, pos, schema) => find$2(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2(schema));
      const isAtBeforeAfterBlockBoundary = (forward, root, pos, schema) => navigateIgnoreEmptyTextNodes(forward, root.dom, pos).forall((newPos) => getClosestBlock$1(root, pos, schema).fold(() => !isInSameBlock(newPos, pos, root.dom), (fromBlock) => !isInSameBlock(newPos, pos, root.dom) && contains(fromBlock, SugarElement.fromDom(newPos.container()))));
      const isAtBlockBoundary = (forward, root, pos, schema) => getClosestBlock$1(root, pos, schema).fold(() => navigateIgnoreEmptyTextNodes(forward, root.dom, pos).forall((newPos) => !isInSameBlock(newPos, pos, root.dom)), (parent2) => navigateIgnoreEmptyTextNodes(forward, parent2.dom, pos).isNone());
      const isAtStartOfBlock = curry(isAtBlockBoundary, false);
      const isAtEndOfBlock = curry(isAtBlockBoundary, true);
      const isBeforeBlock = curry(isAtBeforeAfterBlockBoundary, false);
      const isAfterBlock = curry(isAtBeforeAfterBlockBoundary, true);
      const isBr$2 = (pos) => getElementFromPosition(pos).exists(isBr$6);
      const findBr = (forward, root, pos, schema) => {
        const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), (el) => schema.isBlock(name(el)));
        const scope = head(parentBlocks).getOr(root);
        return fromPosition(forward, scope.dom, pos).filter(isBr$2);
      };
      const isBeforeBr$1 = (root, pos, schema) => getElementFromPosition(pos).exists(isBr$6) || findBr(true, root, pos, schema).isSome();
      const isAfterBr = (root, pos, schema) => getElementFromPrevPosition(pos).exists(isBr$6) || findBr(false, root, pos, schema).isSome();
      const findPreviousBr = curry(findBr, false);
      const findNextBr = curry(findBr, true);
      const isInMiddleOfText = (pos) => CaretPosition.isTextPosition(pos) && !pos.isAtStart() && !pos.isAtEnd();
      const getClosestBlock = (root, pos, schema) => {
        const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), (el) => schema.isBlock(name(el)));
        return head(parentBlocks).getOr(root);
      };
      const hasSpaceBefore = (root, pos, schema) => {
        if (isInMiddleOfText(pos)) {
          return isAfterSpace(pos);
        } else {
          return isAfterSpace(pos) || prevPosition(getClosestBlock(root, pos, schema).dom, pos).exists(isAfterSpace);
        }
      };
      const hasSpaceAfter = (root, pos, schema) => {
        if (isInMiddleOfText(pos)) {
          return isBeforeSpace(pos);
        } else {
          return isBeforeSpace(pos) || nextPosition(getClosestBlock(root, pos, schema).dom, pos).exists(isBeforeSpace);
        }
      };
      const isPreValue = (value2) => contains$2(["pre", "pre-wrap"], value2);
      const isInPre = (pos) => getElementFromPosition(pos).bind((elm) => closest$4(elm, isElement$8)).exists((elm) => isPreValue(get$7(elm, "white-space")));
      const isAtBeginningOfBody = (root, pos) => prevPosition(root.dom, pos).isNone();
      const isAtEndOfBody = (root, pos) => nextPosition(root.dom, pos).isNone();
      const isAtLineBoundary = (root, pos, schema) => isAtBeginningOfBody(root, pos) || isAtEndOfBody(root, pos) || isAtStartOfBlock(root, pos, schema) || isAtEndOfBlock(root, pos, schema) || isAfterBr(root, pos, schema) || isBeforeBr$1(root, pos, schema);
      const isCefBlock = (node) => isNonNullable(node) && isContentEditableFalse$a(node) && isBlockLike(node);
      const isSiblingCefBlock = (root, direction) => (container) => {
        return isCefBlock(new DomTreeWalker(container, root)[direction]());
      };
      const isBeforeCefBlock = (root, pos) => {
        const nextPos = nextPosition(root.dom, pos).getOr(pos);
        const isNextCefBlock = isSiblingCefBlock(root.dom, "next");
        return pos.isAtEnd() && (isNextCefBlock(pos.container()) || isNextCefBlock(nextPos.container()));
      };
      const isAfterCefBlock = (root, pos) => {
        const prevPos = prevPosition(root.dom, pos).getOr(pos);
        const isPrevCefBlock = isSiblingCefBlock(root.dom, "prev");
        return pos.isAtStart() && (isPrevCefBlock(pos.container()) || isPrevCefBlock(prevPos.container()));
      };
      const needsToHaveNbsp = (root, pos, schema) => {
        if (isInPre(pos)) {
          return false;
        } else {
          return isAtLineBoundary(root, pos, schema) || hasSpaceBefore(root, pos, schema) || hasSpaceAfter(root, pos, schema);
        }
      };
      const needsToBeNbspLeft = (root, pos, schema) => {
        if (isInPre(pos)) {
          return false;
        } else {
          return isAtStartOfBlock(root, pos, schema) || isBeforeBlock(root, pos, schema) || isAfterBr(root, pos, schema) || hasSpaceBefore(root, pos, schema) || isAfterCefBlock(root, pos);
        }
      };
      const leanRight = (pos) => {
        const container = pos.container();
        const offset = pos.offset();
        if (isText$b(container) && offset < container.data.length) {
          return CaretPosition(container, offset + 1);
        } else {
          return pos;
        }
      };
      const needsToBeNbspRight = (root, pos, schema) => {
        if (isInPre(pos)) {
          return false;
        } else {
          return isAtEndOfBlock(root, pos, schema) || isAfterBlock(root, pos, schema) || isBeforeBr$1(root, pos, schema) || hasSpaceAfter(root, pos, schema) || isBeforeCefBlock(root, pos);
        }
      };
      const needsToBeNbsp = (root, pos, schema) => needsToBeNbspLeft(root, pos, schema) || needsToBeNbspRight(root, leanRight(pos), schema);
      const isNbspAt = (text2, offset) => isNbsp(text2.charAt(offset));
      const isWhiteSpaceAt = (text2, offset) => isWhiteSpace(text2.charAt(offset));
      const hasNbsp = (pos) => {
        const container = pos.container();
        return isText$b(container) && contains$1(container.data, nbsp);
      };
      const normalizeNbspMiddle = (text2) => {
        const chars = text2.split("");
        return map$3(chars, (chr, i) => {
          if (isNbsp(chr) && i > 0 && i < chars.length - 1 && isContent(chars[i - 1]) && isContent(chars[i + 1])) {
            return " ";
          } else {
            return chr;
          }
        }).join("");
      };
      const normalizeNbspAtStart = (root, node, makeNbsp, schema) => {
        const text2 = node.data;
        const firstPos = CaretPosition(node, 0);
        if (!makeNbsp && isNbspAt(text2, 0) && !needsToBeNbsp(root, firstPos, schema)) {
          node.data = " " + text2.slice(1);
          return true;
        } else if (makeNbsp && isWhiteSpaceAt(text2, 0) && needsToBeNbspLeft(root, firstPos, schema)) {
          node.data = nbsp + text2.slice(1);
          return true;
        } else {
          return false;
        }
      };
      const normalizeNbspInMiddleOfTextNode = (node) => {
        const text2 = node.data;
        const newText = normalizeNbspMiddle(text2);
        if (newText !== text2) {
          node.data = newText;
          return true;
        } else {
          return false;
        }
      };
      const normalizeNbspAtEnd = (root, node, makeNbsp, schema) => {
        const text2 = node.data;
        const lastPos = CaretPosition(node, text2.length - 1);
        if (!makeNbsp && isNbspAt(text2, text2.length - 1) && !needsToBeNbsp(root, lastPos, schema)) {
          node.data = text2.slice(0, -1) + " ";
          return true;
        } else if (makeNbsp && isWhiteSpaceAt(text2, text2.length - 1) && needsToBeNbspRight(root, lastPos, schema)) {
          node.data = text2.slice(0, -1) + nbsp;
          return true;
        } else {
          return false;
        }
      };
      const normalizeNbsps$1 = (root, pos, schema) => {
        const container = pos.container();
        if (!isText$b(container)) {
          return Optional.none();
        }
        if (hasNbsp(pos)) {
          const normalized = normalizeNbspAtStart(root, container, false, schema) || normalizeNbspInMiddleOfTextNode(container) || normalizeNbspAtEnd(root, container, false, schema);
          return someIf(normalized, pos);
        } else if (needsToBeNbsp(root, pos, schema)) {
          const normalized = normalizeNbspAtStart(root, container, true, schema) || normalizeNbspAtEnd(root, container, true, schema);
          return someIf(normalized, pos);
        } else {
          return Optional.none();
        }
      };
      const normalizeNbspsInEditor = (editor) => {
        const root = SugarElement.fromDom(editor.getBody());
        if (editor.selection.isCollapsed()) {
          normalizeNbsps$1(root, CaretPosition.fromRangeStart(editor.selection.getRng()), editor.schema).each((pos) => {
            editor.selection.setRng(pos.toRange());
          });
        }
      };
      const normalize$1 = (node, offset, count2, schema) => {
        if (count2 === 0) {
          return;
        }
        const elm = SugarElement.fromDom(node);
        const root = ancestor$5(elm, (el) => schema.isBlock(name(el))).getOr(elm);
        const whitespace = node.data.slice(offset, offset + count2);
        const isEndOfContent = offset + count2 >= node.data.length && needsToBeNbspRight(root, CaretPosition(node, node.data.length), schema);
        const isStartOfContent = offset === 0 && needsToBeNbspLeft(root, CaretPosition(node, 0), schema);
        node.replaceData(offset, count2, normalize$4(whitespace, 4, isStartOfContent, isEndOfContent));
      };
      const normalizeWhitespaceAfter = (node, offset, schema) => {
        const content = node.data.slice(offset);
        const whitespaceCount = content.length - lTrim(content).length;
        normalize$1(node, offset, whitespaceCount, schema);
      };
      const normalizeWhitespaceBefore = (node, offset, schema) => {
        const content = node.data.slice(0, offset);
        const whitespaceCount = content.length - rTrim(content).length;
        normalize$1(node, offset - whitespaceCount, whitespaceCount, schema);
      };
      const mergeTextNodes = (prevNode, nextNode, schema, normalizeWhitespace, mergeToPrev = true) => {
        const whitespaceOffset = rTrim(prevNode.data).length;
        const newNode = mergeToPrev ? prevNode : nextNode;
        const removeNode2 = mergeToPrev ? nextNode : prevNode;
        if (mergeToPrev) {
          newNode.appendData(removeNode2.data);
        } else {
          newNode.insertData(0, removeNode2.data);
        }
        remove$8(SugarElement.fromDom(removeNode2));
        if (normalizeWhitespace) {
          normalizeWhitespaceAfter(newNode, whitespaceOffset, schema);
        }
        return newNode;
      };
      const needsReposition = (pos, elm) => {
        const container = pos.container();
        const offset = pos.offset();
        return !CaretPosition.isTextPosition(pos) && container === elm.parentNode && offset > CaretPosition.before(elm).offset();
      };
      const reposition = (elm, pos) => needsReposition(pos, elm) ? CaretPosition(pos.container(), pos.offset() - 1) : pos;
      const beforeOrStartOf = (node) => isText$b(node) ? CaretPosition(node, 0) : CaretPosition.before(node);
      const afterOrEndOf = (node) => isText$b(node) ? CaretPosition(node, node.data.length) : CaretPosition.after(node);
      const getPreviousSiblingCaretPosition = (elm) => {
        if (isCaretCandidate$3(elm.previousSibling)) {
          return Optional.some(afterOrEndOf(elm.previousSibling));
        } else {
          return elm.previousSibling ? lastPositionIn(elm.previousSibling) : Optional.none();
        }
      };
      const getNextSiblingCaretPosition = (elm) => {
        if (isCaretCandidate$3(elm.nextSibling)) {
          return Optional.some(beforeOrStartOf(elm.nextSibling));
        } else {
          return elm.nextSibling ? firstPositionIn(elm.nextSibling) : Optional.none();
        }
      };
      const findCaretPositionBackwardsFromElm = (rootElement, elm) => {
        return Optional.from(elm.previousSibling ? elm.previousSibling : elm.parentNode).bind((node) => prevPosition(rootElement, CaretPosition.before(node))).orThunk(() => nextPosition(rootElement, CaretPosition.after(elm)));
      };
      const findCaretPositionForwardsFromElm = (rootElement, elm) => nextPosition(rootElement, CaretPosition.after(elm)).orThunk(() => prevPosition(rootElement, CaretPosition.before(elm)));
      const findCaretPositionBackwards = (rootElement, elm) => getPreviousSiblingCaretPosition(elm).orThunk(() => getNextSiblingCaretPosition(elm)).orThunk(() => findCaretPositionBackwardsFromElm(rootElement, elm));
      const findCaretPositionForward = (rootElement, elm) => getNextSiblingCaretPosition(elm).orThunk(() => getPreviousSiblingCaretPosition(elm)).orThunk(() => findCaretPositionForwardsFromElm(rootElement, elm));
      const findCaretPosition = (forward, rootElement, elm) => forward ? findCaretPositionForward(rootElement, elm) : findCaretPositionBackwards(rootElement, elm);
      const findCaretPosOutsideElmAfterDelete = (forward, rootElement, elm) => findCaretPosition(forward, rootElement, elm).map(curry(reposition, elm));
      const setSelection$1 = (editor, forward, pos) => {
        pos.fold(() => {
          editor.focus();
        }, (pos2) => {
          editor.selection.setRng(pos2.toRange(), forward);
        });
      };
      const eqRawNode = (rawNode) => (elm) => elm.dom === rawNode;
      const isBlock$1 = (editor, elm) => elm && has$2(editor.schema.getBlockElements(), name(elm));
      const paddEmptyBlock = (schema, elm, preserveEmptyCaret) => {
        if (isEmpty$4(schema, elm)) {
          const br = SugarElement.fromHtml('<br data-mce-bogus="1">');
          if (preserveEmptyCaret) {
            each$e(children$1(elm), (node) => {
              if (!isEmptyCaretFormatElement(node)) {
                remove$8(node);
              }
            });
          } else {
            empty(elm);
          }
          append$1(elm, br);
          return Optional.some(CaretPosition.before(br.dom));
        } else {
          return Optional.none();
        }
      };
      const deleteNormalized = (elm, afterDeletePosOpt, schema, normalizeWhitespace) => {
        const prevTextOpt = prevSibling(elm).filter(isText$c);
        const nextTextOpt = nextSibling(elm).filter(isText$c);
        remove$8(elm);
        return lift3(prevTextOpt, nextTextOpt, afterDeletePosOpt, (prev2, next2, pos) => {
          const prevNode = prev2.dom, nextNode = next2.dom;
          const offset = prevNode.data.length;
          mergeTextNodes(prevNode, nextNode, schema, normalizeWhitespace);
          return pos.container() === nextNode ? CaretPosition(prevNode, offset) : pos;
        }).orThunk(() => {
          if (normalizeWhitespace) {
            prevTextOpt.each((elm2) => normalizeWhitespaceBefore(elm2.dom, elm2.dom.length, schema));
            nextTextOpt.each((elm2) => normalizeWhitespaceAfter(elm2.dom, 0, schema));
          }
          return afterDeletePosOpt;
        });
      };
      const isInlineElement = (editor, element) => has$2(editor.schema.getTextInlineElements(), name(element));
      const deleteElement$2 = (editor, forward, elm, moveCaret2 = true, preserveEmptyCaret = false) => {
        const afterDeletePos = findCaretPosOutsideElmAfterDelete(forward, editor.getBody(), elm.dom);
        const parentBlock = ancestor$5(elm, curry(isBlock$1, editor), eqRawNode(editor.getBody()));
        const normalizedAfterDeletePos = deleteNormalized(elm, afterDeletePos, editor.schema, isInlineElement(editor, elm));
        if (editor.dom.isEmpty(editor.getBody())) {
          editor.setContent("");
          editor.selection.setCursorLocation();
        } else {
          parentBlock.bind((elm2) => paddEmptyBlock(editor.schema, elm2, preserveEmptyCaret)).fold(() => {
            if (moveCaret2) {
              setSelection$1(editor, forward, normalizedAfterDeletePos);
            }
          }, (paddPos) => {
            if (moveCaret2) {
              setSelection$1(editor, forward, Optional.some(paddPos));
            }
          });
        }
      };
      const strongRtl = /[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/;
      const hasStrongRtl = (text2) => strongRtl.test(text2);
      const isInlineTarget = (editor, elm) => is$2(SugarElement.fromDom(elm), getInlineBoundarySelector(editor)) && !isTransparentBlock(editor.schema, elm) && editor.dom.isEditable(elm);
      const isRtl = (element) => DOMUtils.DOM.getStyle(element, "direction", true) === "rtl" || hasStrongRtl(element.textContent ?? "");
      const findInlineParents = (isInlineTarget2, rootNode, pos) => filter$5(DOMUtils.DOM.getParents(pos.container(), "*", rootNode), isInlineTarget2);
      const findRootInline = (isInlineTarget2, rootNode, pos) => {
        const parents2 = findInlineParents(isInlineTarget2, rootNode, pos);
        return Optional.from(parents2[parents2.length - 1]);
      };
      const hasSameParentBlock = (rootNode, node1, node2) => {
        const block1 = getParentBlock$3(node1, rootNode);
        const block2 = getParentBlock$3(node2, rootNode);
        return isNonNullable(block1) && block1 === block2;
      };
      const isAtZwsp = (pos) => isBeforeInline(pos) || isAfterInline(pos);
      const normalizePosition = (forward, pos) => {
        const container = pos.container(), offset = pos.offset();
        if (forward) {
          if (isCaretContainerInline(container)) {
            if (isText$b(container.nextSibling)) {
              return CaretPosition(container.nextSibling, 0);
            } else {
              return CaretPosition.after(container);
            }
          } else {
            return isBeforeInline(pos) ? CaretPosition(container, offset + 1) : pos;
          }
        } else {
          if (isCaretContainerInline(container)) {
            if (isText$b(container.previousSibling)) {
              return CaretPosition(container.previousSibling, container.previousSibling.data.length);
            } else {
              return CaretPosition.before(container);
            }
          } else {
            return isAfterInline(pos) ? CaretPosition(container, offset - 1) : pos;
          }
        }
      };
      const normalizeForwards = curry(normalizePosition, true);
      const normalizeBackwards = curry(normalizePosition, false);
      const execCommandIgnoreInputEvents = (editor, command) => {
        const inputBlocker = (e) => e.stopImmediatePropagation();
        editor.on("beforeinput input", inputBlocker, true);
        editor.getDoc().execCommand(command);
        editor.off("beforeinput input", inputBlocker);
      };
      const execEditorDeleteCommand = (editor) => {
        editor.execCommand("delete");
      };
      const execNativeDeleteCommand = (editor) => execCommandIgnoreInputEvents(editor, "Delete");
      const execNativeForwardDeleteCommand = (editor) => execCommandIgnoreInputEvents(editor, "ForwardDelete");
      const isBeforeRoot = (rootNode) => (elm) => is$4(parent(elm), rootNode, eq);
      const isTextBlockOrListItem = (element) => isTextBlock$3(element) || isListItem$2(element);
      const getParentBlock$2 = (rootNode, elm) => {
        if (contains(rootNode, elm)) {
          return closest$4(elm, isTextBlockOrListItem, isBeforeRoot(rootNode));
        } else {
          return Optional.none();
        }
      };
      const paddEmptyBody = (editor, moveSelection2 = true) => {
        if (editor.dom.isEmpty(editor.getBody())) {
          editor.setContent("", { no_selection: !moveSelection2 });
        }
      };
      const willDeleteLastPositionInElement = (forward, fromPos, elm) => lift2(firstPositionIn(elm), lastPositionIn(elm), (firstPos, lastPos) => {
        const normalizedFirstPos = normalizePosition(true, firstPos);
        const normalizedLastPos = normalizePosition(false, lastPos);
        const normalizedFromPos = normalizePosition(false, fromPos);
        if (forward) {
          return nextPosition(elm, normalizedFromPos).exists((nextPos) => nextPos.isEqual(normalizedLastPos) && fromPos.isEqual(normalizedFirstPos));
        } else {
          return prevPosition(elm, normalizedFromPos).exists((prevPos) => prevPos.isEqual(normalizedFirstPos) && fromPos.isEqual(normalizedLastPos));
        }
      }).getOr(true);
      const freefallRtl = (root) => {
        const child2 = isComment$1(root) ? prevSibling(root) : lastChild(root);
        return child2.bind(freefallRtl).orThunk(() => Optional.some(root));
      };
      const deleteRangeContents = (editor, rng, root, moveSelection2 = true) => {
        rng.deleteContents();
        const lastNode = freefallRtl(root).getOr(root);
        const lastBlock = SugarElement.fromDom(editor.dom.getParent(lastNode.dom, editor.dom.isBlock) ?? root.dom);
        if (lastBlock.dom === editor.getBody()) {
          paddEmptyBody(editor, moveSelection2);
        } else if (isEmpty$4(editor.schema, lastBlock, { checkRootAsContent: false })) {
          fillWithPaddingBr(lastBlock);
          if (moveSelection2) {
            editor.selection.setCursorLocation(lastBlock.dom, 0);
          }
        }
        if (!eq(root, lastBlock)) {
          const additionalCleanupNodes = is$4(parent(lastBlock), root) ? [] : siblings(lastBlock);
          each$e(additionalCleanupNodes.concat(children$1(root)), (node) => {
            if (!eq(node, lastBlock) && !contains(node, lastBlock) && isEmpty$4(editor.schema, node)) {
              remove$8(node);
            }
          });
        }
      };
      const isRootFromElement = (root) => (cur) => eq(root, cur);
      const getTableCells = (table2) => descendants(table2, "td,th");
      const getTable$1 = (node, isRoot2) => getClosestTable(SugarElement.fromDom(node), isRoot2);
      const selectionInTableWithNestedTable = (details) => {
        return lift2(details.startTable, details.endTable, (startTable, endTable) => {
          const isStartTableParentOfEndTable = descendant(startTable, (t) => eq(t, endTable));
          const isEndTableParentOfStartTable = descendant(endTable, (t) => eq(t, startTable));
          return !isStartTableParentOfEndTable && !isEndTableParentOfStartTable ? details : {
            ...details,
            startTable: isStartTableParentOfEndTable ? Optional.none() : details.startTable,
            endTable: isEndTableParentOfStartTable ? Optional.none() : details.endTable,
            isSameTable: false,
            isMultiTable: false
          };
        }).getOr(details);
      };
      const adjustQuirksInDetails = (details) => {
        return selectionInTableWithNestedTable(details);
      };
      const getTableDetailsFromRange = (rng, isRoot2) => {
        const startTable = getTable$1(rng.startContainer, isRoot2);
        const endTable = getTable$1(rng.endContainer, isRoot2);
        const isStartInTable = startTable.isSome();
        const isEndInTable = endTable.isSome();
        const isSameTable = lift2(startTable, endTable, eq).getOr(false);
        const isMultiTable = !isSameTable && isStartInTable && isEndInTable;
        return adjustQuirksInDetails({
          startTable,
          endTable,
          isStartInTable,
          isEndInTable,
          isSameTable,
          isMultiTable
        });
      };
      const tableCellRng = (start2, end2) => ({
        start: start2,
        end: end2
      });
      const tableSelection = (rng, table2, cells2) => ({
        rng,
        table: table2,
        cells: cells2
      });
      const deleteAction = Adt.generate([
        { singleCellTable: ["rng", "cell"] },
        { fullTable: ["table"] },
        { partialTable: ["cells", "outsideDetails"] },
        { multiTable: ["startTableCells", "endTableCells", "betweenRng"] }
      ]);
      const getClosestCell$1 = (container, isRoot2) => closest$3(SugarElement.fromDom(container), "td,th", isRoot2);
      const isExpandedCellRng = (cellRng) => !eq(cellRng.start, cellRng.end);
      const getTableFromCellRng = (cellRng, isRoot2) => getClosestTable(cellRng.start, isRoot2).bind((startParentTable) => getClosestTable(cellRng.end, isRoot2).bind((endParentTable) => someIf(eq(startParentTable, endParentTable), startParentTable)));
      const isSingleCellTable = (cellRng, isRoot2) => !isExpandedCellRng(cellRng) && getTableFromCellRng(cellRng, isRoot2).exists((table2) => {
        const rows = table2.dom.rows;
        return rows.length === 1 && rows[0].cells.length === 1;
      });
      const getCellRng = (rng, isRoot2) => {
        const startCell = getClosestCell$1(rng.startContainer, isRoot2);
        const endCell = getClosestCell$1(rng.endContainer, isRoot2);
        return lift2(startCell, endCell, tableCellRng);
      };
      const getCellRangeFromStartTable = (isRoot2) => (startCell) => getClosestTable(startCell, isRoot2).bind((table2) => last$2(getTableCells(table2)).map((endCell) => tableCellRng(startCell, endCell)));
      const getCellRangeFromEndTable = (isRoot2) => (endCell) => getClosestTable(endCell, isRoot2).bind((table2) => head(getTableCells(table2)).map((startCell) => tableCellRng(startCell, endCell)));
      const getTableSelectionFromCellRng = (isRoot2) => (cellRng) => getTableFromCellRng(cellRng, isRoot2).map((table2) => tableSelection(cellRng, table2, getTableCells(table2)));
      const getTableSelections = (cellRng, selectionDetails, rng, isRoot2) => {
        if (rng.collapsed || !cellRng.forall(isExpandedCellRng)) {
          return Optional.none();
        } else if (selectionDetails.isSameTable) {
          const sameTableSelection = cellRng.bind(getTableSelectionFromCellRng(isRoot2));
          return Optional.some({
            start: sameTableSelection,
            end: sameTableSelection
          });
        } else {
          const startCell = getClosestCell$1(rng.startContainer, isRoot2);
          const endCell = getClosestCell$1(rng.endContainer, isRoot2);
          const startTableSelection = startCell.bind(getCellRangeFromStartTable(isRoot2)).bind(getTableSelectionFromCellRng(isRoot2));
          const endTableSelection = endCell.bind(getCellRangeFromEndTable(isRoot2)).bind(getTableSelectionFromCellRng(isRoot2));
          return Optional.some({
            start: startTableSelection,
            end: endTableSelection
          });
        }
      };
      const getCellIndex = (cells2, cell2) => findIndex$2(cells2, (x) => eq(x, cell2));
      const getSelectedCells = (tableSelection2) => lift2(getCellIndex(tableSelection2.cells, tableSelection2.rng.start), getCellIndex(tableSelection2.cells, tableSelection2.rng.end), (startIndex, endIndex) => tableSelection2.cells.slice(startIndex, endIndex + 1));
      const isSingleCellTableContentSelected = (optCellRng, rng, isRoot2) => optCellRng.exists((cellRng) => isSingleCellTable(cellRng, isRoot2) && hasAllContentsSelected(cellRng.start, rng));
      const unselectCells = (rng, selectionDetails) => {
        const { startTable, endTable } = selectionDetails;
        const otherContentRng = rng.cloneRange();
        startTable.each((table2) => otherContentRng.setStartAfter(table2.dom));
        endTable.each((table2) => otherContentRng.setEndBefore(table2.dom));
        return otherContentRng;
      };
      const handleSingleTable = (cellRng, selectionDetails, rng, isRoot2) => getTableSelections(cellRng, selectionDetails, rng, isRoot2).bind(({ start: start2, end: end2 }) => start2.or(end2)).bind((tableSelection2) => {
        const { isSameTable } = selectionDetails;
        const selectedCells = getSelectedCells(tableSelection2).getOr([]);
        if (isSameTable && tableSelection2.cells.length === selectedCells.length) {
          return Optional.some(deleteAction.fullTable(tableSelection2.table));
        } else if (selectedCells.length > 0) {
          if (isSameTable) {
            return Optional.some(deleteAction.partialTable(selectedCells, Optional.none()));
          } else {
            const otherContentRng = unselectCells(rng, selectionDetails);
            return Optional.some(deleteAction.partialTable(selectedCells, Optional.some({
              ...selectionDetails,
              rng: otherContentRng
            })));
          }
        } else {
          return Optional.none();
        }
      });
      const handleMultiTable = (cellRng, selectionDetails, rng, isRoot2) => getTableSelections(cellRng, selectionDetails, rng, isRoot2).bind(({ start: start2, end: end2 }) => {
        const startTableSelectedCells = start2.bind(getSelectedCells).getOr([]);
        const endTableSelectedCells = end2.bind(getSelectedCells).getOr([]);
        if (startTableSelectedCells.length > 0 && endTableSelectedCells.length > 0) {
          const otherContentRng = unselectCells(rng, selectionDetails);
          return Optional.some(deleteAction.multiTable(startTableSelectedCells, endTableSelectedCells, otherContentRng));
        } else {
          return Optional.none();
        }
      });
      const getActionFromRange = (root, rng) => {
        const isRoot2 = isRootFromElement(root);
        const optCellRng = getCellRng(rng, isRoot2);
        const selectionDetails = getTableDetailsFromRange(rng, isRoot2);
        if (isSingleCellTableContentSelected(optCellRng, rng, isRoot2)) {
          return optCellRng.map((cellRng) => deleteAction.singleCellTable(rng, cellRng.start));
        } else if (selectionDetails.isMultiTable) {
          return handleMultiTable(optCellRng, selectionDetails, rng, isRoot2);
        } else {
          return handleSingleTable(optCellRng, selectionDetails, rng, isRoot2);
        }
      };
      const cleanCells = (cells2) => each$e(cells2, (cell2) => {
        remove$9(cell2, "contenteditable");
        fillWithPaddingBr(cell2);
      });
      const getOutsideBlock = (editor, container) => Optional.from(editor.dom.getParent(container, editor.dom.isBlock)).map(SugarElement.fromDom);
      const handleEmptyBlock = (editor, startInTable, emptyBlock2) => {
        emptyBlock2.each((block) => {
          if (startInTable) {
            remove$8(block);
          } else {
            fillWithPaddingBr(block);
            editor.selection.setCursorLocation(block.dom, 0);
          }
        });
      };
      const deleteContentInsideCell = (editor, cell2, rng, isFirstCellInSelection) => {
        const insideTableRng = rng.cloneRange();
        if (isFirstCellInSelection) {
          insideTableRng.setStart(rng.startContainer, rng.startOffset);
          insideTableRng.setEndAfter(cell2.dom.lastChild);
        } else {
          insideTableRng.setStartBefore(cell2.dom.firstChild);
          insideTableRng.setEnd(rng.endContainer, rng.endOffset);
        }
        deleteCellContents(editor, insideTableRng, cell2, false).each((action2) => action2());
      };
      const collapseAndRestoreCellSelection = (editor) => {
        const selectedCells = getCellsFromEditor(editor);
        const selectedNode = SugarElement.fromDom(editor.selection.getNode());
        if (isTableCell$3(selectedNode.dom) && isEmpty$4(editor.schema, selectedNode)) {
          editor.selection.setCursorLocation(selectedNode.dom, 0);
        } else {
          editor.selection.collapse(true);
        }
        if (selectedCells.length > 1 && exists(selectedCells, (cell2) => eq(cell2, selectedNode))) {
          set$4(selectedNode, "data-mce-selected", "1");
        }
      };
      const emptySingleTableCells = (editor, cells2, outsideDetails) => Optional.some(() => {
        const editorRng = editor.selection.getRng();
        const cellsToClean = outsideDetails.bind(({ rng, isStartInTable }) => {
          const outsideBlock = getOutsideBlock(editor, isStartInTable ? rng.endContainer : rng.startContainer);
          rng.deleteContents();
          handleEmptyBlock(editor, isStartInTable, outsideBlock.filter(curry(isEmpty$4, editor.schema)));
          const endPointCell = isStartInTable ? cells2[0] : cells2[cells2.length - 1];
          deleteContentInsideCell(editor, endPointCell, editorRng, isStartInTable);
          if (!isEmpty$4(editor.schema, endPointCell)) {
            return Optional.some(isStartInTable ? cells2.slice(1) : cells2.slice(0, -1));
          } else {
            return Optional.none();
          }
        }).getOr(cells2);
        cleanCells(cellsToClean);
        collapseAndRestoreCellSelection(editor);
      });
      const emptyMultiTableCells = (editor, startTableCells, endTableCells, betweenRng) => Optional.some(() => {
        const rng = editor.selection.getRng();
        const startCell = startTableCells[0];
        const endCell = endTableCells[endTableCells.length - 1];
        deleteContentInsideCell(editor, startCell, rng, true);
        deleteContentInsideCell(editor, endCell, rng, false);
        const startTableCellsToClean = isEmpty$4(editor.schema, startCell) ? startTableCells : startTableCells.slice(1);
        const endTableCellsToClean = isEmpty$4(editor.schema, endCell) ? endTableCells : endTableCells.slice(0, -1);
        cleanCells(startTableCellsToClean.concat(endTableCellsToClean));
        betweenRng.deleteContents();
        collapseAndRestoreCellSelection(editor);
      });
      const deleteCellContents = (editor, rng, cell2, moveSelection2 = true) => Optional.some(() => {
        deleteRangeContents(editor, rng, cell2, moveSelection2);
      });
      const deleteTableElement = (editor, table2) => Optional.some(() => deleteElement$2(editor, false, table2));
      const deleteCellRange = (editor, rootElm, rng) => getActionFromRange(rootElm, rng).bind((action2) => action2.fold(curry(deleteCellContents, editor), curry(deleteTableElement, editor), curry(emptySingleTableCells, editor), curry(emptyMultiTableCells, editor)));
      const deleteCaptionRange = (editor, caption) => emptyElement(editor, caption);
      const deleteTableRange = (editor, rootElm, rng, startElm) => getParentCaption(rootElm, startElm).fold(() => deleteCellRange(editor, rootElm, rng), (caption) => deleteCaptionRange(editor, caption));
      const deleteRange$4 = (editor, startElm, selectedCells) => {
        const rootNode = SugarElement.fromDom(editor.getBody());
        const rng = editor.selection.getRng();
        return selectedCells.length !== 0 ? emptySingleTableCells(editor, selectedCells, Optional.none()) : deleteTableRange(editor, rootNode, rng, startElm);
      };
      const getParentCell = (rootElm, elm) => find$2(parentsAndSelf(elm, rootElm), isTableCell$2);
      const getParentCaption = (rootElm, elm) => find$2(parentsAndSelf(elm, rootElm), isTag("caption"));
      const deleteBetweenCells = (editor, rootElm, forward, fromCell, from2) => (
        // TODO: TINY-8865 - This may not be safe to cast as Node below and alternative solutions need to be looked into
        navigate(forward, editor.getBody(), from2).bind((to2) => getParentCell(rootElm, SugarElement.fromDom(to2.getNode())).bind((toCell) => eq(toCell, fromCell) ? Optional.none() : Optional.some(noop)))
      );
      const emptyElement = (editor, elm) => Optional.some(() => {
        fillWithPaddingBr(elm);
        editor.selection.setCursorLocation(elm.dom, 0);
      });
      const isDeleteOfLastCharPos = (fromCaption, forward, from2, to2) => firstPositionIn(fromCaption.dom).bind((first2) => lastPositionIn(fromCaption.dom).map((last2) => forward ? from2.isEqual(first2) && to2.isEqual(last2) : from2.isEqual(last2) && to2.isEqual(first2))).getOr(true);
      const emptyCaretCaption = (editor, elm) => emptyElement(editor, elm);
      const validateCaretCaption = (rootElm, fromCaption, to2) => (
        // TODO: TINY-8865 - This may not be safe to cast as Node below and alternative solutions need to be looked into
        getParentCaption(rootElm, SugarElement.fromDom(to2.getNode())).fold(() => Optional.some(noop), (toCaption) => someIf(!eq(toCaption, fromCaption), noop))
      );
      const deleteCaretInsideCaption = (editor, rootElm, forward, fromCaption, from2) => navigate(forward, editor.getBody(), from2).fold(() => Optional.some(noop), (to2) => isDeleteOfLastCharPos(fromCaption, forward, from2, to2) ? emptyCaretCaption(editor, fromCaption) : validateCaretCaption(rootElm, fromCaption, to2));
      const deleteCaretCells = (editor, forward, rootElm, startElm) => {
        const from2 = CaretPosition.fromRangeStart(editor.selection.getRng());
        return getParentCell(rootElm, startElm).bind((fromCell) => isEmpty$4(editor.schema, fromCell, { checkRootAsContent: false }) ? emptyElement(editor, fromCell) : deleteBetweenCells(editor, rootElm, forward, fromCell, from2));
      };
      const deleteCaretCaption = (editor, forward, rootElm, fromCaption) => {
        const from2 = CaretPosition.fromRangeStart(editor.selection.getRng());
        return isEmpty$4(editor.schema, fromCaption) ? emptyElement(editor, fromCaption) : deleteCaretInsideCaption(editor, rootElm, forward, fromCaption, from2);
      };
      const isNearTable = (forward, pos) => forward ? isBeforeTable(pos) : isAfterTable(pos);
      const isBeforeOrAfterTable = (editor, forward) => {
        const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng());
        return isNearTable(forward, fromPos) || fromPosition(forward, editor.getBody(), fromPos).exists((pos) => isNearTable(forward, pos));
      };
      const deleteCaret$3 = (editor, forward, startElm) => {
        const rootElm = SugarElement.fromDom(editor.getBody());
        return getParentCaption(rootElm, startElm).fold(() => deleteCaretCells(editor, forward, rootElm, startElm).orThunk(() => someIf(isBeforeOrAfterTable(editor, forward), noop)), (fromCaption) => deleteCaretCaption(editor, forward, rootElm, fromCaption));
      };
      const backspaceDelete$d = (editor, forward) => {
        const startElm = SugarElement.fromDom(editor.selection.getStart(true));
        const cells2 = getCellsFromEditor(editor);
        return editor.selection.isCollapsed() && cells2.length === 0 ? deleteCaret$3(editor, forward, startElm) : deleteRange$4(editor, startElm, cells2);
      };
      const getContentEditableRoot$1 = (root, node) => {
        let tempNode = node;
        while (tempNode && tempNode !== root) {
          if (isContentEditableTrue$3(tempNode) || isContentEditableFalse$a(tempNode)) {
            return tempNode;
          }
          tempNode = tempNode.parentNode;
        }
        return null;
      };
      const internalAttributesPrefixes = [
        "data-ephox-",
        "data-mce-",
        "data-alloy-",
        "data-snooker-",
        "_"
      ];
      const each$9 = Tools.each;
      const ElementUtils = (editor) => {
        const dom2 = editor.dom;
        const internalAttributes = new Set(editor.serializer.getTempAttrs());
        const compare = (node1, node2) => {
          if (node1.nodeName !== node2.nodeName || node1.nodeType !== node2.nodeType) {
            return false;
          }
          const getAttribs = (node) => {
            const attribs = {};
            each$9(dom2.getAttribs(node), (attr) => {
              const name2 = attr.nodeName.toLowerCase();
              if (name2 !== "style" && !isAttributeInternal(name2)) {
                attribs[name2] = dom2.getAttrib(node, name2);
              }
            });
            return attribs;
          };
          const compareObjects = (obj1, obj2) => {
            for (const name2 in obj1) {
              if (has$2(obj1, name2)) {
                const value2 = obj2[name2];
                if (isUndefined(value2)) {
                  return false;
                }
                if (obj1[name2] !== value2) {
                  return false;
                }
                delete obj2[name2];
              }
            }
            for (const name2 in obj2) {
              if (has$2(obj2, name2)) {
                return false;
              }
            }
            return true;
          };
          if (isElement$7(node1) && isElement$7(node2)) {
            if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
              return false;
            }
            if (!compareObjects(dom2.parseStyle(dom2.getAttrib(node1, "style")), dom2.parseStyle(dom2.getAttrib(node2, "style")))) {
              return false;
            }
          }
          return !isBookmarkNode$1(node1) && !isBookmarkNode$1(node2);
        };
        const isAttributeInternal = (attributeName) => exists(internalAttributesPrefixes, (value2) => startsWith(attributeName, value2)) || internalAttributes.has(attributeName);
        return {
          compare,
          isAttributeInternal
        };
      };
      const getNormalizedPoint$1 = (container, offset) => {
        if (isText$b(container)) {
          return { container, offset };
        }
        const node = RangeUtils.getNode(container, offset);
        if (isText$b(node)) {
          return {
            container: node,
            offset: offset >= container.childNodes.length ? node.data.length : 0
          };
        } else if (node.previousSibling && isText$b(node.previousSibling)) {
          return {
            container: node.previousSibling,
            offset: node.previousSibling.data.length
          };
        } else if (node.nextSibling && isText$b(node.nextSibling)) {
          return {
            container: node.nextSibling,
            offset: 0
          };
        }
        return { container, offset };
      };
      const normalizeRange$1 = (rng) => {
        const outRng = rng.cloneRange();
        const rangeStart = getNormalizedPoint$1(rng.startContainer, rng.startOffset);
        outRng.setStart(rangeStart.container, rangeStart.offset);
        const rangeEnd = getNormalizedPoint$1(rng.endContainer, rng.endOffset);
        outRng.setEnd(rangeEnd.container, rangeEnd.offset);
        return outRng;
      };
      const DOM$b = DOMUtils.DOM;
      const defaultMarker = () => DOM$b.create("span", { "data-mce-type": "bookmark" });
      const setupEndPoint = (container, offset, createMarker2) => {
        if (isElement$7(container)) {
          const offsetNode = createMarker2();
          if (container.hasChildNodes()) {
            if (offset === container.childNodes.length) {
              container.appendChild(offsetNode);
            } else {
              container.insertBefore(offsetNode, container.childNodes[offset]);
            }
          } else {
            container.appendChild(offsetNode);
          }
          return { container: offsetNode, offset: 0 };
        } else {
          return { container, offset };
        }
      };
      const restoreEndPoint = (container, offset) => {
        const nodeIndex2 = (container2) => {
          let node = container2.parentNode?.firstChild;
          let idx = 0;
          while (node) {
            if (node === container2) {
              return idx;
            }
            if (!isElement$7(node) || node.getAttribute("data-mce-type") !== "bookmark") {
              idx++;
            }
            node = node.nextSibling;
          }
          return -1;
        };
        if (isElement$7(container) && isNonNullable(container.parentNode)) {
          const node = container;
          offset = nodeIndex2(container);
          container = container.parentNode;
          DOM$b.remove(node);
          if (!container.hasChildNodes() && DOM$b.isBlock(container)) {
            container.appendChild(DOM$b.create("br"));
          }
        }
        return { container, offset };
      };
      const createNormalizedRange = (startContainer, startOffset, endContainer, endOffset) => {
        const rng = DOM$b.createRng();
        rng.setStart(startContainer, startOffset);
        rng.setEnd(endContainer, endOffset);
        return normalizeRange$1(rng);
      };
      const createBookmark = (rng, createMarker2 = defaultMarker) => {
        const { container: startContainer, offset: startOffset } = setupEndPoint(rng.startContainer, rng.startOffset, createMarker2);
        if (rng.collapsed) {
          return { startContainer, startOffset };
        } else {
          const { container: endContainer, offset: endOffset } = setupEndPoint(rng.endContainer, rng.endOffset, createMarker2);
          return { startContainer, startOffset, endContainer, endOffset };
        }
      };
      const resolveBookmark = (bookmark) => {
        const { container: startContainer, offset: startOffset } = restoreEndPoint(bookmark.startContainer, bookmark.startOffset);
        if (!isUndefined(bookmark.endContainer) && !isUndefined(bookmark.endOffset)) {
          const { container: endContainer, offset: endOffset } = restoreEndPoint(bookmark.endContainer, bookmark.endOffset);
          return createNormalizedRange(startContainer, startOffset, endContainer, endOffset);
        } else {
          return createNormalizedRange(startContainer, startOffset, startContainer, startOffset);
        }
      };
      const applyStyles = (dom2, elm, format, vars) => {
        Tools.each(format.styles, (value2, name2) => {
          dom2.setStyle(elm, name2, replaceVars(value2, vars));
        });
        if (format.styles) {
          const styleVal = dom2.getAttrib(elm, "style");
          if (styleVal) {
            dom2.setAttrib(elm, "data-mce-style", styleVal);
          }
        }
      };
      const setElementFormat = (ed, elm, fmt, vars, node) => {
        const dom2 = ed.dom;
        if (isFunction(fmt.onformat)) {
          fmt.onformat(elm, fmt, vars, node);
        }
        applyStyles(dom2, elm, fmt, vars);
        Tools.each(fmt.attributes, (value2, name2) => {
          dom2.setAttrib(elm, name2, replaceVars(value2, vars));
        });
        Tools.each(fmt.classes, (value2) => {
          const newValue = replaceVars(value2, vars);
          if (!dom2.hasClass(elm, newValue)) {
            dom2.addClass(elm, newValue);
          }
        });
      };
      const isApplyFormat = (format) => !isArray$1(format.attributes) && !isArray$1(format.styles);
      const isEq$3 = isEq$5;
      const matchesUnInheritedFormatSelector = (ed, node, name2) => {
        const formatList = ed.formatter.get(name2);
        if (formatList) {
          for (let i = 0; i < formatList.length; i++) {
            const format = formatList[i];
            if (isSelectorFormat(format) && format.inherit === false && ed.dom.is(node, format.selector)) {
              return true;
            }
          }
        }
        return false;
      };
      const matchParents = (editor, node, name2, vars, similar) => {
        const root = editor.dom.getRoot();
        if (node === root) {
          return false;
        }
        const matchedNode = editor.dom.getParent(node, (elm) => {
          if (matchesUnInheritedFormatSelector(editor, elm, name2)) {
            return true;
          }
          return elm.parentNode === root || !!matchNode$1(editor, elm, name2, vars, true);
        });
        return !!matchNode$1(editor, matchedNode, name2, vars, similar);
      };
      const matchName = (dom2, node, format) => {
        if (isInlineFormat(format) && isEq$3(node, format.inline)) {
          return true;
        }
        if (isBlockFormat(format) && isEq$3(node, format.block)) {
          return true;
        }
        if (isSelectorFormat(format)) {
          return isElement$7(node) && dom2.is(node, format.selector);
        }
        return false;
      };
      const matchItems = (dom2, node, format, itemName, similar, vars) => {
        const items = format[itemName];
        const matchAttributes = itemName === "attributes";
        if (isFunction(format.onmatch)) {
          return format.onmatch(node, format, itemName);
        }
        if (items) {
          if (!isArrayLike(items)) {
            for (const key in items) {
              if (has$2(items, key)) {
                const value2 = matchAttributes ? dom2.getAttrib(node, key) : getStyle(dom2, node, key);
                const expectedValue = replaceVars(items[key], vars);
                const isEmptyValue = isNullable(value2) || isEmpty$5(value2);
                if (isEmptyValue && isNullable(expectedValue)) {
                  continue;
                }
                if (similar && isEmptyValue && !format.exact) {
                  return false;
                }
                if ((!similar || format.exact) && !isEq$3(value2, normalizeStyleValue(expectedValue, key))) {
                  return false;
                }
              }
            }
          } else {
            for (let i = 0; i < items.length; i++) {
              if (matchAttributes ? dom2.getAttrib(node, items[i]) : getStyle(dom2, node, items[i])) {
                return true;
              }
            }
          }
        }
        return true;
      };
      const matchNode$1 = (ed, node, name2, vars, similar) => {
        const formatList = ed.formatter.get(name2);
        const dom2 = ed.dom;
        if (formatList && isElement$7(node)) {
          for (let i = 0; i < formatList.length; i++) {
            const format = formatList[i];
            if (matchName(ed.dom, node, format) && matchItems(dom2, node, format, "attributes", similar, vars) && matchItems(dom2, node, format, "styles", similar, vars)) {
              const classes = format.classes;
              if (classes) {
                for (let x = 0; x < classes.length; x++) {
                  if (!ed.dom.hasClass(node, replaceVars(classes[x], vars))) {
                    return;
                  }
                }
              }
              return format;
            }
          }
        }
        return void 0;
      };
      const match$2 = (editor, name2, vars, node, similar) => {
        if (node) {
          return matchParents(editor, node, name2, vars, similar);
        }
        node = editor.selection.getNode();
        if (matchParents(editor, node, name2, vars, similar)) {
          return true;
        }
        const startNode = editor.selection.getStart();
        if (startNode !== node) {
          if (matchParents(editor, startNode, name2, vars, similar)) {
            return true;
          }
        }
        return false;
      };
      const matchAll = (editor, names, vars) => {
        const matchedFormatNames = [];
        const checkedMap = {};
        const startElement = editor.selection.getStart();
        editor.dom.getParent(startElement, (node) => {
          for (let i = 0; i < names.length; i++) {
            const name2 = names[i];
            if (!checkedMap[name2] && matchNode$1(editor, node, name2, vars)) {
              checkedMap[name2] = true;
              matchedFormatNames.push(name2);
            }
          }
        }, editor.dom.getRoot());
        return matchedFormatNames;
      };
      const closest = (editor, names) => {
        const isRoot2 = (elm) => eq(elm, SugarElement.fromDom(editor.getBody()));
        const match2 = (elm, name2) => matchNode$1(editor, elm.dom, name2) ? Optional.some(name2) : Optional.none();
        return Optional.from(editor.selection.getStart(true)).bind((rawElm) => closest$1(SugarElement.fromDom(rawElm), (elm) => findMap(names, (name2) => match2(elm, name2)), isRoot2)).getOrNull();
      };
      const canApply = (editor, name2) => {
        const formatList = editor.formatter.get(name2);
        const dom2 = editor.dom;
        if (formatList && editor.selection.isEditable()) {
          const startNode = editor.selection.getStart();
          const parents2 = getParents$2(dom2, startNode);
          for (let x = formatList.length - 1; x >= 0; x--) {
            const format = formatList[x];
            if (!isSelectorFormat(format)) {
              return true;
            }
            for (let i = parents2.length - 1; i >= 0; i--) {
              if (dom2.is(parents2[i], format.selector)) {
                return true;
              }
            }
          }
        }
        return false;
      };
      const matchAllOnNode = (editor, node, formatNames) => foldl(formatNames, (acc, name2) => {
        const matchSimilar = isVariableFormatName(editor, name2);
        if (editor.formatter.matchNode(node, name2, {}, matchSimilar)) {
          return acc.concat([name2]);
        } else {
          return acc;
        }
      }, []);
      const ZWSP = ZWSP$1;
      const importNode = (ownerDocument, node) => {
        return ownerDocument.importNode(node, true);
      };
      const findFirstTextNode = (node) => {
        if (node) {
          const walker = new DomTreeWalker(node, node);
          for (let tempNode = walker.current(); tempNode; tempNode = walker.next()) {
            if (isText$b(tempNode)) {
              return tempNode;
            }
          }
        }
        return null;
      };
      const createCaretContainer = (fill) => {
        const caretContainer = SugarElement.fromTag("span");
        setAll$1(caretContainer, {
          // style: 'color:red',
          "id": CARET_ID,
          "data-mce-bogus": "1",
          "data-mce-type": "format-caret"
        });
        if (fill) {
          append$1(caretContainer, SugarElement.fromText(ZWSP));
        }
        return caretContainer;
      };
      const trimZwspFromCaretContainer = (caretContainerNode) => {
        const textNode = findFirstTextNode(caretContainerNode);
        if (textNode && textNode.data.charAt(0) === ZWSP) {
          textNode.deleteData(0, 1);
        }
        return textNode;
      };
      const removeCaretContainerNode = (editor, node, moveCaret2) => {
        const dom2 = editor.dom, selection = editor.selection;
        if (isCaretContainerEmpty(node)) {
          deleteElement$2(editor, false, SugarElement.fromDom(node), moveCaret2, true);
        } else {
          const rng = selection.getRng();
          const block = dom2.getParent(node, dom2.isBlock);
          const startContainer = rng.startContainer;
          const startOffset = rng.startOffset;
          const endContainer = rng.endContainer;
          const endOffset = rng.endOffset;
          const textNode = trimZwspFromCaretContainer(node);
          dom2.remove(node, true);
          if (startContainer === textNode && startOffset > 0) {
            rng.setStart(textNode, startOffset - 1);
          }
          if (endContainer === textNode && endOffset > 0) {
            rng.setEnd(textNode, endOffset - 1);
          }
          if (block && dom2.isEmpty(block)) {
            fillWithPaddingBr(SugarElement.fromDom(block));
          }
          selection.setRng(rng);
        }
      };
      const removeCaretContainer = (editor, node, moveCaret2) => {
        const dom2 = editor.dom, selection = editor.selection;
        if (!node) {
          node = getParentCaretContainer(editor.getBody(), selection.getStart());
          if (!node) {
            while (node = dom2.get(CARET_ID)) {
              removeCaretContainerNode(editor, node, moveCaret2);
            }
          }
        } else {
          removeCaretContainerNode(editor, node, moveCaret2);
        }
      };
      const insertCaretContainerNode = (editor, caretContainer, formatNode) => {
        const dom2 = editor.dom;
        const block = dom2.getParent(formatNode, curry(isTextBlock$2, editor.schema));
        if (block && dom2.isEmpty(block)) {
          formatNode.parentNode?.replaceChild(caretContainer, formatNode);
        } else {
          removeTrailingBr(SugarElement.fromDom(formatNode));
          if (dom2.isEmpty(formatNode)) {
            formatNode.parentNode?.replaceChild(caretContainer, formatNode);
          } else {
            dom2.insertAfter(caretContainer, formatNode);
          }
        }
      };
      const appendNode = (parentNode, node) => {
        parentNode.appendChild(node);
        return node;
      };
      const insertFormatNodesIntoCaretContainer = (formatNodes, caretContainer) => {
        const innerMostFormatNode = foldr(formatNodes, (parentNode, formatNode) => {
          return appendNode(parentNode, formatNode.cloneNode(false));
        }, caretContainer);
        const doc = innerMostFormatNode.ownerDocument ?? document;
        return appendNode(innerMostFormatNode, doc.createTextNode(ZWSP));
      };
      const cleanFormatNode = (editor, caretContainer, formatNode, name2, vars, similar) => {
        const formatter = editor.formatter;
        const dom2 = editor.dom;
        const validFormats = filter$5(keys(formatter.get()), (formatName) => formatName !== name2 && !contains$1(formatName, "removeformat"));
        const matchedFormats = matchAllOnNode(editor, formatNode, validFormats);
        const uniqueFormats = filter$5(matchedFormats, (fmtName) => !areSimilarFormats(editor, fmtName, name2));
        if (uniqueFormats.length > 0) {
          const clonedFormatNode = formatNode.cloneNode(false);
          dom2.add(caretContainer, clonedFormatNode);
          formatter.remove(name2, vars, clonedFormatNode, similar);
          dom2.remove(clonedFormatNode);
          return Optional.some(clonedFormatNode);
        } else {
          return Optional.none();
        }
      };
      const normalizeNbsps = (node) => set$1(node, get$4(node).replace(new RegExp(`${nbsp}$`), " "));
      const normalizeNbspsBetween = (editor, caretContainer) => {
        const handler = () => {
          if (caretContainer !== null && !editor.dom.isEmpty(caretContainer)) {
            prevSibling(SugarElement.fromDom(caretContainer)).each((node) => {
              if (isText$c(node)) {
                normalizeNbsps(node);
              } else {
                descendant$2(node, (e) => isText$c(e)).each((textNode) => {
                  if (isText$c(textNode)) {
                    normalizeNbsps(textNode);
                  }
                });
              }
            });
          }
        };
        editor.once("input", (e) => {
          if (e.data && !isWhiteSpace(e.data)) {
            if (!e.isComposing) {
              handler();
            } else {
              editor.once("compositionend", () => {
                handler();
              });
            }
          }
        });
      };
      const applyCaretFormat = (editor, name2, vars) => {
        let caretContainer;
        const selection = editor.selection;
        const formatList = editor.formatter.get(name2);
        if (!formatList) {
          return;
        }
        const selectionRng = selection.getRng();
        let offset = selectionRng.startOffset;
        const container = selectionRng.startContainer;
        const text2 = container.nodeValue;
        caretContainer = getParentCaretContainer(editor.getBody(), selection.getStart());
        const wordcharRegex = /[^\s\u00a0\u00ad\u200b\ufeff]/;
        if (text2 && offset > 0 && offset < text2.length && wordcharRegex.test(text2.charAt(offset)) && wordcharRegex.test(text2.charAt(offset - 1))) {
          const bookmark = selection.getBookmark();
          selectionRng.collapse(true);
          let rng = expandRng(editor.dom, selectionRng, formatList);
          rng = split(rng);
          editor.formatter.apply(name2, vars, rng);
          selection.moveToBookmark(bookmark);
        } else {
          let textNode = caretContainer ? findFirstTextNode(caretContainer) : null;
          if (!caretContainer || textNode?.data !== ZWSP) {
            caretContainer = importNode(editor.getDoc(), createCaretContainer(true).dom);
            textNode = caretContainer.firstChild;
            selectionRng.insertNode(caretContainer);
            offset = 1;
            normalizeNbspsBetween(editor, caretContainer);
            editor.formatter.apply(name2, vars, caretContainer);
          } else {
            editor.formatter.apply(name2, vars, caretContainer);
          }
          selection.setCursorLocation(textNode, offset);
        }
      };
      const removeCaretFormat = (editor, name2, vars, similar) => {
        const dom2 = editor.dom;
        const selection = editor.selection;
        let hasContentAfter = false;
        const formatList = editor.formatter.get(name2);
        if (!formatList) {
          return;
        }
        const rng = selection.getRng();
        const container = rng.startContainer;
        const offset = rng.startOffset;
        let node = container;
        if (isText$b(container)) {
          if (offset !== container.data.length) {
            hasContentAfter = true;
          }
          node = node.parentNode;
        }
        const parents2 = [];
        let formatNode;
        while (node) {
          if (matchNode$1(editor, node, name2, vars, similar)) {
            formatNode = node;
            break;
          }
          if (node.nextSibling) {
            hasContentAfter = true;
          }
          parents2.push(node);
          node = node.parentNode;
        }
        if (!formatNode) {
          return;
        }
        if (hasContentAfter) {
          const bookmark = selection.getBookmark();
          rng.collapse(true);
          let expandedRng = expandRng(dom2, rng, formatList, { includeTrailingSpace: true });
          expandedRng = split(expandedRng);
          editor.formatter.remove(name2, vars, expandedRng, similar);
          selection.moveToBookmark(bookmark);
        } else {
          const caretContainer = getParentCaretContainer(editor.getBody(), formatNode);
          const parentsAfter = isNonNullable(caretContainer) ? dom2.getParents(formatNode.parentNode, always, caretContainer) : [];
          const newCaretContainer = createCaretContainer(false).dom;
          insertCaretContainerNode(editor, newCaretContainer, caretContainer ?? formatNode);
          const cleanedFormatNode = cleanFormatNode(editor, newCaretContainer, formatNode, name2, vars, similar);
          const caretTextNode = insertFormatNodesIntoCaretContainer([
            ...parents2,
            ...cleanedFormatNode.toArray(),
            ...parentsAfter
          ], newCaretContainer);
          if (caretContainer) {
            removeCaretContainerNode(editor, caretContainer, isNonNullable(caretContainer));
          }
          selection.setCursorLocation(caretTextNode, 1);
          normalizeNbspsBetween(editor, newCaretContainer);
          if (dom2.isEmpty(formatNode)) {
            dom2.remove(formatNode);
          }
        }
      };
      const disableCaretContainer = (editor, keyCode, moveCaret2) => {
        const selection = editor.selection, body = editor.getBody();
        removeCaretContainer(editor, null, moveCaret2);
        if ((keyCode === 8 || keyCode === 46) && selection.isCollapsed() && selection.getStart().innerHTML === ZWSP) {
          removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()), true);
        }
        if (keyCode === 37 || keyCode === 39) {
          removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()), true);
        }
      };
      const endsWithNbsp = (element) => isText$b(element) && endsWith(element.data, nbsp);
      const setup$B = (editor) => {
        editor.on("mouseup keydown", (e) => {
          disableCaretContainer(editor, e.keyCode, endsWithNbsp(editor.selection.getRng().endContainer));
        });
      };
      const createCaretFormat = (formatNodes) => {
        const caretContainer = createCaretContainer(false);
        const innerMost = insertFormatNodesIntoCaretContainer(formatNodes, caretContainer.dom);
        return { caretContainer, caretPosition: CaretPosition(innerMost, 0) };
      };
      const replaceWithCaretFormat = (targetNode, formatNodes) => {
        const { caretContainer, caretPosition } = createCaretFormat(formatNodes);
        before$4(SugarElement.fromDom(targetNode), caretContainer);
        remove$8(SugarElement.fromDom(targetNode));
        return caretPosition;
      };
      const createCaretFormatAtStart$1 = (rng, formatNodes) => {
        const { caretContainer, caretPosition } = createCaretFormat(formatNodes);
        rng.insertNode(caretContainer.dom);
        return caretPosition;
      };
      const isFormatElement = (editor, element) => {
        if (isCaretNode(element.dom)) {
          return false;
        }
        const inlineElements = editor.schema.getTextInlineElements();
        return has$2(inlineElements, name(element)) && !isCaretNode(element.dom) && !isBogus$1(element.dom);
      };
      const listItemStyles = ["fontWeight", "fontStyle", "color", "fontSize", "fontFamily"];
      const hasListStyles = (fmt) => isObject(fmt.styles) && exists(keys(fmt.styles), (name2) => contains$2(listItemStyles, name2));
      const findExpandedListItemFormat = (formats) => find$2(formats, (fmt) => isInlineFormat(fmt) && fmt.inline === "span" && hasListStyles(fmt));
      const getExpandedListItemFormat = (formatter, format) => {
        const formatList = formatter.get(format);
        return isArray$1(formatList) ? findExpandedListItemFormat(formatList) : Optional.none();
      };
      const isRngStartAtStartOfElement = (rng, elm) => prevPosition(elm, CaretPosition.fromRangeStart(rng)).isNone();
      const isRngEndAtEndOfElement = (rng, elm) => {
        return nextPosition(elm, CaretPosition.fromRangeEnd(rng)).exists((pos) => !isBr$7(pos.getNode()) || nextPosition(elm, pos).isSome()) === false;
      };
      const isEditableListItem = (dom2) => (elm) => isListItem$3(elm) && dom2.isEditable(elm);
      const getFullySelectedBlocks = (selection) => {
        const blocks = selection.getSelectedBlocks();
        const rng = selection.getRng();
        if (selection.isCollapsed()) {
          return [];
        }
        if (blocks.length === 1) {
          return isRngStartAtStartOfElement(rng, blocks[0]) && isRngEndAtEndOfElement(rng, blocks[0]) ? blocks : [];
        } else {
          const first2 = head(blocks).filter((elm) => isRngStartAtStartOfElement(rng, elm)).toArray();
          const last2 = last$2(blocks).filter((elm) => isRngEndAtEndOfElement(rng, elm)).toArray();
          const middle = blocks.slice(1, -1);
          return first2.concat(middle).concat(last2);
        }
      };
      const getFullySelectedListItems = (selection) => filter$5(getFullySelectedBlocks(selection), isEditableListItem(selection.dom));
      const getPartiallySelectedListItems = (selection) => filter$5(selection.getSelectedBlocks(), isEditableListItem(selection.dom));
      const each$8 = Tools.each;
      const isElementNode = (node) => isElement$7(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$1(node);
      const findElementSibling = (node, siblingName) => {
        for (let sibling2 = node; sibling2; sibling2 = sibling2[siblingName]) {
          if (isText$b(sibling2) && isNotEmpty(sibling2.data)) {
            return node;
          }
          if (isElement$7(sibling2) && !isBookmarkNode$1(sibling2)) {
            return sibling2;
          }
        }
        return node;
      };
      const mergeSiblingsNodes = (editor, prev2, next2) => {
        const elementUtils = ElementUtils(editor);
        const isPrevEditable = isHTMLElement(prev2) && editor.dom.isEditable(prev2);
        const isNextEditable = isHTMLElement(next2) && editor.dom.isEditable(next2);
        if (isPrevEditable && isNextEditable) {
          const prevSibling2 = findElementSibling(prev2, "previousSibling");
          const nextSibling2 = findElementSibling(next2, "nextSibling");
          if (elementUtils.compare(prevSibling2, nextSibling2)) {
            for (let sibling2 = prevSibling2.nextSibling; sibling2 && sibling2 !== nextSibling2; ) {
              const tmpSibling = sibling2;
              sibling2 = sibling2.nextSibling;
              prevSibling2.appendChild(tmpSibling);
            }
            editor.dom.remove(nextSibling2);
            Tools.each(Tools.grep(nextSibling2.childNodes), (node) => {
              prevSibling2.appendChild(node);
            });
            return prevSibling2;
          }
        }
        return next2;
      };
      const mergeSiblings = (editor, format, vars, node) => {
        if (node && format.merge_siblings !== false) {
          const newNode = mergeSiblingsNodes(editor, getNonWhiteSpaceSibling(node), node) ?? node;
          mergeSiblingsNodes(editor, newNode, getNonWhiteSpaceSibling(newNode, true));
        }
      };
      const clearChildStyles = (dom2, format, node) => {
        if (format.clear_child_styles) {
          const selector = format.links ? "*:not(a)" : "*";
          each$8(dom2.select(selector, node), (childNode) => {
            if (isElementNode(childNode) && dom2.isEditable(childNode)) {
              each$8(format.styles, (_value, name2) => {
                dom2.setStyle(childNode, name2, "");
              });
            }
          });
        }
      };
      const processChildElements = (node, filter2, process2) => {
        each$8(node.childNodes, (node2) => {
          if (isElementNode(node2)) {
            if (filter2(node2)) {
              process2(node2);
            }
            if (node2.hasChildNodes()) {
              processChildElements(node2, filter2, process2);
            }
          }
        });
      };
      const unwrapEmptySpan = (dom2, node) => {
        if (node.nodeName === "SPAN" && dom2.getAttribs(node).length === 0) {
          dom2.remove(node, true);
        }
      };
      const hasStyle = (dom2, name2) => (node) => !!(node && getStyle(dom2, node, name2));
      const applyStyle = (dom2, name2, value2) => (node) => {
        dom2.setStyle(node, name2, value2);
        if (node.getAttribute("style") === "") {
          node.removeAttribute("style");
        }
        unwrapEmptySpan(dom2, node);
      };
      const removeResult = Adt.generate([
        { keep: [] },
        { rename: ["name"] },
        { removed: [] }
      ]);
      const MCE_ATTR_RE = /^(src|href|style)$/;
      const each$7 = Tools.each;
      const isEq$2 = isEq$5;
      const isTableCellOrRow = (node) => /^(TR|TH|TD)$/.test(node.nodeName);
      const isChildOfInlineParent = (dom2, node, parent2) => dom2.isChildOf(node, parent2) && node !== parent2 && !dom2.isBlock(parent2);
      const getContainer = (ed, rng, start2) => {
        let container = rng[start2 ? "startContainer" : "endContainer"];
        let offset = rng[start2 ? "startOffset" : "endOffset"];
        if (isElement$7(container)) {
          const lastIdx = container.childNodes.length - 1;
          if (!start2 && offset) {
            offset--;
          }
          container = container.childNodes[offset > lastIdx ? lastIdx : offset];
        }
        if (isText$b(container) && start2 && offset >= container.data.length) {
          container = new DomTreeWalker(container, ed.getBody()).next() || container;
        }
        if (isText$b(container) && !start2 && offset === 0) {
          container = new DomTreeWalker(container, ed.getBody()).prev() || container;
        }
        return container;
      };
      const normalizeTableSelection = (node, start2) => {
        const prop = start2 ? "firstChild" : "lastChild";
        const childNode = node[prop];
        if (isTableCellOrRow(node) && childNode) {
          if (node.nodeName === "TR") {
            return childNode[prop] || childNode;
          } else {
            return childNode;
          }
        }
        return node;
      };
      const wrap$1 = (dom2, node, name2, attrs) => {
        const wrapper = dom2.create(name2, attrs);
        node.parentNode?.insertBefore(wrapper, node);
        wrapper.appendChild(node);
        return wrapper;
      };
      const wrapWithSiblings = (dom2, node, next2, name2, attrs) => {
        const start2 = SugarElement.fromDom(node);
        const wrapper = SugarElement.fromDom(dom2.create(name2, attrs));
        const siblings2 = next2 ? nextSiblings(start2) : prevSiblings(start2);
        append(wrapper, siblings2);
        if (next2) {
          before$4(start2, wrapper);
          prepend(wrapper, start2);
        } else {
          after$4(start2, wrapper);
          append$1(wrapper, start2);
        }
        return wrapper.dom;
      };
      const isColorFormatAndAnchor = (node, format) => format.links && node.nodeName === "A";
      const removeNode = (ed, node, format) => {
        const parentNode = node.parentNode;
        let rootBlockElm;
        const dom2 = ed.dom;
        const forcedRootBlock = getForcedRootBlock(ed);
        if (isBlockFormat(format)) {
          if (parentNode === dom2.getRoot()) {
            if (!format.list_block || !isEq$2(node, format.list_block)) {
              each$e(from(node.childNodes), (node2) => {
                if (isValid(ed, forcedRootBlock, node2.nodeName.toLowerCase())) {
                  if (!rootBlockElm) {
                    rootBlockElm = wrap$1(dom2, node2, forcedRootBlock);
                    dom2.setAttribs(rootBlockElm, getForcedRootBlockAttrs(ed));
                  } else {
                    rootBlockElm.appendChild(node2);
                  }
                } else {
                  rootBlockElm = null;
                }
              });
            }
          }
        }
        if (isMixedFormat(format) && !isEq$2(format.inline, node)) {
          return;
        }
        dom2.remove(node, true);
      };
      const processFormatAttrOrStyle = (name2, value2, vars) => {
        if (isNumber(name2)) {
          return {
            name: value2,
            value: null
          };
        } else {
          return {
            name: name2,
            value: replaceVars(value2, vars)
          };
        }
      };
      const removeEmptyStyleAttributeIfNeeded = (dom2, elm) => {
        if (dom2.getAttrib(elm, "style") === "") {
          elm.removeAttribute("style");
          elm.removeAttribute("data-mce-style");
        }
      };
      const removeStyles$1 = (dom2, elm, format, vars, compareNode) => {
        let stylesModified = false;
        each$7(format.styles, (value2, name2) => {
          const { name: styleName, value: styleValue } = processFormatAttrOrStyle(name2, value2, vars);
          const normalizedStyleValue = normalizeStyleValue(styleValue, styleName);
          if (format.remove_similar || isNull(styleValue) || !isElement$7(compareNode) || isEq$2(getStyle(dom2, compareNode, styleName), normalizedStyleValue)) {
            dom2.setStyle(elm, styleName, "");
          }
          stylesModified = true;
        });
        if (stylesModified) {
          removeEmptyStyleAttributeIfNeeded(dom2, elm);
        }
      };
      const removeListStyleFormats = (editor, name2, vars) => {
        if (name2 === "removeformat") {
          each$e(getPartiallySelectedListItems(editor.selection), (li) => {
            each$e(listItemStyles, (name3) => editor.dom.setStyle(li, name3, ""));
            removeEmptyStyleAttributeIfNeeded(editor.dom, li);
          });
        } else {
          getExpandedListItemFormat(editor.formatter, name2).each((liFmt) => {
            each$e(getPartiallySelectedListItems(editor.selection), (li) => removeStyles$1(editor.dom, li, liFmt, vars, null));
          });
        }
      };
      const removeNodeFormatInternal = (ed, format, vars, node, compareNode) => {
        const dom2 = ed.dom;
        const elementUtils = ElementUtils(ed);
        const schema = ed.schema;
        if (isInlineFormat(format) && isTransparentElementName(schema, format.inline) && isTransparentBlock(schema, node) && node.parentElement === ed.getBody()) {
          removeNode(ed, node, format);
          return removeResult.removed();
        }
        if (!format.ceFalseOverride && node && dom2.getContentEditableParent(node) === "false") {
          return removeResult.keep();
        }
        if (node && !matchName(dom2, node, format) && !isColorFormatAndAnchor(node, format)) {
          return removeResult.keep();
        }
        const elm = node;
        const preserveAttributes = format.preserve_attributes;
        if (isInlineFormat(format) && format.remove === "all" && isArray$1(preserveAttributes)) {
          const attrsToPreserve = filter$5(dom2.getAttribs(elm), (attr) => contains$2(preserveAttributes, attr.name.toLowerCase()));
          dom2.removeAllAttribs(elm);
          each$e(attrsToPreserve, (attr) => dom2.setAttrib(elm, attr.name, attr.value));
          if (attrsToPreserve.length > 0) {
            return removeResult.rename("span");
          }
        }
        if (format.remove !== "all") {
          removeStyles$1(dom2, elm, format, vars, compareNode);
          each$7(format.attributes, (value2, name2) => {
            const { name: attrName, value: attrValue } = processFormatAttrOrStyle(name2, value2, vars);
            if (format.remove_similar || isNull(attrValue) || !isElement$7(compareNode) || isEq$2(dom2.getAttrib(compareNode, attrName), attrValue)) {
              if (attrName === "class") {
                const currentValue = dom2.getAttrib(elm, attrName);
                if (currentValue) {
                  let valueOut = "";
                  each$e(currentValue.split(/\s+/), (cls) => {
                    if (/mce\-\w+/.test(cls)) {
                      valueOut += (valueOut ? " " : "") + cls;
                    }
                  });
                  if (valueOut) {
                    dom2.setAttrib(elm, attrName, valueOut);
                    return;
                  }
                }
              }
              if (MCE_ATTR_RE.test(attrName)) {
                elm.removeAttribute("data-mce-" + attrName);
              }
              if (attrName === "style" && matchNodeNames$1(["li"])(elm) && dom2.getStyle(elm, "list-style-type") === "none") {
                elm.removeAttribute(attrName);
                dom2.setStyle(elm, "list-style-type", "none");
                return;
              }
              if (attrName === "class") {
                elm.removeAttribute("className");
              }
              elm.removeAttribute(attrName);
            }
          });
          each$7(format.classes, (value2) => {
            value2 = replaceVars(value2, vars);
            if (!isElement$7(compareNode) || dom2.hasClass(compareNode, value2)) {
              dom2.removeClass(elm, value2);
            }
          });
          const attrs = dom2.getAttribs(elm);
          for (let i = 0; i < attrs.length; i++) {
            const attrName = attrs[i].nodeName;
            if (!elementUtils.isAttributeInternal(attrName)) {
              return removeResult.keep();
            }
          }
        }
        if (format.remove !== "none") {
          removeNode(ed, elm, format);
          return removeResult.removed();
        }
        return removeResult.keep();
      };
      const findFormatRoot = (editor, container, name2, vars, similar) => {
        let formatRoot;
        if (container.parentNode) {
          each$e(getParents$2(editor.dom, container.parentNode).reverse(), (parent2) => {
            if (!formatRoot && isElement$7(parent2) && parent2.id !== "_start" && parent2.id !== "_end") {
              const format = matchNode$1(editor, parent2, name2, vars, similar);
              if (format && format.split !== false) {
                formatRoot = parent2;
              }
            }
          });
        }
        return formatRoot;
      };
      const removeNodeFormatFromClone = (editor, format, vars, clone2) => removeNodeFormatInternal(editor, format, vars, clone2, clone2).fold(constant(clone2), (newName) => {
        const fragment = editor.dom.createFragment();
        fragment.appendChild(clone2);
        return editor.dom.rename(clone2, newName);
      }, constant(null));
      const wrapAndSplit = (editor, formatList, formatRoot, container, target, split2, format, vars) => {
        let lastClone;
        let firstClone;
        const dom2 = editor.dom;
        if (formatRoot) {
          const formatRootParent = formatRoot.parentNode;
          for (let parent2 = container.parentNode; parent2 && parent2 !== formatRootParent; parent2 = parent2.parentNode) {
            let clone2 = dom2.clone(parent2, false);
            for (let i = 0; i < formatList.length; i++) {
              clone2 = removeNodeFormatFromClone(editor, formatList[i], vars, clone2);
              if (clone2 === null) {
                break;
              }
            }
            if (clone2) {
              if (lastClone) {
                clone2.appendChild(lastClone);
              }
              if (!firstClone) {
                firstClone = clone2;
              }
              lastClone = clone2;
            }
          }
          if (split2 && (!format.mixed || !dom2.isBlock(formatRoot))) {
            container = dom2.split(formatRoot, container) ?? container;
          }
          if (lastClone && firstClone) {
            target.parentNode?.insertBefore(lastClone, target);
            firstClone.appendChild(target);
            if (isInlineFormat(format)) {
              mergeSiblings(editor, format, vars, lastClone);
            }
          }
        }
        return container;
      };
      const removeFormatInternal = (ed, name2, vars, node, similar) => {
        const formatList = ed.formatter.get(name2);
        const format = formatList[0];
        const dom2 = ed.dom;
        const selection = ed.selection;
        const splitToFormatRoot = (container) => {
          const formatRoot = findFormatRoot(ed, container, name2, vars, similar);
          return wrapAndSplit(ed, formatList, formatRoot, container, container, true, format, vars);
        };
        const isRemoveBookmarkNode = (node2) => isBookmarkNode$1(node2) && isElement$7(node2) && (node2.id === "_start" || node2.id === "_end");
        const removeFormatOnNode = (node2) => exists(formatList, (fmt) => removeNodeFormat(ed, fmt, vars, node2, node2));
        const process2 = (node2) => {
          const children2 = from(node2.childNodes);
          const removed = removeFormatOnNode(node2);
          const currentNodeMatches = removed || exists(formatList, (f) => matchName(dom2, node2, f));
          const parentNode = node2.parentNode;
          if (!currentNodeMatches && isNonNullable(parentNode) && shouldExpandToSelector(format)) {
            removeFormatOnNode(parentNode);
          }
          if (format.deep) {
            if (children2.length) {
              for (let i = 0; i < children2.length; i++) {
                process2(children2[i]);
              }
            }
          }
          const textDecorations = ["underline", "line-through", "overline"];
          each$e(textDecorations, (decoration) => {
            if (isElement$7(node2) && ed.dom.getStyle(node2, "text-decoration") === decoration && node2.parentNode && getTextDecoration(dom2, node2.parentNode) === decoration) {
              removeNodeFormat(ed, {
                deep: false,
                exact: true,
                inline: "span",
                styles: {
                  textDecoration: decoration
                }
              }, void 0, node2);
            }
          });
        };
        const unwrap2 = (start2) => {
          const node2 = dom2.get(start2 ? "_start" : "_end");
          if (node2) {
            let out = node2[start2 ? "firstChild" : "lastChild"];
            if (isRemoveBookmarkNode(out)) {
              out = out[start2 ? "firstChild" : "lastChild"];
            }
            if (isText$b(out) && out.data.length === 0) {
              out = start2 ? node2.previousSibling || node2.nextSibling : node2.nextSibling || node2.previousSibling;
            }
            dom2.remove(node2, true);
            return out;
          } else {
            return null;
          }
        };
        const removeRngStyle = (rng) => {
          let startContainer;
          let endContainer;
          let expandedRng = expandRng(dom2, rng, formatList, { includeTrailingSpace: rng.collapsed });
          if (format.split) {
            expandedRng = split(expandedRng);
            startContainer = getContainer(ed, expandedRng, true);
            endContainer = getContainer(ed, expandedRng);
            if (startContainer !== endContainer) {
              startContainer = normalizeTableSelection(startContainer, true);
              endContainer = normalizeTableSelection(endContainer, false);
              if (isChildOfInlineParent(dom2, startContainer, endContainer)) {
                const marker = Optional.from(startContainer.firstChild).getOr(startContainer);
                splitToFormatRoot(wrapWithSiblings(dom2, marker, true, "span", { "id": "_start", "data-mce-type": "bookmark" }));
                unwrap2(true);
                return;
              }
              if (isChildOfInlineParent(dom2, endContainer, startContainer)) {
                const marker = Optional.from(endContainer.lastChild).getOr(endContainer);
                splitToFormatRoot(wrapWithSiblings(dom2, marker, false, "span", { "id": "_end", "data-mce-type": "bookmark" }));
                unwrap2(false);
                return;
              }
              startContainer = wrap$1(dom2, startContainer, "span", { "id": "_start", "data-mce-type": "bookmark" });
              endContainer = wrap$1(dom2, endContainer, "span", { "id": "_end", "data-mce-type": "bookmark" });
              const newRng = dom2.createRng();
              newRng.setStartAfter(startContainer);
              newRng.setEndBefore(endContainer);
              walk$3(dom2, newRng, (nodes) => {
                each$e(nodes, (n) => {
                  if (!isBookmarkNode$1(n) && !isBookmarkNode$1(n.parentNode)) {
                    splitToFormatRoot(n);
                  }
                });
              });
              splitToFormatRoot(startContainer);
              splitToFormatRoot(endContainer);
              startContainer = unwrap2(true);
              endContainer = unwrap2();
            } else {
              startContainer = endContainer = splitToFormatRoot(startContainer);
            }
            expandedRng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer;
            expandedRng.startOffset = dom2.nodeIndex(startContainer);
            expandedRng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer;
            expandedRng.endOffset = dom2.nodeIndex(endContainer) + 1;
          }
          walk$3(dom2, expandedRng, (nodes) => {
            each$e(nodes, process2);
          });
        };
        if (node) {
          if (isNode(node)) {
            const rng = dom2.createRng();
            rng.setStartBefore(node);
            rng.setEndAfter(node);
            removeRngStyle(rng);
          } else {
            removeRngStyle(node);
          }
          fireFormatRemove(ed, name2, node, vars);
          return;
        }
        if (!selection.isCollapsed() || !isInlineFormat(format) || getCellsFromEditor(ed).length) {
          preserveSelection(
            ed,
            () => runOnRanges(ed, removeRngStyle),
            // Before trying to move the start of the selection, check if start element still has formatting then we are at: "<b>text|</b>text"
            // and need to move the start into the next text node
            (startNode) => isInlineFormat(format) && match$2(ed, name2, vars, startNode)
          );
          ed.nodeChanged();
        } else {
          removeCaretFormat(ed, name2, vars, similar);
        }
        removeListStyleFormats(ed, name2, vars);
        fireFormatRemove(ed, name2, node, vars);
      };
      const removeFormat$1 = (ed, name2, vars, node, similar) => {
        if (node || ed.selection.isEditable()) {
          removeFormatInternal(ed, name2, vars, node, similar);
        }
      };
      const removeFormatOnElement = (editor, format, vars, node) => {
        return removeNodeFormatInternal(editor, format, vars, node).fold(() => Optional.some(node), (newName) => Optional.some(editor.dom.rename(node, newName)), Optional.none);
      };
      const removeNodeFormat = (editor, format, vars, node, compareNode) => {
        return removeNodeFormatInternal(editor, format, vars, node, compareNode).fold(never, (newName) => {
          editor.dom.rename(node, newName);
          return true;
        }, always);
      };
      const fontSizeAlteringFormats = ["fontsize", "subscript", "superscript"];
      const formatsToActOn = ["strikethrough", ...fontSizeAlteringFormats];
      const hasFormat = (formatter, el, format) => isNonNullable(formatter.matchNode(el.dom, format, {}, format === "fontsize"));
      const isFontSizeAlteringElement = (formatter, el) => exists(fontSizeAlteringFormats, (format) => hasFormat(formatter, el, format));
      const isNormalizingFormat = (format) => contains$2(formatsToActOn, format);
      const gatherWrapperData = (isRoot2, scope, hasFormat2, createFormatElement, removeFormatFromElement) => {
        const parents2 = parents$1(scope, isRoot2).filter(isElement$8);
        return findLastIndex(parents2, hasFormat2).map((index) => {
          const container = parents2[index];
          const innerWrapper = createFormatElement(container);
          const outerWrappers = [
            ...removeFormatFromElement(shallow(container)).toArray(),
            ...bind$3(parents2.slice(0, index), (wrapper) => {
              if (hasFormat2(wrapper)) {
                return removeFormatFromElement(wrapper).toArray();
              } else {
                return [shallow(wrapper)];
              }
            })
          ];
          return { container, innerWrapper, outerWrappers };
        });
      };
      const wrapChildrenInInnerWrapper = (target, wrapper, hasFormat2, removeFormatFromElement) => {
        each$e(children$1(target), (child2) => {
          if (isElement$8(child2) && hasFormat2(child2)) {
            if (removeFormatFromElement(child2).isNone()) {
              unwrap(child2);
            }
          }
        });
        each$e(children$1(target), (child2) => append$1(wrapper, child2));
        prepend(target, wrapper);
      };
      const wrapInOuterWrappers = (target, wrappers) => {
        if (wrappers.length > 0) {
          const outermost = wrappers[wrappers.length - 1];
          before$4(target, outermost);
          const innerMost = foldl(wrappers.slice(0, wrappers.length - 1), (acc, wrapper) => {
            append$1(acc, wrapper);
            return wrapper;
          }, outermost);
          append$1(innerMost, target);
        }
      };
      const normalizeFontSizeElementsInternal = (domUtils, fontSizeElements, hasFormat2, createFormatElement, removeFormatFromElement) => {
        const isRoot2 = (el) => eq(SugarElement.fromDom(domUtils.getRoot()), el) || domUtils.isBlock(el.dom);
        each$e(fontSizeElements, (fontSizeElement) => {
          gatherWrapperData(isRoot2, fontSizeElement, hasFormat2, createFormatElement, removeFormatFromElement).each(({ container, innerWrapper, outerWrappers }) => {
            domUtils.split(container.dom, fontSizeElement.dom);
            wrapChildrenInInnerWrapper(fontSizeElement, innerWrapper, hasFormat2, removeFormatFromElement);
            wrapInOuterWrappers(fontSizeElement, outerWrappers);
          });
        });
      };
      const normalizeFontSizeElementsWithFormat = (editor, formatName, fontSizeElements) => {
        const hasFormat2 = (el) => isNonNullable(matchNode$1(editor, el.dom, formatName));
        const createFormatElement = (el) => {
          const newEl = SugarElement.fromTag(name(el));
          const format = matchNode$1(editor, el.dom, formatName, {});
          if (isNonNullable(format) && isApplyFormat(format)) {
            setElementFormat(editor, newEl.dom, format);
          }
          return newEl;
        };
        const removeFormatFromElement = (el) => {
          const format = matchNode$1(editor, el.dom, formatName, {});
          if (isNonNullable(format)) {
            return removeFormatOnElement(editor, format, {}, el.dom).map(SugarElement.fromDom);
          } else {
            return Optional.some(el);
          }
        };
        const bookmark = createBookmark(editor.selection.getRng());
        normalizeFontSizeElementsInternal(editor.dom, fontSizeElements, hasFormat2, createFormatElement, removeFormatFromElement);
        editor.selection.setRng(resolveBookmark(bookmark));
      };
      const collectFontSizeElements = (formatter, wrappers) => bind$3(wrappers, (wrapper) => {
        const fontSizeDescendants = descendants$1(wrapper, (el) => isFontSizeAlteringElement(formatter, el));
        return isFontSizeAlteringElement(formatter, wrapper) ? [wrapper, ...fontSizeDescendants] : fontSizeDescendants;
      });
      const normalizeFontSizeElementsAfterApply = (editor, appliedFormat, wrappers) => {
        if (isNormalizingFormat(appliedFormat)) {
          const fontSizeElements = collectFontSizeElements(editor.formatter, wrappers);
          normalizeFontSizeElementsWithFormat(editor, "strikethrough", fontSizeElements);
        }
      };
      const normalizeElements = (editor, elements) => {
        const fontSizeElements = filter$5(elements, (el) => isFontSizeAlteringElement(editor.formatter, el));
        normalizeFontSizeElementsWithFormat(editor, "strikethrough", fontSizeElements);
      };
      const isHeading = (node) => ["h1", "h2", "h3", "h4", "h5", "h6"].includes(node.name);
      const isSummary = (node) => node.name === "summary";
      const traverse = (root, fn) => {
        let node = root;
        while (node = node.walk()) {
          fn(node);
        }
      };
      const matchNode = (nodeFilters, attributeFilters, node, matches) => {
        const name2 = node.name;
        for (let ni = 0, nl = nodeFilters.length; ni < nl; ni++) {
          const filter2 = nodeFilters[ni];
          if (filter2.name === name2) {
            const match2 = matches.nodes[name2];
            if (match2) {
              match2.nodes.push(node);
            } else {
              matches.nodes[name2] = { filter: filter2, nodes: [node] };
            }
          }
        }
        if (node.attributes) {
          for (let ai = 0, al = attributeFilters.length; ai < al; ai++) {
            const filter2 = attributeFilters[ai];
            const attrName = filter2.name;
            if (attrName in node.attributes.map) {
              const match2 = matches.attributes[attrName];
              if (match2) {
                match2.nodes.push(node);
              } else {
                matches.attributes[attrName] = { filter: filter2, nodes: [node] };
              }
            }
          }
        }
      };
      const findMatchingNodes = (nodeFilters, attributeFilters, node) => {
        const matches = { nodes: {}, attributes: {} };
        if (node.firstChild) {
          traverse(node, (childNode) => {
            matchNode(nodeFilters, attributeFilters, childNode, matches);
          });
        }
        return matches;
      };
      const runFilters = (matches, args) => {
        const run = (matchRecord, filteringAttributes) => {
          each$d(matchRecord, (match2) => {
            const nodes = from(match2.nodes);
            each$e(match2.filter.callbacks, (callback) => {
              for (let i = nodes.length - 1; i >= 0; i--) {
                const node = nodes[i];
                const valueMatches = filteringAttributes ? node.attr(match2.filter.name) !== void 0 : node.name === match2.filter.name;
                if (!valueMatches || isNullable(node.parent)) {
                  nodes.splice(i, 1);
                }
              }
              if (nodes.length > 0) {
                callback(nodes, match2.filter.name, args);
              }
            });
          });
        };
        run(matches.nodes, false);
        run(matches.attributes, true);
      };
      const filter$1 = (nodeFilters, attributeFilters, node, args = {}) => {
        const matches = findMatchingNodes(nodeFilters, attributeFilters, node);
        runFilters(matches, args);
      };
      const paddEmptyNode = (settings, args, isBlock2, node) => {
        const brPreferred = settings.pad_empty_with_br || args.insert;
        if (brPreferred && isBlock2(node)) {
          const astNode = new AstNode("br", 1);
          if (args.insert) {
            astNode.attr("data-mce-bogus", "1");
          }
          node.empty().append(astNode);
        } else {
          node.empty().append(new AstNode("#text", 3)).value = nbsp;
        }
      };
      const isPaddedWithNbsp = (node) => hasOnlyChild(node, "#text") && node?.firstChild?.value === nbsp;
      const hasOnlyChild = (node, name2) => {
        const firstChild2 = node?.firstChild;
        return isNonNullable(firstChild2) && firstChild2 === node.lastChild && firstChild2.name === name2;
      };
      const isPadded = (schema, node) => {
        const rule = schema.getElementRule(node.name);
        return rule?.paddEmpty === true;
      };
      const isEmpty$2 = (schema, nonEmptyElements, whitespaceElements, node) => node.isEmpty(nonEmptyElements, whitespaceElements, (node2) => isPadded(schema, node2));
      const isLineBreakNode = (node, isBlock2) => isNonNullable(node) && (isBlock2(node) || node.name === "br");
      const findClosestEditingHost = (scope) => {
        let editableNode;
        for (let node = scope; node; node = node.parent) {
          const contentEditable = node.attr("contenteditable");
          if (contentEditable === "false") {
            break;
          } else if (contentEditable === "true") {
            editableNode = node;
          }
        }
        return Optional.from(editableNode);
      };
      const getAllDescendants = (scope) => {
        const collection = [];
        for (let node = scope.firstChild; isNonNullable(node); node = node.walk()) {
          collection.push(node);
        }
        return collection;
      };
      const removeOrUnwrapInvalidNode = (node, schema, originalNodeParent = node.parent) => {
        if (schema.getSpecialElements()[node.name]) {
          node.empty().remove();
        } else {
          const children2 = node.children();
          for (const childNode of children2) {
            if (originalNodeParent && !schema.isValidChild(originalNodeParent.name, childNode.name)) {
              removeOrUnwrapInvalidNode(childNode, schema, originalNodeParent);
            }
          }
          node.unwrap();
        }
      };
      const cleanInvalidNodes = (nodes, schema, rootNode, onCreate = noop) => {
        const textBlockElements = schema.getTextBlockElements();
        const nonEmptyElements = schema.getNonEmptyElements();
        const whitespaceElements = schema.getWhitespaceElements();
        const nonSplittableElements = Tools.makeMap("tr,td,th,tbody,thead,tfoot,table,summary");
        const fixed = /* @__PURE__ */ new Set();
        const isSplittableElement = (node) => node !== rootNode && !nonSplittableElements[node.name];
        for (let ni = 0; ni < nodes.length; ni++) {
          const node = nodes[ni];
          let parent2;
          let newParent;
          let tempNode;
          if (!node.parent || fixed.has(node)) {
            continue;
          }
          if (textBlockElements[node.name] && node.parent.name === "li") {
            let sibling2 = node.next;
            while (sibling2) {
              if (textBlockElements[sibling2.name]) {
                sibling2.name = "li";
                fixed.add(sibling2);
                node.parent.insert(sibling2, node.parent);
              } else {
                break;
              }
              sibling2 = sibling2.next;
            }
            node.unwrap();
            continue;
          }
          const parents2 = [node];
          for (parent2 = node.parent; parent2 && !schema.isValidChild(parent2.name, node.name) && isSplittableElement(parent2); parent2 = parent2.parent) {
            parents2.push(parent2);
          }
          if (parent2 && parents2.length > 1) {
            if (!isInvalid(schema, node, parent2)) {
              parents2.reverse();
              newParent = parents2[0].clone();
              onCreate(newParent);
              let currentNode = newParent;
              for (let i = 0; i < parents2.length - 1; i++) {
                if (schema.isValidChild(currentNode.name, parents2[i].name) && i > 0) {
                  tempNode = parents2[i].clone();
                  onCreate(tempNode);
                  currentNode.append(tempNode);
                } else {
                  tempNode = currentNode;
                }
                for (let childNode = parents2[i].firstChild; childNode && childNode !== parents2[i + 1]; ) {
                  const nextNode = childNode.next;
                  tempNode.append(childNode);
                  childNode = nextNode;
                }
                currentNode = tempNode;
              }
              if (!isEmpty$2(schema, nonEmptyElements, whitespaceElements, newParent)) {
                parent2.insert(newParent, parents2[0], true);
                parent2.insert(node, newParent);
              } else {
                parent2.insert(node, parents2[0], true);
              }
              parent2 = parents2[0];
              if (isEmpty$2(schema, nonEmptyElements, whitespaceElements, parent2) || hasOnlyChild(parent2, "br")) {
                parent2.empty().remove();
              }
            } else {
              removeOrUnwrapInvalidNode(node, schema);
            }
          } else if (node.parent) {
            if (node.name === "li") {
              let sibling2 = node.prev;
              if (sibling2 && (sibling2.name === "ul" || sibling2.name === "ol")) {
                sibling2.append(node);
                continue;
              }
              sibling2 = node.next;
              if (sibling2 && (sibling2.name === "ul" || sibling2.name === "ol") && sibling2.firstChild) {
                sibling2.insert(node, sibling2.firstChild, true);
                continue;
              }
              const wrapper = new AstNode("ul", 1);
              onCreate(wrapper);
              node.wrap(wrapper);
              continue;
            }
            if (schema.isValidChild(node.parent.name, "div") && schema.isValidChild("div", node.name)) {
              const wrapper = new AstNode("div", 1);
              onCreate(wrapper);
              node.wrap(wrapper);
            } else {
              removeOrUnwrapInvalidNode(node, schema);
            }
          }
        }
      };
      const hasClosest = (node, parentName) => {
        let tempNode = node;
        while (tempNode) {
          if (tempNode.name === parentName) {
            return true;
          }
          tempNode = tempNode.parent;
        }
        return false;
      };
      const isInvalid = (schema, node, parent2 = node.parent) => {
        if (!parent2) {
          return false;
        }
        if (schema.children[node.name] && !schema.isValidChild(parent2.name, node.name)) {
          return true;
        }
        if (node.name === "a" && hasClosest(parent2, "a")) {
          return true;
        }
        if (isSummary(parent2) && isHeading(node)) {
          return !(parent2?.firstChild === node && parent2?.lastChild === node);
        }
        return false;
      };
      const createRange = (sc, so, ec, eo) => {
        const rng = document.createRange();
        rng.setStart(sc, so);
        rng.setEnd(ec, eo);
        return rng;
      };
      const normalizeBlockSelectionRange = (rng) => {
        const startPos = CaretPosition.fromRangeStart(rng);
        const endPos = CaretPosition.fromRangeEnd(rng);
        const rootNode = rng.commonAncestorContainer;
        return fromPosition(false, rootNode, endPos).map((newEndPos) => {
          if (!isInSameBlock(startPos, endPos, rootNode) && isInSameBlock(startPos, newEndPos, rootNode)) {
            return createRange(startPos.container(), startPos.offset(), newEndPos.container(), newEndPos.offset());
          } else {
            return rng;
          }
        }).getOr(rng);
      };
      const normalize = (rng) => rng.collapsed ? rng : normalizeBlockSelectionRange(rng);
      const explode$1 = Tools.explode;
      const create$8 = () => {
        const filters = {};
        const addFilter = (name2, callback) => {
          each$e(explode$1(name2), (name3) => {
            if (!has$2(filters, name3)) {
              filters[name3] = { name: name3, callbacks: [] };
            }
            filters[name3].callbacks.push(callback);
          });
        };
        const getFilters = () => values(filters);
        const removeFilter = (name2, callback) => {
          each$e(explode$1(name2), (name3) => {
            if (has$2(filters, name3)) {
              if (isNonNullable(callback)) {
                const filter2 = filters[name3];
                const newCallbacks = filter$5(filter2.callbacks, (c) => c !== callback);
                if (newCallbacks.length > 0) {
                  filter2.callbacks = newCallbacks;
                } else {
                  delete filters[name3];
                }
              } else {
                delete filters[name3];
              }
            }
          });
        };
        return {
          addFilter,
          getFilters,
          removeFilter
        };
      };
      const encodeData = (data2) => data2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
      const decodeData$1 = (data2) => data2.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
      const removeAttrs = (node, names) => {
        each$e(names, (name2) => {
          node.attr(name2, null);
        });
      };
      const addFontToSpansFilter = (domParser, styles, fontSizes) => {
        domParser.addNodeFilter("font", (nodes) => {
          each$e(nodes, (node) => {
            const props = styles.parse(node.attr("style"));
            const color = node.attr("color");
            const face = node.attr("face");
            const size = node.attr("size");
            if (color) {
              props.color = color;
            }
            if (face) {
              props["font-family"] = face;
            }
            if (size) {
              toInt(size).each((num) => {
                props["font-size"] = fontSizes[num - 1];
              });
            }
            node.name = "span";
            node.attr("style", styles.serialize(props));
            removeAttrs(node, ["color", "face", "size"]);
          });
        });
      };
      const addStrikeFilter = (domParser, schema, styles) => {
        domParser.addNodeFilter("strike", (nodes) => {
          const convertToSTag = schema.type !== "html4";
          each$e(nodes, (node) => {
            if (convertToSTag) {
              node.name = "s";
            } else {
              const props = styles.parse(node.attr("style"));
              props["text-decoration"] = "line-through";
              node.name = "span";
              node.attr("style", styles.serialize(props));
            }
          });
        });
      };
      const addFilters = (domParser, settings, schema) => {
        const styles = Styles();
        if (settings.convert_fonts_to_spans) {
          addFontToSpansFilter(domParser, styles, Tools.explode(settings.font_size_legacy_values ?? ""));
        }
        addStrikeFilter(domParser, schema, styles);
      };
      const register$5 = (domParser, settings, schema) => {
        if (settings.inline_styles) {
          addFilters(domParser, settings, schema);
        }
      };
      const blobUriToBlob = (url) => fetch(url).then((res) => res.ok ? res.blob() : Promise.reject()).catch(() => Promise.reject({
        message: `Cannot convert ${url} to Blob. Resource might not exist or is inaccessible.`,
        uriType: "blob"
      }));
      const extractBase64Data = (data2) => {
        const matches = /([a-z0-9+\/=\s]+)/i.exec(data2);
        return matches ? matches[1] : "";
      };
      const decodeData = (data2) => {
        try {
          return decodeURIComponent(data2);
        } catch {
          return data2;
        }
      };
      const parseDataUri = (uri) => {
        const [type2, ...rest] = uri.split(",");
        const data2 = rest.join(",");
        const matches = /data:([^/]+\/[^;]+)(;.+)?/.exec(type2);
        if (matches) {
          const base64Encoded = matches[2] === ";base64";
          const decodedData = decodeData(data2);
          const extractedData = base64Encoded ? extractBase64Data(decodedData) : decodedData;
          return Optional.some({
            type: matches[1],
            data: extractedData,
            base64Encoded
          });
        } else {
          return Optional.none();
        }
      };
      const buildBlob = (type2, data2, base64Encoded = true) => {
        let str = data2;
        if (base64Encoded) {
          try {
            str = atob(data2);
          } catch {
            return Optional.none();
          }
        }
        const arr = new Uint8Array(str.length);
        for (let i = 0; i < arr.length; i++) {
          arr[i] = str.charCodeAt(i);
        }
        return Optional.some(new Blob([arr], { type: type2 }));
      };
      const dataUriToBlob = (uri) => {
        return new Promise((resolve2, reject) => {
          parseDataUri(uri).bind(({ type: type2, data: data2, base64Encoded }) => buildBlob(type2, data2, base64Encoded)).fold(() => reject("Invalid data URI"), resolve2);
        });
      };
      const uriToBlob = (url) => {
        if (startsWith(url, "blob:")) {
          return blobUriToBlob(url);
        } else if (startsWith(url, "data:")) {
          return dataUriToBlob(url);
        } else {
          return Promise.reject("Unknown URI format");
        }
      };
      const blobToDataUri = (blob) => {
        return new Promise((resolve2, reject) => {
          const reader = new FileReader();
          reader.onloadend = () => {
            resolve2(reader.result);
          };
          reader.onerror = () => {
            reject(reader.error?.message);
          };
          reader.readAsDataURL(blob);
        });
      };
      let count$1 = 0;
      const uniqueId$1 = (prefix) => {
        return (prefix || "blobid") + count$1++;
      };
      const processDataUri = (dataUri, base64Only, generateBlobInfo) => {
        return parseDataUri(dataUri).bind(({ data: data2, type: type2, base64Encoded }) => {
          if (base64Only && !base64Encoded) {
            return Optional.none();
          } else {
            const base64 = base64Encoded ? data2 : btoa(data2);
            return generateBlobInfo(base64, type2);
          }
        });
      };
      const createBlobInfo$1 = (blobCache, blob, base64) => {
        const blobInfo = blobCache.create(uniqueId$1(), blob, base64);
        blobCache.add(blobInfo);
        return blobInfo;
      };
      const dataUriToBlobInfo = (blobCache, dataUri, base64Only = false) => {
        return processDataUri(dataUri, base64Only, (base64, type2) => Optional.from(blobCache.getByData(base64, type2)).orThunk(() => buildBlob(type2, base64).map((blob) => createBlobInfo$1(blobCache, blob, base64))));
      };
      const imageToBlobInfo = (blobCache, imageSrc) => {
        const invalidDataUri = () => Promise.reject("Invalid data URI");
        if (startsWith(imageSrc, "blob:")) {
          const blobInfo = blobCache.getByUri(imageSrc);
          if (isNonNullable(blobInfo)) {
            return Promise.resolve(blobInfo);
          } else {
            return uriToBlob(imageSrc).then((blob) => {
              return blobToDataUri(blob).then((dataUri) => {
                return processDataUri(dataUri, false, (base64) => {
                  return Optional.some(createBlobInfo$1(blobCache, blob, base64));
                }).getOrThunk(invalidDataUri);
              });
            });
          }
        } else if (startsWith(imageSrc, "data:")) {
          return dataUriToBlobInfo(blobCache, imageSrc).fold(invalidDataUri, (blobInfo) => Promise.resolve(blobInfo));
        } else {
          return Promise.reject("Unknown image data format");
        }
      };
      const hostCaptureRegex = /^(?:(?:(?:[A-Za-z][A-Za-z\d.+-]{0,14}:\/\/(?:[-.~*+=!&;:'%@?^${}(),\w]+@)?|www\.|[-;:&=+$,.\w]+@)([A-Za-z\d-]+(?:\.[A-Za-z\d-]+)*))(?::\d+)?(?:\/(?:[-.~*+=!;:'%@$(),\/\w]*[-~*+=%@$()\/\w])?)?(?:\?(?:[-.~*+=!&;:'%@?^${}(),\/\w]+)?)?(?:#(?:[-.~*+=!&;:'%@?^${}(),\/\w]+)?)?)$/;
      const extractHost = (url) => Optional.from(url.match(hostCaptureRegex)).bind((ms) => get$b(ms, 1)).map((h) => startsWith(h, "www.") ? h.substring(4) : h);
      const sandboxIframe = (iframeNode, exclusions) => {
        if (Optional.from(iframeNode.attr("src")).bind(extractHost).forall((host) => !contains$2(exclusions, host))) {
          iframeNode.attr("sandbox", "");
        }
      };
      const isMimeType = (mime, type2) => startsWith(mime, `${type2}/`);
      const getEmbedType = (type2) => {
        if (isUndefined(type2)) {
          return "iframe";
        } else if (isMimeType(type2, "image")) {
          return "img";
        } else if (isMimeType(type2, "video")) {
          return "video";
        } else if (isMimeType(type2, "audio")) {
          return "audio";
        } else {
          return "iframe";
        }
      };
      const createSafeEmbed = ({ type: type2, src, width, height } = {}, sandboxIframes, sandboxIframesExclusions) => {
        const name2 = getEmbedType(type2);
        const embed = new AstNode(name2, 1);
        embed.attr(name2 === "audio" ? { src } : { src, width, height });
        if (name2 === "audio" || name2 === "video") {
          embed.attr("controls", "");
        }
        if (name2 === "iframe" && sandboxIframes) {
          sandboxIframe(embed, sandboxIframesExclusions);
        }
        return embed;
      };
      const isBogusImage = (img) => isNonNullable(img.attr("data-mce-bogus"));
      const isInternalImageSource = (img) => img.attr("src") === Env.transparentSrc || isNonNullable(img.attr("data-mce-placeholder"));
      const registerBase64ImageFilter = (parser, settings) => {
        const { blob_cache: blobCache } = settings;
        if (blobCache) {
          const processImage = (img) => {
            const inputSrc = img.attr("src");
            if (isInternalImageSource(img) || isBogusImage(img) || isNullable(inputSrc)) {
              return;
            }
            dataUriToBlobInfo(blobCache, inputSrc, true).each((blobInfo) => {
              img.attr("src", blobInfo.blobUri());
            });
          };
          parser.addAttributeFilter("src", (nodes) => each$e(nodes, processImage));
        }
      };
      const register$4 = (parser, settings) => {
        const schema = parser.schema;
        parser.addAttributeFilter("href", (nodes) => {
          let i = nodes.length;
          const appendRel = (rel) => {
            const parts = rel.split(" ").filter((p) => p.length > 0);
            return parts.concat(["noopener"]).sort().join(" ");
          };
          const addNoOpener = (rel) => {
            const newRel = rel ? Tools.trim(rel) : "";
            if (!/\b(noopener)\b/g.test(newRel)) {
              return appendRel(newRel);
            } else {
              return newRel;
            }
          };
          if (!settings.allow_unsafe_link_target) {
            while (i--) {
              const node = nodes[i];
              if (node.name === "a" && node.attr("target") === "_blank") {
                node.attr("rel", addNoOpener(node.attr("rel")));
              }
            }
          }
        });
        if (!settings.allow_html_in_named_anchor) {
          parser.addAttributeFilter("id,name", (nodes) => {
            let i = nodes.length, sibling2, prevSibling2, parent2, node;
            while (i--) {
              node = nodes[i];
              if (node.name === "a" && node.firstChild && !node.attr("href")) {
                parent2 = node.parent;
                sibling2 = node.lastChild;
                while (sibling2 && parent2) {
                  prevSibling2 = sibling2.prev;
                  parent2.insert(sibling2, node);
                  sibling2 = prevSibling2;
                }
              }
            }
          });
        }
        if (settings.fix_list_elements) {
          parser.addNodeFilter("ul,ol", (nodes) => {
            let i = nodes.length, node, parentNode;
            while (i--) {
              node = nodes[i];
              parentNode = node.parent;
              if (parentNode && (parentNode.name === "ul" || parentNode.name === "ol")) {
                if (node.prev && node.prev.name === "li") {
                  node.prev.append(node);
                } else {
                  const li = new AstNode("li", 1);
                  li.attr("style", "list-style-type: none");
                  node.wrap(li);
                }
              }
            }
          });
        }
        const validClasses = schema.getValidClasses();
        if (settings.validate && validClasses) {
          parser.addAttributeFilter("class", (nodes) => {
            let i = nodes.length;
            while (i--) {
              const node = nodes[i];
              const clazz = node.attr("class") ?? "";
              const classList = Tools.explode(clazz, " ");
              let classValue = "";
              for (let ci = 0; ci < classList.length; ci++) {
                const className = classList[ci];
                let valid = false;
                let validClassesMap = validClasses["*"];
                if (validClassesMap && validClassesMap[className]) {
                  valid = true;
                }
                validClassesMap = validClasses[node.name];
                if (!valid && validClassesMap && validClassesMap[className]) {
                  valid = true;
                }
                if (valid) {
                  if (classValue) {
                    classValue += " ";
                  }
                  classValue += className;
                }
              }
              if (!classValue.length) {
                classValue = null;
              }
              node.attr("class", classValue);
            }
          });
        }
        registerBase64ImageFilter(parser, settings);
        const shouldSandboxIframes2 = settings.sandbox_iframes ?? false;
        const sandboxIframesExclusions = unique$1(settings.sandbox_iframes_exclusions ?? []);
        if (settings.convert_unsafe_embeds) {
          parser.addNodeFilter("object,embed", (nodes) => each$e(nodes, (node) => {
            node.replace(createSafeEmbed({
              type: node.attr("type"),
              src: node.name === "object" ? node.attr("data") : node.attr("src"),
              width: node.attr("width"),
              height: node.attr("height")
            }, shouldSandboxIframes2, sandboxIframesExclusions));
          }));
        }
        if (shouldSandboxIframes2) {
          parser.addNodeFilter("iframe", (nodes) => each$e(nodes, (node) => sandboxIframe(node, sandboxIframesExclusions)));
        }
      };
      const {
        entries,
        setPrototypeOf,
        isFrozen,
        getPrototypeOf,
        getOwnPropertyDescriptor
      } = Object;
      let {
        freeze,
        seal,
        create: create$7
      } = Object;
      let {
        apply,
        construct
      } = typeof Reflect !== "undefined" && Reflect;
      if (!freeze) {
        freeze = function freeze2(x) {
          return x;
        };
      }
      if (!seal) {
        seal = function seal2(x) {
          return x;
        };
      }
      if (!apply) {
        apply = function apply2(fun, thisValue, args) {
          return fun.apply(thisValue, args);
        };
      }
      if (!construct) {
        construct = function construct2(Func, args) {
          return new Func(...args);
        };
      }
      const arrayForEach = unapply(Array.prototype.forEach);
      const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
      const arrayPop = unapply(Array.prototype.pop);
      const arrayPush = unapply(Array.prototype.push);
      const arraySplice = unapply(Array.prototype.splice);
      const stringToLowerCase = unapply(String.prototype.toLowerCase);
      const stringToString = unapply(String.prototype.toString);
      const stringMatch = unapply(String.prototype.match);
      const stringReplace = unapply(String.prototype.replace);
      const stringIndexOf = unapply(String.prototype.indexOf);
      const stringTrim = unapply(String.prototype.trim);
      const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
      const regExpTest = unapply(RegExp.prototype.test);
      const typeErrorCreate = unconstruct(TypeError);
      function unapply(func) {
        return function(thisArg) {
          if (thisArg instanceof RegExp) {
            thisArg.lastIndex = 0;
          }
          for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
          }
          return apply(func, thisArg, args);
        };
      }
      function unconstruct(func) {
        return function() {
          for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
            args[_key2] = arguments[_key2];
          }
          return construct(func, args);
        };
      }
      function addToSet(set2, array) {
        let transformCaseFunc = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : stringToLowerCase;
        if (setPrototypeOf) {
          setPrototypeOf(set2, null);
        }
        let l = array.length;
        while (l--) {
          let element = array[l];
          if (typeof element === "string") {
            const lcElement = transformCaseFunc(element);
            if (lcElement !== element) {
              if (!isFrozen(array)) {
                array[l] = lcElement;
              }
              element = lcElement;
            }
          }
          set2[element] = true;
        }
        return set2;
      }
      function cleanArray(array) {
        for (let index = 0; index < array.length; index++) {
          const isPropertyExist = objectHasOwnProperty(array, index);
          if (!isPropertyExist) {
            array[index] = null;
          }
        }
        return array;
      }
      function clone(object) {
        const newObject = create$7(null);
        for (const [property, value2] of entries(object)) {
          const isPropertyExist = objectHasOwnProperty(object, property);
          if (isPropertyExist) {
            if (Array.isArray(value2)) {
              newObject[property] = cleanArray(value2);
            } else if (value2 && typeof value2 === "object" && value2.constructor === Object) {
              newObject[property] = clone(value2);
            } else {
              newObject[property] = value2;
            }
          }
        }
        return newObject;
      }
      function lookupGetter(object, prop) {
        while (object !== null) {
          const desc = getOwnPropertyDescriptor(object, prop);
          if (desc) {
            if (desc.get) {
              return unapply(desc.get);
            }
            if (typeof desc.value === "function") {
              return unapply(desc.value);
            }
          }
          object = getPrototypeOf(object);
        }
        function fallbackValue() {
          return null;
        }
        return fallbackValue;
      }
      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", "section", "select", "shadow", "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"]);
      const svg$1 = freeze(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]);
      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"]);
      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"]);
      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"]);
      const mathMlDisallowed = freeze(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]);
      const text = freeze(["#text"]);
      const html = freeze(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "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", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns", "slot"]);
      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", "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", "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-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"]);
      const mathMl = freeze(["accent", "accentunder", "align", "bevelled", "close", "columnsalign", "columnlines", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lspace", "lquote", "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"]);
      const xml = freeze(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]);
      const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm);
      const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
      const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm);
      const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/);
      const ARIA_ATTR = seal(/^aria-[\-\w]+$/);
      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
      );
      const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
      const ATTR_WHITESPACE = seal(
        /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
        // eslint-disable-line no-control-regex
      );
      const DOCTYPE_NAME = seal(/^html$/i);
      const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
      var EXPRESSIONS = Object.freeze({
        __proto__: null,
        ARIA_ATTR,
        ATTR_WHITESPACE,
        CUSTOM_ELEMENT,
        DATA_ATTR,
        DOCTYPE_NAME,
        ERB_EXPR,
        IS_ALLOWED_URI,
        IS_SCRIPT_OR_DATA,
        MUSTACHE_EXPR,
        TMPLIT_EXPR
      });
      const NODE_TYPE = {
        element: 1,
        attribute: 2,
        text: 3,
        cdataSection: 4,
        entityReference: 5,
        // Deprecated
        entityNode: 6,
        // Deprecated
        progressingInstruction: 7,
        comment: 8,
        document: 9,
        documentType: 10,
        documentFragment: 11,
        notation: 12
        // Deprecated
      };
      const getGlobal = function getGlobal2() {
        return typeof window === "undefined" ? null : window;
      };
      const _createTrustedTypesPolicy = function _createTrustedTypesPolicy2(trustedTypes, purifyHostElement) {
        if (typeof trustedTypes !== "object" || typeof trustedTypes.createPolicy !== "function") {
          return null;
        }
        let suffix = null;
        const ATTR_NAME = "data-tt-policy-suffix";
        if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
          suffix = purifyHostElement.getAttribute(ATTR_NAME);
        }
        const policyName = "dompurify" + (suffix ? "#" + suffix : "");
        try {
          return trustedTypes.createPolicy(policyName, {
            createHTML(html2) {
              return html2;
            },
            createScriptURL(scriptUrl) {
              return scriptUrl;
            }
          });
        } catch (_) {
          console.warn("TrustedTypes policy " + policyName + " could not be created.");
          return null;
        }
      };
      const _createHooksMap = function _createHooksMap2() {
        return {
          afterSanitizeAttributes: [],
          afterSanitizeElements: [],
          afterSanitizeShadowDOM: [],
          beforeSanitizeAttributes: [],
          beforeSanitizeElements: [],
          beforeSanitizeShadowDOM: [],
          uponSanitizeAttribute: [],
          uponSanitizeElement: [],
          uponSanitizeShadowNode: []
        };
      };
      function createDOMPurify() {
        let window2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
        const DOMPurify = (root) => createDOMPurify(root);
        DOMPurify.version = "3.2.6";
        DOMPurify.removed = [];
        if (!window2 || !window2.document || window2.document.nodeType !== NODE_TYPE.document || !window2.Element) {
          DOMPurify.isSupported = false;
          return DOMPurify;
        }
        let {
          document: document2
        } = window2;
        const originalDocument = document2;
        const currentScript = originalDocument.currentScript;
        const {
          DocumentFragment,
          HTMLTemplateElement,
          Node,
          Element,
          NodeFilter: NodeFilter2,
          NamedNodeMap = window2.NamedNodeMap || window2.MozNamedAttrMap,
          HTMLFormElement,
          DOMParser: DOMParser2,
          trustedTypes
        } = window2;
        const ElementPrototype = Element.prototype;
        const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
        const remove2 = lookupGetter(ElementPrototype, "remove");
        const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
        const getChildNodes2 = lookupGetter(ElementPrototype, "childNodes");
        const getParentNode = lookupGetter(ElementPrototype, "parentNode");
        if (typeof HTMLTemplateElement === "function") {
          const template = document2.createElement("template");
          if (template.content && template.content.ownerDocument) {
            document2 = template.content.ownerDocument;
          }
        }
        let trustedTypesPolicy;
        let emptyHTML = "";
        const {
          implementation,
          createNodeIterator,
          createDocumentFragment,
          getElementsByTagName
        } = document2;
        const {
          importNode: importNode2
        } = originalDocument;
        let hooks = _createHooksMap();
        DOMPurify.isSupported = typeof entries === "function" && typeof getParentNode === "function" && implementation && implementation.createHTMLDocument !== void 0;
        const {
          MUSTACHE_EXPR: MUSTACHE_EXPR2,
          ERB_EXPR: ERB_EXPR2,
          TMPLIT_EXPR: TMPLIT_EXPR2,
          DATA_ATTR: DATA_ATTR2,
          ARIA_ATTR: ARIA_ATTR2,
          IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA2,
          ATTR_WHITESPACE: ATTR_WHITESPACE2,
          CUSTOM_ELEMENT: CUSTOM_ELEMENT2
        } = EXPRESSIONS;
        let {
          IS_ALLOWED_URI: IS_ALLOWED_URI$1
        } = EXPRESSIONS;
        let ALLOWED_TAGS = null;
        const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
        let ALLOWED_ATTR = null;
        const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
        let CUSTOM_ELEMENT_HANDLING = Object.seal(create$7(null, {
          tagNameCheck: {
            writable: true,
            configurable: false,
            enumerable: true,
            value: null
          },
          attributeNameCheck: {
            writable: true,
            configurable: false,
            enumerable: true,
            value: null
          },
          allowCustomizedBuiltInElements: {
            writable: true,
            configurable: false,
            enumerable: true,
            value: false
          }
        }));
        let FORBID_TAGS = null;
        let FORBID_ATTR = null;
        let ALLOW_ARIA_ATTR = true;
        let ALLOW_DATA_ATTR = true;
        let ALLOW_UNKNOWN_PROTOCOLS = false;
        let ALLOW_SELF_CLOSE_IN_ATTR = true;
        let SAFE_FOR_TEMPLATES = false;
        let SAFE_FOR_XML = true;
        let WHOLE_DOCUMENT = false;
        let SET_CONFIG = false;
        let FORCE_BODY = false;
        let RETURN_DOM = false;
        let RETURN_DOM_FRAGMENT = false;
        let RETURN_TRUSTED_TYPE = false;
        let SANITIZE_DOM = true;
        let SANITIZE_NAMED_PROPS = false;
        const SANITIZE_NAMED_PROPS_PREFIX = "user-content-";
        let KEEP_CONTENT = true;
        let IN_PLACE = false;
        let USE_PROFILES = {};
        let FORBID_CONTENTS = null;
        const DEFAULT_FORBID_CONTENTS = addToSet({}, ["annotation-xml", "audio", "colgroup", "desc", "foreignobject", "head", "iframe", "math", "mi", "mn", "mo", "ms", "mtext", "noembed", "noframes", "noscript", "plaintext", "script", "style", "svg", "template", "thead", "title", "video", "xmp"]);
        let DATA_URI_TAGS = null;
        const DEFAULT_DATA_URI_TAGS = addToSet({}, ["audio", "video", "img", "source", "image", "track"]);
        let URI_SAFE_ATTRIBUTES = null;
        const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]);
        const MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML";
        const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
        const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
        let NAMESPACE = HTML_NAMESPACE;
        let IS_EMPTY_INPUT = false;
        let ALLOWED_NAMESPACES = null;
        const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
        let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ["mi", "mo", "mn", "ms", "mtext"]);
        let HTML_INTEGRATION_POINTS = addToSet({}, ["annotation-xml"]);
        const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ["title", "style", "font", "a", "script"]);
        let PARSER_MEDIA_TYPE = null;
        const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"];
        const DEFAULT_PARSER_MEDIA_TYPE = "text/html";
        let transformCaseFunc = null;
        let CONFIG = null;
        const formElement = document2.createElement("form");
        const isRegexOrFunction = function isRegexOrFunction2(testValue) {
          return testValue instanceof RegExp || testValue instanceof Function;
        };
        const _parseConfig = function _parseConfig2() {
          let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
          if (CONFIG && CONFIG === cfg) {
            return;
          }
          if (!cfg || typeof cfg !== "object") {
            cfg = {};
          }
          cfg = clone(cfg);
          PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
          SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
          transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase;
          ALLOWED_TAGS = objectHasOwnProperty(cfg, "ALLOWED_TAGS") ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
          ALLOWED_ATTR = objectHasOwnProperty(cfg, "ALLOWED_ATTR") ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
          ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, "ALLOWED_NAMESPACES") ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
          URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, "ADD_URI_SAFE_ATTR") ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
          DATA_URI_TAGS = objectHasOwnProperty(cfg, "ADD_DATA_URI_TAGS") ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
          FORBID_CONTENTS = objectHasOwnProperty(cfg, "FORBID_CONTENTS") ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
          FORBID_TAGS = objectHasOwnProperty(cfg, "FORBID_TAGS") ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
          FORBID_ATTR = objectHasOwnProperty(cfg, "FORBID_ATTR") ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
          USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES : false;
          ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
          ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
          ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;
          ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false;
          SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;
          SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false;
          WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;
          RETURN_DOM = cfg.RETURN_DOM || false;
          RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;
          RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;
          FORCE_BODY = cfg.FORCE_BODY || false;
          SANITIZE_DOM = cfg.SANITIZE_DOM !== false;
          SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false;
          KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
          IN_PLACE = cfg.IN_PLACE || false;
          IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
          NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
          MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
          HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
          CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
          if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
            CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
          }
          if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
            CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
          }
          if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === "boolean") {
            CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
          }
          if (SAFE_FOR_TEMPLATES) {
            ALLOW_DATA_ATTR = false;
          }
          if (RETURN_DOM_FRAGMENT) {
            RETURN_DOM = true;
          }
          if (USE_PROFILES) {
            ALLOWED_TAGS = addToSet({}, text);
            ALLOWED_ATTR = [];
            if (USE_PROFILES.html === true) {
              addToSet(ALLOWED_TAGS, html$1);
              addToSet(ALLOWED_ATTR, html);
            }
            if (USE_PROFILES.svg === true) {
              addToSet(ALLOWED_TAGS, svg$1);
              addToSet(ALLOWED_ATTR, svg);
              addToSet(ALLOWED_ATTR, xml);
            }
            if (USE_PROFILES.svgFilters === true) {
              addToSet(ALLOWED_TAGS, svgFilters);
              addToSet(ALLOWED_ATTR, svg);
              addToSet(ALLOWED_ATTR, xml);
            }
            if (USE_PROFILES.mathMl === true) {
              addToSet(ALLOWED_TAGS, mathMl$1);
              addToSet(ALLOWED_ATTR, mathMl);
              addToSet(ALLOWED_ATTR, xml);
            }
          }
          if (cfg.ADD_TAGS) {
            if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
              ALLOWED_TAGS = clone(ALLOWED_TAGS);
            }
            addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
          }
          if (cfg.ADD_ATTR) {
            if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
              ALLOWED_ATTR = clone(ALLOWED_ATTR);
            }
            addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
          }
          if (cfg.ADD_URI_SAFE_ATTR) {
            addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
          }
          if (cfg.FORBID_CONTENTS) {
            if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
              FORBID_CONTENTS = clone(FORBID_CONTENTS);
            }
            addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
          }
          if (KEEP_CONTENT) {
            ALLOWED_TAGS["#text"] = true;
          }
          if (WHOLE_DOCUMENT) {
            addToSet(ALLOWED_TAGS, ["html", "head", "body"]);
          }
          if (ALLOWED_TAGS.table) {
            addToSet(ALLOWED_TAGS, ["tbody"]);
            delete FORBID_TAGS.tbody;
          }
          if (cfg.TRUSTED_TYPES_POLICY) {
            if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== "function") {
              throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
            }
            if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") {
              throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
            }
            trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
            emptyHTML = trustedTypesPolicy.createHTML("");
          } else {
            if (trustedTypesPolicy === void 0) {
              trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
            }
            if (trustedTypesPolicy !== null && typeof emptyHTML === "string") {
              emptyHTML = trustedTypesPolicy.createHTML("");
            }
          }
          if (freeze) {
            freeze(cfg);
          }
          CONFIG = cfg;
        };
        const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
        const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
        const _checkValidNamespace = function _checkValidNamespace2(element) {
          let parent2 = getParentNode(element);
          if (!parent2 || !parent2.tagName) {
            parent2 = {
              namespaceURI: NAMESPACE,
              tagName: "template"
            };
          }
          const tagName = stringToLowerCase(element.tagName);
          const parentTagName = stringToLowerCase(parent2.tagName);
          if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
            return false;
          }
          if (element.namespaceURI === SVG_NAMESPACE) {
            if (parent2.namespaceURI === HTML_NAMESPACE) {
              return tagName === "svg";
            }
            if (parent2.namespaceURI === MATHML_NAMESPACE) {
              return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
            }
            return Boolean(ALL_SVG_TAGS[tagName]);
          }
          if (element.namespaceURI === MATHML_NAMESPACE) {
            if (parent2.namespaceURI === HTML_NAMESPACE) {
              return tagName === "math";
            }
            if (parent2.namespaceURI === SVG_NAMESPACE) {
              return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
            }
            return Boolean(ALL_MATHML_TAGS[tagName]);
          }
          if (element.namespaceURI === HTML_NAMESPACE) {
            if (parent2.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
              return false;
            }
            if (parent2.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
              return false;
            }
            return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
          }
          if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[element.namespaceURI]) {
            return true;
          }
          return false;
        };
        const _forceRemove = function _forceRemove2(node) {
          arrayPush(DOMPurify.removed, {
            element: node
          });
          try {
            getParentNode(node).removeChild(node);
          } catch (_) {
            remove2(node);
          }
        };
        const _removeAttribute = function _removeAttribute2(name2, element) {
          try {
            arrayPush(DOMPurify.removed, {
              attribute: element.getAttributeNode(name2),
              from: element
            });
          } catch (_) {
            arrayPush(DOMPurify.removed, {
              attribute: null,
              from: element
            });
          }
          element.removeAttribute(name2);
          if (name2 === "is") {
            if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
              try {
                _forceRemove(element);
              } catch (_) {
              }
            } else {
              try {
                element.setAttribute(name2, "");
              } catch (_) {
              }
            }
          }
        };
        const _initDocument = function _initDocument2(dirty) {
          let doc = null;
          let leadingWhitespace = null;
          if (FORCE_BODY) {
            dirty = "<remove></remove>" + dirty;
          } else {
            const matches = stringMatch(dirty, /^[\r\n\t ]+/);
            leadingWhitespace = matches && matches[0];
          }
          if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && NAMESPACE === HTML_NAMESPACE) {
            dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + "</body></html>";
          }
          const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
          if (NAMESPACE === HTML_NAMESPACE) {
            try {
              doc = new DOMParser2().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
            } catch (_) {
            }
          }
          if (!doc || !doc.documentElement) {
            doc = implementation.createDocument(NAMESPACE, "template", null);
            try {
              doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
            } catch (_) {
            }
          }
          const body = doc.body || doc.documentElement;
          if (dirty && leadingWhitespace) {
            body.insertBefore(document2.createTextNode(leadingWhitespace), body.childNodes[0] || null);
          }
          if (NAMESPACE === HTML_NAMESPACE) {
            return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? "html" : "body")[0];
          }
          return WHOLE_DOCUMENT ? doc.documentElement : body;
        };
        const _createNodeIterator = function _createNodeIterator2(root) {
          return createNodeIterator.call(
            root.ownerDocument || root,
            root,
            // eslint-disable-next-line no-bitwise
            NodeFilter2.SHOW_ELEMENT | NodeFilter2.SHOW_COMMENT | NodeFilter2.SHOW_TEXT | NodeFilter2.SHOW_PROCESSING_INSTRUCTION | NodeFilter2.SHOW_CDATA_SECTION,
            null
          );
        };
        const _isClobbered = function _isClobbered2(element) {
          return element instanceof HTMLFormElement && (typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function");
        };
        const _isNode = function _isNode2(value2) {
          return typeof Node === "function" && value2 instanceof Node;
        };
        function _executeHooks(hooks2, currentNode, data2) {
          arrayForEach(hooks2, (hook) => {
            hook.call(DOMPurify, currentNode, data2, CONFIG);
          });
        }
        const _sanitizeElements = function _sanitizeElements2(currentNode) {
          let content = null;
          _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
          if (_isClobbered(currentNode)) {
            _forceRemove(currentNode);
            return true;
          }
          const tagName = transformCaseFunc(currentNode.nodeName);
          _executeHooks(hooks.uponSanitizeElement, currentNode, {
            tagName,
            allowedTags: ALLOWED_TAGS
          });
          if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
            _forceRemove(currentNode);
            return true;
          }
          if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
            _forceRemove(currentNode);
            return true;
          }
          if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
            _forceRemove(currentNode);
            return true;
          }
          if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
            if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
              if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
                return false;
              }
              if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
                return false;
              }
            }
            if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
              const parentNode = getParentNode(currentNode) || currentNode.parentNode;
              const childNodes = getChildNodes2(currentNode) || currentNode.childNodes;
              if (childNodes && parentNode) {
                const childCount = childNodes.length;
                for (let i = childCount - 1; i >= 0; --i) {
                  const childClone = cloneNode(childNodes[i], true);
                  childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
                  parentNode.insertBefore(childClone, getNextSibling(currentNode));
                }
              }
            }
            _forceRemove(currentNode);
            return true;
          }
          if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
            _forceRemove(currentNode);
            return true;
          }
          if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
            _forceRemove(currentNode);
            return true;
          }
          if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
            content = currentNode.textContent;
            arrayForEach([MUSTACHE_EXPR2, ERB_EXPR2, TMPLIT_EXPR2], (expr) => {
              content = stringReplace(content, expr, " ");
            });
            if (currentNode.textContent !== content) {
              arrayPush(DOMPurify.removed, {
                element: currentNode.cloneNode()
              });
              currentNode.textContent = content;
            }
          }
          _executeHooks(hooks.afterSanitizeElements, currentNode, null);
          return false;
        };
        const _isValidAttribute = function _isValidAttribute2(lcTag, lcName, value2) {
          if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value2 in document2 || value2 in formElement)) {
            return false;
          }
          if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR2, lcName)) ;
          else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR2, lcName)) ;
          else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
            if (
              // First condition does a very basic check if a) it's basically a valid custom element tagname AND
              // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
              // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
              _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)) || // Alternative, second condition checks if it's an `is`-attribute, AND
              // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
              lcName === "is" && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value2) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value2))
            ) ;
            else {
              return false;
            }
          } else if (URI_SAFE_ATTRIBUTES[lcName]) ;
          else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value2, ATTR_WHITESPACE2, ""))) ;
          else if ((lcName === "src" || lcName === "xlink:href" || lcName === "href") && lcTag !== "script" && stringIndexOf(value2, "data:") === 0 && DATA_URI_TAGS[lcTag]) ;
          else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA2, stringReplace(value2, ATTR_WHITESPACE2, ""))) ;
          else if (value2) {
            return false;
          } else ;
          return true;
        };
        const _isBasicCustomElement = function _isBasicCustomElement2(tagName) {
          return tagName !== "annotation-xml" && stringMatch(tagName, CUSTOM_ELEMENT2);
        };
        const _sanitizeAttributes = function _sanitizeAttributes2(currentNode) {
          _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
          const {
            attributes
          } = currentNode;
          if (!attributes || _isClobbered(currentNode)) {
            return;
          }
          const hookEvent = {
            attrName: "",
            attrValue: "",
            keepAttr: true,
            allowedAttributes: ALLOWED_ATTR,
            forceKeepAttr: void 0
          };
          let l = attributes.length;
          while (l--) {
            const attr = attributes[l];
            const {
              name: name2,
              namespaceURI,
              value: attrValue
            } = attr;
            const lcName = transformCaseFunc(name2);
            const initValue = attrValue;
            let value2 = name2 === "value" ? initValue : stringTrim(initValue);
            hookEvent.attrName = lcName;
            hookEvent.attrValue = value2;
            hookEvent.keepAttr = true;
            hookEvent.forceKeepAttr = void 0;
            _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
            value2 = hookEvent.attrValue;
            if (SANITIZE_NAMED_PROPS && (lcName === "id" || lcName === "name")) {
              _removeAttribute(name2, currentNode);
              value2 = SANITIZE_NAMED_PROPS_PREFIX + value2;
            }
            if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value2)) {
              _removeAttribute(name2, currentNode);
              continue;
            }
            if (hookEvent.forceKeepAttr) {
              continue;
            }
            if (!hookEvent.keepAttr) {
              _removeAttribute(name2, currentNode);
              continue;
            }
            if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value2)) {
              _removeAttribute(name2, currentNode);
              continue;
            }
            if (SAFE_FOR_TEMPLATES) {
              arrayForEach([MUSTACHE_EXPR2, ERB_EXPR2, TMPLIT_EXPR2], (expr) => {
                value2 = stringReplace(value2, expr, " ");
              });
            }
            const lcTag = transformCaseFunc(currentNode.nodeName);
            if (!_isValidAttribute(lcTag, lcName, value2)) {
              _removeAttribute(name2, currentNode);
              continue;
            }
            if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function") {
              if (namespaceURI) ;
              else {
                switch (trustedTypes.getAttributeType(lcTag, lcName)) {
                  case "TrustedHTML": {
                    value2 = trustedTypesPolicy.createHTML(value2);
                    break;
                  }
                  case "TrustedScriptURL": {
                    value2 = trustedTypesPolicy.createScriptURL(value2);
                    break;
                  }
                }
              }
            }
            if (value2 !== initValue) {
              try {
                if (namespaceURI) {
                  currentNode.setAttributeNS(namespaceURI, name2, value2);
                } else {
                  currentNode.setAttribute(name2, value2);
                }
                if (_isClobbered(currentNode)) {
                  _forceRemove(currentNode);
                } else {
                  arrayPop(DOMPurify.removed);
                }
              } catch (_) {
                _removeAttribute(name2, currentNode);
              }
            }
          }
          _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
        };
        const _sanitizeShadowDOM = function _sanitizeShadowDOM2(fragment) {
          let shadowNode = null;
          const shadowIterator = _createNodeIterator(fragment);
          _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
          while (shadowNode = shadowIterator.nextNode()) {
            _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
            _sanitizeElements(shadowNode);
            _sanitizeAttributes(shadowNode);
            if (shadowNode.content instanceof DocumentFragment) {
              _sanitizeShadowDOM2(shadowNode.content);
            }
          }
          _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
        };
        DOMPurify.sanitize = function(dirty) {
          let cfg = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
          let body = null;
          let importedNode = null;
          let currentNode = null;
          let returnNode = null;
          IS_EMPTY_INPUT = !dirty;
          if (IS_EMPTY_INPUT) {
            dirty = "<!-->";
          }
          if (typeof dirty !== "string" && !_isNode(dirty)) {
            if (typeof dirty.toString === "function") {
              dirty = dirty.toString();
              if (typeof dirty !== "string") {
                throw typeErrorCreate("dirty is not a string, aborting");
              }
            } else {
              throw typeErrorCreate("toString is not a function");
            }
          }
          if (!DOMPurify.isSupported) {
            return dirty;
          }
          if (!SET_CONFIG) {
            _parseConfig(cfg);
          }
          DOMPurify.removed = [];
          if (typeof dirty === "string") {
            IN_PLACE = false;
          }
          if (IN_PLACE) {
            if (dirty.nodeName) {
              const tagName = transformCaseFunc(dirty.nodeName);
              if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
                throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
              }
            }
          } else if (dirty instanceof Node) {
            body = _initDocument("<!---->");
            importedNode = body.ownerDocument.importNode(dirty, true);
            if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === "BODY") {
              body = importedNode;
            } else if (importedNode.nodeName === "HTML") {
              body = importedNode;
            } else {
              body.appendChild(importedNode);
            }
          } else {
            if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
            dirty.indexOf("<") === -1) {
              return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
            }
            body = _initDocument(dirty);
            if (!body) {
              return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : "";
            }
          }
          if (body && FORCE_BODY) {
            _forceRemove(body.firstChild);
          }
          const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
          while (currentNode = nodeIterator.nextNode()) {
            _sanitizeElements(currentNode);
            _sanitizeAttributes(currentNode);
            if (currentNode.content instanceof DocumentFragment) {
              _sanitizeShadowDOM(currentNode.content);
            }
          }
          if (IN_PLACE) {
            return dirty;
          }
          if (RETURN_DOM) {
            if (RETURN_DOM_FRAGMENT) {
              returnNode = createDocumentFragment.call(body.ownerDocument);
              while (body.firstChild) {
                returnNode.appendChild(body.firstChild);
              }
            } else {
              returnNode = body;
            }
            if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
              returnNode = importNode2.call(originalDocument, returnNode, true);
            }
            return returnNode;
          }
          let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
          if (WHOLE_DOCUMENT && ALLOWED_TAGS["!doctype"] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
            serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
          }
          if (SAFE_FOR_TEMPLATES) {
            arrayForEach([MUSTACHE_EXPR2, ERB_EXPR2, TMPLIT_EXPR2], (expr) => {
              serializedHTML = stringReplace(serializedHTML, expr, " ");
            });
          }
          return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
        };
        DOMPurify.setConfig = function() {
          let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
          _parseConfig(cfg);
          SET_CONFIG = true;
        };
        DOMPurify.clearConfig = function() {
          CONFIG = null;
          SET_CONFIG = false;
        };
        DOMPurify.isValidAttribute = function(tag, attr, value2) {
          if (!CONFIG) {
            _parseConfig({});
          }
          const lcTag = transformCaseFunc(tag);
          const lcName = transformCaseFunc(attr);
          return _isValidAttribute(lcTag, lcName, value2);
        };
        DOMPurify.addHook = function(entryPoint, hookFunction) {
          if (typeof hookFunction !== "function") {
            return;
          }
          arrayPush(hooks[entryPoint], hookFunction);
        };
        DOMPurify.removeHook = function(entryPoint, hookFunction) {
          if (hookFunction !== void 0) {
            const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
            return index === -1 ? void 0 : arraySplice(hooks[entryPoint], index, 1)[0];
          }
          return arrayPop(hooks[entryPoint]);
        };
        DOMPurify.removeHooks = function(entryPoint) {
          hooks[entryPoint] = [];
        };
        DOMPurify.removeAllHooks = function() {
          hooks = _createHooksMap();
        };
        return DOMPurify;
      }
      var purify = createDOMPurify();
      const each$6 = Tools.each, trim = Tools.trim;
      const queryParts = [
        "source",
        "protocol",
        "authority",
        "userInfo",
        "user",
        "password",
        "host",
        "port",
        "relative",
        "path",
        "directory",
        "file",
        "query",
        "anchor"
      ];
      const DEFAULT_PORTS = {
        ftp: 21,
        http: 80,
        https: 443,
        mailto: 25
      };
      const safeSvgDataUrlElements = ["img", "video"];
      const blockSvgDataUris = (allowSvgDataUrls, tagName) => {
        if (isNonNullable(allowSvgDataUrls)) {
          return !allowSvgDataUrls;
        } else {
          return isNonNullable(tagName) ? !contains$2(safeSvgDataUrlElements, tagName) : true;
        }
      };
      const decodeUri = (encodedUri) => {
        try {
          return decodeURIComponent(encodedUri);
        } catch {
          return unescape(encodedUri);
        }
      };
      const isInvalidUri = (settings, uri, tagName) => {
        const decodedUri = decodeUri(uri).replace(/\s/g, "");
        if (settings.allow_script_urls) {
          return false;
        } else if (/((java|vb)script|mhtml):/i.test(decodedUri)) {
          return true;
        } else if (settings.allow_html_data_urls) {
          return false;
        } else if (/^data:image\//i.test(decodedUri)) {
          return blockSvgDataUris(settings.allow_svg_data_urls, tagName) && /^data:image\/svg\+xml/i.test(decodedUri);
        } else {
          return /^data:/i.test(decodedUri);
        }
      };
      class URI {
        static parseDataUri(uri) {
          let type2;
          const uriComponents = decodeURIComponent(uri).split(",");
          const matches = /data:([^;]+)/.exec(uriComponents[0]);
          if (matches) {
            type2 = matches[1];
          }
          return {
            type: type2,
            data: uriComponents[1]
          };
        }
        /**
         * Check to see if a URI is safe to use in the Document Object Model (DOM). This will return
         * true if the URI can be used in the DOM without potentially triggering a security issue.
         *
         * @method isDomSafe
         * @static
         * @param {String} uri The URI to be validated.
         * @param {Object} context An optional HTML tag name where the element is being used.
         * @param {Object} options An optional set of options to use when determining if the URI is safe.
         * @return {Boolean} True if the URI is safe, otherwise false.
         */
        static isDomSafe(uri, context2, options = {}) {
          if (options.allow_script_urls) {
            return true;
          } else {
            const decodedUri = Entities.decode(uri).replace(/[\s\u0000-\u001F]+/g, "");
            return !isInvalidUri(options, decodedUri, context2);
          }
        }
        static getDocumentBaseUrl(loc) {
          let baseUrl;
          if (loc.protocol.indexOf("http") !== 0 && loc.protocol !== "file:") {
            baseUrl = loc.href ?? "";
          } else {
            baseUrl = loc.protocol + "//" + loc.host + loc.pathname;
          }
          if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) {
            baseUrl = baseUrl.replace(/[\?#].*$/, "").replace(/[\/\\][^\/]+$/, "");
            if (!/[\/\\]$/.test(baseUrl)) {
              baseUrl += "/";
            }
          }
          return baseUrl;
        }
        source;
        protocol;
        authority;
        userInfo;
        user;
        password;
        host;
        port;
        relative;
        path = "";
        directory = "";
        file;
        query;
        anchor;
        settings;
        /**
         * Constructs a new URI instance.
         *
         * @constructor
         * @method URI
         * @param {String} url URI string to parse.
         * @param {Object} settings Optional settings object.
         */
        constructor(url, settings = {}) {
          url = trim(url);
          this.settings = settings;
          const baseUri = settings.base_uri;
          const self = this;
          if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
            self.source = url;
            return;
          }
          const isProtocolRelative = url.indexOf("//") === 0;
          if (url.indexOf("/") === 0 && !isProtocolRelative) {
            url = (baseUri ? baseUri.protocol || "http" : "http") + "://mce_host" + url;
          }
          if (!/^[\w\-]*:?\/\//.test(url)) {
            const baseUrl = baseUri ? baseUri.path : new URI(document.location.href).directory;
            if (baseUri?.protocol === "") {
              url = "//mce_host" + self.toAbsPath(baseUrl, url);
            } else {
              const match2 = /([^#?]*)([#?]?.*)/.exec(url);
              if (match2) {
                url = (baseUri && baseUri.protocol || "http") + "://mce_host" + self.toAbsPath(baseUrl, match2[1]) + match2[2];
              }
            }
          }
          url = url.replace(/@@/g, "(mce_at)");
          const urlMatch = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?(\[[a-zA-Z0-9:.%]+\]|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
          if (urlMatch) {
            each$6(queryParts, (v, i) => {
              let part = urlMatch[i];
              if (part) {
                part = part.replace(/\(mce_at\)/g, "@@");
              }
              self[v] = part;
            });
          }
          if (baseUri) {
            if (!self.protocol) {
              self.protocol = baseUri.protocol;
            }
            if (!self.userInfo) {
              self.userInfo = baseUri.userInfo;
            }
            if (!self.port && self.host === "mce_host") {
              self.port = baseUri.port;
            }
            if (!self.host || self.host === "mce_host") {
              self.host = baseUri.host;
            }
            self.source = "";
          }
          if (isProtocolRelative) {
            self.protocol = "";
          }
        }
        /**
         * Sets the internal path part of the URI.
         *
         * @method setPath
         * @param {String} path Path string to set.
         */
        setPath(path2) {
          const pathMatch = /^(.*?)\/?(\w+)?$/.exec(path2);
          if (pathMatch) {
            this.path = pathMatch[0];
            this.directory = pathMatch[1];
            this.file = pathMatch[2];
          }
          this.source = "";
          this.getURI();
        }
        /**
         * Converts the specified URI into a relative URI based on the current URI instance location.
         *
         * @method toRelative
         * @param {String} uri URI to convert into a relative path/URI.
         * @return {String} Relative URI from the point specified in the current URI instance.
         * @example
         * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm
         * const url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm');
         */
        toRelative(uri) {
          if (uri === "./") {
            return uri;
          }
          const relativeUri = new URI(uri, { base_uri: this });
          if (relativeUri.host !== "mce_host" && this.host !== relativeUri.host && relativeUri.host || this.port !== relativeUri.port || this.protocol !== relativeUri.protocol && relativeUri.protocol !== "") {
            return relativeUri.getURI();
          }
          const tu = this.getURI(), uu = relativeUri.getURI();
          if (tu === uu || tu.charAt(tu.length - 1) === "/" && tu.substr(0, tu.length - 1) === uu) {
            return tu;
          }
          let output = this.toRelPath(this.path, relativeUri.path);
          if (relativeUri.query) {
            output += "?" + relativeUri.query;
          }
          if (relativeUri.anchor) {
            output += "#" + relativeUri.anchor;
          }
          return output;
        }
        /**
         * Converts the specified URI into a absolute URI based on the current URI instance location.
         *
         * @method toAbsolute
         * @param {String} uri URI to convert into a relative path/URI.
         * @param {Boolean} noHost No host and protocol prefix.
         * @return {String} Absolute URI from the point specified in the current URI instance.
         * @example
         * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm
         * const url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm');
         */
        toAbsolute(uri, noHost) {
          const absoluteUri = new URI(uri, { base_uri: this });
          return absoluteUri.getURI(noHost && this.isSameOrigin(absoluteUri));
        }
        /**
         * Determine whether the given URI has the same origin as this URI.  Based on RFC-6454.
         * Supports default ports for protocols listed in DEFAULT_PORTS.  Unsupported protocols will fail safe: they
         * won't match, if the port specifications differ.
         *
         * @method isSameOrigin
         * @param {tinymce.util.URI} uri Uri instance to compare.
         * @returns {Boolean} True if the origins are the same.
         */
        isSameOrigin(uri) {
          if (this.host == uri.host && this.protocol == uri.protocol) {
            if (this.port == uri.port) {
              return true;
            }
            const defaultPort = this.protocol ? DEFAULT_PORTS[this.protocol] : null;
            if (defaultPort && (this.port || defaultPort) == (uri.port || defaultPort)) {
              return true;
            }
          }
          return false;
        }
        /**
         * Converts a absolute path into a relative path.
         *
         * @method toRelPath
         * @param {String} base Base point to convert the path from.
         * @param {String} path Absolute path to convert into a relative path.
         */
        toRelPath(base, path2) {
          let breakPoint = 0, out = "", i, l;
          const normalizedBase = base.substring(0, base.lastIndexOf("/")).split("/");
          const items = path2.split("/");
          if (normalizedBase.length >= items.length) {
            for (i = 0, l = normalizedBase.length; i < l; i++) {
              if (i >= items.length || normalizedBase[i] !== items[i]) {
                breakPoint = i + 1;
                break;
              }
            }
          }
          if (normalizedBase.length < items.length) {
            for (i = 0, l = items.length; i < l; i++) {
              if (i >= normalizedBase.length || normalizedBase[i] !== items[i]) {
                breakPoint = i + 1;
                break;
              }
            }
          }
          if (breakPoint === 1) {
            return path2;
          }
          for (i = 0, l = normalizedBase.length - (breakPoint - 1); i < l; i++) {
            out += "../";
          }
          for (i = breakPoint - 1, l = items.length; i < l; i++) {
            if (i !== breakPoint - 1) {
              out += "/" + items[i];
            } else {
              out += items[i];
            }
          }
          return out;
        }
        /**
         * Converts a relative path into a absolute path.
         *
         * @method toAbsPath
         * @param {String} base Base point to convert the path from.
         * @param {String} path Relative path to convert into an absolute path.
         */
        toAbsPath(base, path2) {
          let nb = 0;
          const tr = /\/$/.test(path2) ? "/" : "";
          const normalizedBase = base.split("/");
          const normalizedPath = path2.split("/");
          const baseParts = [];
          each$6(normalizedBase, (k) => {
            if (k) {
              baseParts.push(k);
            }
          });
          const pathParts = [];
          for (let i2 = normalizedPath.length - 1; i2 >= 0; i2--) {
            if (normalizedPath[i2].length === 0 || normalizedPath[i2] === ".") {
              continue;
            }
            if (normalizedPath[i2] === "..") {
              nb++;
              continue;
            }
            if (nb > 0) {
              nb--;
              continue;
            }
            pathParts.push(normalizedPath[i2]);
          }
          const i = baseParts.length - nb;
          let outPath;
          if (i <= 0) {
            outPath = reverse(pathParts).join("/");
          } else {
            outPath = baseParts.slice(0, i).join("/") + "/" + reverse(pathParts).join("/");
          }
          if (outPath.indexOf("/") !== 0) {
            outPath = "/" + outPath;
          }
          if (tr && outPath.lastIndexOf("/") !== outPath.length - 1) {
            outPath += tr;
          }
          return outPath;
        }
        /**
         * Returns the full URI of the internal structure.
         *
         * @method getURI
         * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false.
         */
        getURI(noProtoHost = false) {
          let s;
          if (!this.source || noProtoHost) {
            s = "";
            if (!noProtoHost) {
              if (this.protocol) {
                s += this.protocol + "://";
              } else {
                s += "//";
              }
              if (this.userInfo) {
                s += this.userInfo + "@";
              }
              if (this.host) {
                s += this.host;
              }
              if (this.port) {
                s += ":" + this.port;
              }
            }
            if (this.path) {
              s += this.path;
            }
            if (this.query) {
              s += "?" + this.query;
            }
            if (this.anchor) {
              s += "#" + this.anchor;
            }
            this.source = s;
          }
          return this.source;
        }
      }
      const filteredUrlAttrs = Tools.makeMap("src,href,data,background,action,formaction,poster,xlink:href");
      const internalElementAttr = "data-mce-type";
      let uid = 0;
      const processNode = (node, settings, schema, scope, evt) => {
        const validate2 = settings.validate;
        const specialElements = schema.getSpecialElements();
        if (node.nodeType === COMMENT) {
          if (!settings.allow_conditional_comments && /^\[if/i.test(node.nodeValue ?? "")) {
            node.nodeValue = " " + node.nodeValue;
          }
          if (settings.sanitize && settings.allow_html_in_comments && isString(node.nodeValue)) {
            node.nodeValue = encodeData(node.nodeValue);
          }
        }
        const lcTagName = evt?.tagName ?? node.nodeName.toLowerCase();
        if (scope !== "html" && schema.isValid(scope)) {
          if (isNonNullable(evt)) {
            evt.allowedTags[lcTagName] = true;
          }
          return;
        }
        if (node.nodeType !== ELEMENT || lcTagName === "body") {
          return;
        }
        const element = SugarElement.fromDom(node);
        const isInternalElement = has$1(element, internalElementAttr);
        const bogus = get$9(element, "data-mce-bogus");
        if (!isInternalElement && isString(bogus)) {
          if (bogus === "all") {
            remove$8(element);
          } else {
            unwrap(element);
          }
          return;
        }
        const rule = schema.getElementRule(lcTagName);
        if (validate2 && !rule) {
          if (has$2(specialElements, lcTagName)) {
            remove$8(element);
          } else {
            unwrap(element);
          }
          return;
        } else {
          if (isNonNullable(evt)) {
            evt.allowedTags[lcTagName] = true;
          }
        }
        if (validate2 && rule && !isInternalElement) {
          each$e(rule.attributesForced ?? [], (attr) => {
            set$4(element, attr.name, attr.value === "{$uid}" ? `mce_${uid++}` : attr.value);
          });
          each$e(rule.attributesDefault ?? [], (attr) => {
            if (!has$1(element, attr.name)) {
              set$4(element, attr.name, attr.value === "{$uid}" ? `mce_${uid++}` : attr.value);
            }
          });
          if (rule.attributesRequired && !exists(rule.attributesRequired, (attr) => has$1(element, attr))) {
            unwrap(element);
            return;
          }
          if (rule.removeEmptyAttrs && hasNone(element)) {
            unwrap(element);
            return;
          }
          if (rule.outputName && rule.outputName !== lcTagName) {
            mutate(element, rule.outputName);
          }
        }
      };
      const processAttr = (ele, settings, schema, scope, evt) => {
        const tagName = ele.tagName.toLowerCase();
        const { attrName, attrValue } = evt;
        evt.keepAttr = shouldKeepAttribute(settings, schema, scope, tagName, attrName, attrValue);
        if (evt.keepAttr) {
          evt.allowedAttributes[attrName] = true;
          if (isBooleanAttributeOfNonCustomElement(attrName, schema, ele.nodeName)) {
            evt.attrValue = attrName;
          }
          if (settings.allow_svg_data_urls && startsWith(attrValue, "data:image/svg+xml")) {
            evt.forceKeepAttr = true;
          }
        } else if (isRequiredAttributeOfInternalElement(ele, attrName)) {
          evt.forceKeepAttr = true;
        }
      };
      const shouldKeepAttribute = (settings, schema, scope, tagName, attrName, attrValue) => {
        if (scope !== "html" && !isNonHtmlElementRootName(tagName)) {
          return true;
        }
        return !(attrName in filteredUrlAttrs && isInvalidUri(settings, attrValue, tagName)) && (!settings.validate || schema.isValid(tagName, attrName) || startsWith(attrName, "data-") || startsWith(attrName, "aria-"));
      };
      const isRequiredAttributeOfInternalElement = (ele, attrName) => ele.hasAttribute(internalElementAttr) && (attrName === "id" || attrName === "class" || attrName === "style");
      const isBooleanAttributeOfNonCustomElement = (attrName, schema, nodeName) => attrName in schema.getBoolAttrs() && !has$2(schema.getCustomElements(), nodeName.toLowerCase());
      const filterAttributes = (ele, settings, schema, scope) => {
        const { attributes } = ele;
        for (let i = attributes.length - 1; i >= 0; i--) {
          const attr = attributes[i];
          const attrName = attr.name;
          const attrValue = attr.value;
          if (!shouldKeepAttribute(settings, schema, scope, ele.tagName.toLowerCase(), attrName, attrValue) && !isRequiredAttributeOfInternalElement(ele, attrName)) {
            ele.removeAttribute(attrName);
          } else if (isBooleanAttributeOfNonCustomElement(attrName, schema, ele.nodeName)) {
            ele.setAttribute(attrName, attrName);
          }
        }
      };
      const setupPurify = (settings, schema, namespaceTracker) => {
        const purify$1 = purify();
        purify$1.addHook("uponSanitizeElement", (ele, evt) => {
          processNode(ele, settings, schema, namespaceTracker.track(ele), evt);
        });
        purify$1.addHook("uponSanitizeAttribute", (ele, evt) => {
          processAttr(ele, settings, schema, namespaceTracker.current(), evt);
        });
        return purify$1;
      };
      const getPurifyConfig = (settings, mimeType) => {
        const basePurifyConfig = {
          IN_PLACE: true,
          ALLOW_UNKNOWN_PROTOCOLS: true,
          // Deliberately ban all tags and attributes by default, and then un-ban them on demand in hooks
          // #comment and #cdata-section are always allowed as they aren't controlled via the schema
          // body is also allowed due to the DOMPurify checking the root node before sanitizing
          ALLOWED_TAGS: ["#comment", "#cdata-section", "body", "html"],
          ALLOWED_ATTR: []
        };
        const config = { ...basePurifyConfig };
        config.PARSER_MEDIA_TYPE = mimeType;
        if (settings.allow_script_urls) {
          config.ALLOWED_URI_REGEXP = /.*/;
        } else if (settings.allow_html_data_urls) {
          config.ALLOWED_URI_REGEXP = /^(?!(\w+script|mhtml):)/i;
        }
        return config;
      };
      const sanitizeSvgElement = (ele) => {
        const xlinkAttrs = ["type", "href", "role", "arcrole", "title", "show", "actuate", "label", "from", "to"].map((name2) => `xlink:${name2}`);
        const config = {
          IN_PLACE: true,
          USE_PROFILES: {
            html: true,
            svg: true,
            svgFilters: true
          },
          ALLOWED_ATTR: xlinkAttrs
        };
        purify().sanitize(ele, config);
      };
      const sanitizeMathmlElement = (node, settings) => {
        const config = {
          IN_PLACE: true,
          USE_PROFILES: {
            mathMl: true
          }
        };
        const purify$1 = purify();
        const allowedEncodings = settings.allow_mathml_annotation_encodings;
        const hasAllowedEncodings = isArray$1(allowedEncodings) && allowedEncodings.length > 0;
        const hasValidEncoding = (el) => {
          const encoding = el.getAttribute("encoding");
          return hasAllowedEncodings && isString(encoding) && contains$2(allowedEncodings, encoding);
        };
        const isValidElementOpt = (node2, lcTagName) => {
          if (hasAllowedEncodings && lcTagName === "semantics") {
            return Optional.some(true);
          } else if (lcTagName === "annotation") {
            return Optional.some(isElement$7(node2) && hasValidEncoding(node2));
          } else if (isArray$1(settings.extended_mathml_elements)) {
            if (settings.extended_mathml_elements.includes(lcTagName)) {
              return Optional.from(true);
            } else {
              return Optional.none();
            }
          } else {
            return Optional.none();
          }
        };
        purify$1.addHook("uponSanitizeElement", (node2, evt) => {
          const lcTagName = evt.tagName ?? node2.nodeName.toLowerCase();
          const keepElementOpt = isValidElementOpt(node2, lcTagName);
          keepElementOpt.each((keepElement) => {
            evt.allowedTags[lcTagName] = keepElement;
            if (!keepElement && settings.sanitize) {
              if (isElement$7(node2)) {
                node2.remove();
              }
            }
          });
        });
        purify$1.addHook("uponSanitizeAttribute", (_node, event) => {
          if (isArray$1(settings.extended_mathml_attributes)) {
            const keepAttribute = settings.extended_mathml_attributes.includes(event.attrName);
            if (keepAttribute) {
              event.forceKeepAttr = true;
            }
          }
        });
        purify$1.sanitize(node, config);
      };
      const mkSanitizeNamespaceElement = (settings) => (ele) => {
        const namespaceType = toScopeType(ele);
        if (namespaceType === "svg") {
          sanitizeSvgElement(ele);
        } else if (namespaceType === "math") {
          sanitizeMathmlElement(ele, settings);
        } else {
          throw new Error("Not a namespace element");
        }
      };
      const getSanitizer = (settings, schema) => {
        const namespaceTracker = createNamespaceTracker();
        if (settings.sanitize) {
          const purify2 = setupPurify(settings, schema, namespaceTracker);
          const sanitizeHtmlElement = (body, mimeType) => {
            purify2.sanitize(body, getPurifyConfig(settings, mimeType));
            purify2.removed = [];
            namespaceTracker.reset();
          };
          return {
            sanitizeHtmlElement,
            sanitizeNamespaceElement: mkSanitizeNamespaceElement(settings)
          };
        } else {
          const sanitizeHtmlElement = (body, _mimeType) => {
            const nodeIterator = document.createNodeIterator(body, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT);
            let node;
            while (node = nodeIterator.nextNode()) {
              const currentScope = namespaceTracker.track(node);
              processNode(node, settings, schema, currentScope);
              if (isElement$7(node)) {
                filterAttributes(node, settings, schema, currentScope);
              }
            }
            namespaceTracker.reset();
          };
          const sanitizeNamespaceElement = noop;
          return {
            sanitizeHtmlElement,
            sanitizeNamespaceElement
          };
        }
      };
      const extraBlockLikeElements = ["script", "style", "template", "param", "meta", "title", "link"];
      const makeMap = Tools.makeMap, extend$1 = Tools.extend;
      const transferChildren = (parent2, nativeParent, specialElements, nsSanitizer, decodeComments) => {
        const parentName = parent2.name;
        const isSpecial = parentName in specialElements && parentName !== "title" && parentName !== "textarea" && parentName !== "noscript";
        const childNodes = nativeParent.childNodes;
        for (let ni = 0, nl = childNodes.length; ni < nl; ni++) {
          const nativeChild = childNodes[ni];
          const child2 = new AstNode(nativeChild.nodeName.toLowerCase(), nativeChild.nodeType);
          if (isElement$7(nativeChild)) {
            const attributes = nativeChild.attributes;
            for (let ai = 0, al = attributes.length; ai < al; ai++) {
              const attr = attributes[ai];
              child2.attr(attr.name, attr.value);
            }
            if (isNonHtmlElementRootName(child2.name)) {
              nsSanitizer(nativeChild);
              child2.value = nativeChild.innerHTML;
            }
          } else if (isText$b(nativeChild)) {
            child2.value = nativeChild.data;
            if (isSpecial) {
              child2.raw = true;
            }
          } else if (isComment(nativeChild)) {
            child2.value = decodeComments ? decodeData$1(nativeChild.data) : nativeChild.data;
          } else if (isCData(nativeChild) || isPi(nativeChild)) {
            child2.value = nativeChild.data;
          }
          if (isTemplate(nativeChild)) {
            const content = AstNode.create("#text");
            content.value = nativeChild.innerHTML;
            content.raw = true;
            child2.append(content);
          } else if (!isNonHtmlElementRootName(child2.name)) {
            transferChildren(child2, nativeChild, specialElements, nsSanitizer, decodeComments);
          }
          parent2.append(child2);
        }
      };
      const walkTree = (root, preprocessors, postprocessors) => {
        const traverseOrder = [];
        for (let node = root, lastNode = node; node; lastNode = node, node = node.walk()) {
          const tempNode = node;
          each$e(preprocessors, (preprocess2) => preprocess2(tempNode));
          if (isNullable(tempNode.parent) && tempNode !== root) {
            node = lastNode;
          } else {
            traverseOrder.push(tempNode);
          }
        }
        for (let i = traverseOrder.length - 1; i >= 0; i--) {
          const node = traverseOrder[i];
          each$e(postprocessors, (postprocess) => postprocess(node));
        }
      };
      const whitespaceCleaner = (root, schema, settings, args) => {
        const validate2 = settings.validate;
        const nonEmptyElements = schema.getNonEmptyElements();
        const whitespaceElements = schema.getWhitespaceElements();
        const blockElements = extend$1(makeMap(extraBlockLikeElements), schema.getBlockElements());
        const textRootBlockElements = getTextRootBlockElements(schema);
        const allWhiteSpaceRegExp = /[ \t\r\n]+/g;
        const startWhiteSpaceRegExp = /^[ \t\r\n]+/;
        const endWhiteSpaceRegExp = /[ \t\r\n]+$/;
        const hasWhitespaceParent = (node) => {
          let tempNode = node.parent;
          while (isNonNullable(tempNode)) {
            if (tempNode.name in whitespaceElements) {
              return true;
            } else {
              tempNode = tempNode.parent;
            }
          }
          return false;
        };
        const isTextRootBlockEmpty = (node) => {
          let tempNode = node;
          while (isNonNullable(tempNode)) {
            if (tempNode.name in textRootBlockElements) {
              return isEmpty$2(schema, nonEmptyElements, whitespaceElements, tempNode);
            } else {
              tempNode = tempNode.parent;
            }
          }
          return false;
        };
        const isBlock2 = (node) => node.name in blockElements || isTransparentAstBlock(schema, node) || isNonHtmlElementRootName(node.name) && node.parent === root;
        const isAtEdgeOfBlock = (node, start2) => {
          const neighbour = start2 ? node.prev : node.next;
          if (isNonNullable(neighbour) || isNullable(node.parent)) {
            return false;
          }
          return isBlock2(node.parent) && (node.parent !== root || args.isRootContent === true);
        };
        const preprocess2 = (node) => {
          if (node.type === 3) {
            if (!hasWhitespaceParent(node)) {
              let text2 = node.value ?? "";
              text2 = text2.replace(allWhiteSpaceRegExp, " ");
              if (isLineBreakNode(node.prev, isBlock2) || isAtEdgeOfBlock(node, true)) {
                text2 = text2.replace(startWhiteSpaceRegExp, "");
              }
              if (text2.length === 0) {
                node.remove();
              } else if (text2 === " " && node.prev && node.prev.type === COMMENT && node.next && node.next.type === COMMENT) {
                node.remove();
              } else {
                node.value = text2;
              }
            }
          }
        };
        const postprocess = (node) => {
          if (node.type === 1) {
            const elementRule = schema.getElementRule(node.name);
            if (validate2 && elementRule) {
              const isNodeEmpty = isEmpty$2(schema, nonEmptyElements, whitespaceElements, node);
              if (elementRule.paddInEmptyBlock && isNodeEmpty && isTextRootBlockEmpty(node)) {
                paddEmptyNode(settings, args, isBlock2, node);
              } else if (elementRule.removeEmpty && isNodeEmpty) {
                if (isBlock2(node)) {
                  node.remove();
                } else {
                  node.unwrap();
                }
              } else if (elementRule.paddEmpty && (isNodeEmpty || isPaddedWithNbsp(node))) {
                paddEmptyNode(settings, args, isBlock2, node);
              }
            }
          } else if (node.type === 3) {
            if (!hasWhitespaceParent(node)) {
              let text2 = node.value ?? "";
              if (node.next && isBlock2(node.next) || isAtEdgeOfBlock(node, false)) {
                text2 = text2.replace(endWhiteSpaceRegExp, "");
              }
              if (text2.length === 0) {
                node.remove();
              } else {
                node.value = text2;
              }
            }
          }
        };
        return [preprocess2, postprocess];
      };
      const getRootBlockName = (settings, args) => {
        const name2 = args.forced_root_block ?? settings.forced_root_block;
        if (name2 === false) {
          return "";
        } else if (name2 === true) {
          return "p";
        } else {
          return name2;
        }
      };
      const xhtmlAttribte = ' xmlns="http://www.w3.org/1999/xhtml"';
      const DomParser = (settings = {}, schema = Schema()) => {
        const nodeFilterRegistry = create$8();
        const attributeFilterRegistry = create$8();
        const defaultedSettings = {
          validate: true,
          root_name: "body",
          sanitize: true,
          allow_html_in_comments: false,
          ...settings
        };
        const parser = new DOMParser();
        const sanitizer = getSanitizer(defaultedSettings, schema);
        const parseAndSanitizeWithContext = (html2, rootName, format = "html", useDocumentNotBody = false) => {
          const isxhtml = format === "xhtml";
          const mimeType = isxhtml ? "application/xhtml+xml" : "text/html";
          const isSpecialRoot = has$2(schema.getSpecialElements(), rootName.toLowerCase());
          const content = isSpecialRoot ? `<${rootName}>${html2}</${rootName}>` : html2;
          const makeWrap = () => {
            if (/^[\s]*<head/i.test(html2) || /^[\s]*<html/i.test(html2) || /^[\s]*<!DOCTYPE/i.test(html2)) {
              return `<html${isxhtml ? xhtmlAttribte : ""}>${content}</html>`;
            } else {
              if (isxhtml) {
                return `<html${xhtmlAttribte}><head></head><body>${content}</body></html>`;
              } else {
                return `<body>${content}</body>`;
              }
            }
          };
          const document2 = parser.parseFromString(makeWrap(), mimeType);
          const body = useDocumentNotBody ? document2.documentElement : document2.body;
          sanitizer.sanitizeHtmlElement(body, mimeType);
          return isSpecialRoot ? body.firstChild : body;
        };
        const addNodeFilter2 = nodeFilterRegistry.addFilter;
        const getNodeFilters = nodeFilterRegistry.getFilters;
        const removeNodeFilter = nodeFilterRegistry.removeFilter;
        const addAttributeFilter = attributeFilterRegistry.addFilter;
        const getAttributeFilters = attributeFilterRegistry.getFilters;
        const removeAttributeFilter = attributeFilterRegistry.removeFilter;
        const findInvalidChildren = (node, invalidChildren) => {
          if (isInvalid(schema, node)) {
            invalidChildren.push(node);
          }
        };
        const isWrappableNode = (blockElements, node) => {
          const isInternalElement = isString(node.attr(internalElementAttr));
          const isInlineElement2 = node.type === 1 && (!has$2(blockElements, node.name) && !isTransparentAstBlock(schema, node)) && !isNonHtmlElementRootName(node.name);
          return node.type === 3 || isInlineElement2 && !isInternalElement;
        };
        const addRootBlocks2 = (rootNode, rootBlockName) => {
          const blockElements = extend$1(makeMap(extraBlockLikeElements), schema.getBlockElements());
          const startWhiteSpaceRegExp = /^[ \t\r\n]+/;
          const endWhiteSpaceRegExp = /[ \t\r\n]+$/;
          let node = rootNode.firstChild, rootBlockNode = null;
          const trim2 = (rootBlock) => {
            if (rootBlock) {
              node = rootBlock.firstChild;
              if (node && node.type === 3) {
                node.value = node.value?.replace(startWhiteSpaceRegExp, "");
              }
              node = rootBlock.lastChild;
              if (node && node.type === 3) {
                node.value = node.value?.replace(endWhiteSpaceRegExp, "");
              }
            }
          };
          if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
            return;
          }
          while (node) {
            const next2 = node.next;
            if (isWrappableNode(blockElements, node)) {
              if (!rootBlockNode) {
                rootBlockNode = new AstNode(rootBlockName, 1);
                rootBlockNode.attr(defaultedSettings.forced_root_block_attrs);
                rootNode.insert(rootBlockNode, node);
                rootBlockNode.append(node);
              } else {
                rootBlockNode.append(node);
              }
            } else {
              trim2(rootBlockNode);
              rootBlockNode = null;
            }
            node = next2;
          }
          trim2(rootBlockNode);
        };
        const parse = (html2, args = {}) => {
          const validate2 = defaultedSettings.validate;
          const preferFullDocument = (args.context ?? defaultedSettings.root_name) === "#document";
          const rootName = args.context ?? (preferFullDocument ? "html" : defaultedSettings.root_name);
          const element = parseAndSanitizeWithContext(html2, rootName, args.format, preferFullDocument);
          updateChildren(schema, element);
          const rootNode = new AstNode(rootName, 11);
          transferChildren(rootNode, element, schema.getSpecialElements(), sanitizer.sanitizeNamespaceElement, defaultedSettings.sanitize && defaultedSettings.allow_html_in_comments);
          element.innerHTML = "";
          const [whitespacePre, whitespacePost] = whitespaceCleaner(rootNode, schema, defaultedSettings, args);
          const invalidChildren = [];
          const invalidFinder = validate2 ? (node) => findInvalidChildren(node, invalidChildren) : noop;
          const matches = { nodes: {}, attributes: {} };
          const matchFinder = (node) => matchNode(getNodeFilters(), getAttributeFilters(), node, matches);
          walkTree(rootNode, [whitespacePre, matchFinder], [whitespacePost, invalidFinder]);
          invalidChildren.reverse();
          if (validate2 && invalidChildren.length > 0) {
            if (args.context) {
              args.invalid = true;
            } else {
              cleanInvalidNodes(invalidChildren, schema, rootNode, matchFinder);
            }
          }
          const rootBlockName = getRootBlockName(defaultedSettings, args);
          if (rootBlockName && (rootNode.name === "body" || args.isRootContent)) {
            addRootBlocks2(rootNode, rootBlockName);
          }
          if (!args.invalid) {
            runFilters(matches, args);
          }
          return rootNode;
        };
        const exports2 = {
          schema,
          addAttributeFilter,
          getAttributeFilters,
          removeAttributeFilter,
          addNodeFilter: addNodeFilter2,
          getNodeFilters,
          removeNodeFilter,
          parse
        };
        register$4(exports2, defaultedSettings);
        register$5(exports2, defaultedSettings, schema);
        return exports2;
      };
      const isTreeNode = (content) => content instanceof AstNode;
      const serializeContent = (content) => isTreeNode(content) ? HtmlSerializer({ validate: false }).serialize(content) : content;
      const withSerializedContent = (content, fireEvent2, parserSettings) => {
        const serializedContent = serializeContent(content);
        const eventArgs = fireEvent2(serializedContent);
        if (eventArgs.isDefaultPrevented()) {
          return eventArgs;
        } else if (isTreeNode(content)) {
          if (eventArgs.content !== serializedContent) {
            const rootNode = DomParser({ validate: false, forced_root_block: false, ...parserSettings }).parse(eventArgs.content, { context: content.name });
            return { ...eventArgs, content: rootNode };
          } else {
            return { ...eventArgs, content };
          }
        } else {
          return eventArgs;
        }
      };
      const makeParserSettings = (editor) => ({
        sanitize: shouldSanitizeXss(editor),
        sandbox_iframes: shouldSandboxIframes(editor),
        sandbox_iframes_exclusions: getSandboxIframesExclusions(editor)
      });
      const preProcessGetContent = (editor, args) => {
        if (args.no_events) {
          return Result.value(args);
        } else {
          const eventArgs = fireBeforeGetContent(editor, args);
          if (eventArgs.isDefaultPrevented()) {
            return Result.error(fireGetContent(editor, { content: "", ...eventArgs }).content);
          } else {
            return Result.value(eventArgs);
          }
        }
      };
      const postProcessGetContent = (editor, content, args) => {
        if (args.no_events) {
          return content;
        } else {
          const processedEventArgs = withSerializedContent(content, (content2) => fireGetContent(editor, { ...args, content: content2 }), makeParserSettings(editor));
          return processedEventArgs.content;
        }
      };
      const preProcessSetContent = (editor, args) => {
        if (args.no_events) {
          return Result.value(args);
        } else {
          const processedEventArgs = withSerializedContent(args.content, (content) => fireBeforeSetContent(editor, { ...args, content }), makeParserSettings(editor));
          if (processedEventArgs.isDefaultPrevented()) {
            fireSetContent(editor, processedEventArgs);
            return Result.error(void 0);
          } else {
            return Result.value(processedEventArgs);
          }
        }
      };
      const postProcessSetContent = (editor, content, args) => {
        if (!args.no_events) {
          fireSetContent(editor, { ...args, content });
        }
      };
      const removedOptions = "autoresize_on_init,content_editable_state,padd_empty_with_br,block_elements,boolean_attributes,editor_deselector,editor_selector,elements,file_browser_callback_types,filepicker_validator_handler,force_hex_style_colors,force_p_newlines,gecko_spellcheck,images_dataimg_filter,media_scripts,mode,move_caret_before_on_enter_elements,non_empty_elements,self_closing_elements,short_ended_elements,special,spellchecker_select_languages,spellchecker_whitelist,tab_focus,tabfocus_elements,table_responsive_width,text_block_elements,text_inline_elements,toolbar_drawer,types,validate,whitespace_elements,paste_enable_default_filters,paste_filter_drop,paste_word_valid_elements,paste_retain_style_properties,paste_convert_word_fake_lists,template_cdate_classes,template_mdate_classes,template_selected_content_classes,template_preview_replace_values,template_replace_values,templates,template_cdate_format,template_mdate_format".split(",");
      const deprecatedOptions = ["content_css_cors"];
      const removedPlugins = "bbcode,colorpicker,contextmenu,fullpage,legacyoutput,spellchecker,template,textcolor,rtc".split(",");
      const deprecatedPlugins = [
        {
          name: "export",
          replacedWith: "Export to PDF"
        }
      ];
      const getMatchingOptions = (options, searchingFor) => {
        const settingNames = filter$5(searchingFor, (setting) => has$2(options, setting));
        return sort(settingNames);
      };
      const getRemovedOptions = (options) => {
        const settingNames = getMatchingOptions(options, removedOptions);
        const forcedRootBlock = options.forced_root_block;
        if (forcedRootBlock === false || forcedRootBlock === "") {
          settingNames.push("forced_root_block (false only)");
        }
        return sort(settingNames);
      };
      const getDeprecatedOptions = (options) => getMatchingOptions(options, deprecatedOptions);
      const getMatchingPlugins = (options, searchingFor) => {
        const plugins = Tools.makeMap(options.plugins, " ");
        const hasPlugin = (plugin) => has$2(plugins, plugin);
        const pluginNames = filter$5(searchingFor, hasPlugin);
        return sort(pluginNames);
      };
      const getRemovedPlugins = (options) => getMatchingPlugins(options, removedPlugins);
      const getDeprecatedPlugins = (options) => getMatchingPlugins(options, deprecatedPlugins.map((entry) => entry.name));
      const logRemovedWarnings = (rawOptions, normalizedOptions) => {
        const removedOptions2 = getRemovedOptions(rawOptions);
        const removedPlugins2 = getRemovedPlugins(normalizedOptions);
        const hasRemovedPlugins = removedPlugins2.length > 0;
        const hasRemovedOptions = removedOptions2.length > 0;
        const isLegacyMobileTheme = normalizedOptions.theme === "mobile";
        if (hasRemovedPlugins || hasRemovedOptions || isLegacyMobileTheme) {
          const listJoiner = "\n- ";
          const themesMessage = isLegacyMobileTheme ? `

Themes:${listJoiner}mobile` : "";
          const pluginsMessage = hasRemovedPlugins ? `

Plugins:${listJoiner}${removedPlugins2.join(listJoiner)}` : "";
          const optionsMessage = hasRemovedOptions ? `

Options:${listJoiner}${removedOptions2.join(listJoiner)}` : "";
          console.warn("The following deprecated features are currently enabled and have been removed in TinyMCE 8.0. These features will no longer work and should be removed from the TinyMCE configuration. See https://www.tiny.cloud/docs/tinymce/8/migration-from-7x/ for more information." + themesMessage + pluginsMessage + optionsMessage);
        }
      };
      const getPluginDescription = (name2) => find$2(deprecatedPlugins, (entry) => entry.name === name2).fold(() => name2, (entry) => {
        if (entry.replacedWith) {
          return `${name2}, replaced by ${entry.replacedWith}`;
        } else {
          return name2;
        }
      });
      const logDeprecatedWarnings = (rawOptions, normalizedOptions) => {
        const deprecatedOptions2 = getDeprecatedOptions(rawOptions);
        const deprecatedPlugins2 = getDeprecatedPlugins(normalizedOptions);
        const hasDeprecatedPlugins = deprecatedPlugins2.length > 0;
        const hasDeprecatedOptions = deprecatedOptions2.length > 0;
        if (hasDeprecatedPlugins || hasDeprecatedOptions) {
          const listJoiner = "\n- ";
          const pluginsMessage = hasDeprecatedPlugins ? `

Plugins:${listJoiner}${deprecatedPlugins2.map(getPluginDescription).join(listJoiner)}` : "";
          const optionsMessage = hasDeprecatedOptions ? `

Options:${listJoiner}${deprecatedOptions2.join(listJoiner)}` : "";
          console.warn("The following deprecated features are currently enabled but will be removed soon." + pluginsMessage + optionsMessage);
        }
      };
      const logWarnings = (rawOptions, normalizedOptions) => {
        logRemovedWarnings(rawOptions, normalizedOptions);
        logDeprecatedWarnings(rawOptions, normalizedOptions);
      };
      const deprecatedFeatures = {
        fire: 'The "fire" event api has been deprecated and will be removed in TinyMCE 9. Use "dispatch" instead.',
        selectionSetContent: 'The "editor.selection.setContent" method has been deprecated and will be removed in TinyMCE 9. Use "editor.insertContent" instead.'
      };
      const logFeatureDeprecationWarning = (feature) => {
        console.warn(deprecatedFeatures[feature], new Error().stack);
      };
      const removeEmpty = (text2) => {
        if (text2.dom.length === 0) {
          remove$8(text2);
          return Optional.none();
        } else {
          return Optional.some(text2);
        }
      };
      const walkPastBookmark = (node, start2) => node.filter((elm) => BookmarkManager.isBookmarkNode(elm.dom)).bind(start2 ? nextSibling : prevSibling);
      const merge = (outer, inner, rng, start2, schema) => {
        const outerElm = outer.dom;
        const innerElm = inner.dom;
        const oldLength = start2 ? outerElm.length : innerElm.length;
        if (start2) {
          mergeTextNodes(outerElm, innerElm, schema, false, !start2);
          rng.setStart(innerElm, oldLength);
        } else {
          mergeTextNodes(innerElm, outerElm, schema, false, !start2);
          rng.setEnd(innerElm, oldLength);
        }
      };
      const normalizeTextIfRequired = (inner, start2, schema) => {
        parent(inner).each((root) => {
          const text2 = inner.dom;
          if (start2 && needsToBeNbspLeft(root, CaretPosition(text2, 0), schema)) {
            normalizeWhitespaceAfter(text2, 0, schema);
          } else if (!start2 && needsToBeNbspRight(root, CaretPosition(text2, text2.length), schema)) {
            normalizeWhitespaceBefore(text2, text2.length, schema);
          }
        });
      };
      const mergeAndNormalizeText = (outerNode, innerNode, rng, start2, schema) => {
        outerNode.bind((outer) => {
          const normalizer = start2 ? normalizeWhitespaceBefore : normalizeWhitespaceAfter;
          normalizer(outer.dom, start2 ? outer.dom.length : 0, schema);
          return innerNode.filter(isText$c).map((inner) => merge(outer, inner, rng, start2, schema));
        }).orThunk(() => {
          const innerTextNode = walkPastBookmark(innerNode, start2).or(innerNode).filter(isText$c);
          return innerTextNode.map((inner) => normalizeTextIfRequired(inner, start2, schema));
        });
      };
      const rngSetContent = (rng, fragment, schema) => {
        const firstChild2 = Optional.from(fragment.firstChild).map(SugarElement.fromDom);
        const lastChild2 = Optional.from(fragment.lastChild).map(SugarElement.fromDom);
        rng.deleteContents();
        rng.insertNode(fragment);
        const prevText = firstChild2.bind(prevSibling).filter(isText$c).bind(removeEmpty);
        const nextText = lastChild2.bind(nextSibling).filter(isText$c).bind(removeEmpty);
        mergeAndNormalizeText(prevText, firstChild2, rng, true, schema);
        mergeAndNormalizeText(nextText, lastChild2, rng, false, schema);
        rng.collapse(false);
      };
      const setupArgs$3 = (args, content) => ({
        format: "html",
        ...args,
        set: true,
        selection: true,
        content
      });
      const cleanContent = (editor, args) => {
        if (args.format !== "raw") {
          const rng = editor.selection.getRng();
          const contextBlock = editor.dom.getParent(rng.commonAncestorContainer, editor.dom.isBlock);
          const contextArgs = contextBlock ? { context: contextBlock.nodeName.toLowerCase() } : {};
          const node = editor.parser.parse(args.content, { forced_root_block: false, ...contextArgs, ...args });
          return HtmlSerializer({ validate: false }, editor.schema).serialize(node);
        } else {
          return args.content;
        }
      };
      const setContentInternal$1 = (editor, content, args = {}) => {
        const defaultedArgs = setupArgs$3(args, content);
        preProcessSetContent(editor, defaultedArgs).each((updatedArgs) => {
          const cleanedContent = cleanContent(editor, updatedArgs);
          const rng = editor.selection.getRng();
          rngSetContent(rng, rng.createContextualFragment(cleanedContent), editor.schema);
          editor.selection.setRng(rng);
          scrollRangeIntoView(editor, rng);
          postProcessSetContent(editor, cleanedContent, updatedArgs);
        });
      };
      const setContentExternal = (editor, content, args = {}) => {
        logFeatureDeprecationWarning("selectionSetContent");
        setContentInternal$1(editor, content, args);
      };
      const hasOnlyOneChild$1 = (node) => {
        return isNonNullable(node.firstChild) && node.firstChild === node.lastChild;
      };
      const isPaddingNode = (node) => {
        return node.name === "br" || node.value === nbsp;
      };
      const isPaddedEmptyBlock = (schema, node) => {
        const blockElements = schema.getBlockElements();
        return blockElements[node.name] && hasOnlyOneChild$1(node) && isPaddingNode(node.firstChild);
      };
      const isEmptyFragmentElement = (schema, node) => {
        const nonEmptyElements = schema.getNonEmptyElements();
        return isNonNullable(node) && (node.isEmpty(nonEmptyElements) || isPaddedEmptyBlock(schema, node));
      };
      const isListFragment = (schema, fragment) => {
        let firstChild2 = fragment.firstChild;
        let lastChild2 = fragment.lastChild;
        if (firstChild2 && firstChild2.name === "meta") {
          firstChild2 = firstChild2.next;
        }
        if (lastChild2 && lastChild2.attr("id") === "mce_marker") {
          lastChild2 = lastChild2.prev;
        }
        if (isEmptyFragmentElement(schema, lastChild2)) {
          lastChild2 = lastChild2?.prev;
        }
        if (!firstChild2 || firstChild2 !== lastChild2) {
          return false;
        }
        return firstChild2.name === "ul" || firstChild2.name === "ol";
      };
      const cleanupDomFragment = (domFragment) => {
        const firstChild2 = domFragment.firstChild;
        const lastChild2 = domFragment.lastChild;
        if (firstChild2 && firstChild2.nodeName === "META") {
          firstChild2.parentNode?.removeChild(firstChild2);
        }
        if (lastChild2 && lastChild2.id === "mce_marker") {
          lastChild2.parentNode?.removeChild(lastChild2);
        }
        return domFragment;
      };
      const toDomFragment = (dom2, serializer, fragment) => {
        const html2 = serializer.serialize(fragment);
        const domFragment = dom2.createFragment(html2);
        return cleanupDomFragment(domFragment);
      };
      const listItems = (elm) => {
        return filter$5(elm?.childNodes ?? [], (child2) => {
          return child2.nodeName === "LI";
        });
      };
      const isPadding = (node) => {
        return node.data === nbsp || isBr$7(node);
      };
      const isListItemPadded = (node) => {
        return isNonNullable(node?.firstChild) && node.firstChild === node.lastChild && isPadding(node.firstChild);
      };
      const isEmptyOrPadded = (elm) => {
        return !elm.firstChild || isListItemPadded(elm);
      };
      const trimListItems = (elms) => {
        return elms.length > 0 && isEmptyOrPadded(elms[elms.length - 1]) ? elms.slice(0, -1) : elms;
      };
      const getParentLi = (dom2, node) => {
        const parentBlock = dom2.getParent(node, dom2.isBlock);
        return parentBlock && parentBlock.nodeName === "LI" ? parentBlock : null;
      };
      const isParentBlockLi = (dom2, node) => {
        return !!getParentLi(dom2, node);
      };
      const getSplit = (parentNode, rng) => {
        const beforeRng = rng.cloneRange();
        const afterRng = rng.cloneRange();
        beforeRng.setStartBefore(parentNode);
        afterRng.setEndAfter(parentNode);
        return [
          beforeRng.cloneContents(),
          afterRng.cloneContents()
        ];
      };
      const findFirstIn = (node, rootNode) => {
        const caretPos = CaretPosition.before(node);
        const caretWalker = CaretWalker(rootNode);
        const newCaretPos = caretWalker.next(caretPos);
        return newCaretPos ? newCaretPos.toRange() : null;
      };
      const findLastOf = (node, rootNode) => {
        const caretPos = CaretPosition.after(node);
        const caretWalker = CaretWalker(rootNode);
        const newCaretPos = caretWalker.prev(caretPos);
        return newCaretPos ? newCaretPos.toRange() : null;
      };
      const insertMiddle = (target, elms, rootNode, rng) => {
        const parts = getSplit(target, rng);
        const parentElm = target.parentNode;
        if (parentElm) {
          parentElm.insertBefore(parts[0], target);
          Tools.each(elms, (li) => {
            parentElm.insertBefore(li, target);
          });
          parentElm.insertBefore(parts[1], target);
          parentElm.removeChild(target);
        }
        return findLastOf(elms[elms.length - 1], rootNode);
      };
      const insertBefore$2 = (target, elms, rootNode) => {
        const parentElm = target.parentNode;
        if (parentElm) {
          Tools.each(elms, (elm) => {
            parentElm.insertBefore(elm, target);
          });
        }
        return findFirstIn(target, rootNode);
      };
      const insertAfter$2 = (target, elms, rootNode, dom2) => {
        dom2.insertAfter(elms.reverse(), target);
        return findLastOf(elms[0], rootNode);
      };
      const insertAtCaret$1 = (serializer, dom2, rng, fragment) => {
        const domFragment = toDomFragment(dom2, serializer, fragment);
        const liTarget = getParentLi(dom2, rng.startContainer);
        const liElms = trimListItems(listItems(domFragment.firstChild));
        const BEGINNING = 1, END = 2;
        const rootNode = dom2.getRoot();
        const isAt = (location) => {
          const caretPos = CaretPosition.fromRangeStart(rng);
          const caretWalker = CaretWalker(dom2.getRoot());
          const newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos);
          const newPosNode = newPos?.getNode();
          return newPosNode ? getParentLi(dom2, newPosNode) !== liTarget : true;
        };
        if (!liTarget) {
          return null;
        } else if (isAt(BEGINNING)) {
          return insertBefore$2(liTarget, liElms, rootNode);
        } else if (isAt(END)) {
          return insertAfter$2(liTarget, liElms, rootNode, dom2);
        } else {
          return insertMiddle(liTarget, liElms, rootNode, rng);
        }
      };
      const mergeableWrappedElements = ["pre"];
      const shouldPasteContentOnly = (dom2, fragment, parentNode, root) => {
        const firstNode = fragment.firstChild;
        const lastNode = fragment.lastChild;
        const last2 = lastNode.attr("data-mce-type") === "bookmark" ? lastNode.prev : lastNode;
        const isPastingSingleElement = firstNode === last2;
        const isWrappedElement = contains$2(mergeableWrappedElements, firstNode.name);
        if (isPastingSingleElement && isWrappedElement) {
          const isContentEditable2 = firstNode.attr("contenteditable") !== "false";
          const isPastingInTheSameBlockTag = dom2.getParent(parentNode, dom2.isBlock)?.nodeName.toLowerCase() === firstNode.name;
          const isPastingInContentEditable = Optional.from(getContentEditableRoot$1(root, parentNode)).forall(isContentEditableTrue$3);
          return isContentEditable2 && isPastingInTheSameBlockTag && isPastingInContentEditable;
        } else {
          return false;
        }
      };
      const isTableCell = isTableCell$3;
      const isTableCellContentSelected = (dom2, rng, cell2) => {
        if (isNonNullable(cell2)) {
          const endCell = dom2.getParent(rng.endContainer, isTableCell);
          return cell2 === endCell && hasAllContentsSelected(SugarElement.fromDom(cell2), rng);
        } else {
          return false;
        }
      };
      const isEditableEmptyBlock = (dom2, node) => {
        if (dom2.isBlock(node) && dom2.isEditable(node)) {
          const childNodes = node.childNodes;
          return childNodes.length === 1 && isBr$7(childNodes[0]) || childNodes.length === 0;
        } else {
          return false;
        }
      };
      const validInsertion = (editor, value2, parentNode) => {
        if (parentNode.getAttribute("data-mce-bogus") === "all") {
          parentNode.parentNode?.insertBefore(editor.dom.createFragment(value2), parentNode);
        } else {
          if (isEditableEmptyBlock(editor.dom, parentNode)) {
            editor.dom.setHTML(parentNode, value2);
          } else {
            setContentInternal$1(editor, value2, { no_events: true });
          }
        }
      };
      const trimBrsFromTableCell = (dom2, elm, schema) => {
        Optional.from(dom2.getParent(elm, "td,th")).map(SugarElement.fromDom).each((el) => trimBlockTrailingBr(el, schema));
      };
      const reduceInlineTextElements = (editor, merge2) => {
        const textInlineElements = editor.schema.getTextInlineElements();
        const dom2 = editor.dom;
        if (merge2) {
          const root = editor.getBody();
          const elementUtils = ElementUtils(editor);
          const fragmentSelector = "*[data-mce-fragment]";
          const fragments = dom2.select(fragmentSelector);
          Tools.each(fragments, (node) => {
            const isInline2 = (currentNode) => isNonNullable(textInlineElements[currentNode.nodeName.toLowerCase()]);
            const hasOneChild = (currentNode) => currentNode.childNodes.length === 1;
            const hasNoNonInheritableStyles = (currentNode) => !(hasNonInheritableStyles(dom2, currentNode) || hasConditionalNonInheritableStyles(dom2, currentNode));
            if (hasNoNonInheritableStyles(node) && isInline2(node) && hasOneChild(node)) {
              const styles = getStyleProps(dom2, node);
              const isOverridden = (oldStyles, newStyles) => forall(oldStyles, (style) => contains$2(newStyles, style));
              const overriddenByAllChildren = (childNode) => hasOneChild(node) && dom2.is(childNode, fragmentSelector) && isInline2(childNode) && (childNode.nodeName === node.nodeName && isOverridden(styles, getStyleProps(dom2, childNode)) || overriddenByAllChildren(childNode.children[0]));
              const identicalToParent = (parentNode) => isNonNullable(parentNode) && parentNode !== root && (elementUtils.compare(node, parentNode) || identicalToParent(parentNode.parentElement));
              const conflictWithInsertedParent = (parentNode) => isNonNullable(parentNode) && parentNode !== root && dom2.is(parentNode, fragmentSelector) && (hasStyleConflict(dom2, node, parentNode) || conflictWithInsertedParent(parentNode.parentElement));
              if (overriddenByAllChildren(node.children[0]) || identicalToParent(node.parentElement) && !conflictWithInsertedParent(node.parentElement)) {
                dom2.remove(node, true);
              }
            }
          });
          normalizeElements(editor, fromDom$1(fragments));
        }
      };
      const markFragmentElements = (fragment) => {
        let node = fragment;
        while (node = node.walk()) {
          if (node.type === 1) {
            node.attr("data-mce-fragment", "1");
          }
        }
      };
      const unmarkFragmentElements = (elm) => {
        Tools.each(elm.getElementsByTagName("*"), (elm2) => {
          elm2.removeAttribute("data-mce-fragment");
        });
      };
      const isPartOfFragment = (node) => {
        return !!node.getAttribute("data-mce-fragment");
      };
      const canHaveChildren = (editor, node) => {
        return isNonNullable(node) && !editor.schema.getVoidElements()[node.nodeName];
      };
      const moveSelectionToMarker = (editor, marker) => {
        let nextRng;
        const dom2 = editor.dom;
        const selection = editor.selection;
        if (!marker) {
          return;
        }
        selection.scrollIntoView(marker);
        const parentEditableElm = getContentEditableRoot$1(editor.getBody(), marker);
        if (parentEditableElm && dom2.getContentEditable(parentEditableElm) === "false") {
          dom2.remove(marker);
          selection.select(parentEditableElm);
          return;
        }
        let rng = dom2.createRng();
        const node = marker.previousSibling;
        if (isText$b(node)) {
          rng.setStart(node, node.nodeValue?.length ?? 0);
          const node2 = marker.nextSibling;
          if (isText$b(node2)) {
            node.appendData(node2.data);
            node2.parentNode?.removeChild(node2);
          }
        } else {
          rng.setStartBefore(marker);
          rng.setEndBefore(marker);
        }
        const findNextCaretRng = (rng2) => {
          let caretPos = CaretPosition.fromRangeStart(rng2);
          const caretWalker = CaretWalker(editor.getBody());
          caretPos = caretWalker.next(caretPos);
          return caretPos?.toRange();
        };
        const parentBlock = dom2.getParent(marker, dom2.isBlock);
        dom2.remove(marker);
        if (parentBlock && dom2.isEmpty(parentBlock)) {
          const isCell = isTableCell(parentBlock);
          empty(SugarElement.fromDom(parentBlock));
          rng.setStart(parentBlock, 0);
          rng.setEnd(parentBlock, 0);
          if (!isCell && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) {
            rng = nextRng;
            dom2.remove(parentBlock);
          } else {
            dom2.add(parentBlock, dom2.create("br", isCell ? {} : { "data-mce-bogus": "1" }));
          }
        }
        selection.setRng(rng);
      };
      const deleteSelectedContent = (editor) => {
        const dom2 = editor.dom;
        const rng = normalize(editor.selection.getRng());
        editor.selection.setRng(rng);
        const startCell = dom2.getParent(rng.startContainer, isTableCell);
        if (isTableCellContentSelected(dom2, rng, startCell)) {
          deleteCellContents(editor, rng, SugarElement.fromDom(startCell));
        } else if (isSelectionOverWholeAnchor(rng) || isSelectionOverWholeTextNode(rng)) {
          rng.deleteContents();
        } else {
          editor.getDoc().execCommand("Delete", false);
        }
      };
      const findMarkerNode = (scope) => {
        for (let markerNode = scope; markerNode; markerNode = markerNode.walk()) {
          if (markerNode.attr("id") === "mce_marker") {
            return Optional.some(markerNode);
          }
        }
        return Optional.none();
      };
      const notHeadingsInSummary = (dom2, node, fragment) => {
        return exists(fragment.children(), isHeading) && dom2.getParent(node, dom2.isBlock)?.nodeName === "SUMMARY";
      };
      const insertHtmlAtCaret = (editor, value2, details) => {
        const selection = editor.selection;
        const dom2 = editor.dom;
        const parser = editor.parser;
        const merge2 = details.merge;
        const serializer = HtmlSerializer({
          validate: true
        }, editor.schema);
        const bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;</span>';
        if (!details.preserve_zwsp) {
          value2 = trim$2(value2);
        }
        if (value2.indexOf("{$caret}") === -1) {
          value2 += "{$caret}";
        }
        value2 = value2.replace(/\{\$caret\}/, bookmarkHtml);
        let rng = selection.getRng();
        const caretElement = rng.startContainer;
        const body = editor.getBody();
        if (caretElement === body && selection.isCollapsed()) {
          if (dom2.isBlock(body.firstChild) && canHaveChildren(editor, body.firstChild) && dom2.isEmpty(body.firstChild)) {
            rng = dom2.createRng();
            rng.setStart(body.firstChild, 0);
            rng.setEnd(body.firstChild, 0);
            selection.setRng(rng);
          }
        }
        if (!selection.isCollapsed()) {
          deleteSelectedContent(editor);
        }
        const parentNode = selection.getNode();
        const parserArgs = { context: parentNode.nodeName.toLowerCase(), data: details.data, insert: true };
        const fragment = parser.parse(value2, parserArgs);
        if (details.paste === true && isListFragment(editor.schema, fragment) && isParentBlockLi(dom2, parentNode)) {
          rng = insertAtCaret$1(serializer, dom2, selection.getRng(), fragment);
          if (rng) {
            selection.setRng(rng);
          }
          return value2;
        }
        if (details.paste === true && shouldPasteContentOnly(dom2, fragment, parentNode, editor.getBody())) {
          fragment.firstChild?.unwrap();
        }
        markFragmentElements(fragment);
        let node = fragment.lastChild;
        if (node && node.attr("id") === "mce_marker") {
          const marker = node;
          for (node = node.prev; node; node = node.walk(true)) {
            if (node.name === "table") {
              break;
            }
            if (node.type === 3 || !dom2.isBlock(node.name)) {
              if (node.parent && editor.schema.isValidChild(node.parent.name, "span")) {
                node.parent.insert(marker, node, node.name === "br");
              }
              break;
            }
          }
        }
        editor._selectionOverrides.showBlockCaretContainer(parentNode);
        if (!parserArgs.invalid && !notHeadingsInSummary(dom2, parentNode, fragment)) {
          value2 = serializer.serialize(fragment);
          validInsertion(editor, value2, parentNode);
        } else {
          setContentInternal$1(editor, bookmarkHtml);
          let parentNode2 = selection.getNode();
          let tempNode;
          const rootNode = editor.getBody();
          if (isDocument$1(parentNode2)) {
            parentNode2 = tempNode = rootNode;
          } else {
            tempNode = parentNode2;
          }
          while (tempNode && tempNode !== rootNode) {
            parentNode2 = tempNode;
            tempNode = tempNode.parentNode;
          }
          value2 = parentNode2 === rootNode ? rootNode.innerHTML : dom2.getOuterHTML(parentNode2);
          const root = parser.parse(value2);
          const markerNode = findMarkerNode(root);
          const editingHost = markerNode.bind(findClosestEditingHost).getOr(root);
          markerNode.each((marker) => marker.replace(fragment));
          const fragmentNodes = getAllDescendants(fragment);
          fragment.unwrap();
          const invalidChildren = filter$5(fragmentNodes, (node2) => isInvalid(editor.schema, node2));
          cleanInvalidNodes(invalidChildren, editor.schema, editingHost);
          filter$1(parser.getNodeFilters(), parser.getAttributeFilters(), root);
          value2 = serializer.serialize(root);
          if (parentNode2 === rootNode) {
            dom2.setHTML(rootNode, value2);
          } else {
            dom2.setOuterHTML(parentNode2, value2);
          }
        }
        reduceInlineTextElements(editor, merge2);
        moveSelectionToMarker(editor, dom2.get("mce_marker"));
        unmarkFragmentElements(editor.getBody());
        trimBrsFromTableCell(dom2, selection.getStart(), editor.schema);
        updateCaret(editor.schema, editor.getBody(), selection.getStart());
        return value2;
      };
      const moveSelection = (editor) => {
        if (hasFocus(editor)) {
          firstPositionIn(editor.getBody()).each((pos) => {
            const node = pos.getNode();
            const caretPos = isTable$2(node) ? firstPositionIn(node).getOr(pos) : pos;
            editor.selection.setRng(caretPos.toRange());
          });
        }
      };
      const setEditorHtml = (editor, html2, noSelection) => {
        editor.dom.setHTML(editor.getBody(), html2);
        if (noSelection !== true) {
          moveSelection(editor);
        }
      };
      const setContentString = (editor, body, content, args) => {
        content = trim$2(content);
        if (content.length === 0 || /^\s+$/.test(content)) {
          const padd = '<br data-mce-bogus="1">';
          if (body.nodeName === "TABLE") {
            content = "<tr><td>" + padd + "</td></tr>";
          } else if (/^(UL|OL)$/.test(body.nodeName)) {
            content = "<li>" + padd + "</li>";
          }
          const forcedRootBlockName = getForcedRootBlock(editor);
          if (editor.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
            content = padd;
            content = editor.dom.createHTML(forcedRootBlockName, getForcedRootBlockAttrs(editor), content);
          } else if (!content) {
            content = padd;
          }
          setEditorHtml(editor, content, args.no_selection);
          return { content, html: content };
        } else {
          if (args.format !== "raw") {
            content = HtmlSerializer({ validate: false }, editor.schema).serialize(editor.parser.parse(content, { isRootContent: true, insert: true }));
          }
          const trimmedHtml = isWsPreserveElement(SugarElement.fromDom(body)) ? content : Tools.trim(content);
          setEditorHtml(editor, trimmedHtml, args.no_selection);
          return { content: trimmedHtml, html: trimmedHtml };
        }
      };
      const setContentTree = (editor, body, content, args) => {
        filter$1(editor.parser.getNodeFilters(), editor.parser.getAttributeFilters(), content);
        const html2 = HtmlSerializer({ validate: false }, editor.schema).serialize(content);
        const trimmedHtml = trim$2(isWsPreserveElement(SugarElement.fromDom(body)) ? html2 : Tools.trim(html2));
        setEditorHtml(editor, trimmedHtml, args.no_selection);
        return { content, html: trimmedHtml };
      };
      const setContentInternal = (editor, content, args) => {
        return Optional.from(editor.getBody()).map((body) => {
          if (isTreeNode(content)) {
            return setContentTree(editor, body, content, args);
          } else {
            return setContentString(editor, body, content, args);
          }
        }).getOr({ content, html: isTreeNode(args.content) ? "" : args.content });
      };
      const postProcessHooks = {};
      const isPre = matchNodeNames$1(["pre"]);
      const addPostProcessHook = (name2, hook) => {
        const hooks = postProcessHooks[name2];
        if (!hooks) {
          postProcessHooks[name2] = [];
        }
        postProcessHooks[name2].push(hook);
      };
      const postProcess$1 = (name2, editor) => {
        if (has$2(postProcessHooks, name2)) {
          each$e(postProcessHooks[name2], (hook) => {
            hook(editor);
          });
        }
      };
      addPostProcessHook("pre", (editor) => {
        const rng = editor.selection.getRng();
        const hasPreSibling = (blocks) => (pre) => {
          const prev2 = pre.previousSibling;
          return isPre(prev2) && contains$2(blocks, prev2);
        };
        const joinPre = (pre1, pre2) => {
          const sPre2 = SugarElement.fromDom(pre2);
          const doc = documentOrOwner(sPre2).dom;
          remove$8(sPre2);
          append(SugarElement.fromDom(pre1), [
            SugarElement.fromTag("br", doc),
            SugarElement.fromTag("br", doc),
            ...children$1(sPre2)
          ]);
        };
        if (!rng.collapsed) {
          const blocks = editor.selection.getSelectedBlocks();
          const preBlocks = filter$5(filter$5(blocks, isPre), hasPreSibling(blocks));
          each$e(preBlocks, (pre) => {
            joinPre(pre.previousSibling, pre);
          });
        }
      });
      const each$5 = Tools.each;
      const mergeTextDecorationsAndColor = (dom2, format, vars, node) => {
        const processTextDecorationsAndColor = (n) => {
          if (isHTMLElement(n) && isElement$7(n.parentNode) && dom2.isEditable(n)) {
            const parentTextDecoration = getTextDecoration(dom2, n.parentNode);
            if (dom2.getStyle(n, "color") && parentTextDecoration) {
              dom2.setStyle(n, "text-decoration", parentTextDecoration);
            } else if (dom2.getStyle(n, "text-decoration") === parentTextDecoration) {
              dom2.setStyle(n, "text-decoration", null);
            }
          }
        };
        if (format.styles && (format.styles.color || format.styles.textDecoration)) {
          Tools.walk(node, processTextDecorationsAndColor, "childNodes");
          processTextDecorationsAndColor(node);
        }
      };
      const mergeBackgroundColorAndFontSize = (dom2, format, vars, node) => {
        if (format.styles && format.styles.backgroundColor) {
          const hasFontSize = hasStyle(dom2, "fontSize");
          processChildElements(node, (elm) => hasFontSize(elm) && dom2.isEditable(elm), applyStyle(dom2, "backgroundColor", replaceVars(format.styles.backgroundColor, vars)));
        }
      };
      const mergeSubSup = (dom2, format, vars, node) => {
        if (isInlineFormat(format) && (format.inline === "sub" || format.inline === "sup")) {
          const hasFontSize = hasStyle(dom2, "fontSize");
          processChildElements(node, (elm) => hasFontSize(elm) && dom2.isEditable(elm), applyStyle(dom2, "fontSize", ""));
          const inverseTagDescendants = filter$5(dom2.select(format.inline === "sup" ? "sub" : "sup", node), dom2.isEditable);
          dom2.remove(inverseTagDescendants, true);
        }
      };
      const mergeWithChildren = (editor, formatList, vars, node) => {
        each$5(formatList, (format) => {
          if (isInlineFormat(format)) {
            each$5(editor.dom.select(format.inline, node), (child2) => {
              if (isElementNode(child2)) {
                removeNodeFormat(editor, format, vars, child2, format.exact ? child2 : null);
              }
            });
          }
          clearChildStyles(editor.dom, format, node);
        });
      };
      const mergeWithParents = (editor, format, name2, vars, node) => {
        const parentNode = node.parentNode;
        if (matchNode$1(editor, parentNode, name2, vars)) {
          if (removeNodeFormat(editor, format, vars, node)) {
            return;
          }
        }
        if (format.merge_with_parents && parentNode) {
          editor.dom.getParent(parentNode, (parent2) => {
            if (matchNode$1(editor, parent2, name2, vars)) {
              removeNodeFormat(editor, format, vars, node);
              return true;
            } else {
              return false;
            }
          });
        }
      };
      const each$4 = Tools.each;
      const canFormatBR = (editor, format, node, parentName) => {
        if (canFormatEmptyLines(editor) && isInlineFormat(format) && node.parentNode) {
          const validBRParentElements = getTextRootBlockElements(editor.schema);
          const hasCaretNodeSibling = sibling(SugarElement.fromDom(node), (sibling2) => isCaretNode(sibling2.dom));
          return hasNonNullableKey(validBRParentElements, parentName) && isEmptyNode(editor.schema, node.parentNode, { skipBogus: false, includeZwsp: true }) && !hasCaretNodeSibling;
        } else {
          return false;
        }
      };
      const applyFormatAction = (ed, name2, vars, node) => {
        const formatList = ed.formatter.get(name2);
        const format = formatList[0];
        const isCollapsed = !node && ed.selection.isCollapsed();
        const dom2 = ed.dom;
        const selection = ed.selection;
        const applyNodeStyle = (formatList2, node2) => {
          let found = false;
          each$4(formatList2, (format2) => {
            if (!isSelectorFormat(format2)) {
              return false;
            }
            if (dom2.getContentEditable(node2) === "false" && !format2.ceFalseOverride) {
              return true;
            }
            if (isNonNullable(format2.collapsed) && format2.collapsed !== isCollapsed) {
              return true;
            }
            if (dom2.is(node2, format2.selector) && !isCaretNode(node2)) {
              setElementFormat(ed, node2, format2, vars, node2);
              found = true;
              return false;
            }
            return true;
          });
          return found;
        };
        const createWrapElement = (wrapName) => {
          if (isString(wrapName)) {
            const wrapElm = dom2.create(wrapName);
            setElementFormat(ed, wrapElm, format, vars, node);
            return wrapElm;
          } else {
            return null;
          }
        };
        const applyRngStyle = (dom3, rng, nodeSpecific) => {
          const newWrappers = [];
          let contentEditable = true;
          const wrapName = format.inline || format.block;
          const wrapElm = createWrapElement(wrapName);
          const isMatchingWrappingBlock = (node2) => isWrappingBlockFormat(format) && matchNode$1(ed, node2, name2, vars);
          const canRenameBlock = (node2, parentName, isEditableDescendant) => {
            const isValidBlockFormatForNode = isNonWrappingBlockFormat(format) && isTextBlock$2(ed.schema, node2) && isValid(ed, parentName, wrapName);
            return isEditableDescendant && isValidBlockFormatForNode;
          };
          const canWrapNode = (node2, parentName, isEditableDescendant, isWrappableNoneditableElm) => {
            const nodeName = node2.nodeName.toLowerCase();
            const isValidWrapNode = isValid(ed, wrapName, nodeName) && isValid(ed, parentName, wrapName);
            const isZwsp$12 = !nodeSpecific && isText$b(node2) && isZwsp(node2.data);
            const isCaret = isCaretNode(node2);
            const isCorrectFormatForNode = !isInlineFormat(format) || !dom3.isBlock(node2);
            return (isEditableDescendant || isWrappableNoneditableElm) && isValidWrapNode && !isZwsp$12 && !isCaret && isCorrectFormatForNode;
          };
          walk$3(dom3, rng, (nodes) => {
            let currentWrapElm;
            const process2 = (node2) => {
              let hasContentEditableState2 = false;
              let lastContentEditable = contentEditable;
              let isWrappableNoneditableElm = false;
              const parentNode = node2.parentNode;
              const parentName = parentNode.nodeName.toLowerCase();
              const contentEditableValue = dom3.getContentEditable(node2);
              if (isNonNullable(contentEditableValue)) {
                lastContentEditable = contentEditable;
                contentEditable = contentEditableValue === "true";
                hasContentEditableState2 = true;
                isWrappableNoneditableElm = isWrappableNoneditable(ed, node2);
              }
              const isEditableDescendant = contentEditable && !hasContentEditableState2;
              if (isBr$7(node2) && !canFormatBR(ed, format, node2, parentName)) {
                currentWrapElm = null;
                if (isBlockFormat(format)) {
                  dom3.remove(node2);
                }
                return;
              }
              if (isMatchingWrappingBlock(node2)) {
                currentWrapElm = null;
                return;
              }
              if (canRenameBlock(node2, parentName, isEditableDescendant)) {
                const elm = dom3.rename(node2, wrapName);
                setElementFormat(ed, elm, format, vars, node2);
                newWrappers.push(elm);
                currentWrapElm = null;
                return;
              }
              if (isSelectorFormat(format)) {
                let found = applyNodeStyle(formatList, node2);
                if (!found && isNonNullable(parentNode) && shouldExpandToSelector(format)) {
                  found = applyNodeStyle(formatList, parentNode);
                }
                if (!isInlineFormat(format) || found) {
                  currentWrapElm = null;
                  return;
                }
              }
              if (isNonNullable(wrapElm) && canWrapNode(node2, parentName, isEditableDescendant, isWrappableNoneditableElm)) {
                if (!currentWrapElm) {
                  currentWrapElm = dom3.clone(wrapElm, false);
                  parentNode.insertBefore(currentWrapElm, node2);
                  newWrappers.push(currentWrapElm);
                }
                if (isWrappableNoneditableElm && hasContentEditableState2) {
                  contentEditable = lastContentEditable;
                }
                currentWrapElm.appendChild(node2);
              } else {
                currentWrapElm = null;
                each$e(from(node2.childNodes), process2);
                if (hasContentEditableState2) {
                  contentEditable = lastContentEditable;
                }
                currentWrapElm = null;
              }
            };
            each$e(nodes, process2);
          });
          if (format.links === true) {
            each$e(newWrappers, (wrapper) => {
              const process2 = (target) => {
                if (target.nodeName === "A") {
                  setElementFormat(ed, target, format, vars, node);
                }
                each$e(from(target.childNodes), process2);
              };
              process2(wrapper);
            });
          }
          normalizeFontSizeElementsAfterApply(ed, name2, fromDom$1(newWrappers));
          each$e(newWrappers, (node2) => {
            const getChildCount = (node3) => {
              let count2 = 0;
              each$e(node3.childNodes, (node4) => {
                if (!isEmptyTextNode$1(node4) && !isBookmarkNode$1(node4)) {
                  count2++;
                }
              });
              return count2;
            };
            const mergeStyles = (node3) => {
              const childElement = find$2(node3.childNodes, isElementNode$1).filter((child2) => dom3.getContentEditable(child2) !== "false" && matchName(dom3, child2, format));
              return childElement.map((child2) => {
                const clone2 = dom3.clone(child2, false);
                setElementFormat(ed, clone2, format, vars, node3);
                dom3.replace(clone2, node3, true);
                dom3.remove(child2, true);
                return clone2;
              }).getOr(node3);
            };
            const childCount = getChildCount(node2);
            if ((newWrappers.length > 1 || !dom3.isBlock(node2)) && childCount === 0) {
              dom3.remove(node2, true);
              return;
            }
            if (isInlineFormat(format) || isBlockFormat(format) && format.wrapper) {
              if (!format.exact && childCount === 1) {
                node2 = mergeStyles(node2);
              }
              mergeWithChildren(ed, formatList, vars, node2);
              mergeWithParents(ed, format, name2, vars, node2);
              mergeBackgroundColorAndFontSize(dom3, format, vars, node2);
              mergeTextDecorationsAndColor(dom3, format, vars, node2);
              mergeSubSup(dom3, format, vars, node2);
              mergeSiblings(ed, format, vars, node2);
            }
          });
        };
        const targetNode = isNode(node) ? node : selection.getNode();
        if (dom2.getContentEditable(targetNode) === "false" && !isWrappableNoneditable(ed, targetNode)) {
          node = targetNode;
          applyNodeStyle(formatList, node);
          fireFormatApply(ed, name2, node, vars);
          return;
        }
        if (format) {
          if (node) {
            if (isNode(node)) {
              if (!applyNodeStyle(formatList, node)) {
                const rng = dom2.createRng();
                rng.setStartBefore(node);
                rng.setEndAfter(node);
                applyRngStyle(dom2, expandRng(dom2, rng, formatList), true);
              }
            } else {
              applyRngStyle(dom2, node, true);
            }
          } else {
            if (!isCollapsed || !isInlineFormat(format) || getCellsFromEditor(ed).length) {
              selection.setRng(normalize(selection.getRng()));
              preserveSelection(ed, () => {
                runOnRanges(ed, (selectionRng, fake) => {
                  const expandedRng = fake ? selectionRng : expandRng(dom2, selectionRng, formatList);
                  applyRngStyle(dom2, expandedRng, false);
                });
              }, always);
              ed.nodeChanged();
            } else {
              applyCaretFormat(ed, name2, vars);
            }
            getExpandedListItemFormat(ed.formatter, name2).each((liFmt) => {
              each$e(getFullySelectedListItems(ed.selection), (li) => applyStyles(dom2, li, liFmt, vars));
            });
          }
          postProcess$1(name2, ed);
        }
        fireFormatApply(ed, name2, node, vars);
      };
      const applyFormat$1 = (editor, name2, vars, node) => {
        if (node || editor.selection.isEditable()) {
          applyFormatAction(editor, name2, vars, node);
        }
      };
      const hasVars = (value2) => has$2(value2, "vars");
      const setup$A = (registeredFormatListeners, editor) => {
        registeredFormatListeners.set({});
        editor.on("NodeChange", (e) => {
          updateAndFireChangeCallbacks(editor, e.element, registeredFormatListeners.get());
        });
        editor.on("FormatApply FormatRemove", (e) => {
          const element = Optional.from(e.node).map((nodeOrRange) => isNode(nodeOrRange) ? nodeOrRange : nodeOrRange.startContainer).bind((node) => isElement$7(node) ? Optional.some(node) : Optional.from(node.parentElement)).getOrThunk(() => fallbackElement(editor));
          updateAndFireChangeCallbacks(editor, element, registeredFormatListeners.get());
        });
      };
      const fallbackElement = (editor) => editor.selection.getStart();
      const matchingNode = (editor, parents2, format, similar, vars) => {
        const isMatchingNode = (node) => {
          const matchingFormat = editor.formatter.matchNode(node, format, vars ?? {}, similar);
          return !isUndefined(matchingFormat);
        };
        const isUnableToMatch = (node) => {
          if (matchesUnInheritedFormatSelector(editor, node, format)) {
            return true;
          } else {
            if (!similar) {
              return isNonNullable(editor.formatter.matchNode(node, format, vars, true));
            } else {
              return false;
            }
          }
        };
        return findUntil$1(parents2, isMatchingNode, isUnableToMatch);
      };
      const getParents = (editor, elm) => {
        const element = elm ?? fallbackElement(editor);
        return filter$5(getParents$2(editor.dom, element), (node) => isElement$7(node) && !isBogus$1(node));
      };
      const updateAndFireChangeCallbacks = (editor, elm, registeredCallbacks) => {
        const parents2 = getParents(editor, elm);
        each$d(registeredCallbacks, (data2, format) => {
          const runIfChanged = (spec) => {
            const match2 = matchingNode(editor, parents2, format, spec.similar, hasVars(spec) ? spec.vars : void 0);
            const isSet = match2.isSome();
            if (spec.state.get() !== isSet) {
              spec.state.set(isSet);
              const node = match2.getOr(elm);
              if (hasVars(spec)) {
                spec.callback(isSet, { node, format, parents: parents2 });
              } else {
                each$e(spec.callbacks, (callback) => callback(isSet, { node, format, parents: parents2 }));
              }
            }
          };
          each$e([data2.withSimilar, data2.withoutSimilar], runIfChanged);
          each$e(data2.withVars, runIfChanged);
        });
      };
      const addListeners = (editor, registeredFormatListeners, formats, callback, similar, vars) => {
        const formatChangeItems = registeredFormatListeners.get();
        each$e(formats.split(","), (format) => {
          const group = get$a(formatChangeItems, format).getOrThunk(() => {
            const base = {
              withSimilar: {
                state: Cell(false),
                similar: true,
                callbacks: []
              },
              withoutSimilar: {
                state: Cell(false),
                similar: false,
                callbacks: []
              },
              withVars: []
            };
            formatChangeItems[format] = base;
            return base;
          });
          const getCurrent = () => {
            const parents2 = getParents(editor);
            return matchingNode(editor, parents2, format, similar, vars).isSome();
          };
          if (isUndefined(vars)) {
            const toAppendTo = similar ? group.withSimilar : group.withoutSimilar;
            toAppendTo.callbacks.push(callback);
            if (toAppendTo.callbacks.length === 1) {
              toAppendTo.state.set(getCurrent());
            }
          } else {
            group.withVars.push({
              state: Cell(getCurrent()),
              similar,
              vars,
              callback
            });
          }
        });
        registeredFormatListeners.set(formatChangeItems);
      };
      const removeListeners = (registeredFormatListeners, formats, callback) => {
        const formatChangeItems = registeredFormatListeners.get();
        each$e(formats.split(","), (format) => get$a(formatChangeItems, format).each((group) => {
          formatChangeItems[format] = {
            withSimilar: {
              ...group.withSimilar,
              callbacks: filter$5(group.withSimilar.callbacks, (cb) => cb !== callback)
            },
            withoutSimilar: {
              ...group.withoutSimilar,
              callbacks: filter$5(group.withoutSimilar.callbacks, (cb) => cb !== callback)
            },
            withVars: filter$5(group.withVars, (item) => item.callback !== callback)
          };
        }));
        registeredFormatListeners.set(formatChangeItems);
      };
      const formatChangedInternal = (editor, registeredFormatListeners, formats, callback, similar, vars) => {
        addListeners(editor, registeredFormatListeners, formats, callback, similar, vars);
        return {
          unbind: () => removeListeners(registeredFormatListeners, formats, callback)
        };
      };
      const toggle = (editor, name2, vars, node) => {
        const fmt = editor.formatter.get(name2);
        if (fmt) {
          if (match$2(editor, name2, vars, node) && (!("toggle" in fmt[0]) || fmt[0].toggle)) {
            removeFormat$1(editor, name2, vars, node);
          } else {
            applyFormat$1(editor, name2, vars, node);
          }
        }
      };
      const tableModel = (element, width, rows) => ({
        element,
        width,
        rows
      });
      const tableRow = (element, cells2) => ({
        element,
        cells: cells2
      });
      const cellPosition = (x, y) => ({
        x,
        y
      });
      const getSpan = (td, key) => {
        return getOpt(td, key).bind(toInt).getOr(1);
      };
      const fillout = (table2, x, y, tr, td) => {
        const rowspan = getSpan(td, "rowspan");
        const colspan = getSpan(td, "colspan");
        const rows = table2.rows;
        for (let y2 = y; y2 < y + rowspan; y2++) {
          if (!rows[y2]) {
            rows[y2] = tableRow(deep(tr), []);
          }
          for (let x2 = x; x2 < x + colspan; x2++) {
            const cells2 = rows[y2].cells;
            cells2[x2] = y2 === y && x2 === x ? td : shallow(td);
          }
        }
      };
      const cellExists = (table2, x, y) => {
        const rows = table2.rows;
        const cells2 = rows[y] ? rows[y].cells : [];
        return !!cells2[x];
      };
      const skipCellsX = (table2, x, y) => {
        while (cellExists(table2, x, y)) {
          x++;
        }
        return x;
      };
      const getWidth = (rows) => {
        return foldl(rows, (acc, row) => {
          return row.cells.length > acc ? row.cells.length : acc;
        }, 0);
      };
      const findElementPos = (table2, element) => {
        const rows = table2.rows;
        for (let y = 0; y < rows.length; y++) {
          const cells2 = rows[y].cells;
          for (let x = 0; x < cells2.length; x++) {
            if (eq(cells2[x], element)) {
              return Optional.some(cellPosition(x, y));
            }
          }
        }
        return Optional.none();
      };
      const extractRows = (table2, sx, sy, ex, ey) => {
        const newRows = [];
        const rows = table2.rows;
        for (let y = sy; y <= ey; y++) {
          const cells2 = rows[y].cells;
          const slice = sx < ex ? cells2.slice(sx, ex + 1) : cells2.slice(ex, sx + 1);
          newRows.push(tableRow(rows[y].element, slice));
        }
        return newRows;
      };
      const subTable = (table2, startPos, endPos) => {
        const sx = startPos.x, sy = startPos.y;
        const ex = endPos.x, ey = endPos.y;
        const newRows = sy < ey ? extractRows(table2, sx, sy, ex, ey) : extractRows(table2, sx, ey, ex, sy);
        return tableModel(table2.element, getWidth(newRows), newRows);
      };
      const createDomTable = (table2, rows) => {
        const tableElement = shallow(table2.element);
        const tableBody = SugarElement.fromTag("tbody");
        append(tableBody, rows);
        append$1(tableElement, tableBody);
        return tableElement;
      };
      const modelRowsToDomRows = (table2) => {
        return map$3(table2.rows, (row) => {
          const cells2 = map$3(row.cells, (cell2) => {
            const td = deep(cell2);
            remove$9(td, "colspan");
            remove$9(td, "rowspan");
            return td;
          });
          const tr = shallow(row.element);
          append(tr, cells2);
          return tr;
        });
      };
      const fromDom = (tableElm) => {
        const table2 = tableModel(shallow(tableElm), 0, []);
        each$e(descendants(tableElm, "tr"), (tr, y) => {
          each$e(descendants(tr, "td,th"), (td, x) => {
            fillout(table2, skipCellsX(table2, x, y), y, tr, td);
          });
        });
        return tableModel(table2.element, getWidth(table2.rows), table2.rows);
      };
      const toDom = (table2) => {
        return createDomTable(table2, modelRowsToDomRows(table2));
      };
      const subsection = (table2, startElement, endElement) => {
        return findElementPos(table2, startElement).bind((startPos) => {
          return findElementPos(table2, endElement).map((endPos) => {
            return subTable(table2, startPos, endPos);
          });
        });
      };
      const findParentListContainer = (parents2) => find$2(parents2, (elm) => name(elm) === "ul" || name(elm) === "ol");
      const getFullySelectedListWrappers = (parents2, rng) => find$2(parents2, (elm) => name(elm) === "li" && hasAllContentsSelected(elm, rng)).fold(constant([]), (_li) => findParentListContainer(parents2).map((listCont) => {
        const listElm = SugarElement.fromTag(name(listCont));
        const listStyles = filter$4(getAllRaw(listCont), (_style, name2) => startsWith(name2, "list-style"));
        setAll(listElm, listStyles);
        return [
          SugarElement.fromTag("li"),
          listElm
        ];
      }).getOr([]));
      const wrap = (innerElm, elms) => {
        const wrapped = foldl(elms, (acc, elm) => {
          append$1(elm, acc);
          return elm;
        }, innerElm);
        return elms.length > 0 ? fromElements([wrapped]) : wrapped;
      };
      const directListWrappers = (commonAnchorContainer) => {
        if (isListItem$2(commonAnchorContainer)) {
          return parent(commonAnchorContainer).filter(isList$1).fold(constant([]), (listElm) => [commonAnchorContainer, listElm]);
        } else {
          return isList$1(commonAnchorContainer) ? [commonAnchorContainer] : [];
        }
      };
      const getWrapElements = (rootNode, rng, schema) => {
        const commonAnchorContainer = SugarElement.fromDom(rng.commonAncestorContainer);
        const parents2 = parentsAndSelf(commonAnchorContainer, rootNode);
        const wrapElements = filter$5(parents2, (el) => schema.isWrapper(name(el)));
        const listWrappers = getFullySelectedListWrappers(parents2, rng);
        const allWrappers = wrapElements.concat(listWrappers.length ? listWrappers : directListWrappers(commonAnchorContainer));
        return map$3(allWrappers, shallow);
      };
      const emptyFragment = () => fromElements([]);
      const getFragmentFromRange = (rootNode, rng, schema) => wrap(SugarElement.fromDom(rng.cloneContents()), getWrapElements(rootNode, rng, schema));
      const getParentTable = (rootElm, cell2) => ancestor$4(cell2, "table", curry(eq, rootElm));
      const getTableFragment = (rootNode, selectedTableCells) => getParentTable(rootNode, selectedTableCells[0]).bind((tableElm) => {
        const firstCell = selectedTableCells[0];
        const lastCell = selectedTableCells[selectedTableCells.length - 1];
        const fullTableModel = fromDom(tableElm);
        return subsection(fullTableModel, firstCell, lastCell).map((sectionedTableModel) => fromElements([toDom(sectionedTableModel)]));
      }).getOrThunk(emptyFragment);
      const getSelectionFragment = (rootNode, ranges, schema) => ranges.length > 0 && ranges[0].collapsed ? emptyFragment() : getFragmentFromRange(rootNode, ranges[0], schema);
      const read$3 = (rootNode, ranges, schema) => {
        const selectedCells = getCellsFromElementOrRanges(ranges, rootNode);
        return selectedCells.length > 0 ? getTableFragment(rootNode, selectedCells) : getSelectionFragment(rootNode, ranges, schema);
      };
      const isCollapsibleWhitespace = (text2, index) => index >= 0 && index < text2.length && isWhiteSpace(text2.charAt(index));
      const getInnerText = (bin) => {
        return trim$2(bin.innerText);
      };
      const getContextNodeName = (parentBlockOpt) => parentBlockOpt.map((block) => block.nodeName).getOr("div").toLowerCase();
      const getTextContent = (editor) => Optional.from(editor.selection.getRng()).map((rng) => {
        const parentBlockOpt = Optional.from(editor.dom.getParent(rng.commonAncestorContainer, editor.dom.isBlock));
        const body = editor.getBody();
        const contextNodeName = getContextNodeName(parentBlockOpt);
        const rangeContentClone = SugarElement.fromDom(rng.cloneContents());
        cleanupBogusElements(rangeContentClone);
        cleanupInputNames(rangeContentClone);
        const bin = editor.dom.add(body, contextNodeName, {
          "data-mce-bogus": "all",
          "style": "overflow: hidden; opacity: 0;"
        }, rangeContentClone.dom);
        const text2 = getInnerText(bin);
        const nonRenderedText = trim$2(bin.textContent ?? "");
        editor.dom.remove(bin);
        if (isCollapsibleWhitespace(nonRenderedText, 0) || isCollapsibleWhitespace(nonRenderedText, nonRenderedText.length - 1)) {
          const parentBlock = parentBlockOpt.getOr(body);
          const parentBlockText = getInnerText(parentBlock);
          const textIndex = parentBlockText.indexOf(text2);
          if (textIndex === -1) {
            return text2;
          } else {
            const hasProceedingSpace = isCollapsibleWhitespace(parentBlockText, textIndex - 1);
            const hasTrailingSpace = isCollapsibleWhitespace(parentBlockText, textIndex + text2.length);
            return (hasProceedingSpace ? " " : "") + text2 + (hasTrailingSpace ? " " : "");
          }
        } else {
          return text2;
        }
      }).getOr("");
      const getSerializedContent = (editor, args) => {
        const rng = editor.selection.getRng(), tmpElm = editor.dom.create("body");
        const sel = editor.selection.getSel();
        const ranges = processRanges(editor, getRanges(sel));
        const fragment = args.contextual ? read$3(SugarElement.fromDom(editor.getBody()), ranges, editor.schema).dom : rng.cloneContents();
        if (fragment) {
          tmpElm.appendChild(fragment);
        }
        return editor.selection.serializer.serialize(tmpElm, args);
      };
      const extractSelectedContent = (editor, args) => {
        if (args.format === "text") {
          return getTextContent(editor);
        } else {
          const content = getSerializedContent(editor, args);
          if (args.format === "tree") {
            return content;
          } else {
            return editor.selection.isCollapsed() ? "" : content;
          }
        }
      };
      const setupArgs$2 = (args, format) => ({
        ...args,
        format,
        get: true,
        selection: true,
        getInner: true
      });
      const getSelectedContentInternal = (editor, format, args = {}) => {
        const defaultedArgs = setupArgs$2(args, format);
        return preProcessGetContent(editor, defaultedArgs).fold(identity, (updatedArgs) => {
          const content = extractSelectedContent(editor, updatedArgs);
          return postProcessGetContent(editor, content, updatedArgs);
        });
      };
      const KEEP = 0, INSERT = 1, DELETE = 2;
      const diff = (left, right) => {
        const size = left.length + right.length + 2;
        const vDown = new Array(size);
        const vUp = new Array(size);
        const snake = (start2, end2, diag) => {
          return {
            start: start2,
            end: end2,
            diag
          };
        };
        const buildScript = (start1, end1, start2, end2, script2) => {
          const middle = getMiddleSnake(start1, end1, start2, end2);
          if (middle === null || middle.start === end1 && middle.diag === end1 - end2 || middle.end === start1 && middle.diag === start1 - start2) {
            let i = start1;
            let j = start2;
            while (i < end1 || j < end2) {
              if (i < end1 && j < end2 && left[i] === right[j]) {
                script2.push([KEEP, left[i]]);
                ++i;
                ++j;
              } else {
                if (end1 - start1 > end2 - start2) {
                  script2.push([DELETE, left[i]]);
                  ++i;
                } else {
                  script2.push([INSERT, right[j]]);
                  ++j;
                }
              }
            }
          } else {
            buildScript(start1, middle.start, start2, middle.start - middle.diag, script2);
            for (let i2 = middle.start; i2 < middle.end; ++i2) {
              script2.push([KEEP, left[i2]]);
            }
            buildScript(middle.end, end1, middle.end - middle.diag, end2, script2);
          }
        };
        const buildSnake = (start2, diag, end1, end2) => {
          let end3 = start2;
          while (end3 - diag < end2 && end3 < end1 && left[end3] === right[end3 - diag]) {
            ++end3;
          }
          return snake(start2, end3, diag);
        };
        const getMiddleSnake = (start1, end1, start2, end2) => {
          const m = end1 - start1;
          const n = end2 - start2;
          if (m === 0 || n === 0) {
            return null;
          }
          const delta = m - n;
          const sum = n + m;
          const offset = (sum % 2 === 0 ? sum : sum + 1) / 2;
          vDown[1 + offset] = start1;
          vUp[1 + offset] = end1 + 1;
          let d, k, i, x, y;
          for (d = 0; d <= offset; ++d) {
            for (k = -d; k <= d; k += 2) {
              i = k + offset;
              if (k === -d || k !== d && vDown[i - 1] < vDown[i + 1]) {
                vDown[i] = vDown[i + 1];
              } else {
                vDown[i] = vDown[i - 1] + 1;
              }
              x = vDown[i];
              y = x - start1 + start2 - k;
              while (x < end1 && y < end2 && left[x] === right[y]) {
                vDown[i] = ++x;
                ++y;
              }
              if (delta % 2 !== 0 && delta - d <= k && k <= delta + d) {
                if (vUp[i - delta] <= vDown[i]) {
                  return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2);
                }
              }
            }
            for (k = delta - d; k <= delta + d; k += 2) {
              i = k + offset - delta;
              if (k === delta - d || k !== delta + d && vUp[i + 1] <= vUp[i - 1]) {
                vUp[i] = vUp[i + 1] - 1;
              } else {
                vUp[i] = vUp[i - 1];
              }
              x = vUp[i] - 1;
              y = x - start1 + start2 - k;
              while (x >= start1 && y >= start2 && left[x] === right[y]) {
                vUp[i] = x--;
                y--;
              }
              if (delta % 2 === 0 && -d <= k && k <= d) {
                if (vUp[i] <= vDown[i + delta]) {
                  return buildSnake(vUp[i], k + start1 - start2, end1, end2);
                }
              }
            }
          }
          return null;
        };
        const script = [];
        buildScript(0, left.length, 0, right.length, script);
        return script;
      };
      const getOuterHtml = (elm) => {
        if (isElement$7(elm)) {
          return elm.outerHTML;
        } else if (isText$b(elm)) {
          return Entities.encodeRaw(elm.data, false);
        } else if (isComment(elm)) {
          return "<!--" + elm.data + "-->";
        }
        return "";
      };
      const createFragment = (html2) => {
        let node;
        const container = document.createElement("div");
        const frag = document.createDocumentFragment();
        if (html2) {
          container.innerHTML = html2;
        }
        while (node = container.firstChild) {
          frag.appendChild(node);
        }
        return frag;
      };
      const insertAt = (elm, html2, index) => {
        const fragment = createFragment(html2);
        if (elm.hasChildNodes() && index < elm.childNodes.length) {
          const target = elm.childNodes[index];
          elm.insertBefore(fragment, target);
        } else {
          elm.appendChild(fragment);
        }
      };
      const removeAt = (elm, index) => {
        if (elm.hasChildNodes() && index < elm.childNodes.length) {
          const target = elm.childNodes[index];
          elm.removeChild(target);
        }
      };
      const applyDiff = (diff2, elm) => {
        let index = 0;
        each$e(diff2, (action2) => {
          if (action2[0] === KEEP) {
            index++;
          } else if (action2[0] === INSERT) {
            insertAt(elm, action2[1], index);
            index++;
          } else if (action2[0] === DELETE) {
            removeAt(elm, index);
          }
        });
      };
      const read$2 = (elm, trimZwsp2) => filter$5(map$3(from(elm.childNodes), trimZwsp2 ? compose(trim$2, getOuterHtml) : getOuterHtml), (item) => {
        return item.length > 0;
      });
      const write = (fragments, elm) => {
        const currentFragments = map$3(from(elm.childNodes), getOuterHtml);
        applyDiff(diff(currentFragments, fragments), elm);
        return elm;
      };
      const lazyTempDocument = cached(() => document.implementation.createHTMLDocument("undo"));
      const hasIframes = (body) => body.querySelector("iframe") !== null;
      const createFragmentedLevel = (fragments) => {
        return {
          type: "fragmented",
          fragments,
          content: "",
          bookmark: null,
          beforeBookmark: null
        };
      };
      const createCompleteLevel = (content) => {
        return {
          type: "complete",
          fragments: null,
          content,
          bookmark: null,
          beforeBookmark: null
        };
      };
      const createFromEditor = (editor) => {
        const tempAttrs = editor.serializer.getTempAttrs();
        const body = trim$1(editor.getBody(), tempAttrs);
        return hasIframes(body) ? createFragmentedLevel(read$2(body, true)) : createCompleteLevel(trim$2(body.innerHTML));
      };
      const applyToEditor = (editor, level, before2) => {
        const bookmark = before2 ? level.beforeBookmark : level.bookmark;
        if (level.type === "fragmented") {
          write(level.fragments, editor.getBody());
        } else {
          editor.setContent(level.content, {
            format: "raw",
            // If we have a path bookmark, we need to check if the bookmark location was a fake caret.
            // If the bookmark was not a fake caret, then we need to ensure that setContent does not move the selection
            // as this can create a new fake caret - particularly if the first element in the body is contenteditable=false.
            // The creation of this new fake caret will cause our path offset to be off by one when restoring the original selection.
            no_selection: isNonNullable(bookmark) && isPathBookmark(bookmark) ? !bookmark.isFakeCaret : true
          });
        }
        if (bookmark) {
          editor.selection.moveToBookmark(bookmark);
          editor.selection.scrollIntoView();
        }
      };
      const getLevelContent = (level) => {
        return level.type === "fragmented" ? level.fragments.join("") : level.content;
      };
      const getCleanLevelContent = (level) => {
        const elm = SugarElement.fromTag("body", lazyTempDocument());
        set$3(elm, getLevelContent(level));
        each$e(descendants(elm, "*[data-mce-bogus]"), unwrap);
        return get$8(elm);
      };
      const hasEqualContent = (level1, level2) => getLevelContent(level1) === getLevelContent(level2);
      const hasEqualCleanedContent = (level1, level2) => getCleanLevelContent(level1) === getCleanLevelContent(level2);
      const isEq$1 = (level1, level2) => {
        if (!level1 || !level2) {
          return false;
        } else if (hasEqualContent(level1, level2)) {
          return true;
        } else {
          return hasEqualCleanedContent(level1, level2);
        }
      };
      const isUnlocked = (locks) => locks.get() === 0;
      const setTyping = (undoManager, typing, locks) => {
        if (isUnlocked(locks)) {
          undoManager.typing = typing;
        }
      };
      const endTyping = (undoManager, locks) => {
        if (undoManager.typing) {
          setTyping(undoManager, false, locks);
          undoManager.add();
        }
      };
      const endTypingLevelIgnoreLocks = (undoManager) => {
        if (undoManager.typing) {
          undoManager.typing = false;
          undoManager.add();
        }
      };
      const beforeChange$1 = (editor, locks, beforeBookmark) => {
        if (isUnlocked(locks)) {
          beforeBookmark.set(getUndoBookmark(editor.selection));
        }
      };
      const addUndoLevel$1 = (editor, undoManager, index, locks, beforeBookmark, level, event) => {
        const currentLevel = createFromEditor(editor);
        const newLevel = Tools.extend(level || {}, currentLevel);
        if (!isUnlocked(locks) || editor.removed) {
          return null;
        }
        const lastLevel = undoManager.data[index.get()];
        if (editor.dispatch("BeforeAddUndo", { level: newLevel, lastLevel, originalEvent: event }).isDefaultPrevented()) {
          return null;
        }
        if (lastLevel && isEq$1(lastLevel, newLevel)) {
          return null;
        }
        if (undoManager.data[index.get()]) {
          beforeBookmark.get().each((bm) => {
            undoManager.data[index.get()].beforeBookmark = bm;
          });
        }
        const customUndoRedoLevels = getCustomUndoRedoLevels(editor);
        if (customUndoRedoLevels) {
          if (undoManager.data.length > customUndoRedoLevels) {
            for (let i = 0; i < undoManager.data.length - 1; i++) {
              undoManager.data[i] = undoManager.data[i + 1];
            }
            undoManager.data.length--;
            index.set(undoManager.data.length);
          }
        }
        newLevel.bookmark = getUndoBookmark(editor.selection);
        if (index.get() < undoManager.data.length - 1) {
          undoManager.data.length = index.get() + 1;
        }
        undoManager.data.push(newLevel);
        index.set(undoManager.data.length - 1);
        const args = { level: newLevel, lastLevel, originalEvent: event };
        if (index.get() > 0) {
          editor.setDirty(true);
          editor.dispatch("AddUndo", args);
          editor.dispatch("change", args);
        } else {
          editor.dispatch("AddUndo", args);
        }
        return newLevel;
      };
      const clear$1 = (editor, undoManager, index) => {
        undoManager.data = [];
        index.set(0);
        undoManager.typing = false;
        editor.dispatch("ClearUndos");
      };
      const extra$1 = (editor, undoManager, index, callback1, callback2) => {
        if (undoManager.transact(callback1)) {
          const bookmark = undoManager.data[index.get()].bookmark;
          const lastLevel = undoManager.data[index.get() - 1];
          applyToEditor(editor, lastLevel, true);
          if (undoManager.transact(callback2)) {
            undoManager.data[index.get() - 1].beforeBookmark = bookmark;
          }
        }
      };
      const redo$1 = (editor, index, data2) => {
        let level;
        if (index.get() < data2.length - 1) {
          index.set(index.get() + 1);
          level = data2[index.get()];
          applyToEditor(editor, level, false);
          editor.setDirty(true);
          editor.dispatch("Redo", { level });
        }
        return level;
      };
      const undo$1 = (editor, undoManager, locks, index) => {
        let level;
        if (undoManager.typing) {
          undoManager.add();
          undoManager.typing = false;
          setTyping(undoManager, false, locks);
        }
        if (index.get() > 0) {
          index.set(index.get() - 1);
          level = undoManager.data[index.get()];
          applyToEditor(editor, level, true);
          editor.setDirty(true);
          editor.dispatch("Undo", { level });
        }
        return level;
      };
      const reset$1 = (undoManager) => {
        undoManager.clear();
        undoManager.add();
      };
      const hasUndo$1 = (editor, undoManager, index) => (
        // Has undo levels or typing and content isn't the same as the initial level
        index.get() > 0 || undoManager.typing && undoManager.data[0] && !isEq$1(createFromEditor(editor), undoManager.data[0])
      );
      const hasRedo$1 = (undoManager, index) => index.get() < undoManager.data.length - 1 && !undoManager.typing;
      const transact$1 = (undoManager, locks, callback) => {
        endTyping(undoManager, locks);
        undoManager.beforeChange();
        undoManager.ignore(callback);
        return undoManager.add();
      };
      const ignore$1 = (locks, callback) => {
        try {
          locks.set(locks.get() + 1);
          callback();
        } finally {
          locks.set(locks.get() - 1);
        }
      };
      const addVisualInternal = (editor, elm) => {
        const dom2 = editor.dom;
        const scope = isNonNullable(elm) ? elm : editor.getBody();
        each$e(dom2.select("table,a", scope), (matchedElm) => {
          switch (matchedElm.nodeName) {
            case "TABLE":
              const cls = getVisualAidsTableClass(editor);
              const value2 = dom2.getAttrib(matchedElm, "border");
              if ((!value2 || value2 === "0") && editor.hasVisual) {
                dom2.addClass(matchedElm, cls);
              } else {
                dom2.removeClass(matchedElm, cls);
              }
              break;
            case "A":
              if (!dom2.getAttrib(matchedElm, "href")) {
                const value3 = dom2.getAttrib(matchedElm, "name") || matchedElm.id;
                const cls2 = getVisualAidsAnchorClass(editor);
                if (value3 && editor.hasVisual) {
                  dom2.addClass(matchedElm, cls2);
                } else {
                  dom2.removeClass(matchedElm, cls2);
                }
              }
              break;
          }
        });
        editor.dispatch("VisualAid", { element: elm, hasVisual: editor.hasVisual });
      };
      const makePlainAdaptor = (editor) => ({
        init: {
          bindEvents: noop
        },
        undoManager: {
          beforeChange: (locks, beforeBookmark) => beforeChange$1(editor, locks, beforeBookmark),
          add: (undoManager, index, locks, beforeBookmark, level, event) => addUndoLevel$1(editor, undoManager, index, locks, beforeBookmark, level, event),
          undo: (undoManager, locks, index) => undo$1(editor, undoManager, locks, index),
          redo: (index, data2) => redo$1(editor, index, data2),
          clear: (undoManager, index) => clear$1(editor, undoManager, index),
          reset: (undoManager) => reset$1(undoManager),
          hasUndo: (undoManager, index) => hasUndo$1(editor, undoManager, index),
          hasRedo: (undoManager, index) => hasRedo$1(undoManager, index),
          transact: (undoManager, locks, callback) => transact$1(undoManager, locks, callback),
          ignore: (locks, callback) => ignore$1(locks, callback),
          extra: (undoManager, index, callback1, callback2) => extra$1(editor, undoManager, index, callback1, callback2)
        },
        formatter: {
          match: (name2, vars, node, similar) => match$2(editor, name2, vars, node, similar),
          matchAll: (names, vars) => matchAll(editor, names, vars),
          matchNode: (node, name2, vars, similar) => matchNode$1(editor, node, name2, vars, similar),
          canApply: (name2) => canApply(editor, name2),
          closest: (names) => closest(editor, names),
          apply: (name2, vars, node) => applyFormat$1(editor, name2, vars, node),
          remove: (name2, vars, node, similar) => removeFormat$1(editor, name2, vars, node, similar),
          toggle: (name2, vars, node) => toggle(editor, name2, vars, node),
          formatChanged: (registeredFormatListeners, formats, callback, similar, vars) => formatChangedInternal(editor, registeredFormatListeners, formats, callback, similar, vars)
        },
        editor: {
          getContent: (args) => getContentInternal(editor, args),
          setContent: (content, args) => setContentInternal(editor, content, args),
          insertContent: (value2, details) => insertHtmlAtCaret(editor, value2, details),
          addVisual: (elm) => addVisualInternal(editor, elm)
        },
        selection: {
          getContent: (format, args) => getSelectedContentInternal(editor, format, args)
        },
        autocompleter: {
          addDecoration: noop,
          // This was never fully implemented in RTC
          removeDecoration: noop
          // This was never fully implemented in RTC
        },
        raw: {
          getModel: () => Optional.none()
        }
      });
      const makeRtcAdaptor = (rtcEditor) => {
        const defaultVars = (vars) => isObject(vars) ? vars : {};
        const { init: init2, undoManager, formatter, editor, selection, autocompleter, raw } = rtcEditor;
        return {
          init: {
            bindEvents: init2.bindEvents
          },
          undoManager: {
            beforeChange: undoManager.beforeChange,
            add: undoManager.add,
            undo: undoManager.undo,
            redo: undoManager.redo,
            clear: undoManager.clear,
            reset: undoManager.reset,
            hasUndo: undoManager.hasUndo,
            hasRedo: undoManager.hasRedo,
            transact: (_undoManager, _locks, fn) => undoManager.transact(fn),
            ignore: (_locks, callback) => undoManager.ignore(callback),
            extra: (_undoManager, _index, callback1, callback2) => undoManager.extra(callback1, callback2)
          },
          formatter: {
            match: (name2, vars, _node, similar) => formatter.match(name2, defaultVars(vars), similar),
            matchAll: formatter.matchAll,
            matchNode: formatter.matchNode,
            canApply: (name2) => formatter.canApply(name2),
            closest: (names) => formatter.closest(names),
            apply: (name2, vars, _node) => formatter.apply(name2, defaultVars(vars)),
            remove: (name2, vars, _node, _similar) => formatter.remove(name2, defaultVars(vars)),
            toggle: (name2, vars, _node) => formatter.toggle(name2, defaultVars(vars)),
            formatChanged: (_rfl, formats, callback, similar, vars) => formatter.formatChanged(formats, callback, similar, vars)
          },
          editor: {
            getContent: (args) => editor.getContent(args),
            setContent: (content, args) => {
              return { content: editor.setContent(content, args), html: "" };
            },
            insertContent: (content, _details) => {
              editor.insertContent(content);
              return "";
            },
            addVisual: editor.addVisual
          },
          selection: {
            getContent: (_format, args) => selection.getContent(args)
          },
          autocompleter: {
            addDecoration: autocompleter.addDecoration,
            removeDecoration: autocompleter.removeDecoration
          },
          raw: {
            getModel: () => Optional.some(raw.getRawModel())
          }
        };
      };
      const makeNoopAdaptor = () => {
        const nul = constant(null);
        const empty2 = constant("");
        return {
          init: {
            bindEvents: noop
          },
          undoManager: {
            beforeChange: noop,
            add: nul,
            undo: nul,
            redo: nul,
            clear: noop,
            reset: noop,
            hasUndo: never,
            hasRedo: never,
            transact: nul,
            ignore: noop,
            extra: noop
          },
          formatter: {
            match: never,
            matchAll: constant([]),
            matchNode: constant(void 0),
            canApply: never,
            closest: empty2,
            apply: noop,
            remove: noop,
            toggle: noop,
            formatChanged: constant({ unbind: noop })
          },
          editor: {
            getContent: empty2,
            setContent: constant({ content: "", html: "" }),
            insertContent: constant(""),
            addVisual: noop
          },
          selection: {
            getContent: empty2
          },
          autocompleter: {
            addDecoration: noop,
            removeDecoration: noop
          },
          raw: {
            getModel: constant(Optional.none())
          }
        };
      };
      const isRtc = (editor) => has$2(editor.plugins, "rtc");
      const getRtcSetup = (editor) => get$a(editor.plugins, "rtc").bind((rtcPlugin) => (
        // This might not exist if the stub plugin is loaded on cloud
        Optional.from(rtcPlugin.setup)
      ));
      const setup$z = (editor) => {
        const editorCast = editor;
        return getRtcSetup(editor).fold(() => {
          editorCast.rtcInstance = makePlainAdaptor(editor);
          return Optional.none();
        }, (setup2) => {
          editorCast.rtcInstance = makeNoopAdaptor();
          return Optional.some(() => setup2().then((rtcEditor) => {
            editorCast.rtcInstance = makeRtcAdaptor(rtcEditor);
            return rtcEditor.rtc.isRemote;
          }));
        });
      };
      const getRtcInstanceWithFallback = (editor) => (
        // Calls to editor.getContent/editor.setContent should still work even if the rtcInstance is not yet available
        editor.rtcInstance ? editor.rtcInstance : makePlainAdaptor(editor)
      );
      const getRtcInstanceWithError = (editor) => {
        const rtcInstance = editor.rtcInstance;
        if (!rtcInstance) {
          throw new Error("Failed to get RTC instance not yet initialized.");
        } else {
          return rtcInstance;
        }
      };
      const beforeChange = (editor, locks, beforeBookmark) => {
        getRtcInstanceWithError(editor).undoManager.beforeChange(locks, beforeBookmark);
      };
      const addUndoLevel = (editor, undoManager, index, locks, beforeBookmark, level, event) => getRtcInstanceWithError(editor).undoManager.add(undoManager, index, locks, beforeBookmark, level, event);
      const undo = (editor, undoManager, locks, index) => getRtcInstanceWithError(editor).undoManager.undo(undoManager, locks, index);
      const redo = (editor, index, data2) => getRtcInstanceWithError(editor).undoManager.redo(index, data2);
      const clear = (editor, undoManager, index) => {
        getRtcInstanceWithError(editor).undoManager.clear(undoManager, index);
      };
      const reset = (editor, undoManager) => {
        getRtcInstanceWithError(editor).undoManager.reset(undoManager);
      };
      const hasUndo = (editor, undoManager, index) => getRtcInstanceWithError(editor).undoManager.hasUndo(undoManager, index);
      const hasRedo = (editor, undoManager, index) => getRtcInstanceWithError(editor).undoManager.hasRedo(undoManager, index);
      const transact = (editor, undoManager, locks, callback) => getRtcInstanceWithError(editor).undoManager.transact(undoManager, locks, callback);
      const ignore = (editor, locks, callback) => {
        getRtcInstanceWithError(editor).undoManager.ignore(locks, callback);
      };
      const extra = (editor, undoManager, index, callback1, callback2) => {
        getRtcInstanceWithError(editor).undoManager.extra(undoManager, index, callback1, callback2);
      };
      const matchFormat = (editor, name2, vars, node, similar) => getRtcInstanceWithError(editor).formatter.match(name2, vars, node, similar);
      const matchAllFormats = (editor, names, vars) => getRtcInstanceWithError(editor).formatter.matchAll(names, vars);
      const matchNodeFormat = (editor, node, name2, vars, similar) => getRtcInstanceWithError(editor).formatter.matchNode(node, name2, vars, similar);
      const canApplyFormat = (editor, name2) => getRtcInstanceWithError(editor).formatter.canApply(name2);
      const closestFormat = (editor, names) => getRtcInstanceWithError(editor).formatter.closest(names);
      const applyFormat = (editor, name2, vars, node) => {
        getRtcInstanceWithError(editor).formatter.apply(name2, vars, node);
      };
      const removeFormat = (editor, name2, vars, node, similar) => {
        getRtcInstanceWithError(editor).formatter.remove(name2, vars, node, similar);
      };
      const toggleFormat = (editor, name2, vars, node) => {
        getRtcInstanceWithError(editor).formatter.toggle(name2, vars, node);
      };
      const formatChanged = (editor, registeredFormatListeners, formats, callback, similar, vars) => getRtcInstanceWithError(editor).formatter.formatChanged(registeredFormatListeners, formats, callback, similar, vars);
      const getContent$2 = (editor, args) => getRtcInstanceWithFallback(editor).editor.getContent(args);
      const setContent$1 = (editor, content, args) => getRtcInstanceWithFallback(editor).editor.setContent(content, args);
      const insertContent$1 = (editor, value2, details) => getRtcInstanceWithFallback(editor).editor.insertContent(value2, details);
      const getSelectedContent = (editor, format, args) => getRtcInstanceWithError(editor).selection.getContent(format, args);
      const addVisual$1 = (editor, elm) => getRtcInstanceWithError(editor).editor.addVisual(elm);
      const bindEvents = (editor) => getRtcInstanceWithError(editor).init.bindEvents();
      const getContent$1 = (editor, args = {}) => {
        const format = args.format ? args.format : "html";
        return getSelectedContent(editor, format, args);
      };
      const deleteFromCallbackMap = (callbackMap, selector, callback) => {
        if (has$2(callbackMap, selector)) {
          const newCallbacks = filter$5(callbackMap[selector], (cb) => cb !== callback);
          if (newCallbacks.length === 0) {
            delete callbackMap[selector];
          } else {
            callbackMap[selector] = newCallbacks;
          }
        }
      };
      var SelectorChanged = (dom2, editor) => {
        let selectorChangedData;
        let currentSelectors;
        const findMatchingNode = (selector, nodes) => find$2(nodes, (node) => dom2.is(node, selector));
        const getParents2 = (elem) => dom2.getParents(elem, void 0, dom2.getRoot());
        const setup2 = () => {
          selectorChangedData = {};
          currentSelectors = {};
          editor.on("NodeChange", (e) => {
            const node = e.element;
            const parents2 = getParents2(node);
            const matchedSelectors = {};
            each$d(selectorChangedData, (callbacks, selector) => {
              findMatchingNode(selector, parents2).each((node2) => {
                if (!currentSelectors[selector]) {
                  each$e(callbacks, (callback) => {
                    callback(true, { node: node2, selector, parents: parents2 });
                  });
                  currentSelectors[selector] = callbacks;
                }
                matchedSelectors[selector] = callbacks;
              });
            });
            each$d(currentSelectors, (callbacks, selector) => {
              if (!matchedSelectors[selector]) {
                delete currentSelectors[selector];
                each$e(callbacks, (callback) => {
                  callback(false, { node, selector, parents: parents2 });
                });
              }
            });
          });
        };
        return {
          selectorChangedWithUnbind: (selector, callback) => {
            if (!selectorChangedData) {
              setup2();
            }
            if (!selectorChangedData[selector]) {
              selectorChangedData[selector] = [];
            }
            selectorChangedData[selector].push(callback);
            findMatchingNode(selector, getParents2(editor.selection.getStart())).each(() => {
              currentSelectors[selector] = selectorChangedData[selector];
            });
            return {
              unbind: () => {
                deleteFromCallbackMap(selectorChangedData, selector, callback);
                deleteFromCallbackMap(currentSelectors, selector, callback);
              }
            };
          }
        };
      };
      const isAttachedToDom = (node) => {
        return !!(node && node.ownerDocument) && contains(SugarElement.fromDom(node.ownerDocument), SugarElement.fromDom(node));
      };
      const isValidRange = (rng) => {
        if (!rng) {
          return false;
        } else {
          return isAttachedToDom(rng.startContainer) && isAttachedToDom(rng.endContainer);
        }
      };
      const EditorSelection = (dom2, win, serializer, editor) => {
        let selectedRange;
        let explicitRange;
        const { selectorChangedWithUnbind } = SelectorChanged(dom2, editor);
        const setCursorLocation = (node, offset) => {
          const rng = dom2.createRng();
          if (isNonNullable(node) && isNonNullable(offset)) {
            rng.setStart(node, offset);
            rng.setEnd(node, offset);
            setRng(rng);
            collapse2(false);
          } else {
            moveEndPoint(dom2, rng, editor.getBody(), true);
            setRng(rng);
          }
        };
        const getContent2 = (args) => getContent$1(editor, args);
        const setContent2 = (content, args) => setContentExternal(editor, content, args);
        const getStart$12 = (real) => getStart(editor.getBody(), getRng$1(), real);
        const getEnd$1 = (real) => getEnd(editor.getBody(), getRng$1(), real);
        const getBookmark2 = (type2, normalized) => bookmarkManager.getBookmark(type2, normalized);
        const moveToBookmark2 = (bookmark) => bookmarkManager.moveToBookmark(bookmark);
        const select$1 = (node, content) => {
          select(dom2, node, content).each(setRng);
          return node;
        };
        const isCollapsed = () => {
          const rng = getRng$1(), sel = getSel();
          if (!rng || rng.item) {
            return false;
          }
          if (rng.compareEndPoints) {
            return rng.compareEndPoints("StartToEnd", rng) === 0;
          }
          return !sel || rng.collapsed;
        };
        const isEditable2 = () => {
          if (editor.mode.isReadOnly()) {
            return false;
          }
          const rng = getRng$1();
          const fakeSelectedElements = editor.getBody().querySelectorAll('[data-mce-selected="1"]');
          if (fakeSelectedElements.length > 0) {
            return forall(fakeSelectedElements, (el) => dom2.isEditable(el.parentElement));
          } else {
            return isEditableRange(dom2, rng);
          }
        };
        const collapse2 = (toStart) => {
          const rng = getRng$1();
          rng.collapse(!!toStart);
          setRng(rng);
        };
        const getSel = () => win.getSelection ? win.getSelection() : win.document.selection;
        const getRng$1 = () => {
          let rng;
          const tryCompareBoundaryPoints = (how, sourceRange, destinationRange) => {
            try {
              return sourceRange.compareBoundaryPoints(how, destinationRange);
            } catch {
              return -1;
            }
          };
          const doc = win.document;
          if (isNonNullable(editor.bookmark) && !hasFocus(editor)) {
            const bookmark = getRng(editor);
            if (bookmark.isSome()) {
              return bookmark.map((r2) => processRanges(editor, [r2])[0]).getOr(doc.createRange());
            }
          }
          try {
            const selection = getSel();
            if (selection && !isRestrictedNode(selection.anchorNode)) {
              if (selection.rangeCount > 0) {
                rng = selection.getRangeAt(0);
              } else {
                rng = doc.createRange();
              }
              rng = processRanges(editor, [rng])[0];
            }
          } catch {
          }
          if (!rng) {
            rng = doc.createRange();
          }
          if (isDocument$1(rng.startContainer) && rng.collapsed) {
            const elm = dom2.getRoot();
            rng.setStart(elm, 0);
            rng.setEnd(elm, 0);
          }
          if (selectedRange && explicitRange) {
            if (tryCompareBoundaryPoints(rng.START_TO_START, rng, selectedRange) === 0 && tryCompareBoundaryPoints(rng.END_TO_END, rng, selectedRange) === 0) {
              rng = explicitRange;
            } else {
              selectedRange = null;
              explicitRange = null;
            }
          }
          return rng;
        };
        const setRng = (rng, forward) => {
          if (!isValidRange(rng)) {
            return;
          }
          const sel = getSel();
          const evt = editor.dispatch("SetSelectionRange", { range: rng, forward });
          rng = evt.range;
          if (sel) {
            explicitRange = rng;
            try {
              sel.removeAllRanges();
              sel.addRange(rng);
            } catch {
            }
            if (forward === false && sel.extend) {
              sel.collapse(rng.endContainer, rng.endOffset);
              sel.extend(rng.startContainer, rng.startOffset);
            }
            selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
          }
          if (!rng.collapsed && rng.startContainer === rng.endContainer && sel?.setBaseAndExtent) {
            if (rng.endOffset - rng.startOffset < 2) {
              if (rng.startContainer.hasChildNodes()) {
                const node = rng.startContainer.childNodes[rng.startOffset];
                if (node && node.nodeName === "IMG") {
                  sel.setBaseAndExtent(rng.startContainer, rng.startOffset, rng.endContainer, rng.endOffset);
                  if (sel.anchorNode !== rng.startContainer || sel.focusNode !== rng.endContainer) {
                    sel.setBaseAndExtent(node, 0, node, 1);
                  }
                }
              }
            }
          }
          editor.dispatch("AfterSetSelectionRange", { range: rng, forward });
        };
        const setNode = (elm) => {
          setContentInternal$1(editor, dom2.getOuterHTML(elm));
          return elm;
        };
        const getNode$12 = () => getNode(editor.getBody(), getRng$1());
        const getSelectedBlocks$1 = (startElm, endElm) => getSelectedBlocks(dom2, getRng$1(), startElm, endElm);
        const isForward = () => {
          const sel = getSel();
          const anchorNode = sel?.anchorNode;
          const focusNode = sel?.focusNode;
          if (!sel || !anchorNode || !focusNode || isRestrictedNode(anchorNode) || isRestrictedNode(focusNode)) {
            return true;
          }
          const anchorRange = dom2.createRng();
          const focusRange = dom2.createRng();
          try {
            anchorRange.setStart(anchorNode, sel.anchorOffset);
            anchorRange.collapse(true);
            focusRange.setStart(focusNode, sel.focusOffset);
            focusRange.collapse(true);
          } catch {
            return true;
          }
          return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
        };
        const normalize2 = () => {
          const rng = getRng$1();
          const sel = getSel();
          if (!hasMultipleRanges(sel) && hasAnyRanges(editor)) {
            const normRng = normalize$2(dom2, rng);
            normRng.each((normRng2) => {
              setRng(normRng2, isForward());
            });
            return normRng.getOr(rng);
          }
          return rng;
        };
        const selectorChanged = (selector, callback) => {
          selectorChangedWithUnbind(selector, callback);
          return exports2;
        };
        const getScrollContainer = () => {
          let scrollContainer;
          let node = dom2.getRoot();
          while (node && node.nodeName !== "BODY") {
            if (node.scrollHeight > node.clientHeight) {
              scrollContainer = node;
              break;
            }
            node = node.parentNode;
          }
          return scrollContainer;
        };
        const scrollIntoView = (elm, alignToTop) => {
          if (isNonNullable(elm)) {
            scrollElementIntoView(editor, elm, alignToTop);
          } else {
            scrollRangeIntoView(editor, getRng$1(), alignToTop);
          }
        };
        const placeCaretAt2 = (clientX, clientY) => setRng(fromPoint(clientX, clientY, editor.getDoc()));
        const getBoundingClientRect2 = () => {
          const rng = getRng$1();
          return rng.collapsed ? CaretPosition.fromRangeStart(rng).getClientRects()[0] : rng.getBoundingClientRect();
        };
        const destroy2 = () => {
          win = selectedRange = explicitRange = null;
          controlSelection.destroy();
        };
        const expand = (options = { type: "word" }) => setRng(RangeUtils(dom2).expand(getRng$1(), options));
        const exports2 = {
          dom: dom2,
          win,
          serializer,
          editor,
          expand,
          collapse: collapse2,
          setCursorLocation,
          getContent: getContent2,
          setContent: setContent2,
          getBookmark: getBookmark2,
          moveToBookmark: moveToBookmark2,
          select: select$1,
          isCollapsed,
          isEditable: isEditable2,
          isForward,
          setNode,
          getNode: getNode$12,
          getSel,
          setRng,
          getRng: getRng$1,
          getStart: getStart$12,
          getEnd: getEnd$1,
          getSelectedBlocks: getSelectedBlocks$1,
          normalize: normalize2,
          selectorChanged,
          selectorChangedWithUnbind,
          getScrollContainer,
          scrollIntoView,
          placeCaretAt: placeCaretAt2,
          getBoundingClientRect: getBoundingClientRect2,
          destroy: destroy2
        };
        const bookmarkManager = BookmarkManager(exports2);
        const controlSelection = ControlSelection(exports2, editor);
        exports2.bookmarkManager = bookmarkManager;
        exports2.controlSelection = controlSelection;
        return exports2;
      };
      const addNodeFilter = (settings, htmlParser, schema) => {
        htmlParser.addNodeFilter("br", (nodes, _, args) => {
          const blockElements = Tools.extend({}, schema.getBlockElements());
          const nonEmptyElements = schema.getNonEmptyElements();
          const whitespaceElements = schema.getWhitespaceElements();
          blockElements.body = 1;
          const isBlock2 = (node) => node.name in blockElements || isTransparentAstBlock(schema, node);
          for (let i = 0, l = nodes.length; i < l; i++) {
            let node = nodes[i];
            let parent2 = node.parent;
            if (parent2 && isBlock2(parent2) && node === parent2.lastChild) {
              let prev2 = node.prev;
              while (prev2) {
                const prevName = prev2.name;
                if (prevName !== "span" || prev2.attr("data-mce-type") !== "bookmark") {
                  if (prevName === "br") {
                    node = null;
                  }
                  break;
                }
                prev2 = prev2.prev;
              }
              if (node) {
                node.remove();
                if (isEmpty$2(schema, nonEmptyElements, whitespaceElements, parent2)) {
                  const elementRule = schema.getElementRule(parent2.name);
                  if (elementRule) {
                    if (elementRule.removeEmpty) {
                      parent2.remove();
                    } else if (elementRule.paddEmpty) {
                      paddEmptyNode(settings, args, isBlock2, parent2);
                    }
                  }
                }
              }
            } else {
              let lastParent = node;
              while (parent2 && parent2.firstChild === lastParent && parent2.lastChild === lastParent) {
                lastParent = parent2;
                if (blockElements[parent2.name]) {
                  break;
                }
                parent2 = parent2.parent;
              }
              if (lastParent === parent2) {
                const textNode = new AstNode("#text", 3);
                textNode.value = nbsp;
                node.replace(textNode);
              }
            }
          }
        });
      };
      const register$3 = (htmlParser, settings, dom2) => {
        htmlParser.addAttributeFilter("data-mce-tabindex", (nodes, name2) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            node.attr("tabindex", node.attr("data-mce-tabindex"));
            node.attr(name2, null);
          }
        });
        htmlParser.addAttributeFilter("src,href,style", (nodes, name2) => {
          const internalName = "data-mce-" + name2;
          const urlConverter = settings.url_converter;
          const urlConverterScope = settings.url_converter_scope;
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            let value2 = node.attr(internalName);
            if (value2 !== void 0) {
              node.attr(name2, value2.length > 0 ? value2 : null);
              node.attr(internalName, null);
            } else {
              value2 = node.attr(name2);
              if (name2 === "style") {
                value2 = dom2.serializeStyle(dom2.parseStyle(value2), node.name);
              } else if (urlConverter) {
                value2 = urlConverter.call(urlConverterScope, value2, name2, node.name);
              }
              node.attr(name2, value2.length > 0 ? value2 : null);
            }
          }
        });
        htmlParser.addAttributeFilter("class", (nodes) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            let value2 = node.attr("class");
            if (value2) {
              value2 = value2.replace(/(?:^|\s)mce-item-\w+(?!\S)/g, "");
              node.attr("class", value2.length > 0 ? value2 : null);
            }
          }
        });
        htmlParser.addAttributeFilter("data-mce-type", (nodes, name2, args) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            if (node.attr("data-mce-type") === "bookmark" && !args.cleanup) {
              const hasChildren = Optional.from(node.firstChild).exists((firstChild2) => !isZwsp(firstChild2.value ?? ""));
              if (hasChildren) {
                node.unwrap();
              } else {
                node.remove();
              }
            }
          }
        });
        htmlParser.addNodeFilter("script,style", (nodes, name2) => {
          const trim2 = (value2) => {
            return value2.replace(/(<!--\[CDATA\[|\]\]-->)/g, "\n").replace(/^[\r\n]*|[\r\n]*$/g, "").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, "").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, "");
          };
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            const firstChild2 = node.firstChild;
            const value2 = firstChild2?.value ?? "";
            if (name2 === "script") {
              const type2 = node.attr("type");
              if (type2) {
                node.attr("type", type2 === "mce-no/type" ? null : type2.replace(/^mce\-/, ""));
              }
              if (settings.element_format === "xhtml" && firstChild2 && value2.length > 0) {
                firstChild2.value = "// <![CDATA[\n" + trim2(value2) + "\n// ]]>";
              }
            } else {
              if (settings.element_format === "xhtml" && firstChild2 && value2.length > 0) {
                firstChild2.value = "<!--\n" + trim2(value2) + "\n-->";
              }
            }
          }
        });
        htmlParser.addNodeFilter("#comment", (nodes) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            const value2 = node.value;
            if (settings.preserve_cdata && value2?.indexOf("[CDATA[") === 0) {
              node.name = "#cdata";
              node.type = 4;
              node.value = dom2.decode(value2.replace(/^\[CDATA\[|\]\]$/g, ""));
            } else if (value2?.indexOf("mce:protected ") === 0) {
              node.name = "#text";
              node.type = 3;
              node.raw = true;
              node.value = unescape(value2).substr(14);
            }
          }
        });
        htmlParser.addNodeFilter("xml:namespace,input", (nodes, name2) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            if (node.type === 7) {
              node.remove();
            } else if (node.type === 1) {
              if (name2 === "input" && !node.attr("type")) {
                node.attr("type", "text");
              }
            }
          }
        });
        htmlParser.addAttributeFilter("data-mce-type", (nodes) => {
          each$e(nodes, (node) => {
            if (node.attr("data-mce-type") === "format-caret") {
              if (node.isEmpty(htmlParser.schema.getNonEmptyElements())) {
                node.remove();
              } else {
                node.unwrap();
              }
            }
          });
        });
        htmlParser.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected,data-mce-expando,data-mce-block,data-mce-type,data-mce-resize,data-mce-placeholder", (nodes, name2) => {
          let i = nodes.length;
          while (i--) {
            nodes[i].attr(name2, null);
          }
        });
        if (settings.remove_trailing_brs) {
          addNodeFilter(settings, htmlParser, htmlParser.schema);
        }
      };
      const trimTrailingBr = (rootNode) => {
        const isBr2 = (node) => {
          return node?.name === "br";
        };
        const brNode1 = rootNode.lastChild;
        if (isBr2(brNode1)) {
          const brNode2 = brNode1.prev;
          if (isBr2(brNode2)) {
            brNode1.remove();
            brNode2.remove();
          }
        }
      };
      const preProcess$1 = (editor, node, args) => {
        let oldDoc;
        const dom2 = editor.dom;
        let clonedNode = node.cloneNode(true);
        const impl = document.implementation;
        if (impl.createHTMLDocument) {
          const doc = impl.createHTMLDocument("");
          Tools.each(clonedNode.nodeName === "BODY" ? clonedNode.childNodes : [clonedNode], (node2) => {
            doc.body.appendChild(doc.importNode(node2, true));
          });
          if (clonedNode.nodeName !== "BODY") {
            clonedNode = doc.body.firstChild;
          } else {
            clonedNode = doc.body;
          }
          oldDoc = dom2.doc;
          dom2.doc = doc;
        }
        firePreProcess(editor, { ...args, node: clonedNode });
        if (oldDoc) {
          dom2.doc = oldDoc;
        }
        return clonedNode;
      };
      const shouldFireEvent = (editor, args) => {
        return isNonNullable(editor) && editor.hasEventListeners("PreProcess") && !args.no_events;
      };
      const process$1 = (editor, node, args) => {
        return shouldFireEvent(editor, args) ? preProcess$1(editor, node, args) : node;
      };
      const addTempAttr = (htmlParser, tempAttrs, name2) => {
        if (Tools.inArray(tempAttrs, name2) === -1) {
          htmlParser.addAttributeFilter(name2, (nodes, name3) => {
            let i = nodes.length;
            while (i--) {
              nodes[i].attr(name3, null);
            }
          });
          tempAttrs.push(name2);
        }
      };
      const postProcess = (editor, args, content) => {
        if (!args.no_events && editor) {
          const outArgs = firePostProcess(editor, { ...args, content });
          return outArgs.content;
        } else {
          return content;
        }
      };
      const getHtmlFromNode = (dom2, node, args) => {
        const html2 = trim$2(args.getInner ? node.innerHTML : dom2.getOuterHTML(node));
        return args.selection || isWsPreserveElement(SugarElement.fromDom(node)) ? html2 : Tools.trim(html2);
      };
      const parseHtml = (htmlParser, html2, args) => {
        const parserArgs = args.selection ? { forced_root_block: false, ...args } : args;
        const rootNode = htmlParser.parse(html2, parserArgs);
        trimTrailingBr(rootNode);
        return rootNode;
      };
      const serializeNode = (settings, schema, node) => {
        const htmlSerializer = HtmlSerializer(settings, schema);
        return htmlSerializer.serialize(node);
      };
      const toHtml = (editor, settings, schema, rootNode, args) => {
        const content = serializeNode(settings, schema, rootNode);
        return postProcess(editor, args, content);
      };
      const DomSerializerImpl = (settings, editor) => {
        const tempAttrs = ["data-mce-selected"];
        const defaultedSettings = {
          entity_encoding: "named",
          remove_trailing_brs: true,
          pad_empty_with_br: false,
          ...settings
        };
        const dom2 = editor && editor.dom ? editor.dom : DOMUtils.DOM;
        const schema = editor && editor.schema ? editor.schema : Schema(defaultedSettings);
        const htmlParser = DomParser(defaultedSettings, schema);
        register$3(htmlParser, defaultedSettings, dom2);
        const serialize = (node, domSerializerArgs = {}) => {
          const { indent: indent2, entity_encoding, ...rest } = domSerializerArgs;
          const parserArgs = { format: "html", ...rest };
          const targetNode = process$1(editor, node, parserArgs);
          const html2 = getHtmlFromNode(dom2, targetNode, parserArgs);
          const rootNode = parseHtml(htmlParser, html2, parserArgs);
          if (parserArgs.format === "tree") {
            return rootNode;
          }
          const serializerSettings = {
            ...defaultedSettings,
            ...isNonNullable(indent2) ? { indent: indent2 } : {},
            ...isNonNullable(entity_encoding) ? { entity_encoding } : {}
          };
          return toHtml(editor, serializerSettings, schema, rootNode, parserArgs);
        };
        return {
          schema,
          addNodeFilter: htmlParser.addNodeFilter,
          addAttributeFilter: htmlParser.addAttributeFilter,
          serialize,
          addRules: schema.addValidElements,
          setRules: schema.setValidElements,
          addTempAttr: curry(addTempAttr, htmlParser, tempAttrs),
          getTempAttrs: constant(tempAttrs),
          getNodeFilters: htmlParser.getNodeFilters,
          getAttributeFilters: htmlParser.getAttributeFilters,
          removeNodeFilter: htmlParser.removeNodeFilter,
          removeAttributeFilter: htmlParser.removeAttributeFilter
        };
      };
      const DomSerializer = (settings, editor) => {
        const domSerializer = DomSerializerImpl(settings, editor);
        return {
          /**
           * Schema instance that was used to when the Serializer was constructed.
           *
           * @field {tinymce.html.Schema} schema
           */
          schema: domSerializer.schema,
          /**
           * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name
           * and then execute the callback once it has finished parsing the document.
           *
           * @method addNodeFilter
           * @param {String} name Comma separated list of nodes to collect.
           * @param {Function} callback Callback function to execute once it has collected nodes.
           * @example
           * serializer.addNodeFilter('p,h1', (nodes, name) => {
           *   for (let i = 0; i < nodes.length; i++) {
           *     console.log(nodes[i].name);
           *   }
           * });
           */
          addNodeFilter: domSerializer.addNodeFilter,
          /**
           * Adds an attribute filter function to the parser used by the serializer, the parser will
           * collect nodes that has the specified attributes
           * and then execute the callback once it has finished parsing the document.
           *
           * @method addAttributeFilter
           * @param {String} name Comma separated list of attributes to collect.
           * @param {Function} callback Callback function to execute once it has collected nodes.
           * @example
           * serializer.addAttributeFilter('src,href', (nodes, name) => {
           *   for (let i = 0; i < nodes.length; i++) {
           *     console.log(nodes[i].name);
           *   }
           * });
           */
          addAttributeFilter: domSerializer.addAttributeFilter,
          /**
           * Serializes the specified browser DOM node into a HTML string.
           *
           * @method serialize
           * @param {DOMNode} node DOM node to serialize.
           * @param {Object} args Arguments option that gets passed to event handlers.
           */
          serialize: domSerializer.serialize,
          /**
           * Adds valid elements rules to the serializers schema instance this enables you to specify things
           * like what elements should be outputted and what attributes specific elements might have.
           * Consult the TinyMCE Documentation for more details on this format.
           *
           * @method addRules
           * @param {String} rules Valid elements rules string to add to schema.
           */
          addRules: domSerializer.addRules,
          /**
           * Sets the valid elements rules to the serializers schema instance this enables you to specify things
           * like what elements should be outputted and what attributes specific elements might have.
           * Consult the TinyMCE Documentation for more details on this format.
           *
           * @method setRules
           * @param {String} rules Valid elements rules string.
           */
          setRules: domSerializer.setRules,
          /**
           * Adds a temporary internal attribute these attributes will get removed on undo and
           * when getting contents out of the editor.
           *
           * @method addTempAttr
           * @param {String} name string
           */
          addTempAttr: domSerializer.addTempAttr,
          /**
           * Returns an array of all added temp attrs names.
           *
           * @method getTempAttrs
           * @return {String[]} Array with attribute names.
           */
          getTempAttrs: domSerializer.getTempAttrs,
          getNodeFilters: domSerializer.getNodeFilters,
          getAttributeFilters: domSerializer.getAttributeFilters,
          /**
           * Removes a node filter function or removes all filter functions from the parser used by the serializer for the node names provided.
           *
           * @method removeNodeFilter
           * @param {String} name Comma separated list of node names to remove filters for.
           * @param {Function} callback Optional callback function to only remove a specific callback.
           * @example
           * // Remove a single filter
           * serializer.removeNodeFilter('p,h1', someCallback);
           *
           * // Remove all filters
           * serializer.removeNodeFilter('p,h1');
           */
          removeNodeFilter: domSerializer.removeNodeFilter,
          /**
           * Removes an attribute filter function or removes all filter functions from the parser used by the serializer for the attribute names provided.
           *
           * @method removeAttributeFilter
           * @param {String} name Comma separated list of attribute names to remove filters for.
           * @param {Function} callback Optional callback function to only remove a specific callback.
           * @example
           * // Remove a single filter
           * serializer.removeAttributeFilter('src,href', someCallback);
           *
           * // Remove all filters
           * serializer.removeAttributeFilter('src,href');
           */
          removeAttributeFilter: domSerializer.removeAttributeFilter
        };
      };
      const defaultFormat$1 = "html";
      const setupArgs$1 = (args, format) => ({
        ...args,
        format,
        get: true,
        getInner: true
      });
      const getContent = (editor, args = {}) => {
        const format = args.format ? args.format : defaultFormat$1;
        const defaultedArgs = setupArgs$1(args, format);
        return preProcessGetContent(editor, defaultedArgs).fold(identity, (updatedArgs) => {
          const content = getContent$2(editor, updatedArgs);
          return postProcessGetContent(editor, content, updatedArgs);
        });
      };
      const defaultFormat = "html";
      const setupArgs = (args, content) => ({
        format: defaultFormat,
        ...args,
        set: true,
        content
      });
      const setContent = (editor, content, args = {}) => {
        const defaultedArgs = setupArgs(args, content);
        preProcessSetContent(editor, defaultedArgs).each((updatedArgs) => {
          const result = setContent$1(editor, updatedArgs.content, updatedArgs);
          postProcessSetContent(editor, result.html, updatedArgs);
        });
      };
      const DOM$a = DOMUtils.DOM;
      const restoreOriginalStyles = (editor) => {
        DOM$a.setStyle(editor.id, "display", editor.orgDisplay);
      };
      const safeDestroy = (x) => Optional.from(x).each((x2) => x2.destroy());
      const clearDomReferences = (editor) => {
        const ed = editor;
        ed.contentAreaContainer = ed.formElement = ed.container = ed.editorContainer = null;
        ed.bodyElement = ed.contentDocument = ed.contentWindow = null;
        ed.iframeElement = ed.targetElm = null;
        const selection = editor.selection;
        if (selection) {
          const dom2 = selection.dom;
          ed.selection = selection.win = selection.dom = dom2.doc = null;
        }
      };
      const restoreForm = (editor) => {
        const form = editor.formElement;
        if (form) {
          if (form._mceOldSubmit) {
            form.submit = form._mceOldSubmit;
            delete form._mceOldSubmit;
          }
          DOM$a.unbind(form, "submit reset", editor.formEventDelegate);
        }
      };
      const remove$1 = (editor) => {
        if (!editor.removed) {
          const { _selectionOverrides, editorUpload } = editor;
          const body = editor.getBody();
          const element = editor.getElement();
          if (body) {
            editor.save({ is_removing: true });
          }
          editor.removed = true;
          editor.unbindAllNativeEvents();
          if (editor.hasHiddenInput && isNonNullable(element?.nextSibling)) {
            DOM$a.remove(element.nextSibling);
          }
          fireRemove(editor);
          editor.editorManager.remove(editor);
          if (!editor.inline && body) {
            restoreOriginalStyles(editor);
          }
          fireDetach(editor);
          DOM$a.remove(editor.getContainer());
          safeDestroy(_selectionOverrides);
          safeDestroy(editorUpload);
          editor.destroy();
        }
      };
      const destroy = (editor, automatic) => {
        const { selection, dom: dom2 } = editor;
        if (editor.destroyed) {
          return;
        }
        if (!automatic && !editor.removed) {
          editor.remove();
          return;
        }
        if (!automatic) {
          editor.editorManager.off("beforeunload", editor._beforeUnload);
          if (editor.theme && editor.theme.destroy) {
            editor.theme.destroy();
          }
          safeDestroy(selection);
          safeDestroy(dom2);
        }
        restoreForm(editor);
        clearDomReferences(editor);
        editor.destroyed = true;
      };
      const CreateIconManager = () => {
        const lookup2 = {};
        const add2 = (id, iconPack) => {
          lookup2[id] = iconPack;
        };
        const get2 = (id) => {
          if (lookup2[id]) {
            return lookup2[id];
          } else {
            return { icons: {} };
          }
        };
        const has2 = (id) => has$2(lookup2, id);
        return {
          add: add2,
          get: get2,
          has: has2
        };
      };
      const IconManager = CreateIconManager();
      const ModelManager = AddOnManager.ModelManager;
      const getProp = (propName, elm) => {
        const rawElm = elm.dom;
        return rawElm[propName];
      };
      const getComputedSizeProp = (propName, elm) => parseInt(get$7(elm, propName), 10);
      const getClientWidth = curry(getProp, "clientWidth");
      const getClientHeight = curry(getProp, "clientHeight");
      const getMarginTop = curry(getComputedSizeProp, "margin-top");
      const getMarginLeft = curry(getComputedSizeProp, "margin-left");
      const getBoundingClientRect = (elm) => elm.dom.getBoundingClientRect();
      const isInsideElementContentArea = (bodyElm, clientX, clientY) => {
        const clientWidth = getClientWidth(bodyElm);
        const clientHeight = getClientHeight(bodyElm);
        return clientX >= 0 && clientY >= 0 && clientX <= clientWidth && clientY <= clientHeight;
      };
      const transpose = (inline, elm, clientX, clientY) => {
        const clientRect = getBoundingClientRect(elm);
        const deltaX = inline ? clientRect.left + elm.dom.clientLeft + getMarginLeft(elm) : 0;
        const deltaY = inline ? clientRect.top + elm.dom.clientTop + getMarginTop(elm) : 0;
        const x = clientX - deltaX;
        const y = clientY - deltaY;
        return { x, y };
      };
      const isXYInContentArea = (editor, clientX, clientY) => {
        const bodyElm = SugarElement.fromDom(editor.getBody());
        const targetElm = editor.inline ? bodyElm : documentElement(bodyElm);
        const transposedPoint = transpose(editor.inline, targetElm, clientX, clientY);
        return isInsideElementContentArea(targetElm, transposedPoint.x, transposedPoint.y);
      };
      const fromDomSafe = (node) => Optional.from(node).map(SugarElement.fromDom);
      const isEditorAttachedToDom = (editor) => {
        const rawContainer = editor.inline ? editor.getBody() : editor.getContentAreaContainer();
        return fromDomSafe(rawContainer).map(inBody).getOr(false);
      };
      var NotificationManagerImpl = () => {
        const unimplemented = () => {
          throw new Error("Theme did not provide a NotificationManager implementation.");
        };
        return {
          open: unimplemented,
          close: unimplemented,
          getArgs: unimplemented
        };
      };
      const NotificationManager = (editor) => {
        const notifications = [];
        const getImplementation = () => {
          const theme = editor.theme;
          return theme && theme.getNotificationManagerImpl ? theme.getNotificationManagerImpl() : NotificationManagerImpl();
        };
        const getTopNotification = () => {
          return Optional.from(notifications[0]);
        };
        const isEqual2 = (a, b) => {
          return a.type === b.type && a.text === b.text && !a.progressBar && !a.timeout && !b.progressBar && !b.timeout;
        };
        const reposition2 = () => {
          getTopNotification().each((notification) => {
            notification.reposition();
          });
        };
        const addNotification = (notification) => {
          notifications.push(notification);
        };
        const closeNotification = (notification) => {
          findIndex$2(notifications, (otherNotification) => {
            return otherNotification === notification;
          }).each((index) => {
            notifications.splice(index, 1);
          });
        };
        const open = (spec, fireEvent2 = true) => {
          if (editor.removed || !isEditorAttachedToDom(editor)) {
            return {};
          }
          if (fireEvent2) {
            editor.dispatch("BeforeOpenNotification", { notification: spec });
          }
          return find$2(notifications, (notification) => {
            return isEqual2(getImplementation().getArgs(notification), spec);
          }).getOrThunk(() => {
            editor.editorManager.setActive(editor);
            const notification = getImplementation().open(spec, () => {
              closeNotification(notification);
            }, () => hasEditorOrUiFocus(editor));
            addNotification(notification);
            reposition2();
            editor.dispatch("OpenNotification", { notification: { ...notification } });
            return notification;
          });
        };
        const close = () => {
          getTopNotification().each((notification) => {
            getImplementation().close(notification);
            closeNotification(notification);
            reposition2();
          });
        };
        const getNotifications = constant(notifications);
        const registerEvents2 = (editor2) => {
          editor2.on("SkinLoaded", () => {
            const serviceMessage = getServiceMessage(editor2);
            if (serviceMessage) {
              open({
                text: serviceMessage,
                type: "warning",
                timeout: 0
              }, false);
            }
            reposition2();
          });
          editor2.on("show ResizeEditor ResizeWindow NodeChange ToggleView FullscreenStateChanged", () => {
            requestAnimationFrame(reposition2);
          });
          editor2.on("remove", () => {
            each$e(notifications.slice(), (notification) => {
              getImplementation().close(notification);
            });
          });
          editor2.on("keydown", (e) => {
            const isF12 = e.key?.toLowerCase() === "f12" || e.keyCode === 123;
            if (e.altKey && isF12) {
              e.preventDefault();
              getTopNotification().map((notificationApi) => SugarElement.fromDom(notificationApi.getEl())).each((elm) => focus$1(elm));
            }
          });
        };
        registerEvents2(editor);
        return {
          /**
           * Opens a new notification.
           *
           * @method open
           * @param {Object} args A <code>name: value</code> collection containing settings such as: <code>timeout</code>, <code>type</code>, and message (<code>text</code>).
           * <br /><br />
           * For information on the available settings, see: <a href="https://www.tiny.cloud/docs/tinymce/8/creating-custom-notifications/">Create custom notifications</a>.
           */
          open,
          /**
           * Closes the top most notification.
           *
           * @method close
           */
          close,
          /**
           * Returns the currently opened notification objects.
           *
           * @method getNotifications
           * @return {Array} Array of the currently opened notifications.
           */
          getNotifications
        };
      };
      const PluginManager = AddOnManager.PluginManager;
      const ThemeManager = AddOnManager.ThemeManager;
      var WindowManagerImpl = () => {
        const unimplemented = () => {
          throw new Error("Theme did not provide a WindowManager implementation.");
        };
        return {
          open: unimplemented,
          openUrl: unimplemented,
          alert: unimplemented,
          confirm: unimplemented,
          close: unimplemented
        };
      };
      const WindowManager = (editor) => {
        let dialogs = [];
        const getImplementation = () => {
          const theme = editor.theme;
          return theme && theme.getWindowManagerImpl ? theme.getWindowManagerImpl() : WindowManagerImpl();
        };
        const funcBind = (scope, f) => {
          return (...args) => {
            return f ? f.apply(scope, args) : void 0;
          };
        };
        const fireOpenEvent = (dialog) => {
          editor.dispatch("OpenWindow", {
            dialog
          });
        };
        const fireCloseEvent = (dialog) => {
          editor.dispatch("CloseWindow", {
            dialog
          });
        };
        const addDialog = (dialog, triggerElement) => {
          dialogs.push({ instanceApi: dialog, triggerElement });
          fireOpenEvent(dialog);
        };
        const closeDialog = (dialog) => {
          fireCloseEvent(dialog);
          const dialogTriggerElement = findMap(dialogs, ({ instanceApi, triggerElement }) => instanceApi === dialog ? triggerElement : Optional.none());
          dialogs = filter$5(dialogs, ({ instanceApi }) => instanceApi !== dialog);
          if (dialogs.length === 0) {
            editor.focus();
          } else {
            dialogTriggerElement.filter(inBody).each(focus$1);
          }
        };
        const getTopDialog = () => {
          return Optional.from(dialogs[dialogs.length - 1]);
        };
        const storeSelectionAndOpenDialog = (openDialog) => {
          editor.editorManager.setActive(editor);
          store(editor);
          const activeEl = active();
          editor.ui.show();
          const dialog = openDialog();
          addDialog(dialog, activeEl);
          return dialog;
        };
        const open = (args, params) => {
          return storeSelectionAndOpenDialog(() => getImplementation().open(args, params, closeDialog));
        };
        const openUrl = (args) => {
          return storeSelectionAndOpenDialog(() => getImplementation().openUrl(args, closeDialog));
        };
        const restoreFocus = (activeEl) => {
          if (dialogs.length !== 0) {
            activeEl.each((el) => focus$1(el));
          }
        };
        const alert = (message, callback, scope) => {
          const activeEl = active();
          const windowManagerImpl = getImplementation();
          windowManagerImpl.alert(message, funcBind(scope ? scope : windowManagerImpl, () => {
            restoreFocus(activeEl);
            callback?.();
          }));
        };
        const confirm = (message, callback, scope) => {
          const activeEl = active();
          const windowManagerImpl = getImplementation();
          windowManagerImpl.confirm(message, funcBind(scope ? scope : windowManagerImpl, (state) => {
            restoreFocus(activeEl);
            callback?.(state);
          }));
        };
        const close = () => {
          getTopDialog().each(({ instanceApi: dialog }) => {
            getImplementation().close(dialog);
            closeDialog(dialog);
          });
        };
        editor.on("remove", () => {
          each$e(dialogs, ({ instanceApi: dialog }) => {
            getImplementation().close(dialog);
          });
        });
        return {
          /**
           * Opens a new window.
           *
           * @method open
           * @param {Object} config For information on the available options, see: <a href="https://www.tiny.cloud/docs/tinymce/8/dialog-configuration/#options">Dialog - Configuration options</a>.
           * @param {Object} params (Optional) For information on the available options, see: <a href="https://www.tiny.cloud/docs/tinymce/8/dialog-configuration/#configuration-parameters">Dialog - Configuration parameters</a>.
           * @returns {WindowManager.DialogInstanceApi} A new dialog instance.
           */
          open,
          /**
           * Opens a new window for the specified url.
           *
           * @method openUrl
           * @param {Object} config For information on the available options, see: <a href="https://www.tiny.cloud/docs/tinymce/8/urldialog/#configuration">URL dialog - Configuration</a>.
           * @returns {WindowManager.UrlDialogInstanceApi} A new URL dialog instance.
           */
          openUrl,
          /**
           * Creates an alert dialog. Do not use the blocking behavior of this
           * native version. Use the callback method instead; then it can be extended.
           *
           * @method alert
           * @param {String} message Text to display in the new alert dialog.
           * @param {Function} callback (Optional) Callback function to be executed after the user has selected ok.
           * @param {Object} scope (Optional) Scope to execute the callback in.
           * @example
           * // Displays an alert box using the active editors window manager instance
           * tinymce.activeEditor.windowManager.alert('Hello world!');
           */
          alert,
          /**
           * Creates an alert dialog. Do not use the blocking behavior of this
           * native version. Use the callback method instead; then it can be extended.
           *
           * @method confirm
           * @param {String} message Text to display in the new confirm dialog.
           * @param {Function} callback (Optional) Callback function to be executed after the user has selected ok or cancel.
           * @param {Object} scope (Optional) Scope to execute the callback in.
           * @example
           * // Displays a confirm box and an alert message will be displayed depending on what you choose in the confirm
           * tinymce.activeEditor.windowManager.confirm('Do you want to do something?', (state) => {
           *   const message = state ? 'Ok' : 'Cancel';
           *   tinymce.activeEditor.windowManager.alert(message);
           * });
           */
          confirm,
          /**
           * Closes the top most window.
           *
           * @method close
           */
          close
        };
      };
      const displayNotification$1 = (editor, message) => {
        editor.notificationManager.open({
          type: "error",
          text: message
        });
      };
      const displayError = (editor, message) => {
        if (editor._skinLoaded) {
          displayNotification$1(editor, message);
        } else {
          editor.on("SkinLoaded", () => {
            displayNotification$1(editor, message);
          });
        }
      };
      const uploadError = (editor, message) => {
        displayError(editor, I18n.translate(["Failed to upload image: {0}", message]));
      };
      const logError = (editor, errorType, msg) => {
        fireError(editor, errorType, { message: msg });
        console.error(msg);
      };
      const createLoadError = (type2, url, name2) => name2 ? `Failed to load ${type2}: ${name2} from url ${url}` : `Failed to load ${type2} url: ${url}`;
      const pluginLoadError = (editor, url, name2) => {
        logError(editor, "PluginLoadError", createLoadError("plugin", url, name2));
      };
      const iconsLoadError = (editor, url, name2) => {
        logError(editor, "IconsLoadError", createLoadError("icons", url, name2));
      };
      const languageLoadError = (editor, url, name2) => {
        logError(editor, "LanguageLoadError", createLoadError("language", url, name2));
      };
      const themeLoadError = (editor, url, name2) => {
        logError(editor, "ThemeLoadError", createLoadError("theme", url, name2));
      };
      const modelLoadError = (editor, url, name2) => {
        logError(editor, "ModelLoadError", createLoadError("model", url, name2));
      };
      const licenseKeyManagerLoadError = (editor, url) => {
        logError(editor, "LicenseKeyManagerLoadError", createLoadError("license key manager", url));
      };
      const componentLoadError = (editor, url) => {
        logError(editor, "ComponentLoadError", createLoadError("component", url));
      };
      const pluginInitError = (editor, name2, err) => {
        const message = I18n.translate(["Failed to initialize plugin: {0}", name2]);
        fireError(editor, "PluginLoadError", { message });
        initError(message, err);
        displayError(editor, message);
      };
      const initError = (message, ...x) => {
        const console2 = window.console;
        if (console2) {
          if (console2.error) {
            console2.error(message, ...x);
          } else {
            console2.log(message, ...x);
          }
        }
      };
      const processedEditors = /* @__PURE__ */ new WeakMap();
      const forceDisable = (editor) => {
        if (processedEditors.has(editor)) {
          return;
        }
        processedEditors.set(editor, true);
        const switchModeListener = () => {
          editor.on("SwitchMode", (e) => {
            const { mode } = e;
            if (mode !== "readonly") {
              editor.mode.set("readonly");
            }
          });
        };
        const disabledStateChangeListener = () => {
          editor.on("DisabledStateChange", (e) => {
            const { state } = e;
            if (!state) {
              e.preventDefault();
            }
          }, true);
        };
        if (editor.initialized) {
          if (!editor.removed) {
            editor.mode.set("readonly");
          }
          editor.options.set("disabled", true);
        } else {
          editor.on("init", () => {
            if (!editor.removed) {
              editor.mode.set("readonly");
            }
            editor.options.set("disabled", true);
          });
        }
        disabledStateChangeListener();
        switchModeListener();
      };
      const displayNotification = (editor, messageData) => {
        const { type: type2, message } = messageData;
        editor.notificationManager.open({
          type: type2,
          text: message
        });
      };
      const getConsoleFn = (type2) => {
        switch (type2) {
          case "error":
            return console.error;
          case "info":
            return console.info;
          case "warn":
            return console.warn;
          case "log":
          default:
            return console.log;
        }
      };
      const displayConsoleMessage = (messageData) => {
        const consoleFn = getConsoleFn(messageData.type);
        consoleFn(messageData.message);
      };
      const reportMessage = (editor, message) => {
        const { console: console2, editor: editorUi } = message;
        if (isNonNullable(editorUi)) {
          if (editor._skinLoaded) {
            displayNotification(editor, editorUi);
          } else {
            editor.on("SkinLoaded", () => {
              displayNotification(editor, editorUi);
            });
          }
        }
        if (isNonNullable(console2)) {
          displayConsoleMessage(console2);
        }
      };
      const DOCS_URL = "https://www.tiny.cloud/docs/tinymce/latest/license-key/";
      const DOCS_URL_MESSAGE = `Read more: ${DOCS_URL}`;
      const PROVIDE_LICENSE_KEY_MESSAGE = `Make sure to provide a valid license key or add license_key: 'gpl' to the init config to agree to the open source license terms.`;
      const reportNoKeyError = (editor) => {
        const baseMessage = "The editor is disabled because a TinyMCE license key has not been provided.";
        reportMessage(editor, {
          console: {
            type: "error",
            message: [
              `${baseMessage}`,
              PROVIDE_LICENSE_KEY_MESSAGE,
              DOCS_URL_MESSAGE
            ].join(" ")
          },
          editor: {
            type: "warning",
            message: `${baseMessage}`
          }
        });
      };
      const reportLoadError = (editor, onlineStatus) => {
        const key = `${onlineStatus === "online" ? "API" : "license"} key`;
        const baseMessage = `The editor is disabled because the TinyMCE ${key} could not be validated.`;
        reportMessage(editor, {
          console: {
            type: "error",
            message: [
              `${baseMessage}`,
              `The TinyMCE Commercial License Key Manager plugin is required for the provided ${key} to be validated but could not be loaded.`,
              DOCS_URL_MESSAGE
            ].join(" ")
          },
          editor: {
            type: "warning",
            message: `${baseMessage}`
          }
        });
      };
      const reportInvalidPlugin = (editor, pluginCode) => {
        const baseMessage = `The "${pluginCode}" plugin requires a valid TinyMCE license key.`;
        reportMessage(editor, {
          console: {
            type: "error",
            message: [
              `${baseMessage}`,
              DOCS_URL_MESSAGE
            ].join(" ")
          }
        });
      };
      const PLUGIN_CODE$1 = "licensekeymanager";
      const getOnlineStatus = (editor) => {
        const hasApiKey = isString(getApiKey(editor));
        return hasApiKey ? "online" : "offline";
      };
      const getLicenseKeyType = (editor) => {
        const licenseKey = getLicenseKey(editor)?.toLowerCase();
        if (licenseKey === "gpl") {
          return "gpl";
        } else if (isNullable(licenseKey)) {
          return "no_key";
        } else {
          return "non_gpl";
        }
      };
      const determineStrategy = (editor) => {
        const onlineStatus = getOnlineStatus(editor);
        const licenseKeyType = getLicenseKeyType(editor);
        const forcePlugin = new Set(getPlugins(editor)).has(PLUGIN_CODE$1);
        if (licenseKeyType !== "gpl" || onlineStatus === "online" || forcePlugin) {
          return {
            type: "use_plugin",
            onlineStatus,
            licenseKeyType,
            forcePlugin
          };
        } else {
          return {
            type: "use_gpl",
            onlineStatus,
            licenseKeyType,
            forcePlugin
          };
        }
      };
      const NoLicenseKeyManager = (editor) => ({
        validate: (data2) => {
          const { plugin } = data2;
          const hasPlugin = isString(plugin);
          if (hasPlugin) {
            reportInvalidPlugin(editor, plugin);
          }
          return Promise.resolve(false);
        }
      });
      const GplLicenseKeyManager = (editor) => ({
        validate: (data2) => {
          const { plugin } = data2;
          const hasPlugin = isString(plugin);
          if (hasPlugin) {
            reportInvalidPlugin(editor, plugin);
          }
          return Promise.resolve(!hasPlugin);
        }
      });
      const ADDON_KEY = "manager";
      const PLUGIN_CODE = PLUGIN_CODE$1;
      const setup$y = () => {
        const addOnManager = AddOnManager();
        const add2 = (addOn) => {
          addOnManager.add(ADDON_KEY, addOn);
        };
        const load = (editor, suffix) => {
          const strategy = determineStrategy(editor);
          if (strategy.type === "use_plugin") {
            const url = `plugins/${PLUGIN_CODE}/plugin${suffix}.js`;
            addOnManager.load(ADDON_KEY, url).catch(() => {
              licenseKeyManagerLoadError(editor, url);
            });
          }
        };
        const init2 = (editor) => {
          const setLicenseKeyManager = (licenseKeyManager) => {
            Object.defineProperty(editor, "licenseKeyManager", {
              value: licenseKeyManager,
              writable: false,
              configurable: false,
              enumerable: true
            });
          };
          const strategy = determineStrategy(editor);
          const LicenseKeyManager = addOnManager.get(ADDON_KEY);
          if (isNonNullable(LicenseKeyManager)) {
            const licenseKeyManagerApi = LicenseKeyManager(editor, addOnManager.urls[ADDON_KEY]);
            setLicenseKeyManager(licenseKeyManagerApi);
          } else {
            switch (strategy.type) {
              case "use_gpl": {
                setLicenseKeyManager(GplLicenseKeyManager(editor));
                break;
              }
              case "use_plugin": {
                forceDisable(editor);
                setLicenseKeyManager(NoLicenseKeyManager(editor));
                if (strategy.onlineStatus === "offline" && strategy.licenseKeyType === "no_key") {
                  reportNoKeyError(editor);
                } else {
                  reportLoadError(editor, strategy.onlineStatus);
                }
                break;
              }
            }
          }
          editor.licenseKeyManager.validate({});
        };
        return {
          load,
          add: add2,
          init: init2
        };
      };
      const LicenseKeyManagerLoader = setup$y();
      const removeFakeSelection = (editor) => {
        Optional.from(editor.selection.getNode()).each((elm) => {
          elm.removeAttribute("data-mce-selected");
        });
      };
      const setEditorCommandState = (editor, cmd, state) => {
        try {
          editor.getDoc().execCommand(cmd, false, String(state));
        } catch {
        }
      };
      const setCommonEditorCommands = (editor, state) => {
        setEditorCommandState(editor, "StyleWithCSS", state);
        setEditorCommandState(editor, "enableInlineTableEditing", state);
        setEditorCommandState(editor, "enableObjectResizing", state);
      };
      const restoreFakeSelection = (editor) => {
        editor.selection.setRng(editor.selection.getRng());
      };
      const toggleClass = (elm, cls, state) => {
        if (has(elm, cls) && !state) {
          remove$4(elm, cls);
        } else if (state) {
          add$2(elm, cls);
        }
      };
      const disableEditor = (editor) => {
        const body = SugarElement.fromDom(editor.getBody());
        toggleClass(body, "mce-content-readonly", true);
        editor.selection.controlSelection.hideResizeRect();
        editor._selectionOverrides.hideFakeCaret();
        removeFakeSelection(editor);
      };
      const enableEditor = (editor) => {
        const body = SugarElement.fromDom(editor.getBody());
        toggleClass(body, "mce-content-readonly", false);
        if (editor.hasEditableRoot()) {
          set(body, true);
        }
        setCommonEditorCommands(editor, false);
        if (hasEditorOrUiFocus(editor)) {
          editor.focus();
        }
        restoreFakeSelection(editor);
        editor.nodeChanged();
      };
      const isDisabled = (editor) => isDisabled$1(editor);
      const internalContentEditableAttr = "data-mce-contenteditable";
      const switchOffContentEditableTrue = (elm) => {
        each$e(descendants(elm, '*[contenteditable="true"]'), (elm2) => {
          set$4(elm2, internalContentEditableAttr, "true");
          set(elm2, false);
        });
      };
      const switchOnContentEditableTrue = (elm) => {
        each$e(descendants(elm, `*[${internalContentEditableAttr}="true"]`), (elm2) => {
          remove$9(elm2, internalContentEditableAttr);
          set(elm2, true);
        });
      };
      const toggleDisabled = (editor, state) => {
        const body = SugarElement.fromDom(editor.getBody());
        if (state) {
          disableEditor(editor);
          set(body, false);
          switchOffContentEditableTrue(body);
        } else {
          switchOnContentEditableTrue(body);
          enableEditor(editor);
        }
      };
      const registerDisabledContentFilters = (editor) => {
        if (editor.serializer) {
          registerFilters(editor);
        } else {
          editor.on("PreInit", () => {
            registerFilters(editor);
          });
        }
      };
      const registerFilters = (editor) => {
        editor.parser.addAttributeFilter("contenteditable", (nodes) => {
          if (isDisabled(editor)) {
            each$e(nodes, (node) => {
              node.attr(internalContentEditableAttr, node.attr("contenteditable"));
              node.attr("contenteditable", "false");
            });
          }
        });
        editor.serializer.addAttributeFilter(internalContentEditableAttr, (nodes) => {
          if (isDisabled(editor)) {
            each$e(nodes, (node) => {
              node.attr("contenteditable", node.attr(internalContentEditableAttr));
            });
          }
        });
        editor.serializer.addTempAttr(internalContentEditableAttr);
      };
      const isClickEvent = (e) => e.type === "click";
      const allowedEvents = ["copy"];
      const isAllowedEventInDisabledMode = (e) => contains$2(allowedEvents, e.type);
      const getAnchorHrefOpt = (editor, elm) => {
        const isRoot2 = (elm2) => eq(elm2, SugarElement.fromDom(editor.getBody()));
        return closest$3(elm, "a", isRoot2).bind((a) => getOpt(a, "href"));
      };
      const processDisabledEvents = (editor, e) => {
        if (isClickEvent(e) && !VK.metaKeyPressed(e)) {
          const elm = SugarElement.fromDom(e.target);
          getAnchorHrefOpt(editor, elm).each((href) => {
            e.preventDefault();
            if (/^#/.test(href)) {
              const targetEl = editor.dom.select(`${href},[name="${removeLeading(href, "#")}"]`);
              if (targetEl.length) {
                editor.selection.scrollIntoView(targetEl[0], true);
              }
            } else {
              window.open(href, "_blank", "rel=noopener noreferrer,menubar=yes,toolbar=yes,location=yes,status=yes,resizable=yes,scrollbars=yes");
            }
          });
        } else if (isAllowedEventInDisabledMode(e)) {
          editor.dispatch(e.type, e);
        }
      };
      const registerDisabledModeEventHandlers = (editor) => {
        editor.on("ShowCaret ObjectSelected", (e) => {
          if (isDisabled(editor)) {
            e.preventDefault();
          }
        });
        editor.on("DisabledStateChange", (e) => {
          if (!e.isDefaultPrevented()) {
            toggleDisabled(editor, e.state);
          }
        });
      };
      const registerEventsAndFilters$1 = (editor) => {
        registerDisabledContentFilters(editor);
        registerDisabledModeEventHandlers(editor);
      };
      const isContentCssSkinName = (url) => /^[a-z0-9\-]+$/i.test(url);
      const toContentSkinResourceName = (url) => "content/" + url + "/content.css";
      const isBundledCssSkinName = (url) => tinymce.Resource.has(toContentSkinResourceName(url));
      const getContentCssUrls = (editor) => {
        return transformToUrls(editor, getContentCss(editor));
      };
      const getFontCssUrls = (editor) => {
        return transformToUrls(editor, getFontCss(editor));
      };
      const transformToUrls = (editor, cssLinks) => {
        const skinUrl = editor.editorManager.baseURL + "/skins/content";
        const suffix = editor.editorManager.suffix;
        const contentCssFile = `content${suffix}.css`;
        return map$3(cssLinks, (url) => {
          if (isBundledCssSkinName(url)) {
            return url;
          } else if (isContentCssSkinName(url) && !editor.inline) {
            return `${skinUrl}/${url}/${contentCssFile}`;
          } else {
            return editor.documentBaseURI.toAbsolute(url);
          }
        });
      };
      const appendContentCssFromSettings = (editor) => {
        editor.contentCSS = editor.contentCSS.concat(getContentCssUrls(editor), getFontCssUrls(editor));
      };
      const getAllImages = (elm) => {
        return elm ? from(elm.getElementsByTagName("img")) : [];
      };
      const ImageScanner = (uploadStatus, blobCache) => {
        const cachedPromises = {};
        const findAll2 = (elm, predicate = always) => {
          const images = filter$5(getAllImages(elm), (img) => {
            const src = img.src;
            if (img.hasAttribute("data-mce-bogus")) {
              return false;
            }
            if (img.hasAttribute("data-mce-placeholder")) {
              return false;
            }
            if (!src || src === Env.transparentSrc) {
              return false;
            }
            if (startsWith(src, "blob:")) {
              return !uploadStatus.isUploaded(src) && predicate(img);
            }
            if (startsWith(src, "data:")) {
              return predicate(img);
            }
            return false;
          });
          const promises = map$3(images, (img) => {
            const imageSrc = img.src;
            if (has$2(cachedPromises, imageSrc)) {
              return cachedPromises[imageSrc].then((imageInfo) => {
                if (isString(imageInfo)) {
                  return imageInfo;
                } else {
                  return {
                    image: img,
                    blobInfo: imageInfo.blobInfo
                  };
                }
              });
            } else {
              const newPromise = imageToBlobInfo(blobCache, imageSrc).then((blobInfo) => {
                delete cachedPromises[imageSrc];
                return { image: img, blobInfo };
              }).catch((error2) => {
                delete cachedPromises[imageSrc];
                return error2;
              });
              cachedPromises[imageSrc] = newPromise;
              return newPromise;
            }
          });
          return Promise.all(promises);
        };
        return {
          findAll: findAll2
        };
      };
      const UploadStatus = () => {
        const PENDING = 1, UPLOADED = 2;
        let blobUriStatuses = {};
        const createStatus = (status, resultUri) => {
          return {
            status,
            resultUri
          };
        };
        const hasBlobUri = (blobUri) => {
          return blobUri in blobUriStatuses;
        };
        const getResultUri = (blobUri) => {
          const result = blobUriStatuses[blobUri];
          return result ? result.resultUri : null;
        };
        const isPending = (blobUri) => {
          return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === PENDING : false;
        };
        const isUploaded = (blobUri) => {
          return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === UPLOADED : false;
        };
        const markPending = (blobUri) => {
          blobUriStatuses[blobUri] = createStatus(PENDING, null);
        };
        const markUploaded = (blobUri, resultUri) => {
          blobUriStatuses[blobUri] = createStatus(UPLOADED, resultUri);
        };
        const removeFailed = (blobUri) => {
          delete blobUriStatuses[blobUri];
        };
        const destroy2 = () => {
          blobUriStatuses = {};
        };
        return {
          hasBlobUri,
          getResultUri,
          isPending,
          isUploaded,
          markPending,
          markUploaded,
          removeFailed,
          destroy: destroy2
        };
      };
      let count = 0;
      const seed = () => {
        const rnd = () => {
          return Math.round(random() * 4294967295).toString(36);
        };
        const now = (/* @__PURE__ */ new Date()).getTime();
        return "s" + now.toString(36) + rnd() + rnd() + rnd();
      };
      const uuid = (prefix) => {
        return prefix + count++ + seed();
      };
      const BlobCache = () => {
        let cache = [];
        const mimeToExt = (mime) => {
          const mimes = {
            "image/jpeg": "jpg",
            "image/jpg": "jpg",
            "image/gif": "gif",
            "image/png": "png",
            "image/apng": "apng",
            "image/avif": "avif",
            "image/svg+xml": "svg",
            "image/webp": "webp",
            "image/bmp": "bmp",
            "image/tiff": "tiff"
          };
          return mimes[mime.toLowerCase()] || "dat";
        };
        const create2 = (o, blob, base64, name2, filename) => {
          if (isString(o)) {
            const id = o;
            return toBlobInfo({
              id,
              name: name2,
              filename,
              blob,
              base64
            });
          } else if (isObject(o)) {
            return toBlobInfo(o);
          } else {
            throw new Error("Unknown input type");
          }
        };
        const toBlobInfo = (o) => {
          if (!o.blob || !o.base64) {
            throw new Error("blob and base64 representations of the image are required for BlobInfo to be created");
          }
          const id = o.id || uuid("blobid");
          const name2 = o.name || id;
          const blob = o.blob;
          return {
            id: constant(id),
            name: constant(name2),
            filename: constant(o.filename || name2 + "." + mimeToExt(blob.type)),
            blob: constant(blob),
            base64: constant(o.base64),
            blobUri: constant(o.blobUri || URL.createObjectURL(blob)),
            uri: constant(o.uri)
          };
        };
        const add2 = (blobInfo) => {
          if (!get2(blobInfo.id())) {
            cache.push(blobInfo);
          }
        };
        const findFirst = (predicate) => find$2(cache, predicate).getOrUndefined();
        const get2 = (id) => findFirst((cachedBlobInfo) => cachedBlobInfo.id() === id);
        const getByUri = (blobUri) => findFirst((blobInfo) => blobInfo.blobUri() === blobUri);
        const getByData = (base64, type2) => findFirst((blobInfo) => blobInfo.base64() === base64 && blobInfo.blob().type === type2);
        const removeByUri = (blobUri) => {
          cache = filter$5(cache, (blobInfo) => {
            if (blobInfo.blobUri() === blobUri) {
              URL.revokeObjectURL(blobInfo.blobUri());
              return false;
            }
            return true;
          });
        };
        const destroy2 = () => {
          each$e(cache, (cachedBlobInfo) => {
            URL.revokeObjectURL(cachedBlobInfo.blobUri());
          });
          cache = [];
        };
        return {
          create: create2,
          add: add2,
          get: get2,
          getByUri,
          getByData,
          findFirst,
          removeByUri,
          destroy: destroy2
        };
      };
      const Uploader = (uploadStatus, settings) => {
        const pendingPromises = {};
        const pathJoin = (path1, path2) => {
          if (path1) {
            return path1.replace(/\/$/, "") + "/" + path2.replace(/^\//, "");
          }
          return path2;
        };
        const defaultHandler = (blobInfo, progress) => new Promise((success, failure) => {
          const xhr = new XMLHttpRequest();
          xhr.open("POST", settings.url);
          xhr.withCredentials = settings.credentials;
          xhr.upload.onprogress = (e) => {
            progress(e.loaded / e.total * 100);
          };
          xhr.onerror = () => {
            failure("Image upload failed due to a XHR Transport error. Code: " + xhr.status);
          };
          xhr.onload = () => {
            if (xhr.status < 200 || xhr.status >= 300) {
              failure("HTTP Error: " + xhr.status);
              return;
            }
            const json = JSON.parse(xhr.responseText);
            if (!json || !isString(json.location)) {
              failure("Invalid JSON: " + xhr.responseText);
              return;
            }
            success(pathJoin(settings.basePath, json.location));
          };
          const formData = new FormData();
          formData.append("file", blobInfo.blob(), blobInfo.filename());
          xhr.send(formData);
        });
        const uploadHandler = isFunction(settings.handler) ? settings.handler : defaultHandler;
        const noUpload = () => new Promise((resolve2) => {
          resolve2([]);
        });
        const handlerSuccess = (blobInfo, url) => ({
          url,
          blobInfo,
          status: true
        });
        const handlerFailure = (blobInfo, error2) => ({
          url: "",
          blobInfo,
          status: false,
          error: error2
        });
        const resolvePending = (blobUri, result) => {
          Tools.each(pendingPromises[blobUri], (resolve2) => {
            resolve2(result);
          });
          delete pendingPromises[blobUri];
        };
        const uploadBlobInfo = (blobInfo, handler, openNotification2) => {
          uploadStatus.markPending(blobInfo.blobUri());
          return new Promise((resolve2) => {
            let notification;
            let progress;
            try {
              const closeNotification = () => {
                if (notification) {
                  notification.close();
                  progress = noop;
                }
              };
              const success = (url) => {
                closeNotification();
                uploadStatus.markUploaded(blobInfo.blobUri(), url);
                resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url));
                resolve2(handlerSuccess(blobInfo, url));
              };
              const failure = (error2) => {
                closeNotification();
                uploadStatus.removeFailed(blobInfo.blobUri());
                resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, error2));
                resolve2(handlerFailure(blobInfo, error2));
              };
              progress = (percent) => {
                if (percent < 0 || percent > 100) {
                  return;
                }
                Optional.from(notification).orThunk(() => Optional.from(openNotification2).map(apply$1)).each((n) => {
                  notification = n;
                  n.progressBar.value(percent);
                });
              };
              handler(blobInfo, progress).then(success, (err) => {
                failure(isString(err) ? { message: err } : err);
              });
            } catch (ex) {
              resolve2(handlerFailure(blobInfo, ex));
            }
          });
        };
        const isDefaultHandler = (handler) => handler === defaultHandler;
        const pendingUploadBlobInfo = (blobInfo) => {
          const blobUri = blobInfo.blobUri();
          return new Promise((resolve2) => {
            pendingPromises[blobUri] = pendingPromises[blobUri] || [];
            pendingPromises[blobUri].push(resolve2);
          });
        };
        const uploadBlobs = (blobInfos, openNotification2) => {
          blobInfos = Tools.grep(blobInfos, (blobInfo) => !uploadStatus.isUploaded(blobInfo.blobUri()));
          return Promise.all(Tools.map(blobInfos, (blobInfo) => uploadStatus.isPending(blobInfo.blobUri()) ? pendingUploadBlobInfo(blobInfo) : uploadBlobInfo(blobInfo, uploadHandler, openNotification2)));
        };
        const upload = (blobInfos, openNotification2) => !settings.url && isDefaultHandler(uploadHandler) ? noUpload() : uploadBlobs(blobInfos, openNotification2);
        return {
          upload
        };
      };
      const openNotification = (editor) => () => editor.notificationManager.open({
        text: editor.translate("Image uploading..."),
        type: "info",
        timeout: -1,
        progressBar: true
      });
      const createUploader = (editor, uploadStatus) => Uploader(uploadStatus, {
        url: getImageUploadUrl(editor),
        basePath: getImageUploadBasePath(editor),
        credentials: getImagesUploadCredentials(editor),
        handler: getImagesUploadHandler(editor)
      });
      const ImageUploader = (editor) => {
        const uploadStatus = UploadStatus();
        const uploader = createUploader(editor, uploadStatus);
        return {
          /**
           * Uploads images to the configured image upload URL (`images_upload_url`) or passes the images to the defined image upload handler function (`images_upload_handler`).
           *
           * @method upload
           * @param {Array} blobInfos  A BlobInfo array containing the image data to upload. A BlobInfo can be created by calling `editor.editorUpload.blobCache.create()`.
           * @param {Boolean} showNotification (Optional) When set to true, a notification with a progress bar will be shown during image uploads.
           */
          upload: (blobInfos, showNotification = true) => uploader.upload(blobInfos, showNotification ? openNotification(editor) : void 0)
        };
      };
      const isEmptyForPadding = (editor, element) => editor.dom.isEmpty(element.dom) && isNonNullable(editor.schema.getTextBlockElements()[name(element)]);
      const addPaddingToEmpty = (editor) => (element) => {
        if (isEmptyForPadding(editor, element)) {
          append$1(element, SugarElement.fromHtml('<br data-mce-bogus="1" />'));
        }
      };
      const EditorUpload = (editor) => {
        const blobCache = BlobCache();
        let uploader, imageScanner;
        const uploadStatus = UploadStatus();
        const urlFilters = [];
        const aliveGuard = (callback) => {
          return (result) => {
            if (editor.selection) {
              return callback(result);
            }
            return [];
          };
        };
        const cacheInvalidator = (url) => url + (url.indexOf("?") === -1 ? "?" : "&") + (/* @__PURE__ */ new Date()).getTime();
        const replaceString = (content, search2, replace) => {
          let index = 0;
          do {
            index = content.indexOf(search2, index);
            if (index !== -1) {
              content = content.substring(0, index) + replace + content.substr(index + search2.length);
              index += replace.length - search2.length + 1;
            }
          } while (index !== -1);
          return content;
        };
        const replaceImageUrl = (content, targetUrl, replacementUrl) => {
          const replacementString = `src="${replacementUrl}"${replacementUrl === Env.transparentSrc ? ' data-mce-placeholder="1"' : ""}`;
          content = replaceString(content, `src="${targetUrl}"`, replacementString);
          content = replaceString(content, 'data-mce-src="' + targetUrl + '"', 'data-mce-src="' + replacementUrl + '"');
          return content;
        };
        const replaceUrlInUndoStack = (targetUrl, replacementUrl) => {
          each$e(editor.undoManager.data, (level) => {
            if (level.type === "fragmented") {
              level.fragments = map$3(level.fragments, (fragment) => replaceImageUrl(fragment, targetUrl, replacementUrl));
            } else {
              level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
            }
          });
        };
        const replaceImageUriInView = (image, resultUri) => {
          const src = editor.convertURL(resultUri, "src");
          replaceUrlInUndoStack(image.src, resultUri);
          setAll$1(SugarElement.fromDom(image), {
            "src": shouldReuseFileName(editor) ? cacheInvalidator(resultUri) : resultUri,
            "data-mce-src": src
          });
        };
        const uploadImages = () => {
          if (!uploader) {
            uploader = createUploader(editor, uploadStatus);
          }
          return scanForImages().then(aliveGuard((imageInfos) => {
            const blobInfos = map$3(imageInfos, (imageInfo) => imageInfo.blobInfo);
            return uploader.upload(blobInfos, openNotification(editor)).then(aliveGuard((result) => {
              const imagesToRemove = [];
              let shouldDispatchChange = false;
              const filteredResult = map$3(result, (uploadInfo, index) => {
                const { blobInfo, image } = imageInfos[index];
                let removed = false;
                if (uploadInfo.status && shouldReplaceBlobUris(editor)) {
                  if (uploadInfo.url && !contains$1(image.src, uploadInfo.url)) {
                    shouldDispatchChange = true;
                  }
                  blobCache.removeByUri(image.src);
                  if (isRtc(editor)) ;
                  else {
                    replaceImageUriInView(image, uploadInfo.url);
                  }
                } else if (uploadInfo.error) {
                  if (uploadInfo.error.remove) {
                    replaceUrlInUndoStack(image.src, Env.transparentSrc);
                    imagesToRemove.push(image);
                    removed = true;
                  }
                  uploadError(editor, uploadInfo.error.message);
                }
                return {
                  element: image,
                  status: uploadInfo.status,
                  uploadUri: uploadInfo.url,
                  blobInfo,
                  removed
                };
              });
              if (imagesToRemove.length > 0 && !isRtc(editor)) {
                editor.undoManager.transact(() => {
                  each$e(fromDom$1(imagesToRemove), (sugarElement) => {
                    const parentOpt = parent(sugarElement);
                    remove$8(sugarElement);
                    parentOpt.each(addPaddingToEmpty(editor));
                    blobCache.removeByUri(sugarElement.dom.src);
                  });
                });
              } else if (shouldDispatchChange) {
                editor.undoManager.dispatchChange();
              }
              return filteredResult;
            }));
          }));
        };
        const uploadImagesAuto = () => isAutomaticUploadsEnabled(editor) ? uploadImages() : Promise.resolve([]);
        const isValidDataUriImage = (imgElm) => forall(urlFilters, (filter2) => filter2(imgElm));
        const addFilter = (filter2) => {
          urlFilters.push(filter2);
        };
        const scanForImages = () => {
          if (!imageScanner) {
            imageScanner = ImageScanner(uploadStatus, blobCache);
          }
          return imageScanner.findAll(editor.getBody(), isValidDataUriImage).then(aliveGuard((result) => {
            const filteredResult = filter$5(result, (resultItem) => {
              if (isString(resultItem)) {
                displayError(editor, resultItem);
                return false;
              } else if (resultItem.uriType === "blob") {
                return false;
              } else {
                return true;
              }
            });
            if (isRtc(editor)) ;
            else {
              each$e(filteredResult, (resultItem) => {
                replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri());
                resultItem.image.src = resultItem.blobInfo.blobUri();
                resultItem.image.removeAttribute("data-mce-src");
              });
            }
            return filteredResult;
          }));
        };
        const destroy2 = () => {
          blobCache.destroy();
          uploadStatus.destroy();
          imageScanner = uploader = null;
        };
        const replaceBlobUris = (content) => {
          return content.replace(/src="(blob:[^"]+)"/g, (match2, blobUri) => {
            const resultUri = uploadStatus.getResultUri(blobUri);
            if (resultUri) {
              return 'src="' + resultUri + '"';
            }
            let blobInfo = blobCache.getByUri(blobUri);
            if (!blobInfo) {
              blobInfo = foldl(editor.editorManager.get(), (result, editor2) => {
                return result || editor2.editorUpload && editor2.editorUpload.blobCache.getByUri(blobUri);
              }, void 0);
            }
            if (blobInfo) {
              const blob = blobInfo.blob();
              return 'src="data:' + blob.type + ";base64," + blobInfo.base64() + '"';
            }
            return match2;
          });
        };
        editor.on("SetContent", () => {
          if (isAutomaticUploadsEnabled(editor)) {
            uploadImagesAuto();
          } else {
            scanForImages();
          }
        });
        editor.on("RawSaveContent", (e) => {
          e.content = replaceBlobUris(e.content);
        });
        editor.on("GetContent", (e) => {
          if (e.source_view || e.format === "raw" || e.format === "tree") {
            return;
          }
          e.content = replaceBlobUris(e.content);
        });
        editor.on("PostRender", () => {
          editor.parser.addNodeFilter("img", (images) => {
            each$e(images, (img) => {
              const src = img.attr("src");
              if (!src || blobCache.getByUri(src)) {
                return;
              }
              const resultUri = uploadStatus.getResultUri(src);
              if (resultUri) {
                img.attr("src", resultUri);
              }
            });
          });
        });
        return {
          blobCache,
          addFilter,
          uploadImages,
          uploadImagesAuto,
          scanForImages,
          destroy: destroy2
        };
      };
      const get$1 = (editor) => {
        const dom2 = editor.dom;
        const schemaType = editor.schema.type;
        const formats = {
          valigntop: [
            { selector: "td,th", styles: { verticalAlign: "top" } }
          ],
          valignmiddle: [
            { selector: "td,th", styles: { verticalAlign: "middle" } }
          ],
          valignbottom: [
            { selector: "td,th", styles: { verticalAlign: "bottom" } }
          ],
          alignleft: [
            {
              selector: "figure.image",
              collapsed: false,
              classes: "align-left",
              ceFalseOverride: true,
              preview: "font-family font-size"
            },
            {
              selector: "figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre",
              styles: {
                textAlign: "left"
              },
              inherit: false,
              preview: false
            },
            {
              selector: "img,audio,video",
              collapsed: false,
              styles: {
                float: "left"
              },
              preview: "font-family font-size"
            },
            {
              selector: ".mce-placeholder",
              styles: {
                float: "left"
              },
              ceFalseOverride: true
            },
            {
              selector: "table",
              collapsed: false,
              styles: {
                marginLeft: "0px",
                marginRight: "auto"
              },
              onformat: (table2) => {
                dom2.setStyle(table2, "float", null);
              },
              preview: "font-family font-size"
            },
            {
              selector: ".mce-preview-object,[data-ephox-embed-iri]",
              ceFalseOverride: true,
              styles: {
                float: "left"
              }
            }
          ],
          aligncenter: [
            {
              selector: "figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre",
              styles: {
                textAlign: "center"
              },
              inherit: false,
              preview: "font-family font-size"
            },
            {
              selector: "figure.image",
              collapsed: false,
              classes: "align-center",
              ceFalseOverride: true,
              preview: "font-family font-size"
            },
            {
              selector: "img,audio,video",
              collapsed: false,
              styles: {
                display: "block",
                marginLeft: "auto",
                marginRight: "auto"
              },
              preview: false
            },
            {
              selector: ".mce-placeholder",
              styles: {
                display: "block",
                marginLeft: "auto",
                marginRight: "auto"
              },
              ceFalseOverride: true
            },
            {
              selector: "table",
              collapsed: false,
              styles: {
                marginLeft: "auto",
                marginRight: "auto"
              },
              preview: "font-family font-size"
            },
            {
              selector: ".mce-preview-object",
              ceFalseOverride: true,
              styles: {
                display: "table",
                // Needs to be `table` to properly render while editing
                marginLeft: "auto",
                marginRight: "auto"
              },
              preview: false
            },
            {
              selector: "[data-ephox-embed-iri]",
              ceFalseOverride: true,
              styles: {
                marginLeft: "auto",
                marginRight: "auto"
              },
              preview: false
            }
          ],
          alignright: [
            {
              selector: "figure.image",
              collapsed: false,
              classes: "align-right",
              ceFalseOverride: true,
              preview: "font-family font-size"
            },
            {
              selector: "figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre",
              styles: {
                textAlign: "right"
              },
              inherit: false,
              preview: "font-family font-size"
            },
            {
              selector: "img,audio,video",
              collapsed: false,
              styles: {
                float: "right"
              },
              preview: "font-family font-size"
            },
            {
              selector: ".mce-placeholder",
              styles: {
                float: "right"
              },
              ceFalseOverride: true
            },
            {
              selector: "table",
              collapsed: false,
              styles: {
                marginRight: "0px",
                marginLeft: "auto"
              },
              onformat: (table2) => {
                dom2.setStyle(table2, "float", null);
              },
              preview: "font-family font-size"
            },
            {
              selector: ".mce-preview-object,[data-ephox-embed-iri]",
              ceFalseOverride: true,
              styles: {
                float: "right"
              },
              preview: false
            }
          ],
          alignjustify: [
            {
              selector: "figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre",
              styles: {
                textAlign: "justify"
              },
              inherit: false,
              preview: "font-family font-size"
            }
          ],
          bold: [
            { inline: "strong", remove: "all", preserve_attributes: ["class", "style"] },
            { inline: "span", styles: { fontWeight: "bold" } },
            { inline: "b", remove: "all", preserve_attributes: ["class", "style"] }
          ],
          italic: [
            { inline: "em", remove: "all", preserve_attributes: ["class", "style"] },
            { inline: "span", styles: { fontStyle: "italic" } },
            { inline: "i", remove: "all", preserve_attributes: ["class", "style"] }
          ],
          underline: [
            { inline: "span", styles: { textDecoration: "underline" }, exact: true },
            { inline: "u", remove: "all", preserve_attributes: ["class", "style"] }
          ],
          strikethrough: /* @__PURE__ */ (() => {
            const span = { inline: "span", styles: { textDecoration: "line-through" }, exact: true };
            const strike = { inline: "strike", remove: "all", preserve_attributes: ["class", "style"] };
            const s = { inline: "s", remove: "all", preserve_attributes: ["class", "style"] };
            return schemaType !== "html4" ? [s, span, strike] : [span, s, strike];
          })(),
          forecolor: { inline: "span", styles: { color: "%value" }, links: true, remove_similar: true, clear_child_styles: true },
          hilitecolor: { inline: "span", styles: { backgroundColor: "%value" }, links: true, remove_similar: true, clear_child_styles: true },
          fontname: { inline: "span", toggle: false, styles: { fontFamily: "%value" }, clear_child_styles: true },
          fontsize: { inline: "span", toggle: false, styles: { fontSize: "%value" }, clear_child_styles: true },
          lineheight: { selector: "h1,h2,h3,h4,h5,h6,p,li,td,th,div", styles: { lineHeight: "%value" } },
          fontsize_class: { inline: "span", attributes: { class: "%value" } },
          blockquote: { block: "blockquote", wrapper: true, remove: "all" },
          subscript: { inline: "sub" },
          superscript: { inline: "sup" },
          code: { inline: "code" },
          samp: { inline: "samp" },
          link: {
            inline: "a",
            selector: "a",
            remove: "all",
            split: true,
            deep: true,
            onmatch: (node, _fmt, _itemName) => {
              return isElement$7(node) && node.hasAttribute("href");
            },
            onformat: (elm, _fmt, vars) => {
              Tools.each(vars, (value2, key) => {
                dom2.setAttrib(elm, key, value2);
              });
            }
          },
          lang: {
            inline: "span",
            clear_child_styles: true,
            remove_similar: true,
            attributes: {
              "lang": "%value",
              "data-mce-lang": (vars) => vars?.customValue ?? null
            }
          },
          removeformat: [
            {
              selector: "b,strong,em,i,font,u,strike,s,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins,small",
              remove: "all",
              split: true,
              expand: false,
              block_expand: true,
              deep: true
            },
            { selector: "span", attributes: ["style", "class"], remove: "empty", split: true, expand: false, deep: true },
            { selector: "*", attributes: ["style", "class"], split: false, expand: false, deep: true }
          ]
        };
        Tools.each("p h1 h2 h3 h4 h5 h6 div address pre dt dd".split(/\s/), (name2) => {
          formats[name2] = { block: name2, remove: "all" };
        });
        return formats;
      };
      const genericBase = {
        remove_similar: true,
        inherit: false
      };
      const cellBase = {
        selector: "td,th",
        ...genericBase
      };
      const cellFormats = {
        tablecellbackgroundcolor: {
          styles: { backgroundColor: "%value" },
          ...cellBase
        },
        tablecellverticalalign: {
          styles: {
            "vertical-align": "%value"
          },
          ...cellBase
        },
        tablecellbordercolor: {
          styles: { borderColor: "%value" },
          ...cellBase
        },
        tablecellclass: {
          classes: ["%value"],
          ...cellBase
        },
        tableclass: {
          selector: "table",
          classes: ["%value"],
          ...genericBase
        },
        tablecellborderstyle: {
          styles: { borderStyle: "%value" },
          ...cellBase
        },
        tablecellborderwidth: {
          styles: { borderWidth: "%value" },
          ...cellBase
        }
      };
      const get = constant(cellFormats);
      const FormatRegistry = (editor) => {
        const formats = {};
        const get$22 = (name2) => isNonNullable(name2) ? formats[name2] : formats;
        const has2 = (name2) => has$2(formats, name2);
        const register2 = (name2, format) => {
          if (name2) {
            if (!isString(name2)) {
              each$d(name2, (format2, name3) => {
                register2(name3, format2);
              });
            } else {
              if (!isArray$1(format)) {
                format = [format];
              }
              each$e(format, (format2) => {
                if (isUndefined(format2.deep)) {
                  format2.deep = !isSelectorFormat(format2);
                }
                if (isUndefined(format2.split)) {
                  format2.split = !isSelectorFormat(format2) || isInlineFormat(format2);
                }
                if (isUndefined(format2.remove) && isSelectorFormat(format2) && !isInlineFormat(format2)) {
                  format2.remove = "none";
                }
                if (isSelectorFormat(format2) && isInlineFormat(format2)) {
                  format2.mixed = true;
                  format2.block_expand = true;
                }
                if (isString(format2.classes)) {
                  format2.classes = format2.classes.split(/\s+/);
                }
              });
              formats[name2] = format;
            }
          }
        };
        const unregister = (name2) => {
          if (name2 && formats[name2]) {
            delete formats[name2];
          }
          return formats;
        };
        register2(get$1(editor));
        register2(get());
        register2(getFormats(editor));
        return {
          get: get$22,
          has: has2,
          register: register2,
          unregister
        };
      };
      const each$3 = Tools.each;
      const dom = DOMUtils.DOM;
      const isPreviewItem = (item) => isNonNullable(item) && isObject(item);
      const parsedSelectorToHtml = (ancestry, editor) => {
        const schema = editor && editor.schema || Schema({});
        const decorate = (elm, item) => {
          if (item.classes.length > 0) {
            dom.addClass(elm, item.classes.join(" "));
          }
          dom.setAttribs(elm, item.attrs);
        };
        const createElement = (sItem) => {
          const item = isString(sItem) ? {
            name: sItem,
            classes: [],
            attrs: {}
          } : sItem;
          const elm = dom.create(item.name);
          decorate(elm, item);
          return elm;
        };
        const getRequiredParent = (elm, candidate) => {
          const elmRule = schema.getElementRule(elm.nodeName.toLowerCase());
          const parentsRequired = elmRule?.parentsRequired;
          if (parentsRequired && parentsRequired.length) {
            return candidate && contains$2(parentsRequired, candidate) ? candidate : parentsRequired[0];
          } else {
            return false;
          }
        };
        const wrapInHtml = (elm, ancestors2, siblings2) => {
          let parentCandidate;
          const ancestor2 = ancestors2[0];
          const ancestorName = isPreviewItem(ancestor2) ? ancestor2.name : void 0;
          const parentRequired = getRequiredParent(elm, ancestorName);
          if (parentRequired) {
            if (ancestorName === parentRequired) {
              parentCandidate = ancestor2;
              ancestors2 = ancestors2.slice(1);
            } else {
              parentCandidate = parentRequired;
            }
          } else if (ancestor2) {
            parentCandidate = ancestor2;
            ancestors2 = ancestors2.slice(1);
          } else if (!siblings2) {
            return elm;
          }
          const parent2 = parentCandidate ? createElement(parentCandidate) : dom.create("div");
          parent2.appendChild(elm);
          if (siblings2) {
            Tools.each(siblings2, (sibling2) => {
              const siblingElm = createElement(sibling2);
              parent2.insertBefore(siblingElm, elm);
            });
          }
          const parentSiblings = isPreviewItem(parentCandidate) ? parentCandidate.siblings : void 0;
          return wrapInHtml(parent2, ancestors2, parentSiblings);
        };
        const fragment = dom.create("div");
        if (ancestry.length > 0) {
          const item = ancestry[0];
          const elm = createElement(item);
          const siblings2 = isPreviewItem(item) ? item.siblings : void 0;
          fragment.appendChild(wrapInHtml(elm, ancestry.slice(1), siblings2));
        }
        return fragment;
      };
      const parseSelectorItem = (item) => {
        item = Tools.trim(item);
        let tagName = "div";
        const obj = {
          name: tagName,
          classes: [],
          attrs: {},
          selector: item
        };
        if (item !== "*") {
          tagName = item.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g, ($0, $1, $2, $3, $4) => {
            switch ($1) {
              case "#":
                obj.attrs.id = $2;
                break;
              case ".":
                obj.classes.push($2);
                break;
              case ":":
                if (Tools.inArray("checked disabled enabled read-only required".split(" "), $2) !== -1) {
                  obj.attrs[$2] = $2;
                }
                break;
            }
            if ($3 === "[") {
              const m = $4.match(/([\w\-]+)(?:\=\"([^\"]+))?/);
              if (m) {
                obj.attrs[m[1]] = m[2];
              }
            }
            return "";
          });
        }
        obj.name = tagName || "div";
        return obj;
      };
      const parseSelector = (selector) => {
        if (!isString(selector)) {
          return [];
        }
        selector = selector.split(/\s*,\s*/)[0];
        selector = selector.replace(/\s*(~\+|~|\+|>)\s*/g, "$1");
        return Tools.map(selector.split(/(?:>|\s+(?![^\[\]]+\]))/), (item) => {
          const siblings2 = Tools.map(item.split(/(?:~\+|~|\+)/), parseSelectorItem);
          const obj = siblings2.pop();
          if (siblings2.length) {
            obj.siblings = siblings2;
          }
          return obj;
        }).reverse();
      };
      const getCssText = (editor, format) => {
        let previewCss = "";
        let previewStyles = getPreviewStyles(editor);
        if (previewStyles === "") {
          return "";
        }
        const removeVars = (val) => {
          return isString(val) ? val.replace(/%(\w+)/g, "") : "";
        };
        const getComputedStyle = (name3, elm) => {
          return dom.getStyle(elm ?? editor.getBody(), name3, true);
        };
        if (isString(format)) {
          const formats = editor.formatter.get(format);
          if (!formats) {
            return "";
          }
          format = formats[0];
        }
        if ("preview" in format) {
          const preview = format.preview;
          if (preview === false) {
            return "";
          } else {
            previewStyles = preview || previewStyles;
          }
        }
        let name2 = format.block || format.inline || "span";
        let previewFrag;
        const items = parseSelector(format.selector);
        if (items.length > 0) {
          if (!items[0].name) {
            items[0].name = name2;
          }
          name2 = format.selector;
          previewFrag = parsedSelectorToHtml(items, editor);
        } else {
          previewFrag = parsedSelectorToHtml([name2], editor);
        }
        const previewElm = dom.select(name2, previewFrag)[0] || previewFrag.firstChild;
        each$3(format.styles, (value2, name3) => {
          const newValue = removeVars(value2);
          if (newValue) {
            dom.setStyle(previewElm, name3, newValue);
          }
        });
        each$3(format.attributes, (value2, name3) => {
          const newValue = removeVars(value2);
          if (newValue) {
            dom.setAttrib(previewElm, name3, newValue);
          }
        });
        each$3(format.classes, (value2) => {
          const newValue = removeVars(value2);
          if (!dom.hasClass(previewElm, newValue)) {
            dom.addClass(previewElm, newValue);
          }
        });
        editor.dispatch("PreviewFormats");
        dom.setStyles(previewFrag, { position: "absolute", left: -65535 });
        editor.getBody().appendChild(previewFrag);
        const rawParentFontSize = getComputedStyle("fontSize");
        const parentFontSize = /px$/.test(rawParentFontSize) ? parseInt(rawParentFontSize, 10) : 0;
        each$3(previewStyles.split(" "), (name3) => {
          let value2 = getComputedStyle(name3, previewElm);
          if (name3 === "background-color" && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value2)) {
            value2 = getComputedStyle(name3);
            if (rgbaToHexString(value2).toLowerCase() === "#ffffff") {
              return;
            }
          }
          if (name3 === "color") {
            if (rgbaToHexString(value2).toLowerCase() === "#000000") {
              return;
            }
          }
          if (name3 === "font-size") {
            if (/em|%$/.test(value2)) {
              if (parentFontSize === 0) {
                return;
              }
              const numValue = parseFloat(value2) / (/%$/.test(value2) ? 100 : 1);
              value2 = numValue * parentFontSize + "px";
            }
          }
          if (name3 === "border" && value2) {
            previewCss += "padding:0 2px;";
          }
          previewCss += name3 + ":" + value2 + ";";
        });
        editor.dispatch("AfterPreviewFormats");
        dom.remove(previewFrag);
        return previewCss;
      };
      const setup$x = (editor) => {
        editor.addShortcut("meta+b", "", "Bold");
        editor.addShortcut("meta+i", "", "Italic");
        editor.addShortcut("meta+u", "", "Underline");
        for (let i = 1; i <= 6; i++) {
          editor.addShortcut("access+" + i, "", ["FormatBlock", false, "h" + i]);
        }
        editor.addShortcut("access+7", "", ["FormatBlock", false, "p"]);
        editor.addShortcut("access+8", "", ["FormatBlock", false, "div"]);
        editor.addShortcut("access+9", "", ["FormatBlock", false, "address"]);
      };
      const Formatter = (editor) => {
        const formats = FormatRegistry(editor);
        const formatChangeState = Cell({});
        setup$x(editor);
        setup$B(editor);
        if (!isRtc(editor)) {
          setup$A(formatChangeState, editor);
        }
        return {
          /**
           * Returns the format by name or all formats if no name is specified.
           *
           * @method get
           * @param {String} name Optional name to retrieve by.
           * @return {Array/Object} Array/Object with all registered formats or a specific format.
           */
          get: formats.get,
          /**
           * Returns true or false if a format is registered for the specified name.
           *
           * @method has
           * @param {String} name Format name to check if a format exists.
           * @return {Boolean} True/False if a format for the specified name exists.
           */
          has: formats.has,
          /**
           * Registers a specific format by name.
           *
           * @method register
           * @param {Object/String} name Name of the format for example "bold".
           * @param {Object/Array} format Optional format object or array of format variants
           * can only be omitted if the first arg is an object.
           */
          register: formats.register,
          /**
           * Unregister a specific format by name.
           *
           * @method unregister
           * @param {String} name Name of the format for example "bold".
           */
          unregister: formats.unregister,
          /**
           * Applies the specified format to the current selection or specified node.
           *
           * @method apply
           * @param {String} name Name of format to apply.
           * @param {Object} vars Optional list of variables to replace within format before applying it.
           * @param {Node} node Optional node to apply the format to defaults to current selection.
           */
          apply: (name2, vars, node) => {
            applyFormat(editor, name2, vars, node);
          },
          /**
           * Removes the specified format from the current selection or specified node.
           *
           * @method remove
           * @param {String} name Name of format to remove.
           * @param {Object} vars Optional list of variables to replace within format before removing it.
           * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
           */
          remove: (name2, vars, node, similar) => {
            removeFormat(editor, name2, vars, node, similar);
          },
          /**
           * Toggles the specified format on/off.
           *
           * @method toggle
           * @param {String} name Name of format to apply/remove.
           * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
           * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
           */
          toggle: (name2, vars, node) => {
            toggleFormat(editor, name2, vars, node);
          },
          /**
           * Matches the current selection or specified node against the specified format name.
           *
           * @method match
           * @param {String} name Name of format to match.
           * @param {Object} vars Optional list of variables to replace before checking it.
           * @param {Node} node Optional node to check.
           * @param {Boolean} similar Optional argument to specify that similar formats should be checked instead of only exact formats.
           * @return {Boolean} true/false if the specified selection/node matches the format.
           */
          match: (name2, vars, node, similar) => matchFormat(editor, name2, vars, node, similar),
          /**
           * Finds the closest matching format from a set of formats for the current selection.
           *
           * @method closest
           * @param {Array} names Format names to check for.
           * @return {String} The closest matching format name or null.
           */
          closest: (names) => closestFormat(editor, names),
          /**
           * Matches the current selection against the array of formats and returns a new array with matching formats.
           *
           * @method matchAll
           * @param {Array} names Name of format to match.
           * @param {Object} vars Optional list of variables to replace before checking it.
           * @return {Array} Array with matched formats.
           */
          matchAll: (names, vars) => matchAllFormats(editor, names, vars),
          /**
           * Return true/false if the specified node has the specified format.
           *
           * @method matchNode
           * @param {Node} node Node to check the format on.
           * @param {String} name Format name to check.
           * @param {Object} vars Optional list of variables to replace before checking it.
           * @param {Boolean} similar Match format that has similar properties.
           * @return {Object} Returns the format object it matches or undefined if it doesn't match.
           */
          matchNode: (node, name2, vars, similar) => matchNodeFormat(editor, node, name2, vars, similar),
          /**
           * Returns true/false if the specified format can be applied to the current selection or not. It
           * will currently only check the state for selector formats, it returns true on all other format types.
           *
           * @method canApply
           * @param {String} name Name of format to check.
           * @return {Boolean} true/false if the specified format can be applied to the current selection/node.
           */
          canApply: (name2) => canApplyFormat(editor, name2),
          /**
           * Executes the specified callback when the current selection matches the formats or not.
           *
           * @method formatChanged
           * @param {String} formats Comma separated list of formats to check for.
           * @param {Function} callback Callback with state and args when the format is changed/toggled on/off.
           * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
           * @param {Object} vars Restrict the format being watched to only match if the variables applied are equal to vars.
           */
          formatChanged: (formats2, callback, similar, vars) => formatChanged(editor, formatChangeState, formats2, callback, similar, vars),
          /**
           * Returns a preview css text for the specified format.
           *
           * @method getCssText
           * @param {String/Object} format Format to generate preview css text for.
           * @return {String} Css text for the specified format.
           * @example
           * const cssText1 = editor.formatter.getCssText('bold');
           * const cssText2 = editor.formatter.getCssText({ inline: 'b' });
           */
          getCssText: curry(getCssText, editor)
        };
      };
      const shouldIgnoreCommand = (cmd) => {
        switch (cmd.toLowerCase()) {
          case "undo":
          case "redo":
          case "mcefocus":
            return true;
          default:
            return false;
        }
      };
      const registerEvents = (editor, undoManager, locks) => {
        const isFirstTypedCharacter = Cell(false);
        const addNonTypingUndoLevel = (e) => {
          setTyping(undoManager, false, locks);
          undoManager.add({}, e);
        };
        editor.on("init", () => {
          undoManager.add();
        });
        editor.on("BeforeExecCommand", (e) => {
          const cmd = e.command;
          if (!shouldIgnoreCommand(cmd)) {
            endTyping(undoManager, locks);
            undoManager.beforeChange();
          }
        });
        editor.on("ExecCommand", (e) => {
          const cmd = e.command;
          if (!shouldIgnoreCommand(cmd)) {
            addNonTypingUndoLevel(e);
          }
        });
        editor.on("ObjectResizeStart cut", () => {
          undoManager.beforeChange();
        });
        editor.on("SaveContent ObjectResized blur", addNonTypingUndoLevel);
        editor.on("dragend", addNonTypingUndoLevel);
        editor.on("keyup", (e) => {
          const keyCode = e.keyCode;
          if (e.isDefaultPrevented()) {
            return;
          }
          const isMeta = Env.os.isMacOS() && e.key === "Meta";
          if (keyCode >= 33 && keyCode <= 36 || keyCode >= 37 && keyCode <= 40 || keyCode === 45 || e.ctrlKey || isMeta) {
            addNonTypingUndoLevel();
            editor.nodeChanged();
          }
          if (keyCode === 46 || keyCode === 8) {
            editor.nodeChanged();
          }
          if (isFirstTypedCharacter.get() && undoManager.typing && !isEq$1(createFromEditor(editor), undoManager.data[0])) {
            if (!editor.isDirty()) {
              editor.setDirty(true);
            }
            editor.dispatch("TypingUndo");
            isFirstTypedCharacter.set(false);
            editor.nodeChanged();
          }
        });
        editor.on("keydown", (e) => {
          const keyCode = e.keyCode;
          if (e.isDefaultPrevented()) {
            return;
          }
          if (keyCode >= 33 && keyCode <= 36 || keyCode >= 37 && keyCode <= 40 || keyCode === 45) {
            if (undoManager.typing) {
              addNonTypingUndoLevel(e);
            }
            return;
          }
          const modKey = e.ctrlKey && !e.altKey || e.metaKey;
          if ((keyCode < 16 || keyCode > 20) && keyCode !== 224 && keyCode !== 91 && !undoManager.typing && !modKey) {
            undoManager.beforeChange();
            setTyping(undoManager, true, locks);
            undoManager.add({}, e);
            isFirstTypedCharacter.set(true);
            return;
          }
          const hasOnlyMetaOrCtrlModifier = Env.os.isMacOS() ? e.metaKey : e.ctrlKey && !e.altKey;
          if (hasOnlyMetaOrCtrlModifier) {
            undoManager.beforeChange();
          }
        });
        editor.on("mousedown", (e) => {
          if (undoManager.typing) {
            addNonTypingUndoLevel(e);
          }
        });
        const isInsertReplacementText = (event) => event.inputType === "insertReplacementText";
        const isInsertTextDataNull = (event) => event.inputType === "insertText" && event.data === null;
        const isInsertFromPasteOrDrop = (event) => event.inputType === "insertFromPaste" || event.inputType === "insertFromDrop";
        editor.on("input", (e) => {
          if (e.inputType && (isInsertReplacementText(e) || isInsertTextDataNull(e) || isInsertFromPasteOrDrop(e))) {
            addNonTypingUndoLevel(e);
          }
        });
        editor.on("AddUndo Undo Redo ClearUndos", (e) => {
          if (!e.isDefaultPrevented()) {
            editor.nodeChanged();
          }
        });
      };
      const addKeyboardShortcuts = (editor) => {
        editor.addShortcut("meta+z", "", "Undo");
        editor.addShortcut("meta+y,meta+shift+z", "", "Redo");
      };
      const UndoManager = (editor) => {
        const beforeBookmark = value$1();
        const locks = Cell(0);
        const index = Cell(0);
        const undoManager = {
          data: [],
          // Gets mutated both internally and externally by plugins like remark, not documented
          /**
           * State if the user is currently typing or not. This will add a typing operation into one undo
           * level instead of one new level for each keystroke.
           *
           * @field {Boolean} typing
           */
          typing: false,
          /**
           * Stores away a bookmark to be used when performing an undo action so that the selection is before
           * the change has been made.
           *
           * @method beforeChange
           */
          beforeChange: () => {
            beforeChange(editor, locks, beforeBookmark);
          },
          /**
           * Adds a new undo level/snapshot to the undo list.
           *
           * @method add
           * @param {Object} level Optional undo level object to add.
           * @param {EditorEvent} event Optional event responsible for the creation of the undo level.
           * @return {Object} Undo level that got added or null if a level wasn't needed.
           */
          add: (level, event) => {
            return addUndoLevel(editor, undoManager, index, locks, beforeBookmark, level, event);
          },
          /**
           * Dispatch a change event with current editor status as level and current undoManager layer as lastLevel
           *
           * @method dispatchChange
           */
          dispatchChange: () => {
            editor.setDirty(true);
            const level = createFromEditor(editor);
            level.bookmark = getUndoBookmark(editor.selection);
            editor.dispatch("change", {
              level,
              lastLevel: get$b(undoManager.data, index.get()).getOrUndefined()
            });
          },
          /**
           * Undoes the last action.
           *
           * @method undo
           * @return {Object} Undo level or null if no undo was performed.
           */
          undo: () => {
            return undo(editor, undoManager, locks, index);
          },
          /**
           * Redoes the last action.
           *
           * @method redo
           * @return {Object} Redo level or null if no redo was performed.
           */
          redo: () => {
            return redo(editor, index, undoManager.data);
          },
          /**
           * Removes all undo levels.
           *
           * @method clear
           */
          clear: () => {
            clear(editor, undoManager, index);
          },
          /**
           * Resets the undo manager levels by clearing all levels and then adding an initial level.
           *
           * @method reset
           */
          reset: () => {
            reset(editor, undoManager);
          },
          /**
           * Returns true/false if the undo manager has any undo levels.
           *
           * @method hasUndo
           * @return {Boolean} true/false if the undo manager has any undo levels.
           */
          hasUndo: () => {
            return hasUndo(editor, undoManager, index);
          },
          /**
           * Returns true/false if the undo manager has any redo levels.
           *
           * @method hasRedo
           * @return {Boolean} true/false if the undo manager has any redo levels.
           */
          hasRedo: () => {
            return hasRedo(editor, undoManager, index);
          },
          /**
           * Executes the specified mutator function as an undo transaction. The selection
           * before the modification will be stored to the undo stack and if the DOM changes
           * it will add a new undo level. Any logic within the translation that adds undo levels will
           * be ignored. So a translation can include calls to execCommand or editor.insertContent.
           *
           * @method transact
           * @param {Function} callback Function that gets executed and has dom manipulation logic in it.
           * @return {Object} Undo level that got added or null it a level wasn't needed.
           */
          transact: (callback) => {
            return transact(editor, undoManager, locks, callback);
          },
          /**
           * Executes the specified mutator function as an undo transaction. But without adding an undo level.
           * Any logic within the translation that adds undo levels will be ignored. So a translation can
           * include calls to execCommand or editor.insertContent.
           *
           * @method ignore
           * @param {Function} callback Function that gets executed and has dom manipulation logic in it.
           */
          ignore: (callback) => {
            ignore(editor, locks, callback);
          },
          /**
           * Adds an extra "hidden" undo level by first applying the first mutation and store that to the undo stack
           * then roll back that change and do the second mutation on top of the stack. This will produce an extra
           * undo level that the user doesn't see until they undo.
           *
           * @method extra
           * @param {Function} callback1 Function that does mutation but gets stored as a "hidden" extra undo level.
           * @param {Function} callback2 Function that does mutation but gets displayed to the user.
           */
          extra: (callback1, callback2) => {
            extra(editor, undoManager, index, callback1, callback2);
          }
        };
        if (!isRtc(editor)) {
          registerEvents(editor, undoManager, locks);
        }
        addKeyboardShortcuts(editor);
        return undoManager;
      };
      const nonTypingKeycodes = [
        // tab, esc, home, end
        9,
        27,
        VK.HOME,
        VK.END,
        // pause, capslock, print screen, numlock, scroll lock
        19,
        20,
        44,
        144,
        145,
        // page up/down, insert
        33,
        34,
        45,
        // alt, shift, ctrl
        16,
        17,
        18,
        // meta/windows key
        91,
        92,
        93,
        // direction
        VK.DOWN,
        VK.UP,
        VK.LEFT,
        VK.RIGHT
      ].concat(
        // Meta key on firefox is different
        Env.browser.isFirefox() ? [224] : []
      );
      const placeholderAttr = "data-mce-placeholder";
      const isKeyboardEvent = (e) => e.type === "keydown" || e.type === "keyup";
      const isDeleteEvent = (e) => {
        const keyCode = e.keyCode;
        return keyCode === VK.BACKSPACE || keyCode === VK.DELETE;
      };
      const isNonTypingKeyboardEvent = (e) => {
        if (isKeyboardEvent(e)) {
          const keyCode = e.keyCode;
          return !isDeleteEvent(e) && (VK.metaKeyPressed(e) || e.altKey || keyCode >= 112 && keyCode <= 123 || contains$2(nonTypingKeycodes, keyCode));
        } else {
          return false;
        }
      };
      const isTypingKeyboardEvent = (e) => (
        // 229 === Unidentified, so since we don't know what it is treat it as a non typing event on keyup but as a typing event on keydown
        // Android will generally always send a 229 keycode since it uses an IME to input text
        isKeyboardEvent(e) && !(isDeleteEvent(e) || e.type === "keyup" && e.keyCode === 229)
      );
      const isVisuallyEmpty = (dom2, rootElm, forcedRootBlock) => {
        if (dom2.isEmpty(rootElm, void 0, { skipBogus: false, includeZwsp: true })) {
          const firstElement2 = rootElm.firstElementChild;
          if (!firstElement2) {
            return true;
          } else if (dom2.getStyle(rootElm.firstElementChild, "padding-left") || dom2.getStyle(rootElm.firstElementChild, "padding-right")) {
            return false;
          } else {
            return forcedRootBlock === firstElement2.nodeName.toLowerCase();
          }
        } else {
          return false;
        }
      };
      const setup$w = (editor) => {
        const dom2 = editor.dom;
        const rootBlock = getForcedRootBlock(editor);
        const placeholder = getPlaceholder(editor) ?? "";
        const updatePlaceholder = (e, initial) => {
          if (isNonTypingKeyboardEvent(e)) {
            return;
          }
          const body = editor.getBody();
          const showPlaceholder = isTypingKeyboardEvent(e) ? false : isVisuallyEmpty(dom2, body, rootBlock);
          const isPlaceholderShown = dom2.getAttrib(body, placeholderAttr) !== "";
          if (isPlaceholderShown !== showPlaceholder || initial) {
            dom2.setAttrib(body, placeholderAttr, showPlaceholder ? placeholder : null);
            firePlaceholderToggle(editor, showPlaceholder);
            editor.on(showPlaceholder ? "keydown" : "keyup", updatePlaceholder);
            editor.off(showPlaceholder ? "keyup" : "keydown", updatePlaceholder);
          }
        };
        if (isNotEmpty(placeholder)) {
          editor.on("init", (e) => {
            updatePlaceholder(e, true);
            editor.on("change SetContent ExecCommand", updatePlaceholder);
            editor.on("paste", (e2) => Delay.setEditorTimeout(editor, () => updatePlaceholder(e2)));
          });
        }
      };
      const matchNodeName = (name2) => (node) => isNonNullable(node) && node.nodeName.toLowerCase() === name2;
      const matchNodeNames = (regex) => (node) => isNonNullable(node) && regex.test(node.nodeName);
      const isTextNode$1 = (node) => isNonNullable(node) && node.nodeType === 3;
      const isElement$1 = (node) => isNonNullable(node) && node.nodeType === 1;
      const isListNode = matchNodeNames(/^(OL|UL|DL)$/);
      const isOlUlNode = matchNodeNames(/^(OL|UL)$/);
      const isListItemNode = matchNodeNames(/^(LI|DT|DD)$/);
      const isDlItemNode = matchNodeNames(/^(DT|DD)$/);
      const isBr$1 = matchNodeName("br");
      const isFirstChild$1 = (node) => node.parentNode?.firstChild === node;
      const isTextBlock = (editor, node) => isNonNullable(node) && node.nodeName in editor.schema.getTextBlockElements();
      const isBlock = (node, blockElements) => isNonNullable(node) && node.nodeName in blockElements;
      const isVoid = (editor, node) => isNonNullable(node) && node.nodeName in editor.schema.getVoidElements();
      const isBogusBr = (dom2, node) => {
        if (!isBr$1(node)) {
          return false;
        }
        return dom2.isBlock(node.nextSibling) && !isBr$1(node.previousSibling);
      };
      const isEmpty$1 = (dom2, elm, keepBookmarks) => {
        const empty2 = dom2.isEmpty(elm);
        if (keepBookmarks && dom2.select("span[data-mce-type=bookmark]", elm).length > 0) {
          return false;
        }
        return empty2;
      };
      const isChildOfBody = (dom2, elm) => dom2.isChildOf(elm, dom2.getRoot());
      const DOM$9 = DOMUtils.DOM;
      const normalizeList = (dom2, list) => {
        const parentNode = list.parentElement;
        if (parentNode && parentNode.nodeName === "LI" && parentNode.firstChild === list) {
          const sibling2 = parentNode.previousSibling;
          if (sibling2 && sibling2.nodeName === "LI") {
            sibling2.appendChild(list);
            if (isEmpty$1(dom2, parentNode)) {
              DOM$9.remove(parentNode);
            }
          } else {
            DOM$9.setStyle(parentNode, "listStyleType", "none");
          }
        }
        if (isListNode(parentNode)) {
          const sibling2 = parentNode.previousSibling;
          if (sibling2 && sibling2.nodeName === "LI") {
            sibling2.appendChild(list);
          }
        }
      };
      const normalizeLists = (dom2, element) => {
        const lists2 = Tools.grep(dom2.select("ol,ul", element));
        Tools.each(lists2, (list) => {
          normalizeList(dom2, list);
        });
      };
      const getNormalizedPoint = (container, offset) => {
        if (isTextNode$1(container)) {
          return { container, offset };
        }
        const node = RangeUtils.getNode(container, offset);
        if (isTextNode$1(node)) {
          return {
            container: node,
            offset: offset >= container.childNodes.length ? node.data.length : 0
          };
        } else if (node.previousSibling && isTextNode$1(node.previousSibling)) {
          return {
            container: node.previousSibling,
            offset: node.previousSibling.data.length
          };
        } else if (node.nextSibling && isTextNode$1(node.nextSibling)) {
          return {
            container: node.nextSibling,
            offset: 0
          };
        }
        return { container, offset };
      };
      const normalizeRange = (rng) => {
        const outRng = rng.cloneRange();
        const rangeStart = getNormalizedPoint(rng.startContainer, rng.startOffset);
        outRng.setStart(rangeStart.container, rangeStart.offset);
        const rangeEnd = getNormalizedPoint(rng.endContainer, rng.endOffset);
        outRng.setEnd(rangeEnd.container, rangeEnd.offset);
        return outRng;
      };
      const listNames = ["OL", "UL", "DL"];
      const listSelector = listNames.join(",");
      const getParentList = (editor, node) => {
        const selectionStart = node || editor.selection.getStart(true);
        return editor.dom.getParent(selectionStart, listSelector, getClosestListHost(editor, selectionStart));
      };
      const isParentListSelected = (parentList, selectedBlocks) => isNonNullable(parentList) && selectedBlocks.length === 1 && selectedBlocks[0] === parentList;
      const findSubLists = (parentList) => filter$5(parentList.querySelectorAll(listSelector), isListNode);
      const getSelectedSubLists = (editor) => {
        const parentList = getParentList(editor);
        const selectedBlocks = editor.selection.getSelectedBlocks();
        if (isParentListSelected(parentList, selectedBlocks)) {
          return findSubLists(parentList);
        } else {
          return filter$5(selectedBlocks, (elm) => {
            return isListNode(elm) && parentList !== elm;
          });
        }
      };
      const findParentListItemsNodes = (editor, elms) => {
        const listItemsElms = Tools.map(elms, (elm) => {
          const parentLi = editor.dom.getParent(elm, "li,dd,dt", getClosestListHost(editor, elm));
          return parentLi ? parentLi : elm;
        });
        return unique$1(listItemsElms);
      };
      const getSelectedListItems = (editor) => {
        const selectedBlocks = editor.selection.getSelectedBlocks();
        return filter$5(findParentListItemsNodes(editor, selectedBlocks), isListItemNode);
      };
      const getSelectedDlItems = (editor) => filter$5(getSelectedListItems(editor), isDlItemNode);
      const getClosestEditingHost = (editor, elm) => {
        const parentTableCell = editor.dom.getParents(elm, "TD,TH");
        return parentTableCell.length > 0 ? parentTableCell[0] : editor.getBody();
      };
      const isListHost = (schema, node) => !isListNode(node) && !isListItemNode(node) && exists(listNames, (listName) => schema.isValidChild(node.nodeName, listName));
      const getClosestListHost = (editor, elm) => {
        const parentBlocks = editor.dom.getParents(elm, editor.dom.isBlock);
        const isNotForcedRootBlock = (elm2) => elm2.nodeName.toLowerCase() !== getForcedRootBlock(editor);
        const parentBlock = find$2(parentBlocks, (elm2) => isNotForcedRootBlock(elm2) && isListHost(editor.schema, elm2));
        return parentBlock.getOr(editor.getBody());
      };
      const isListInsideAnLiWithFirstAndLastNotListElement = (list) => parent(list).exists((parent2) => isListItemNode(parent2.dom) && firstChild(parent2).exists((firstChild2) => !isListNode(firstChild2.dom)) && lastChild(parent2).exists((lastChild2) => !isListNode(lastChild2.dom)));
      const findLastParentListNode = (editor, elm) => {
        const parentLists = editor.dom.getParents(elm, "ol,ul", getClosestListHost(editor, elm));
        return last$2(parentLists);
      };
      const getSelectedLists = (editor) => {
        const firstList = findLastParentListNode(editor, editor.selection.getStart());
        const subsequentLists = filter$5(editor.selection.getSelectedBlocks(), isOlUlNode);
        return firstList.toArray().concat(subsequentLists);
      };
      const getParentLists = (editor) => {
        const elm = editor.selection.getStart();
        return editor.dom.getParents(elm, "ol,ul", getClosestListHost(editor, elm));
      };
      const getSelectedListRoots = (editor) => {
        const selectedLists = getSelectedLists(editor);
        const parentLists = getParentLists(editor);
        return find$2(parentLists, (p) => isListInsideAnLiWithFirstAndLastNotListElement(SugarElement.fromDom(p))).fold(() => getUniqueListRoots(editor, selectedLists), (l) => [l]);
      };
      const getUniqueListRoots = (editor, lists2) => {
        const listRoots = map$3(lists2, (list) => findLastParentListNode(editor, list).getOr(list));
        return unique$1(listRoots);
      };
      const isCustomList = (list) => /\btox\-/.test(list.className);
      const isWithinNonEditable = (editor, element) => element !== null && !editor.dom.isEditable(element);
      const selectionIsWithinNonEditableList = (editor) => {
        const parentList = getParentList(editor);
        return isWithinNonEditable(editor, parentList) || !editor.selection.isEditable();
      };
      const isWithinNonEditableList$1 = (editor, element) => {
        const parentList = editor.dom.getParent(element, "ol,ul,dl");
        return isWithinNonEditable(editor, parentList) || !editor.selection.isEditable();
      };
      const fireListEvent = (editor, action2, element) => editor.dispatch("ListMutation", { action: action2, element });
      const createTextBlock$1 = (editor, contentNode, attrs = {}) => {
        const dom2 = editor.dom;
        const blockElements = editor.schema.getBlockElements();
        const fragment = dom2.createFragment();
        const blockName = getForcedRootBlock(editor);
        const blockAttrs = getForcedRootBlockAttrs(editor);
        let node;
        let textBlock;
        let hasContentNode = false;
        textBlock = dom2.create(blockName, {
          ...blockAttrs,
          ...attrs.style ? { style: attrs.style } : {}
        });
        if (!isBlock(contentNode.firstChild, blockElements)) {
          fragment.appendChild(textBlock);
        }
        while (node = contentNode.firstChild) {
          const nodeName = node.nodeName;
          if (!hasContentNode && (nodeName !== "SPAN" || node.getAttribute("data-mce-type") !== "bookmark")) {
            hasContentNode = true;
          }
          if (isBlock(node, blockElements)) {
            fragment.appendChild(node);
            textBlock = null;
          } else {
            if (!textBlock) {
              textBlock = dom2.create(blockName, blockAttrs);
              fragment.appendChild(textBlock);
            }
            textBlock.appendChild(node);
          }
        }
        if (!hasContentNode && textBlock) {
          textBlock.appendChild(dom2.create("br", { "data-mce-bogus": "1" }));
        }
        return fragment;
      };
      const isList = (el) => is$1(el, "OL,UL");
      const isListItem$1 = (el) => is$1(el, "LI");
      const hasFirstChildList = (el) => firstChild(el).exists(isList);
      const hasLastChildList = (el) => lastChild(el).exists(isList);
      const canIncreaseDepthOfList = (editor, amount) => {
        return getListMaxDepth(editor).map((max2) => max2 >= amount).getOr(true);
      };
      const isEntryList = (entry) => "listAttributes" in entry;
      const isEntryComment = (entry) => "isComment" in entry;
      const isEntryFragment = (entry) => "isFragment" in entry;
      const isIndented = (entry) => entry.depth > 0;
      const isSelected = (entry) => entry.isSelected;
      const cloneItemContent = (li) => {
        const children2 = children$1(li);
        const content = hasLastChildList(li) ? children2.slice(0, -1) : children2;
        return map$3(content, deep);
      };
      const createEntry = (li, depth, isSelected2) => parent(li).filter(isElement$8).map((list) => ({
        depth,
        dirty: false,
        isSelected: isSelected2,
        content: cloneItemContent(li),
        itemAttributes: clone$4(li),
        listAttributes: clone$4(list),
        listType: name(list),
        isInPreviousLi: false
      }));
      const joinSegment = (parent2, child2) => {
        append$1(parent2.item, child2.list);
      };
      const joinSegments = (segments) => {
        for (let i = 1; i < segments.length; i++) {
          joinSegment(segments[i - 1], segments[i]);
        }
      };
      const appendSegments = (head$1, tail) => {
        lift2(last$2(head$1), head(tail), joinSegment);
      };
      const createSegment = (scope, listType) => {
        const segment = {
          list: SugarElement.fromTag(listType, scope),
          item: SugarElement.fromTag("li", scope)
        };
        append$1(segment.list, segment.item);
        return segment;
      };
      const createSegments = (scope, entry, size) => {
        const segments = [];
        for (let i = 0; i < size; i++) {
          segments.push(createSegment(scope, isEntryList(entry) ? entry.listType : entry.parentListType));
        }
        return segments;
      };
      const populateSegments = (segments, entry) => {
        for (let i = 0; i < segments.length - 1; i++) {
          set$2(segments[i].item, "list-style-type", "none");
        }
        last$2(segments).each((segment) => {
          if (isEntryList(entry)) {
            setAll$1(segment.list, entry.listAttributes);
            setAll$1(segment.item, entry.itemAttributes);
          }
          append(segment.item, entry.content);
        });
      };
      const normalizeSegment = (segment, entry) => {
        if (name(segment.list) !== entry.listType) {
          segment.list = mutate(segment.list, entry.listType);
        }
        setAll$1(segment.list, entry.listAttributes);
      };
      const createItem = (scope, attr, content) => {
        const item = SugarElement.fromTag("li", scope);
        setAll$1(item, attr);
        append(item, content);
        return item;
      };
      const appendItem = (segment, item) => {
        append$1(segment.list, item);
        segment.item = item;
      };
      const writeShallow = (scope, cast, entry) => {
        const newCast = cast.slice(0, entry.depth);
        last$2(newCast).each((segment) => {
          if (isEntryList(entry)) {
            const item = createItem(scope, entry.itemAttributes, entry.content);
            appendItem(segment, item);
            normalizeSegment(segment, entry);
          } else if (isEntryFragment(entry)) {
            append(segment.item, entry.content);
          } else {
            const item = SugarElement.fromHtml(`<!--${entry.content}-->`);
            append$1(segment.list, item);
          }
        });
        return newCast;
      };
      const writeDeep = (scope, cast, entry) => {
        const segments = createSegments(scope, entry, entry.depth - cast.length);
        joinSegments(segments);
        populateSegments(segments, entry);
        appendSegments(cast, segments);
        return cast.concat(segments);
      };
      const composeList = (scope, entries2) => {
        let firstCommentEntryOpt = Optional.none();
        const cast = foldl(entries2, (cast2, entry, i) => {
          if (!isEntryComment(entry)) {
            return entry.depth > cast2.length ? writeDeep(scope, cast2, entry) : writeShallow(scope, cast2, entry);
          } else {
            if (i === 0) {
              firstCommentEntryOpt = Optional.some(entry);
              return cast2;
            }
            return writeShallow(scope, cast2, entry);
          }
        }, []);
        firstCommentEntryOpt.each((firstCommentEntry) => {
          const item = SugarElement.fromHtml(`<!--${firstCommentEntry.content}-->`);
          head(cast).each((fistCast) => {
            prepend(fistCast.list, item);
          });
        });
        return head(cast).map((segment) => segment.list);
      };
      const indentEntry = (editor, indentation, entry) => {
        switch (indentation) {
          case "Indent":
            if (canIncreaseDepthOfList(editor, entry.depth)) {
              entry.depth++;
            } else {
              return;
            }
            break;
          case "Outdent":
            entry.depth--;
            break;
          case "Flatten":
            entry.depth = 0;
        }
        entry.dirty = true;
      };
      const cloneListProperties = (target, source) => {
        if (isEntryList(target) && isEntryList(source)) {
          target.listType = source.listType;
          target.listAttributes = { ...source.listAttributes };
        }
      };
      const cleanListProperties = (entry) => {
        entry.listAttributes = filter$4(entry.listAttributes, (_value, key) => key !== "start");
      };
      const closestSiblingEntry = (entries2, start2) => {
        const depth = entries2[start2].depth;
        const matches = (entry) => entry.depth === depth && !entry.dirty;
        const until = (entry) => entry.depth < depth;
        return findUntil$1(reverse(entries2.slice(0, start2)), matches, until).orThunk(() => findUntil$1(entries2.slice(start2 + 1), matches, until));
      };
      const normalizeEntries = (entries2) => {
        each$e(entries2, (entry, i) => {
          closestSiblingEntry(entries2, i).fold(() => {
            if (entry.dirty && isEntryList(entry)) {
              cleanListProperties(entry);
            }
          }, (matchingEntry) => cloneListProperties(entry, matchingEntry));
        });
        return entries2;
      };
      const parseSingleItem = (depth, itemSelection, selectionState, item) => {
        if (isComment$1(item)) {
          return [{
            depth: depth + 1,
            content: item.dom.nodeValue ?? "",
            dirty: false,
            isSelected: false,
            isComment: true
          }];
        }
        itemSelection.each((selection) => {
          if (eq(selection.start, item)) {
            selectionState.set(true);
          }
        });
        const currentItemEntry = createEntry(item, depth, selectionState.get());
        itemSelection.each((selection) => {
          if (eq(selection.end, item)) {
            selectionState.set(false);
          }
        });
        const childListEntries = lastChild(item).filter(isList).map((list) => parseList(depth, itemSelection, selectionState, list)).getOr([]);
        return currentItemEntry.toArray().concat(childListEntries);
      };
      const parseItem = (depth, itemSelection, selectionState, item) => firstChild(item).filter(isList).fold(() => parseSingleItem(depth, itemSelection, selectionState, item), (list) => {
        const parsedSiblings = foldl(children$1(item), (acc, liChild, i) => {
          if (i === 0) {
            return acc;
          } else {
            if (isListItem$1(liChild)) {
              return acc.concat(parseSingleItem(depth, itemSelection, selectionState, liChild));
            } else {
              const fragment = {
                isFragment: true,
                depth,
                content: [liChild],
                isSelected: false,
                dirty: false,
                parentListType: name(list)
              };
              return acc.concat(fragment);
            }
          }
        }, []);
        return parseList(depth, itemSelection, selectionState, list).concat(parsedSiblings);
      });
      const parseList = (depth, itemSelection, selectionState, list) => bind$3(children$1(list), (element) => {
        const parser = isList(element) ? parseList : parseItem;
        const newDepth = depth + 1;
        return parser(newDepth, itemSelection, selectionState, element);
      });
      const parseLists = (lists2, itemSelection) => {
        const selectionState = Cell(false);
        const initialDepth = 0;
        return map$3(lists2, (list) => ({
          sourceList: list,
          entries: parseList(initialDepth, itemSelection, selectionState, list)
        }));
      };
      const outdentedComposer = (editor, entries2) => {
        const normalizedEntries = normalizeEntries(entries2);
        return map$3(normalizedEntries, (entry) => {
          const content = !isEntryComment(entry) ? fromElements(entry.content) : fromElements([SugarElement.fromHtml(`<!--${entry.content}-->`)]);
          const listItemAttrs = isEntryList(entry) ? entry.itemAttributes : {};
          return SugarElement.fromDom(createTextBlock$1(editor, content.dom, listItemAttrs));
        });
      };
      const indentedComposer = (editor, entries2) => {
        const normalizedEntries = normalizeEntries(entries2);
        return composeList(editor.contentDocument, normalizedEntries).toArray();
      };
      const composeEntries = (editor, entries2) => bind$3(groupBy(entries2, isIndented), (entries3) => {
        const groupIsIndented = head(entries3).exists(isIndented);
        return groupIsIndented ? indentedComposer(editor, entries3) : outdentedComposer(editor, entries3);
      });
      const indentSelectedEntries = (editor, entries2, indentation) => {
        each$e(filter$5(entries2, isSelected), (entry) => indentEntry(editor, indentation, entry));
      };
      const getItemSelection = (editor) => {
        const selectedListItems = map$3(getSelectedListItems(editor), SugarElement.fromDom);
        return lift2(find$2(selectedListItems, not(hasFirstChildList)), find$2(reverse(selectedListItems), not(hasFirstChildList)), (start2, end2) => ({ start: start2, end: end2 }));
      };
      const listIndentation = (editor, lists2, indentation) => {
        const entrySets = parseLists(lists2, getItemSelection(editor));
        each$e(entrySets, (entrySet) => {
          indentSelectedEntries(editor, entrySet.entries, indentation);
          const composedLists = composeEntries(editor, entrySet.entries);
          each$e(composedLists, (composedList) => {
            fireListEvent(editor, indentation === "Indent" ? "IndentList" : "OutdentList", composedList.dom);
          });
          before$3(entrySet.sourceList, composedLists);
          remove$8(entrySet.sourceList);
        });
      };
      const canIndent$1 = (editor) => getListMaxDepth(editor).forall((max2) => {
        const blocks = editor.selection.getSelectedBlocks();
        return exists(blocks, (element) => {
          return closest$3(SugarElement.fromDom(element), "li").forall((sugarElement) => ancestors(sugarElement, "ol,ul").length <= max2);
        });
      });
      const DOM$8 = DOMUtils.DOM;
      const splitList = (editor, list, li) => {
        const removeAndKeepBookmarks = (targetNode) => {
          const parent3 = targetNode.parentNode;
          if (parent3) {
            Tools.each(bookmarks, (node) => {
              parent3.insertBefore(node, li.parentNode);
            });
          }
          DOM$8.remove(targetNode);
        };
        const bookmarks = DOM$8.select('span[data-mce-type="bookmark"]', list);
        const newBlock = createTextBlock$1(editor, li);
        const tmpRng = DOM$8.createRng();
        tmpRng.setStartAfter(li);
        tmpRng.setEndAfter(list);
        const fragment = tmpRng.extractContents();
        for (let node = fragment.firstChild; node; node = node.firstChild) {
          if (node.nodeName === "LI" && editor.dom.isEmpty(node)) {
            DOM$8.remove(node);
            break;
          }
        }
        if (!editor.dom.isEmpty(fragment)) {
          DOM$8.insertAfter(fragment, list);
        }
        DOM$8.insertAfter(newBlock, list);
        const parent2 = li.parentElement;
        if (parent2 && isEmpty$1(editor.dom, parent2)) {
          removeAndKeepBookmarks(parent2);
        }
        DOM$8.remove(li);
        if (isEmpty$1(editor.dom, list)) {
          DOM$8.remove(list);
        }
      };
      const isDescriptionDetail = isTag("dd");
      const isDescriptionTerm = isTag("dt");
      const outdentDlItem = (editor, item) => {
        if (isDescriptionDetail(item)) {
          mutate(item, "dt");
        } else if (isDescriptionTerm(item)) {
          parentElement(item).each((dl) => splitList(editor, dl.dom, item.dom));
        }
      };
      const indentDlItem = (item) => {
        if (isDescriptionTerm(item)) {
          mutate(item, "dd");
        }
      };
      const dlIndentation = (editor, indentation, dlItems) => {
        if (indentation === "Indent") {
          each$e(dlItems, indentDlItem);
        } else {
          each$e(dlItems, (item) => outdentDlItem(editor, item));
        }
      };
      const selectionIndentation = (editor, indentation) => {
        const lists2 = fromDom$1(getSelectedListRoots(editor));
        const dlItems = fromDom$1(getSelectedDlItems(editor));
        let isHandled = false;
        if (lists2.length || dlItems.length) {
          const bookmark = editor.selection.getBookmark();
          listIndentation(editor, lists2, indentation);
          dlIndentation(editor, indentation, dlItems);
          editor.selection.moveToBookmark(bookmark);
          editor.selection.setRng(normalizeRange(editor.selection.getRng()));
          editor.nodeChanged();
          isHandled = true;
        }
        return isHandled;
      };
      const handleIndentation = (editor, indentation) => !selectionIsWithinNonEditableList(editor) && selectionIndentation(editor, indentation);
      const indentListSelection = (editor) => handleIndentation(
        editor,
        "Indent"
        /* Indentation.Indent */
      );
      const outdentListSelection = (editor) => handleIndentation(
        editor,
        "Outdent"
        /* Indentation.Outdent */
      );
      const flattenListSelection = (editor) => handleIndentation(
        editor,
        "Flatten"
        /* Indentation.Flatten */
      );
      const listToggleActionFromListName = (listName) => {
        switch (listName) {
          case "UL":
            return "ToggleUlList";
          case "OL":
            return "ToggleOlList";
          case "DL":
            return "ToggleDLList";
        }
      };
      const updateListStyle = (dom2, el, detail) => {
        const type2 = detail["list-style-type"] ? detail["list-style-type"] : null;
        dom2.setStyle(el, "list-style-type", type2);
      };
      const setAttribs = (elm, attrs) => {
        Tools.each(attrs, (value2, key) => {
          elm.setAttribute(key, value2);
        });
      };
      const updateListAttrs = (dom2, el, detail) => {
        setAttribs(el, detail["list-attributes"]);
        Tools.each(dom2.select("li", el), (li) => {
          setAttribs(li, detail["list-item-attributes"]);
        });
      };
      const updateListWithDetails = (dom2, el, detail) => {
        updateListStyle(dom2, el, detail);
        updateListAttrs(dom2, el, detail);
      };
      const removeStyles = (dom2, element, styles) => {
        Tools.each(styles, (style) => dom2.setStyle(element, style, ""));
      };
      const isInline$1 = (editor, node) => isNonNullable(node) && !isBlock(node, editor.schema.getBlockElements());
      const getEndPointNode = (editor, rng, start2, root) => {
        let container = rng[start2 ? "startContainer" : "endContainer"];
        const offset = rng[start2 ? "startOffset" : "endOffset"];
        if (isElement$1(container)) {
          container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
        }
        if (!start2 && isBr$1(container.nextSibling)) {
          container = container.nextSibling;
        }
        const findBlockAncestor = (node) => {
          while (!editor.dom.isBlock(node) && node.parentNode && root !== node) {
            node = node.parentNode;
          }
          return node;
        };
        const findBetterContainer = (container2, forward) => {
          const walker = new DomTreeWalker(container2, findBlockAncestor(container2));
          const dir = forward ? "next" : "prev";
          let node;
          while (node = walker[dir]()) {
            if (!(isVoid(editor, node) || isZwsp$2(node.textContent) || node.textContent?.length === 0)) {
              return Optional.some(node);
            }
          }
          return Optional.none();
        };
        if (start2 && isTextNode$1(container)) {
          if (isZwsp$2(container.textContent)) {
            container = findBetterContainer(container, false).getOr(container);
          } else {
            if (container.parentNode !== null && isInline$1(editor, container.parentNode)) {
              container = container.parentNode;
            }
            while (container.previousSibling !== null && (isInline$1(editor, container.previousSibling) || isTextNode$1(container.previousSibling))) {
              container = container.previousSibling;
            }
          }
        }
        if (!start2 && isTextNode$1(container)) {
          if (isZwsp$2(container.textContent)) {
            container = findBetterContainer(container, true).getOr(container);
          } else {
            if (container.parentNode !== null && isInline$1(editor, container.parentNode)) {
              container = container.parentNode;
            }
            while (container.nextSibling !== null && (isInline$1(editor, container.nextSibling) || isTextNode$1(container.nextSibling))) {
              container = container.nextSibling;
            }
          }
        }
        while (container.parentNode !== root) {
          const parent2 = container.parentNode;
          if (isTextBlock(editor, container)) {
            return container;
          }
          if (/^(TD|TH)$/.test(parent2.nodeName)) {
            return container;
          }
          container = parent2;
        }
        return container;
      };
      const getSelectedTextBlocks = (editor, rng, root) => {
        const textBlocks2 = [];
        const dom2 = editor.dom;
        const startNode = getEndPointNode(editor, rng, true, root);
        const endNode = getEndPointNode(editor, rng, false, root);
        let block;
        const siblings2 = [];
        for (let node = startNode; node; node = node.nextSibling) {
          siblings2.push(node);
          if (node === endNode) {
            break;
          }
        }
        Tools.each(siblings2, (node) => {
          if (isTextBlock(editor, node)) {
            textBlocks2.push(node);
            block = null;
            return;
          }
          if (dom2.isBlock(node) || isBr$1(node)) {
            if (isBr$1(node)) {
              dom2.remove(node);
            }
            block = null;
            return;
          }
          const nextSibling2 = node.nextSibling;
          if (BookmarkManager.isBookmarkNode(node)) {
            if (isListNode(nextSibling2) || isTextBlock(editor, nextSibling2) || !nextSibling2 && node.parentNode === root) {
              block = null;
              return;
            }
          }
          if (!block) {
            block = dom2.create("p");
            node.parentNode?.insertBefore(block, node);
            textBlocks2.push(block);
          }
          block.appendChild(node);
        });
        return textBlocks2;
      };
      const hasCompatibleStyle = (dom2, sib, detail) => {
        const sibStyle = dom2.getStyle(sib, "list-style-type");
        let detailStyle = detail ? detail["list-style-type"] : "";
        detailStyle = detailStyle === null ? "" : detailStyle;
        return sibStyle === detailStyle;
      };
      const getRootSearchStart = (editor, range2) => {
        const start2 = editor.selection.getStart(true);
        const startPoint = getEndPointNode(editor, range2, true, editor.getBody());
        if (ancestor$2(SugarElement.fromDom(startPoint), SugarElement.fromDom(range2.commonAncestorContainer))) {
          return range2.commonAncestorContainer;
        } else {
          return start2;
        }
      };
      const applyList = (editor, listName, detail) => {
        const rng = editor.selection.getRng();
        let listItemName = "LI";
        const root = getClosestListHost(editor, getRootSearchStart(editor, rng));
        const dom2 = editor.dom;
        if (dom2.getContentEditable(editor.selection.getNode()) === "false") {
          return;
        }
        listName = listName.toUpperCase();
        if (listName === "DL") {
          listItemName = "DT";
        }
        const bookmark = createBookmark(rng);
        const selectedTextBlocks = filter$5(getSelectedTextBlocks(editor, rng, root), editor.dom.isEditable);
        Tools.each(selectedTextBlocks, (block) => {
          let listBlock;
          const sibling2 = block.previousSibling;
          const parent2 = block.parentNode;
          if (!isListItemNode(parent2)) {
            if (sibling2 && isListNode(sibling2) && sibling2.nodeName === listName && hasCompatibleStyle(dom2, sibling2, detail)) {
              listBlock = sibling2;
              block = dom2.rename(block, listItemName);
              sibling2.appendChild(block);
            } else {
              listBlock = dom2.create(listName);
              parent2.insertBefore(listBlock, block);
              listBlock.appendChild(block);
              block = dom2.rename(block, listItemName);
            }
            removeStyles(dom2, block, [
              "margin",
              "margin-right",
              "margin-bottom",
              "margin-left",
              "margin-top",
              "padding",
              "padding-right",
              "padding-bottom",
              "padding-left",
              "padding-top"
            ]);
            updateListWithDetails(dom2, listBlock, detail);
            mergeWithAdjacentLists(editor.dom, listBlock);
          }
        });
        editor.selection.setRng(resolveBookmark(bookmark));
      };
      const isValidLists = (list1, list2) => {
        return isListNode(list1) && list1.nodeName === list2?.nodeName;
      };
      const hasSameListStyle = (dom2, list1, list2) => {
        const targetStyle = dom2.getStyle(list1, "list-style-type", true);
        const style = dom2.getStyle(list2, "list-style-type", true);
        return targetStyle === style;
      };
      const hasSameClasses = (elm1, elm2) => {
        return elm1.className === elm2.className;
      };
      const shouldMerge = (dom2, list1, list2) => {
        return isValidLists(list1, list2) && // Note: isValidLists will ensure list1 and list2 are a HTMLElement. Unfortunately TypeScript doesn't
        // support type guards on multiple variables. See https://github.com/microsoft/TypeScript/issues/26916
        hasSameListStyle(dom2, list1, list2) && hasSameClasses(list1, list2);
      };
      const mergeWithAdjacentLists = (dom2, listBlock) => {
        let node;
        let sibling2 = listBlock.nextSibling;
        if (shouldMerge(dom2, listBlock, sibling2)) {
          const liSibling = sibling2;
          while (node = liSibling.firstChild) {
            listBlock.appendChild(node);
          }
          dom2.remove(liSibling);
        }
        sibling2 = listBlock.previousSibling;
        if (shouldMerge(dom2, listBlock, sibling2)) {
          const liSibling = sibling2;
          while (node = liSibling.lastChild) {
            listBlock.insertBefore(node, listBlock.firstChild);
          }
          dom2.remove(liSibling);
        }
      };
      const updateList$1 = (editor, list, listName, detail) => {
        if (list.nodeName !== listName) {
          const newList = editor.dom.rename(list, listName);
          updateListWithDetails(editor.dom, newList, detail);
          fireListEvent(editor, listToggleActionFromListName(listName), newList);
        } else {
          updateListWithDetails(editor.dom, list, detail);
          fireListEvent(editor, listToggleActionFromListName(listName), list);
        }
      };
      const updateCustomList = (editor, list, listName, detail) => {
        list.classList.forEach((cls, _, classList) => {
          if (cls.startsWith("tox-")) {
            classList.remove(cls);
            if (classList.length === 0) {
              list.removeAttribute("class");
            }
          }
        });
        if (list.nodeName !== listName) {
          const newList = editor.dom.rename(list, listName);
          updateListWithDetails(editor.dom, newList, detail);
          fireListEvent(editor, listToggleActionFromListName(listName), newList);
        } else {
          updateListWithDetails(editor.dom, list, detail);
          fireListEvent(editor, listToggleActionFromListName(listName), list);
        }
      };
      const toggleMultipleLists = (editor, parentList, lists2, listName, detail) => {
        const parentIsList = isListNode(parentList);
        if (parentIsList && parentList.nodeName === listName && !hasListStyleDetail(detail) && !isCustomList(parentList)) {
          flattenListSelection(editor);
        } else {
          applyList(editor, listName, detail);
          const bookmark = createBookmark(editor.selection.getRng());
          const allLists = parentIsList ? [parentList, ...lists2] : lists2;
          const updateFunction = parentIsList && isCustomList(parentList) ? updateCustomList : updateList$1;
          Tools.each(allLists, (elm) => {
            updateFunction(editor, elm, listName, detail);
          });
          editor.selection.setRng(resolveBookmark(bookmark));
        }
      };
      const hasListStyleDetail = (detail) => {
        return "list-style-type" in detail;
      };
      const toggleSingleList = (editor, parentList, listName, detail) => {
        if (parentList === editor.getBody()) {
          return;
        }
        if (parentList) {
          if (parentList.nodeName === listName && !hasListStyleDetail(detail) && !isCustomList(parentList)) {
            flattenListSelection(editor);
          } else {
            const bookmark = createBookmark(editor.selection.getRng());
            if (isCustomList(parentList)) {
              parentList.classList.forEach((cls, _, classList) => {
                if (cls.startsWith("tox-")) {
                  classList.remove(cls);
                  if (classList.length === 0) {
                    parentList.removeAttribute("class");
                  }
                }
              });
            }
            updateListWithDetails(editor.dom, parentList, detail);
            const newList = editor.dom.rename(parentList, listName);
            mergeWithAdjacentLists(editor.dom, newList);
            editor.selection.setRng(resolveBookmark(bookmark));
            applyList(editor, listName, detail);
            fireListEvent(editor, listToggleActionFromListName(listName), newList);
          }
        } else {
          applyList(editor, listName, detail);
          fireListEvent(editor, listToggleActionFromListName(listName), parentList);
        }
      };
      const toggleList = (editor, listName, _detail) => {
        const parentList = getParentList(editor);
        if (isWithinNonEditableList$1(editor, parentList)) {
          return;
        }
        const selectedSubLists = getSelectedSubLists(editor);
        const detail = isObject(_detail) ? _detail : {};
        if (selectedSubLists.length > 0) {
          toggleMultipleLists(editor, parentList, selectedSubLists, listName, detail);
        } else {
          toggleSingleList(editor, parentList, listName, detail);
        }
      };
      const findNextCaretContainer = (editor, rng, isForward, root) => {
        let node = rng.startContainer;
        const offset = rng.startOffset;
        if (isTextNode$1(node) && (isForward ? offset < node.data.length : offset > 0)) {
          return node;
        }
        const nonEmptyBlocks = editor.schema.getNonEmptyElements();
        if (isElement$1(node)) {
          node = RangeUtils.getNode(node, offset);
        }
        const walker = new DomTreeWalker(node, root);
        if (isForward) {
          if (isBogusBr(editor.dom, node)) {
            walker.next();
          }
        }
        const walkFn = isForward ? walker.next.bind(walker) : walker.prev2.bind(walker);
        while (node = walkFn()) {
          if (node.nodeName === "LI" && !node.hasChildNodes()) {
            return node;
          }
          if (nonEmptyBlocks[node.nodeName]) {
            return node;
          }
          if (isTextNode$1(node) && node.data.length > 0) {
            return node;
          }
        }
        return null;
      };
      const hasOnlyOneBlockChild = (dom2, elm) => {
        const childNodes = elm.childNodes;
        return childNodes.length === 1 && !isListNode(childNodes[0]) && dom2.isBlock(childNodes[0]);
      };
      const isUnwrappable = (node) => Optional.from(node).map(SugarElement.fromDom).filter(isHTMLElement$1).exists((el) => isEditable$2(el) && !contains$2(["details"], name(el)));
      const unwrapSingleBlockChild = (dom2, elm) => {
        if (hasOnlyOneBlockChild(dom2, elm) && isUnwrappable(elm.firstChild)) {
          dom2.remove(elm.firstChild, true);
        }
      };
      const moveChildren = (dom2, fromElm, toElm) => {
        let node;
        const targetElm = hasOnlyOneBlockChild(dom2, toElm) ? toElm.firstChild : toElm;
        unwrapSingleBlockChild(dom2, fromElm);
        if (!isEmpty$1(dom2, fromElm, true)) {
          while (node = fromElm.firstChild) {
            targetElm.appendChild(node);
          }
        }
      };
      const mergeLiElements = (dom2, fromElm, toElm) => {
        let listNode;
        const ul = fromElm.parentNode;
        if (!isChildOfBody(dom2, fromElm) || !isChildOfBody(dom2, toElm)) {
          return;
        }
        if (isListNode(toElm.lastChild)) {
          listNode = toElm.lastChild;
        }
        if (ul === toElm.lastChild) {
          if (isBr$1(ul.previousSibling)) {
            dom2.remove(ul.previousSibling);
          }
        }
        const node = toElm.lastChild;
        if (node && isBr$1(node) && fromElm.hasChildNodes()) {
          dom2.remove(node);
        }
        if (isEmpty$1(dom2, toElm, true)) {
          empty(SugarElement.fromDom(toElm));
        }
        moveChildren(dom2, fromElm, toElm);
        if (listNode) {
          toElm.appendChild(listNode);
        }
        const contains$12 = contains(SugarElement.fromDom(toElm), SugarElement.fromDom(fromElm));
        const nestedLists = contains$12 ? dom2.getParents(fromElm, isListNode, toElm) : [];
        dom2.remove(fromElm);
        each$e(nestedLists, (list) => {
          if (isEmpty$1(dom2, list) && list !== dom2.getRoot()) {
            dom2.remove(list);
          }
        });
      };
      const mergeIntoEmptyLi = (editor, fromLi, toLi) => {
        empty(SugarElement.fromDom(toLi));
        mergeLiElements(editor.dom, fromLi, toLi);
        editor.selection.setCursorLocation(toLi, 0);
      };
      const mergeForward = (editor, rng, fromLi, toLi) => {
        const dom2 = editor.dom;
        if (dom2.isEmpty(toLi)) {
          mergeIntoEmptyLi(editor, fromLi, toLi);
        } else {
          const bookmark = createBookmark(rng);
          mergeLiElements(dom2, fromLi, toLi);
          editor.selection.setRng(resolveBookmark(bookmark));
        }
      };
      const mergeBackward = (editor, rng, fromLi, toLi) => {
        const bookmark = createBookmark(rng);
        mergeLiElements(editor.dom, fromLi, toLi);
        const resolvedBookmark = resolveBookmark(bookmark);
        editor.selection.setRng(resolvedBookmark);
      };
      const backspaceDeleteFromListToListCaret = (editor, isForward) => {
        const dom2 = editor.dom, selection = editor.selection;
        const selectionStartElm = selection.getStart();
        const root = getClosestEditingHost(editor, selectionStartElm);
        const li = dom2.getParent(selection.getStart(), "LI", root);
        if (li) {
          const ul = li.parentElement;
          if (ul === editor.getBody() && isEmpty$1(dom2, ul)) {
            return true;
          }
          const rng = normalizeRange(selection.getRng());
          const otherLi = dom2.getParent(findNextCaretContainer(editor, rng, isForward, root), "LI", root);
          const willMergeParentIntoChild = otherLi && (isForward ? dom2.isChildOf(li, otherLi) : dom2.isChildOf(otherLi, li));
          if (otherLi && otherLi !== li && !willMergeParentIntoChild) {
            editor.undoManager.transact(() => {
              if (isForward) {
                mergeForward(editor, rng, otherLi, li);
              } else {
                if (isFirstChild$1(li)) {
                  outdentListSelection(editor);
                } else {
                  mergeBackward(editor, rng, li, otherLi);
                }
              }
            });
            return true;
          } else if (willMergeParentIntoChild && !isForward && otherLi !== li) {
            const commonAncestorParent = rng.commonAncestorContainer.parentElement;
            if (!commonAncestorParent || dom2.isChildOf(otherLi, commonAncestorParent)) {
              return false;
            }
            editor.undoManager.transact(() => {
              const bookmark = createBookmark(rng);
              moveChildren(dom2, commonAncestorParent, otherLi);
              commonAncestorParent.remove();
              const resolvedBookmark = resolveBookmark(bookmark);
              editor.selection.setRng(resolvedBookmark);
            });
            return true;
          } else if (!otherLi) {
            if (!isForward && rng.startOffset === 0 && rng.endOffset === 0) {
              editor.undoManager.transact(() => {
                flattenListSelection(editor);
              });
              return true;
            }
          }
        }
        return false;
      };
      const removeBlock = (dom2, block, root) => {
        const parentBlock = dom2.getParent(block.parentNode, dom2.isBlock, root);
        dom2.remove(block);
        if (parentBlock && dom2.isEmpty(parentBlock)) {
          dom2.remove(parentBlock);
        }
      };
      const backspaceDeleteIntoListCaret = (editor, isForward) => {
        const dom2 = editor.dom;
        const selectionStartElm = editor.selection.getStart();
        const root = getClosestEditingHost(editor, selectionStartElm);
        const block = dom2.getParent(selectionStartElm, dom2.isBlock, root);
        if (block && dom2.isEmpty(block, void 0, { checkRootAsContent: true })) {
          const rng = normalizeRange(editor.selection.getRng());
          const nextCaretContainer = findNextCaretContainer(editor, rng, isForward, root);
          const otherLi = dom2.getParent(nextCaretContainer, "LI", root);
          if (nextCaretContainer && otherLi) {
            const findValidElement = (element) => contains$2(["td", "th", "caption"], name(element));
            const findRoot = (node) => node.dom === root;
            const otherLiCell = closest$4(SugarElement.fromDom(otherLi), findValidElement, findRoot);
            const caretCell = closest$4(SugarElement.fromDom(rng.startContainer), findValidElement, findRoot);
            if (!equals(otherLiCell, caretCell, eq)) {
              return false;
            }
            editor.undoManager.transact(() => {
              const parentNode = otherLi.parentNode;
              removeBlock(dom2, block, root);
              mergeWithAdjacentLists(dom2, parentNode);
              editor.selection.select(nextCaretContainer, true);
              editor.selection.collapse(isForward);
            });
            return true;
          }
        }
        return false;
      };
      const backspaceDeleteCaret$1 = (editor, isForward) => {
        return backspaceDeleteFromListToListCaret(editor, isForward) || backspaceDeleteIntoListCaret(editor, isForward);
      };
      const hasListSelection = (editor) => {
        const selectionStartElm = editor.selection.getStart();
        const root = getClosestEditingHost(editor, selectionStartElm);
        const startListParent = editor.dom.getParent(selectionStartElm, "LI,DT,DD", root);
        return isNonNullable(startListParent) || getSelectedListItems(editor).length > 0;
      };
      const backspaceDeleteRange$1 = (editor) => {
        if (hasListSelection(editor)) {
          editor.undoManager.transact(() => {
            let shouldFireInput = true;
            const inputHandler = () => shouldFireInput = false;
            editor.on("input", inputHandler);
            editor.execCommand("Delete");
            editor.off("input", inputHandler);
            if (shouldFireInput) {
              editor.dispatch("input");
            }
            normalizeLists(editor.dom, editor.getBody());
          });
          return true;
        }
        return false;
      };
      const backspaceDelete$c = (editor, isForward) => {
        const selection = editor.selection;
        return !isWithinNonEditableList$1(editor, selection.getNode()) && (selection.isCollapsed() ? backspaceDeleteCaret$1(editor, isForward) : backspaceDeleteRange$1(editor));
      };
      const blockPosition = (block, position) => ({
        block,
        position
      });
      const blockBoundary = (from2, to2) => ({
        from: from2,
        to: to2
      });
      const getBlockPosition = (rootNode, pos) => {
        const rootElm = SugarElement.fromDom(rootNode);
        const containerElm = SugarElement.fromDom(pos.container());
        return getParentBlock$2(rootElm, containerElm).map((block) => blockPosition(block, pos));
      };
      const isNotAncestorial = (blockBoundary2) => !(contains(blockBoundary2.to.block, blockBoundary2.from.block) || contains(blockBoundary2.from.block, blockBoundary2.to.block));
      const isDifferentBlocks = (blockBoundary2) => !eq(blockBoundary2.from.block, blockBoundary2.to.block);
      const getClosestHost = (root, scope) => {
        const isRoot2 = (node) => eq(node, root);
        const isHost = (node) => isTableCell$2(node) || isContentEditableTrue$3(node.dom);
        return closest$4(scope, isHost, isRoot2).filter(isElement$8).getOr(root);
      };
      const hasSameHost = (rootNode, blockBoundary2) => {
        const root = SugarElement.fromDom(rootNode);
        return eq(getClosestHost(root, blockBoundary2.from.block), getClosestHost(root, blockBoundary2.to.block));
      };
      const isEditable$1 = (blockBoundary2) => isContentEditableFalse$a(blockBoundary2.from.block.dom) === false && isContentEditableFalse$a(blockBoundary2.to.block.dom) === false;
      const hasValidBlocks = (blockBoundary2) => {
        const isValidBlock = (block) => isTextBlock$3(block) || hasBlockAttr(block.dom) || isListItem$2(block);
        return isValidBlock(blockBoundary2.from.block) && isValidBlock(blockBoundary2.to.block);
      };
      const skipLastBr = (schema, rootNode, forward, blockPosition2) => {
        if (isBr$7(blockPosition2.position.getNode()) && !isEmpty$4(schema, blockPosition2.block)) {
          return positionIn(false, blockPosition2.block.dom).bind((lastPositionInBlock) => {
            if (lastPositionInBlock.isEqual(blockPosition2.position)) {
              return fromPosition(forward, rootNode, lastPositionInBlock).bind((to2) => getBlockPosition(rootNode, to2));
            } else {
              return Optional.some(blockPosition2);
            }
          }).getOr(blockPosition2);
        } else {
          return blockPosition2;
        }
      };
      const readFromRange = (schema, rootNode, forward, rng) => {
        const fromBlockPos = getBlockPosition(rootNode, CaretPosition.fromRangeStart(rng));
        const toBlockPos = fromBlockPos.bind((blockPos) => fromPosition(forward, rootNode, blockPos.position).bind((to2) => getBlockPosition(rootNode, to2).map((blockPos2) => skipLastBr(schema, rootNode, forward, blockPos2))));
        return lift2(fromBlockPos, toBlockPos, blockBoundary).filter((blockBoundary2) => isDifferentBlocks(blockBoundary2) && hasSameHost(rootNode, blockBoundary2) && isEditable$1(blockBoundary2) && hasValidBlocks(blockBoundary2) && isNotAncestorial(blockBoundary2));
      };
      const read$1 = (schema, rootNode, forward, rng) => rng.collapsed ? readFromRange(schema, rootNode, forward, rng) : Optional.none();
      const getChildrenUntilBlockBoundary = (block, schema) => {
        const children2 = children$1(block);
        return findIndex$2(children2, (el) => schema.isBlock(name(el))).fold(constant(children2), (index) => children2.slice(0, index));
      };
      const extractChildren = (block, schema) => {
        const children2 = getChildrenUntilBlockBoundary(block, schema);
        each$e(children2, remove$8);
        return children2;
      };
      const removeEmptyRoot = (schema, rootNode, block) => {
        const parents2 = parentsAndSelf(block, rootNode);
        return find$2(parents2.reverse(), (element) => isEmpty$4(schema, element)).each(remove$8);
      };
      const isEmptyBefore = (schema, el) => filter$5(prevSiblings(el), (el2) => !isEmpty$4(schema, el2)).length === 0;
      const nestedBlockMerge = (rootNode, fromBlock, toBlock, schema, insertionPoint) => {
        if (isEmpty$4(schema, toBlock)) {
          fillWithPaddingBr(toBlock);
          return firstPositionIn(toBlock.dom);
        }
        if (isEmptyBefore(schema, insertionPoint) && isEmpty$4(schema, fromBlock)) {
          before$4(insertionPoint, SugarElement.fromTag("br"));
        }
        const position = prevPosition(toBlock.dom, CaretPosition.before(insertionPoint.dom));
        each$e(extractChildren(fromBlock, schema), (child2) => {
          before$4(insertionPoint, child2);
        });
        removeEmptyRoot(schema, rootNode, fromBlock);
        return position;
      };
      const isInline = (schema, node) => schema.isInline(name(node));
      const sidelongBlockMerge = (rootNode, fromBlock, toBlock, schema) => {
        if (isEmpty$4(schema, toBlock)) {
          if (isEmpty$4(schema, fromBlock)) {
            const getInlineToBlockDescendants = (el) => {
              const helper = (node, elements) => firstChild(node).fold(() => elements, (child2) => isInline(schema, child2) ? helper(child2, elements.concat(shallow(child2))) : elements);
              return helper(el, []);
            };
            const newFromBlockDescendants = foldr(getInlineToBlockDescendants(toBlock), (element, descendant2) => {
              wrap$2(element, descendant2);
              return descendant2;
            }, createPaddingBr());
            empty(fromBlock);
            append$1(fromBlock, newFromBlockDescendants);
          }
          remove$8(toBlock);
          return firstPositionIn(fromBlock.dom);
        }
        const position = lastPositionIn(toBlock.dom);
        each$e(extractChildren(fromBlock, schema), (child2) => {
          append$1(toBlock, child2);
        });
        removeEmptyRoot(schema, rootNode, fromBlock);
        return position;
      };
      const findInsertionPoint = (toBlock, block) => {
        const parentsAndSelf$1 = parentsAndSelf(block, toBlock);
        return Optional.from(parentsAndSelf$1[parentsAndSelf$1.length - 1]);
      };
      const getInsertionPoint = (fromBlock, toBlock) => contains(toBlock, fromBlock) ? findInsertionPoint(toBlock, fromBlock) : Optional.none();
      const trimBr = (first2, block) => {
        positionIn(first2, block.dom).bind((position) => Optional.from(position.getNode())).map(SugarElement.fromDom).filter(isBr$6).each(remove$8);
      };
      const mergeBlockInto = (rootNode, fromBlock, toBlock, schema) => {
        trimBr(true, fromBlock);
        trimBr(false, toBlock);
        return getInsertionPoint(fromBlock, toBlock).fold(curry(sidelongBlockMerge, rootNode, fromBlock, toBlock, schema), curry(nestedBlockMerge, rootNode, fromBlock, toBlock, schema));
      };
      const mergeBlocks = (rootNode, forward, block1, block2, schema) => forward ? mergeBlockInto(rootNode, block2, block1, schema) : mergeBlockInto(rootNode, block1, block2, schema);
      const backspaceDelete$b = (editor, forward) => {
        const rootNode = SugarElement.fromDom(editor.getBody());
        const position = read$1(editor.schema, rootNode.dom, forward, editor.selection.getRng()).map((blockBoundary2) => () => {
          mergeBlocks(rootNode, forward, blockBoundary2.from.block, blockBoundary2.to.block, editor.schema).each((pos) => {
            editor.selection.setRng(pos.toRange());
          });
        });
        return position;
      };
      const deleteRangeMergeBlocks = (rootNode, selection, schema) => {
        const rng = selection.getRng();
        return lift2(getParentBlock$2(rootNode, SugarElement.fromDom(rng.startContainer)), getParentBlock$2(rootNode, SugarElement.fromDom(rng.endContainer)), (block1, block2) => {
          if (!eq(block1, block2)) {
            return Optional.some(() => {
              rng.deleteContents();
              mergeBlocks(rootNode, true, block1, block2, schema).each((pos) => {
                selection.setRng(pos.toRange());
              });
            });
          } else {
            return Optional.none();
          }
        }).getOr(Optional.none());
      };
      const isRawNodeInTable = (root, rawNode) => {
        const node = SugarElement.fromDom(rawNode);
        const isRoot2 = curry(eq, root);
        return ancestor$5(node, isTableCell$2, isRoot2).isSome();
      };
      const isSelectionInTable = (root, rng) => isRawNodeInTable(root, rng.startContainer) || isRawNodeInTable(root, rng.endContainer);
      const isEverythingSelected = (root, rng) => {
        const noPrevious = prevPosition(root.dom, CaretPosition.fromRangeStart(rng)).isNone();
        const noNext = nextPosition(root.dom, CaretPosition.fromRangeEnd(rng)).isNone();
        return !isSelectionInTable(root, rng) && noPrevious && noNext;
      };
      const emptyEditor = (editor) => {
        return Optional.some(() => {
          editor.setContent("");
          editor.selection.setCursorLocation();
        });
      };
      const deleteRange$3 = (editor) => {
        const rootNode = SugarElement.fromDom(editor.getBody());
        const rng = editor.selection.getRng();
        return isEverythingSelected(rootNode, rng) ? emptyEditor(editor) : deleteRangeMergeBlocks(rootNode, editor.selection, editor.schema);
      };
      const backspaceDelete$a = (editor, _forward) => editor.selection.isCollapsed() ? Optional.none() : deleteRange$3(editor);
      const showCaret = (direction, editor, node, before2, scrollIntoView) => (
        // TODO: Figure out a better way to handle this dependency
        Optional.from(editor._selectionOverrides.showCaret(direction, node, before2, scrollIntoView))
      );
      const getNodeRange = (node) => {
        const rng = node.ownerDocument.createRange();
        rng.selectNode(node);
        return rng;
      };
      const selectNode = (editor, node) => {
        const e = editor.dispatch("BeforeObjectSelected", { target: node });
        if (e.isDefaultPrevented()) {
          return Optional.none();
        }
        return Optional.some(getNodeRange(node));
      };
      const renderCaretAtRange = (editor, range2, scrollIntoView) => {
        const normalizedRange = normalizeRange$2(1, editor.getBody(), range2);
        const caretPosition = CaretPosition.fromRangeStart(normalizedRange);
        const caretPositionNode = caretPosition.getNode();
        if (isInlineFakeCaretTarget(caretPositionNode)) {
          return showCaret(1, editor, caretPositionNode, !caretPosition.isAtEnd(), false);
        }
        const caretPositionBeforeNode = caretPosition.getNode(true);
        if (isInlineFakeCaretTarget(caretPositionBeforeNode)) {
          return showCaret(1, editor, caretPositionBeforeNode, false, false);
        }
        const ceRoot = getContentEditableRoot$1(editor.dom.getRoot(), caretPosition.getNode());
        if (isInlineFakeCaretTarget(ceRoot)) {
          return showCaret(1, editor, ceRoot, false, scrollIntoView);
        }
        return Optional.none();
      };
      const renderRangeCaret = (editor, range2, scrollIntoView) => range2.collapsed ? renderCaretAtRange(editor, range2, scrollIntoView).getOr(range2) : range2;
      const isBeforeBoundary = (pos) => isBeforeContentEditableFalse(pos) || isBeforeMedia(pos);
      const isAfterBoundary = (pos) => isAfterContentEditableFalse(pos) || isAfterMedia(pos);
      const trimEmptyTextNode = (dom2, node) => {
        if (isText$b(node) && node.data.length === 0) {
          dom2.remove(node);
        }
      };
      const deleteContentAndShowCaret = (editor, range2, node, direction, forward, peekCaretPosition) => {
        showCaret(direction, editor, peekCaretPosition.getNode(!forward), forward, true).each((caretRange) => {
          if (range2.collapsed) {
            const deleteRange2 = range2.cloneRange();
            if (forward) {
              deleteRange2.setEnd(caretRange.startContainer, caretRange.startOffset);
            } else {
              deleteRange2.setStart(caretRange.endContainer, caretRange.endOffset);
            }
            deleteRange2.deleteContents();
          } else {
            range2.deleteContents();
          }
          editor.selection.setRng(caretRange);
        });
        trimEmptyTextNode(editor.dom, node);
      };
      const deleteBoundaryText = (editor, forward) => {
        const range2 = editor.selection.getRng();
        if (!isText$b(range2.commonAncestorContainer)) {
          return Optional.none();
        }
        const direction = forward ? 1 : -1;
        const caretWalker = CaretWalker(editor.getBody());
        const getNextPosFn = curry(getVisualCaretPosition, forward ? caretWalker.next : caretWalker.prev);
        const isBeforeFn = forward ? isBeforeBoundary : isAfterBoundary;
        const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range2);
        const nextCaretPosition = getNextPosFn(caretPosition);
        const normalizedNextCaretPosition = nextCaretPosition ? normalizePosition(forward, nextCaretPosition) : nextCaretPosition;
        if (!normalizedNextCaretPosition || !isMoveInsideSameBlock(caretPosition, normalizedNextCaretPosition)) {
          return Optional.none();
        } else if (isBeforeFn(normalizedNextCaretPosition)) {
          return Optional.some(() => deleteContentAndShowCaret(editor, range2, caretPosition.getNode(), direction, forward, normalizedNextCaretPosition));
        }
        const peekCaretPosition = getNextPosFn(normalizedNextCaretPosition);
        if (peekCaretPosition && isBeforeFn(peekCaretPosition)) {
          if (isMoveInsideSameBlock(normalizedNextCaretPosition, peekCaretPosition)) {
            return Optional.some(() => deleteContentAndShowCaret(editor, range2, caretPosition.getNode(), direction, forward, peekCaretPosition));
          }
        }
        return Optional.none();
      };
      const backspaceDelete$9 = (editor, forward) => deleteBoundaryText(editor, forward);
      const getEdgeCefPosition = (editor, atStart) => {
        const root = editor.getBody();
        return atStart ? firstPositionIn(root).filter(isBeforeContentEditableFalse) : lastPositionIn(root).filter(isAfterContentEditableFalse);
      };
      const isCefAtEdgeSelected = (editor) => {
        const rng = editor.selection.getRng();
        return !rng.collapsed && (getEdgeCefPosition(editor, true).exists((pos) => pos.isEqual(CaretPosition.fromRangeStart(rng))) || getEdgeCefPosition(editor, false).exists((pos) => pos.isEqual(CaretPosition.fromRangeEnd(rng))));
      };
      const isCompoundElement = (node) => isNonNullable(node) && (isTableCell$2(SugarElement.fromDom(node)) || isListItem$2(SugarElement.fromDom(node)));
      const DeleteAction = Adt.generate([
        { remove: ["element"] },
        { moveToElement: ["element"] },
        { moveToPosition: ["position"] }
      ]);
      const isAtContentEditableBlockCaret = (forward, from2) => {
        const elm = from2.getNode(!forward);
        const caretLocation = forward ? "after" : "before";
        return isElement$7(elm) && elm.getAttribute("data-mce-caret") === caretLocation;
      };
      const isDeleteFromCefDifferentBlocks = (root, forward, from2, to2, schema) => {
        const inSameBlock = (elm) => schema.isInline(elm.nodeName.toLowerCase()) && !isInSameBlock(from2, to2, root);
        return getRelativeCefElm(!forward, from2).fold(() => getRelativeCefElm(forward, to2).fold(never, inSameBlock), inSameBlock);
      };
      const deleteEmptyBlockOrMoveToCef = (schema, root, forward, from2, to2) => {
        const toCefElm = to2.getNode(!forward);
        return getParentBlock$2(SugarElement.fromDom(root), SugarElement.fromDom(from2.getNode())).map((blockElm) => isEmpty$4(schema, blockElm) ? DeleteAction.remove(blockElm.dom) : DeleteAction.moveToElement(toCefElm)).orThunk(() => Optional.some(DeleteAction.moveToElement(toCefElm)));
      };
      const findCefPosition = (root, forward, from2, schema) => fromPosition(forward, root, from2).bind((to2) => {
        if (isCompoundElement(to2.getNode())) {
          return Optional.none();
        } else if (isDeleteFromCefDifferentBlocks(root, forward, from2, to2, schema)) {
          return Optional.none();
        } else if (forward && isContentEditableFalse$a(to2.getNode())) {
          return deleteEmptyBlockOrMoveToCef(schema, root, forward, from2, to2);
        } else if (!forward && isContentEditableFalse$a(to2.getNode(true))) {
          return deleteEmptyBlockOrMoveToCef(schema, root, forward, from2, to2);
        } else if (forward && isAfterContentEditableFalse(from2)) {
          return Optional.some(DeleteAction.moveToPosition(to2));
        } else if (!forward && isBeforeContentEditableFalse(from2)) {
          return Optional.some(DeleteAction.moveToPosition(to2));
        } else {
          return Optional.none();
        }
      });
      const getContentEditableBlockAction = (forward, elm) => {
        if (isNullable(elm)) {
          return Optional.none();
        } else if (forward && isContentEditableFalse$a(elm.nextSibling)) {
          return Optional.some(DeleteAction.moveToElement(elm.nextSibling));
        } else if (!forward && isContentEditableFalse$a(elm.previousSibling)) {
          return Optional.some(DeleteAction.moveToElement(elm.previousSibling));
        } else {
          return Optional.none();
        }
      };
      const skipMoveToActionFromInlineCefToContent = (root, from2, deleteAction2) => deleteAction2.fold((elm) => Optional.some(DeleteAction.remove(elm)), (elm) => Optional.some(DeleteAction.moveToElement(elm)), (to2) => {
        if (isInSameBlock(from2, to2, root)) {
          return Optional.none();
        } else {
          return Optional.some(DeleteAction.moveToPosition(to2));
        }
      });
      const getContentEditableAction = (root, forward, from2, schema) => {
        if (isAtContentEditableBlockCaret(forward, from2)) {
          return getContentEditableBlockAction(forward, from2.getNode(!forward)).orThunk(() => findCefPosition(root, forward, from2, schema));
        } else {
          return findCefPosition(root, forward, from2, schema).bind((deleteAction2) => skipMoveToActionFromInlineCefToContent(root, from2, deleteAction2));
        }
      };
      const read = (root, forward, rng, schema) => {
        const normalizedRange = normalizeRange$2(forward ? 1 : -1, root, rng);
        const from2 = CaretPosition.fromRangeStart(normalizedRange);
        const rootElement = SugarElement.fromDom(root);
        if (!forward && isAfterContentEditableFalse(from2)) {
          return Optional.some(DeleteAction.remove(from2.getNode(true)));
        } else if (forward && isBeforeContentEditableFalse(from2)) {
          return Optional.some(DeleteAction.remove(from2.getNode()));
        } else if (!forward && isBeforeContentEditableFalse(from2) && isAfterBr(rootElement, from2, schema)) {
          return findPreviousBr(rootElement, from2, schema).map((br) => DeleteAction.remove(br.getNode()));
        } else if (forward && isAfterContentEditableFalse(from2) && isBeforeBr$1(rootElement, from2, schema)) {
          return findNextBr(rootElement, from2, schema).map((br) => DeleteAction.remove(br.getNode()));
        } else {
          return getContentEditableAction(root, forward, from2, schema);
        }
      };
      const deleteElement$1 = (editor, forward) => (element) => {
        editor._selectionOverrides.hideFakeCaret();
        deleteElement$2(editor, forward, SugarElement.fromDom(element));
        return true;
      };
      const moveToElement = (editor, forward) => (element) => {
        const pos = forward ? CaretPosition.before(element) : CaretPosition.after(element);
        editor.selection.setRng(pos.toRange());
        return true;
      };
      const moveToPosition = (editor) => (pos) => {
        editor.selection.setRng(pos.toRange());
        return true;
      };
      const getAncestorCe = (editor, node) => Optional.from(getContentEditableRoot$1(editor.getBody(), node));
      const backspaceDeleteCaret = (editor, forward) => {
        const selectedNode = editor.selection.getNode();
        return getAncestorCe(editor, selectedNode).filter(isContentEditableFalse$a).fold(() => read(editor.getBody(), forward, editor.selection.getRng(), editor.schema).map((deleteAction2) => () => deleteAction2.fold(deleteElement$1(editor, forward), moveToElement(editor, forward), moveToPosition(editor))), () => Optional.some(noop));
      };
      const deleteOffscreenSelection = (rootElement) => {
        each$e(descendants(rootElement, ".mce-offscreen-selection"), remove$8);
      };
      const backspaceDeleteRange = (editor, forward) => {
        const selectedNode = editor.selection.getNode();
        if (isContentEditableFalse$a(selectedNode) && !isTableCell$3(selectedNode)) {
          const hasCefAncestor = getAncestorCe(editor, selectedNode.parentNode).filter(isContentEditableFalse$a);
          return hasCefAncestor.fold(() => Optional.some(() => {
            deleteOffscreenSelection(SugarElement.fromDom(editor.getBody()));
            deleteElement$2(editor, forward, SugarElement.fromDom(editor.selection.getNode()));
            paddEmptyBody(editor);
          }), () => Optional.some(noop));
        }
        if (isCefAtEdgeSelected(editor)) {
          return Optional.some(() => {
            deleteRangeContents(editor, editor.selection.getRng(), SugarElement.fromDom(editor.getBody()));
          });
        }
        return Optional.none();
      };
      const paddEmptyElement = (editor) => {
        const dom2 = editor.dom, selection = editor.selection;
        const ceRoot = getContentEditableRoot$1(editor.getBody(), selection.getNode());
        if (isContentEditableTrue$3(ceRoot) && dom2.isBlock(ceRoot) && dom2.isEmpty(ceRoot)) {
          const br = dom2.create("br", { "data-mce-bogus": "1" });
          dom2.setHTML(ceRoot, "");
          ceRoot.appendChild(br);
          selection.setRng(CaretPosition.before(br).toRange());
        }
        return true;
      };
      const backspaceDelete$8 = (editor, forward) => {
        if (editor.selection.isCollapsed()) {
          return backspaceDeleteCaret(editor, forward);
        } else {
          return backspaceDeleteRange(editor, forward);
        }
      };
      const backspaceDelete$7 = (editor, forward) => {
        const dom2 = editor.dom;
        const startBlock = dom2.getParent(editor.selection.getStart(), dom2.isBlock);
        const endBlock = dom2.getParent(editor.selection.getEnd(), dom2.isBlock);
        const body = editor.getBody();
        const startBlockName = startBlock?.nodeName?.toLowerCase();
        if (startBlockName === "div" && startBlock && endBlock && startBlock === body.firstChild && endBlock === body.lastChild && !dom2.isEmpty(body)) {
          const wrapper = startBlock.cloneNode(false);
          const deleteAction2 = () => {
            if (forward) {
              execNativeForwardDeleteCommand(editor);
            } else {
              execNativeDeleteCommand(editor);
            }
            if (body.firstChild !== startBlock) {
              const bookmark = createBookmark(editor.selection.getRng(), () => document.createElement("span"));
              Array.from(body.childNodes).forEach((node) => wrapper.appendChild(node));
              body.appendChild(wrapper);
              editor.selection.setRng(resolveBookmark(bookmark));
            }
          };
          return Optional.some(deleteAction2);
        }
        return Optional.none();
      };
      const deleteCaret$2 = (editor, forward) => {
        const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng());
        return fromPosition(forward, editor.getBody(), fromPos).filter((pos) => forward ? isBeforeImageBlock(pos) : isAfterImageBlock(pos)).bind((pos) => getChildNodeAtRelativeOffset(forward ? 0 : -1, pos)).map((elm) => () => editor.selection.select(elm));
      };
      const backspaceDelete$6 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$2(editor, forward) : Optional.none();
      const isText$2 = isText$b;
      const startsWithCaretContainer = (node) => isText$2(node) && node.data[0] === ZWSP$1;
      const endsWithCaretContainer = (node) => isText$2(node) && node.data[node.data.length - 1] === ZWSP$1;
      const createZwsp = (node) => {
        const doc = node.ownerDocument ?? document;
        return doc.createTextNode(ZWSP$1);
      };
      const insertBefore$1 = (node) => {
        if (isText$2(node.previousSibling)) {
          if (endsWithCaretContainer(node.previousSibling)) {
            return node.previousSibling;
          } else {
            node.previousSibling.appendData(ZWSP$1);
            return node.previousSibling;
          }
        } else if (isText$2(node)) {
          if (startsWithCaretContainer(node)) {
            return node;
          } else {
            node.insertData(0, ZWSP$1);
            return node;
          }
        } else {
          const newNode = createZwsp(node);
          node.parentNode?.insertBefore(newNode, node);
          return newNode;
        }
      };
      const insertAfter$1 = (node) => {
        if (isText$2(node.nextSibling)) {
          if (startsWithCaretContainer(node.nextSibling)) {
            return node.nextSibling;
          } else {
            node.nextSibling.insertData(0, ZWSP$1);
            return node.nextSibling;
          }
        } else if (isText$2(node)) {
          if (endsWithCaretContainer(node)) {
            return node;
          } else {
            node.appendData(ZWSP$1);
            return node;
          }
        } else {
          const newNode = createZwsp(node);
          if (node.nextSibling) {
            node.parentNode?.insertBefore(newNode, node.nextSibling);
          } else {
            node.parentNode?.appendChild(newNode);
          }
          return newNode;
        }
      };
      const insertInline = (before2, node) => before2 ? insertBefore$1(node) : insertAfter$1(node);
      const insertInlineBefore = curry(insertInline, true);
      const insertInlineAfter = curry(insertInline, false);
      const insertInlinePos = (pos, before2) => {
        if (isText$b(pos.container())) {
          return insertInline(before2, pos.container());
        } else {
          return insertInline(before2, pos.getNode());
        }
      };
      const isPosCaretContainer = (pos, caret) => {
        const caretNode = caret.get();
        return caretNode && pos.container() === caretNode && isCaretContainerInline(caretNode);
      };
      const renderCaret = (caret, location) => location.fold((element) => {
        remove$2(caret.get());
        const text2 = insertInlineBefore(element);
        caret.set(text2);
        return Optional.some(CaretPosition(text2, text2.length - 1));
      }, (element) => (
        // Start
        firstPositionIn(element).map((pos) => {
          if (!isPosCaretContainer(pos, caret)) {
            remove$2(caret.get());
            const text2 = insertInlinePos(pos, true);
            caret.set(text2);
            return CaretPosition(text2, 1);
          } else {
            const node = caret.get();
            return CaretPosition(node, 1);
          }
        })
      ), (element) => (
        // End
        lastPositionIn(element).map((pos) => {
          if (!isPosCaretContainer(pos, caret)) {
            remove$2(caret.get());
            const text2 = insertInlinePos(pos, false);
            caret.set(text2);
            return CaretPosition(text2, text2.length - 1);
          } else {
            const node = caret.get();
            return CaretPosition(node, node.length - 1);
          }
        })
      ), (element) => {
        remove$2(caret.get());
        const text2 = insertInlineAfter(element);
        caret.set(text2);
        return Optional.some(CaretPosition(text2, 1));
      });
      const evaluateUntil = (fns, args) => {
        for (let i = 0; i < fns.length; i++) {
          const result = fns[i].apply(null, args);
          if (result.isSome()) {
            return result;
          }
        }
        return Optional.none();
      };
      const Location = Adt.generate([
        { before: ["element"] },
        { start: ["element"] },
        { end: ["element"] },
        { after: ["element"] }
      ]);
      const rescope$1 = (rootNode, node) => {
        const parentBlock = getParentBlock$3(node, rootNode);
        return parentBlock ? parentBlock : rootNode;
      };
      const before = (isInlineTarget2, rootNode, pos) => {
        const nPos = normalizeForwards(pos);
        const scope = rescope$1(rootNode, nPos.container());
        return findRootInline(isInlineTarget2, scope, nPos).fold(() => nextPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget2, scope)).map((inline) => Location.before(inline)), Optional.none);
      };
      const isNotInsideFormatCaretContainer = (rootNode, elm) => getParentCaretContainer(rootNode, elm) === null;
      const findInsideRootInline = (isInlineTarget2, rootNode, pos) => findRootInline(isInlineTarget2, rootNode, pos).filter(curry(isNotInsideFormatCaretContainer, rootNode));
      const start$1 = (isInlineTarget2, rootNode, pos) => {
        const nPos = normalizeBackwards(pos);
        return findInsideRootInline(isInlineTarget2, rootNode, nPos).bind((inline) => {
          const prevPos = prevPosition(inline, nPos);
          return prevPos.isNone() ? Optional.some(Location.start(inline)) : Optional.none();
        });
      };
      const end = (isInlineTarget2, rootNode, pos) => {
        const nPos = normalizeForwards(pos);
        return findInsideRootInline(isInlineTarget2, rootNode, nPos).bind((inline) => {
          const nextPos = nextPosition(inline, nPos);
          return nextPos.isNone() ? Optional.some(Location.end(inline)) : Optional.none();
        });
      };
      const after = (isInlineTarget2, rootNode, pos) => {
        const nPos = normalizeBackwards(pos);
        const scope = rescope$1(rootNode, nPos.container());
        return findRootInline(isInlineTarget2, scope, nPos).fold(() => prevPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget2, scope)).map((inline) => Location.after(inline)), Optional.none);
      };
      const isValidLocation = (location) => !isRtl(getElement(location));
      const readLocation = (isInlineTarget2, rootNode, pos) => {
        const location = evaluateUntil([
          before,
          start$1,
          end,
          after
        ], [isInlineTarget2, rootNode, pos]);
        return location.filter(isValidLocation);
      };
      const getElement = (location) => location.fold(
        identity,
        // Before
        identity,
        // Start
        identity,
        // End
        identity
        // After
      );
      const getName = (location) => location.fold(
        constant("before"),
        // Before
        constant("start"),
        // Start
        constant("end"),
        // End
        constant("after")
        // After
      );
      const outside = (location) => location.fold(
        Location.before,
        // Before
        Location.before,
        // Start
        Location.after,
        // End
        Location.after
        // After
      );
      const inside = (location) => location.fold(
        Location.start,
        // Before
        Location.start,
        // Start
        Location.end,
        // End
        Location.end
        // After
      );
      const isEq = (location1, location2) => getName(location1) === getName(location2) && getElement(location1) === getElement(location2);
      const betweenInlines = (forward, isInlineTarget2, rootNode, from2, to2, location) => lift2(findRootInline(isInlineTarget2, rootNode, from2), findRootInline(isInlineTarget2, rootNode, to2), (fromInline, toInline) => {
        if (fromInline !== toInline && hasSameParentBlock(rootNode, fromInline, toInline)) {
          return Location.after(forward ? fromInline : toInline);
        } else {
          return location;
        }
      }).getOr(location);
      const skipNoMovement = (fromLocation, toLocation) => fromLocation.fold(always, (fromLocation2) => !isEq(fromLocation2, toLocation));
      const findLocationTraverse = (forward, isInlineTarget2, rootNode, fromLocation, pos) => {
        const from2 = normalizePosition(forward, pos);
        const to2 = fromPosition(forward, rootNode, from2).map(curry(normalizePosition, forward));
        const location = to2.fold(() => fromLocation.map(outside), (to3) => readLocation(isInlineTarget2, rootNode, to3).map(curry(betweenInlines, forward, isInlineTarget2, rootNode, from2, to3)).filter(curry(skipNoMovement, fromLocation)));
        return location.filter(isValidLocation);
      };
      const findLocationSimple = (forward, location) => {
        if (forward) {
          return location.fold(
            compose(Optional.some, Location.start),
            // Before -> Start
            Optional.none,
            compose(Optional.some, Location.after),
            // End -> After
            Optional.none
          );
        } else {
          return location.fold(
            Optional.none,
            compose(Optional.some, Location.before),
            // Before <- Start
            Optional.none,
            compose(Optional.some, Location.end)
            // End <- After
          );
        }
      };
      const findLocation$1 = (forward, isInlineTarget2, rootNode, pos) => {
        const from2 = normalizePosition(forward, pos);
        const fromLocation = readLocation(isInlineTarget2, rootNode, from2);
        return readLocation(isInlineTarget2, rootNode, from2).bind(curry(findLocationSimple, forward)).orThunk(() => findLocationTraverse(forward, isInlineTarget2, rootNode, fromLocation, pos));
      };
      const hasSelectionModifyApi = (editor) => {
        return isFunction(editor.selection.getSel().modify);
      };
      const moveRel = (forward, selection, pos) => {
        const delta = forward ? 1 : -1;
        selection.setRng(CaretPosition(pos.container(), pos.offset() + delta).toRange());
        selection.getSel().modify("move", forward ? "forward" : "backward", "word");
        return true;
      };
      const moveByWord = (forward, editor) => {
        const rng = editor.selection.getRng();
        const pos = forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng);
        if (!hasSelectionModifyApi(editor)) {
          return false;
        } else if (forward && isBeforeInline(pos)) {
          return moveRel(true, editor.selection, pos);
        } else if (!forward && isAfterInline(pos)) {
          return moveRel(false, editor.selection, pos);
        } else {
          return false;
        }
      };
      var BreakType;
      (function(BreakType2) {
        BreakType2[BreakType2["Br"] = 0] = "Br";
        BreakType2[BreakType2["Block"] = 1] = "Block";
        BreakType2[BreakType2["Wrap"] = 2] = "Wrap";
        BreakType2[BreakType2["Eol"] = 3] = "Eol";
      })(BreakType || (BreakType = {}));
      const flip = (direction, positions) => direction === -1 ? reverse(positions) : positions;
      const walk$1 = (direction, caretWalker, pos) => direction === 1 ? caretWalker.next(pos) : caretWalker.prev(pos);
      const getBreakType = (scope, direction, currentPos, nextPos) => {
        if (isBr$7(nextPos.getNode(
          direction === 1
          /* HDirection.Forwards */
        ))) {
          return BreakType.Br;
        } else if (isInSameBlock(currentPos, nextPos) === false) {
          return BreakType.Block;
        } else {
          return BreakType.Wrap;
        }
      };
      const getPositionsUntil = (predicate, direction, scope, start2) => {
        const caretWalker = CaretWalker(scope);
        let currentPos = start2;
        const positions = [];
        while (currentPos) {
          const nextPos = walk$1(direction, caretWalker, currentPos);
          if (!nextPos) {
            break;
          }
          if (isBr$7(nextPos.getNode(false))) {
            if (direction === 1) {
              return { positions: flip(direction, positions).concat([nextPos]), breakType: BreakType.Br, breakAt: Optional.some(nextPos) };
            } else {
              return { positions: flip(direction, positions), breakType: BreakType.Br, breakAt: Optional.some(nextPos) };
            }
          }
          if (!nextPos.isVisible()) {
            currentPos = nextPos;
            continue;
          }
          if (predicate(currentPos, nextPos)) {
            const breakType = getBreakType(scope, direction, currentPos, nextPos);
            return { positions: flip(direction, positions), breakType, breakAt: Optional.some(nextPos) };
          }
          positions.push(nextPos);
          currentPos = nextPos;
        }
        return { positions: flip(direction, positions), breakType: BreakType.Eol, breakAt: Optional.none() };
      };
      const getAdjacentLinePositions = (direction, getPositionsUntilBreak, scope, start2) => getPositionsUntilBreak(scope, start2).breakAt.map((pos) => {
        const positions = getPositionsUntilBreak(scope, pos).positions;
        return direction === -1 ? positions.concat(pos) : [pos].concat(positions);
      }).getOr([]);
      const findClosestHorizontalPositionFromPoint = (positions, x) => foldl(positions, (acc, newPos) => acc.fold(() => Optional.some(newPos), (lastPos) => lift2(head(lastPos.getClientRects()), head(newPos.getClientRects()), (lastRect, newRect) => {
        const lastDist = Math.abs(x - lastRect.left);
        const newDist = Math.abs(x - newRect.left);
        return newDist <= lastDist ? newPos : lastPos;
      }).or(acc)), Optional.none());
      const findClosestHorizontalPosition = (positions, pos) => head(pos.getClientRects()).bind((targetRect) => findClosestHorizontalPositionFromPoint(positions, targetRect.left));
      const getPositionsUntilPreviousLine = curry(getPositionsUntil, CaretPosition.isAbove, -1);
      const getPositionsUntilNextLine = curry(getPositionsUntil, CaretPosition.isBelow, 1);
      const getPositionsAbove = curry(getAdjacentLinePositions, -1, getPositionsUntilPreviousLine);
      const getPositionsBelow = curry(getAdjacentLinePositions, 1, getPositionsUntilNextLine);
      const isAtFirstLine = (scope, pos) => getPositionsUntilPreviousLine(scope, pos).breakAt.isNone();
      const isAtLastLine = (scope, pos) => getPositionsUntilNextLine(scope, pos).breakAt.isNone();
      const getFirstLinePositions = (scope) => firstPositionIn(scope).map((pos) => [pos].concat(getPositionsUntilNextLine(scope, pos).positions)).getOr([]);
      const getLastLinePositions = (scope) => lastPositionIn(scope).map((pos) => getPositionsUntilPreviousLine(scope, pos).positions.concat(pos)).getOr([]);
      const getClosestPositionAbove = (scope, pos) => findClosestHorizontalPosition(getPositionsAbove(scope, pos), pos);
      const getClosestPositionBelow = (scope, pos) => findClosestHorizontalPosition(getPositionsBelow(scope, pos), pos);
      const isContentEditableFalse$4 = isContentEditableFalse$a;
      const distanceToRectLeft$1 = (clientRect, clientX) => Math.abs(clientRect.left - clientX);
      const distanceToRectRight$1 = (clientRect, clientX) => Math.abs(clientRect.right - clientX);
      const isNodeClientRect = (rect) => hasNonNullableKey(rect, "node");
      const findClosestClientRect = (clientRects, clientX) => reduce(clientRects, (oldClientRect, clientRect) => {
        const oldDistance = Math.min(distanceToRectLeft$1(oldClientRect, clientX), distanceToRectRight$1(oldClientRect, clientX));
        const newDistance = Math.min(distanceToRectLeft$1(clientRect, clientX), distanceToRectRight$1(clientRect, clientX));
        if (newDistance === oldDistance && isNodeClientRect(clientRect) && isContentEditableFalse$4(clientRect.node)) {
          return clientRect;
        }
        if (newDistance < oldDistance) {
          return clientRect;
        }
        return oldClientRect;
      });
      const getNodeClientRects = (node) => {
        const toArrayWithNode = (clientRects) => {
          return map$3(clientRects, (rect) => {
            const clientRect = clone$1(rect);
            clientRect.node = node;
            return clientRect;
          });
        };
        if (isElement$7(node)) {
          return toArrayWithNode(node.getClientRects());
        } else if (isText$b(node)) {
          const rng = node.ownerDocument.createRange();
          rng.setStart(node, 0);
          rng.setEnd(node, node.data.length);
          return toArrayWithNode(rng.getClientRects());
        } else {
          return [];
        }
      };
      const getClientRects = (nodes) => bind$3(nodes, getNodeClientRects);
      var VDirection;
      (function(VDirection2) {
        VDirection2[VDirection2["Up"] = -1] = "Up";
        VDirection2[VDirection2["Down"] = 1] = "Down";
      })(VDirection || (VDirection = {}));
      const findUntil = (direction, root, predicateFn, node) => {
        let currentNode = node;
        while (currentNode = findNode(currentNode, direction, isEditableCaretCandidate$1, root)) {
          if (predicateFn(currentNode)) {
            return;
          }
        }
      };
      const walkUntil = (direction, isAboveFn, isBeflowFn, root, predicateFn, caretPosition) => {
        let line = 0;
        const result = [];
        const add2 = (node2) => {
          let clientRects = getClientRects([node2]);
          if (direction === VDirection.Up) {
            clientRects = clientRects.reverse();
          }
          for (let i = 0; i < clientRects.length; i++) {
            const clientRect = clientRects[i];
            if (isBeflowFn(clientRect, targetClientRect)) {
              continue;
            }
            if (result.length > 0 && isAboveFn(clientRect, last(result))) {
              line++;
            }
            clientRect.line = line;
            if (predicateFn(clientRect)) {
              return true;
            }
            result.push(clientRect);
          }
          return false;
        };
        const targetClientRect = last(caretPosition.getClientRects());
        if (!targetClientRect) {
          return result;
        }
        const node = caretPosition.getNode();
        if (node) {
          add2(node);
          findUntil(direction, root, add2, node);
        }
        return result;
      };
      const aboveLineNumber = (lineNumber, clientRect) => clientRect.line > lineNumber;
      const isLineNumber = (lineNumber, clientRect) => clientRect.line === lineNumber;
      const upUntil = curry(walkUntil, VDirection.Up, isAbove$1, isBelow$1);
      const downUntil = curry(walkUntil, VDirection.Down, isBelow$1, isAbove$1);
      const getLastClientRect = (caretPosition) => {
        return last(caretPosition.getClientRects());
      };
      const positionsUntil = (direction, root, predicateFn, node) => {
        const caretWalker = CaretWalker(root);
        let walkFn;
        let isBelowFn;
        let isAboveFn;
        let caretPosition;
        const result = [];
        let line = 0;
        if (direction === VDirection.Down) {
          walkFn = caretWalker.next;
          isBelowFn = isBelow$1;
          isAboveFn = isAbove$1;
          caretPosition = CaretPosition.after(node);
        } else {
          walkFn = caretWalker.prev;
          isBelowFn = isAbove$1;
          isAboveFn = isBelow$1;
          caretPosition = CaretPosition.before(node);
        }
        const targetClientRect = getLastClientRect(caretPosition);
        do {
          if (!caretPosition.isVisible()) {
            continue;
          }
          const rect = getLastClientRect(caretPosition);
          if (isAboveFn(rect, targetClientRect)) {
            continue;
          }
          if (result.length > 0 && isBelowFn(rect, last(result))) {
            line++;
          }
          const clientRect = clone$1(rect);
          clientRect.position = caretPosition;
          clientRect.line = line;
          if (predicateFn(clientRect)) {
            return result;
          }
          result.push(clientRect);
        } while (caretPosition = walkFn(caretPosition));
        return result;
      };
      const isAboveLine = (lineNumber) => (clientRect) => aboveLineNumber(lineNumber, clientRect);
      const isLine = (lineNumber) => (clientRect) => isLineNumber(lineNumber, clientRect);
      const moveToRange = (editor, rng) => {
        editor.selection.setRng(rng);
        scrollRangeIntoView(editor, editor.selection.getRng());
      };
      const renderRangeCaretOpt = (editor, range2, scrollIntoView) => Optional.some(renderRangeCaret(editor, range2, scrollIntoView));
      const getAbsPositionElement = (pos, direction) => {
        const node = pos.getNode(
          direction === -1
          /* HDirection.Backwards */
        );
        return isNonNullable(node) && isAbsPositionedElement(node) ? Optional.some(node) : Optional.none();
      };
      const elementToRange = (editor, node) => {
        const rng = editor.dom.createRng();
        rng.selectNode(node);
        return rng;
      };
      const moveHorizontally = (editor, direction, range2, isBefore, isAfter, isElement2) => {
        const forwards = direction === 1;
        const caretWalker = CaretWalker(editor.getBody());
        const getNextPosFn = curry(getVisualCaretPosition, forwards ? caretWalker.next : caretWalker.prev);
        const isBeforeFn = forwards ? isBefore : isAfter;
        if (!range2.collapsed) {
          const node = getSelectedNode(range2);
          if (isElement2(node)) {
            if (isAbsPositionedElement(node)) {
              const caretPosition2 = getNormalizedRangeEndPoint(direction, editor.getBody(), range2);
              return Optional.from(getNextPosFn(caretPosition2)).map((next2) => next2.toRange());
            } else {
              return showCaret(direction, editor, node, direction === -1, false);
            }
          } else if (isCefAtEdgeSelected(editor)) {
            const newRange = range2.cloneRange();
            newRange.collapse(
              direction === -1
              /* HDirection.Backwards */
            );
            return Optional.from(newRange);
          }
        }
        const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range2);
        if (isBeforeFn(caretPosition)) {
          return selectNode(editor, caretPosition.getNode(!forwards));
        }
        let nextCaretPosition = getNextPosFn(caretPosition);
        const rangeIsInContainerBlock = isRangeInCaretContainerBlock(range2);
        if (!nextCaretPosition) {
          return rangeIsInContainerBlock ? Optional.some(range2) : Optional.none();
        } else {
          nextCaretPosition = normalizePosition(forwards, nextCaretPosition);
        }
        if (isBeforeFn(nextCaretPosition)) {
          return getAbsPositionElement(nextCaretPosition, direction).fold(() => showCaret(direction, editor, nextCaretPosition?.getNode(!forwards), forwards, false), (el) => Optional.some(elementToRange(editor, el)));
        }
        const peekCaretPosition = getNextPosFn(nextCaretPosition);
        if (peekCaretPosition && isBeforeFn(peekCaretPosition)) {
          if (isMoveInsideSameBlock(nextCaretPosition, peekCaretPosition)) {
            return getAbsPositionElement(nextCaretPosition, direction).fold(() => showCaret(direction, editor, peekCaretPosition.getNode(!forwards), forwards, false), (el) => Optional.some(elementToRange(editor, el)));
          }
        }
        if (rangeIsInContainerBlock) {
          return renderRangeCaretOpt(editor, nextCaretPosition.toRange(), false);
        }
        return Optional.none();
      };
      const moveVertically$1 = (editor, direction, range2, isBefore, isAfter, isElement2) => {
        const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range2);
        const caretClientRect = last(caretPosition.getClientRects());
        const forwards = direction === VDirection.Down;
        const root = editor.getBody();
        if (!caretClientRect) {
          return Optional.none();
        }
        if (isCefAtEdgeSelected(editor)) {
          const caretPosition2 = forwards ? CaretPosition.fromRangeEnd(range2) : CaretPosition.fromRangeStart(range2);
          const getClosestFn = !forwards ? getClosestPositionAbove : getClosestPositionBelow;
          return getClosestFn(root, caretPosition2).orThunk(() => Optional.from(caretPosition2)).map((pos) => pos.toRange());
        }
        const walkerFn = forwards ? downUntil : upUntil;
        const linePositions = walkerFn(root, isAboveLine(1), caretPosition);
        const nextLinePositions = filter$5(linePositions, isLine(1));
        const clientX = caretClientRect.left;
        const nextLineRect = findClosestClientRect(nextLinePositions, clientX);
        if (nextLineRect && isElement2(nextLineRect.node)) {
          const dist1 = Math.abs(clientX - nextLineRect.left);
          const dist2 = Math.abs(clientX - nextLineRect.right);
          return showCaret(direction, editor, nextLineRect.node, dist1 < dist2, false);
        }
        let currentNode;
        if (isBefore(caretPosition)) {
          currentNode = caretPosition.getNode();
        } else if (isAfter(caretPosition)) {
          currentNode = caretPosition.getNode(true);
        } else {
          currentNode = getSelectedNode(range2);
        }
        if (currentNode) {
          const caretPositions = positionsUntil(direction, root, isAboveLine(1), currentNode);
          let closestNextLineRect = findClosestClientRect(filter$5(caretPositions, isLine(1)), clientX);
          if (closestNextLineRect) {
            return renderRangeCaretOpt(editor, closestNextLineRect.position.toRange(), false);
          }
          closestNextLineRect = last(filter$5(caretPositions, isLine(0)));
          if (closestNextLineRect) {
            return renderRangeCaretOpt(editor, closestNextLineRect.position.toRange(), false);
          }
        }
        if (nextLinePositions.length === 0) {
          return getLineEndPoint(editor, forwards).filter(forwards ? isAfter : isBefore).map((pos) => renderRangeCaret(editor, pos.toRange(), false));
        }
        return Optional.none();
      };
      const getLineEndPoint = (editor, forward) => {
        const rng = editor.selection.getRng();
        const from2 = forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng);
        const host = getEditingHost(from2.container(), editor.getBody());
        if (forward) {
          const lineInfo = getPositionsUntilNextLine(host, from2);
          return last$2(lineInfo.positions);
        } else {
          const lineInfo = getPositionsUntilPreviousLine(host, from2);
          return head(lineInfo.positions);
        }
      };
      const moveToLineEndPoint$3 = (editor, forward, isElementPosition) => getLineEndPoint(editor, forward).filter(isElementPosition).exists((pos) => {
        editor.selection.setRng(pos.toRange());
        return true;
      });
      const setCaretPosition = (editor, pos) => {
        const rng = editor.dom.createRng();
        rng.setStart(pos.container(), pos.offset());
        rng.setEnd(pos.container(), pos.offset());
        editor.selection.setRng(rng);
      };
      const setSelected = (state, elm) => {
        if (state) {
          elm.setAttribute("data-mce-selected", "inline-boundary");
        } else {
          elm.removeAttribute("data-mce-selected");
        }
      };
      const renderCaretLocation = (editor, caret, location) => renderCaret(caret, location).map((pos) => {
        setCaretPosition(editor, pos);
        return location;
      });
      const getPositionFromRange = (range2, root, forward) => {
        const start2 = CaretPosition.fromRangeStart(range2);
        if (range2.collapsed) {
          return start2;
        } else {
          const end2 = CaretPosition.fromRangeEnd(range2);
          return forward ? prevPosition(root, end2).getOr(end2) : nextPosition(root, start2).getOr(start2);
        }
      };
      const findLocation = (editor, caret, forward) => {
        const rootNode = editor.getBody();
        const from2 = getPositionFromRange(editor.selection.getRng(), rootNode, forward);
        const isInlineTarget$1 = curry(isInlineTarget, editor);
        const location = findLocation$1(forward, isInlineTarget$1, rootNode, from2);
        return location.bind((location2) => renderCaretLocation(editor, caret, location2));
      };
      const toggleInlines = (isInlineTarget2, dom2, elms) => {
        const inlineBoundaries = map$3(descendants(SugarElement.fromDom(dom2.getRoot()), '*[data-mce-selected="inline-boundary"]'), (e) => e.dom);
        const selectedInlines = filter$5(inlineBoundaries, isInlineTarget2);
        const targetInlines = filter$5(elms, isInlineTarget2);
        each$e(difference(selectedInlines, targetInlines), curry(setSelected, false));
        each$e(difference(targetInlines, selectedInlines), curry(setSelected, true));
      };
      const safeRemoveCaretContainer = (editor, caret) => {
        const caretValue = caret.get();
        if (editor.selection.isCollapsed() && !editor.composing && caretValue) {
          const pos = CaretPosition.fromRangeStart(editor.selection.getRng());
          if (CaretPosition.isTextPosition(pos) && !isAtZwsp(pos)) {
            setCaretPosition(editor, removeAndReposition(caretValue, pos));
            caret.set(null);
          }
        }
      };
      const renderInsideInlineCaret = (isInlineTarget2, editor, caret, elms) => {
        if (editor.selection.isCollapsed()) {
          const inlines = filter$5(elms, isInlineTarget2);
          each$e(inlines, (_inline) => {
            const pos = CaretPosition.fromRangeStart(editor.selection.getRng());
            readLocation(isInlineTarget2, editor.getBody(), pos).bind((location) => renderCaretLocation(editor, caret, location));
          });
        }
      };
      const move$3 = (editor, caret, forward) => isInlineBoundariesEnabled(editor) ? findLocation(editor, caret, forward).isSome() : false;
      const moveWord = (forward, editor, _caret) => isInlineBoundariesEnabled(editor) ? moveByWord(forward, editor) : false;
      const setupSelectedState = (editor) => {
        const caret = Cell(null);
        const isInlineTarget$1 = curry(isInlineTarget, editor);
        editor.on("NodeChange", (e) => {
          if (isInlineBoundariesEnabled(editor)) {
            toggleInlines(isInlineTarget$1, editor.dom, e.parents);
            safeRemoveCaretContainer(editor, caret);
            renderInsideInlineCaret(isInlineTarget$1, editor, caret, e.parents);
          }
        });
        return caret;
      };
      const moveNextWord = curry(moveWord, true);
      const movePrevWord = curry(moveWord, false);
      const moveToLineEndPoint$2 = (editor, forward, caret) => {
        if (isInlineBoundariesEnabled(editor)) {
          const linePoint = getLineEndPoint(editor, forward).getOrThunk(() => {
            const rng = editor.selection.getRng();
            return forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng);
          });
          return readLocation(curry(isInlineTarget, editor), editor.getBody(), linePoint).exists((loc) => {
            const outsideLoc = outside(loc);
            return renderCaret(caret, outsideLoc).exists((pos) => {
              setCaretPosition(editor, pos);
              return true;
            });
          });
        } else {
          return false;
        }
      };
      const rangeFromPositions = (from2, to2) => {
        const range2 = document.createRange();
        range2.setStart(from2.container(), from2.offset());
        range2.setEnd(to2.container(), to2.offset());
        return range2;
      };
      const hasOnlyTwoOrLessPositionsLeft = (elm) => lift2(firstPositionIn(elm), lastPositionIn(elm), (firstPos, lastPos) => {
        const normalizedFirstPos = normalizePosition(true, firstPos);
        const normalizedLastPos = normalizePosition(false, lastPos);
        return nextPosition(elm, normalizedFirstPos).forall((pos) => pos.isEqual(normalizedLastPos));
      }).getOr(true);
      const setCaretLocation = (editor, caret) => (location) => renderCaret(caret, location).map((pos) => () => setCaretPosition(editor, pos));
      const deleteFromTo = (editor, caret, from2, to2) => {
        const rootNode = editor.getBody();
        const isInlineTarget$1 = curry(isInlineTarget, editor);
        editor.undoManager.ignore(() => {
          editor.selection.setRng(rangeFromPositions(from2, to2));
          execNativeDeleteCommand(editor);
          readLocation(isInlineTarget$1, rootNode, CaretPosition.fromRangeStart(editor.selection.getRng())).map(inside).bind(setCaretLocation(editor, caret)).each(call);
        });
        editor.nodeChanged();
      };
      const rescope = (rootNode, node) => {
        const parentBlock = getParentBlock$3(node, rootNode);
        return parentBlock ? parentBlock : rootNode;
      };
      const backspaceDeleteCollapsed = (editor, caret, forward, from2) => {
        const rootNode = rescope(editor.getBody(), from2.container());
        const isInlineTarget$1 = curry(isInlineTarget, editor);
        const fromLocation = readLocation(isInlineTarget$1, rootNode, from2);
        const location = fromLocation.bind((location2) => {
          if (forward) {
            return location2.fold(
              constant(Optional.some(inside(location2))),
              // Before
              Optional.none,
              // Start
              constant(Optional.some(outside(location2))),
              // End
              Optional.none
              // After
            );
          } else {
            return location2.fold(
              Optional.none,
              // Before
              constant(Optional.some(outside(location2))),
              // Start
              Optional.none,
              // End
              constant(Optional.some(inside(location2)))
              // After
            );
          }
        });
        return location.map(setCaretLocation(editor, caret)).getOrThunk(() => {
          const toPosition = navigate(forward, rootNode, from2);
          const toLocation = toPosition.bind((pos) => readLocation(isInlineTarget$1, rootNode, pos));
          return lift2(fromLocation, toLocation, () => findRootInline(isInlineTarget$1, rootNode, from2).bind((elm) => {
            if (hasOnlyTwoOrLessPositionsLeft(elm)) {
              return Optional.some(() => {
                deleteElement$2(editor, forward, SugarElement.fromDom(elm));
              });
            } else {
              return Optional.none();
            }
          })).getOrThunk(() => toLocation.bind(() => toPosition.map((to2) => {
            return () => {
              if (forward) {
                deleteFromTo(editor, caret, from2, to2);
              } else {
                deleteFromTo(editor, caret, to2, from2);
              }
            };
          })));
        });
      };
      const backspaceDelete$5 = (editor, caret, forward) => {
        if (editor.selection.isCollapsed() && isInlineBoundariesEnabled(editor)) {
          const from2 = CaretPosition.fromRangeStart(editor.selection.getRng());
          return backspaceDeleteCollapsed(editor, caret, forward, from2);
        }
        return Optional.none();
      };
      const hasMultipleChildren = (elm) => childNodesCount(elm) > 1;
      const getParentsUntil = (editor, pred) => {
        const rootElm = SugarElement.fromDom(editor.getBody());
        const startElm = SugarElement.fromDom(editor.selection.getStart());
        const parents2 = parentsAndSelf(startElm, rootElm);
        return findIndex$2(parents2, pred).fold(constant(parents2), (index) => parents2.slice(0, index));
      };
      const hasOnlyOneChild = (elm) => childNodesCount(elm) === 1;
      const getParentInlinesUntilMultichildInline = (editor) => getParentsUntil(editor, (elm) => editor.schema.isBlock(name(elm)) || hasMultipleChildren(elm));
      const getParentInlines = (editor) => getParentsUntil(editor, (el) => editor.schema.isBlock(name(el)));
      const getFormatNodes = (editor, parentInlines) => {
        const isFormatElement$1 = curry(isFormatElement, editor);
        return bind$3(parentInlines, (elm) => isFormatElement$1(elm) ? [elm.dom] : []);
      };
      const getFormatNodesAtStart = (editor) => {
        const parentInlines = getParentInlines(editor);
        return getFormatNodes(editor, parentInlines);
      };
      const deleteLastPosition = (forward, editor, target, parentInlines) => {
        const formatNodes = getFormatNodes(editor, parentInlines);
        if (formatNodes.length === 0) {
          deleteElement$2(editor, forward, target);
        } else {
          const pos = replaceWithCaretFormat(target.dom, formatNodes);
          editor.selection.setRng(pos.toRange());
        }
      };
      const deleteCaret$1 = (editor, forward) => {
        const parentInlines = filter$5(getParentInlinesUntilMultichildInline(editor), hasOnlyOneChild);
        return last$2(parentInlines).bind((target) => {
          const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng());
          if (willDeleteLastPositionInElement(forward, fromPos, target.dom) && !isEmptyCaretFormatElement(target)) {
            return Optional.some(() => deleteLastPosition(forward, editor, target, parentInlines));
          } else {
            return Optional.none();
          }
        });
      };
      const isBrInEmptyElement = (editor, elm) => {
        const parentElm = elm.parentElement;
        return isBr$7(elm) && !isNull(parentElm) && editor.dom.isEmpty(parentElm);
      };
      const isEmptyCaret = (elm) => isEmptyCaretFormatElement(SugarElement.fromDom(elm));
      const createCaretFormatAtStart = (editor, formatNodes) => {
        const startElm = editor.selection.getStart();
        const pos = isBrInEmptyElement(editor, startElm) || isEmptyCaret(startElm) ? replaceWithCaretFormat(startElm, formatNodes) : createCaretFormatAtStart$1(editor.selection.getRng(), formatNodes);
        editor.selection.setRng(pos.toRange());
      };
      const updateCaretFormat = (editor, updateFormats) => {
        const missingFormats = difference(updateFormats, getFormatNodesAtStart(editor));
        if (missingFormats.length > 0) {
          createCaretFormatAtStart(editor, missingFormats);
        }
      };
      const rangeStartsAtTextContainer = (rng) => isText$b(rng.startContainer);
      const rangeStartsAtStartOfTextContainer = (rng) => rng.startOffset === 0 && rangeStartsAtTextContainer(rng);
      const rangeStartParentIsFormatElement = (editor, rng) => {
        const startParent = rng.startContainer.parentElement;
        return !isNull(startParent) && isFormatElement(editor, SugarElement.fromDom(startParent));
      };
      const rangeStartAndEndHaveSameParent = (rng) => {
        const startParent = rng.startContainer.parentNode;
        const endParent = rng.endContainer.parentNode;
        return !isNull(startParent) && !isNull(endParent) && startParent.isEqualNode(endParent);
      };
      const rangeEndsAtEndOfEndContainer = (rng) => {
        const endContainer = rng.endContainer;
        return rng.endOffset === (isText$b(endContainer) ? endContainer.length : endContainer.childNodes.length);
      };
      const rangeEndsAtEndOfStartContainer = (rng) => rangeStartAndEndHaveSameParent(rng) && rangeEndsAtEndOfEndContainer(rng);
      const rangeEndsAfterEndOfStartContainer = (rng) => !rng.endContainer.isEqualNode(rng.commonAncestorContainer);
      const rangeEndsAtOrAfterEndOfStartContainer = (rng) => rangeEndsAtEndOfStartContainer(rng) || rangeEndsAfterEndOfStartContainer(rng);
      const requiresDeleteRangeOverride = (editor) => {
        const rng = editor.selection.getRng();
        return rangeStartsAtStartOfTextContainer(rng) && rangeStartParentIsFormatElement(editor, rng) && rangeEndsAtOrAfterEndOfStartContainer(rng);
      };
      const deleteRange$2 = (editor) => {
        if (requiresDeleteRangeOverride(editor)) {
          const formatNodes = getFormatNodesAtStart(editor);
          return Optional.some(() => {
            execNativeDeleteCommand(editor);
            updateCaretFormat(editor, formatNodes);
          });
        } else {
          return Optional.none();
        }
      };
      const backspaceDelete$4 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$1(editor, forward) : deleteRange$2(editor);
      const hasAncestorInlineCaret = (elm, schema) => ancestor$3(elm, (node) => isCaretNode(node.dom), (el) => schema.isBlock(name(el)));
      const hasAncestorInlineCaretAtStart = (editor) => hasAncestorInlineCaret(SugarElement.fromDom(editor.selection.getStart()), editor.schema);
      const requiresRefreshCaretOverride = (editor) => {
        const rng = editor.selection.getRng();
        return rng.collapsed && (rangeStartsAtTextContainer(rng) || editor.dom.isEmpty(rng.startContainer)) && !hasAncestorInlineCaretAtStart(editor);
      };
      const refreshCaret = (editor) => {
        if (requiresRefreshCaretOverride(editor)) {
          createCaretFormatAtStart(editor, []);
        }
        return true;
      };
      const deleteElement = (editor, forward, element) => {
        if (isNonNullable(element)) {
          return Optional.some(() => {
            editor._selectionOverrides.hideFakeCaret();
            deleteElement$2(editor, forward, SugarElement.fromDom(element));
          });
        } else {
          return Optional.none();
        }
      };
      const deleteCaret = (editor, forward) => {
        const isNearMedia = forward ? isBeforeMedia : isAfterMedia;
        const direction = forward ? 1 : -1;
        const fromPos = getNormalizedRangeEndPoint(direction, editor.getBody(), editor.selection.getRng());
        if (isNearMedia(fromPos)) {
          return deleteElement(editor, forward, fromPos.getNode(!forward));
        } else {
          return Optional.from(normalizePosition(forward, fromPos)).filter((pos) => isNearMedia(pos) && isMoveInsideSameBlock(fromPos, pos)).bind((pos) => deleteElement(editor, forward, pos.getNode(!forward)));
        }
      };
      const deleteRange$1 = (editor, forward) => {
        const selectedNode = editor.selection.getNode();
        return isMedia$2(selectedNode) ? deleteElement(editor, forward, selectedNode) : Optional.none();
      };
      const backspaceDelete$3 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret(editor, forward) : deleteRange$1(editor, forward);
      const isEditable = (target) => closest$4(target, (elm) => isContentEditableTrue$3(elm.dom) || isContentEditableFalse$a(elm.dom)).exists((elm) => isContentEditableTrue$3(elm.dom));
      const parseIndentValue = (value2) => toInt(value2 ?? "").getOr(0);
      const getIndentStyleName = (useMargin, element) => {
        const indentStyleName = useMargin || isTable$1(element) ? "margin" : "padding";
        const suffix = get$7(element, "direction") === "rtl" ? "-right" : "-left";
        return indentStyleName + suffix;
      };
      const indentElement = (dom2, command, useMargin, value2, unit, element) => {
        const indentStyleName = getIndentStyleName(useMargin, SugarElement.fromDom(element));
        const parsedValue = parseIndentValue(dom2.getStyle(element, indentStyleName));
        if (command === "outdent") {
          const styleValue = Math.max(0, parsedValue - value2);
          dom2.setStyle(element, indentStyleName, styleValue ? styleValue + unit : "");
        } else {
          const styleValue = parsedValue + value2 + unit;
          dom2.setStyle(element, indentStyleName, styleValue);
        }
      };
      const validateBlocks = (editor, blocks) => forall(blocks, (block) => {
        const indentStyleName = getIndentStyleName(shouldIndentUseMargin(editor), block);
        const intentValue = getRaw$1(block, indentStyleName).map(parseIndentValue).getOr(0);
        const contentEditable = editor.dom.getContentEditable(block.dom);
        return contentEditable !== "false" && intentValue > 0;
      });
      const canOutdent = (editor) => {
        const blocks = getBlocksToIndent(editor);
        return !editor.mode.isReadOnly() && (blocks.length > 1 || validateBlocks(editor, blocks));
      };
      const canIndent = (editor) => !editor.mode.isReadOnly() && canIndent$1(editor);
      const isListComponent = (el) => isList$1(el) || isListItem$2(el);
      const parentIsListComponent = (el) => parent(el).exists(isListComponent);
      const getBlocksToIndent = (editor) => filter$5(fromDom$1(editor.selection.getSelectedBlocks()), (el) => !isListComponent(el) && !parentIsListComponent(el) && isEditable(el));
      const handle = (editor, command) => {
        if (editor.mode.isReadOnly()) {
          return;
        }
        const { dom: dom2 } = editor;
        const indentation = getIndentation(editor);
        const indentUnit = /[a-z%]+$/i.exec(indentation)?.[0] ?? "px";
        const indentValue = parseIndentValue(indentation);
        const useMargin = shouldIndentUseMargin(editor);
        each$e(getBlocksToIndent(editor), (block) => {
          indentElement(dom2, command, useMargin, indentValue, indentUnit, block.dom);
        });
        if (command === "indent") {
          indentListSelection(editor);
        } else {
          outdentListSelection(editor);
        }
      };
      const indent = (editor) => handle(editor, "indent");
      const outdent = (editor) => handle(editor, "outdent");
      const backspaceDelete$2 = (editor) => {
        if (editor.selection.isCollapsed() && canOutdent(editor)) {
          const dom2 = editor.dom;
          const rng = editor.selection.getRng();
          const pos = CaretPosition.fromRangeStart(rng);
          const block = dom2.getParent(rng.startContainer, dom2.isBlock);
          if (block !== null && isAtStartOfBlock(SugarElement.fromDom(block), pos, editor.schema)) {
            return Optional.some(() => outdent(editor));
          }
        }
        return Optional.none();
      };
      const deleteRange = (editor, forward) => {
        const rng = normalize(editor.selection.getRng());
        return isSelectionOverWholeHTMLElement(rng) ? Optional.some(() => deleteElement$2(editor, forward, SugarElement.fromDom(editor.selection.getNode()))) : Optional.none();
      };
      const backspaceDelete$1 = (editor, forward) => editor.selection.isCollapsed() ? Optional.none() : deleteRange(editor, forward);
      const findAction = (editor, caret, forward) => findMap([
        backspaceDelete$2,
        backspaceDelete$8,
        backspaceDelete$9,
        (editor2, forward2) => backspaceDelete$5(editor2, caret, forward2),
        backspaceDelete$b,
        backspaceDelete$d,
        backspaceDelete$6,
        backspaceDelete$3,
        backspaceDelete$a,
        backspaceDelete$4,
        backspaceDelete$7,
        backspaceDelete$1
      ], (item) => item(editor, forward)).filter((_) => editor.selection.isEditable());
      const deleteCommand = (editor, caret) => {
        const result = findAction(editor, caret, false);
        result.fold(() => {
          if (editor.selection.isEditable()) {
            execNativeDeleteCommand(editor);
            paddEmptyBody(editor);
          }
        }, call);
        if (hasListSelection(editor)) {
          normalizeLists(editor.dom, editor.getBody());
        }
      };
      const forwardDeleteCommand = (editor, caret) => {
        const result = findAction(editor, caret, true);
        result.fold(() => {
          if (editor.selection.isEditable()) {
            execNativeForwardDeleteCommand(editor);
          }
        }, call);
        if (hasListSelection(editor)) {
          normalizeLists(editor.dom, editor.getBody());
        }
      };
      const setup$v = (editor, caret) => {
        editor.addCommand("delete", () => {
          deleteCommand(editor, caret);
        });
        editor.addCommand("forwardDelete", () => {
          forwardDeleteCommand(editor, caret);
        });
      };
      const SIGNIFICANT_MOVE = 5;
      const LONGPRESS_DELAY = 400;
      const getTouch = (event) => {
        if (event.touches === void 0 || event.touches.length !== 1) {
          return Optional.none();
        }
        return Optional.some(event.touches[0]);
      };
      const isFarEnough = (touch, data2) => {
        const distX = Math.abs(touch.clientX - data2.x);
        const distY = Math.abs(touch.clientY - data2.y);
        return distX > SIGNIFICANT_MOVE || distY > SIGNIFICANT_MOVE;
      };
      const setup$u = (editor) => {
        const startData = value$1();
        const longpressFired = Cell(false);
        const debounceLongpress = last$1((e) => {
          editor.dispatch("longpress", { ...e, type: "longpress" });
          longpressFired.set(true);
        }, LONGPRESS_DELAY);
        editor.on("touchstart", (e) => {
          getTouch(e).each((touch) => {
            debounceLongpress.cancel();
            const data2 = {
              x: touch.clientX,
              y: touch.clientY,
              target: e.target
            };
            debounceLongpress.throttle(e);
            longpressFired.set(false);
            startData.set(data2);
          });
        }, true);
        editor.on("touchmove", (e) => {
          debounceLongpress.cancel();
          getTouch(e).each((touch) => {
            startData.on((data2) => {
              if (isFarEnough(touch, data2)) {
                startData.clear();
                longpressFired.set(false);
                editor.dispatch("longpresscancel");
              }
            });
          });
        }, true);
        editor.on("touchend touchcancel", (e) => {
          debounceLongpress.cancel();
          if (e.type === "touchcancel") {
            return;
          }
          startData.get().filter((data2) => data2.target.isEqualNode(e.target)).each(() => {
            if (longpressFired.get()) {
              e.preventDefault();
            } else {
              editor.dispatch("tap", { ...e, type: "tap" });
            }
          });
        }, true);
      };
      const isBlockElement = (blockElements, node) => has$2(blockElements, node.nodeName);
      const isValidTarget = (schema, node) => {
        if (isText$b(node)) {
          return true;
        } else if (isElement$7(node)) {
          return !isBlockElement(schema.getBlockElements(), node) && !isBookmarkNode$1(node) && !isTransparentBlock(schema, node) && !isNonHtmlElementRoot(node) && !isTemplate(node);
        } else {
          return false;
        }
      };
      const hasBlockParent = (blockElements, root, node) => {
        return exists(parents(SugarElement.fromDom(node), SugarElement.fromDom(root)), (elm) => {
          return isBlockElement(blockElements, elm.dom);
        });
      };
      const shouldRemoveTextNode = (blockElements, node) => {
        if (isText$b(node)) {
          if (node.data.length === 0) {
            return true;
          } else if (/^\s+$/.test(node.data)) {
            return !node.nextSibling || isBlockElement(blockElements, node.nextSibling) || isNonHtmlElementRoot(node.nextSibling);
          }
        }
        return false;
      };
      const createRootBlock = (editor) => editor.dom.create(getForcedRootBlock(editor), getForcedRootBlockAttrs(editor));
      const addRootBlocks = (editor) => {
        const dom2 = editor.dom, selection = editor.selection;
        const schema = editor.schema;
        const blockElements = schema.getBlockElements();
        const startNode = selection.getStart();
        const rootNode = editor.getBody();
        let rootBlockNode;
        let tempNode;
        let bm = null;
        const forcedRootBlock = getForcedRootBlock(editor);
        if (!startNode || !isElement$7(startNode)) {
          return;
        }
        const rootNodeName = rootNode.nodeName.toLowerCase();
        if (!schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase()) || hasBlockParent(blockElements, rootNode, startNode)) {
          return;
        }
        if (rootNode.firstChild === rootNode.lastChild && isBr$7(rootNode.firstChild)) {
          rootBlockNode = createRootBlock(editor);
          rootBlockNode.appendChild(createPaddingBr().dom);
          rootNode.replaceChild(rootBlockNode, rootNode.firstChild);
          editor.selection.setCursorLocation(rootBlockNode, 0);
          editor.nodeChanged();
          return;
        }
        let node = rootNode.firstChild;
        while (node) {
          if (isElement$7(node)) {
            updateElement(schema, node);
          }
          if (isValidTarget(schema, node)) {
            if (shouldRemoveTextNode(blockElements, node)) {
              tempNode = node;
              node = node.nextSibling;
              dom2.remove(tempNode);
              continue;
            }
            if (!rootBlockNode) {
              if (!bm && editor.hasFocus()) {
                bm = createBookmark(editor.selection.getRng(), () => document.createElement("span"));
              }
              if (!node.parentNode) {
                node = null;
                break;
              }
              rootBlockNode = createRootBlock(editor);
              rootNode.insertBefore(rootBlockNode, node);
            }
            tempNode = node;
            node = node.nextSibling;
            rootBlockNode.appendChild(tempNode);
          } else {
            rootBlockNode = null;
            node = node.nextSibling;
          }
        }
        if (bm) {
          editor.selection.setRng(resolveBookmark(bm));
          editor.nodeChanged();
        }
      };
      const insertEmptyLine = (editor, root, insertBlock2) => {
        const block = SugarElement.fromDom(createRootBlock(editor));
        const br = createPaddingBr();
        append$1(block, br);
        insertBlock2(root, block);
        const rng = document.createRange();
        rng.setStartBefore(br.dom);
        rng.setEndBefore(br.dom);
        return rng;
      };
      const setup$t = (editor) => {
        editor.on("NodeChange", () => addRootBlocks(editor));
      };
      const hasClass = (checkClassName) => (node) => (" " + node.attr("class") + " ").indexOf(checkClassName) !== -1;
      const replaceMatchWithSpan = (editor, content, cls) => {
        return function(match2) {
          const args = arguments, index = args[args.length - 2];
          const prevChar = index > 0 ? content.charAt(index - 1) : "";
          if (prevChar === '"') {
            return match2;
          }
          if (prevChar === ">") {
            const findStartTagIndex = content.lastIndexOf("<", index);
            if (findStartTagIndex !== -1) {
              const tagHtml = content.substring(findStartTagIndex, index);
              if (tagHtml.indexOf('contenteditable="false"') !== -1) {
                return match2;
              }
            }
          }
          return '<span class="' + cls + '" data-mce-content="' + editor.dom.encode(args[0]) + '">' + editor.dom.encode(typeof args[1] === "string" ? args[1] : args[0]) + "</span>";
        };
      };
      const convertRegExpsToNonEditable = (editor, nonEditableRegExps, e) => {
        let i = nonEditableRegExps.length, content = e.content;
        if (e.format === "raw") {
          return;
        }
        while (i--) {
          content = content.replace(nonEditableRegExps[i], replaceMatchWithSpan(editor, content, getNonEditableClass(editor)));
        }
        e.content = content;
      };
      const isValidContent = (nonEditableRegExps, content) => {
        return forall(nonEditableRegExps, (re) => {
          const matches = content.match(re);
          return matches !== null && matches[0].length === content.length;
        });
      };
      const setup$s = (editor) => {
        const contentEditableAttrName = "contenteditable";
        const editClass = " " + Tools.trim(getEditableClass(editor)) + " ";
        const nonEditClass = " " + Tools.trim(getNonEditableClass(editor)) + " ";
        const hasEditClass = hasClass(editClass);
        const hasNonEditClass = hasClass(nonEditClass);
        const nonEditableRegExps = getNonEditableRegExps(editor);
        if (nonEditableRegExps.length > 0) {
          editor.on("BeforeSetContent", (e) => {
            convertRegExpsToNonEditable(editor, nonEditableRegExps, e);
          });
        }
        editor.parser.addAttributeFilter("class", (nodes) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            if (hasEditClass(node)) {
              node.attr(contentEditableAttrName, "true");
            } else if (hasNonEditClass(node)) {
              node.attr(contentEditableAttrName, "false");
            }
          }
        });
        editor.serializer.addAttributeFilter(contentEditableAttrName, (nodes) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            if (!hasEditClass(node) && !hasNonEditClass(node)) {
              continue;
            }
            const content = node.attr("data-mce-content");
            if (nonEditableRegExps.length > 0 && content) {
              if (isValidContent(nonEditableRegExps, content)) {
                node.name = "#text";
                node.type = 3;
                node.raw = true;
                node.value = content;
              } else {
                node.remove();
              }
            } else {
              node.attr(contentEditableAttrName, null);
            }
          }
        });
      };
      const findBlockCaretContainer = (editor) => descendant$1(SugarElement.fromDom(editor.getBody()), "*[data-mce-caret]").map((elm) => elm.dom).getOrNull();
      const showBlockCaretContainer = (editor, blockCaretContainer) => {
        if (blockCaretContainer.hasAttribute("data-mce-caret")) {
          showCaretContainerBlock(blockCaretContainer);
          editor.selection.setRng(editor.selection.getRng());
          editor.selection.scrollIntoView(blockCaretContainer);
        }
      };
      const handleBlockContainer = (editor, e) => {
        const blockCaretContainer = findBlockCaretContainer(editor);
        if (!blockCaretContainer) {
          return;
        }
        if (e.type === "compositionstart") {
          e.preventDefault();
          e.stopPropagation();
          showBlockCaretContainer(editor, blockCaretContainer);
          return;
        }
        if (hasContent(blockCaretContainer)) {
          showBlockCaretContainer(editor, blockCaretContainer);
          editor.undoManager.add();
        }
      };
      const setup$r = (editor) => {
        editor.on("keyup compositionstart", curry(handleBlockContainer, editor));
      };
      const isContentEditableFalse$3 = isContentEditableFalse$a;
      const moveToCeFalseHorizontally = (direction, editor, range2) => moveHorizontally(editor, direction, range2, isBeforeContentEditableFalse, isAfterContentEditableFalse, isContentEditableFalse$3);
      const moveToCeFalseVertically = (direction, editor, range2) => {
        const isBefore = (caretPosition) => isBeforeContentEditableFalse(caretPosition) || isBeforeTable(caretPosition);
        const isAfter = (caretPosition) => isAfterContentEditableFalse(caretPosition) || isAfterTable(caretPosition);
        return moveVertically$1(editor, direction, range2, isBefore, isAfter, isContentEditableFalse$3);
      };
      const createTextBlock = (editor) => {
        const textBlock = editor.dom.create(getForcedRootBlock(editor));
        textBlock.innerHTML = '<br data-mce-bogus="1">';
        return textBlock;
      };
      const exitPreBlock = (editor, direction, range2) => {
        const caretWalker = CaretWalker(editor.getBody());
        const getVisualCaretPosition$1 = curry(getVisualCaretPosition, direction === 1 ? caretWalker.next : caretWalker.prev);
        if (range2.collapsed) {
          const pre = editor.dom.getParent(range2.startContainer, "PRE");
          if (!pre) {
            return;
          }
          const caretPos = getVisualCaretPosition$1(CaretPosition.fromRangeStart(range2));
          if (!caretPos) {
            const newBlock = SugarElement.fromDom(createTextBlock(editor));
            if (direction === 1) {
              after$4(SugarElement.fromDom(pre), newBlock);
            } else {
              before$4(SugarElement.fromDom(pre), newBlock);
            }
            editor.selection.select(newBlock.dom, true);
            editor.selection.collapse();
          }
        }
      };
      const getHorizontalRange = (editor, forward) => {
        const direction = forward ? 1 : -1;
        const range2 = editor.selection.getRng();
        return moveToCeFalseHorizontally(direction, editor, range2).orThunk(() => {
          exitPreBlock(editor, direction, range2);
          return Optional.none();
        });
      };
      const getVerticalRange = (editor, down) => {
        const direction = down ? 1 : -1;
        const range2 = editor.selection.getRng();
        return moveToCeFalseVertically(direction, editor, range2).orThunk(() => {
          exitPreBlock(editor, direction, range2);
          return Optional.none();
        });
      };
      const flipDirection = (selection, forward) => {
        const elm = forward ? selection.getEnd(true) : selection.getStart(true);
        return isRtl(elm) ? !forward : forward;
      };
      const moveH$2 = (editor, forward) => getHorizontalRange(editor, flipDirection(editor.selection, forward)).exists((newRange) => {
        moveToRange(editor, newRange);
        return true;
      });
      const moveV$5 = (editor, down) => getVerticalRange(editor, down).exists((newRange) => {
        moveToRange(editor, newRange);
        return true;
      });
      const moveToLineEndPoint$1 = (editor, forward) => {
        const isCefPosition = forward ? isAfterContentEditableFalse : isBeforeContentEditableFalse;
        return moveToLineEndPoint$3(editor, forward, isCefPosition);
      };
      const selectToEndPoint = (editor, forward) => getEdgeCefPosition(editor, !forward).map((pos) => {
        const rng = pos.toRange();
        const curRng = editor.selection.getRng();
        if (forward) {
          rng.setStart(curRng.startContainer, curRng.startOffset);
        } else {
          rng.setEnd(curRng.endContainer, curRng.endOffset);
        }
        return rng;
      }).exists((rng) => {
        moveToRange(editor, rng);
        return true;
      });
      const getClosestCetBlock = (position, root) => {
        const isRoot2 = (el) => eq(el, root);
        const isCet = (el) => isContentEditableTrue$3(el.dom);
        const startNode = SugarElement.fromDom(position.container());
        const closestCetBlock = closest$4(startNode, isCet, isRoot2);
        return closestCetBlock.filter((b) => !isRoot2(b));
      };
      const moveVertically = (editor, position, down) => {
        const getNextPosition = down ? getClosestPositionBelow : getClosestPositionAbove;
        return getNextPosition(editor.getBody(), position).map((nextPosition2) => nextPosition2.toRange());
      };
      const moveToNextOrPreviousLine = (editor, down) => {
        const startPosition = CaretPosition.fromRangeStart(editor.selection.getRng());
        const endPosition = CaretPosition.fromRangeEnd(editor.selection.getRng());
        const root = SugarElement.fromDom(editor.getBody());
        const closestCetBlock = flatten(lift2(getClosestCetBlock(startPosition, root), getClosestCetBlock(endPosition, root), (c1, c2) => eq(c1, c2) ? Optional.some(c1) : Optional.none()));
        return closestCetBlock.fold(never, (cetBlock) => {
          if (down && isAtLastLine(cetBlock.dom, endPosition) || !down && isAtFirstLine(cetBlock.dom, startPosition)) {
            return moveVertically(editor, down ? endPosition : startPosition, down).exists((newRange) => {
              moveToRange(editor, newRange);
              return true;
            });
          }
          return false;
        });
      };
      const moveV$4 = (editor, down) => moveToNextOrPreviousLine(editor, down);
      const isTarget = (node) => contains$2(["figcaption"], name(node));
      const getClosestTargetBlock = (pos, root, schema) => {
        const isRoot2 = curry(eq, root);
        return closest$4(SugarElement.fromDom(pos.container()), (el) => schema.isBlock(name(el)), isRoot2).filter(isTarget);
      };
      const isAtFirstOrLastLine = (root, forward, pos) => forward ? isAtLastLine(root.dom, pos) : isAtFirstLine(root.dom, pos);
      const moveCaretToNewEmptyLine = (editor, forward) => {
        const root = SugarElement.fromDom(editor.getBody());
        const pos = CaretPosition.fromRangeStart(editor.selection.getRng());
        return getClosestTargetBlock(pos, root, editor.schema).exists(() => {
          if (isAtFirstOrLastLine(root, forward, pos)) {
            const insertFn = forward ? append$1 : prepend;
            const rng = insertEmptyLine(editor, root, insertFn);
            editor.selection.setRng(rng);
            return true;
          } else {
            return false;
          }
        });
      };
      const moveV$3 = (editor, forward) => {
        if (editor.selection.isCollapsed()) {
          return moveCaretToNewEmptyLine(editor, forward);
        } else {
          return false;
        }
      };
      const moveUp = (editor, details, summary) => {
        const rng = editor.selection.getRng();
        const pos = CaretPosition.fromRangeStart(rng);
        const root = editor.getBody();
        if (root.firstChild === details && isAtFirstLine(summary, pos)) {
          editor.execCommand("InsertNewBlockBefore");
          return true;
        } else {
          return false;
        }
      };
      const moveDown = (editor, details) => {
        const rng = editor.selection.getRng();
        const pos = CaretPosition.fromRangeStart(rng);
        const root = editor.getBody();
        if (root.lastChild === details && isAtLastLine(details, pos)) {
          editor.execCommand("InsertNewBlockAfter");
          return true;
        } else {
          return false;
        }
      };
      const move$2 = (editor, forward) => {
        if (forward) {
          return Optional.from(editor.dom.getParent(editor.selection.getNode(), "details")).map((details) => moveDown(editor, details)).getOr(false);
        } else {
          return Optional.from(editor.dom.getParent(editor.selection.getNode(), "summary")).bind((summary) => Optional.from(editor.dom.getParent(summary, "details")).map((details) => moveUp(editor, details, summary))).getOr(false);
        }
      };
      const moveV$2 = (editor, forward) => move$2(editor, forward);
      const baseKeyPattern = {
        shiftKey: false,
        altKey: false,
        ctrlKey: false,
        metaKey: false,
        keyCode: 0
      };
      const defaultPatterns = (patterns) => map$3(patterns, (pattern) => ({
        ...baseKeyPattern,
        ...pattern
      }));
      const defaultDelayedPatterns = (patterns) => map$3(patterns, (pattern) => ({
        ...baseKeyPattern,
        ...pattern
      }));
      const matchesEvent = (pattern, evt) => evt.keyCode === pattern.keyCode && evt.shiftKey === pattern.shiftKey && evt.altKey === pattern.altKey && evt.ctrlKey === pattern.ctrlKey && evt.metaKey === pattern.metaKey;
      const match$1 = (patterns, evt) => bind$3(defaultPatterns(patterns), (pattern) => matchesEvent(pattern, evt) ? [pattern] : []);
      const matchDelayed = (patterns, evt) => bind$3(defaultDelayedPatterns(patterns), (pattern) => matchesEvent(pattern, evt) ? [pattern] : []);
      const action = (f, ...x) => () => f.apply(null, x);
      const execute = (patterns, evt) => find$2(match$1(patterns, evt), (pattern) => pattern.action());
      const executeWithDelayedAction = (patterns, evt) => findMap(matchDelayed(patterns, evt), (pattern) => pattern.action());
      const moveH$1 = (editor, forward) => {
        const direction = forward ? 1 : -1;
        const range2 = editor.selection.getRng();
        return moveHorizontally(editor, direction, range2, isBeforeMedia, isAfterMedia, isMedia$2).exists((newRange) => {
          moveToRange(editor, newRange);
          return true;
        });
      };
      const moveV$1 = (editor, down) => {
        const direction = down ? 1 : -1;
        const range2 = editor.selection.getRng();
        return moveVertically$1(editor, direction, range2, isBeforeMedia, isAfterMedia, isMedia$2).exists((newRange) => {
          moveToRange(editor, newRange);
          return true;
        });
      };
      const moveToLineEndPoint = (editor, forward) => {
        const isNearMedia = forward ? isAfterMedia : isBeforeMedia;
        return moveToLineEndPoint$3(editor, forward, isNearMedia);
      };
      const firstLayer = (scope, selector) => {
        return filterFirstLayer(scope, selector, always);
      };
      const filterFirstLayer = (scope, selector, predicate) => {
        return bind$3(children$1(scope), (x) => {
          if (is$2(x, selector)) {
            return predicate(x) ? [x] : [];
          } else {
            return filterFirstLayer(x, selector, predicate);
          }
        });
      };
      const lookup$1 = (tags, element, isRoot2 = never) => {
        if (isRoot2(element)) {
          return Optional.none();
        }
        if (contains$2(tags, name(element))) {
          return Optional.some(element);
        }
        const isRootOrUpperTable = (elm) => is$2(elm, "table") || isRoot2(elm);
        return ancestor$4(element, tags.join(","), isRootOrUpperTable);
      };
      const cell = (element, isRoot2) => lookup$1(["td", "th"], element, isRoot2);
      const cells = (ancestor2) => firstLayer(ancestor2, "th,td");
      const table = (element, isRoot2) => closest$3(element, "table", isRoot2);
      const adt = Adt.generate([
        { none: ["current"] },
        { first: ["current"] },
        { middle: ["current", "target"] },
        { last: ["current"] }
      ]);
      const none = (current) => adt.none(current);
      const CellLocation = {
        ...adt,
        none
      };
      const walk = (all2, current, index, direction, isEligible = always) => {
        const forwards = direction === 1;
        if (!forwards && index <= 0) {
          return CellLocation.first(all2[0]);
        } else if (forwards && index >= all2.length - 1) {
          return CellLocation.last(all2[all2.length - 1]);
        } else {
          const newIndex = index + direction;
          const elem = all2[newIndex];
          return isEligible(elem) ? CellLocation.middle(current, elem) : walk(all2, current, newIndex, direction, isEligible);
        }
      };
      const detect = (current, isRoot2) => {
        return table(current, isRoot2).bind((table2) => {
          const all2 = cells(table2);
          const index = findIndex$2(all2, (x) => eq(current, x));
          return index.map((index2) => ({ index: index2, all: all2 }));
        });
      };
      const next = (current, isEligible, isRoot2) => {
        const detection = detect(current, isRoot2);
        return detection.fold(() => {
          return CellLocation.none(current);
        }, (info) => {
          return walk(info.all, current, info.index, 1, isEligible);
        });
      };
      const prev = (current, isEligible, isRoot2) => {
        const detection = detect(current, isRoot2);
        return detection.fold(() => {
          return CellLocation.none();
        }, (info) => {
          return walk(info.all, current, info.index, -1, isEligible);
        });
      };
      var TagBoundaries = [
        "body",
        "p",
        "div",
        "article",
        "aside",
        "figcaption",
        "figure",
        "footer",
        "header",
        "nav",
        "section",
        "ol",
        "ul",
        "li",
        "table",
        "thead",
        "tbody",
        "tfoot",
        "caption",
        "tr",
        "td",
        "th",
        "h1",
        "h2",
        "h3",
        "h4",
        "h5",
        "h6",
        "blockquote",
        "pre",
        "address"
      ];
      var DomUniverse = () => {
        const clone2 = (element) => {
          return SugarElement.fromDom(element.dom.cloneNode(false));
        };
        const document2 = (element) => documentOrOwner(element).dom;
        const isBoundary2 = (element) => {
          if (!isElement$8(element)) {
            return false;
          }
          if (name(element) === "body") {
            return true;
          }
          return contains$2(TagBoundaries, name(element));
        };
        const isEmptyTag = (element) => {
          if (!isElement$8(element)) {
            return false;
          }
          return contains$2(["br", "img", "hr", "input"], name(element));
        };
        const isNonEditable = (element) => isElement$8(element) && get$9(element, "contenteditable") === "false";
        const comparePosition = (element, other) => {
          return element.dom.compareDocumentPosition(other.dom);
        };
        const copyAttributesTo = (source, destination) => {
          const as = clone$4(source);
          setAll$1(destination, as);
        };
        const isSpecial = (element) => {
          const tag = name(element);
          return contains$2([
            "script",
            "noscript",
            "iframe",
            "noframes",
            "noembed",
            "title",
            "style",
            "textarea",
            "xmp"
          ], tag);
        };
        const getLanguage = (element) => isElement$8(element) ? getOpt(element, "lang") : Optional.none();
        return {
          up: constant({
            selector: ancestor$4,
            closest: closest$3,
            predicate: ancestor$5,
            all: parents$1
          }),
          down: constant({
            selector: descendants,
            predicate: descendants$1
          }),
          styles: constant({
            get: get$7,
            getRaw: getRaw$1,
            set: set$2,
            remove: remove$7
          }),
          attrs: constant({
            get: get$9,
            set: set$4,
            remove: remove$9,
            copyTo: copyAttributesTo
          }),
          insert: constant({
            before: before$4,
            after: after$4,
            afterAll: after$3,
            append: append$1,
            appendAll: append,
            prepend,
            wrap: wrap$2
          }),
          remove: constant({
            unwrap,
            remove: remove$8
          }),
          create: constant({
            nu: SugarElement.fromTag,
            clone: clone2,
            text: SugarElement.fromText
          }),
          query: constant({
            comparePosition,
            prevSibling,
            nextSibling
          }),
          property: constant({
            children: children$1,
            name,
            parent,
            document: document2,
            isText: isText$c,
            isComment: isComment$1,
            isElement: isElement$8,
            isSpecial,
            getLanguage,
            getText: get$4,
            setText: set$1,
            isBoundary: isBoundary2,
            isEmptyTag,
            isNonEditable
          }),
          eq,
          is: is$1
        };
      };
      const point$1 = (element, offset) => ({
        element,
        offset
      });
      const toLast$1 = (universe2, node) => {
        if (universe2.property().isText(node)) {
          return point$1(node, universe2.property().getText(node).length);
        } else {
          const children2 = universe2.property().children(node);
          return children2.length > 0 ? toLast$1(universe2, children2[children2.length - 1]) : point$1(node, children2.length);
        }
      };
      const toLeaf$3 = (universe2, element, offset) => {
        const children2 = universe2.property().children(element);
        if (children2.length > 0 && offset < children2.length) {
          return toLeaf$3(universe2, children2[offset], 0);
        } else if (children2.length > 0 && universe2.property().isElement(element) && children2.length === offset) {
          return toLast$1(universe2, children2[children2.length - 1]);
        } else {
          return point$1(element, offset);
        }
      };
      const toLeaf$2 = toLeaf$3;
      const universe = DomUniverse();
      const toLeaf$1 = (element, offset) => {
        return toLeaf$2(universe, element, offset);
      };
      const imageId = generate("image");
      const getDragImage = (transfer) => {
        const dt = transfer;
        return Optional.from(dt[imageId]);
      };
      const setDragImage = (transfer, imageData) => {
        const dt = transfer;
        dt[imageId] = imageData;
      };
      const eventId = generate("event");
      const getEvent = (transfer) => {
        const dt = transfer;
        return Optional.from(dt[eventId]);
      };
      const mkSetEventFn = (type2) => (transfer) => {
        const dt = transfer;
        dt[eventId] = type2;
      };
      const setEvent = (transfer, type2) => mkSetEventFn(type2)(transfer);
      const setDragstartEvent = mkSetEventFn(
        0
        /* Event.Dragstart */
      );
      const setDropEvent = mkSetEventFn(
        2
        /* Event.Drop */
      );
      const setDragendEvent = mkSetEventFn(
        1
        /* Event.Dragend */
      );
      const checkEvent = (expectedType) => (transfer) => {
        const dt = transfer;
        return Optional.from(dt[eventId]).exists((type2) => type2 === expectedType);
      };
      const isInDragStartEvent = checkEvent(
        0
        /* Event.Dragstart */
      );
      const createEmptyFileList = () => Object.freeze({
        length: 0,
        item: (_) => null
      });
      const modeId = generate("mode");
      const getMode = (transfer) => {
        const dt = transfer;
        return Optional.from(dt[modeId]);
      };
      const mkSetModeFn = (mode) => (transfer) => {
        const dt = transfer;
        dt[modeId] = mode;
      };
      const setMode$1 = (transfer, mode) => mkSetModeFn(mode)(transfer);
      const setReadWriteMode = mkSetModeFn(
        0
        /* Mode.ReadWrite */
      );
      const setReadOnlyMode = mkSetModeFn(
        2
        /* Mode.ReadOnly */
      );
      const setProtectedMode = mkSetModeFn(
        1
        /* Mode.Protected */
      );
      const checkMode = (expectedMode) => (transfer) => {
        const dt = transfer;
        return Optional.from(dt[modeId]).exists((mode) => mode === expectedMode);
      };
      const isInReadWriteMode = checkMode(
        0
        /* Mode.ReadWrite */
      );
      const isInProtectedMode = checkMode(
        1
        /* Mode.Protected */
      );
      const normalizeItems = (dataTransfer, itemsImpl) => ({
        ...itemsImpl,
        get length() {
          return itemsImpl.length;
        },
        add: (data2, type2) => {
          if (isInReadWriteMode(dataTransfer)) {
            if (isString(data2)) {
              if (!isUndefined(type2)) {
                return itemsImpl.add(data2, type2);
              }
            } else {
              return itemsImpl.add(data2);
            }
          }
          return null;
        },
        remove: (idx) => {
          if (isInReadWriteMode(dataTransfer)) {
            itemsImpl.remove(idx);
          }
        },
        clear: () => {
          if (isInReadWriteMode(dataTransfer)) {
            itemsImpl.clear();
          }
        }
      });
      const validDropEffects = ["none", "copy", "link", "move"];
      const validEffectAlloweds = ["none", "copy", "copyLink", "copyMove", "link", "linkMove", "move", "all", "uninitialized"];
      const createDataTransfer = () => {
        const dataTransferImpl = new window.DataTransfer();
        let dropEffect = "move";
        let effectAllowed = "all";
        const dataTransfer = {
          get dropEffect() {
            return dropEffect;
          },
          set dropEffect(effect) {
            if (contains$2(validDropEffects, effect)) {
              dropEffect = effect;
            }
          },
          get effectAllowed() {
            return effectAllowed;
          },
          set effectAllowed(allowed) {
            if (isInDragStartEvent(dataTransfer) && contains$2(validEffectAlloweds, allowed)) {
              effectAllowed = allowed;
            }
          },
          get items() {
            return normalizeItems(dataTransfer, dataTransferImpl.items);
          },
          get files() {
            if (isInProtectedMode(dataTransfer)) {
              return createEmptyFileList();
            } else {
              return dataTransferImpl.files;
            }
          },
          get types() {
            return dataTransferImpl.types;
          },
          setDragImage: (image, x, y) => {
            if (isInReadWriteMode(dataTransfer)) {
              setDragImage(dataTransfer, { image, x, y });
              dataTransferImpl.setDragImage(image, x, y);
            }
          },
          getData: (format) => {
            if (isInProtectedMode(dataTransfer)) {
              return "";
            } else {
              return dataTransferImpl.getData(format);
            }
          },
          setData: (format, data2) => {
            if (isInReadWriteMode(dataTransfer)) {
              dataTransferImpl.setData(format, data2);
            }
          },
          clearData: (format) => {
            if (isInReadWriteMode(dataTransfer)) {
              dataTransferImpl.clearData(format);
            }
          }
        };
        setReadWriteMode(dataTransfer);
        return dataTransfer;
      };
      const cloneDataTransfer = (original) => {
        const clone2 = createDataTransfer();
        const originalMode = getMode(original);
        setReadOnlyMode(original);
        setDragstartEvent(clone2);
        clone2.dropEffect = original.dropEffect;
        clone2.effectAllowed = original.effectAllowed;
        getDragImage(original).each((imageData) => clone2.setDragImage(imageData.image, imageData.x, imageData.y));
        each$e(original.types, (type2) => {
          if (type2 !== "Files") {
            clone2.setData(type2, original.getData(type2));
          }
        });
        each$e(original.files, (file) => clone2.items.add(file));
        getEvent(original).each((type2) => {
          setEvent(clone2, type2);
        });
        originalMode.each((mode) => {
          setMode$1(original, mode);
          setMode$1(clone2, mode);
        });
        return clone2;
      };
      const getHtmlData = (dataTransfer) => {
        const html2 = dataTransfer.getData("text/html");
        return html2 === "" ? Optional.none() : Optional.some(html2);
      };
      const setHtmlData = (dataTransfer, html2) => dataTransfer.setData("text/html", html2);
      const deflate = (rect, delta) => ({
        left: rect.left - delta,
        top: rect.top - delta,
        right: rect.right + delta * 2,
        bottom: rect.bottom + delta * 2,
        width: rect.width + delta,
        height: rect.height + delta
      });
      const getCorners = (getYAxisValue, tds) => bind$3(tds, (td) => {
        const rect = deflate(clone$1(td.getBoundingClientRect()), -1);
        return [
          { x: rect.left, y: getYAxisValue(rect), cell: td },
          { x: rect.right, y: getYAxisValue(rect), cell: td }
        ];
      });
      const findClosestCorner = (corners, x, y) => foldl(corners, (acc, newCorner) => acc.fold(() => Optional.some(newCorner), (oldCorner) => {
        const oldDist = Math.sqrt(Math.abs(oldCorner.x - x) + Math.abs(oldCorner.y - y));
        const newDist = Math.sqrt(Math.abs(newCorner.x - x) + Math.abs(newCorner.y - y));
        return Optional.some(newDist < oldDist ? newCorner : oldCorner);
      }), Optional.none());
      const getClosestCell = (getYAxisValue, isTargetCorner, table2, x, y) => {
        const cells2 = descendants(SugarElement.fromDom(table2), "td,th,caption").map((e) => e.dom);
        const corners = filter$5(getCorners(getYAxisValue, cells2), (corner) => isTargetCorner(corner, y));
        return findClosestCorner(corners, x, y).map((corner) => corner.cell);
      };
      const getBottomValue = (rect) => rect.bottom;
      const getTopValue = (rect) => rect.top;
      const isAbove = (corner, y) => corner.y < y;
      const isBelow = (corner, y) => corner.y > y;
      const getClosestCellAbove = curry(getClosestCell, getBottomValue, isAbove);
      const getClosestCellBelow = curry(getClosestCell, getTopValue, isBelow);
      const findClosestPositionInAboveCell = (table2, pos) => head(pos.getClientRects()).bind((rect) => getClosestCellAbove(table2, rect.left, rect.top)).bind((cell2) => findClosestHorizontalPosition(getLastLinePositions(cell2), pos));
      const findClosestPositionInBelowCell = (table2, pos) => last$2(pos.getClientRects()).bind((rect) => getClosestCellBelow(table2, rect.left, rect.top)).bind((cell2) => findClosestHorizontalPosition(getFirstLinePositions(cell2), pos));
      const hasNextBreak = (getPositionsUntil2, scope, lineInfo) => lineInfo.breakAt.exists((breakPos) => getPositionsUntil2(scope, breakPos).breakAt.isSome());
      const startsWithWrapBreak = (lineInfo) => lineInfo.breakType === BreakType.Wrap && lineInfo.positions.length === 0;
      const startsWithBrBreak = (lineInfo) => lineInfo.breakType === BreakType.Br && lineInfo.positions.length === 1;
      const isAtTableCellLine = (getPositionsUntil2, scope, pos) => {
        const lineInfo = getPositionsUntil2(scope, pos);
        if (startsWithWrapBreak(lineInfo) || !isBr$7(pos.getNode()) && startsWithBrBreak(lineInfo)) {
          return !hasNextBreak(getPositionsUntil2, scope, lineInfo);
        } else {
          return lineInfo.breakAt.isNone();
        }
      };
      const isAtFirstTableCellLine = curry(isAtTableCellLine, getPositionsUntilPreviousLine);
      const isAtLastTableCellLine = curry(isAtTableCellLine, getPositionsUntilNextLine);
      const isCaretAtStartOrEndOfTable = (forward, rng, table2) => {
        const caretPos = CaretPosition.fromRangeStart(rng);
        return positionIn(!forward, table2).exists((pos) => pos.isEqual(caretPos));
      };
      const navigateHorizontally = (editor, forward, table2, _td) => {
        const rng = editor.selection.getRng();
        const direction = forward ? 1 : -1;
        if (isFakeCaretTableBrowser() && isCaretAtStartOrEndOfTable(forward, rng, table2)) {
          showCaret(direction, editor, table2, !forward, false).each((newRng) => {
            moveToRange(editor, newRng);
          });
          return true;
        }
        return false;
      };
      const getClosestAbovePosition = (root, table2, start2) => findClosestPositionInAboveCell(table2, start2).orThunk(() => head(start2.getClientRects()).bind((rect) => findClosestHorizontalPositionFromPoint(getPositionsAbove(root, CaretPosition.before(table2)), rect.left))).getOr(CaretPosition.before(table2));
      const getClosestBelowPosition = (root, table2, start2) => findClosestPositionInBelowCell(table2, start2).orThunk(() => head(start2.getClientRects()).bind((rect) => findClosestHorizontalPositionFromPoint(getPositionsBelow(root, CaretPosition.after(table2)), rect.left))).getOr(CaretPosition.after(table2));
      const getTable = (previous, pos) => {
        const node = pos.getNode(previous);
        return isTable$2(node) ? Optional.some(node) : Optional.none();
      };
      const renderBlock = (down, editor, table2) => {
        editor.undoManager.transact(() => {
          const insertFn = down ? after$4 : before$4;
          const rng = insertEmptyLine(editor, SugarElement.fromDom(table2), insertFn);
          moveToRange(editor, rng);
        });
      };
      const moveCaret = (editor, down, pos) => {
        const table2 = down ? getTable(true, pos) : getTable(false, pos);
        const last2 = down === false;
        table2.fold(() => moveToRange(editor, pos.toRange()), (table3) => positionIn(last2, editor.getBody()).filter((lastPos) => lastPos.isEqual(pos)).fold(() => moveToRange(editor, pos.toRange()), (_) => renderBlock(down, editor, table3)));
      };
      const navigateVertically = (editor, down, table2, td) => {
        const rng = editor.selection.getRng();
        const pos = CaretPosition.fromRangeStart(rng);
        const root = editor.getBody();
        if (!down && isAtFirstTableCellLine(td, pos)) {
          const newPos = getClosestAbovePosition(root, table2, pos);
          moveCaret(editor, down, newPos);
          return true;
        } else if (down && isAtLastTableCellLine(td, pos)) {
          const newPos = getClosestBelowPosition(root, table2, pos);
          moveCaret(editor, down, newPos);
          return true;
        } else {
          return false;
        }
      };
      const move$1 = (editor, forward, mover) => Optional.from(editor.dom.getParent(editor.selection.getNode(), "td,th")).bind((td) => Optional.from(editor.dom.getParent(td, "table")).map((table2) => mover(editor, forward, table2, td))).getOr(false);
      const moveH = (editor, forward) => move$1(editor, forward, navigateHorizontally);
      const moveV = (editor, forward) => move$1(editor, forward, navigateVertically);
      const getCellFirstCursorPosition = (cell2) => {
        const selection = SimSelection.exact(cell2, 0, cell2, 0);
        return toNative(selection);
      };
      const rowHasEditableCell = (cell2) => {
        return isCellEditable(cell2) || siblings(cell2).some((element) => isHTMLElement$1(element) && isCellEditable(element));
      };
      const tabGo = (editor, isRoot2, cell2) => {
        return cell2.fold(Optional.none, Optional.none, (_current, next2) => {
          return first(next2).map((cell3) => {
            return getCellFirstCursorPosition(cell3);
          });
        }, (current) => {
          if (editor.mode.isReadOnly() || !isCellInEditableTable(current) || !rowHasEditableCell(current)) {
            return Optional.none();
          }
          editor.execCommand("mceTableInsertRowAfter");
          return tabForward(editor, isRoot2, current);
        });
      };
      const isCellInEditableTable = (cell2) => closest$4(cell2, isTag("table")).exists(isEditable$2);
      const tabForward = (editor, isRoot2, cell2) => tabGo(editor, isRoot2, next(cell2, isCellEditable));
      const tabBackward = (editor, isRoot2, cell2) => tabGo(editor, isRoot2, prev(cell2, isCellEditable));
      const isCellEditable = (cell2) => isEditable$2(cell2) || descendant(cell2, isEditableHTMLElement);
      const isEditableHTMLElement = (node) => isHTMLElement$1(node) && isEditable$2(node);
      const handleTab = (editor, forward) => {
        const rootElements = ["table", "li", "dl"];
        const body = SugarElement.fromDom(editor.getBody());
        const isRoot2 = (element) => {
          const name$1 = name(element);
          return eq(element, body) || contains$2(rootElements, name$1);
        };
        const rng = editor.selection.getRng();
        const container = SugarElement.fromDom(!forward ? rng.startContainer : rng.endContainer);
        return cell(container, isRoot2).map((cell2) => {
          table(cell2, isRoot2).each((table2) => {
            editor.model.table.clearSelectedCells(table2.dom);
          });
          editor.selection.collapse(!forward);
          const navigation = !forward ? tabBackward : tabForward;
          const rng2 = navigation(editor, isRoot2, cell2);
          rng2.each((range2) => {
            editor.selection.setRng(range2);
          });
          return true;
        }).getOr(false);
      };
      const executeKeydownOverride$4 = (editor, caret, evt) => {
        const isMac = Env.os.isMacOS() || Env.os.isiOS();
        const isFirefox2 = Env.browser.isFirefox();
        execute([
          { keyCode: VK.RIGHT, action: action(moveH$2, editor, true) },
          { keyCode: VK.LEFT, action: action(moveH$2, editor, false) },
          { keyCode: VK.UP, action: action(moveV$5, editor, false) },
          { keyCode: VK.DOWN, action: action(moveV$5, editor, true) },
          ...isMac ? [
            { keyCode: VK.UP, action: action(selectToEndPoint, editor, false), metaKey: true, shiftKey: true },
            { keyCode: VK.DOWN, action: action(selectToEndPoint, editor, true), metaKey: true, shiftKey: true }
          ] : [],
          { keyCode: VK.RIGHT, action: action(moveH, editor, true) },
          { keyCode: VK.LEFT, action: action(moveH, editor, false) },
          { keyCode: VK.UP, action: action(moveV, editor, false) },
          { keyCode: VK.DOWN, action: action(moveV, editor, true) },
          { keyCode: VK.UP, action: action(moveV, editor, false) },
          { keyCode: VK.UP, action: action(moveV$2, editor, false) },
          { keyCode: VK.DOWN, action: action(moveV$2, editor, true) },
          { keyCode: VK.RIGHT, action: action(moveH$1, editor, true) },
          { keyCode: VK.LEFT, action: action(moveH$1, editor, false) },
          { keyCode: VK.UP, action: action(moveV$1, editor, false) },
          { keyCode: VK.DOWN, action: action(moveV$1, editor, true) },
          { keyCode: VK.RIGHT, action: action(move$3, editor, caret, true) },
          { keyCode: VK.LEFT, action: action(move$3, editor, caret, false) },
          { keyCode: VK.RIGHT, ctrlKey: !isMac, altKey: isMac, action: action(moveNextWord, editor, caret) },
          { keyCode: VK.LEFT, ctrlKey: !isMac, altKey: isMac, action: action(movePrevWord, editor, caret) },
          { keyCode: VK.UP, action: action(moveV$3, editor, false) },
          { keyCode: VK.DOWN, action: action(moveV$3, editor, true) },
          ...isFirefox2 ? [
            { keyCode: VK.UP, action: action(moveV$4, editor, false) },
            { keyCode: VK.DOWN, action: action(moveV$4, editor, true) }
          ] : []
        ], evt).each((_) => {
          evt.preventDefault();
        });
      };
      const setup$q = (editor, caret) => {
        editor.on("keydown", (evt) => {
          if (!evt.isDefaultPrevented()) {
            executeKeydownOverride$4(editor, caret, evt);
          }
        });
      };
      const point = (container, offset) => ({
        container,
        offset
      });
      const DOM$7 = DOMUtils.DOM;
      const alwaysNext = (startNode) => (node) => startNode === node ? -1 : 0;
      const isBoundary = (dom2) => (node) => dom2.isBlock(node) || contains$2(["BR", "IMG", "HR", "INPUT"], node.nodeName) || dom2.getContentEditable(node) === "false";
      const textBefore = (node, offset, rootNode) => {
        if (isText$b(node) && offset >= 0) {
          return Optional.some(point(node, offset));
        } else {
          const textSeeker = TextSeeker(DOM$7);
          return Optional.from(textSeeker.backwards(node, offset, alwaysNext(node), rootNode)).map((prev2) => point(prev2.container, prev2.container.data.length));
        }
      };
      const textAfter = (node, offset, rootNode) => {
        if (isText$b(node) && offset >= node.length) {
          return Optional.some(point(node, offset));
        } else {
          const textSeeker = TextSeeker(DOM$7);
          return Optional.from(textSeeker.forwards(node, offset, alwaysNext(node), rootNode)).map((prev2) => point(prev2.container, 0));
        }
      };
      const scanLeft = (node, offset, rootNode) => {
        if (!isText$b(node)) {
          return Optional.none();
        }
        const text2 = node.data;
        if (offset >= 0 && offset <= text2.length) {
          return Optional.some(point(node, offset));
        } else {
          const textSeeker = TextSeeker(DOM$7);
          return Optional.from(textSeeker.backwards(node, offset, alwaysNext(node), rootNode)).bind((prev2) => {
            const prevText = prev2.container.data;
            return scanLeft(prev2.container, offset + prevText.length, rootNode);
          });
        }
      };
      const scanRight = (node, offset, rootNode) => {
        if (!isText$b(node)) {
          return Optional.none();
        }
        const text2 = node.data;
        if (offset <= text2.length) {
          return Optional.some(point(node, offset));
        } else {
          const textSeeker = TextSeeker(DOM$7);
          return Optional.from(textSeeker.forwards(node, offset, alwaysNext(node), rootNode)).bind((next2) => scanRight(next2.container, offset - text2.length, rootNode));
        }
      };
      const repeatLeft = (dom2, node, offset, process2, rootNode) => {
        const search2 = TextSeeker(dom2, isBoundary(dom2));
        return Optional.from(search2.backwards(node, offset, process2, rootNode));
      };
      const isValidTextRange = (rng) => rng.collapsed && isText$b(rng.startContainer);
      const getText = (rng) => trim$2(rng.toString().replace(/\u00A0/g, " "));
      const isWhitespace = (chr) => chr !== "" && "  \uFEFF\f\n\r	\v".indexOf(chr) !== -1;
      const stripTrigger = (text2, trigger) => text2.substring(trigger.length);
      const findTrigger = (text2, index, trigger, includeWhitespace = false) => {
        let i;
        const firstChar = trigger.charAt(0);
        for (i = index - 1; i >= 0; i--) {
          const char = text2.charAt(i);
          if (!includeWhitespace && isWhitespace(char)) {
            return Optional.none();
          }
          if (firstChar === char && contains$1(text2, trigger, i, index)) {
            break;
          }
        }
        return Optional.some(i);
      };
      const getContext = (dom2, initRange, trigger, includeWhitespace = false) => {
        if (!isValidTextRange(initRange)) {
          return Optional.none();
        }
        const buffer = { text: "", offset: 0 };
        const findTriggerIndex = (element, offset, text2) => {
          buffer.text = text2 + buffer.text;
          buffer.offset += offset;
          return findTrigger(buffer.text, buffer.offset, trigger, includeWhitespace).getOr(offset);
        };
        const root = dom2.getParent(initRange.startContainer, dom2.isBlock) || dom2.getRoot();
        return repeatLeft(dom2, initRange.startContainer, initRange.startOffset, findTriggerIndex, root).bind((spot) => {
          const range2 = initRange.cloneRange();
          range2.setStart(spot.container, spot.offset);
          range2.setEnd(initRange.endContainer, initRange.endOffset);
          if (range2.collapsed) {
            return Optional.none();
          }
          const text2 = getText(range2);
          const triggerIndex = text2.lastIndexOf(trigger);
          if (triggerIndex !== 0) {
            return Optional.none();
          } else {
            return Optional.some({ text: stripTrigger(text2, trigger), range: range2, trigger });
          }
        });
      };
      const isText$1 = (node) => node.nodeType === TEXT;
      const isElement = (node) => node.nodeType === ELEMENT;
      const toLast = (node) => {
        if (isText$1(node)) {
          return point(node, node.data.length);
        } else {
          const children2 = node.childNodes;
          return children2.length > 0 ? toLast(children2[children2.length - 1]) : point(node, children2.length);
        }
      };
      const toLeaf = (node, offset) => {
        const children2 = node.childNodes;
        if (children2.length > 0 && offset < children2.length) {
          return toLeaf(children2[offset], 0);
        } else if (children2.length > 0 && isElement(node) && children2.length === offset) {
          return toLast(children2[children2.length - 1]);
        } else {
          return point(node, offset);
        }
      };
      const isPreviousCharContent = (dom2, leaf) => {
        const root = dom2.getParent(leaf.container, dom2.isBlock) ?? dom2.getRoot();
        return repeatLeft(dom2, leaf.container, leaf.offset, (_element, offset) => offset === 0 ? -1 : offset, root).filter((spot) => {
          const char = spot.container.data.charAt(spot.offset - 1);
          return !isWhitespace(char);
        }).isSome();
      };
      const isStartOfWord = (dom2) => (rng) => {
        const leaf = toLeaf(rng.startContainer, rng.startOffset);
        return !isPreviousCharContent(dom2, leaf);
      };
      const getTriggerContext = (dom2, initRange, database) => findMap(database.triggers, (trigger) => getContext(dom2, initRange, trigger));
      const lookup = (editor, getDatabase) => {
        const database = getDatabase();
        const rng = editor.selection.getRng();
        return getTriggerContext(editor.dom, rng, database).bind((context2) => lookupWithContext(editor, getDatabase, context2));
      };
      const lookupWithContext = (editor, getDatabase, context2, fetchOptions = {}) => {
        const database = getDatabase();
        const rng = editor.selection.getRng();
        const startText = rng.startContainer.nodeValue ?? "";
        const autocompleters = filter$5(database.lookupByTrigger(context2.trigger), (autocompleter) => context2.text.length >= autocompleter.minChars && autocompleter.matches.getOrThunk(() => isStartOfWord(editor.dom))(context2.range, startText, context2.text));
        if (autocompleters.length === 0) {
          return Optional.none();
        }
        const lookupData = Promise.all(map$3(autocompleters, (ac) => {
          const fetchResult = ac.fetch(context2.text, ac.maxResults, fetchOptions);
          return fetchResult.then((results) => ({
            matchText: context2.text,
            items: results,
            columns: ac.columns,
            onAction: ac.onAction,
            highlightOn: ac.highlightOn
          }));
        }));
        return Optional.some({
          lookupData,
          context: context2
        });
      };
      var SimpleResultType;
      (function(SimpleResultType2) {
        SimpleResultType2[SimpleResultType2["Error"] = 0] = "Error";
        SimpleResultType2[SimpleResultType2["Value"] = 1] = "Value";
      })(SimpleResultType || (SimpleResultType = {}));
      const fold$1 = (res, onError, onValue) => res.stype === SimpleResultType.Error ? onError(res.serror) : onValue(res.svalue);
      const partition = (results) => {
        const values2 = [];
        const errors = [];
        each$e(results, (obj) => {
          fold$1(obj, (err) => errors.push(err), (val) => values2.push(val));
        });
        return { values: values2, errors };
      };
      const mapError = (res, f) => {
        if (res.stype === SimpleResultType.Error) {
          return { stype: SimpleResultType.Error, serror: f(res.serror) };
        } else {
          return res;
        }
      };
      const map = (res, f) => {
        if (res.stype === SimpleResultType.Value) {
          return { stype: SimpleResultType.Value, svalue: f(res.svalue) };
        } else {
          return res;
        }
      };
      const bind = (res, f) => {
        if (res.stype === SimpleResultType.Value) {
          return f(res.svalue);
        } else {
          return res;
        }
      };
      const bindError = (res, f) => {
        if (res.stype === SimpleResultType.Error) {
          return f(res.serror);
        } else {
          return res;
        }
      };
      const svalue = (v) => ({ stype: SimpleResultType.Value, svalue: v });
      const serror = (e) => ({ stype: SimpleResultType.Error, serror: e });
      const toResult = (res) => fold$1(res, Result.error, Result.value);
      const fromResult = (res) => res.fold(serror, svalue);
      const SimpleResult = {
        fromResult,
        toResult,
        svalue,
        partition,
        serror,
        bind,
        bindError,
        map,
        mapError,
        fold: fold$1
      };
      const formatObj = (input) => {
        return isObject(input) && keys(input).length > 100 ? " removed due to size" : JSON.stringify(input, null, 2);
      };
      const formatErrors = (errors) => {
        const es = errors.length > 10 ? errors.slice(0, 10).concat([
          {
            path: [],
            getErrorInfo: constant("... (only showing first ten failures)")
          }
        ]) : errors;
        return map$3(es, (e) => {
          return "Failed path: (" + e.path.join(" > ") + ")\n" + e.getErrorInfo();
        });
      };
      const nu = (path2, getErrorInfo) => {
        return SimpleResult.serror([{
          path: path2,
          // This is lazy so that it isn't calculated unnecessarily
          getErrorInfo
        }]);
      };
      const missingRequired = (path2, key, obj) => nu(path2, () => 'Could not find valid *required* value for "' + key + '" in ' + formatObj(obj));
      const custom = (path2, err) => nu(path2, constant(err));
      const value = (validator) => {
        const extract = (path2, val) => {
          return SimpleResult.bindError(validator(val), (err) => custom(path2, err));
        };
        const toString2 = constant("val");
        return {
          extract,
          toString: toString2
        };
      };
      const anyValue$1 = value(SimpleResult.svalue);
      const anyValue = constant(anyValue$1);
      const typedValue = (validator, expectedType) => value((a) => {
        const actualType = typeof a;
        return validator(a) ? SimpleResult.svalue(a) : SimpleResult.serror(`Expected type: ${expectedType} but got: ${actualType}`);
      });
      const number = typedValue(isNumber, "number");
      const string = typedValue(isString, "string");
      typedValue(isBoolean, "boolean");
      const functionProcessor = typedValue(isFunction, "function");
      const isPostMessageable = (val) => {
        if (Object(val) !== val) {
          return true;
        }
        switch ({}.toString.call(val).slice(8, -1)) {
          // Class
          case "Boolean":
          case "Number":
          case "String":
          case "Date":
          case "RegExp":
          case "Blob":
          case "FileList":
          case "ImageData":
          case "ImageBitmap":
          case "ArrayBuffer":
            return true;
          case "Array":
          case "Object":
            return Object.keys(val).every((prop) => isPostMessageable(val[prop]));
          default:
            return false;
        }
      };
      value((a) => {
        if (isPostMessageable(a)) {
          return SimpleResult.svalue(a);
        } else {
          return SimpleResult.serror("Expected value to be acceptable for sending via postMessage");
        }
      });
      const required$1 = () => ({ tag: "required", process: {} });
      const defaultedThunk = (fallbackThunk) => ({ tag: "defaultedThunk", process: fallbackThunk });
      const defaulted$1 = (fallback2) => defaultedThunk(constant(fallback2));
      const asOption = () => ({ tag: "option", process: {} });
      const field$1 = (key, newKey, presence, prop) => ({ tag: "field", key, newKey, presence, prop });
      const fold = (value2, ifField, ifCustom) => {
        switch (value2.tag) {
          case "field":
            return ifField(value2.key, value2.newKey, value2.presence, value2.prop);
          case "custom":
            return ifCustom(value2.newKey, value2.instantiator);
        }
      };
      const mergeValues = (values2, base) => {
        return SimpleResult.svalue(deepMerge(base, merge$1.apply(void 0, values2)));
      };
      const mergeErrors = (errors) => compose(SimpleResult.serror, flatten$1)(errors);
      const consolidateObj = (objects, base) => {
        const partition2 = SimpleResult.partition(objects);
        return partition2.errors.length > 0 ? mergeErrors(partition2.errors) : mergeValues(partition2.values, base);
      };
      const consolidateArr = (objects) => {
        const partitions = SimpleResult.partition(objects);
        return partitions.errors.length > 0 ? mergeErrors(partitions.errors) : SimpleResult.svalue(partitions.values);
      };
      const ResultCombine = {
        consolidateObj,
        consolidateArr
      };
      const requiredAccess = (path2, obj, key, bundle) => (
        // In required mode, if it is undefined, it is an error.
        get$a(obj, key).fold(() => missingRequired(path2, key, obj), bundle)
      );
      const fallbackAccess = (obj, key, fallback2, bundle) => {
        const v = get$a(obj, key).getOrThunk(() => fallback2(obj));
        return bundle(v);
      };
      const optionAccess = (obj, key, bundle) => bundle(get$a(obj, key));
      const optionDefaultedAccess = (obj, key, fallback2, bundle) => {
        const opt = get$a(obj, key).map((val) => val === true ? fallback2(obj) : val);
        return bundle(opt);
      };
      const extractField = (field2, path2, obj, key, prop) => {
        const bundle = (av) => prop.extract(path2.concat([key]), av);
        const bundleAsOption = (optValue) => optValue.fold(() => SimpleResult.svalue(Optional.none()), (ov) => {
          const result = prop.extract(path2.concat([key]), ov);
          return SimpleResult.map(result, Optional.some);
        });
        switch (field2.tag) {
          case "required":
            return requiredAccess(path2, obj, key, bundle);
          case "defaultedThunk":
            return fallbackAccess(obj, key, field2.process, bundle);
          case "option":
            return optionAccess(obj, key, bundleAsOption);
          case "defaultedOptionThunk":
            return optionDefaultedAccess(obj, key, field2.process, bundleAsOption);
          case "mergeWithThunk": {
            return fallbackAccess(obj, key, constant({}), (v) => {
              const result = deepMerge(field2.process(obj), v);
              return bundle(result);
            });
          }
        }
      };
      const extractFields = (path2, obj, fields) => {
        const success = {};
        const errors = [];
        for (const field2 of fields) {
          fold(field2, (key, newKey, presence, prop) => {
            const result = extractField(presence, path2, obj, key, prop);
            SimpleResult.fold(result, (err) => {
              errors.push(...err);
            }, (res) => {
              success[newKey] = res;
            });
          }, (newKey, instantiator) => {
            success[newKey] = instantiator(obj);
          });
        }
        return errors.length > 0 ? SimpleResult.serror(errors) : SimpleResult.svalue(success);
      };
      const objOf = (values2) => {
        const extract = (path2, o) => extractFields(path2, o, values2);
        const toString2 = () => {
          const fieldStrings = map$3(values2, (value2) => fold(value2, (key, _okey, _presence, prop) => key + " -> " + prop.toString(), (newKey, _instantiator) => "state(" + newKey + ")"));
          return "obj{\n" + fieldStrings.join("\n") + "}";
        };
        return {
          extract,
          toString: toString2
        };
      };
      const arrOf = (prop) => {
        const extract = (path2, array) => {
          const results = map$3(array, (a, i) => prop.extract(path2.concat(["[" + i + "]"]), a));
          return ResultCombine.consolidateArr(results);
        };
        const toString2 = () => "array(" + prop.toString() + ")";
        return {
          extract,
          toString: toString2
        };
      };
      const extractValue = (label, prop, obj) => {
        const res = prop.extract([label], obj);
        return SimpleResult.mapError(res, (errs) => ({ input: obj, errors: errs }));
      };
      const asRaw = (label, prop, obj) => SimpleResult.toResult(extractValue(label, prop, obj));
      const formatError = (errInfo) => {
        return "Errors: \n" + formatErrors(errInfo.errors).join("\n") + "\n\nInput object: " + formatObj(errInfo.input);
      };
      const field = field$1;
      const required = (key) => field(key, key, required$1(), anyValue());
      const requiredOf = (key, schema) => field(key, key, required$1(), schema);
      const requiredString = (key) => requiredOf(key, string);
      const requiredFunction = (key) => requiredOf(key, functionProcessor);
      const option = (key) => field(key, key, asOption(), anyValue());
      const optionOf = (key, schema) => field(key, key, asOption(), schema);
      const optionString = (key) => optionOf(key, string);
      const optionFunction = (key) => optionOf(key, functionProcessor);
      const defaulted = (key, fallback2) => field(key, key, defaulted$1(fallback2), anyValue());
      const defaultedOf = (key, fallback2, schema) => field(key, key, defaulted$1(fallback2), schema);
      const defaultedNumber = (key, fallback2) => defaultedOf(key, fallback2, number);
      const defaultedArrayOf = (key, fallback2, schema) => defaultedOf(key, fallback2, arrOf(schema));
      const type = requiredString("type");
      const fetch$1 = requiredFunction("fetch");
      const onAction = requiredFunction("onAction");
      optionString("name");
      optionString("text");
      optionString("role");
      optionString("icon");
      optionString("url");
      optionString("tooltip");
      optionString("chevronTooltip");
      optionString("label");
      optionString("shortcut");
      const defaultedColumns = (num) => defaulted("columns", num);
      const autocompleterSchema = objOf([
        type,
        requiredString("trigger"),
        defaultedNumber("minChars", 1),
        defaultedColumns(1),
        defaultedNumber("maxResults", 10),
        optionFunction("matches"),
        fetch$1,
        onAction,
        defaultedArrayOf("highlightOn", [], string)
      ]);
      const createAutocompleter = (spec) => asRaw("Autocompleter", autocompleterSchema, spec);
      const create$6 = () => {
        const buttons = {};
        const menuItems = {};
        const popups = {};
        const icons = {};
        const contextMenus = {};
        const contextToolbars = {};
        const contexts = {};
        const sidebars = {};
        const views = {};
        const add2 = (collection, type2) => (name2, spec) => {
          collection[name2.toLowerCase()] = { ...spec, type: type2 };
        };
        const addDefaulted = (collection, type2) => (name2, spec) => {
          collection[name2.toLowerCase()] = { type: type2, ...spec };
        };
        const addIcon = (name2, svgData) => icons[name2.toLowerCase()] = svgData;
        const addContext = (name2, pred) => contexts[name2.toLowerCase()] = pred;
        return {
          addButton: add2(buttons, "button"),
          addGroupToolbarButton: add2(buttons, "grouptoolbarbutton"),
          addToggleButton: add2(buttons, "togglebutton"),
          addMenuButton: add2(buttons, "menubutton"),
          addSplitButton: add2(buttons, "splitbutton"),
          addMenuItem: add2(menuItems, "menuitem"),
          addNestedMenuItem: add2(menuItems, "nestedmenuitem"),
          addToggleMenuItem: add2(menuItems, "togglemenuitem"),
          addAutocompleter: add2(popups, "autocompleter"),
          addContextMenu: add2(contextMenus, "contextmenu"),
          addContextToolbar: add2(contextToolbars, "contexttoolbar"),
          addContextForm: addDefaulted(contextToolbars, "contextform"),
          addSidebar: add2(sidebars, "sidebar"),
          addView: add2(views, "views"),
          addIcon,
          addContext,
          getAll: () => ({
            buttons,
            menuItems,
            icons,
            // TODO: should popups be combined with context menus? We'd need to make a new add function.
            // Right now using `add` shares the key namespace, which prevents registering both
            // a completer and a context menu with the same name
            popups,
            contextMenus,
            contextToolbars,
            sidebars,
            views,
            contexts
          })
        };
      };
      const register$2 = (editor) => {
        const popups = editor.ui.registry.getAll().popups;
        const dataset = map$2(popups, (popup) => createAutocompleter(popup).fold((err) => {
          throw new Error(formatError(err));
        }, identity));
        const triggers = stringArray(mapToArray(dataset, (v) => v.trigger));
        const datasetValues = values(dataset);
        const lookupByTrigger = (trigger) => filter$5(datasetValues, (dv) => dv.trigger === trigger);
        return {
          dataset,
          triggers,
          lookupByTrigger
        };
      };
      const setupEditorInput = (editor, api2) => {
        const update = last$1(api2.load, 50);
        editor.on("input", (e) => {
          if (e.inputType === "insertCompositionText" && !editor.composing) {
            return;
          }
          update.throttle();
        });
        editor.on("keydown", (e) => {
          const keyCode = e.which;
          if (keyCode === 8) {
            update.throttle();
          } else if (keyCode === 27) {
            update.cancel();
            api2.cancelIfNecessary();
          } else if (keyCode === 38 || keyCode === 40) {
            update.cancel();
          }
        }, true);
        editor.on("remove", update.cancel);
      };
      const setup$p = (editor) => {
        const activeAutocompleter = value$1();
        const uiActive = Cell(false);
        const isActive = activeAutocompleter.isSet;
        const cancelIfNecessary = () => {
          if (isActive()) {
            fireAutocompleterEnd(editor);
            uiActive.set(false);
            activeAutocompleter.clear();
          }
        };
        const commenceIfNecessary = (context2) => {
          if (!isActive()) {
            activeAutocompleter.set({
              trigger: context2.trigger,
              matchLength: context2.text.length
            });
          }
        };
        const getAutocompleters = cached(() => register$2(editor));
        const doLookup = (fetchOptions) => activeAutocompleter.get().map((ac) => getContext(editor.dom, editor.selection.getRng(), ac.trigger, true).bind((newContext) => lookupWithContext(editor, getAutocompleters, newContext, fetchOptions))).getOrThunk(() => lookup(editor, getAutocompleters));
        const load = (fetchOptions) => {
          doLookup(fetchOptions).fold(cancelIfNecessary, (lookupInfo) => {
            commenceIfNecessary(lookupInfo.context);
            lookupInfo.lookupData.then((lookupData) => {
              activeAutocompleter.get().map((ac) => {
                const context2 = lookupInfo.context;
                if (ac.trigger !== context2.trigger) {
                  return;
                }
                activeAutocompleter.set({
                  ...ac,
                  matchLength: context2.text.length
                });
                if (uiActive.get()) {
                  fireAutocompleterUpdateActiveRange(editor, { range: context2.range });
                  fireAutocompleterUpdate(editor, { lookupData });
                } else {
                  uiActive.set(true);
                  fireAutocompleterUpdateActiveRange(editor, { range: context2.range });
                  fireAutocompleterStart(editor, { lookupData });
                }
              });
            });
          });
        };
        const isRangeInsideOrEqual = (innerRange, outerRange) => {
          const startComparison = innerRange.compareBoundaryPoints(window.Range.START_TO_START, outerRange);
          const endComparison = innerRange.compareBoundaryPoints(window.Range.END_TO_END, outerRange);
          return startComparison >= 0 && endComparison <= 0;
        };
        const readActiveRange = () => {
          return activeAutocompleter.get().bind(({ trigger }) => {
            const selRange = editor.selection.getRng();
            return getContext(editor.dom, selRange, trigger, uiActive.get()).filter(({ range: range2 }) => isRangeInsideOrEqual(selRange, range2)).map(({ range: range2 }) => range2);
          });
        };
        editor.addCommand("mceAutocompleterReload", (_ui, value2) => {
          const fetchOptions = isObject(value2) ? value2.fetchOptions : {};
          load(fetchOptions);
        });
        editor.addCommand("mceAutocompleterClose", cancelIfNecessary);
        editor.addCommand("mceAutocompleterRefreshActiveRange", () => {
          readActiveRange().each((range2) => {
            fireAutocompleterUpdateActiveRange(editor, { range: range2 });
          });
        });
        editor.editorCommands.addQueryStateHandler("mceAutoCompleterInRange", () => readActiveRange().isSome());
        setupEditorInput(editor, {
          cancelIfNecessary,
          load
        });
      };
      const browser$1 = detect$1().browser;
      const isSafari = browser$1.isSafari();
      const emptyNodeContents = (node) => fillWithPaddingBr(SugarElement.fromDom(node));
      const isEntireNodeSelected = (rng, node) => rng.startOffset === 0 && rng.endOffset === node.textContent?.length;
      const getParentDetailsElementAtPos = (dom2, pos) => Optional.from(dom2.getParent(pos.container(), "details"));
      const isInDetailsElement = (dom2, pos) => getParentDetailsElementAtPos(dom2, pos).isSome();
      const getDetailsElements = (dom2, rng) => {
        const startDetails = Optional.from(dom2.getParent(rng.startContainer, "details"));
        const endDetails = Optional.from(dom2.getParent(rng.endContainer, "details"));
        if (startDetails.isSome() || endDetails.isSome()) {
          const startSummary = startDetails.bind((details) => Optional.from(dom2.select("summary", details)[0]));
          return Optional.some({ startSummary, startDetails, endDetails });
        } else {
          return Optional.none();
        }
      };
      const isCaretInTheBeginningOf = (caretPos, element) => firstPositionIn(element).exists((pos) => pos.isEqual(caretPos));
      const isCaretInTheEndOf = (caretPos, element) => {
        return lastPositionIn(element).exists((pos) => {
          if (isBr$7(pos.getNode())) {
            return prevPosition(element, pos).exists((pos2) => pos2.isEqual(caretPos)) || pos.isEqual(caretPos);
          } else {
            return pos.isEqual(caretPos);
          }
        });
      };
      const isCaretAtStartOfSummary = (caretPos, detailsElements) => detailsElements.startSummary.exists((summary) => isCaretInTheBeginningOf(caretPos, summary));
      const isCaretAtEndOfSummary = (caretPos, detailsElements) => detailsElements.startSummary.exists((summary) => isCaretInTheEndOf(caretPos, summary));
      const isCaretInFirstPositionInBody = (caretPos, detailsElements) => detailsElements.startDetails.exists((details) => prevPosition(details, caretPos).forall((pos) => detailsElements.startSummary.exists((summary) => !summary.contains(caretPos.container()) && summary.contains(pos.container()))));
      const isCaretInLastPositionInBody = (root, caretPos, detailsElements) => detailsElements.startDetails.exists((details) => nextPosition(root, caretPos).forall((pos) => !details.contains(pos.container())));
      const setCaretToPosition = (editor, position) => {
        const node = position.getNode();
        if (!isUndefined(node)) {
          editor.selection.setCursorLocation(node, position.offset());
        }
      };
      const moveCaretToDetailsPos = (editor, pos, forward) => {
        const details = editor.dom.getParent(pos.container(), "details");
        if (details && !details.open) {
          const summary = editor.dom.select("summary", details)[0];
          if (summary) {
            const newPos = forward ? firstPositionIn(summary) : lastPositionIn(summary);
            newPos.each((pos2) => setCaretToPosition(editor, pos2));
          }
        } else {
          setCaretToPosition(editor, pos);
        }
      };
      const isPartialDelete = (rng, detailsElements) => {
        const containsStart = (element) => element.contains(rng.startContainer);
        const containsEnd = (element) => element.contains(rng.endContainer);
        const startInSummary = detailsElements.startSummary.exists(containsStart);
        const endInSummary = detailsElements.startSummary.exists(containsEnd);
        const isPartiallySelectedDetailsElements = detailsElements.startDetails.forall((startDetails) => detailsElements.endDetails.forall((endDetails) => startDetails !== endDetails));
        const isInPartiallySelectedSummary = (startInSummary || endInSummary) && !(startInSummary && endInSummary);
        return isInPartiallySelectedSummary || isPartiallySelectedDetailsElements;
      };
      const shouldPreventDeleteIntoDetails = (editor, forward, granularity) => {
        const { dom: dom2, selection } = editor;
        const root = editor.getBody();
        if (granularity === "character") {
          const caretPos = CaretPosition.fromRangeStart(selection.getRng());
          const parentBlock = dom2.getParent(caretPos.container(), dom2.isBlock);
          const parentDetailsAtCaret = getParentDetailsElementAtPos(dom2, caretPos);
          const inEmptyParentBlock = parentBlock && dom2.isEmpty(parentBlock);
          const isFirstBlock = isNull(parentBlock?.previousSibling);
          const isLastBlock = isNull(parentBlock?.nextSibling);
          if (inEmptyParentBlock) {
            const firstOrLast = forward ? isLastBlock : isFirstBlock;
            if (firstOrLast) {
              const isBeforeAfterDetails = navigate(!forward, root, caretPos).exists((pos) => {
                return isInDetailsElement(dom2, pos) && !equals(parentDetailsAtCaret, getParentDetailsElementAtPos(dom2, pos));
              });
              if (isBeforeAfterDetails) {
                return true;
              }
            }
          }
          return navigate(forward, root, caretPos).fold(never, (pos) => {
            const parentDetailsAtNewPos = getParentDetailsElementAtPos(dom2, pos);
            if (isInDetailsElement(dom2, pos) && !equals(parentDetailsAtCaret, parentDetailsAtNewPos)) {
              if (!forward) {
                moveCaretToDetailsPos(editor, pos, false);
              }
              if (parentBlock && inEmptyParentBlock) {
                if (forward && isFirstBlock) {
                  return true;
                } else if (!forward && isLastBlock) {
                  return true;
                }
                moveCaretToDetailsPos(editor, pos, forward);
                editor.dom.remove(parentBlock);
              }
              return true;
            } else {
              return false;
            }
          });
        } else {
          return false;
        }
      };
      const shouldPreventDeleteSummaryAction = (editor, detailElements, forward, granularity) => {
        const selection = editor.selection;
        const rng = selection.getRng();
        const caretPos = CaretPosition.fromRangeStart(rng);
        const root = editor.getBody();
        if (granularity === "selection") {
          return isPartialDelete(rng, detailElements);
        } else if (forward) {
          return isCaretAtEndOfSummary(caretPos, detailElements) || isCaretInLastPositionInBody(root, caretPos, detailElements);
        } else {
          return isCaretAtStartOfSummary(caretPos, detailElements) || isCaretInFirstPositionInBody(caretPos, detailElements);
        }
      };
      const shouldPreventDeleteAction = (editor, forward, granularity) => getDetailsElements(editor.dom, editor.selection.getRng()).fold(() => shouldPreventDeleteIntoDetails(editor, forward, granularity), (detailsElements) => shouldPreventDeleteSummaryAction(editor, detailsElements, forward, granularity) || shouldPreventDeleteIntoDetails(editor, forward, granularity));
      const handleDeleteActionSafari = (editor, forward, granularity) => {
        const selection = editor.selection;
        const node = selection.getNode();
        const rng = selection.getRng();
        const caretPos = CaretPosition.fromRangeStart(rng);
        if (isSummary$1(node)) {
          if (granularity === "selection" && isEntireNodeSelected(rng, node) || willDeleteLastPositionInElement(forward, caretPos, node)) {
            emptyNodeContents(node);
          } else {
            editor.undoManager.transact(() => {
              const sel = selection.getSel();
              let { anchorNode, anchorOffset, focusNode, focusOffset } = sel ?? {};
              const applySelection = () => {
                if (isNonNullable(anchorNode) && isNonNullable(anchorOffset) && isNonNullable(focusNode) && isNonNullable(focusOffset)) {
                  sel?.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
                }
              };
              const updateSelection = () => {
                anchorNode = sel?.anchorNode;
                anchorOffset = sel?.anchorOffset;
                focusNode = sel?.focusNode;
                focusOffset = sel?.focusOffset;
              };
              const appendAllChildNodes = (from2, to2) => {
                each$e(from2.childNodes, (child2) => {
                  if (isNode(child2)) {
                    to2.appendChild(child2);
                  }
                });
              };
              const container = editor.dom.create("span", { "data-mce-bogus": "1" });
              appendAllChildNodes(node, container);
              node.appendChild(container);
              applySelection();
              if (granularity === "word" || granularity === "line") {
                sel?.modify("extend", forward ? "right" : "left", granularity);
              }
              if (!selection.isCollapsed() && isEntireNodeSelected(selection.getRng(), container)) {
                emptyNodeContents(node);
              } else {
                editor.execCommand(forward ? "ForwardDelete" : "Delete");
                updateSelection();
                appendAllChildNodes(container, node);
                applySelection();
              }
              editor.dom.remove(container);
            });
          }
          return true;
        } else {
          return false;
        }
      };
      const backspaceDelete = (editor, forward, granularity) => shouldPreventDeleteAction(editor, forward, granularity) || isSafari && handleDeleteActionSafari(editor, forward, granularity) ? Optional.some(noop) : Optional.none();
      const createAndFireInputEvent = (eventType) => (editor, inputType, specifics = {}) => {
        const target = editor.getBody();
        const overrides = {
          bubbles: true,
          composed: true,
          data: null,
          isComposing: false,
          detail: 0,
          view: null,
          target,
          currentTarget: target,
          eventPhase: Event.AT_TARGET,
          originalTarget: target,
          explicitOriginalTarget: target,
          isTrusted: false,
          srcElement: target,
          cancelable: false,
          preventDefault: noop,
          inputType
        };
        const input = clone$2(new InputEvent(eventType));
        return editor.dispatch(eventType, { ...input, ...overrides, ...specifics });
      };
      const fireInputEvent = createAndFireInputEvent("input");
      const fireBeforeInputEvent = createAndFireInputEvent("beforeinput");
      const platform$2 = detect$1();
      const os = platform$2.os;
      const isMacOSOriOS = os.isMacOS() || os.isiOS();
      const browser = platform$2.browser;
      const isFirefox = browser.isFirefox();
      const executeKeydownOverride$3 = (editor, caret, evt) => {
        const inputType = evt.keyCode === VK.BACKSPACE ? "deleteContentBackward" : "deleteContentForward";
        const isCollapsed = editor.selection.isCollapsed();
        const unmodifiedGranularity = isCollapsed ? "character" : "selection";
        const getModifiedGranularity = (isWord) => {
          if (isCollapsed) {
            return isWord ? "word" : "line";
          } else {
            return "selection";
          }
        };
        executeWithDelayedAction([
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$2, editor) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$8, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$8, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$9, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$9, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$5, editor, caret, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$5, editor, caret, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$d, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$d, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete, editor, false, unmodifiedGranularity) },
          { keyCode: VK.DELETE, action: action(backspaceDelete, editor, true, unmodifiedGranularity) },
          ...isMacOSOriOS ? [
            { keyCode: VK.BACKSPACE, altKey: true, action: action(backspaceDelete, editor, false, getModifiedGranularity(true)) },
            { keyCode: VK.DELETE, altKey: true, action: action(backspaceDelete, editor, true, getModifiedGranularity(true)) },
            { keyCode: VK.BACKSPACE, metaKey: true, action: action(backspaceDelete, editor, false, getModifiedGranularity(false)) }
          ] : [
            { keyCode: VK.BACKSPACE, ctrlKey: true, action: action(backspaceDelete, editor, false, getModifiedGranularity(true)) },
            { keyCode: VK.DELETE, ctrlKey: true, action: action(backspaceDelete, editor, true, getModifiedGranularity(true)) }
          ],
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$6, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$6, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$3, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$3, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$a, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$a, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$b, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$b, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$4, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$4, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$7, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$7, editor, true) },
          { keyCode: VK.BACKSPACE, action: action(backspaceDelete$1, editor, false) },
          { keyCode: VK.DELETE, action: action(backspaceDelete$1, editor, true) }
        ], evt).filter((_) => editor.selection.isEditable()).each((applyAction) => {
          evt.preventDefault();
          const beforeInput = fireBeforeInputEvent(editor, inputType);
          if (!beforeInput.isDefaultPrevented()) {
            applyAction();
            fireInputEvent(editor, inputType);
          }
        });
      };
      const executeKeyupOverride = (editor, evt, isBackspaceKeydown, formatNodes) => execute([
        { keyCode: VK.BACKSPACE, action: action(paddEmptyElement, editor) },
        { keyCode: VK.DELETE, action: action(paddEmptyElement, editor) },
        ...isMacOSOriOS ? [
          { keyCode: VK.BACKSPACE, altKey: true, action: action(refreshCaret, editor) },
          { keyCode: VK.DELETE, altKey: true, action: action(refreshCaret, editor) },
          // macOS surpresses keyup events for most keys including Backspace when Meta key is engaged
          // To emulate Meta + Backspace on macOS, add a pattern for the meta key when backspace was
          // detected on keydown
          ...isBackspaceKeydown ? [{
            // Firefox detects macOS Command key code as "Command" not "Meta"
            keyCode: isFirefox ? 224 : 91,
            action: action(() => {
              updateCaretFormat(editor, formatNodes);
              return refreshCaret(editor);
            })
          }] : []
        ] : [
          { keyCode: VK.BACKSPACE, ctrlKey: true, action: action(refreshCaret, editor) },
          { keyCode: VK.DELETE, ctrlKey: true, action: action(refreshCaret, editor) }
        ]
      ], evt);
      const setup$o = (editor, caret) => {
        let isBackspaceKeydown = false;
        let formatNodes = [];
        editor.on("keydown", (evt) => {
          isBackspaceKeydown = evt.keyCode === VK.BACKSPACE;
          formatNodes = getFormatNodesAtStart(editor);
          if (!evt.isDefaultPrevented()) {
            executeKeydownOverride$3(editor, caret, evt);
          }
        });
        editor.on("keyup", (evt) => {
          if (!evt.isDefaultPrevented()) {
            executeKeyupOverride(editor, evt, isBackspaceKeydown, formatNodes);
            formatNodes.length = 0;
          }
          isBackspaceKeydown = false;
        });
      };
      const reduceFontStyleNesting = (block, node) => {
        const blockSugar = SugarElement.fromDom(block);
        const nodeSugar = SugarElement.fromDom(node);
        const isSpan2 = isTag("span");
        const isEndBlock = curry(eq, blockSugar);
        const hasFontSize = (element) => isElement$8(element) && getRaw$1(element, "font-size").isSome();
        const elementsWithFontSize = [
          ...hasFontSize(nodeSugar) ? [nodeSugar] : [],
          ...ancestors$1(nodeSugar, hasFontSize, isEndBlock)
        ];
        each$e(elementsWithFontSize.slice(1), (element) => {
          remove$7(element, "font-size");
          remove$9(element, "data-mce-style");
          if (isSpan2(element) && hasNone(element)) {
            unwrap(element);
          }
        });
      };
      const firstNonWhiteSpaceNodeSibling = (node) => {
        while (node) {
          if (isElement$7(node) || isText$b(node) && node.data && /[\r\n\s]/.test(node.data)) {
            return Optional.from(SugarElement.fromDom(node));
          }
          node = node.nextSibling;
        }
        return Optional.none();
      };
      const moveToCaretPosition = (editor, root) => {
        const dom2 = editor.dom;
        const moveCaretBeforeOnEnterElementsMap = editor.schema.getMoveCaretBeforeOnEnterElements();
        if (!root) {
          return;
        }
        if (/^(LI|DT|DD)$/.test(root.nodeName)) {
          const isList2 = (e) => /^(ul|ol|dl)$/.test(name(e));
          const findFirstList = (e) => isList2(e) ? Optional.from(e) : descendant$2(e, isList2);
          const isEmpty2 = (e) => dom2.isEmpty(e.dom);
          firstNonWhiteSpaceNodeSibling(root.firstChild).each((firstChild2) => {
            findFirstList(firstChild2).fold(() => {
              if (isEmpty2(firstChild2)) {
                const element = toLeaf$1(firstChild2, 0).element;
                if (isElement$8(element) && !isBr$6(element)) {
                  append$1(element, SugarElement.fromText(nbsp));
                }
              }
            }, (firstList) => {
              before$4(firstList, SugarElement.fromText(nbsp));
            });
          });
        }
        const rng = dom2.createRng();
        root.normalize();
        if (root.hasChildNodes()) {
          const walker = new DomTreeWalker(root, root);
          let lastNode = root;
          let node;
          while (node = walker.current()) {
            if (isText$b(node)) {
              rng.setStart(node, 0);
              rng.setEnd(node, 0);
              break;
            }
            if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) {
              rng.setStartBefore(node);
              rng.setEndBefore(node);
              break;
            }
            lastNode = node;
            node = walker.next();
          }
          if (!node) {
            rng.setStart(lastNode, 0);
            rng.setEnd(lastNode, 0);
          }
        } else {
          if (isBr$7(root)) {
            if (root.nextSibling && dom2.isBlock(root.nextSibling)) {
              rng.setStartBefore(root);
              rng.setEndBefore(root);
            } else {
              rng.setStartAfter(root);
              rng.setEndAfter(root);
            }
          } else {
            rng.setStart(root, 0);
            rng.setEnd(root, 0);
          }
        }
        editor.selection.setRng(rng);
        scrollRangeIntoView(editor, rng);
      };
      const getEditableRoot = (dom2, node) => {
        const root = dom2.getRoot();
        let editableRoot;
        let parent2 = node;
        while (parent2 !== root && parent2 && dom2.getContentEditable(parent2) !== "false") {
          if (dom2.getContentEditable(parent2) === "true") {
            editableRoot = parent2;
            break;
          }
          parent2 = parent2.parentNode;
        }
        return parent2 !== root ? editableRoot : root;
      };
      const getParentBlock$1 = (editor) => {
        return Optional.from(editor.dom.getParent(editor.selection.getStart(true), editor.dom.isBlock));
      };
      const getParentBlockName = (editor) => {
        return getParentBlock$1(editor).fold(constant(""), (parentBlock) => {
          return parentBlock.nodeName.toUpperCase();
        });
      };
      const isListItemParentBlock = (editor) => {
        return getParentBlock$1(editor).filter((elm) => {
          return isListItem$2(SugarElement.fromDom(elm));
        }).isSome();
      };
      const emptyBlock = (elm) => {
        elm.innerHTML = '<br data-mce-bogus="1">';
      };
      const applyAttributes = (editor, node, forcedRootBlockAttrs) => {
        const dom2 = editor.dom;
        Optional.from(forcedRootBlockAttrs.style).map(dom2.parseStyle).each((attrStyles) => {
          const currentStyles = getAllRaw(SugarElement.fromDom(node));
          const newStyles = { ...currentStyles, ...attrStyles };
          dom2.setStyles(node, newStyles);
        });
        const attrClassesOpt = Optional.from(forcedRootBlockAttrs.class).map((attrClasses) => attrClasses.split(/\s+/));
        const currentClassesOpt = Optional.from(node.className).map((currentClasses) => filter$5(currentClasses.split(/\s+/), (clazz) => clazz !== ""));
        lift2(attrClassesOpt, currentClassesOpt, (attrClasses, currentClasses) => {
          const filteredClasses = filter$5(currentClasses, (clazz) => !contains$2(attrClasses, clazz));
          const newClasses = [...attrClasses, ...filteredClasses];
          dom2.setAttrib(node, "class", newClasses.join(" "));
        });
        const appliedAttrs = ["style", "class"];
        const remainingAttrs = filter$4(forcedRootBlockAttrs, (_, attrs) => !contains$2(appliedAttrs, attrs));
        dom2.setAttribs(node, remainingAttrs);
      };
      const setForcedBlockAttrs = (editor, node) => {
        const forcedRootBlockName = getForcedRootBlock(editor);
        if (forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
          const forcedRootBlockAttrs = getForcedRootBlockAttrs(editor);
          applyAttributes(editor, node, forcedRootBlockAttrs);
        }
      };
      const createNewBlock = (editor, container, parentBlock, editableRoot, keepStyles = true, name2, styles) => {
        const dom2 = editor.dom;
        const schema = editor.schema;
        const newBlockName = getForcedRootBlock(editor);
        const parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : "";
        let node = container;
        const textInlineElements = schema.getTextInlineElements();
        let block;
        if (name2 || parentBlockName === "TABLE" || parentBlockName === "HR") {
          block = dom2.create(name2 || newBlockName, styles || {});
        } else {
          block = parentBlock.cloneNode(false);
        }
        let caretNode = block;
        if (!keepStyles) {
          dom2.setAttrib(block, "style", null);
          dom2.setAttrib(block, "class", null);
        } else {
          do {
            if (textInlineElements[node.nodeName]) {
              if (isCaretNode(node) || isBookmarkNode$1(node)) {
                continue;
              }
              const clonedNode = node.cloneNode(false);
              dom2.setAttrib(clonedNode, "id", "");
              if (block.hasChildNodes()) {
                clonedNode.appendChild(block.firstChild);
                block.appendChild(clonedNode);
              } else {
                caretNode = clonedNode;
                block.appendChild(clonedNode);
              }
            }
          } while ((node = node.parentNode) && node !== editableRoot);
          reduceFontStyleNesting(block, caretNode);
        }
        setForcedBlockAttrs(editor, block);
        emptyBlock(caretNode);
        return block;
      };
      const getDetailsRoot = (editor, element) => editor.dom.getParent(element, isDetails);
      const isAtDetailsEdge = (root, element, isTextBlock2) => {
        let node = element;
        while (node && node !== root && isNull(node.nextSibling)) {
          const parent2 = node.parentElement;
          if (!parent2 || !isTextBlock2(parent2)) {
            return isDetails(parent2);
          }
          node = parent2;
        }
        return false;
      };
      const isLastEmptyBlockInDetails = (editor, shiftKey, element) => !shiftKey && element.nodeName.toLowerCase() === getForcedRootBlock(editor) && editor.dom.isEmpty(element) && isAtDetailsEdge(editor.getBody(), element, (el) => has$2(editor.schema.getTextBlockElements(), el.nodeName.toLowerCase()));
      const insertNewLine = (editor, createNewBlock2, parentBlock) => {
        const newBlock = createNewBlock2(getForcedRootBlock(editor));
        const root = getDetailsRoot(editor, parentBlock);
        if (!root) {
          return;
        }
        editor.dom.insertAfter(newBlock, root);
        moveToCaretPosition(editor, newBlock);
        if ((parentBlock.parentElement?.childNodes?.length ?? 0) > 1) {
          editor.dom.remove(parentBlock);
        }
      };
      const hasFirstChild = (elm, name2) => {
        return elm.firstChild && elm.firstChild.nodeName === name2;
      };
      const isFirstChild = (elm) => {
        return elm.parentNode?.firstChild === elm;
      };
      const hasParent = (elm, parentName) => {
        const parentNode = elm?.parentNode;
        return isNonNullable(parentNode) && parentNode.nodeName === parentName;
      };
      const isListBlock = (elm) => {
        return isNonNullable(elm) && /^(OL|UL|LI)$/.test(elm.nodeName);
      };
      const isListItem = (elm) => {
        return isNonNullable(elm) && /^(LI|DT|DD)$/.test(elm.nodeName);
      };
      const isNestedList = (elm) => {
        return isListBlock(elm) && isListBlock(elm.parentNode);
      };
      const getContainerBlock = (containerBlock) => {
        const containerBlockParent = containerBlock.parentNode;
        return isListItem(containerBlockParent) ? containerBlockParent : containerBlock;
      };
      const isFirstOrLastLi = (containerBlock, parentBlock, first2) => {
        let node = containerBlock[first2 ? "firstChild" : "lastChild"];
        while (node) {
          if (isElement$7(node)) {
            break;
          }
          node = node[first2 ? "nextSibling" : "previousSibling"];
        }
        return node === parentBlock;
      };
      const getStyles = (elm) => foldl(mapToArray(getAllRaw(SugarElement.fromDom(elm)), (style, styleName) => `${styleName}: ${style};`), (acc, s) => acc + s, "");
      const insert$4 = (editor, createNewBlock2, containerBlock, parentBlock, newBlockName) => {
        const dom2 = editor.dom;
        const rng = editor.selection.getRng();
        const containerParent = containerBlock.parentNode;
        if (containerBlock === editor.getBody() || !containerParent) {
          return;
        }
        if (isNestedList(containerBlock)) {
          newBlockName = "LI";
        }
        const parentBlockStyles = isListItem(parentBlock) ? getStyles(parentBlock) : void 0;
        let newBlock = isListItem(parentBlock) && parentBlockStyles ? createNewBlock2(newBlockName, { style: getStyles(parentBlock) }) : createNewBlock2(newBlockName);
        if (isFirstOrLastLi(containerBlock, parentBlock, true) && isFirstOrLastLi(containerBlock, parentBlock, false)) {
          if (hasParent(containerBlock, "LI")) {
            const containerBlockParent = getContainerBlock(containerBlock);
            dom2.insertAfter(newBlock, containerBlockParent);
            if (isFirstChild(containerBlock)) {
              dom2.remove(containerBlockParent);
            } else {
              dom2.remove(containerBlock);
            }
          } else {
            dom2.replace(newBlock, containerBlock);
          }
        } else if (isFirstOrLastLi(containerBlock, parentBlock, true)) {
          if (hasParent(containerBlock, "LI")) {
            dom2.insertAfter(newBlock, getContainerBlock(containerBlock));
            newBlock.appendChild(dom2.doc.createTextNode(" "));
            newBlock.appendChild(containerBlock);
          } else {
            containerParent.insertBefore(newBlock, containerBlock);
          }
          dom2.remove(parentBlock);
        } else if (isFirstOrLastLi(containerBlock, parentBlock, false)) {
          dom2.insertAfter(newBlock, getContainerBlock(containerBlock));
          dom2.remove(parentBlock);
        } else {
          containerBlock = getContainerBlock(containerBlock);
          const tmpRng = rng.cloneRange();
          tmpRng.setStartAfter(parentBlock);
          tmpRng.setEndAfter(containerBlock);
          const fragment = tmpRng.extractContents();
          if (newBlockName === "LI" && hasFirstChild(fragment, "LI")) {
            const previousChildren = filter$5(map$3(newBlock.children, SugarElement.fromDom), not(isTag("br")));
            newBlock = fragment.firstChild;
            dom2.insertAfter(fragment, containerBlock);
            each$e(previousChildren, (child2) => prepend(SugarElement.fromDom(newBlock), child2));
            if (parentBlockStyles) {
              newBlock.setAttribute("style", parentBlockStyles);
            }
          } else {
            dom2.insertAfter(fragment, containerBlock);
            dom2.insertAfter(newBlock, containerBlock);
          }
          dom2.remove(parentBlock);
        }
        moveToCaretPosition(editor, newBlock);
      };
      const trimZwsp = (fragment) => {
        each$e(descendants$1(SugarElement.fromDom(fragment), isText$c), (text2) => {
          const rawNode = text2.dom;
          rawNode.nodeValue = trim$2(rawNode.data);
        });
      };
      const isWithinNonEditableList = (editor, node) => {
        const parentList = editor.dom.getParent(node, "ol,ul,dl");
        return parentList !== null && editor.dom.getContentEditableParent(parentList) === "false";
      };
      const isEmptyAnchor = (dom2, elm) => {
        return elm && elm.nodeName === "A" && dom2.isEmpty(elm);
      };
      const containerAndPreviousSiblingName = (container, nodeName) => {
        return container.nodeName === nodeName || container.previousSibling && container.previousSibling.nodeName === nodeName;
      };
      const containerAndNextSiblingName = (container, nodeName) => {
        return container.nodeName === nodeName || container.nextSibling && container.nextSibling.nodeName === nodeName;
      };
      const canSplitBlock = (dom2, node) => {
        return isNonNullable(node) && dom2.isBlock(node) && !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && !/^(fixed|absolute)/i.test(node.style.position) && dom2.isEditable(node.parentNode) && dom2.getContentEditable(node) !== "false";
      };
      const trimInlineElementsOnLeftSideOfBlock = (dom2, nonEmptyElementsMap, block) => {
        const firstChilds = [];
        if (!block) {
          return;
        }
        let currentNode = block;
        while (currentNode = currentNode.firstChild) {
          if (dom2.isBlock(currentNode)) {
            return;
          }
          if (isElement$7(currentNode) && !nonEmptyElementsMap[currentNode.nodeName.toLowerCase()]) {
            firstChilds.push(currentNode);
          }
        }
        let i = firstChilds.length;
        while (i--) {
          currentNode = firstChilds[i];
          if (!currentNode.hasChildNodes() || currentNode.firstChild === currentNode.lastChild && currentNode.firstChild?.nodeValue === "") {
            dom2.remove(currentNode);
          } else {
            if (isEmptyAnchor(dom2, currentNode)) {
              dom2.remove(currentNode);
            }
          }
        }
      };
      const normalizeZwspOffset = (start2, container, offset) => {
        if (!isText$b(container)) {
          return offset;
        } else if (start2) {
          return offset === 1 && container.data.charAt(offset - 1) === ZWSP$1 ? 0 : offset;
        } else {
          return offset === container.data.length - 1 && container.data.charAt(offset) === ZWSP$1 ? container.data.length : offset;
        }
      };
      const includeZwspInRange = (rng) => {
        const newRng = rng.cloneRange();
        newRng.setStart(rng.startContainer, normalizeZwspOffset(true, rng.startContainer, rng.startOffset));
        newRng.setEnd(rng.endContainer, normalizeZwspOffset(false, rng.endContainer, rng.endOffset));
        return newRng;
      };
      const trimLeadingLineBreaks = (node) => {
        let currentNode = node;
        do {
          if (isText$b(currentNode)) {
            currentNode.data = currentNode.data.replace(/^[\r\n]+/, "");
          }
          currentNode = currentNode.firstChild;
        } while (currentNode);
      };
      const wrapSelfAndSiblingsInDefaultBlock = (editor, newBlockName, rng, container, offset) => {
        const dom2 = editor.dom;
        const editableRoot = getEditableRoot(dom2, container) ?? dom2.getRoot();
        let parentBlock = dom2.getParent(container, dom2.isBlock);
        if (!parentBlock || !canSplitBlock(dom2, parentBlock)) {
          parentBlock = parentBlock || editableRoot;
          if (!parentBlock.hasChildNodes()) {
            const newBlock = dom2.create(newBlockName);
            setForcedBlockAttrs(editor, newBlock);
            parentBlock.appendChild(newBlock);
            rng.setStart(newBlock, 0);
            rng.setEnd(newBlock, 0);
            return newBlock;
          }
          let node = container;
          while (node && node.parentNode !== parentBlock) {
            node = node.parentNode;
          }
          let startNode;
          while (node && !dom2.isBlock(node)) {
            startNode = node;
            node = node.previousSibling;
          }
          const startNodeName = startNode?.parentElement?.nodeName;
          if (startNode && startNodeName && editor.schema.isValidChild(startNodeName, newBlockName.toLowerCase())) {
            const startNodeParent = startNode.parentNode;
            const newBlock = dom2.create(newBlockName);
            setForcedBlockAttrs(editor, newBlock);
            startNodeParent.insertBefore(newBlock, startNode);
            node = startNode;
            while (node && !dom2.isBlock(node)) {
              const next2 = node.nextSibling;
              newBlock.appendChild(node);
              node = next2;
            }
            rng.setStart(container, offset);
            rng.setEnd(container, offset);
          }
        }
        return container;
      };
      const addBrToBlockIfNeeded = (dom2, block) => {
        block.normalize();
        const lastChild2 = block.lastChild;
        if (!lastChild2 || isElement$7(lastChild2) && /^(left|right)$/gi.test(dom2.getStyle(lastChild2, "float", true))) {
          dom2.add(block, "br");
        }
      };
      const shouldEndContainer = (editor, container) => {
        const optionValue = shouldEndContainerOnEmptyBlock(editor);
        if (isNullable(container)) {
          return false;
        } else if (isString(optionValue)) {
          return contains$2(Tools.explode(optionValue), container.nodeName.toLowerCase());
        } else {
          return optionValue;
        }
      };
      const insert$3 = (editor, evt) => {
        let container;
        let offset;
        let parentBlockName;
        let containerBlock;
        let isAfterLastNodeInContainer = false;
        const dom2 = editor.dom;
        const schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements();
        const rng = editor.selection.getRng();
        const newBlockName = getForcedRootBlock(editor);
        const start2 = SugarElement.fromDom(rng.startContainer);
        const child2 = child$1(start2, rng.startOffset);
        const isCef = child2.exists((element) => isHTMLElement$1(element) && !isEditable$2(element));
        const collapsedAndCef = rng.collapsed && isCef;
        const createNewBlock$1 = (name2, styles) => {
          return createNewBlock(editor, container, parentBlock, editableRoot, shouldKeepStyles(editor), name2, styles);
        };
        const isCaretAtStartOrEndOfBlock = (start3) => {
          const normalizedOffset = normalizeZwspOffset(start3, container, offset);
          if (isText$b(container) && (start3 ? normalizedOffset > 0 : normalizedOffset < container.data.length)) {
            return false;
          }
          if ((container.parentNode === parentBlock || container === parentBlock) && isAfterLastNodeInContainer && !start3) {
            return true;
          }
          if (start3 && isElement$7(container) && container === parentBlock.firstChild) {
            return true;
          }
          if (containerAndPreviousSiblingName(container, "TABLE") || containerAndPreviousSiblingName(container, "HR")) {
            if (containerAndNextSiblingName(container, "BR")) {
              return !start3;
            }
            return isAfterLastNodeInContainer && !start3 || !isAfterLastNodeInContainer && start3;
          }
          const walker = new DomTreeWalker(container, parentBlock);
          if (isText$b(container)) {
            if (start3 && normalizedOffset === 0) {
              walker.prev();
            } else if (!start3 && normalizedOffset === container.data.length) {
              walker.next();
            }
          }
          let node;
          while (node = walker.current()) {
            if (isElement$7(node)) {
              if (!node.getAttribute("data-mce-bogus")) {
                const name2 = node.nodeName.toLowerCase();
                if (nonEmptyElementsMap[name2] && name2 !== "br") {
                  return false;
                }
              }
            } else if (isText$b(node) && !isWhitespaceText(node.data)) {
              return false;
            }
            if (start3) {
              walker.prev();
            } else {
              walker.next();
            }
          }
          return true;
        };
        const insertNewBlockAfter = () => {
          let block;
          if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName !== "HGROUP") {
            block = createNewBlock$1(newBlockName);
          } else {
            block = createNewBlock$1();
          }
          if (shouldEndContainer(editor, containerBlock) && canSplitBlock(dom2, containerBlock) && dom2.isEmpty(parentBlock, void 0, { includeZwsp: true })) {
            block = dom2.split(containerBlock, parentBlock);
          } else {
            dom2.insertAfter(block, parentBlock);
          }
          moveToCaretPosition(editor, block);
          return block;
        };
        normalize$2(dom2, rng).each((normRng) => {
          rng.setStart(normRng.startContainer, normRng.startOffset);
          rng.setEnd(normRng.endContainer, normRng.endOffset);
        });
        container = rng.startContainer;
        offset = rng.startOffset;
        const shiftKey = !!(evt && evt.shiftKey);
        const ctrlKey = !!(evt && evt.ctrlKey);
        if (isElement$7(container) && container.hasChildNodes() && !collapsedAndCef) {
          isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
          container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
          if (isAfterLastNodeInContainer && isText$b(container)) {
            offset = container.data.length;
          } else {
            offset = 0;
          }
        }
        const editableRoot = getEditableRoot(dom2, container);
        if (!editableRoot || isWithinNonEditableList(editor, container)) {
          return;
        }
        if (!shiftKey) {
          container = wrapSelfAndSiblingsInDefaultBlock(editor, newBlockName, rng, container, offset);
        }
        let parentBlock = dom2.getParent(container, dom2.isBlock) || dom2.getRoot();
        containerBlock = isNonNullable(parentBlock?.parentNode) ? dom2.getParent(parentBlock.parentNode, dom2.isBlock) : null;
        parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : "";
        const containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : "";
        if (containerBlockName === "LI" && !ctrlKey) {
          const liBlock = containerBlock;
          parentBlock = liBlock;
          containerBlock = liBlock.parentNode;
          parentBlockName = containerBlockName;
        }
        if (isElement$7(containerBlock) && isLastEmptyBlockInDetails(editor, shiftKey, parentBlock)) {
          return insertNewLine(editor, createNewBlock$1, parentBlock);
        }
        if (/^(LI|DT|DD)$/.test(parentBlockName) && isElement$7(containerBlock)) {
          if (dom2.isEmpty(parentBlock)) {
            insert$4(editor, createNewBlock$1, containerBlock, parentBlock, newBlockName);
            return;
          }
        }
        if (!collapsedAndCef && (parentBlock === editor.getBody() || !canSplitBlock(dom2, parentBlock))) {
          return;
        }
        const parentBlockParent = parentBlock.parentNode;
        let newBlock;
        if (collapsedAndCef) {
          newBlock = createNewBlock$1(newBlockName);
          child2.fold(() => {
            append$1(start2, SugarElement.fromDom(newBlock));
          }, (child3) => {
            before$4(child3, SugarElement.fromDom(newBlock));
          });
          editor.selection.setCursorLocation(newBlock, 0);
        } else if (isCaretContainerBlock$1(parentBlock)) {
          newBlock = showCaretContainerBlock(parentBlock);
          if (dom2.isEmpty(parentBlock)) {
            emptyBlock(parentBlock);
          }
          setForcedBlockAttrs(editor, newBlock);
          moveToCaretPosition(editor, newBlock);
        } else if (isCaretAtStartOrEndOfBlock(false)) {
          newBlock = insertNewBlockAfter();
        } else if (isCaretAtStartOrEndOfBlock(true) && parentBlockParent) {
          const caretPos = CaretPosition.fromRangeStart(rng);
          const afterTable = isAfterTable(caretPos);
          const parentBlockSugar = SugarElement.fromDom(parentBlock);
          const afterBr = isAfterBr(parentBlockSugar, caretPos, editor.schema);
          const prevBrOpt = afterBr ? findPreviousBr(parentBlockSugar, caretPos, editor.schema).bind((pos) => Optional.from(pos.getNode())) : Optional.none();
          newBlock = parentBlockParent.insertBefore(createNewBlock$1(), parentBlock);
          const root = containerAndPreviousSiblingName(parentBlock, "HR") || afterTable ? newBlock : prevBrOpt.getOr(parentBlock);
          moveToCaretPosition(editor, root);
        } else {
          const tmpRng = includeZwspInRange(rng).cloneRange();
          tmpRng.setEndAfter(parentBlock);
          const fragment = tmpRng.extractContents();
          trimZwsp(fragment);
          trimLeadingLineBreaks(fragment);
          newBlock = fragment.firstChild;
          if (parentBlock === newBlock) {
            if (isNonNullable(parentBlockParent)) {
              dom2.insertAfter(fragment, parentBlockParent);
            }
          } else {
            dom2.insertAfter(fragment, parentBlock);
          }
          trimInlineElementsOnLeftSideOfBlock(dom2, nonEmptyElementsMap, newBlock);
          addBrToBlockIfNeeded(dom2, parentBlock);
          if (dom2.isEmpty(parentBlock)) {
            emptyBlock(parentBlock);
          }
          newBlock.normalize();
          if (dom2.isEmpty(newBlock)) {
            dom2.remove(newBlock);
            insertNewBlockAfter();
          } else {
            setForcedBlockAttrs(editor, newBlock);
            moveToCaretPosition(editor, newBlock);
          }
        }
        dom2.setAttrib(newBlock, "id", "");
        editor.dispatch("NewBlock", { newBlock });
      };
      const fakeEventName$1 = "insertParagraph";
      const blockbreak = {
        insert: insert$3,
        fakeEventName: fakeEventName$1
      };
      const hasRightSideContent = (schema, container, parentBlock) => {
        const walker = new DomTreeWalker(container, parentBlock);
        let node;
        const nonEmptyElementsMap = schema.getNonEmptyElements();
        while (node = walker.next()) {
          if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || isText$b(node) && node.length > 0) {
            return true;
          }
        }
        return false;
      };
      const moveSelectionToBr = (editor, brElm, extraBr) => {
        const rng = editor.dom.createRng();
        if (!extraBr) {
          rng.setStartAfter(brElm);
          rng.setEndAfter(brElm);
        } else {
          rng.setStartBefore(brElm);
          rng.setEndBefore(brElm);
        }
        editor.selection.setRng(rng);
        scrollRangeIntoView(editor, rng);
      };
      const insertBrAtCaret = (editor, evt) => {
        const selection = editor.selection;
        const dom2 = editor.dom;
        const rng = selection.getRng();
        let brElm;
        let extraBr = false;
        normalize$2(dom2, rng).each((normRng) => {
          rng.setStart(normRng.startContainer, normRng.startOffset);
          rng.setEnd(normRng.endContainer, normRng.endOffset);
        });
        let offset = rng.startOffset;
        let container = rng.startContainer;
        if (isElement$7(container) && container.hasChildNodes()) {
          const isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
          container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
          if (isAfterLastNodeInContainer && isText$b(container)) {
            offset = container.data.length;
          } else {
            offset = 0;
          }
        }
        let parentBlock = dom2.getParent(container, dom2.isBlock);
        const containerBlock = parentBlock && parentBlock.parentNode ? dom2.getParent(parentBlock.parentNode, dom2.isBlock) : null;
        const containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : "";
        const isControlKey = !!(evt && evt.ctrlKey);
        if (containerBlockName === "LI" && !isControlKey) {
          parentBlock = containerBlock;
        }
        if (isText$b(container) && offset >= container.data.length) {
          if (!hasRightSideContent(editor.schema, container, parentBlock || dom2.getRoot())) {
            brElm = dom2.create("br");
            rng.insertNode(brElm);
            rng.setStartAfter(brElm);
            rng.setEndAfter(brElm);
            extraBr = true;
          }
        }
        brElm = dom2.create("br");
        rangeInsertNode(dom2, rng, brElm);
        moveSelectionToBr(editor, brElm, extraBr);
        editor.undoManager.add();
      };
      const insertBrBefore = (editor, inline) => {
        const br = SugarElement.fromTag("br");
        before$4(SugarElement.fromDom(inline), br);
        editor.undoManager.add();
      };
      const insertBrAfter = (editor, inline) => {
        if (!hasBrAfter(editor.getBody(), inline)) {
          after$4(SugarElement.fromDom(inline), SugarElement.fromTag("br"));
        }
        const br = SugarElement.fromTag("br");
        after$4(SugarElement.fromDom(inline), br);
        moveSelectionToBr(editor, br.dom, false);
        editor.undoManager.add();
      };
      const isBeforeBr = (pos) => {
        return isBr$7(pos.getNode());
      };
      const hasBrAfter = (rootNode, startNode) => {
        if (isBeforeBr(CaretPosition.after(startNode))) {
          return true;
        } else {
          return nextPosition(rootNode, CaretPosition.after(startNode)).map((pos) => {
            return isBr$7(pos.getNode());
          }).getOr(false);
        }
      };
      const isAnchorLink = (elm) => {
        return elm && elm.nodeName === "A" && "href" in elm;
      };
      const isInsideAnchor = (location) => {
        return location.fold(never, isAnchorLink, isAnchorLink, never);
      };
      const readInlineAnchorLocation = (editor) => {
        const isInlineTarget$1 = curry(isInlineTarget, editor);
        const position = CaretPosition.fromRangeStart(editor.selection.getRng());
        return readLocation(isInlineTarget$1, editor.getBody(), position).filter(isInsideAnchor);
      };
      const insertBrOutsideAnchor = (editor, location) => {
        location.fold(noop, curry(insertBrBefore, editor), curry(insertBrAfter, editor), noop);
      };
      const insert$2 = (editor, evt) => {
        const anchorLocation = readInlineAnchorLocation(editor);
        if (anchorLocation.isSome()) {
          anchorLocation.each(curry(insertBrOutsideAnchor, editor));
        } else {
          insertBrAtCaret(editor, evt);
        }
      };
      const fakeEventName = "insertLineBreak";
      const linebreak = {
        insert: insert$2,
        fakeEventName
      };
      const matchesSelector = (editor, selector) => {
        return getParentBlock$1(editor).filter((parentBlock) => {
          return selector.length > 0 && is$2(SugarElement.fromDom(parentBlock), selector);
        }).isSome();
      };
      const shouldInsertBr = (editor) => {
        return matchesSelector(editor, getBrNewLineSelector(editor));
      };
      const shouldBlockNewLine$1 = (editor) => {
        return matchesSelector(editor, getNoNewLineSelector(editor));
      };
      const newLineAction = Adt.generate([
        { br: [] },
        { block: [] },
        { none: [] }
      ]);
      const shouldBlockNewLine = (editor, _shiftKey) => {
        return shouldBlockNewLine$1(editor);
      };
      const inListBlock = (requiredState) => {
        return (editor, _shiftKey) => {
          return isListItemParentBlock(editor) === requiredState;
        };
      };
      const inBlock = (blockName, requiredState) => (editor, _shiftKey) => {
        const state = getParentBlockName(editor) === blockName.toUpperCase();
        return state === requiredState;
      };
      const inCefBlock = (editor) => {
        const editableRoot = getEditableRoot(editor.dom, editor.selection.getStart());
        return isNullable(editableRoot);
      };
      const inPreBlock = (requiredState) => inBlock("pre", requiredState);
      const inSummaryBlock = () => inBlock("summary", true);
      const shouldPutBrInPre = (requiredState) => {
        return (editor, _shiftKey) => {
          return shouldPutBrInPre$1(editor) === requiredState;
        };
      };
      const inBrContext = (editor, _shiftKey) => {
        return shouldInsertBr(editor);
      };
      const hasShiftKey = (_editor, shiftKey) => {
        return shiftKey;
      };
      const canInsertIntoEditableRoot = (editor) => {
        const forcedRootBlock = getForcedRootBlock(editor);
        const rootEditable = getEditableRoot(editor.dom, editor.selection.getStart());
        return isNonNullable(rootEditable) && editor.schema.isValidChild(rootEditable.nodeName, forcedRootBlock);
      };
      const isInRootWithEmptyOrCEF = (editor) => {
        const rng = editor.selection.getRng();
        const start2 = SugarElement.fromDom(rng.startContainer);
        const child2 = child$1(start2, rng.startOffset);
        const isCefOpt = child2.map((element) => isHTMLElement$1(element) && !isEditable$2(element));
        return rng.collapsed && isCefOpt.getOr(true);
      };
      const match = (predicates, action2) => {
        return (editor, shiftKey) => {
          const isMatch = foldl(predicates, (res, p) => {
            return res && p(editor, shiftKey);
          }, true);
          return isMatch ? Optional.some(action2) : Optional.none();
        };
      };
      const getAction = (editor, evt) => {
        return evaluateUntil([
          match([shouldBlockNewLine], newLineAction.none()),
          // If the pre block is cef, do not try to insert a new line (or delete contents)
          match([inPreBlock(true), inCefBlock], newLineAction.none()),
          match([inSummaryBlock()], newLineAction.br()),
          match([inPreBlock(true), shouldPutBrInPre(false), hasShiftKey], newLineAction.br()),
          match([inPreBlock(true), shouldPutBrInPre(false)], newLineAction.block()),
          match([inPreBlock(true), shouldPutBrInPre(true), hasShiftKey], newLineAction.block()),
          match([inPreBlock(true), shouldPutBrInPre(true)], newLineAction.br()),
          // TODO: TINY-9127 investigate if the list handling (and pre) is correct here.
          match([inListBlock(true), hasShiftKey], newLineAction.br()),
          match([inListBlock(true)], newLineAction.block()),
          match([inBrContext], newLineAction.br()),
          match([hasShiftKey], newLineAction.br()),
          match([canInsertIntoEditableRoot], newLineAction.block()),
          match([isInRootWithEmptyOrCEF], newLineAction.block())
        ], [editor, !!(evt && evt.shiftKey)]).getOr(newLineAction.none());
      };
      const insertBreak = (breakType, editor, evt) => {
        if (editor.mode.isReadOnly()) {
          return;
        }
        if (!editor.selection.isCollapsed()) {
          execEditorDeleteCommand(editor);
        }
        if (isNonNullable(evt)) {
          const event = fireBeforeInputEvent(editor, breakType.fakeEventName);
          if (event.isDefaultPrevented()) {
            return;
          }
        }
        breakType.insert(editor, evt);
        if (isNonNullable(evt)) {
          fireInputEvent(editor, breakType.fakeEventName);
        }
      };
      const insert$1 = (editor, evt) => {
        if (editor.mode.isReadOnly()) {
          return;
        }
        const br = () => insertBreak(linebreak, editor, evt);
        const block = () => insertBreak(blockbreak, editor, evt);
        const logicalAction = getAction(editor, evt);
        switch (getNewlineBehavior(editor)) {
          case "linebreak":
            logicalAction.fold(br, br, noop);
            break;
          case "block":
            logicalAction.fold(block, block, noop);
            break;
          case "invert":
            logicalAction.fold(block, br, noop);
            break;
          // implied by the options processor, unnecessary
          // case 'default':
          default:
            logicalAction.fold(br, block, noop);
            break;
        }
      };
      const platform$1 = detect$1();
      const isIOSSafari = platform$1.os.isiOS() && platform$1.browser.isSafari();
      const handleEnterKeyEvent = (editor, event) => {
        if (event.isDefaultPrevented()) {
          return;
        }
        event.preventDefault();
        endTypingLevelIgnoreLocks(editor.undoManager);
        editor.undoManager.transact(() => {
          insert$1(editor, event);
        });
      };
      const isCaretAfterKoreanCharacter = (rng) => {
        if (!rng.collapsed) {
          return false;
        }
        const startContainer = rng.startContainer;
        if (isText$b(startContainer)) {
          const koreanCharRegex = /^[\uAC00-\uD7AF\u1100-\u11FF\u3130-\u318F\uA960-\uA97F\uD7B0-\uD7FF]$/;
          const char = startContainer.data.charAt(rng.startOffset - 1);
          return koreanCharRegex.test(char);
        } else {
          return false;
        }
      };
      const setup$n = (editor) => {
        let iOSSafariKeydownBookmark = Optional.none();
        const iOSSafariKeydownOverride = (editor2) => {
          iOSSafariKeydownBookmark = Optional.some(editor2.selection.getBookmark());
          editor2.undoManager.add();
        };
        const iOSSafariKeyupOverride = (editor2, event) => {
          editor2.undoManager.undo();
          iOSSafariKeydownBookmark.fold(noop, (b) => editor2.selection.moveToBookmark(b));
          handleEnterKeyEvent(editor2, event);
          iOSSafariKeydownBookmark = Optional.none();
        };
        editor.on("keydown", (event) => {
          if (event.keyCode === VK.ENTER) {
            if (isIOSSafari && isCaretAfterKoreanCharacter(editor.selection.getRng())) {
              iOSSafariKeydownOverride(editor);
            } else {
              handleEnterKeyEvent(editor, event);
            }
          }
        });
        editor.on("keyup", (event) => {
          if (event.keyCode === VK.ENTER) {
            iOSSafariKeydownBookmark.each(() => iOSSafariKeyupOverride(editor, event));
          }
        });
      };
      const executeKeydownOverride$2 = (editor, caret, evt) => {
        const isMac = Env.os.isMacOS() || Env.os.isiOS();
        execute([
          { keyCode: VK.END, action: action(moveToLineEndPoint$1, editor, true) },
          { keyCode: VK.HOME, action: action(moveToLineEndPoint$1, editor, false) },
          ...!isMac ? [
            { keyCode: VK.HOME, action: action(selectToEndPoint, editor, false), ctrlKey: true, shiftKey: true },
            { keyCode: VK.END, action: action(selectToEndPoint, editor, true), ctrlKey: true, shiftKey: true }
          ] : [],
          { keyCode: VK.END, action: action(moveToLineEndPoint, editor, true) },
          { keyCode: VK.HOME, action: action(moveToLineEndPoint, editor, false) },
          { keyCode: VK.END, action: action(moveToLineEndPoint$2, editor, true, caret) },
          { keyCode: VK.HOME, action: action(moveToLineEndPoint$2, editor, false, caret) }
        ], evt).each((_) => {
          evt.preventDefault();
        });
      };
      const setup$m = (editor, caret) => {
        editor.on("keydown", (evt) => {
          if (!evt.isDefaultPrevented()) {
            executeKeydownOverride$2(editor, caret, evt);
          }
        });
      };
      const setup$l = (editor) => {
        editor.on("input", (e) => {
          if (!e.isComposing) {
            normalizeNbspsInEditor(editor);
          }
        });
      };
      const platform = detect$1();
      const executeKeyupAction = (editor, caret, evt) => {
        execute([
          { keyCode: VK.PAGE_UP, action: action(moveToLineEndPoint$2, editor, false, caret) },
          { keyCode: VK.PAGE_DOWN, action: action(moveToLineEndPoint$2, editor, true, caret) }
        ], evt);
      };
      const stopImmediatePropagation = (e) => e.stopImmediatePropagation();
      const isPageUpDown = (evt) => evt.keyCode === VK.PAGE_UP || evt.keyCode === VK.PAGE_DOWN;
      const setNodeChangeBlocker = (blocked, editor, block) => {
        if (block && !blocked.get()) {
          editor.on("NodeChange", stopImmediatePropagation, true);
        } else if (!block && blocked.get()) {
          editor.off("NodeChange", stopImmediatePropagation);
        }
        blocked.set(block);
      };
      const setup$k = (editor, caret) => {
        if (platform.os.isMacOS()) {
          return;
        }
        const blocked = Cell(false);
        editor.on("keydown", (evt) => {
          if (isPageUpDown(evt)) {
            setNodeChangeBlocker(blocked, editor, true);
          }
        });
        editor.on("keyup", (evt) => {
          if (!evt.isDefaultPrevented()) {
            executeKeyupAction(editor, caret, evt);
          }
          if (isPageUpDown(evt) && blocked.get()) {
            setNodeChangeBlocker(blocked, editor, false);
            editor.nodeChanged();
          }
        });
      };
      const isValidContainer = (root, container) => root === container || root.contains(container);
      const isInEditableRange = (editor, range2) => {
        if (!isValidContainer(editor.getBody(), range2.startContainer) || !isValidContainer(editor.getBody(), range2.endContainer)) {
          return true;
        }
        return isEditableRange(editor.dom, range2);
      };
      const setup$j = (editor) => {
        editor.on("beforeinput", (e) => {
          if (!editor.selection.isEditable() || exists(e.getTargetRanges(), (rng) => !isInEditableRange(editor, rng))) {
            e.preventDefault();
          }
        });
      };
      const insertTextAtPosition = (text2, pos) => {
        const container = pos.container();
        const offset = pos.offset();
        if (isText$b(container)) {
          container.insertData(offset, text2);
          return Optional.some(CaretPosition(container, offset + text2.length));
        } else {
          return getElementFromPosition(pos).map((elm) => {
            const textNode = SugarElement.fromText(text2);
            if (pos.isAtEnd()) {
              after$4(elm, textNode);
            } else {
              before$4(elm, textNode);
            }
            return CaretPosition(textNode.dom, text2.length);
          });
        }
      };
      const insertNbspAtPosition = curry(insertTextAtPosition, nbsp);
      const insertSpaceAtPosition = curry(insertTextAtPosition, " ");
      const insertSpaceOrNbspAtPosition = (root, pos, schema) => needsToHaveNbsp(root, pos, schema) ? insertNbspAtPosition(pos) : insertSpaceAtPosition(pos);
      const locationToCaretPosition = (root) => (location) => location.fold((element) => prevPosition(root.dom, CaretPosition.before(element)), (element) => firstPositionIn(element), (element) => lastPositionIn(element), (element) => nextPosition(root.dom, CaretPosition.after(element)));
      const insertInlineBoundarySpaceOrNbsp = (root, pos, schema) => (checkPos) => needsToHaveNbsp(root, checkPos, schema) ? insertNbspAtPosition(pos) : insertSpaceAtPosition(pos);
      const setSelection = (editor) => (pos) => {
        editor.selection.setRng(pos.toRange());
        editor.nodeChanged();
      };
      const isInsideSummary = (domUtils, node) => domUtils.isEditable(domUtils.getParent(node, "summary"));
      const insertSpaceOrNbspAtSelection = (editor) => {
        const pos = CaretPosition.fromRangeStart(editor.selection.getRng());
        const root = SugarElement.fromDom(editor.getBody());
        if (editor.selection.isCollapsed()) {
          const isInlineTarget$1 = curry(isInlineTarget, editor);
          const caretPosition = CaretPosition.fromRangeStart(editor.selection.getRng());
          return readLocation(isInlineTarget$1, editor.getBody(), caretPosition).bind(locationToCaretPosition(root)).map((checkPos) => () => insertInlineBoundarySpaceOrNbsp(root, pos, editor.schema)(checkPos).each(setSelection(editor)));
        } else {
          return Optional.none();
        }
      };
      const insertSpaceInSummaryAtSelectionOnFirefox = (editor) => {
        const insertSpaceThunk = () => {
          const root = SugarElement.fromDom(editor.getBody());
          if (!editor.selection.isCollapsed()) {
            editor.getDoc().execCommand("Delete");
          }
          const pos = CaretPosition.fromRangeStart(editor.selection.getRng());
          insertSpaceOrNbspAtPosition(root, pos, editor.schema).each(setSelection(editor));
        };
        return someIf(Env.browser.isFirefox() && editor.selection.isEditable() && isInsideSummary(editor.dom, editor.selection.getRng().startContainer), insertSpaceThunk);
      };
      const executeKeydownOverride$1 = (editor, evt) => {
        executeWithDelayedAction([
          { keyCode: VK.SPACEBAR, action: action(insertSpaceOrNbspAtSelection, editor) },
          { keyCode: VK.SPACEBAR, action: action(insertSpaceInSummaryAtSelectionOnFirefox, editor) }
        ], evt).each((applyAction) => {
          evt.preventDefault();
          const event = fireBeforeInputEvent(editor, "insertText", { data: " " });
          if (!event.isDefaultPrevented()) {
            applyAction();
            fireInputEvent(editor, "insertText", { data: " " });
          }
        });
      };
      const setup$i = (editor) => {
        editor.on("keydown", (evt) => {
          if (!evt.isDefaultPrevented()) {
            executeKeydownOverride$1(editor, evt);
          }
        });
      };
      const tableTabNavigation = (editor) => {
        if (hasTableTabNavigation(editor)) {
          return [
            { keyCode: VK.TAB, action: action(handleTab, editor, true) },
            { keyCode: VK.TAB, shiftKey: true, action: action(handleTab, editor, false) }
          ];
        } else {
          return [];
        }
      };
      const executeKeydownOverride = (editor, evt) => {
        execute([
          ...tableTabNavigation(editor)
        ], evt).each((_) => {
          evt.preventDefault();
        });
      };
      const setup$h = (editor) => {
        editor.on("keydown", (evt) => {
          if (!evt.isDefaultPrevented()) {
            executeKeydownOverride(editor, evt);
          }
        });
      };
      const setup$g = (editor) => {
        editor.addShortcut("Meta+P", "", "mcePrint");
        setup$p(editor);
        if (isRtc(editor)) {
          return Cell(null);
        } else {
          const caret = setupSelectedState(editor);
          setup$j(editor);
          setup$r(editor);
          setup$q(editor, caret);
          setup$o(editor, caret);
          setup$n(editor);
          setup$i(editor);
          setup$l(editor);
          setup$h(editor);
          setup$m(editor, caret);
          setup$k(editor, caret);
          return caret;
        }
      };
      const updateList = (editor, update) => {
        const parentList = getParentList(editor);
        if (parentList === null || isWithinNonEditableList$1(editor, parentList)) {
          return;
        }
        editor.undoManager.transact(() => {
          if (isObject(update.styles)) {
            editor.dom.setStyles(parentList, update.styles);
          }
          if (isObject(update.attrs)) {
            each$d(update.attrs, (v, k) => editor.dom.setAttrib(parentList, k, v));
          }
        });
      };
      const queryListCommandState = (editor, listName) => () => {
        const parentList = getParentList(editor);
        return isNonNullable(parentList) && parentList.nodeName === listName;
      };
      const setup$f = (editor) => {
        editor.addCommand("InsertUnorderedList", (ui, detail) => {
          toggleList(editor, "UL", detail);
        });
        editor.addCommand("InsertOrderedList", (ui, detail) => {
          toggleList(editor, "OL", detail);
        });
        editor.addCommand("InsertDefinitionList", (ui, detail) => {
          toggleList(editor, "DL", detail);
        });
        editor.addCommand("RemoveList", () => {
          flattenListSelection(editor);
        });
        editor.addCommand("mceListUpdate", (ui, detail) => {
          if (isObject(detail)) {
            updateList(editor, detail);
          }
        });
        editor.addCommand("mceListBackspaceDelete", (_ui, forward) => {
          backspaceDelete$c(editor, forward);
        });
        editor.addQueryStateHandler("InsertUnorderedList", queryListCommandState(editor, "UL"));
        editor.addQueryStateHandler("InsertOrderedList", queryListCommandState(editor, "OL"));
        editor.addQueryStateHandler("InsertDefinitionList", queryListCommandState(editor, "DL"));
      };
      const setup$e = (editor) => {
        editor.on("keydown", (e) => {
          if (e.keyCode === VK.BACKSPACE) {
            if (backspaceDelete$c(editor, false)) {
              e.preventDefault();
            }
          } else if (e.keyCode === VK.DELETE) {
            if (backspaceDelete$c(editor, true)) {
              e.preventDefault();
            }
          }
        });
      };
      const isTextNode = (node) => node.type === 3;
      const isEmpty = (nodeBuffer) => nodeBuffer.length === 0;
      const wrapInvalidChildren = (list) => {
        const insertListItem = (buffer, refNode) => {
          const li = AstNode.create("li");
          each$e(buffer, (node) => li.append(node));
          if (refNode) {
            list.insert(li, refNode, true);
          } else {
            list.append(li);
          }
        };
        const reducer = (buffer, node) => {
          if (isTextNode(node)) {
            return [...buffer, node];
          } else if (!isEmpty(buffer) && !isTextNode(node)) {
            insertListItem(buffer, node);
            return [];
          } else {
            return buffer;
          }
        };
        const restBuffer = foldl(list.children(), reducer, []);
        if (!isEmpty(restBuffer)) {
          insertListItem(restBuffer);
        }
      };
      const setup$d = (editor) => {
        editor.on("PreInit", () => {
          const { parser } = editor;
          parser.addNodeFilter("ul,ol", (nodes) => each$e(nodes, wrapInvalidChildren));
        });
      };
      const setupTabKey = (editor) => {
        editor.on("keydown", (e) => {
          if (e.keyCode !== VK.TAB || VK.metaKeyPressed(e)) {
            return;
          }
          editor.undoManager.transact(() => {
            if (e.shiftKey ? outdentListSelection(editor) : indentListSelection(editor)) {
              e.preventDefault();
            }
          });
        });
      };
      const setup$c = (editor) => {
        if (shouldIndentOnTab(editor)) {
          setupTabKey(editor);
        }
      };
      const setup$b = (editor) => {
        setup$e(editor);
        setup$f(editor);
        setup$d(editor);
        setup$c(editor);
      };
      class NodeChange {
        editor;
        lastPath = [];
        constructor(editor) {
          this.editor = editor;
          let lastRng;
          const self = this;
          if (!("onselectionchange" in editor.getDoc())) {
            editor.on("NodeChange click mouseup keyup focus", (e) => {
              const nativeRng = editor.selection.getRng();
              const fakeRng = {
                startContainer: nativeRng.startContainer,
                startOffset: nativeRng.startOffset,
                endContainer: nativeRng.endContainer,
                endOffset: nativeRng.endOffset
              };
              if (e.type === "nodechange" || !isEq$4(fakeRng, lastRng)) {
                editor.dispatch("SelectionChange");
              }
              lastRng = fakeRng;
            });
          }
          editor.on("contextmenu", () => {
            store(editor);
            editor.dispatch("SelectionChange");
          });
          editor.on("SelectionChange", () => {
            const startElm = editor.selection.getStart(true);
            if (!startElm) {
              return;
            }
            if (hasAnyRanges(editor) && !self.isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {
              editor.nodeChanged({ selectionChange: true });
            }
          });
          editor.on("mouseup", (e) => {
            if (!e.isDefaultPrevented() && hasAnyRanges(editor)) {
              if (editor.selection.getNode().nodeName === "IMG") {
                Delay.setEditorTimeout(editor, () => {
                  editor.nodeChanged();
                });
              } else {
                editor.nodeChanged();
              }
            }
          });
        }
        /**
         * Dispatches out a onNodeChange event to all observers. This method should be called when you
         * need to update the UI states or element path etc.
         *
         * @method nodeChanged
         * @param {Object} args Optional args to pass to NodeChange event handlers.
         */
        nodeChanged(args = {}) {
          const editor = this.editor;
          const selection = editor.selection;
          let node;
          if (editor.initialized && selection && !shouldDisableNodeChange(editor) && !isDisabled$1(editor)) {
            const root = editor.getBody();
            node = selection.getStart(true) || root;
            if (node.ownerDocument !== editor.getDoc() || !editor.dom.isChildOf(node, root)) {
              node = root;
            }
            const parents2 = [];
            editor.dom.getParent(node, (node2) => {
              if (node2 === root) {
                return true;
              } else {
                parents2.push(node2);
                return false;
              }
            });
            editor.dispatch("NodeChange", {
              ...args,
              element: node,
              parents: parents2
            });
          }
        }
        /**
         * Returns true/false if the current element path has been changed or not.
         *
         * @private
         * @return {Boolean} True if the element path is the same false if it's not.
         */
        isSameElementPath(startElm) {
          let i;
          const editor = this.editor;
          const currentPath = reverse(editor.dom.getParents(startElm, always, editor.getBody()));
          if (currentPath.length === this.lastPath.length) {
            for (i = currentPath.length; i >= 0; i--) {
              if (currentPath[i] !== this.lastPath[i]) {
                break;
              }
            }
            if (i === -1) {
              this.lastPath = currentPath;
              return true;
            }
          }
          this.lastPath = currentPath;
          return false;
        }
      }
      const internalMimeType = "x-tinymce/html";
      const internalHtmlMime = constant(internalMimeType);
      const internalMark = "<!-- " + internalMimeType + " -->";
      const mark = (html2) => internalMark + html2;
      const unmark = (html2) => html2.replace(internalMark, "");
      const isMarked = (html2) => html2.indexOf(internalMark) !== -1;
      const isPlainText = (text2) => {
        return !/<(?:\/?(?!(?:div|p|br|span)>)\w+|(?:(?!(?:span style="white-space:\s?pre;?">)|br\s?\/>))\w+\s[^>]+)>/i.test(text2);
      };
      const openContainer = (rootTag, rootAttrs) => {
        let tag = "<" + rootTag;
        const attrs = mapToArray(rootAttrs, (value2, key) => key + '="' + Entities.encodeAllRaw(value2) + '"');
        if (attrs.length) {
          tag += " " + attrs.join(" ");
        }
        return tag + ">";
      };
      const toBlockElements = (text2, rootTag, rootAttrs) => {
        const blocks = text2.split(/\n\n/);
        const tagOpen = openContainer(rootTag, rootAttrs);
        const tagClose = "</" + rootTag + ">";
        const paragraphs = map$3(blocks, (p) => {
          return p.split(/\n/).join("<br />");
        });
        const stitch = (p) => {
          return tagOpen + p + tagClose;
        };
        return paragraphs.length === 1 ? paragraphs[0] : map$3(paragraphs, stitch).join("");
      };
      const pasteBinDefaultContent = "%MCEPASTEBIN%";
      const create$5 = (editor, lastRngCell) => {
        const { dom: dom2, selection } = editor;
        const body = editor.getBody();
        lastRngCell.set(selection.getRng());
        const pasteBinElm = dom2.add(editor.getBody(), "div", {
          "id": "mcepastebin",
          "class": "mce-pastebin",
          "contentEditable": true,
          "data-mce-bogus": "all",
          "style": "position: fixed; top: 50%; width: 10px; height: 10px; overflow: hidden; opacity: 0"
        }, pasteBinDefaultContent);
        if (Env.browser.isFirefox()) {
          dom2.setStyle(pasteBinElm, "left", dom2.getStyle(body, "direction", true) === "rtl" ? 65535 : -65535);
        }
        dom2.bind(pasteBinElm, "beforedeactivate focusin focusout", (e) => {
          e.stopPropagation();
        });
        pasteBinElm.focus();
        selection.select(pasteBinElm, true);
      };
      const remove = (editor, lastRngCell) => {
        const dom2 = editor.dom;
        if (getEl(editor)) {
          let pasteBinClone;
          const lastRng = lastRngCell.get();
          while (pasteBinClone = getEl(editor)) {
            dom2.remove(pasteBinClone);
            dom2.unbind(pasteBinClone);
          }
          if (lastRng) {
            editor.selection.setRng(lastRng);
          }
        }
        lastRngCell.set(null);
      };
      const getEl = (editor) => editor.dom.get("mcepastebin");
      const isPasteBin = (elm) => isNonNullable(elm) && elm.id === "mcepastebin";
      const getHtml = (editor) => {
        const dom2 = editor.dom;
        const copyAndRemove = (toElm, fromElm) => {
          toElm.appendChild(fromElm);
          dom2.remove(fromElm, true);
        };
        const [pasteBinElm, ...pasteBinClones] = filter$5(editor.getBody().childNodes, isPasteBin);
        each$e(pasteBinClones, (pasteBinClone) => {
          copyAndRemove(pasteBinElm, pasteBinClone);
        });
        const dirtyWrappers = dom2.select("div[id=mcepastebin]", pasteBinElm);
        for (let i = dirtyWrappers.length - 1; i >= 0; i--) {
          const cleanWrapper = dom2.create("div");
          pasteBinElm.insertBefore(cleanWrapper, dirtyWrappers[i]);
          copyAndRemove(cleanWrapper, dirtyWrappers[i]);
        }
        return pasteBinElm ? pasteBinElm.innerHTML : "";
      };
      const isDefaultPasteBinContent = (content) => content === pasteBinDefaultContent;
      const PasteBin = (editor) => {
        const lastRng = Cell(null);
        return {
          create: () => create$5(editor, lastRng),
          remove: () => remove(editor, lastRng),
          getEl: () => getEl(editor),
          getHtml: () => getHtml(editor),
          getLastRng: lastRng.get
        };
      };
      const filter = (content, items) => {
        Tools.each(items, (v) => {
          if (is$5(v, RegExp)) {
            content = content.replace(v, "");
          } else {
            content = content.replace(v[0], v[1]);
          }
        });
        return content;
      };
      const innerText = (html2) => {
        const schema = Schema();
        const domParser = DomParser({}, schema);
        let text2 = "";
        const voidElements = schema.getVoidElements();
        const ignoreElements = Tools.makeMap("script noscript style textarea video audio iframe object", " ");
        const blockElements = schema.getBlockElements();
        const walk2 = (node) => {
          const name2 = node.name, currentNode = node;
          if (name2 === "br") {
            text2 += "\n";
            return;
          }
          if (name2 === "wbr") {
            return;
          }
          if (voidElements[name2]) {
            text2 += " ";
          }
          if (ignoreElements[name2]) {
            text2 += " ";
            return;
          }
          if (node.type === 3) {
            text2 += node.value;
          }
          if (!(node.name in schema.getVoidElements())) {
            let currentNode2 = node.firstChild;
            if (currentNode2) {
              do {
                walk2(currentNode2);
              } while (currentNode2 = currentNode2.next);
            }
          }
          if (blockElements[name2] && currentNode.next) {
            text2 += "\n";
            if (name2 === "p") {
              text2 += "\n";
            }
          }
        };
        html2 = filter(html2, [
          /<!\[[^\]]+\]>/g
          // Conditional comments
        ]);
        walk2(domParser.parse(html2));
        return text2;
      };
      const trimHtml = (html2) => {
        const trimSpaces = (all2, s1, s2) => {
          if (!s1 && !s2) {
            return " ";
          }
          return nbsp;
        };
        html2 = filter(html2, [
          /^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/ig,
          // Remove anything but the contents within the BODY element
          /<!--StartFragment-->|<!--EndFragment-->/g,
          // Inner fragments (tables from excel on mac)
          [/( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g, trimSpaces],
          /<br class="Apple-interchange-newline">/g,
          /<br>$/i
          // Trailing BR elements
        ]);
        return html2;
      };
      const createIdGenerator = (prefix) => {
        let count2 = 0;
        return () => {
          return prefix + count2++;
        };
      };
      const getImageMimeType = (ext) => {
        const lowerExt = ext.toLowerCase();
        const mimeOverrides = {
          jpg: "jpeg",
          jpe: "jpeg",
          jfi: "jpeg",
          jif: "jpeg",
          jfif: "jpeg",
          pjpeg: "jpeg",
          pjp: "jpeg",
          svg: "svg+xml"
        };
        return Tools.hasOwn(mimeOverrides, lowerExt) ? "image/" + mimeOverrides[lowerExt] : "image/" + lowerExt;
      };
      const preProcess = (editor, html2) => {
        const parser = DomParser({
          sanitize: shouldSanitizeXss(editor),
          sandbox_iframes: shouldSandboxIframes(editor),
          sandbox_iframes_exclusions: getSandboxIframesExclusions(editor),
          convert_unsafe_embeds: shouldConvertUnsafeEmbeds(editor)
        }, editor.schema);
        parser.addNodeFilter("meta", (nodes) => {
          Tools.each(nodes, (node) => {
            node.remove();
          });
        });
        const fragment = parser.parse(html2, { forced_root_block: false, isRootContent: true });
        return HtmlSerializer({ validate: true }, editor.schema).serialize(fragment);
      };
      const processResult = (content, cancelled) => ({ content, cancelled });
      const postProcessFilter = (editor, html2, internal) => {
        const tempBody = editor.dom.create("div", { style: "display:none" }, html2);
        const postProcessArgs = firePastePostProcess(editor, tempBody, internal);
        return processResult(postProcessArgs.node.innerHTML, postProcessArgs.isDefaultPrevented());
      };
      const filterContent = (editor, content, internal) => {
        const preProcessArgs = firePastePreProcess(editor, content, internal);
        const filteredContent = preProcess(editor, preProcessArgs.content);
        if (editor.hasEventListeners("PastePostProcess") && !preProcessArgs.isDefaultPrevented()) {
          return postProcessFilter(editor, filteredContent, internal);
        } else {
          return processResult(filteredContent, preProcessArgs.isDefaultPrevented());
        }
      };
      const process = (editor, html2, internal) => {
        return filterContent(editor, html2, internal);
      };
      const pasteHtml$1 = (editor, html2) => {
        editor.insertContent(html2, {
          merge: shouldPasteMergeFormats(editor),
          paste: true
        });
        return true;
      };
      const isAbsoluteUrl = (url) => /^https?:\/\/[\w\-\/+=.,!;:&%@^~(){}?#]+$/i.test(url);
      const isImageUrl = (editor, url) => {
        return isAbsoluteUrl(url) && exists(getAllowedImageFileTypes(editor), (type2) => endsWith(url.toLowerCase(), `.${type2.toLowerCase()}`));
      };
      const createImage = (editor, url, pasteHtmlFn) => {
        editor.undoManager.extra(() => {
          pasteHtmlFn(editor, url);
        }, () => {
          editor.insertContent('<img src="' + url + '">');
        });
        return true;
      };
      const createLink = (editor, url, pasteHtmlFn) => {
        editor.undoManager.extra(() => {
          pasteHtmlFn(editor, url);
        }, () => {
          editor.execCommand("mceInsertLink", false, url);
        });
        return true;
      };
      const linkSelection = (editor, html2, pasteHtmlFn) => !editor.selection.isCollapsed() && isAbsoluteUrl(html2) ? createLink(editor, html2, pasteHtmlFn) : false;
      const insertImage = (editor, html2, pasteHtmlFn) => isImageUrl(editor, html2) ? createImage(editor, html2, pasteHtmlFn) : false;
      const smartInsertContent = (editor, html2) => {
        Tools.each([
          linkSelection,
          insertImage,
          pasteHtml$1
        ], (action2) => {
          return !action2(editor, html2, pasteHtml$1);
        });
      };
      const insertContent = (editor, html2, pasteAsText) => {
        if (pasteAsText || !isSmartPasteEnabled(editor)) {
          pasteHtml$1(editor, html2);
        } else {
          smartInsertContent(editor, html2);
        }
      };
      const uniqueId = createIdGenerator("mceclip");
      const createPasteDataTransfer = (html2) => {
        const dataTransfer = createDataTransfer();
        setHtmlData(dataTransfer, html2);
        setReadOnlyMode(dataTransfer);
        return dataTransfer;
      };
      const doPaste = (editor, content, internal, pasteAsText, shouldSimulateInputEvent) => {
        const res = process(editor, content, internal);
        if (!res.cancelled) {
          const content2 = res.content;
          const doPasteAction = () => insertContent(editor, content2, pasteAsText);
          if (shouldSimulateInputEvent) {
            const args = fireBeforeInputEvent(editor, "insertFromPaste", { dataTransfer: createPasteDataTransfer(content2) });
            if (!args.isDefaultPrevented()) {
              doPasteAction();
              fireInputEvent(editor, "insertFromPaste");
            }
          } else {
            doPasteAction();
          }
        }
      };
      const pasteHtml = (editor, html2, internalFlag, shouldSimulateInputEvent) => {
        const internal = internalFlag ? internalFlag : isMarked(html2);
        doPaste(editor, unmark(html2), internal, false, shouldSimulateInputEvent);
      };
      const pasteText = (editor, text2, shouldSimulateInputEvent) => {
        const encodedText = editor.dom.encode(text2).replace(/\r\n/g, "\n");
        const normalizedText = normalize$4(encodedText, getPasteTabSpaces(editor));
        const html2 = toBlockElements(normalizedText, getForcedRootBlock(editor), getForcedRootBlockAttrs(editor));
        doPaste(editor, html2, false, true, shouldSimulateInputEvent);
      };
      const getDataTransferItems = (dataTransfer) => {
        const items = {};
        if (dataTransfer && dataTransfer.types) {
          for (let i = 0; i < dataTransfer.types.length; i++) {
            const contentType = dataTransfer.types[i];
            try {
              items[contentType] = dataTransfer.getData(contentType);
            } catch {
              items[contentType] = "";
            }
          }
        }
        return items;
      };
      const hasContentType = (clipboardContent, mimeType) => mimeType in clipboardContent && clipboardContent[mimeType].length > 0;
      const hasHtmlOrText = (content) => hasContentType(content, "text/html") || hasContentType(content, "text/plain");
      const extractFilename = (editor, str) => {
        const m = str.match(/([\s\S]+?)(?:\.[a-z0-9.]+)$/i);
        return isNonNullable(m) ? editor.dom.encode(m[1]) : void 0;
      };
      const createBlobInfo = (editor, blobCache, file, base64) => {
        const id = uniqueId();
        const useFileName = shouldReuseFileName(editor) && isNonNullable(file.name);
        const name2 = useFileName ? extractFilename(editor, file.name) : id;
        const filename = useFileName ? file.name : void 0;
        const blobInfo = blobCache.create(id, file, base64, name2, filename);
        blobCache.add(blobInfo);
        return blobInfo;
      };
      const pasteImage = (editor, imageItem) => {
        parseDataUri(imageItem.uri).each(({ data: data2, type: type2, base64Encoded }) => {
          const base64 = base64Encoded ? data2 : btoa(data2);
          const file = imageItem.file;
          const blobCache = editor.editorUpload.blobCache;
          const existingBlobInfo = blobCache.getByData(base64, type2);
          const blobInfo = existingBlobInfo ?? createBlobInfo(editor, blobCache, file, base64);
          pasteHtml(editor, `<img src="${blobInfo.blobUri()}">`, false, true);
        });
      };
      const isClipboardEvent = (event) => event.type === "paste";
      const readFilesAsDataUris = (items) => Promise.all(map$3(items, (file) => {
        return blobToDataUri(file).then((uri) => ({ file, uri }));
      }));
      const isImage = (editor) => {
        const allowedExtensions = getAllowedImageFileTypes(editor);
        return (file) => startsWith(file.type, "image/") && exists(allowedExtensions, (extension) => {
          return getImageMimeType(extension) === file.type;
        });
      };
      const getImagesFromDataTransfer = (editor, dataTransfer) => {
        const items = dataTransfer.items ? bind$3(from(dataTransfer.items), (item) => {
          return item.kind === "file" ? [item.getAsFile()] : [];
        }) : [];
        const files = dataTransfer.files ? from(dataTransfer.files) : [];
        return filter$5(items.length > 0 ? items : files, isImage(editor));
      };
      const pasteImageData = (editor, e, rng) => {
        const dataTransfer = isClipboardEvent(e) ? e.clipboardData : e.dataTransfer;
        if (shouldPasteDataImages(editor) && dataTransfer) {
          const images = getImagesFromDataTransfer(editor, dataTransfer);
          if (images.length > 0) {
            e.preventDefault();
            readFilesAsDataUris(images).then((fileResults) => {
              if (rng) {
                editor.selection.setRng(rng);
              }
              each$e(fileResults, (result) => {
                pasteImage(editor, result);
              });
            });
            return true;
          }
        }
        return false;
      };
      const isBrokenAndroidClipboardEvent = (e) => Env.os.isAndroid() && e.clipboardData?.items?.length === 0;
      const isKeyboardPasteEvent = (e) => VK.metaKeyPressed(e) && e.keyCode === 86 || e.shiftKey && e.keyCode === 45;
      const insertClipboardContent = (editor, clipboardContent, html2, plainTextMode, shouldSimulateInputEvent) => {
        let content = trimHtml(html2);
        const isInternal = hasContentType(clipboardContent, internalHtmlMime()) || isMarked(html2);
        const isPlainTextHtml = !isInternal && isPlainText(content);
        const isAbsoluteUrl$1 = isAbsoluteUrl(content);
        if (isDefaultPasteBinContent(content) || !content.length || isPlainTextHtml && !isAbsoluteUrl$1) {
          plainTextMode = true;
        }
        if (plainTextMode || isAbsoluteUrl$1) {
          if (hasContentType(clipboardContent, "text/plain") && isPlainTextHtml) {
            content = clipboardContent["text/plain"];
          } else {
            content = innerText(content);
          }
        }
        if (isDefaultPasteBinContent(content)) {
          return;
        }
        if (plainTextMode) {
          pasteText(editor, content, shouldSimulateInputEvent);
        } else {
          pasteHtml(editor, content, isInternal, shouldSimulateInputEvent);
        }
      };
      const registerEventHandlers = (editor, pasteBin, pasteFormat) => {
        let keyboardPastePlainTextState;
        const getLastRng = () => pasteBin.getLastRng() || editor.selection.getRng();
        editor.on("keydown", (e) => {
          if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
            keyboardPastePlainTextState = e.shiftKey && e.keyCode === 86;
          }
        });
        editor.on("paste", (e) => {
          if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) {
            return;
          }
          const plainTextMode = pasteFormat.get() === "text" || keyboardPastePlainTextState;
          keyboardPastePlainTextState = false;
          const clipboardContent = getDataTransferItems(e.clipboardData);
          if (!hasHtmlOrText(clipboardContent) && pasteImageData(editor, e, getLastRng())) {
            return;
          }
          if (hasContentType(clipboardContent, "text/html")) {
            e.preventDefault();
            insertClipboardContent(editor, clipboardContent, clipboardContent["text/html"], plainTextMode, true);
          } else if (hasContentType(clipboardContent, "text/plain") && hasContentType(clipboardContent, "text/uri-list")) {
            e.preventDefault();
            insertClipboardContent(editor, clipboardContent, clipboardContent["text/plain"], plainTextMode, true);
          } else {
            pasteBin.create();
            Delay.setEditorTimeout(editor, () => {
              const html2 = pasteBin.getHtml();
              pasteBin.remove();
              insertClipboardContent(editor, clipboardContent, html2, plainTextMode, false);
            }, 0);
          }
        });
      };
      const registerDataImageFilter = (editor) => {
        const isWebKitFakeUrl = (src) => startsWith(src, "webkit-fake-url");
        const isDataUri = (src) => startsWith(src, "data:");
        const isPasteInsert = (args) => args.data?.paste === true;
        editor.parser.addNodeFilter("img", (nodes, name2, args) => {
          if (!shouldPasteDataImages(editor) && isPasteInsert(args)) {
            for (const node of nodes) {
              const src = node.attr("src");
              if (isString(src) && !node.attr("data-mce-object") && src !== Env.transparentSrc) {
                if (isWebKitFakeUrl(src)) {
                  node.remove();
                } else if (!shouldAllowHtmlDataUrls(editor) && isDataUri(src)) {
                  node.remove();
                }
              }
            }
          }
        });
      };
      const registerEventsAndFilters = (editor, pasteBin, pasteFormat) => {
        registerEventHandlers(editor, pasteBin, pasteFormat);
        registerDataImageFilter(editor);
      };
      const togglePlainTextPaste = (editor, pasteFormat) => {
        if (pasteFormat.get() === "text") {
          pasteFormat.set("html");
          firePastePlainTextToggle(editor, false);
        } else {
          pasteFormat.set("text");
          firePastePlainTextToggle(editor, true);
        }
        editor.focus();
      };
      const register$1 = (editor, pasteFormat) => {
        editor.addCommand("mceTogglePlainTextPaste", () => {
          togglePlainTextPaste(editor, pasteFormat);
        });
        editor.addCommand("mceInsertClipboardContent", (ui, value2) => {
          if (value2.html) {
            pasteHtml(editor, value2.html, value2.internal, false);
          }
          if (value2.text) {
            pasteText(editor, value2.text, false);
          }
        });
      };
      const setHtml5Clipboard = (clipboardData, html2, text2) => {
        if (clipboardData) {
          try {
            clipboardData.clearData();
            clipboardData.setData("text/html", html2);
            clipboardData.setData("text/plain", text2);
            clipboardData.setData(internalHtmlMime(), html2);
            return true;
          } catch {
            return false;
          }
        } else {
          return false;
        }
      };
      const setClipboardData = (evt, data2, fallback2, done) => {
        if (setHtml5Clipboard(evt.clipboardData, data2.html, data2.text)) {
          evt.preventDefault();
          done();
        } else {
          fallback2(data2.html, done);
        }
      };
      const fallback = (editor) => (html2, done) => {
        const { dom: dom2, selection } = editor;
        const outer = dom2.create("div", { "contenteditable": "false", "data-mce-bogus": "all" });
        const inner = dom2.create("div", { contenteditable: "true" }, html2);
        dom2.setStyles(outer, {
          position: "fixed",
          top: "0",
          left: "-3000px",
          width: "1000px",
          overflow: "hidden"
        });
        outer.appendChild(inner);
        dom2.add(editor.getBody(), outer);
        const range2 = selection.getRng();
        inner.focus();
        const offscreenRange = dom2.createRng();
        offscreenRange.selectNodeContents(inner);
        selection.setRng(offscreenRange);
        Delay.setEditorTimeout(editor, () => {
          selection.setRng(range2);
          dom2.remove(outer);
          done();
        }, 0);
      };
      const getData = (editor) => ({
        html: mark(editor.selection.getContent({ contextual: true })),
        text: editor.selection.getContent({ format: "text" })
      });
      const isTableSelection = (editor) => !!editor.dom.getParent(editor.selection.getStart(), "td[data-mce-selected],th[data-mce-selected]", editor.getBody());
      const hasSelectedContent = (editor) => !editor.selection.isCollapsed() || isTableSelection(editor);
      const cut = (editor, caret) => (evt) => {
        if (!evt.isDefaultPrevented() && hasSelectedContent(editor) && editor.selection.isEditable()) {
          setClipboardData(evt, getData(editor), fallback(editor), () => {
            if (Env.browser.isChromium() || Env.browser.isFirefox()) {
              const rng = editor.selection.getRng();
              Delay.setEditorTimeout(editor, () => {
                editor.selection.setRng(rng);
                deleteCommand(editor, caret);
              }, 0);
            } else {
              deleteCommand(editor, caret);
            }
          });
        }
      };
      const copy = (editor) => (evt) => {
        if (!evt.isDefaultPrevented() && hasSelectedContent(editor)) {
          setClipboardData(evt, getData(editor), fallback(editor), noop);
        }
      };
      const register = (editor, caret) => {
        editor.on("cut", cut(editor, caret));
        editor.on("copy", copy(editor));
      };
      const getCaretRangeFromEvent = (editor, e) => (
        // TODO: TINY-7075 Remove the "?? 0" here when agar passes valid client coords
        RangeUtils.getCaretRangeFromPoint(e.clientX ?? 0, e.clientY ?? 0, editor.getDoc())
      );
      const isPlainTextFileUrl = (content) => {
        const plainTextContent = content["text/plain"];
        return plainTextContent ? plainTextContent.indexOf("file://") === 0 : false;
      };
      const setFocusedRange = (editor, rng) => {
        if (rng) {
          editor.selection.setRng(rng);
        }
        editor.focus();
      };
      const hasImage = (dataTransfer) => exists(dataTransfer.files, (file) => /^image\//.test(file.type));
      const needsCustomInternalDrop = (dom2, schema, target, dropContent) => {
        const parentTransparent = dom2.getParent(target, (node) => isTransparentBlock(schema, node));
        const inSummary = !isNull(dom2.getParent(target, "summary"));
        if (inSummary) {
          return true;
        } else if (parentTransparent && has$2(dropContent, "text/html")) {
          const fragment = new DOMParser().parseFromString(dropContent["text/html"], "text/html").body;
          return !isNull(fragment.querySelector(parentTransparent.nodeName.toLowerCase()));
        } else {
          return false;
        }
      };
      const setupSummaryDeleteByDragFix = (editor) => {
        editor.on("input", (e) => {
          const hasNoSummary = (el) => isNull(el.querySelector("summary"));
          if (e.inputType === "deleteByDrag") {
            const brokenDetailElements = filter$5(editor.dom.select("details"), hasNoSummary);
            each$e(brokenDetailElements, (details) => {
              if (isBr$7(details.firstChild)) {
                details.firstChild.remove();
              }
              const summary = editor.dom.create("summary");
              summary.appendChild(createPaddingBr().dom);
              details.prepend(summary);
            });
          }
        });
      };
      const setup$a = (editor, draggingInternallyState) => {
        if (shouldPasteBlockDrop(editor)) {
          editor.on("dragend dragover draggesture dragdrop drop drag", (e) => {
            e.preventDefault();
            e.stopPropagation();
          });
        }
        if (!shouldPasteDataImages(editor)) {
          editor.on("drop", (e) => {
            const dataTransfer = e.dataTransfer;
            if (dataTransfer && hasImage(dataTransfer)) {
              e.preventDefault();
            }
          });
        }
        editor.on("drop", (e) => {
          if (e.isDefaultPrevented()) {
            return;
          }
          const rng = getCaretRangeFromEvent(editor, e);
          if (isNullable(rng)) {
            return;
          }
          const dropContent = getDataTransferItems(e.dataTransfer);
          const internal = hasContentType(dropContent, internalHtmlMime());
          if ((!hasHtmlOrText(dropContent) || isPlainTextFileUrl(dropContent)) && pasteImageData(editor, e, rng)) {
            return;
          }
          const internalContent = dropContent[internalHtmlMime()];
          const content = internalContent || dropContent["text/html"] || dropContent["text/plain"];
          const needsInternalDrop = needsCustomInternalDrop(editor.dom, editor.schema, rng.startContainer, dropContent);
          const isInternalDrop = draggingInternallyState.get();
          if (isInternalDrop && !needsInternalDrop) {
            return;
          }
          if (content) {
            e.preventDefault();
            Delay.setEditorTimeout(editor, () => {
              editor.undoManager.transact(() => {
                if (internalContent || isInternalDrop && needsInternalDrop) {
                  editor.execCommand("Delete");
                }
                setFocusedRange(editor, rng);
                const trimmedContent = trimHtml(content);
                if (dropContent["text/html"]) {
                  pasteHtml(editor, trimmedContent, internal, true);
                } else {
                  pasteText(editor, trimmedContent, true);
                }
              });
            });
          }
        });
        editor.on("dragstart", (_e) => {
          draggingInternallyState.set(true);
        });
        editor.on("dragover dragend", (e) => {
          if (shouldPasteDataImages(editor) && !draggingInternallyState.get()) {
            e.preventDefault();
            setFocusedRange(editor, getCaretRangeFromEvent(editor, e));
          }
          if (e.type === "dragend") {
            draggingInternallyState.set(false);
          }
        });
        setupSummaryDeleteByDragFix(editor);
      };
      const setup$9 = (editor) => {
        const processEvent = (f) => (e) => {
          f(editor, e);
        };
        const preProcess2 = getPastePreProcess(editor);
        if (isFunction(preProcess2)) {
          editor.on("PastePreProcess", processEvent(preProcess2));
        }
        const postProcess2 = getPastePostProcess(editor);
        if (isFunction(postProcess2)) {
          editor.on("PastePostProcess", processEvent(postProcess2));
        }
      };
      const addPreProcessFilter = (editor, filterFunc) => {
        editor.on("PastePreProcess", (e) => {
          e.content = filterFunc(editor, e.content, e.internal);
        });
      };
      const rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi;
      const rgbToHex = (value2) => Tools.trim(value2).replace(rgbRegExp, rgbaToHexString).toLowerCase();
      const removeWebKitStyles = (editor, content, internal) => {
        const webKitStylesOption = getPasteWebkitStyles(editor);
        if (internal || webKitStylesOption === "all" || !shouldPasteRemoveWebKitStyles(editor)) {
          return content;
        }
        const webKitStyles = webKitStylesOption ? webKitStylesOption.split(/[, ]/) : [];
        if (webKitStyles && webKitStylesOption !== "none") {
          const dom2 = editor.dom, node = editor.selection.getNode();
          content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, (all2, before2, value2, after2) => {
            const inputStyles = dom2.parseStyle(dom2.decode(value2));
            const outputStyles = {};
            for (let i = 0; i < webKitStyles.length; i++) {
              const inputValue = inputStyles[webKitStyles[i]];
              let compareInput = inputValue;
              let currentValue = dom2.getStyle(node, webKitStyles[i], true);
              if (/color/.test(webKitStyles[i])) {
                compareInput = rgbToHex(compareInput);
                currentValue = rgbToHex(currentValue);
              }
              if (currentValue !== compareInput) {
                outputStyles[webKitStyles[i]] = inputValue;
              }
            }
            const outputStyle = dom2.serializeStyle(outputStyles, "span");
            if (outputStyle) {
              return before2 + ' style="' + outputStyle + '"' + after2;
            }
            return before2 + after2;
          });
        } else {
          content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, "$1$3");
        }
        content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, (all2, before2, value2, after2) => {
          return before2 + ' style="' + value2 + '"' + after2;
        });
        return content;
      };
      const setup$8 = (editor) => {
        if (Env.browser.isChromium() || Env.browser.isSafari()) {
          addPreProcessFilter(editor, removeWebKitStyles);
        }
      };
      const setup$7 = (editor, caret) => {
        const draggingInternallyState = Cell(false);
        const pasteFormat = Cell(isPasteAsTextEnabled(editor) ? "text" : "html");
        const pasteBin = PasteBin(editor);
        setup$8(editor);
        register$1(editor, pasteFormat);
        setup$9(editor);
        editor.addQueryStateHandler("mceTogglePlainTextPaste", () => pasteFormat.get() === "text");
        editor.on("PreInit", () => {
          register(editor, caret);
          setup$a(editor, draggingInternallyState);
          registerEventsAndFilters(editor, pasteBin, pasteFormat);
        });
      };
      const preventSummaryToggle = (editor) => {
        editor.on("click", (e) => {
          if (editor.dom.getParent(e.target, "details")) {
            e.preventDefault();
          }
        });
      };
      const filterDetails = (editor) => {
        editor.parser.addNodeFilter("details", (elms) => {
          const initialStateOption = getDetailsInitialState(editor);
          each$e(elms, (details) => {
            if (initialStateOption === "expanded") {
              details.attr("open", "open");
            } else if (initialStateOption === "collapsed") {
              details.attr("open", null);
            }
          });
        });
        editor.serializer.addNodeFilter("details", (elms) => {
          const serializedStateOption = getDetailsSerializedState(editor);
          each$e(elms, (details) => {
            if (serializedStateOption === "expanded") {
              details.attr("open", "open");
            } else if (serializedStateOption === "collapsed") {
              details.attr("open", null);
            }
          });
        });
      };
      const setup$6 = (editor) => {
        preventSummaryToggle(editor);
        filterDetails(editor);
      };
      const isBr = isBr$7;
      const isText = isText$b;
      const isContentEditableFalse$2 = (elm) => isContentEditableFalse$a(elm.dom);
      const isContentEditableTrue = (elm) => isContentEditableTrue$3(elm.dom);
      const isRoot = (rootNode) => (elm) => eq(SugarElement.fromDom(rootNode), elm);
      const getClosestScope = (node, rootNode, schema) => closest$4(SugarElement.fromDom(node), (elm) => isContentEditableTrue(elm) || schema.isBlock(name(elm)), isRoot(rootNode)).getOr(SugarElement.fromDom(rootNode)).dom;
      const getClosestCef = (node, rootNode) => closest$4(SugarElement.fromDom(node), isContentEditableFalse$2, isRoot(rootNode));
      const findEdgeCaretCandidate = (startNode, scope, forward) => {
        const walker = new DomTreeWalker(startNode, scope);
        const next2 = forward ? walker.next.bind(walker) : walker.prev.bind(walker);
        let result = startNode;
        for (let current = forward ? startNode : next2(); current && !isBr(current); current = next2()) {
          if (isCaretCandidate$3(current)) {
            result = current;
          }
        }
        return result;
      };
      const findClosestBlockRange = (startRng, rootNode, schema) => {
        const startPos = CaretPosition.fromRangeStart(startRng);
        const clickNode = startPos.getNode();
        const scope = getClosestScope(clickNode, rootNode, schema);
        const startNode = findEdgeCaretCandidate(clickNode, scope, false);
        const endNode = findEdgeCaretCandidate(clickNode, scope, true);
        const rng = document.createRange();
        getClosestCef(startNode, scope).fold(() => {
          if (isText(startNode)) {
            rng.setStart(startNode, 0);
          } else {
            rng.setStartBefore(startNode);
          }
        }, (cef) => rng.setStartBefore(cef.dom));
        getClosestCef(endNode, scope).fold(() => {
          if (isText(endNode)) {
            rng.setEnd(endNode, endNode.data.length);
          } else {
            rng.setEndAfter(endNode);
          }
        }, (cef) => rng.setEndAfter(cef.dom));
        return rng;
      };
      const onTripleClickSelect = (editor) => {
        const rng = findClosestBlockRange(editor.selection.getRng(), editor.getBody(), editor.schema);
        editor.selection.setRng(normalize(rng));
      };
      const setup$5 = (editor) => {
        editor.on("mousedown", (e) => {
          if (e.detail >= 3) {
            e.preventDefault();
            onTripleClickSelect(editor);
          }
        });
      };
      var FakeCaretPosition;
      (function(FakeCaretPosition2) {
        FakeCaretPosition2["Before"] = "before";
        FakeCaretPosition2["After"] = "after";
      })(FakeCaretPosition || (FakeCaretPosition = {}));
      const distanceToRectLeft = (clientRect, clientX) => Math.abs(clientRect.left - clientX);
      const distanceToRectRight = (clientRect, clientX) => Math.abs(clientRect.right - clientX);
      const isInsideY = (clientY, clientRect) => clientY >= clientRect.top && clientY <= clientRect.bottom;
      const collidesY = (r1, r2) => r1.top < r2.bottom && r1.bottom > r2.top;
      const isOverlapping = (r1, r2) => {
        const overlap = overlapY(r1, r2) / Math.min(r1.height, r2.height);
        return collidesY(r1, r2) && overlap > 0.5;
      };
      const splitRectsPerAxis = (rects, y) => {
        const intersectingRects = filter$5(rects, (rect) => isInsideY(y, rect));
        return boundingClientRectFromRects(intersectingRects).fold(() => [[], rects], (boundingRect) => {
          const { pass: horizontal, fail: vertical } = partition$2(rects, (rect) => isOverlapping(rect, boundingRect));
          return [horizontal, vertical];
        });
      };
      const clientInfo = (rect, clientX) => {
        return {
          node: rect.node,
          position: distanceToRectLeft(rect, clientX) < distanceToRectRight(rect, clientX) ? FakeCaretPosition.Before : FakeCaretPosition.After
        };
      };
      const horizontalDistance = (rect, x, _y) => x > rect.left && x < rect.right ? 0 : Math.min(Math.abs(rect.left - x), Math.abs(rect.right - x));
      const closestChildCaretCandidateNodeRect = (children2, clientX, clientY, findCloserTextNode) => {
        const caretCandidateRect = (rect) => {
          if (isCaretCandidate$3(rect.node)) {
            return Optional.some(rect);
          } else if (isElement$7(rect.node)) {
            return closestChildCaretCandidateNodeRect(from(rect.node.childNodes), clientX, clientY, false);
          } else {
            return Optional.none();
          }
        };
        const tryFindSecondBestTextNode = (closest2, sndClosest, distance) => {
          return caretCandidateRect(sndClosest).filter((rect) => {
            const deltaDistance = Math.abs(distance(closest2, clientX, clientY) - distance(rect, clientX, clientY));
            return deltaDistance < 2 && isText$b(rect.node);
          });
        };
        const findClosestCaretCandidateNodeRect = (rects, distance) => {
          const sortedRects = sort(rects, (r1, r2) => distance(r1, clientX, clientY) - distance(r2, clientX, clientY));
          return findMap(sortedRects, caretCandidateRect).map((closest2) => {
            if (findCloserTextNode && !isText$b(closest2.node) && sortedRects.length > 1) {
              return tryFindSecondBestTextNode(closest2, sortedRects[1], distance).getOr(closest2);
            } else {
              return closest2;
            }
          });
        };
        const [horizontalRects, verticalRects] = splitRectsPerAxis(getClientRects(children2), clientY);
        const { pass: above, fail: below } = partition$2(verticalRects, (rect) => rect.top < clientY);
        return findClosestCaretCandidateNodeRect(horizontalRects, horizontalDistance).orThunk(() => findClosestCaretCandidateNodeRect(below, distanceToRectEdgeFromXY)).orThunk(() => findClosestCaretCandidateNodeRect(above, distanceToRectEdgeFromXY));
      };
      const traverseUp = (rootElm, scope, clientX, clientY) => {
        const helper = (scope2, prevScope) => {
          const isDragGhostContainer = (node) => isElement$7(node) && node.classList.contains("mce-drag-container");
          const childNodesWithoutGhost = filter$5(scope2.dom.childNodes, not(isDragGhostContainer));
          return prevScope.fold(() => closestChildCaretCandidateNodeRect(childNodesWithoutGhost, clientX, clientY, true), (prevScope2) => {
            const uncheckedChildren = filter$5(childNodesWithoutGhost, (node) => node !== prevScope2.dom);
            return closestChildCaretCandidateNodeRect(uncheckedChildren, clientX, clientY, true);
          }).orThunk(() => {
            const parent2 = eq(scope2, rootElm) ? Optional.none() : parentElement(scope2);
            return parent2.bind((newScope) => helper(newScope, Optional.some(scope2)));
          });
        };
        return helper(scope, Optional.none());
      };
      const closestCaretCandidateNodeRect = (root, clientX, clientY) => {
        const rootElm = SugarElement.fromDom(root);
        const ownerDoc = documentOrOwner(rootElm);
        const elementAtPoint = SugarElement.fromPoint(ownerDoc, clientX, clientY).filter((elm) => contains(rootElm, elm));
        const element = elementAtPoint.getOr(rootElm);
        return traverseUp(rootElm, element, clientX, clientY);
      };
      const closestFakeCaretCandidate = (root, clientX, clientY) => closestCaretCandidateNodeRect(root, clientX, clientY).filter((rect) => isFakeCaretTarget(rect.node)).map((rect) => clientInfo(rect, clientX));
      const getAbsolutePosition = (elm) => {
        const clientRect = elm.getBoundingClientRect();
        const doc = elm.ownerDocument;
        const docElem = doc.documentElement;
        const win = doc.defaultView;
        return {
          top: clientRect.top + (win?.scrollY ?? 0) - docElem.clientTop,
          left: clientRect.left + (win?.scrollX ?? 0) - docElem.clientLeft
        };
      };
      const getBodyPosition = (editor) => editor.inline ? getAbsolutePosition(editor.getBody()) : { left: 0, top: 0 };
      const getScrollPosition = (editor) => {
        const body = editor.getBody();
        return editor.inline ? { left: body.scrollLeft, top: body.scrollTop } : { left: 0, top: 0 };
      };
      const getBodyScroll = (editor) => {
        const body = editor.getBody(), docElm = editor.getDoc().documentElement;
        const inlineScroll = { left: body.scrollLeft, top: body.scrollTop };
        const iframeScroll = { left: body.scrollLeft || docElm.scrollLeft, top: body.scrollTop || docElm.scrollTop };
        return editor.inline ? inlineScroll : iframeScroll;
      };
      const getMousePosition = (editor, event) => {
        if (event.target.ownerDocument !== editor.getDoc()) {
          const iframePosition = getAbsolutePosition(editor.getContentAreaContainer());
          const scrollPosition = getBodyScroll(editor);
          return {
            left: event.pageX - iframePosition.left + scrollPosition.left,
            top: event.pageY - iframePosition.top + scrollPosition.top
          };
        }
        return {
          left: event.pageX,
          top: event.pageY
        };
      };
      const calculatePosition = (bodyPosition, scrollPosition, mousePosition) => ({
        pageX: mousePosition.left - bodyPosition.left + scrollPosition.left,
        pageY: mousePosition.top - bodyPosition.top + scrollPosition.top
      });
      const calc = (editor, event) => calculatePosition(getBodyPosition(editor), getScrollPosition(editor), getMousePosition(editor, event));
      const getTargetProps = (target) => ({ target, srcElement: target });
      const makeDndEventFromMouseEvent = (type2, mouseEvent, target, dataTransfer) => ({
        ...mouseEvent,
        dataTransfer,
        type: type2,
        ...getTargetProps(target)
      });
      const makeDndEvent = (type2, target, dataTransfer) => {
        const fail = die("Function not supported on simulated event.");
        const event = {
          // Event
          bubbles: true,
          cancelBubble: false,
          cancelable: true,
          composed: false,
          currentTarget: null,
          defaultPrevented: false,
          eventPhase: 0,
          isTrusted: true,
          returnValue: false,
          timeStamp: 0,
          type: type2,
          composedPath: fail,
          initEvent: fail,
          preventDefault: noop,
          stopImmediatePropagation: noop,
          stopPropagation: noop,
          AT_TARGET: window.Event.AT_TARGET,
          BUBBLING_PHASE: window.Event.BUBBLING_PHASE,
          CAPTURING_PHASE: window.Event.CAPTURING_PHASE,
          NONE: window.Event.NONE,
          // UIEvent
          altKey: false,
          button: 0,
          buttons: 0,
          clientX: 0,
          clientY: 0,
          ctrlKey: false,
          layerX: 0,
          layerY: 0,
          metaKey: false,
          movementX: 0,
          movementY: 0,
          offsetX: 0,
          offsetY: 0,
          pageX: 0,
          pageY: 0,
          relatedTarget: null,
          screenX: 0,
          screenY: 0,
          shiftKey: false,
          x: 0,
          y: 0,
          detail: 0,
          view: null,
          which: 0,
          initUIEvent: fail,
          initMouseEvent: fail,
          getModifierState: fail,
          // DragEvent
          dataTransfer,
          ...getTargetProps(target)
        };
        return event;
      };
      const makeDataTransferCopyForDragEvent = (dataTransfer, eventType) => {
        const copy2 = cloneDataTransfer(dataTransfer);
        if (eventType === "dragstart") {
          setDragstartEvent(copy2);
          setReadWriteMode(copy2);
        } else if (eventType === "drop") {
          setDropEvent(copy2);
          setReadOnlyMode(copy2);
        } else {
          setDragendEvent(copy2);
          setProtectedMode(copy2);
        }
        return copy2;
      };
      const makeDragEvent = (type2, target, dataTransfer, mouseEvent) => {
        const dataTransferForDispatch = makeDataTransferCopyForDragEvent(dataTransfer, type2);
        return isUndefined(mouseEvent) ? makeDndEvent(type2, target, dataTransferForDispatch) : makeDndEventFromMouseEvent(type2, mouseEvent, target, dataTransferForDispatch);
      };
      const scrollPixelsPerInterval = 32;
      const scrollIntervalValue = 100;
      const mouseRangeToTriggerScrollInsideEditor = 8;
      const mouseRangeToTriggerScrollOutsideEditor = 16;
      const isContentEditableFalse$1 = isContentEditableFalse$a;
      const isContentEditable = or(isContentEditableFalse$1, isContentEditableTrue$3);
      const isDraggable = (dom2, rootElm, elm) => isContentEditableFalse$1(elm) && elm !== rootElm && dom2.isEditable(elm.parentElement);
      const isValidDropTarget = (editor, targetElement, dragElement) => {
        if (isNullable(targetElement)) {
          return false;
        } else if (targetElement === dragElement || editor.dom.isChildOf(targetElement, dragElement)) {
          return false;
        } else {
          return editor.dom.isEditable(targetElement);
        }
      };
      const createGhost = (editor, elm, width, height) => {
        const dom2 = editor.dom;
        const clonedElm = elm.cloneNode(true);
        dom2.setStyles(clonedElm, { width, height });
        dom2.setAttrib(clonedElm, "data-mce-selected", null);
        const ghostElm = dom2.create("div", {
          "class": "mce-drag-container",
          "data-mce-bogus": "all",
          "unselectable": "on",
          "contenteditable": "false"
        });
        dom2.setStyles(ghostElm, {
          position: "absolute",
          opacity: 0.5,
          overflow: "hidden",
          border: 0,
          padding: 0,
          margin: 0,
          width,
          height
        });
        dom2.setStyles(clonedElm, {
          margin: 0,
          boxSizing: "border-box"
        });
        ghostElm.appendChild(clonedElm);
        return ghostElm;
      };
      const appendGhostToBody = (ghostElm, bodyElm) => {
        if (ghostElm.parentNode !== bodyElm) {
          bodyElm.appendChild(ghostElm);
        }
      };
      const scrollEditor = (direction, amount) => (win) => () => {
        const current = direction === "left" ? win.scrollX : win.scrollY;
        win.scroll({
          [direction]: current + amount,
          behavior: "smooth"
        });
      };
      const scrollLeft = scrollEditor("left", -scrollPixelsPerInterval);
      const scrollRight = scrollEditor("left", scrollPixelsPerInterval);
      const scrollUp = scrollEditor("top", -scrollPixelsPerInterval);
      const scrollDown = scrollEditor("top", scrollPixelsPerInterval);
      const moveGhost = (ghostElm, position, width, height, maxX, maxY, mouseY, mouseX, contentAreaContainer, win, state, mouseEventOriginatedFromWithinTheEditor) => {
        let overflowX = 0, overflowY = 0;
        ghostElm.style.left = position.pageX + "px";
        ghostElm.style.top = position.pageY + "px";
        if (position.pageX + width > maxX) {
          overflowX = position.pageX + width - maxX;
        }
        if (position.pageY + height > maxY) {
          overflowY = position.pageY + height - maxY;
        }
        ghostElm.style.width = width - overflowX + "px";
        ghostElm.style.height = height - overflowY + "px";
        const clientHeight = contentAreaContainer.clientHeight;
        const clientWidth = contentAreaContainer.clientWidth;
        const outerMouseY = mouseY + contentAreaContainer.getBoundingClientRect().top;
        const outerMouseX = mouseX + contentAreaContainer.getBoundingClientRect().left;
        state.on((state2) => {
          state2.intervalId.clear();
          if (state2.dragging && mouseEventOriginatedFromWithinTheEditor) {
            if (mouseY + mouseRangeToTriggerScrollInsideEditor >= clientHeight) {
              state2.intervalId.set(scrollDown(win));
            } else if (mouseY - mouseRangeToTriggerScrollInsideEditor <= 0) {
              state2.intervalId.set(scrollUp(win));
            } else if (mouseX + mouseRangeToTriggerScrollInsideEditor >= clientWidth) {
              state2.intervalId.set(scrollRight(win));
            } else if (mouseX - mouseRangeToTriggerScrollInsideEditor <= 0) {
              state2.intervalId.set(scrollLeft(win));
            } else if (outerMouseY + mouseRangeToTriggerScrollOutsideEditor >= window.innerHeight) {
              state2.intervalId.set(scrollDown(window));
            } else if (outerMouseY - mouseRangeToTriggerScrollOutsideEditor <= 0) {
              state2.intervalId.set(scrollUp(window));
            } else if (outerMouseX + mouseRangeToTriggerScrollOutsideEditor >= window.innerWidth) {
              state2.intervalId.set(scrollRight(window));
            } else if (outerMouseX - mouseRangeToTriggerScrollOutsideEditor <= 0) {
              state2.intervalId.set(scrollLeft(window));
            }
          }
        });
      };
      const removeElement = (elm) => {
        if (elm && elm.parentNode) {
          elm.parentNode.removeChild(elm);
        }
      };
      const removeElementWithPadding = (dom2, elm) => {
        const parentBlock = dom2.getParent(elm.parentNode, dom2.isBlock);
        removeElement(elm);
        if (parentBlock && parentBlock !== dom2.getRoot() && dom2.isEmpty(parentBlock)) {
          fillWithPaddingBr(SugarElement.fromDom(parentBlock));
        }
      };
      const isLeftMouseButtonPressed = (e) => e.button === 0;
      const applyRelPos = (state, position) => ({
        pageX: position.pageX - state.relX,
        pageY: position.pageY + 5
      });
      const start = (state, editor) => (e) => {
        if (isLeftMouseButtonPressed(e)) {
          const ceElm = find$2(editor.dom.getParents(e.target), isContentEditable).getOr(null);
          if (isNonNullable(ceElm) && isDraggable(editor.dom, editor.getBody(), ceElm)) {
            const elmPos = editor.dom.getPos(ceElm);
            const bodyElm = editor.getBody();
            const docElm = editor.getDoc().documentElement;
            state.set({
              element: ceElm,
              dataTransfer: createDataTransfer(),
              dragging: false,
              screenX: e.screenX,
              screenY: e.screenY,
              maxX: (editor.inline ? bodyElm.scrollWidth : docElm.offsetWidth) - 2,
              maxY: (editor.inline ? bodyElm.scrollHeight : docElm.offsetHeight) - 2,
              relX: e.pageX - elmPos.x,
              relY: e.pageY - elmPos.y,
              width: ceElm.offsetWidth,
              height: ceElm.offsetHeight,
              ghost: createGhost(editor, ceElm, ceElm.offsetWidth, ceElm.offsetHeight),
              intervalId: repeatable(scrollIntervalValue)
            });
          }
        }
      };
      const placeCaretAt = (editor, clientX, clientY) => {
        editor._selectionOverrides.hideFakeCaret();
        closestFakeCaretCandidate(editor.getBody(), clientX, clientY).fold(() => editor.selection.placeCaretAt(clientX, clientY), (caretInfo) => {
          const range2 = editor._selectionOverrides.showCaret(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false);
          if (range2) {
            editor.selection.setRng(range2);
          } else {
            editor.selection.placeCaretAt(clientX, clientY);
          }
        });
      };
      const dispatchDragEvent = (editor, type2, target, dataTransfer, mouseEvent) => {
        if (type2 === "dragstart") {
          setHtmlData(dataTransfer, editor.dom.getOuterHTML(target));
        }
        const event = makeDragEvent(type2, target, dataTransfer, mouseEvent);
        const args = editor.dispatch(type2, event);
        return args;
      };
      const move = (state, editor) => {
        const throttledPlaceCaretAt = first$1((clientX, clientY) => placeCaretAt(editor, clientX, clientY), 0);
        editor.on("remove", throttledPlaceCaretAt.cancel);
        const state_ = state;
        return (e) => state.on((state2) => {
          const movement = Math.max(Math.abs(e.screenX - state2.screenX), Math.abs(e.screenY - state2.screenY));
          if (!state2.dragging && movement > 10) {
            const args = dispatchDragEvent(editor, "dragstart", state2.element, state2.dataTransfer, e);
            if (isNonNullable(args.dataTransfer)) {
              state2.dataTransfer = args.dataTransfer;
            }
            if (args.isDefaultPrevented()) {
              return;
            }
            state2.dragging = true;
            editor.focus();
          }
          if (state2.dragging) {
            const mouseEventOriginatedFromWithinTheEditor = e.currentTarget === editor.getDoc().documentElement;
            const targetPos = applyRelPos(state2, calc(editor, e));
            appendGhostToBody(state2.ghost, editor.getBody());
            moveGhost(state2.ghost, targetPos, state2.width, state2.height, state2.maxX, state2.maxY, e.clientY, e.clientX, editor.getContentAreaContainer(), editor.getWin(), state_, mouseEventOriginatedFromWithinTheEditor);
            throttledPlaceCaretAt.throttle(e.clientX, e.clientY);
          }
        });
      };
      const getRawTarget = (selection) => {
        const sel = selection.getSel();
        if (isNonNullable(sel)) {
          const rng = sel.getRangeAt(0);
          const startContainer = rng.startContainer;
          return isText$b(startContainer) ? startContainer.parentNode : startContainer;
        } else {
          return null;
        }
      };
      const drop = (state, editor) => (e) => {
        state.on((state2) => {
          state2.intervalId.clear();
          if (state2.dragging) {
            if (isValidDropTarget(editor, getRawTarget(editor.selection), state2.element)) {
              const dropTarget = editor.getDoc().elementFromPoint(e.clientX, e.clientY) ?? editor.getBody();
              const args = dispatchDragEvent(editor, "drop", dropTarget, state2.dataTransfer, e);
              if (!args.isDefaultPrevented()) {
                editor.undoManager.transact(() => {
                  removeElementWithPadding(editor.dom, state2.element);
                  getHtmlData(state2.dataTransfer).each((content) => editor.insertContent(content));
                  editor._selectionOverrides.hideFakeCaret();
                });
              }
            }
            dispatchDragEvent(editor, "dragend", editor.getBody(), state2.dataTransfer, e);
          }
        });
        removeDragState(state);
      };
      const stopDragging = (state, editor, e) => {
        state.on((state2) => {
          state2.intervalId.clear();
          if (state2.dragging) {
            e.fold(() => dispatchDragEvent(editor, "dragend", state2.element, state2.dataTransfer), (mouseEvent) => dispatchDragEvent(editor, "dragend", state2.element, state2.dataTransfer, mouseEvent));
          }
        });
        removeDragState(state);
      };
      const stop = (state, editor) => (e) => stopDragging(state, editor, Optional.some(e));
      const removeDragState = (state) => {
        state.on((state2) => {
          state2.intervalId.clear();
          removeElement(state2.ghost);
        });
        state.clear();
      };
      const bindFakeDragEvents = (editor) => {
        const state = value$1();
        const pageDom = DOMUtils.DOM;
        const rootDocument = document;
        const dragStartHandler = start(state, editor);
        const dragHandler = move(state, editor);
        const dropHandler = drop(state, editor);
        const dragEndHandler = stop(state, editor);
        editor.on("mousedown", dragStartHandler);
        editor.on("mousemove", dragHandler);
        editor.on("mouseup", dropHandler);
        pageDom.bind(rootDocument, "mousemove", dragHandler);
        pageDom.bind(rootDocument, "mouseup", dragEndHandler);
        editor.on("remove", () => {
          pageDom.unbind(rootDocument, "mousemove", dragHandler);
          pageDom.unbind(rootDocument, "mouseup", dragEndHandler);
        });
        editor.on("keydown", (e) => {
          if (e.keyCode === VK.ESC) {
            stopDragging(state, editor, Optional.none());
          }
        });
      };
      const blockUnsupportedFileDrop = (editor) => {
        const preventFileDrop = (e) => {
          if (!e.isDefaultPrevented()) {
            const dataTransfer = e.dataTransfer;
            if (dataTransfer && (contains$2(dataTransfer.types, "Files") || dataTransfer.files.length > 0)) {
              e.preventDefault();
              if (e.type === "drop") {
                displayError(editor, "Dropped file type is not supported");
              }
            }
          }
        };
        const preventFileDropIfUIElement = (e) => {
          if (isUIElement(editor, e.target)) {
            preventFileDrop(e);
          }
        };
        const setup2 = () => {
          const pageDom = DOMUtils.DOM;
          const dom2 = editor.dom;
          const doc = document;
          const editorRoot = editor.inline ? editor.getBody() : editor.getDoc();
          const eventNames = ["drop", "dragover"];
          each$e(eventNames, (name2) => {
            pageDom.bind(doc, name2, preventFileDropIfUIElement);
            dom2.bind(editorRoot, name2, preventFileDrop);
          });
          editor.on("remove", () => {
            each$e(eventNames, (name2) => {
              pageDom.unbind(doc, name2, preventFileDropIfUIElement);
              dom2.unbind(editorRoot, name2, preventFileDrop);
            });
          });
        };
        editor.on("init", () => {
          Delay.setEditorTimeout(editor, setup2, 0);
        });
      };
      const init$2 = (editor) => {
        bindFakeDragEvents(editor);
        if (shouldBlockUnsupportedDrop(editor)) {
          blockUnsupportedFileDrop(editor);
        }
      };
      const setup$4 = (editor) => {
        const renderFocusCaret = first$1(() => {
          if (!editor.removed && editor.getBody().contains(document.activeElement)) {
            const rng = editor.selection.getRng();
            if (rng.collapsed) {
              const caretRange = renderRangeCaret(editor, rng, false);
              editor.selection.setRng(caretRange);
            }
          }
        }, 0);
        editor.on("focus", () => {
          renderFocusCaret.throttle();
        });
        editor.on("blur", () => {
          renderFocusCaret.cancel();
        });
      };
      const setup$3 = (editor) => {
        editor.on("init", () => {
          editor.on("focusin", (e) => {
            const target = e.target;
            if (isMedia$2(target)) {
              const ceRoot = getContentEditableRoot$1(editor.getBody(), target);
              const node = isContentEditableFalse$a(ceRoot) ? ceRoot : target;
              if (editor.selection.getNode() !== node) {
                selectNode(editor, node).each((rng) => editor.selection.setRng(rng));
              }
            }
          });
        });
      };
      const isContentEditableFalse = isContentEditableFalse$a;
      const getContentEditableRoot = (editor, node) => getContentEditableRoot$1(editor.getBody(), node);
      const SelectionOverrides = (editor) => {
        const selection = editor.selection, dom2 = editor.dom;
        const rootNode = editor.getBody();
        const fakeCaret = FakeCaret(editor, rootNode, dom2.isBlock, () => hasFocus(editor));
        const realSelectionId = "sel-" + dom2.uniqueId();
        const elementSelectionAttr2 = "data-mce-selected";
        let selectedElement;
        const isFakeSelectionElement = (node) => isNonNullable(node) && dom2.hasClass(node, "mce-offscreen-selection");
        const isFakeSelectionTargetElement = (node) => node !== rootNode && (isContentEditableFalse(node) || isMedia$2(node)) && dom2.isChildOf(node, rootNode) && dom2.isEditable(node.parentNode);
        const setRange = (range2) => {
          if (range2) {
            selection.setRng(range2);
          }
        };
        const showCaret2 = (direction, node, before2, scrollIntoView = true) => {
          const e = editor.dispatch("ShowCaret", {
            target: node,
            direction,
            before: before2
          });
          if (e.isDefaultPrevented()) {
            return null;
          }
          if (scrollIntoView) {
            selection.scrollIntoView(node, direction === -1);
          }
          return fakeCaret.show(before2, node);
        };
        const showBlockCaretContainer2 = (blockCaretContainer) => {
          if (blockCaretContainer.hasAttribute("data-mce-caret")) {
            showCaretContainerBlock(blockCaretContainer);
            selection.scrollIntoView(blockCaretContainer);
          }
        };
        const registerEvents2 = () => {
          editor.on("click", (e) => {
            if (!dom2.isEditable(e.target)) {
              e.preventDefault();
              editor.focus();
            }
          });
          editor.on("blur NewBlock", removeElementSelection);
          editor.on("ResizeWindow FullscreenStateChanged", fakeCaret.reposition);
          editor.on("tap", (e) => {
            const targetElm = e.target;
            const contentEditableRoot = getContentEditableRoot(editor, targetElm);
            if (isContentEditableFalse(contentEditableRoot)) {
              e.preventDefault();
              selectNode(editor, contentEditableRoot).each(setElementSelection);
            } else if (isFakeSelectionTargetElement(targetElm)) {
              selectNode(editor, targetElm).each(setElementSelection);
            }
          }, true);
          editor.on("mousedown", (e) => {
            const targetElm = e.target;
            if (targetElm !== rootNode && targetElm.nodeName !== "HTML" && !dom2.isChildOf(targetElm, rootNode)) {
              return;
            }
            if (!isXYInContentArea(editor, e.clientX, e.clientY)) {
              return;
            }
            removeElementSelection();
            hideFakeCaret();
            const closestContentEditable = getContentEditableRoot(editor, targetElm);
            if (isContentEditableFalse(closestContentEditable)) {
              e.preventDefault();
              selectNode(editor, closestContentEditable).each(setElementSelection);
            } else {
              closestFakeCaretCandidate(rootNode, e.clientX, e.clientY).each((caretInfo) => {
                e.preventDefault();
                const range2 = showCaret2(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false);
                setRange(range2);
                if (isHTMLElement(closestContentEditable)) {
                  closestContentEditable.focus();
                } else {
                  editor.getBody().focus();
                }
              });
            }
          });
          editor.on("keypress", (e) => {
            if (VK.modifierPressed(e)) {
              return;
            }
            if (isContentEditableFalse(selection.getNode())) {
              e.preventDefault();
            }
          });
          editor.on("GetSelectionRange", (e) => {
            let rng = e.range;
            if (selectedElement) {
              if (!selectedElement.parentNode) {
                selectedElement = null;
                return;
              }
              rng = rng.cloneRange();
              rng.selectNode(selectedElement);
              e.range = rng;
            }
          });
          editor.on("focusin", (e) => {
            if (isMedia$2(e.target)) {
              return;
            }
            if (editor.getBody().contains(e.target) && e.target !== editor.getBody() && !editor.dom.isEditable(e.target.parentNode)) {
              if (fakeCaret.isShowing()) {
                fakeCaret.hide();
              }
              if (!e.target.contains(editor.selection.getNode())) {
                editor.selection.select(e.target, true);
                editor.selection.collapse(true);
              }
              const rng = setElementSelection(editor.selection.getRng(), true);
              if (rng) {
                editor.selection.setRng(rng);
              }
            }
          });
          editor.on("SetSelectionRange", (e) => {
            e.range = normalizeVoidElementSelection(e.range);
            const rng = setElementSelection(e.range, e.forward);
            if (rng) {
              e.range = rng;
            }
          });
          const isPasteBin2 = (node) => isElement$7(node) && node.id === "mcepastebin";
          editor.on("AfterSetSelectionRange", (e) => {
            const rng = e.range;
            const parent2 = rng.startContainer.parentElement;
            if (!isRangeInCaretContainer(rng) && !isPasteBin2(parent2)) {
              hideFakeCaret();
            }
            if (!isFakeSelectionElement(parent2)) {
              removeElementSelection();
            }
          });
          init$2(editor);
          setup$4(editor);
          setup$3(editor);
        };
        const isWithinCaretContainer = (node) => isCaretContainer$2(node) || startsWithCaretContainer$1(node) || endsWithCaretContainer$1(node);
        const isRangeInCaretContainer = (rng) => isWithinCaretContainer(rng.startContainer) || isWithinCaretContainer(rng.endContainer);
        const normalizeVoidElementSelection = (rng) => {
          const voidElements = editor.schema.getVoidElements();
          const newRng = dom2.createRng();
          const startContainer = rng.startContainer;
          const startOffset = rng.startOffset;
          const endContainer = rng.endContainer;
          const endOffset = rng.endOffset;
          if (has$2(voidElements, startContainer.nodeName.toLowerCase())) {
            if (startOffset === 0) {
              newRng.setStartBefore(startContainer);
            } else {
              newRng.setStartAfter(startContainer);
            }
          } else {
            newRng.setStart(startContainer, startOffset);
          }
          if (has$2(voidElements, endContainer.nodeName.toLowerCase())) {
            if (endOffset === 0) {
              newRng.setEndBefore(endContainer);
            } else {
              newRng.setEndAfter(endContainer);
            }
          } else {
            newRng.setEnd(endContainer, endOffset);
          }
          return newRng;
        };
        const setupOffscreenSelection = (node, targetClone) => {
          const body = SugarElement.fromDom(editor.getBody());
          const doc = editor.getDoc();
          const realSelectionContainer = descendant$1(body, "#" + realSelectionId).getOrThunk(() => {
            const newContainer = SugarElement.fromHtml('<div data-mce-bogus="all" class="mce-offscreen-selection"></div>', doc);
            set$4(newContainer, "id", realSelectionId);
            append$1(body, newContainer);
            return newContainer;
          });
          const newRange = dom2.createRng();
          empty(realSelectionContainer);
          append(realSelectionContainer, [
            SugarElement.fromText(nbsp, doc),
            SugarElement.fromDom(targetClone),
            SugarElement.fromText(nbsp, doc)
          ]);
          newRange.setStart(realSelectionContainer.dom.firstChild, 1);
          newRange.setEnd(realSelectionContainer.dom.lastChild, 0);
          setAll(realSelectionContainer, {
            top: dom2.getPos(node, editor.getBody()).y + "px"
          });
          focus$1(realSelectionContainer);
          const sel = selection.getSel();
          if (sel) {
            sel.removeAllRanges();
            sel.addRange(newRange);
          }
          return newRange;
        };
        const selectElement = (elm) => {
          const targetClone = elm.cloneNode(true);
          const e = editor.dispatch("ObjectSelected", { target: elm, targetClone });
          if (e.isDefaultPrevented()) {
            return null;
          }
          const range2 = setupOffscreenSelection(elm, e.targetClone);
          const nodeElm = SugarElement.fromDom(elm);
          each$e(descendants(SugarElement.fromDom(editor.getBody()), `*[${elementSelectionAttr2}]`), (elm2) => {
            if (!eq(nodeElm, elm2)) {
              remove$9(elm2, elementSelectionAttr2);
            }
          });
          if (!dom2.getAttrib(elm, elementSelectionAttr2)) {
            elm.setAttribute(elementSelectionAttr2, "1");
          }
          selectedElement = elm;
          hideFakeCaret();
          return range2;
        };
        const setElementSelection = (range2, forward) => {
          if (!range2) {
            return null;
          }
          if (range2.collapsed) {
            if (!isRangeInCaretContainer(range2)) {
              const dir = forward ? 1 : -1;
              const caretPosition = getNormalizedRangeEndPoint(dir, rootNode, range2);
              const beforeNode = caretPosition.getNode(!forward);
              if (isNonNullable(beforeNode)) {
                if (isFakeCaretTarget(beforeNode)) {
                  return showCaret2(dir, beforeNode, forward ? !caretPosition.isAtEnd() : false, false);
                }
                if (isCaretContainerInline(beforeNode) && isContentEditableFalse$a(beforeNode.nextSibling)) {
                  const rng = dom2.createRng();
                  rng.setStart(beforeNode, 0);
                  rng.setEnd(beforeNode, 0);
                  return rng;
                }
              }
              const afterNode = caretPosition.getNode(forward);
              if (isNonNullable(afterNode)) {
                if (isFakeCaretTarget(afterNode)) {
                  return showCaret2(dir, afterNode, forward ? false : !caretPosition.isAtEnd(), false);
                }
                if (isCaretContainerInline(afterNode) && isContentEditableFalse$a(afterNode.previousSibling)) {
                  const rng = dom2.createRng();
                  rng.setStart(afterNode, 1);
                  rng.setEnd(afterNode, 1);
                  return rng;
                }
              }
            }
            return null;
          }
          let startContainer = range2.startContainer;
          let startOffset = range2.startOffset;
          const endOffset = range2.endOffset;
          if (isText$b(startContainer) && startOffset === 0 && isContentEditableFalse(startContainer.parentNode)) {
            startContainer = startContainer.parentNode;
            startOffset = dom2.nodeIndex(startContainer);
            startContainer = startContainer.parentNode;
          }
          if (!isElement$7(startContainer)) {
            return null;
          }
          if (endOffset === startOffset + 1 && startContainer === range2.endContainer) {
            const node = startContainer.childNodes[startOffset];
            if (isFakeSelectionTargetElement(node)) {
              return selectElement(node);
            }
          }
          return null;
        };
        const removeElementSelection = () => {
          if (selectedElement) {
            selectedElement.removeAttribute(elementSelectionAttr2);
          }
          descendant$1(SugarElement.fromDom(editor.getBody()), "#" + realSelectionId).each(remove$8);
          selectedElement = null;
        };
        const destroy2 = () => {
          fakeCaret.destroy();
          selectedElement = null;
        };
        const hideFakeCaret = () => {
          fakeCaret.hide();
        };
        if (!isRtc(editor)) {
          registerEvents2();
        }
        return {
          showCaret: showCaret2,
          showBlockCaretContainer: showBlockCaretContainer2,
          hideFakeCaret,
          destroy: destroy2
        };
      };
      const getNormalizedTextOffset = (container, offset) => {
        let normalizedOffset = offset;
        for (let node = container.previousSibling; isText$b(node); node = node.previousSibling) {
          normalizedOffset += node.data.length;
        }
        return normalizedOffset;
      };
      const generatePath = (dom2, root, node, offset, normalized) => {
        if (isText$b(node) && (offset < 0 || offset > node.data.length)) {
          return [];
        }
        const p = normalized && isText$b(node) ? [getNormalizedTextOffset(node, offset)] : [offset];
        let current = node;
        while (current !== root && current.parentNode) {
          p.push(dom2.nodeIndex(current, normalized));
          current = current.parentNode;
        }
        return current === root ? p.reverse() : [];
      };
      const generatePathRange = (dom2, root, startNode, startOffset, endNode, endOffset, normalized = false) => {
        const start2 = generatePath(dom2, root, startNode, startOffset, normalized);
        const end2 = generatePath(dom2, root, endNode, endOffset, normalized);
        return { start: start2, end: end2 };
      };
      const resolvePath = (root, path2) => {
        const nodePath = path2.slice();
        const offset = nodePath.pop();
        if (!isNumber(offset)) {
          return Optional.none();
        } else {
          const resolvedNode = foldl(nodePath, (optNode, index) => optNode.bind((node) => Optional.from(node.childNodes[index])), Optional.some(root));
          return resolvedNode.bind((node) => {
            if (isText$b(node) && (offset < 0 || offset > node.data.length)) {
              return Optional.none();
            } else {
              return Optional.some({ node, offset });
            }
          });
        }
      };
      const resolvePathRange = (root, range2) => resolvePath(root, range2.start).bind(({ node: startNode, offset: startOffset }) => resolvePath(root, range2.end).map(({ node: endNode, offset: endOffset }) => {
        const rng = document.createRange();
        rng.setStart(startNode, startOffset);
        rng.setEnd(endNode, endOffset);
        return rng;
      }));
      const generatePathRangeFromRange = (dom2, root, range2, normalized = false) => generatePathRange(dom2, root, range2.startContainer, range2.startOffset, range2.endContainer, range2.endOffset, normalized);
      const cleanEmptyNodes = (dom2, node, isRoot2) => {
        if (node && dom2.isEmpty(node) && !isRoot2(node)) {
          const parent2 = node.parentNode;
          dom2.remove(node, isText$b(node.firstChild) && isWhitespaceText(node.firstChild.data));
          cleanEmptyNodes(dom2, parent2, isRoot2);
        }
      };
      const deleteRng = (dom2, rng, isRoot2, clean = true) => {
        const startParent = rng.startContainer.parentNode;
        const endParent = rng.endContainer.parentNode;
        rng.deleteContents();
        if (clean && !isRoot2(rng.startContainer)) {
          if (isText$b(rng.startContainer) && rng.startContainer.data.length === 0) {
            dom2.remove(rng.startContainer);
          }
          if (isText$b(rng.endContainer) && rng.endContainer.data.length === 0) {
            dom2.remove(rng.endContainer);
          }
          cleanEmptyNodes(dom2, startParent, isRoot2);
          if (startParent !== endParent) {
            cleanEmptyNodes(dom2, endParent, isRoot2);
          }
        }
      };
      const getParentBlock = (editor, rng) => Optional.from(editor.dom.getParent(rng.startContainer, editor.dom.isBlock));
      const resolveFromDynamicPatterns = (patternSet, block, beforeText) => {
        const dynamicPatterns = patternSet.dynamicPatternsLookup({
          text: beforeText,
          block
        });
        return {
          ...patternSet,
          blockPatterns: getBlockPatterns(dynamicPatterns).concat(patternSet.blockPatterns),
          inlinePatterns: getInlinePatterns(dynamicPatterns).concat(patternSet.inlinePatterns)
        };
      };
      const getBeforeText = (dom2, block, node, offset) => {
        const rng = dom2.createRng();
        rng.setStart(block, 0);
        rng.setEnd(node, offset);
        return rng.toString();
      };
      const newMarker = (dom2, id) => dom2.create("span", { "data-mce-type": "bookmark", id });
      const rangeFromMarker = (dom2, marker) => {
        const rng = dom2.createRng();
        rng.setStartAfter(marker.start);
        rng.setEndBefore(marker.end);
        return rng;
      };
      const createMarker = (dom2, markerPrefix, pathRange) => {
        const rng = resolvePathRange(dom2.getRoot(), pathRange).getOrDie("Unable to resolve path range");
        const startNode = rng.startContainer;
        const endNode = rng.endContainer;
        const textEnd = rng.endOffset === 0 ? endNode : endNode.splitText(rng.endOffset);
        const textStart = rng.startOffset === 0 ? startNode : startNode.splitText(rng.startOffset);
        const startParentNode = textStart.parentNode;
        const endParentNode = textEnd.parentNode;
        return {
          prefix: markerPrefix,
          end: endParentNode.insertBefore(newMarker(dom2, markerPrefix + "-end"), textEnd),
          start: startParentNode.insertBefore(newMarker(dom2, markerPrefix + "-start"), textStart)
        };
      };
      const removeMarker = (dom2, marker, isRoot2) => {
        cleanEmptyNodes(dom2, dom2.get(marker.prefix + "-end"), isRoot2);
        cleanEmptyNodes(dom2, dom2.get(marker.prefix + "-start"), isRoot2);
      };
      const isReplacementPattern = (pattern) => pattern.start.length === 0;
      const matchesPattern = (patternContent) => (element, offset) => {
        const text2 = element.data;
        const searchText = text2.substring(0, offset);
        const startEndIndex = searchText.lastIndexOf(patternContent.charAt(patternContent.length - 1));
        const startIndex = searchText.lastIndexOf(patternContent);
        if (startIndex !== -1) {
          return startIndex + patternContent.length;
        } else if (startEndIndex !== -1) {
          return startEndIndex + 1;
        } else {
          return -1;
        }
      };
      const findPatternStartFromSpot = (dom2, pattern, block, spot) => {
        const startPattern = pattern.start;
        const startSpot = repeatLeft(dom2, spot.container, spot.offset, matchesPattern(startPattern), block);
        return startSpot.bind((spot2) => {
          const startPatternIndex = block.textContent?.indexOf(startPattern) ?? -1;
          const isCompleteMatch = startPatternIndex !== -1 && spot2.offset >= startPatternIndex + startPattern.length;
          if (isCompleteMatch) {
            const rng = dom2.createRng();
            rng.setStart(spot2.container, spot2.offset - startPattern.length);
            rng.setEnd(spot2.container, spot2.offset);
            return Optional.some(rng);
          } else {
            const offset = spot2.offset - startPattern.length;
            return scanLeft(spot2.container, offset, block).map((nextSpot) => {
              const rng = dom2.createRng();
              rng.setStart(nextSpot.container, nextSpot.offset);
              rng.setEnd(spot2.container, spot2.offset);
              return rng;
            }).filter((rng) => (
              // Ensure the range content matches the start
              rng.toString() === startPattern
            )).orThunk(() => (
              // No match found, so continue searching
              findPatternStartFromSpot(dom2, pattern, block, point(spot2.container, 0))
            ));
          }
        });
      };
      const findPatternStart = (dom2, pattern, node, offset, block, requireGap = false) => {
        if (pattern.start.length === 0 && !requireGap) {
          const rng = dom2.createRng();
          rng.setStart(node, offset);
          rng.setEnd(node, offset);
          return Optional.some(rng);
        }
        return textBefore(node, offset, block).bind((spot) => {
          const start2 = findPatternStartFromSpot(dom2, pattern, block, spot);
          return start2.bind((startRange) => {
            if (requireGap) {
              if (startRange.endContainer === spot.container && startRange.endOffset === spot.offset) {
                return Optional.none();
              } else if (spot.offset === 0 && startRange.endContainer.textContent?.length === startRange.endOffset) {
                return Optional.none();
              }
            }
            return Optional.some(startRange);
          });
        });
      };
      const findPattern$3 = (editor, block, details, normalizedMatches) => {
        const dom2 = editor.dom;
        const root = dom2.getRoot();
        const pattern = details.pattern;
        const endNode = details.position.container;
        const endOffset = details.position.offset;
        return scanLeft(endNode, endOffset - details.pattern.end.length, block).bind((spot) => {
          const endPathRng = generatePathRange(dom2, root, spot.container, spot.offset, endNode, endOffset, normalizedMatches);
          if (isReplacementPattern(pattern)) {
            return Optional.some({
              matches: [{
                pattern,
                startRng: endPathRng,
                endRng: endPathRng
              }],
              position: spot
            });
          } else {
            const resultsOpt = findPatternsRec(editor, details.remainingPatterns, spot.container, spot.offset, block, normalizedMatches);
            const results = resultsOpt.getOr({ matches: [], position: spot });
            const pos = results.position;
            const start2 = findPatternStart(dom2, pattern, pos.container, pos.offset, block, resultsOpt.isNone());
            return start2.map((startRng) => {
              const startPathRng = generatePathRangeFromRange(dom2, root, startRng, normalizedMatches);
              return {
                matches: results.matches.concat([{
                  pattern,
                  startRng: startPathRng,
                  endRng: endPathRng
                }]),
                position: point(startRng.startContainer, startRng.startOffset)
              };
            });
          }
        });
      };
      const findPatternsRec = (editor, patterns, node, offset, block, normalizedMatches) => {
        const dom2 = editor.dom;
        return textBefore(node, offset, dom2.getRoot()).bind((endSpot) => {
          const text2 = getBeforeText(dom2, block, node, offset);
          for (let i = 0; i < patterns.length; i++) {
            const pattern = patterns[i];
            if (!endsWith(text2, pattern.end)) {
              continue;
            }
            const patternsWithoutCurrent = patterns.slice();
            patternsWithoutCurrent.splice(i, 1);
            const result = findPattern$3(editor, block, {
              pattern,
              remainingPatterns: patternsWithoutCurrent,
              position: endSpot
            }, normalizedMatches);
            if (result.isNone() && offset > 0) {
              return findPatternsRec(editor, patterns, node, offset - 1, block, normalizedMatches);
            }
            if (result.isSome()) {
              return result;
            }
          }
          return Optional.none();
        });
      };
      const applyPattern$2 = (editor, pattern, patternRange) => {
        editor.selection.setRng(patternRange);
        if (pattern.type === "inline-format") {
          each$e(pattern.format, (format) => {
            editor.formatter.apply(format);
          });
        } else {
          editor.execCommand(pattern.cmd, false, pattern.value);
        }
      };
      const applyReplacementPattern = (editor, pattern, marker, isRoot2) => {
        const markerRange = rangeFromMarker(editor.dom, marker);
        deleteRng(editor.dom, markerRange, isRoot2);
        applyPattern$2(editor, pattern, markerRange);
      };
      const applyPatternWithContent = (editor, pattern, startMarker, endMarker, isRoot2) => {
        const dom2 = editor.dom;
        const markerEndRange = rangeFromMarker(dom2, endMarker);
        const markerStartRange = rangeFromMarker(dom2, startMarker);
        deleteRng(dom2, markerStartRange, isRoot2);
        deleteRng(dom2, markerEndRange, isRoot2);
        const patternMarker = { prefix: startMarker.prefix, start: startMarker.end, end: endMarker.start };
        const patternRange = rangeFromMarker(dom2, patternMarker);
        applyPattern$2(editor, pattern, patternRange);
      };
      const addMarkers = (dom2, matches) => {
        const markerPrefix = generate("mce_textpattern");
        const matchesWithEnds = foldr(matches, (acc, match2) => {
          const endMarker = createMarker(dom2, markerPrefix + `_end${acc.length}`, match2.endRng);
          return acc.concat([{
            ...match2,
            endMarker
          }]);
        }, []);
        return foldr(matchesWithEnds, (acc, match2) => {
          const idx = matchesWithEnds.length - acc.length - 1;
          const startMarker = isReplacementPattern(match2.pattern) ? match2.endMarker : createMarker(dom2, markerPrefix + `_start${idx}`, match2.startRng);
          return acc.concat([{
            ...match2,
            startMarker
          }]);
        }, []);
      };
      const sortPatterns$1 = (patterns) => sort(patterns, (a, b) => b.end.length - a.end.length);
      const getBestMatches = (matches, matchesWithSortedPatterns) => {
        const hasSameMatches = forall(matches, (match2) => exists(matchesWithSortedPatterns, (sortedMatch) => match2.pattern.start === sortedMatch.pattern.start && match2.pattern.end === sortedMatch.pattern.end));
        if (matches.length === matchesWithSortedPatterns.length) {
          if (hasSameMatches) {
            return matches;
          } else {
            return matchesWithSortedPatterns;
          }
        }
        return matches.length > matchesWithSortedPatterns.length ? matches : matchesWithSortedPatterns;
      };
      const findPatterns$2 = (editor, block, node, offset, patternSet, normalizedMatches) => {
        const matches = findPatternsRec(editor, patternSet.inlinePatterns, node, offset, block, normalizedMatches).fold(() => [], (result) => result.matches);
        const matchesWithSortedPatterns = findPatternsRec(editor, sortPatterns$1(patternSet.inlinePatterns), node, offset, block, normalizedMatches).fold(() => [], (result) => result.matches);
        return getBestMatches(matches, matchesWithSortedPatterns);
      };
      const applyMatches$2 = (editor, matches) => {
        if (matches.length === 0) {
          return;
        }
        const dom2 = editor.dom;
        const bookmark = editor.selection.getBookmark();
        const matchesWithMarkers = addMarkers(dom2, matches);
        each$e(matchesWithMarkers, (match2) => {
          const block = dom2.getParent(match2.startMarker.start, dom2.isBlock);
          const isRoot2 = (node) => node === block;
          if (isReplacementPattern(match2.pattern)) {
            applyReplacementPattern(editor, match2.pattern, match2.endMarker, isRoot2);
          } else {
            applyPatternWithContent(editor, match2.pattern, match2.startMarker, match2.endMarker, isRoot2);
          }
          removeMarker(dom2, match2.endMarker, isRoot2);
          removeMarker(dom2, match2.startMarker, isRoot2);
        });
        editor.selection.moveToBookmark(bookmark);
      };
      const stripPattern$1 = (dom2, block, pattern) => {
        return textAfter(block, 0, block).map((spot) => {
          const node = spot.container;
          scanRight(node, pattern.start.length, block).each((end2) => {
            const rng = dom2.createRng();
            rng.setStart(node, 0);
            rng.setEnd(end2.container, end2.offset);
            deleteRng(dom2, rng, (e) => e === block);
          });
          return node;
        });
      };
      const createApplyPattern = (stripPattern2) => (editor, match2) => {
        const dom2 = editor.dom;
        const pattern = match2.pattern;
        const rng = resolvePathRange(dom2.getRoot(), match2.range).getOrDie("Unable to resolve path range");
        const isBlockFormatName = (name2, formatter) => {
          const formatSet = formatter.get(name2);
          return isArray$1(formatSet) && head(formatSet).exists((format) => has$2(format, "block"));
        };
        getParentBlock(editor, rng).each((block) => {
          if (pattern.type === "block-format") {
            if (isBlockFormatName(pattern.format, editor.formatter)) {
              editor.undoManager.transact(() => {
                stripPattern2(editor.dom, block, pattern);
                editor.formatter.apply(pattern.format);
              });
            }
          } else if (pattern.type === "block-command") {
            editor.undoManager.transact(() => {
              stripPattern2(editor.dom, block, pattern);
              editor.execCommand(pattern.cmd, false, pattern.value);
            });
          }
        });
        return true;
      };
      const sortPatterns = (patterns) => sort(patterns, (a, b) => b.start.length - a.start.length);
      const findPattern$2 = (predicate) => (patterns, text2) => {
        const sortedPatterns = sortPatterns(patterns);
        const nuText = text2.replace(nbsp, " ");
        return find$2(sortedPatterns, (pattern) => predicate(pattern, text2, nuText));
      };
      const createFindPatterns = (findPattern2, skipFullMatch) => (editor, block, patternSet, normalizedMatches, text2 = block.textContent ?? "") => {
        const dom2 = editor.dom;
        const forcedRootBlock = getForcedRootBlock(editor);
        if (!dom2.is(block, forcedRootBlock)) {
          return [];
        }
        return findPattern2(patternSet.blockPatterns, text2).map((pattern) => {
          if (skipFullMatch && Tools.trim(text2).length === pattern.start.length) {
            return [];
          }
          return [{
            pattern,
            range: generatePathRange(dom2, dom2.getRoot(), block, 0, block, 0, normalizedMatches)
          }];
        }).getOr([]);
      };
      const startsWithSingleSpace = (s) => /^\s[^\s]/.test(s);
      const stripPattern = (dom2, block, pattern) => {
        stripPattern$1(dom2, block, pattern).each((node) => {
          const text2 = SugarElement.fromDom(node);
          const textContent = get$4(text2);
          if (startsWithSingleSpace(textContent)) {
            set$1(text2, textContent.slice(1));
          }
        });
      };
      const applyPattern$1 = createApplyPattern(stripPattern);
      const findPattern$1 = findPattern$2((pattern, text2, nuText) => text2.indexOf(pattern.start) === 0 || nuText.indexOf(pattern.start) === 0);
      const findPatterns$1 = createFindPatterns(findPattern$1, true);
      const getMatches$1 = (editor, patternSet) => {
        const rng = editor.selection.getRng();
        return getParentBlock(editor, rng).map((block) => {
          const offset = Math.max(0, rng.startOffset);
          const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, block.textContent ?? "");
          const inlineMatches = findPatterns$2(editor, block, rng.startContainer, offset, dynamicPatternSet, true);
          const blockMatches = findPatterns$1(editor, block, dynamicPatternSet, true);
          return { inlineMatches, blockMatches };
        }).filter(({ inlineMatches, blockMatches }) => blockMatches.length > 0 || inlineMatches.length > 0);
      };
      const applyMatches$1 = (editor, matches) => {
        if (matches.length === 0) {
          return;
        }
        const bookmark = editor.selection.getBookmark();
        each$e(matches, (match2) => applyPattern$1(editor, match2));
        editor.selection.moveToBookmark(bookmark);
      };
      const applyPattern = createApplyPattern(stripPattern$1);
      const findPattern = findPattern$2((pattern, text2, nuText) => text2 === pattern.start || nuText === pattern.start);
      const findPatterns = createFindPatterns(findPattern, false);
      const getMatches = (editor, patternSet) => {
        const rng = editor.selection.getRng();
        return getParentBlock(editor, rng).map((block) => {
          const offset = Math.max(0, rng.startOffset);
          const beforeText = getBeforeText(editor.dom, block, rng.startContainer, offset);
          const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, beforeText);
          return findPatterns(editor, block, dynamicPatternSet, false, beforeText);
        }).filter((matches) => matches.length > 0);
      };
      const applyMatches = (editor, matches) => {
        each$e(matches, (match2) => applyPattern(editor, match2));
      };
      const handleEnter = (editor, patternSet) => getMatches$1(editor, patternSet).fold(never, ({ inlineMatches, blockMatches }) => {
        editor.undoManager.add();
        editor.undoManager.extra(() => {
          editor.execCommand("mceInsertNewLine");
        }, () => {
          insert$5(editor);
          applyMatches$2(editor, inlineMatches);
          applyMatches$1(editor, blockMatches);
          const range2 = editor.selection.getRng();
          const spot = textBefore(range2.startContainer, range2.startOffset, editor.dom.getRoot());
          editor.execCommand("mceInsertNewLine");
          spot.each((s) => {
            const node = s.container;
            if (node.data.charAt(s.offset - 1) === zeroWidth) {
              node.deleteData(s.offset - 1, 1);
              cleanEmptyNodes(editor.dom, node.parentNode, (e) => e === editor.dom.getRoot());
            }
          });
        });
        return true;
      });
      const handleInlineKey = (editor, patternSet) => {
        const rng = editor.selection.getRng();
        getParentBlock(editor, rng).map((block) => {
          const offset = Math.max(0, rng.startOffset - 1);
          const beforeText = getBeforeText(editor.dom, block, rng.startContainer, offset);
          const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, beforeText);
          const inlineMatches = findPatterns$2(editor, block, rng.startContainer, offset, dynamicPatternSet, false);
          if (inlineMatches.length > 0) {
            editor.undoManager.transact(() => {
              applyMatches$2(editor, inlineMatches);
            });
          }
        });
      };
      const handleBlockPatternOnSpace = (editor, patternSet) => getMatches(editor, patternSet).fold(never, (matches) => {
        editor.undoManager.transact(() => {
          applyMatches(editor, matches);
        });
        return true;
      });
      const checkKeyEvent = (codes, event, predicate) => {
        for (let i = 0; i < codes.length; i++) {
          if (predicate(codes[i], event)) {
            return true;
          }
        }
        return false;
      };
      const checkKeyCode = (codes, event) => checkKeyEvent(codes, event, (code, event2) => {
        return code === event2.keyCode && !VK.modifierPressed(event2);
      });
      const checkCharCode = (chars, event) => checkKeyEvent(chars, event, (chr, event2) => {
        return chr.charCodeAt(0) === event2.charCode;
      });
      const setup$2 = (editor) => {
        const charCodes = [",", ".", ";", ":", "!", "?"];
        const keyCodes = [32];
        const getPatternSet = () => createPatternSet(getTextPatterns(editor).filter((pattern) => {
          if (pattern.type === "inline-command" || pattern.type === "block-command") {
            return editor.queryCommandSupported(pattern.cmd);
          }
          return true;
        }), getTextPatternsLookup(editor));
        const hasDynamicPatterns = () => hasTextPatternsLookup(editor);
        editor.on("keydown", (e) => {
          if (e.keyCode === 13 && !VK.modifierPressed(e) && editor.selection.isCollapsed() && editor.selection.isEditable()) {
            const patternSet = filterByTrigger(getPatternSet(), "enter");
            const hasPatterns = patternSet.inlinePatterns.length > 0 || patternSet.blockPatterns.length > 0 || hasDynamicPatterns();
            if (hasPatterns && handleEnter(editor, patternSet)) {
              e.preventDefault();
            }
          }
        }, true);
        editor.on("keydown", (e) => {
          if (e.keyCode === 32 && editor.selection.isCollapsed() && editor.selection.isEditable()) {
            const patternSet = filterByTrigger(getPatternSet(), "space");
            const hasPatterns = patternSet.blockPatterns.length > 0 || hasDynamicPatterns();
            if (hasPatterns && handleBlockPatternOnSpace(editor, patternSet)) {
              e.preventDefault();
            }
          }
        }, true);
        const handleInlineTrigger = () => {
          if (editor.selection.isCollapsed() && editor.selection.isEditable()) {
            const patternSet = filterByTrigger(getPatternSet(), "space");
            const hasPatterns = patternSet.inlinePatterns.length > 0 || hasDynamicPatterns();
            if (hasPatterns) {
              handleInlineKey(editor, patternSet);
            }
          }
        };
        editor.on("keyup", (e) => {
          if (checkKeyCode(keyCodes, e)) {
            handleInlineTrigger();
          }
        });
        editor.on("keypress", (e) => {
          if (checkCharCode(charCodes, e)) {
            Delay.setEditorTimeout(editor, handleInlineTrigger);
          }
        });
      };
      const setup$1 = (editor) => {
        setup$2(editor);
      };
      const Quirks = (editor) => {
        const each2 = Tools.each;
        const BACKSPACE = VK.BACKSPACE, DELETE2 = VK.DELETE, dom2 = editor.dom, selection = editor.selection, parser = editor.parser;
        const browser2 = Env.browser;
        const isGecko = browser2.isFirefox();
        const isWebKit = browser2.isChromium() || browser2.isSafari();
        const isiOS = Env.deviceType.isiPhone() || Env.deviceType.isiPad();
        const isMac = Env.os.isMacOS() || Env.os.isiOS();
        const setEditorCommandState2 = (cmd, state) => {
          try {
            editor.getDoc().execCommand(cmd, false, String(state));
          } catch {
          }
        };
        const isDefaultPrevented = (e) => {
          return e.isDefaultPrevented();
        };
        const emptyEditorWhenDeleting = () => {
          const serializeRng = (rng) => {
            const body = dom2.create("body");
            const contents = rng.cloneContents();
            body.appendChild(contents);
            return selection.serializer.serialize(body, { format: "html" });
          };
          const allContentsSelected = (rng) => {
            const selection2 = serializeRng(rng);
            const allRng = dom2.createRng();
            allRng.selectNode(editor.getBody());
            const allSelection = serializeRng(allRng);
            return selection2 === allSelection;
          };
          editor.on("keydown", (e) => {
            const keyCode = e.keyCode;
            if (!isDefaultPrevented(e) && (keyCode === DELETE2 || keyCode === BACKSPACE) && editor.selection.isEditable()) {
              const isCollapsed = editor.selection.isCollapsed();
              const body = editor.getBody();
              if (isCollapsed && !isEmptyNode(editor.schema, body)) {
                return;
              }
              if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
                return;
              }
              e.preventDefault();
              editor.setContent("");
              if (body.firstChild && dom2.isBlock(body.firstChild)) {
                editor.selection.setCursorLocation(body.firstChild, 0);
              } else {
                editor.selection.setCursorLocation(body, 0);
              }
              editor.nodeChanged();
            }
          });
        };
        const selectAll = () => {
          editor.shortcuts.add("meta+a", null, "SelectAll");
        };
        const documentElementEditingFocus = () => {
          if (!editor.inline) {
            dom2.bind(editor.getDoc(), "mousedown mouseup", (e) => {
              let rng;
              if (e.target === editor.getDoc().documentElement) {
                rng = selection.getRng();
                if (editor.getDoc().getSelection()?.anchorNode !== null) {
                  editor.getBody().focus();
                }
                if (e.type === "mousedown") {
                  if (isCaretContainer$2(rng.startContainer)) {
                    return;
                  }
                  selection.placeCaretAt(e.clientX, e.clientY);
                } else {
                  selection.setRng(rng);
                }
              }
            });
          }
        };
        const removeHrOnBackspace = () => {
          editor.on("keydown", (e) => {
            if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
              if (!editor.getBody().getElementsByTagName("hr").length) {
                return;
              }
              if (selection.isCollapsed() && selection.getRng().startOffset === 0) {
                const node = selection.getNode();
                const previousSibling = node.previousSibling;
                if (node.nodeName === "HR") {
                  dom2.remove(node);
                  e.preventDefault();
                  return;
                }
                if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
                  dom2.remove(previousSibling);
                  e.preventDefault();
                }
              }
            }
          });
        };
        const focusBody2 = () => {
          if (!Range.prototype.getClientRects) {
            editor.on("mousedown", (e) => {
              if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
                const body = editor.getBody();
                body.blur();
                Delay.setEditorTimeout(editor, () => {
                  body.focus();
                });
              }
            });
          }
        };
        const selectControlElements = () => {
          const visualAidsAnchorClass = getVisualAidsAnchorClass(editor);
          editor.on("click", (e) => {
            const target = e.target;
            if (/^(IMG|HR)$/.test(target.nodeName) && dom2.isEditable(target)) {
              e.preventDefault();
              editor.selection.select(target);
              editor.nodeChanged();
            }
            if (target.nodeName === "A" && dom2.hasClass(target, visualAidsAnchorClass) && target.childNodes.length === 0 && dom2.isEditable(target.parentNode)) {
              e.preventDefault();
              selection.select(target);
            }
          });
        };
        const fixFirefoxImageSelection = () => {
          const isEditableImage = (node) => node.nodeName === "IMG" && editor.dom.isEditable(node);
          editor.on("mousedown", (e) => {
            lift2(Optional.from(e.clientX), Optional.from(e.clientY), (clientX, clientY) => {
              const caretPos = editor.getDoc().caretPositionFromPoint(clientX, clientY);
              const img = caretPos?.offsetNode?.childNodes[caretPos.offset - (caretPos.offset > 0 ? 1 : 0)] || caretPos?.offsetNode;
              if (isNonNullable(img) && isEditableImage(img)) {
                const rect = img.getBoundingClientRect();
                e.preventDefault();
                if (!editor.hasFocus()) {
                  editor.focus();
                }
                editor.selection.select(img);
                if (e.clientX < rect.left || e.clientY < rect.top) {
                  editor.selection.collapse(true);
                } else if (e.clientX > rect.right || e.clientY > rect.bottom) {
                  editor.selection.collapse(false);
                }
              }
            });
          });
        };
        const removeStylesWhenDeletingAcrossBlockElements = () => {
          const getAttributeApplyFunction = () => {
            const template = dom2.getAttribs(selection.getStart().cloneNode(false));
            return () => {
              const target = selection.getStart();
              if (target !== editor.getBody()) {
                dom2.setAttrib(target, "style", null);
                each2(template, (attr) => {
                  target.setAttributeNode(attr.cloneNode(true));
                });
              }
            };
          };
          const isSelectionAcrossElements = () => {
            return !selection.isCollapsed() && dom2.getParent(selection.getStart(), dom2.isBlock) !== dom2.getParent(selection.getEnd(), dom2.isBlock);
          };
          editor.on("keypress", (e) => {
            let applyAttributes2;
            if (!isDefaultPrevented(e) && (e.keyCode === 8 || e.keyCode === 46) && isSelectionAcrossElements()) {
              applyAttributes2 = getAttributeApplyFunction();
              editor.getDoc().execCommand("delete", false);
              applyAttributes2();
              e.preventDefault();
              return false;
            } else {
              return true;
            }
          });
          dom2.bind(editor.getDoc(), "cut", (e) => {
            if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
              const applyAttributes2 = getAttributeApplyFunction();
              Delay.setEditorTimeout(editor, () => {
                applyAttributes2();
              });
            }
          });
        };
        const disableBackspaceIntoATable = () => {
          editor.on("keydown", (e) => {
            if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
              if (selection.isCollapsed() && selection.getRng().startOffset === 0) {
                const previousSibling = selection.getNode().previousSibling;
                if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
                  e.preventDefault();
                  return false;
                }
              }
            }
            return true;
          });
        };
        const removeBlockQuoteOnBackSpace = () => {
          editor.on("keydown", (e) => {
            if (isDefaultPrevented(e) || e.keyCode !== VK.BACKSPACE) {
              return;
            }
            let rng = selection.getRng();
            const container = rng.startContainer;
            const offset = rng.startOffset;
            const root = dom2.getRoot();
            let parent2 = container;
            if (!rng.collapsed || offset !== 0) {
              return;
            }
            while (parent2.parentNode && parent2.parentNode.firstChild === parent2 && parent2.parentNode !== root) {
              parent2 = parent2.parentNode;
            }
            if (parent2.nodeName === "BLOCKQUOTE") {
              editor.formatter.toggle("blockquote", void 0, parent2);
              rng = dom2.createRng();
              rng.setStart(container, 0);
              rng.setEnd(container, 0);
              selection.setRng(rng);
            }
          });
        };
        const arrowInFigcaption = () => {
          const isFigcaption = isTag("figcaption");
          editor.on("keydown", (e) => {
            if (e.keyCode === VK.LEFT || e.keyCode === VK.RIGHT) {
              const currentNode = SugarElement.fromDom(editor.selection.getNode());
              if (isFigcaption(currentNode) && editor.selection.isCollapsed()) {
                parent(currentNode).bind((parent2) => {
                  if (editor.selection.getRng().startOffset === 0 && e.keyCode === VK.LEFT) {
                    return prevSibling(parent2);
                  } else if (editor.selection.getRng().endOffset === currentNode.dom.textContent?.length && e.keyCode === VK.RIGHT) {
                    return nextSibling(parent2);
                  } else {
                    return Optional.none();
                  }
                }).each((targetSibling) => {
                  editor.selection.setCursorLocation(targetSibling.dom, 0);
                });
              }
            }
          });
        };
        const setGeckoEditingOptions = () => {
          const setOpts = () => {
            setEditorCommandState2("StyleWithCSS", false);
            setEditorCommandState2("enableInlineTableEditing", false);
            if (!getObjectResizing(editor)) {
              setEditorCommandState2("enableObjectResizing", false);
            }
          };
          if (!isReadOnly$1(editor)) {
            editor.on("BeforeExecCommand mousedown", setOpts);
          }
        };
        const addBrAfterLastLinks = () => {
          const fixLinks = () => {
            each2(dom2.select("a:not([data-mce-block])"), (node) => {
              let parentNode = node.parentNode;
              const root = dom2.getRoot();
              if (parentNode?.lastChild === node) {
                while (parentNode && !dom2.isBlock(parentNode)) {
                  if (parentNode.parentNode?.lastChild !== parentNode || parentNode === root) {
                    return;
                  }
                  parentNode = parentNode.parentNode;
                }
                dom2.add(parentNode, "br", { "data-mce-bogus": 1 });
              }
            });
          };
          editor.on("SetContent ExecCommand", (e) => {
            if (e.type === "setcontent" || e.command === "mceInsertLink") {
              fixLinks();
            }
          });
        };
        const setDefaultBlockType = () => {
          editor.on("init", () => {
            setEditorCommandState2("DefaultParagraphSeparator", getForcedRootBlock(editor));
          });
        };
        const isAllContentSelected = (editor2) => {
          const body = editor2.getBody();
          const rng = editor2.selection.getRng();
          return rng.startContainer === rng.endContainer && rng.startContainer === body && rng.startOffset === 0 && rng.endOffset === body.childNodes.length;
        };
        const normalizeSelection2 = () => {
          editor.on("keyup focusin mouseup", (e) => {
            if (!VK.modifierPressed(e) && !isAllContentSelected(editor)) {
              selection.normalize();
            }
          }, true);
        };
        const showBrokenImageIcon = () => {
          editor.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}");
        };
        const restoreFocusOnKeyDown = () => {
          if (!editor.inline) {
            editor.on("keydown", () => {
              if (document.activeElement === document.body) {
                editor.getWin().focus();
              }
            });
          }
        };
        const bodyHeight = () => {
          if (!editor.inline) {
            editor.contentStyles.push("body {min-height: 150px}");
            editor.on("click", (e) => {
              let rng;
              if (e.target.nodeName === "HTML") {
                rng = editor.selection.getRng();
                editor.getBody().focus();
                editor.selection.setRng(rng);
                editor.selection.normalize();
                editor.nodeChanged();
              }
            });
          }
        };
        const blockCmdArrowNavigation = () => {
          if (isMac) {
            editor.on("keydown", (e) => {
              if (VK.metaKeyPressed(e) && !e.shiftKey && (e.keyCode === 37 || e.keyCode === 39)) {
                e.preventDefault();
                const selection2 = editor.selection.getSel();
                selection2.modify("move", e.keyCode === 37 ? "backward" : "forward", "lineboundary");
              }
            });
          }
        };
        const tapLinksAndImages = () => {
          editor.on("click", (e) => {
            let elm = e.target;
            do {
              if (elm.tagName === "A") {
                e.preventDefault();
                return;
              }
            } while (elm = elm.parentNode);
          });
          editor.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}");
        };
        const blockFormSubmitInsideEditor = () => {
          editor.on("init", () => {
            editor.dom.bind(editor.getBody(), "submit", (e) => {
              e.preventDefault();
            });
          });
        };
        const removeAppleInterchangeBrs = () => {
          parser.addNodeFilter("br", (nodes) => {
            let i = nodes.length;
            while (i--) {
              if (nodes[i].attr("class") === "Apple-interchange-newline") {
                nodes[i].remove();
              }
            }
          });
        };
        const refreshContentEditable = noop;
        const isHidden = () => {
          if (!isGecko || editor.removed) {
            return false;
          }
          const sel = editor.selection.getSel();
          return !sel || !sel.rangeCount || sel.rangeCount === 0;
        };
        const setupRtc = () => {
          if (isWebKit) {
            documentElementEditingFocus();
            selectControlElements();
            blockFormSubmitInsideEditor();
            selectAll();
            if (isiOS) {
              restoreFocusOnKeyDown();
              bodyHeight();
              tapLinksAndImages();
            }
          }
          if (isGecko) {
            focusBody2();
            setGeckoEditingOptions();
            showBrokenImageIcon();
            blockCmdArrowNavigation();
          }
        };
        const setup2 = () => {
          removeBlockQuoteOnBackSpace();
          emptyEditorWhenDeleting();
          if (!Env.windowsPhone) {
            normalizeSelection2();
          }
          if (isWebKit) {
            documentElementEditingFocus();
            selectControlElements();
            setDefaultBlockType();
            blockFormSubmitInsideEditor();
            disableBackspaceIntoATable();
            removeAppleInterchangeBrs();
            if (isiOS) {
              restoreFocusOnKeyDown();
              bodyHeight();
              tapLinksAndImages();
            } else {
              selectAll();
            }
          }
          if (isGecko) {
            arrowInFigcaption();
            fixFirefoxImageSelection();
            removeHrOnBackspace();
            focusBody2();
            removeStylesWhenDeletingAcrossBlockElements();
            setGeckoEditingOptions();
            addBrAfterLastLinks();
            showBrokenImageIcon();
            blockCmdArrowNavigation();
            disableBackspaceIntoATable();
          }
        };
        if (isRtc(editor)) {
          setupRtc();
        } else {
          setup2();
        }
        return {
          refreshContentEditable,
          isHidden
        };
      };
      class ComponentLoadError extends Error {
        url;
        constructor(message, url) {
          super(message);
          this.url = url;
        }
      }
      const hostWindowComponentScripts = {};
      const loadScript = (url, doc, extraAtts) => {
        return new Promise((resolve2, reject) => {
          const script = SugarElement.fromTag("script");
          setAll$1(script, {
            type: "text/javascript",
            src: url,
            ...extraAtts
          });
          const clean = () => {
            remove$8(script);
          };
          bind$1(script, "load", () => {
            clean();
            resolve2();
          });
          bind$1(script, "error", () => {
            clean();
            reject(new Error(`Failed to load script url: ${url}`));
          });
          append$1(getHead(doc), script);
        });
      };
      const loadComponent = async (url, doc) => {
        const extraAtts = ScriptLoader.ScriptLoader.getScriptAttributes(url);
        await loadScript(url, doc, extraAtts).catch(() => Promise.reject(new ComponentLoadError(`Failed to load component url: ${url}`, url)));
        return url;
      };
      const loadComponentsForInlineEditor = (componentUrls) => {
        return mapToArray(componentUrls, (url, elementName) => {
          return get$a(hostWindowComponentScripts, url).getOrThunk(() => {
            if (isNullable(window.customElements.get(elementName))) {
              const loadPromise = loadComponent(url, getDocument());
              hostWindowComponentScripts[url] = loadPromise;
              return loadPromise;
            } else {
              return Promise.resolve(url);
            }
          }).catch((err) => {
            delete hostWindowComponentScripts[url];
            return Promise.reject(err);
          });
        });
      };
      const loadComponentsForIframeEditor = (componentUrls, doc) => {
        const urls = unique$1(values(componentUrls));
        return map$3(urls, (url) => loadComponent(url, SugarElement.fromDom(doc)));
      };
      const loadComponentsForEditor = (editor) => {
        const componentUrls = editor.schema.getComponentUrls();
        if (editor.inline) {
          return loadComponentsForInlineEditor(componentUrls);
        } else {
          return loadComponentsForIframeEditor(componentUrls, editor.getDoc());
        }
      };
      const loadComponentsAsync = async (editor) => {
        const loadPromises = loadComponentsForEditor(editor);
        const rejected = filter$5(await Promise.allSettled(loadPromises), (r2) => r2.status === "rejected");
        if (rejected.length > 0) {
          each$e(rejected, (rejection) => {
            if (rejection.reason instanceof ComponentLoadError) {
              const { url } = rejection.reason;
              componentLoadError(editor, url);
            }
          });
        }
      };
      const loadComponents = (editor) => {
        loadComponentsAsync(editor);
      };
      const DOM$6 = DOMUtils.DOM;
      const appendStyle = (editor, text2) => {
        const body = SugarElement.fromDom(editor.getBody());
        const container = getStyleContainer(getRootNode(body));
        const style = SugarElement.fromTag("style");
        set$4(style, "type", "text/css");
        append$1(style, SugarElement.fromText(text2));
        append$1(container, style);
        editor.on("remove", () => {
          remove$8(style);
        });
      };
      const getRootName = (editor) => editor.inline ? editor.getElement().nodeName.toLowerCase() : void 0;
      const removeUndefined = (obj) => filter$4(obj, (v) => isUndefined(v) === false);
      const mkParserSettings = (editor) => {
        const getOption2 = editor.options.get;
        const blobCache = editor.editorUpload.blobCache;
        return removeUndefined({
          allow_conditional_comments: getOption2("allow_conditional_comments"),
          allow_html_data_urls: getOption2("allow_html_data_urls"),
          allow_svg_data_urls: getOption2("allow_svg_data_urls"),
          allow_html_in_named_anchor: getOption2("allow_html_in_named_anchor"),
          allow_script_urls: getOption2("allow_script_urls"),
          allow_html_in_comments: getOption2("allow_html_in_comments"),
          allow_mathml_annotation_encodings: getOption2("allow_mathml_annotation_encodings"),
          allow_unsafe_link_target: getOption2("allow_unsafe_link_target"),
          convert_unsafe_embeds: getOption2("convert_unsafe_embeds"),
          convert_fonts_to_spans: getOption2("convert_fonts_to_spans"),
          extended_mathml_attributes: getOption2("extended_mathml_attributes"),
          extended_mathml_elements: getOption2("extended_mathml_elements"),
          fix_list_elements: getOption2("fix_list_elements"),
          font_size_legacy_values: getOption2("font_size_legacy_values"),
          forced_root_block: getOption2("forced_root_block"),
          forced_root_block_attrs: getOption2("forced_root_block_attrs"),
          preserve_cdata: getOption2("preserve_cdata"),
          inline_styles: getOption2("inline_styles"),
          root_name: getRootName(editor),
          sandbox_iframes: getOption2("sandbox_iframes"),
          sandbox_iframes_exclusions: getSandboxIframesExclusions(editor),
          sanitize: getOption2("xss_sanitization"),
          validate: true,
          blob_cache: blobCache,
          document: editor.getDoc()
        });
      };
      const mkSchemaSettings = (editor) => {
        const getOption2 = editor.options.get;
        return removeUndefined({
          custom_elements: getOption2("custom_elements"),
          extended_valid_elements: getOption2("extended_valid_elements"),
          invalid_elements: getOption2("invalid_elements"),
          invalid_styles: getOption2("invalid_styles"),
          schema: getOption2("schema"),
          valid_children: getOption2("valid_children"),
          valid_classes: getOption2("valid_classes"),
          valid_elements: getOption2("valid_elements"),
          valid_styles: getOption2("valid_styles"),
          verify_html: getOption2("verify_html"),
          padd_empty_block_inline_children: getOption2("format_empty_lines")
        });
      };
      const mkSerializerSettings = (editor) => {
        const getOption2 = editor.options.get;
        return {
          ...mkParserSettings(editor),
          ...mkSchemaSettings(editor),
          ...removeUndefined({
            // SerializerSettings
            remove_trailing_brs: getOption2("remove_trailing_brs"),
            pad_empty_with_br: getOption2("pad_empty_with_br"),
            url_converter: getOption2("url_converter"),
            url_converter_scope: getOption2("url_converter_scope"),
            // Writer settings
            element_format: getOption2("element_format"),
            entities: getOption2("entities"),
            entity_encoding: getOption2("entity_encoding"),
            indent: getOption2("indent"),
            indent_after: getOption2("indent_after"),
            indent_before: getOption2("indent_before")
          })
        };
      };
      const createParser = (editor) => {
        const parser = DomParser(mkParserSettings(editor), editor.schema);
        parser.addAttributeFilter("src,href,style,tabindex", (nodes, name2) => {
          const dom2 = editor.dom;
          const internalName = "data-mce-" + name2;
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            let value2 = node.attr(name2);
            if (value2 && !node.attr(internalName)) {
              if (value2.indexOf("data:") === 0 || value2.indexOf("blob:") === 0) {
                continue;
              }
              if (name2 === "style") {
                value2 = dom2.serializeStyle(dom2.parseStyle(value2), node.name);
                if (!value2.length) {
                  value2 = null;
                }
                node.attr(internalName, value2);
                node.attr(name2, value2);
              } else if (name2 === "tabindex") {
                node.attr(internalName, value2);
                node.attr(name2, null);
              } else {
                node.attr(internalName, editor.convertURL(value2, name2, node.name));
              }
            }
          }
        });
        parser.addNodeFilter("script", (nodes) => {
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            const type2 = node.attr("type") || "no/type";
            if (type2.indexOf("mce-") !== 0) {
              node.attr("type", "mce-" + type2);
            }
          }
        });
        if (shouldPreserveCData(editor)) {
          parser.addNodeFilter("#cdata", (nodes) => {
            let i = nodes.length;
            while (i--) {
              const node = nodes[i];
              node.type = 8;
              node.name = "#comment";
              node.value = "[CDATA[" + editor.dom.encode(node.value ?? "") + "]]";
            }
          });
        }
        parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div", (nodes) => {
          let i = nodes.length;
          const nonEmptyElements = editor.schema.getNonEmptyElements();
          while (i--) {
            const node = nodes[i];
            if (node.isEmpty(nonEmptyElements) && node.getAll("br").length === 0) {
              node.append(new AstNode("br", 1));
            }
          }
        });
        return parser;
      };
      const autoFocus = (editor) => {
        const autoFocus2 = getAutoFocus(editor);
        if (autoFocus2) {
          Delay.setEditorTimeout(editor, () => {
            let focusEditor2;
            if (autoFocus2 === true) {
              focusEditor2 = editor;
            } else {
              focusEditor2 = editor.editorManager.get(autoFocus2);
            }
            if (focusEditor2 && !focusEditor2.destroyed) {
              focusEditor2.focus();
              focusEditor2.selection.scrollIntoView();
            }
          }, 100);
        }
      };
      const moveSelectionToFirstCaretPosition = (editor) => {
        const root = editor.dom.getRoot();
        if (!editor.inline && (!hasAnyRanges(editor) || editor.selection.getStart(true) === root)) {
          firstPositionIn(root).each((pos) => {
            const node = pos.getNode();
            const caretPos = isTable$2(node) ? firstPositionIn(node).getOr(pos) : pos;
            editor.selection.setRng(caretPos.toRange());
          });
        }
      };
      const initEditor = (editor) => {
        editor.bindPendingEventDelegates();
        editor.initialized = true;
        fireInit(editor);
        editor.focus(true);
        moveSelectionToFirstCaretPosition(editor);
        editor.nodeChanged({ initial: true });
        const initInstanceCallback = getInitInstanceCallback(editor);
        if (isFunction(initInstanceCallback)) {
          initInstanceCallback.call(editor, editor);
        }
        autoFocus(editor);
        if (isDisabled(editor)) {
          toggleDisabled(editor, true);
        }
      };
      const getStyleSheetLoader$1 = (editor) => editor.inline ? editor.ui.styleSheetLoader : editor.dom.styleSheetLoader;
      const makeStylesheetLoadingPromises = (editor, css, framedFonts) => {
        const { pass: bundledCss, fail: normalCss } = partition$2(css, (name2) => tinymce.Resource.has(toContentSkinResourceName(name2)));
        const bundledPromises = bundledCss.map((url) => {
          const css2 = tinymce.Resource.get(toContentSkinResourceName(url));
          if (isString(css2)) {
            return Promise.resolve(getStyleSheetLoader$1(editor).loadRawCss(url, css2));
          }
          return Promise.resolve();
        });
        const promises = [
          ...bundledPromises,
          getStyleSheetLoader$1(editor).loadAll(normalCss)
        ];
        if (editor.inline) {
          return promises;
        } else {
          return promises.concat([
            editor.ui.styleSheetLoader.loadAll(framedFonts)
          ]);
        }
      };
      const loadContentCss = (editor) => {
        const styleSheetLoader = getStyleSheetLoader$1(editor);
        const fontCss = getFontCss(editor);
        const css = editor.contentCSS;
        const removeCss = () => {
          styleSheetLoader.unloadAll(css);
          if (!editor.inline) {
            editor.ui.styleSheetLoader.unloadAll(fontCss);
          }
        };
        const loaded = () => {
          if (editor.removed) {
            removeCss();
          } else {
            editor.on("remove", removeCss);
          }
        };
        if (editor.contentStyles.length > 0) {
          let contentCssText = "";
          Tools.each(editor.contentStyles, (style) => {
            contentCssText += style + "\r\n";
          });
          editor.dom.addStyle(contentCssText);
        }
        const allStylesheets = Promise.all(makeStylesheetLoadingPromises(editor, css, fontCss)).then(loaded).catch(loaded);
        const contentStyle = getContentStyle(editor);
        if (contentStyle) {
          appendStyle(editor, contentStyle);
        }
        return allStylesheets;
      };
      const preInit = (editor) => {
        const doc = editor.getDoc(), body = editor.getBody();
        firePreInit(editor);
        if (!shouldBrowserSpellcheck(editor)) {
          doc.body.spellcheck = false;
          DOM$6.setAttrib(body, "spellcheck", "false");
        }
        editor.quirks = Quirks(editor);
        firePostRender(editor);
        const directionality = getDirectionality(editor);
        if (directionality !== void 0) {
          body.dir = directionality;
        }
        const protect = getProtect(editor);
        if (protect) {
          editor.on("BeforeSetContent", (e) => {
            Tools.each(protect, (pattern) => {
              e.content = e.content.replace(pattern, (str) => {
                return "<!--mce:protected " + escape(str) + "-->";
              });
            });
          });
        }
        editor.on("SetContent", () => {
          editor.addVisual(editor.getBody());
        });
        editor.on("compositionstart compositionend", (e) => {
          editor.composing = e.type === "compositionstart";
        });
      };
      const loadInitialContent = (editor) => {
        if (!isRtc(editor)) {
          editor.load({ initial: true, format: "html" });
        }
        editor.startContent = editor.getContent({ format: "raw" });
      };
      const initEditorWithInitialContent = (editor) => {
        if (editor.removed !== true) {
          loadInitialContent(editor);
          initEditor(editor);
        }
      };
      const startProgress = (editor) => {
        let canceled = false;
        const progressTimeout = setTimeout(() => {
          if (!canceled) {
            editor.setProgressState(true);
          }
        }, 500);
        return () => {
          clearTimeout(progressTimeout);
          canceled = true;
          editor.setProgressState(false);
        };
      };
      const contentBodyLoaded = (editor) => {
        const targetElm = editor.getElement();
        let doc = editor.getDoc();
        if (editor.inline) {
          DOM$6.addClass(targetElm, "mce-content-body");
          editor.contentDocument = doc = document;
          editor.contentWindow = window;
          editor.bodyElement = targetElm;
          editor.contentAreaContainer = targetElm;
        }
        const body = editor.getBody();
        body.disabled = true;
        editor.readonly = isReadOnly$1(editor);
        editor._editableRoot = hasEditableRoot$1(editor);
        if (!isDisabled$1(editor) && editor.hasEditableRoot()) {
          if (editor.inline && DOM$6.getStyle(body, "position", true) === "static") {
            body.style.position = "relative";
          }
          body.contentEditable = "true";
        }
        body.disabled = false;
        editor.editorUpload = EditorUpload(editor);
        editor.schema = Schema(mkSchemaSettings(editor));
        editor.dom = DOMUtils(doc, {
          keep_values: true,
          // Note: Don't bind here, as the binding is handled via the `url_converter_scope`
          // eslint-disable-next-line @typescript-eslint/unbound-method
          url_converter: editor.convertURL,
          url_converter_scope: editor,
          update_styles: true,
          root_element: editor.inline ? editor.getBody() : null,
          collect: editor.inline,
          schema: editor.schema,
          contentCssCors: shouldUseContentCssCors(editor),
          referrerPolicy: getReferrerPolicy(editor),
          crossOrigin: getCrossOrigin(editor),
          onSetAttrib: (e) => {
            editor.dispatch("SetAttrib", e);
          }
        });
        editor.parser = createParser(editor);
        editor.serializer = DomSerializer(mkSerializerSettings(editor), editor);
        editor.selection = EditorSelection(editor.dom, editor.getWin(), editor.serializer, editor);
        editor.annotator = Annotator(editor);
        editor.formatter = Formatter(editor);
        editor.undoManager = UndoManager(editor);
        editor._nodeChangeDispatcher = new NodeChange(editor);
        editor._selectionOverrides = SelectionOverrides(editor);
        setup$b(editor);
        setup$u(editor);
        setup$6(editor);
        setup$s(editor);
        if (!isRtc(editor)) {
          setup$5(editor);
          setup$1(editor);
        }
        const caret = setup$g(editor);
        setup$v(editor, caret);
        setup$t(editor);
        setup$w(editor);
        setup$7(editor, caret);
        const setupRtcThunk = setup$z(editor);
        preInit(editor);
        loadComponents(editor);
        setupRtcThunk.fold(() => {
          const cancelProgress = startProgress(editor);
          loadContentCss(editor).then(() => {
            initEditorWithInitialContent(editor);
            cancelProgress();
          });
        }, (setupRtc) => {
          editor.setProgressState(true);
          loadContentCss(editor).then(() => {
            setupRtc().then((_rtcMode) => {
              editor.setProgressState(false);
              initEditorWithInitialContent(editor);
              bindEvents(editor);
            }, (err) => {
              editor.notificationManager.open({ type: "error", text: String(err) });
              initEditorWithInitialContent(editor);
              bindEvents(editor);
            });
          });
        });
      };
      const DOM$5 = DOMUtils.DOM;
      const createIframeElement = (id, title, customAttrs, tabindex) => {
        const iframe = SugarElement.fromTag("iframe");
        tabindex.each((t) => set$4(iframe, "tabindex", t));
        setAll$1(iframe, customAttrs);
        setAll$1(iframe, {
          id: id + "_ifr",
          frameBorder: "0",
          allowTransparency: "true",
          ...Env.browser.isFirefox() ? { title } : {}
        });
        add$2(iframe, "tox-edit-area__iframe");
        return iframe;
      };
      const getIframeHtml = (editor) => {
        let iframeHTML = getDocType(editor) + "<html><head>";
        if (getDocumentBaseUrl(editor) !== editor.editorManager.documentBaseURL) {
          iframeHTML += '<base href="' + editor.documentBaseURI.getURI() + '" />';
        }
        iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
        const bodyId = getBodyId(editor);
        const bodyClass = getBodyClass(editor);
        const translatedAriaText = editor.translate(getIframeAriaText(editor));
        const iframeBodyAriaLabel = Env.browser.isFirefox() ? "" : `aria-label="${translatedAriaText}"`;
        if (getContentSecurityPolicy(editor)) {
          iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + getContentSecurityPolicy(editor) + '" />';
        }
        iframeHTML += `</head><body id="${bodyId}" class="mce-content-body ${bodyClass}" data-id="${editor.id}" ${iframeBodyAriaLabel}><br></body></html>`;
        return iframeHTML;
      };
      const createIframe = (editor, boxInfo) => {
        const iframeTitle = getIframeAriaText(editor);
        const translatedTitle = editor.translate(iframeTitle);
        const tabindex = getOpt(SugarElement.fromDom(editor.getElement()), "tabindex").bind(toInt);
        const ifr = createIframeElement(editor.id, translatedTitle, getIframeAttrs(editor), tabindex).dom;
        ifr.onload = () => {
          ifr.onload = null;
          editor.dispatch("load");
        };
        editor.contentAreaContainer = boxInfo.iframeContainer;
        editor.iframeElement = ifr;
        editor.iframeHTML = getIframeHtml(editor);
        DOM$5.add(boxInfo.iframeContainer, ifr);
      };
      const setupIframeBody = (editor) => {
        const iframe = editor.iframeElement;
        const ready = () => {
          editor.contentDocument = iframe.contentDocument;
          contentBodyLoaded(editor);
        };
        if (shouldUseDocumentWrite(editor) || Env.browser.isFirefox()) {
          const doc = editor.getDoc();
          doc.open();
          doc.write(editor.iframeHTML);
          doc.close();
          ready();
        } else {
          const binder2 = bind$1(SugarElement.fromDom(iframe), "load", () => {
            binder2.unbind();
            ready();
          });
          iframe.srcdoc = editor.iframeHTML;
        }
      };
      const init$1 = (editor, boxInfo) => {
        createIframe(editor, boxInfo);
        if (boxInfo.editorContainer) {
          boxInfo.editorContainer.style.display = editor.orgDisplay;
          editor.hidden = DOM$5.isHidden(boxInfo.editorContainer);
        }
        editor.getElement().style.display = "none";
        DOM$5.setAttrib(editor.id, "aria-hidden", "true");
        editor.getElement().style.visibility = editor.orgVisibility;
        setupIframeBody(editor);
      };
      const DOM$4 = DOMUtils.DOM;
      const initPlugin = (editor, initializedPlugins, plugin) => {
        const Plugin = PluginManager.get(plugin);
        const pluginUrl = PluginManager.urls[plugin] || editor.editorManager.documentBaseURL.replace(/\/$/, "");
        plugin = Tools.trim(plugin);
        if (Plugin && Tools.inArray(initializedPlugins, plugin) === -1) {
          if (editor.plugins[plugin]) {
            return;
          }
          try {
            const pluginInstance = Plugin(editor, pluginUrl) || {};
            editor.plugins[plugin] = pluginInstance;
            if (isFunction(pluginInstance.init)) {
              pluginInstance.init(editor, pluginUrl);
              initializedPlugins.push(plugin);
            }
          } catch (e) {
            pluginInitError(editor, plugin, e);
          }
        }
      };
      const initTooltipClosing = (editor) => {
        const closeTooltipsListener = (event) => {
          if (event.keyCode === VK.ESC && !event.defaultPrevented) {
            if (fireCloseTooltips(editor).isDefaultPrevented()) {
              event.preventDefault();
            }
          }
        };
        document.addEventListener("keyup", closeTooltipsListener);
        if (!editor.inline) {
          editor.on("keyup", closeTooltipsListener);
        }
        editor.on("remove", () => {
          document.removeEventListener("keyup", closeTooltipsListener);
          if (!editor.inline) {
            editor.off("keyup", closeTooltipsListener);
          }
        });
      };
      const trimLegacyPrefix = (name2) => {
        return name2.replace(/^\-/, "");
      };
      const initPlugins = (editor) => {
        const initializedPlugins = [];
        each$e(getPlugins(editor), (name2) => {
          initPlugin(editor, initializedPlugins, trimLegacyPrefix(name2));
        });
      };
      const initIcons = (editor) => {
        const iconPackName = Tools.trim(getIconPackName(editor));
        const currentIcons = editor.ui.registry.getAll().icons;
        const loadIcons2 = {
          ...IconManager.get("default").icons,
          ...IconManager.get(iconPackName).icons
        };
        each$d(loadIcons2, (svgData, icon) => {
          if (!has$2(currentIcons, icon)) {
            editor.ui.registry.addIcon(icon, svgData);
          }
        });
      };
      const initTheme = (editor) => {
        const theme = getTheme(editor);
        if (isString(theme)) {
          const Theme = ThemeManager.get(theme);
          editor.theme = Theme(editor, ThemeManager.urls[theme]) || {};
          if (isFunction(editor.theme.init)) {
            editor.theme.init(editor, ThemeManager.urls[theme] || editor.editorManager.documentBaseURL.replace(/\/$/, ""));
          }
        } else {
          editor.theme = {};
        }
      };
      const initModel = (editor) => {
        const model = getModel(editor);
        const Model = ModelManager.get(model);
        editor.model = Model(editor, ModelManager.urls[model]);
      };
      const initLicenseKeyManager = (editor) => {
        LicenseKeyManagerLoader.init(editor);
      };
      const renderFromLoadedTheme = (editor) => {
        const render2 = editor.theme.renderUI;
        return render2 ? render2() : renderThemeFalse(editor);
      };
      const renderFromThemeFunc = (editor) => {
        const elm = editor.getElement();
        const theme = getTheme(editor);
        const info = theme(editor, elm);
        if (info.editorContainer.nodeType) {
          info.editorContainer.id = info.editorContainer.id || editor.id + "_parent";
        }
        if (info.iframeContainer && info.iframeContainer.nodeType) {
          info.iframeContainer.id = info.iframeContainer.id || editor.id + "_iframecontainer";
        }
        info.height = info.iframeHeight ? info.iframeHeight : elm.offsetHeight;
        return info;
      };
      const createThemeFalseResult = (element, iframe) => {
        return {
          editorContainer: element,
          iframeContainer: iframe,
          api: {}
        };
      };
      const renderThemeFalseIframe = (targetElement) => {
        const iframeContainer = DOM$4.create("div");
        DOM$4.insertAfter(iframeContainer, targetElement);
        return createThemeFalseResult(iframeContainer, iframeContainer);
      };
      const renderThemeFalse = (editor) => {
        const targetElement = editor.getElement();
        return editor.inline ? createThemeFalseResult(null) : renderThemeFalseIframe(targetElement);
      };
      const renderThemeUi = (editor) => {
        const elm = editor.getElement();
        editor.orgDisplay = elm.style.display;
        if (isString(getTheme(editor))) {
          return renderFromLoadedTheme(editor);
        } else if (isFunction(getTheme(editor))) {
          return renderFromThemeFunc(editor);
        } else {
          return renderThemeFalse(editor);
        }
      };
      const augmentEditorUiApi = (editor, api2) => {
        const uiApiFacade = {
          show: Optional.from(api2.show).getOr(noop),
          hide: Optional.from(api2.hide).getOr(noop),
          isEnabled: Optional.from(api2.isEnabled).getOr(always),
          setEnabled: (state) => {
            const shouldSkip = state && (editor.mode.get() === "readonly" || isDisabled(editor));
            if (!shouldSkip) {
              Optional.from(api2.setEnabled).each((f) => f(state));
            }
          }
        };
        editor.ui = { ...editor.ui, ...uiApiFacade };
      };
      const init = async (editor) => {
        editor.dispatch("ScriptsLoaded");
        initIcons(editor);
        initTooltipClosing(editor);
        initTheme(editor);
        initModel(editor);
        initLicenseKeyManager(editor);
        initPlugins(editor);
        const renderInfo = await renderThemeUi(editor);
        augmentEditorUiApi(editor, Optional.from(renderInfo.api).getOr({}));
        editor.editorContainer = renderInfo.editorContainer;
        appendContentCssFromSettings(editor);
        if (editor.inline) {
          contentBodyLoaded(editor);
        } else {
          init$1(editor, {
            editorContainer: renderInfo.editorContainer,
            iframeContainer: renderInfo.iframeContainer
          });
        }
      };
      const DOM$3 = DOMUtils.DOM;
      const hasSkipLoadPrefix = (name2) => name2.charAt(0) === "-";
      const loadLanguage = (scriptLoader, editor) => {
        const languageCode = getLanguageCode(editor);
        const languageUrl = getLanguageUrl(editor);
        if (!I18n.hasCode(languageCode) && languageCode !== "en") {
          const url = isNotEmpty(languageUrl) ? languageUrl : `${editor.editorManager.baseURL}/langs/${languageCode}.js`;
          scriptLoader.add(url).catch(() => {
            languageLoadError(editor, url, languageCode);
          });
        }
      };
      const loadTheme = (editor, suffix) => {
        const theme = getTheme(editor);
        if (isString(theme) && !hasSkipLoadPrefix(theme) && !has$2(ThemeManager.urls, theme)) {
          const themeUrl = getThemeUrl(editor);
          const url = themeUrl ? editor.documentBaseURI.toAbsolute(themeUrl) : `themes/${theme}/theme${suffix}.js`;
          ThemeManager.load(theme, url).catch(() => {
            themeLoadError(editor, url, theme);
          });
        }
      };
      const loadModel = (editor, suffix) => {
        const model = getModel(editor);
        if (model !== "plugin" && !has$2(ModelManager.urls, model)) {
          const modelUrl = getModelUrl(editor);
          const url = isString(modelUrl) ? editor.documentBaseURI.toAbsolute(modelUrl) : `models/${model}/model${suffix}.js`;
          ModelManager.load(model, url).catch(() => {
            modelLoadError(editor, url, model);
          });
        }
      };
      const loadLicenseKeyManager = (editor, suffix) => {
        LicenseKeyManagerLoader.load(editor, suffix);
      };
      const getIconsUrlMetaFromUrl = (editor) => Optional.from(getIconsUrl(editor)).filter(isNotEmpty).map((url) => ({
        url,
        name: Optional.none()
      }));
      const getIconsUrlMetaFromName = (editor, name2, suffix) => Optional.from(name2).filter((name3) => isNotEmpty(name3) && !IconManager.has(name3)).map((name3) => ({
        url: `${editor.editorManager.baseURL}/icons/${name3}/icons${suffix}.js`,
        name: Optional.some(name3)
      }));
      const loadIcons = (scriptLoader, editor, suffix) => {
        const defaultIconsUrl = getIconsUrlMetaFromName(editor, "default", suffix);
        const customIconsUrl = getIconsUrlMetaFromUrl(editor).orThunk(() => getIconsUrlMetaFromName(editor, getIconPackName(editor), ""));
        each$e(cat([defaultIconsUrl, customIconsUrl]), (urlMeta) => {
          scriptLoader.add(urlMeta.url).catch(() => {
            iconsLoadError(editor, urlMeta.url, urlMeta.name.getOrUndefined());
          });
        });
      };
      const loadPlugins = (editor, suffix) => {
        const loadPlugin = (name2, url) => {
          if (name2 === "licensekeymanager") {
            return;
          }
          PluginManager.load(name2, url).catch(() => {
            pluginLoadError(editor, url, name2);
          });
        };
        each$d(getExternalPlugins$1(editor), (url, name2) => {
          loadPlugin(name2, url);
          editor.options.set("plugins", getPlugins(editor).concat(name2));
        });
        each$e(getPlugins(editor), (plugin) => {
          plugin = Tools.trim(plugin);
          if (plugin && !PluginManager.urls[plugin] && !hasSkipLoadPrefix(plugin)) {
            loadPlugin(plugin, `plugins/${plugin}/plugin${suffix}.js`);
          }
        });
      };
      const isThemeLoaded = (editor) => {
        const theme = getTheme(editor);
        return !isString(theme) || isNonNullable(ThemeManager.get(theme));
      };
      const isModelLoaded = (editor) => {
        const model = getModel(editor);
        return isNonNullable(ModelManager.get(model));
      };
      const loadScripts = (editor, suffix) => {
        const scriptLoader = ScriptLoader.ScriptLoader;
        const initEditor2 = () => {
          if (!editor.removed && isThemeLoaded(editor) && isModelLoaded(editor)) {
            init(editor);
          }
        };
        loadTheme(editor, suffix);
        loadModel(editor, suffix);
        loadLicenseKeyManager(editor, suffix);
        loadLanguage(scriptLoader, editor);
        loadIcons(scriptLoader, editor, suffix);
        loadPlugins(editor, suffix);
        scriptLoader.loadQueue().then(initEditor2, initEditor2);
      };
      const getStyleSheetLoader = (element, editor) => instance.forElement(element, {
        contentCssCors: hasContentCssCors(editor),
        referrerPolicy: getReferrerPolicy(editor)
      });
      const render = (editor) => {
        const id = editor.id;
        I18n.setCode(getLanguageCode(editor));
        const readyHandler = () => {
          DOM$3.unbind(window, "ready", readyHandler);
          editor.render();
        };
        if (!EventUtils.Event.domLoaded) {
          DOM$3.bind(window, "ready", readyHandler);
          return;
        }
        if (!editor.getElement()) {
          return;
        }
        const element = SugarElement.fromDom(editor.getElement());
        const snapshot = clone$4(element);
        editor.on("remove", () => {
          eachr(element.dom.attributes, (attr) => remove$9(element, attr.name));
          setAll$1(element, snapshot);
        });
        editor.ui.styleSheetLoader = getStyleSheetLoader(element, editor);
        if (!isInline$2(editor)) {
          editor.orgVisibility = editor.getElement().style.visibility;
          editor.getElement().style.visibility = "hidden";
        } else {
          editor.inline = true;
        }
        const form = editor.getElement().form || DOM$3.getParent(id, "form");
        if (form) {
          editor.formElement = form;
          if (hasHiddenInput(editor) && !isTextareaOrInput(editor.getElement())) {
            DOM$3.insertAfter(DOM$3.create("input", { type: "hidden", name: id }), id);
            editor.hasHiddenInput = true;
          }
          editor.formEventDelegate = (e) => {
            editor.dispatch(e.type, e);
          };
          DOM$3.bind(form, "submit reset", editor.formEventDelegate);
          editor.on("reset", () => {
            editor.resetContent();
          });
          if (shouldPatchSubmit(editor) && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
            form._mceOldSubmit = form.submit;
            form.submit = () => {
              editor.editorManager.triggerSave();
              editor.setDirty(false);
              return form._mceOldSubmit(form);
            };
          }
        }
        editor.windowManager = WindowManager(editor);
        editor.notificationManager = NotificationManager(editor);
        if (isEncodingXml(editor)) {
          editor.on("GetContent", (e) => {
            if (e.save) {
              e.content = DOM$3.encode(e.content);
            }
          });
        }
        if (shouldAddFormSubmitTrigger(editor)) {
          editor.on("submit", () => {
            if (editor.initialized) {
              editor.save();
            }
          });
        }
        if (shouldAddUnloadTrigger(editor)) {
          editor._beforeUnload = () => {
            if (editor.initialized && !editor.destroyed && !editor.isHidden()) {
              editor.save({ format: "raw", no_events: true, set_dirty: false });
            }
          };
          editor.editorManager.on("BeforeUnload", editor._beforeUnload);
        }
        editor.editorManager.add(editor);
        loadScripts(editor, editor.suffix);
      };
      const AvatarColors = [
        "#E41B60",
        // Pink
        "#AD1457",
        // Dark Pink
        "#1939EC",
        // Indigo
        "#001CB5",
        // Dark Indigo
        "#648000",
        // Lime
        "#465B00",
        // Dark Lime
        "#006CE7",
        // Blue
        "#0054B4",
        // Dark Blue
        "#00838F",
        // Cyan
        "#006064",
        // Dark Cyan
        "#00866F",
        // Turquoise
        "#004D40",
        // Dark Turquoise
        "#51742F",
        // Green
        "#385021",
        // Dark Green
        "#CF4900",
        // Orange
        "#A84600",
        // Dark Orange
        "#CC0000",
        // Red
        "#6A1B9A",
        // Dark Red
        "#9C27B0",
        // Purple
        "#6A00AB",
        // Dark Purple
        "#3041BA",
        // Navy Blue
        "#0A1877",
        // Dark Navy Blue
        "#774433",
        // Brown
        "#452B24",
        // Dark Brown
        "#607D8B",
        // Blue Gray
        "#455A64"
        // Dark Blue Gray
      ];
      const getFirstChar = (name2) => {
        if (Intl.Segmenter) {
          const segmenter = new Intl.Segmenter();
          const iterator = segmenter.segment(name2)[Symbol.iterator]();
          return `${iterator.next().value?.segment}`;
        } else {
          return name2.trim()[0];
        }
      };
      const djb2Hash = (key, maxValue) => {
        let hash = 5381;
        for (let i = 0; i < key.length; i++) {
          hash = (hash << 5) + hash + key.charCodeAt(i);
        }
        return (hash >>> 0) % (maxValue + 1);
      };
      const getColor = (id) => {
        const colorIdx = djb2Hash(id ?? "", AvatarColors.length - 1);
        return AvatarColors[colorIdx];
      };
      const generateAvatarSvg = (content, color, size) => {
        const halfSize = size / 2;
        return `<svg height="${size}" width="${size}" xmlns="http://www.w3.org/2000/svg"><circle cx="${halfSize}" cy="${halfSize}" r="${halfSize}" fill="${color}"/><text x="50%" y="50%" text-anchor="middle" dominant-baseline="central" fill="#FFF" font-family="sans-serif" font-size="${halfSize}">` + content + `</text></svg>`;
      };
      const generateAvatar = (content, color, size) => "data:image/svg+xml," + encodeURIComponent(generateAvatarSvg(content, color, size));
      const generateUserAvatar = (user, config = { size: 36 }) => generateAvatar(getFirstChar(user.name), getColor(user.id), config.size);
      const userSchema = objOf([
        required("id"),
        optionString("name"),
        optionString("avatar"),
        option("custom")
      ]);
      const objectCat = (obj) => {
        const result = {};
        each$d(obj, (value2, key) => {
          value2.each((v) => {
            result[key] = v;
          });
        });
        return result;
      };
      const validateResponse = (items) => {
        if (!Array.isArray(items)) {
          throw new Error("fetch_users must return an array");
        }
        const results = map$3(items, (item) => asRaw("Invalid user object", userSchema, item));
        const { errors, values: values2 } = partition$1(results);
        if (errors.length > 0) {
          const formattedErrors = map$3(errors, (error2, idx) => `User at index ${idx}: ${formatError(error2)}`);
          console.warn("User validation errors:\n" + formattedErrors.join("\n"));
        }
        return map$3(values2, (user) => {
          const { id, name: name2, avatar, ...rest } = user;
          return {
            id,
            name: name2.getOr(id),
            avatar: avatar.getOr(generateUserAvatar({ id, name: name2.getOr(id) })),
            ...objectCat(rest)
          };
        });
      };
      const UserLookup = (editor) => {
        const userCache = /* @__PURE__ */ new Map();
        const pendingResolvers = /* @__PURE__ */ new Map();
        const lookup2 = (userId2) => Optional.from(userCache.get(userId2));
        const store2 = (user, userId2) => {
          userCache.set(userId2, user);
        };
        const finallyReject = (userId2, error2) => Optional.from(pendingResolvers.get(userId2)).each(({ reject }) => {
          reject(error2);
          pendingResolvers.delete(userId2);
        });
        const finallyResolve = (userId2, user) => Optional.from(pendingResolvers.get(userId2)).each(({ resolve: resolve2 }) => {
          resolve2(user);
          pendingResolvers.delete(userId2);
        });
        const fetchUsers = (userIds) => {
          const fetchUsersFn = getFetchUsers(editor);
          if (!Array.isArray(userIds)) {
            return {};
          } else if (!fetchUsersFn) {
            return mapToObject(userIds, (userId2) => Promise.resolve({
              id: userId2,
              name: userId2,
              avatar: generateUserAvatar({ id: userId2, name: userId2 })
            }));
          }
          const uncachedIds = unique$1(filter$5(userIds, (userId2) => !lookup2(userId2).isSome()));
          each$e(uncachedIds, (userId2) => {
            const newPromise = new Promise((resolve2, reject) => {
              pendingResolvers.set(userId2, { resolve: resolve2, reject });
            });
            store2(newPromise, userId2);
          });
          if (uncachedIds.length > 0) {
            fetchUsersFn(uncachedIds).then(validateResponse).then((users) => {
              const foundUserIds = new Set(map$3(users, (user) => user.id));
              each$e(users, (user) => finallyResolve(user.id, user));
              each$e(uncachedIds, (userId2) => {
                if (!foundUserIds.has(userId2)) {
                  finallyReject(userId2, new Error(`User ${userId2} not found`));
                }
              });
            }).catch((error2) => {
              each$e(uncachedIds, (userId2) => finallyReject(userId2, error2 instanceof Error ? error2 : new Error("Network error")));
            });
          }
          return foldl(userIds, (acc, userId2) => {
            acc[userId2] = lookup2(userId2).getOr(Promise.resolve({
              id: userId2,
              name: userId2,
              avatar: generateUserAvatar({ id: userId2, name: userId2 })
            }));
            return acc;
          }, {});
        };
        const userId = getUserId(editor);
        return Object.freeze({
          userId,
          fetchUsers
        });
      };
      const createUserLookup = (editor) => UserLookup(editor);
      const setEditableRoot = (editor, state) => {
        if (editor._editableRoot !== state) {
          editor._editableRoot = state;
          if (!isDisabled(editor)) {
            editor.getBody().contentEditable = String(editor.hasEditableRoot());
            editor.nodeChanged();
          }
          fireEditableRootStateChange(editor, state);
        }
      };
      const hasEditableRoot = (editor) => editor._editableRoot;
      const sectionResult = (sections, settings) => ({
        sections: constant(sections),
        options: constant(settings)
      });
      const deviceDetection = detect$1().deviceType;
      const isPhone = deviceDetection.isPhone();
      const isTablet = deviceDetection.isTablet();
      const normalizePlugins = (plugins) => {
        if (isNullable(plugins)) {
          return [];
        } else {
          const pluginNames = isArray$1(plugins) ? plugins : plugins.split(/[ ,]/);
          const trimmedPlugins = map$3(pluginNames, trim$4);
          return filter$5(trimmedPlugins, isNotEmpty);
        }
      };
      const extractSections = (keys2, options) => {
        const result = bifilter(options, (value2, key) => {
          return contains$2(keys2, key);
        });
        return sectionResult(result.t, result.f);
      };
      const getSection = (sectionResult2, name2, defaults = {}) => {
        const sections = sectionResult2.sections();
        const sectionOptions = get$a(sections, name2).getOr({});
        return Tools.extend({}, defaults, sectionOptions);
      };
      const hasSection = (sectionResult2, name2) => {
        return has$2(sectionResult2.sections(), name2);
      };
      const getSectionConfig = (sectionResult2, name2) => {
        return hasSection(sectionResult2, name2) ? sectionResult2.sections()[name2] : {};
      };
      const getMobileOverrideOptions = (mobileOptions, isPhone2) => {
        const defaultMobileOptions = {
          table_grid: false,
          // Table grid relies on hover, which isn't available for touch devices so use the dialog instead
          object_resizing: false,
          // No nice way to do object resizing at this stage
          resize: false,
          // Editor resize doesn't make sense on mobile
          toolbar_mode: get$a(mobileOptions, "toolbar_mode").getOr("scrolling"),
          // Use the default side-scrolling toolbar for tablets/phones
          toolbar_sticky: false
          // Only enable sticky toolbar on desktop by default
        };
        const defaultPhoneOptions = {
          menubar: false
          // Phones don't have a lot of screen space, so disable the menubar
        };
        return {
          ...defaultMobileOptions,
          ...isPhone2 ? defaultPhoneOptions : {}
        };
      };
      const getExternalPlugins = (overrideOptions, options) => {
        const userDefinedExternalPlugins = options.external_plugins ?? {};
        if (overrideOptions && overrideOptions.external_plugins) {
          return Tools.extend({}, overrideOptions.external_plugins, userDefinedExternalPlugins);
        } else {
          return userDefinedExternalPlugins;
        }
      };
      const combinePlugins = (forcedPlugins, plugins) => [
        ...normalizePlugins(forcedPlugins),
        ...normalizePlugins(plugins)
      ];
      const getPlatformPlugins = (isMobileDevice, sectionResult2, desktopPlugins, mobilePlugins) => {
        if (isMobileDevice && hasSection(sectionResult2, "mobile")) {
          return mobilePlugins;
        } else {
          return desktopPlugins;
        }
      };
      const processPlugins = (isMobileDevice, sectionResult2, defaultOverrideOptions, options) => {
        const forcedPlugins = normalizePlugins(defaultOverrideOptions.forced_plugins);
        const desktopPlugins = normalizePlugins(options.plugins);
        const mobileConfig = getSectionConfig(sectionResult2, "mobile");
        const mobilePlugins = mobileConfig.plugins ? normalizePlugins(mobileConfig.plugins) : desktopPlugins;
        const platformPlugins = getPlatformPlugins(isMobileDevice, sectionResult2, desktopPlugins, mobilePlugins);
        const combinedPlugins = combinePlugins(forcedPlugins, platformPlugins);
        return Tools.extend(options, {
          forced_plugins: forcedPlugins,
          plugins: combinedPlugins
        });
      };
      const isOnMobile = (isMobileDevice, sectionResult2) => {
        return isMobileDevice && hasSection(sectionResult2, "mobile");
      };
      const combineOptions = (isMobileDevice, isPhone2, defaultOptions, defaultOverrideOptions, options) => {
        const deviceOverrideOptions = isMobileDevice ? { mobile: getMobileOverrideOptions(options.mobile ?? {}, isPhone2) } : {};
        const sectionResult2 = extractSections(["mobile"], deepMerge(deviceOverrideOptions, options));
        const extendedOptions = Tools.extend(
          // Default options
          defaultOptions,
          // tinymce.overrideOptions options
          defaultOverrideOptions,
          // User options
          sectionResult2.options(),
          // Sections
          isOnMobile(isMobileDevice, sectionResult2) ? getSection(sectionResult2, "mobile") : {},
          // Forced options
          {
            external_plugins: getExternalPlugins(defaultOverrideOptions, sectionResult2.options())
          }
        );
        return processPlugins(isMobileDevice, sectionResult2, defaultOverrideOptions, extendedOptions);
      };
      const normalizeOptions = (defaultOverrideOptions, options) => {
        const copiedOptions = merge$1(options);
        return combineOptions(isPhone || isTablet, isPhone, copiedOptions, defaultOverrideOptions, copiedOptions);
      };
      const addVisual = (editor, elm) => addVisual$1(editor, elm);
      const registerExecCommands$2 = (editor) => {
        const toggleFormat2 = (name2, value2) => {
          editor.formatter.toggle(name2, value2);
          editor.nodeChanged();
        };
        const toggleAlign = (align) => () => {
          each$e("left,center,right,justify".split(","), (name2) => {
            if (align !== name2) {
              editor.formatter.remove("align" + name2);
            }
          });
          if (align !== "none") {
            toggleFormat2("align" + align);
          }
        };
        editor.editorCommands.addCommands({
          JustifyLeft: toggleAlign("left"),
          JustifyCenter: toggleAlign("center"),
          JustifyRight: toggleAlign("right"),
          JustifyFull: toggleAlign("justify"),
          JustifyNone: toggleAlign("none")
        });
      };
      const registerQueryStateCommands = (editor) => {
        const alignStates = (name2) => () => {
          const selection = editor.selection;
          const nodes = selection.isCollapsed() ? [editor.dom.getParent(selection.getNode(), editor.dom.isBlock)] : selection.getSelectedBlocks();
          return exists(nodes, (node) => isNonNullable(editor.formatter.matchNode(node, name2)));
        };
        editor.editorCommands.addCommands({
          JustifyLeft: alignStates("alignleft"),
          JustifyCenter: alignStates("aligncenter"),
          JustifyRight: alignStates("alignright"),
          JustifyFull: alignStates("alignjustify")
        }, "state");
      };
      const registerCommands$a = (editor) => {
        registerExecCommands$2(editor);
        registerQueryStateCommands(editor);
      };
      const registerCommands$9 = (editor) => {
        editor.editorCommands.addCommands({
          "Cut,Copy,Paste": (command) => {
            const doc = editor.getDoc();
            let failed;
            try {
              doc.execCommand(command);
            } catch {
              failed = true;
            }
            if (command === "paste" && !doc.queryCommandEnabled(command)) {
              failed = true;
            }
            if (failed || !doc.queryCommandSupported(command)) {
              let msg = editor.translate(`Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.`);
              if (Env.os.isMacOS() || Env.os.isiOS()) {
                msg = msg.replace(/Ctrl\+/g, "⌘+");
              }
              editor.notificationManager.open({ text: msg, type: "error" });
            }
          }
        });
      };
      const trimOrPadLeftRight = (dom2, rng, html2, schema) => {
        const root = SugarElement.fromDom(dom2.getRoot());
        if (needsToBeNbspLeft(root, CaretPosition.fromRangeStart(rng), schema)) {
          html2 = html2.replace(/^ /, "&nbsp;");
        } else {
          html2 = html2.replace(/^&nbsp;/, " ");
        }
        if (needsToBeNbspRight(root, CaretPosition.fromRangeEnd(rng), schema)) {
          html2 = html2.replace(/(&nbsp;| )(<br( \/)>)?$/, "&nbsp;");
        } else {
          html2 = html2.replace(/&nbsp;(<br( \/)?>)?$/, " ");
        }
        return html2;
      };
      const processValue$1 = (value2) => {
        if (typeof value2 !== "string") {
          const details = Tools.extend({
            paste: value2.paste,
            data: {
              paste: value2.paste
            }
          }, value2);
          return {
            content: value2.content,
            details
          };
        }
        return {
          content: value2,
          details: {}
        };
      };
      const trimOrPad = (editor, value2) => {
        const selection = editor.selection;
        const dom2 = editor.dom;
        if (/^ | $/.test(value2)) {
          return trimOrPadLeftRight(dom2, selection.getRng(), value2, editor.schema);
        } else {
          return value2;
        }
      };
      const insertAtCaret = (editor, value2) => {
        if (editor.selection.isEditable()) {
          const { content, details } = processValue$1(value2);
          preProcessSetContent(editor, { ...details, content: trimOrPad(editor, content), format: "html", set: false, selection: true }).each((args) => {
            const insertedContent = insertContent$1(editor, args.content, details);
            postProcessSetContent(editor, insertedContent, args);
            editor.addVisual();
          });
        }
      };
      const registerCommands$8 = (editor) => {
        editor.editorCommands.addCommands({
          mceCleanup: () => {
            const bm = editor.selection.getBookmark();
            editor.setContent(editor.getContent());
            editor.selection.moveToBookmark(bm);
          },
          insertImage: (_command, _ui, value2) => {
            insertAtCaret(editor, editor.dom.createHTML("img", { src: value2 }));
          },
          insertHorizontalRule: () => {
            editor.execCommand("mceInsertContent", false, "<hr>");
          },
          insertText: (_command, _ui, value2) => {
            insertAtCaret(editor, editor.dom.encode(value2));
          },
          insertHTML: (_command, _ui, value2) => {
            insertAtCaret(editor, value2);
          },
          mceInsertContent: (_command, _ui, value2) => {
            insertAtCaret(editor, value2);
          },
          mceSetContent: (_command, _ui, value2) => {
            editor.setContent(value2);
          },
          mceReplaceContent: (_command, _ui, value2) => {
            editor.execCommand("mceInsertContent", false, value2.replace(/\{\$selection\}/g, editor.selection.getContent({ format: "text" })));
          },
          mceNewDocument: () => {
            editor.setContent(getNewDocumentContent(editor));
          }
        });
      };
      const legacyPropNames = {
        "font-size": "size",
        "font-family": "face"
      };
      const isFont = isTag("font");
      const getSpecifiedFontProp = (propName, rootElm, elm) => {
        const getProperty = (elm2) => getRaw$1(elm2, propName).orThunk(() => {
          if (isFont(elm2)) {
            return get$a(legacyPropNames, propName).bind((legacyPropName) => getOpt(elm2, legacyPropName));
          } else {
            return Optional.none();
          }
        });
        const isRoot2 = (elm2) => eq(SugarElement.fromDom(rootElm), elm2);
        return closest$1(SugarElement.fromDom(elm), (elm2) => getProperty(elm2), isRoot2);
      };
      const normalizeFontFamily = (fontFamily) => (
        // 'Font name', Font -> Font name,Font
        fontFamily.replace(/[\'\"\\]/g, "").replace(/,\s+/g, ",")
      );
      const getComputedFontProp = (propName, elm) => Optional.from(DOMUtils.DOM.getStyle(elm, propName, true));
      const getFontProp = (propName) => (rootElm, elm) => Optional.from(elm).map(SugarElement.fromDom).filter(isElement$8).bind((element) => getSpecifiedFontProp(propName, rootElm, element.dom).or(getComputedFontProp(propName, element.dom))).getOr("");
      const getFontSize = getFontProp("font-size");
      const getFontFamily = compose(normalizeFontFamily, getFontProp("font-family"));
      const findFirstCaretElement = (editor) => firstPositionIn(editor.getBody()).bind((caret) => {
        const container = caret.container();
        return Optional.from(isText$b(container) ? container.parentNode : container);
      });
      const getCaretElement = (editor) => Optional.from(editor.selection.getRng()).bind((rng) => {
        const root = editor.getBody();
        const atStartOfNode = rng.startContainer === root && rng.startOffset === 0;
        return atStartOfNode ? Optional.none() : Optional.from(editor.selection.getStart(true));
      });
      const bindRange = (editor, binder2) => getCaretElement(editor).orThunk(curry(findFirstCaretElement, editor)).map(SugarElement.fromDom).filter(isElement$8).bind(binder2);
      const mapRange = (editor, mapper) => bindRange(editor, compose1(Optional.some, mapper));
      const fromFontSizeNumber = (editor, value2) => {
        if (/^[0-9.]+$/.test(value2)) {
          const fontSizeNumber = parseInt(value2, 10);
          if (fontSizeNumber >= 1 && fontSizeNumber <= 7) {
            const fontSizes = getFontStyleValues(editor);
            const fontClasses = getFontSizeClasses(editor);
            if (fontClasses.length > 0) {
              return fontClasses[fontSizeNumber - 1] || value2;
            } else {
              return fontSizes[fontSizeNumber - 1] || value2;
            }
          } else {
            return value2;
          }
        } else {
          return value2;
        }
      };
      const normalizeFontNames = (font) => {
        const fonts = font.split(/\s*,\s*/);
        return map$3(fonts, (font2) => {
          if (font2.indexOf(" ") !== -1 && !(startsWith(font2, '"') || startsWith(font2, `'`))) {
            return `'${font2}'`;
          } else {
            return font2;
          }
        }).join(",");
      };
      const fontNameAction = (editor, value2) => {
        const font = fromFontSizeNumber(editor, value2);
        editor.formatter.toggle("fontname", { value: normalizeFontNames(font) });
        editor.nodeChanged();
      };
      const fontNameQuery = (editor) => mapRange(editor, (elm) => getFontFamily(editor.getBody(), elm.dom)).getOr("");
      const fontSizeAction = (editor, value2) => {
        editor.formatter.toggle("fontsize", { value: fromFontSizeNumber(editor, value2) });
        editor.nodeChanged();
      };
      const fontSizeQuery = (editor) => mapRange(editor, (elm) => getFontSize(editor.getBody(), elm.dom)).getOr("");
      const lineHeightQuery = (editor) => mapRange(editor, (elm) => {
        const root = SugarElement.fromDom(editor.getBody());
        const specifiedStyle = closest$1(elm, (elm2) => getRaw$1(elm2, "line-height"), curry(eq, root));
        const computedStyle = () => {
          const lineHeight = parseFloat(get$7(elm, "line-height"));
          const fontSize = parseFloat(get$7(elm, "font-size"));
          return String(lineHeight / fontSize);
        };
        return specifiedStyle.getOrThunk(computedStyle);
      }).getOr("");
      const lineHeightAction = (editor, lineHeight) => {
        editor.formatter.toggle("lineheight", { value: String(lineHeight) });
        editor.nodeChanged();
      };
      const registerExecCommands$1 = (editor) => {
        const toggleFormat2 = (name2, value2) => {
          editor.formatter.toggle(name2, value2);
          editor.nodeChanged();
        };
        editor.editorCommands.addCommands({
          "Bold,Italic,Underline,Strikethrough,Superscript,Subscript": (command) => {
            toggleFormat2(command);
          },
          "ForeColor,HiliteColor": (command, _ui, value2) => {
            toggleFormat2(command, { value: value2 });
          },
          "BackColor": (_command, _ui, value2) => {
            toggleFormat2("hilitecolor", { value: value2 });
          },
          "FontName": (_command, _ui, value2) => {
            fontNameAction(editor, value2);
          },
          "FontSize": (_command, _ui, value2) => {
            fontSizeAction(editor, value2);
          },
          "LineHeight": (_command, _ui, value2) => {
            lineHeightAction(editor, value2);
          },
          "Lang": (command, _ui, lang) => {
            toggleFormat2(command, { value: lang.code, customValue: lang.customCode ?? null });
          },
          "RemoveFormat": (command) => {
            editor.formatter.remove(command);
          },
          "mceBlockQuote": () => {
            toggleFormat2("blockquote");
          },
          "FormatBlock": (_command, _ui, value2) => {
            toggleFormat2(isString(value2) ? value2 : "p");
          },
          "mceToggleFormat": (_command, _ui, value2) => {
            toggleFormat2(value2);
          }
        });
      };
      const registerQueryValueCommands = (editor) => {
        const isFormatMatch = (name2) => editor.formatter.match(name2);
        editor.editorCommands.addCommands({
          "Bold,Italic,Underline,Strikethrough,Superscript,Subscript": (command) => isFormatMatch(command),
          "mceBlockQuote": () => isFormatMatch("blockquote")
        }, "state");
        editor.editorCommands.addQueryValueHandler("FontName", () => fontNameQuery(editor));
        editor.editorCommands.addQueryValueHandler("FontSize", () => fontSizeQuery(editor));
        editor.editorCommands.addQueryValueHandler("LineHeight", () => lineHeightQuery(editor));
      };
      const registerCommands$7 = (editor) => {
        registerExecCommands$1(editor);
        registerQueryValueCommands(editor);
      };
      const registerCommands$6 = (editor) => {
        editor.editorCommands.addCommands({
          mceAddUndoLevel: () => {
            editor.undoManager.add();
          },
          mceEndUndoLevel: () => {
            editor.undoManager.add();
          },
          Undo: () => {
            editor.undoManager.undo();
          },
          Redo: () => {
            editor.undoManager.redo();
          }
        });
      };
      const registerCommands$5 = (editor) => {
        editor.editorCommands.addCommands({
          Indent: () => {
            indent(editor);
          },
          Outdent: () => {
            outdent(editor);
          }
        });
        editor.editorCommands.addCommands({
          Outdent: () => canOutdent(editor),
          Indent: () => canIndent(editor)
        }, "state");
      };
      const registerCommands$4 = (editor) => {
        const applyLinkToSelection = (_command, _ui, value2) => {
          if (editor.mode.isReadOnly()) {
            return;
          }
          const linkDetails = isString(value2) ? { href: value2 } : value2;
          const anchor = editor.dom.getParent(editor.selection.getNode(), "a");
          if (isObject(linkDetails) && isString(linkDetails.href)) {
            linkDetails.href = linkDetails.href.replace(/ /g, "%20").replace(/&amp;/g, "&");
            if (!anchor || !linkDetails.href) {
              editor.formatter.remove("link");
            }
            if (linkDetails.href) {
              editor.formatter.apply("link", linkDetails, anchor);
            }
          }
        };
        editor.editorCommands.addCommands({
          unlink: () => {
            if (editor.selection.isEditable()) {
              if (editor.selection.isCollapsed()) {
                const elm = editor.dom.getParent(editor.selection.getStart(), "a");
                if (elm) {
                  editor.dom.remove(elm, true);
                }
                return;
              }
              editor.formatter.remove("link");
            }
          },
          mceInsertLink: applyLinkToSelection,
          createLink: applyLinkToSelection
        });
      };
      const getTopParentBlock = (editor, node, root, container) => {
        const dom2 = editor.dom;
        const selector = (node2) => dom2.isBlock(node2) && node2.parentElement === root;
        const topParentBlock = selector(node) ? node : dom2.getParent(container, selector, root);
        return Optional.from(topParentBlock).map(SugarElement.fromDom);
      };
      const insert = (editor, before2) => {
        if (editor.mode.isReadOnly()) {
          return;
        }
        const dom2 = editor.dom;
        const rng = editor.selection.getRng();
        const node = before2 ? editor.selection.getStart() : editor.selection.getEnd();
        const container = before2 ? rng.startContainer : rng.endContainer;
        const root = getEditableRoot(dom2, container);
        if (!root || !root.isContentEditable) {
          return;
        }
        const insertFn = before2 ? before$4 : after$4;
        const newBlockName = getForcedRootBlock(editor);
        getTopParentBlock(editor, node, root, container).each((parentBlock) => {
          const newBlock = createNewBlock(editor, container, parentBlock.dom, root, false, newBlockName);
          insertFn(parentBlock, SugarElement.fromDom(newBlock));
          editor.selection.setCursorLocation(newBlock, 0);
          editor.dispatch("NewBlock", { newBlock });
          fireInputEvent(editor, "insertParagraph");
        });
      };
      const insertBefore = (editor) => insert(editor, true);
      const insertAfter = (editor) => insert(editor, false);
      const registerCommands$3 = (editor) => {
        editor.editorCommands.addCommands({
          InsertNewBlockBefore: () => {
            insertBefore(editor);
          },
          InsertNewBlockAfter: () => {
            insertAfter(editor);
          }
        });
      };
      const registerCommands$2 = (editor) => {
        editor.editorCommands.addCommands({
          insertParagraph: () => {
            insertBreak(blockbreak, editor);
          },
          mceInsertNewLine: (_command, _ui, value2) => {
            insert$1(editor, value2);
          },
          InsertLineBreak: (_command, _ui, _value) => {
            insertBreak(linebreak, editor);
          }
        });
      };
      const registerCommands$1 = (editor) => {
        editor.editorCommands.addCommands({
          mceSelectNodeDepth: (_command, _ui, value2) => {
            let counter = 0;
            editor.dom.getParent(editor.selection.getNode(), (node) => {
              if (isElement$7(node) && counter++ === value2) {
                editor.selection.select(node);
                return false;
              } else {
                return true;
              }
            }, editor.getBody());
          },
          mceSelectNode: (_command, _ui, value2) => {
            editor.selection.select(value2);
          },
          selectAll: () => {
            const editingHost = editor.dom.getParent(editor.selection.getStart(), isContentEditableTrue$3);
            if (editingHost) {
              const rng = editor.dom.createRng();
              rng.selectNodeContents(editingHost);
              editor.selection.setRng(rng);
            }
          }
        });
      };
      const registerExecCommands = (editor) => {
        editor.editorCommands.addCommands({
          mceRemoveNode: (_command, _ui, value2) => {
            const node = value2 ?? editor.selection.getNode();
            if (node !== editor.getBody()) {
              const bm = editor.selection.getBookmark();
              editor.dom.remove(node, true);
              editor.selection.moveToBookmark(bm);
            }
          },
          mcePrint: () => {
            editor.getWin().print();
          },
          mceFocus: (_command, _ui, value2) => {
            focus(editor, value2 === true);
          },
          mceToggleVisualAid: () => {
            editor.hasVisual = !editor.hasVisual;
            editor.addVisual();
          }
        });
      };
      const registerCommands = (editor) => {
        registerCommands$a(editor);
        registerCommands$9(editor);
        registerCommands$6(editor);
        registerCommands$1(editor);
        registerCommands$8(editor);
        registerCommands$4(editor);
        registerCommands$5(editor);
        registerCommands$3(editor);
        registerCommands$2(editor);
        registerCommands$7(editor);
        registerExecCommands(editor);
      };
      const selectionSafeCommands = ["toggleview"];
      const isSelectionSafeCommand = (command) => contains$2(selectionSafeCommands, command.toLowerCase());
      class EditorCommands {
        editor;
        commands = { state: {}, exec: {}, value: {} };
        constructor(editor) {
          this.editor = editor;
        }
        /**
         * Executes a registered command on the current instance. A list of available commands can be found in
         * the tinymce command identifiers documentation.
         *
         * @method execCommand
         * @param {String} command Command name to execute, for example mceLink or Bold.
         * @param {Boolean} ui Specifies if a UI (dialog) should be presented or not.
         * @param {Object/Array/String/Number/Boolean} value Optional command value, this can be anything.
         * @param {Object} args Optional arguments object.
         * @return {Boolean} true or false if the command was supported or not.
         */
        execCommand(command, ui = false, value2, args) {
          const editor = this.editor;
          const lowerCaseCommand = command.toLowerCase();
          const skipFocus = args?.skip_focus;
          if (editor.removed) {
            return false;
          }
          if (lowerCaseCommand !== "mcefocus") {
            if (!/^(mceAddUndoLevel|mceEndUndoLevel)$/i.test(lowerCaseCommand) && !skipFocus) {
              editor.focus();
            } else {
              restore(editor);
            }
          }
          const eventArgs = editor.dispatch("BeforeExecCommand", { command, ui, value: value2 });
          if (eventArgs.isDefaultPrevented()) {
            return false;
          }
          const func = this.commands.exec[lowerCaseCommand];
          if (isFunction(func)) {
            func(lowerCaseCommand, ui, value2, args);
            editor.dispatch("ExecCommand", { command, ui, value: value2, args });
            return true;
          }
          return false;
        }
        /**
         * Queries the current state for a command. For example: If the current selection is "bold".
         *
         * @method queryCommandState
         * @param {String} command Command to check the state of.
         * @return {Boolean} true/false - For example: If the selected contents is bold or not.
         */
        queryCommandState(command) {
          if (!isSelectionSafeCommand(command) && this.editor.quirks.isHidden() || this.editor.removed) {
            return false;
          }
          const lowerCaseCommand = command.toLowerCase();
          const func = this.commands.state[lowerCaseCommand];
          if (isFunction(func)) {
            return func(lowerCaseCommand);
          }
          return false;
        }
        /**
         * Returns a command specific value, for example the current font size.
         *
         * @method queryCommandValue
         * @param {String} command Command to query value from.
         * @return {String} Command value, for example the current font size or an empty string (`""`) if the query command is not found.
         */
        queryCommandValue(command) {
          if (!isSelectionSafeCommand(command) && this.editor.quirks.isHidden() || this.editor.removed) {
            return "";
          }
          const lowerCaseCommand = command.toLowerCase();
          const func = this.commands.value[lowerCaseCommand];
          if (isFunction(func)) {
            return func(lowerCaseCommand);
          }
          return "";
        }
        addCommands(commandList, type2 = "exec") {
          const commands = this.commands;
          each$d(commandList, (callback, command) => {
            each$e(command.toLowerCase().split(","), (command2) => {
              commands[type2][command2] = callback;
            });
          });
        }
        addCommand(command, callback, scope) {
          const lowerCaseCommand = command.toLowerCase();
          this.commands.exec[lowerCaseCommand] = (_command, ui, value2, args) => callback.call(scope ?? this.editor, ui, value2, args);
        }
        /**
         * Returns true/false if the command is supported or not.
         *
         * @method queryCommandSupported
         * @param {String} command Command that we check support for.
         * @return {Boolean} true/false if the command is supported or not.
         */
        queryCommandSupported(command) {
          const lowerCaseCommand = command.toLowerCase();
          if (this.commands.exec[lowerCaseCommand]) {
            return true;
          } else {
            return false;
          }
        }
        addQueryStateHandler(command, callback, scope) {
          this.commands.state[command.toLowerCase()] = () => callback.call(scope ?? this.editor);
        }
        addQueryValueHandler(command, callback, scope) {
          this.commands.value[command.toLowerCase()] = () => callback.call(scope ?? this.editor);
        }
      }
      const nativeEvents = Tools.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input beforeinput contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit compositionstart compositionend compositionupdate touchstart touchmove touchend touchcancel", " ");
      class EventDispatcher {
        /**
         * Returns true/false if the specified event name is a native browser event or not.
         *
         * @method isNative
         * @param {String} name Name to check if it's native.
         * @return {Boolean} true/false if the event is native or not.
         * @static
         */
        static isNative(name2) {
          return !!nativeEvents[name2.toLowerCase()];
        }
        settings;
        scope;
        toggleEvent;
        bindings = {};
        constructor(settings) {
          this.settings = settings || {};
          this.scope = this.settings.scope || this;
          this.toggleEvent = this.settings.toggleEvent || never;
        }
        /**
         * Fires the specified event by name.
         * <br>
         * <em>Marked for removal in TinyMCE 8.0. Use <code>dispatch</code> instead.</em>
         *
         * @method fire
         * @param {String} name Name of the event to fire.
         * @param {Object?} args Event arguments.
         * @return {Object} Event args instance passed in.
         * @deprecated Use dispatch() instead
         * @example
         * instance.fire('event', {...});
         */
        fire(name2, args) {
          return this.dispatch(name2, args);
        }
        /**
         * Dispatches the specified event by name.
         *
         * @method dispatch
         * @param {String} name Name of the event to dispatch
         * @param {Object?} args Event arguments.
         * @return {Object} Event args instance passed in.
         * @example
         * instance.dispatch('event', {...});
         */
        dispatch(name2, args) {
          const lcName = name2.toLowerCase();
          const event = normalize$3(lcName, args ?? {}, this.scope);
          if (this.settings.beforeFire) {
            this.settings.beforeFire(event);
          }
          const handlers = this.bindings[lcName];
          if (handlers) {
            for (let i = 0, l = handlers.length; i < l; i++) {
              const callback = handlers[i];
              if (callback.removed) {
                continue;
              }
              if (callback.once) {
                this.off(lcName, callback.func);
              }
              if (event.isImmediatePropagationStopped()) {
                return event;
              }
              if (callback.func.call(this.scope, event) === false) {
                event.preventDefault();
                return event;
              }
            }
          }
          return event;
        }
        /**
         * Binds an event listener to a specific event by name.
         *
         * @method on
         * @param {String} name Event name or space separated list of events to bind.
         * @param {Function} callback Callback to be executed when the event occurs.
         * @param {Boolean} prepend Optional flag if the event should be prepended. Use this with care.
         * @return {Object} Current class instance.
         * @example
         * instance.on('event', (e) => {
         *   // Callback logic
         * });
         */
        on(name2, callback, prepend2, extra2) {
          if (callback === false) {
            callback = never;
          }
          if (callback) {
            const wrappedCallback = {
              func: callback,
              removed: false
            };
            if (extra2) {
              Tools.extend(wrappedCallback, extra2);
            }
            const names = name2.toLowerCase().split(" ");
            let i = names.length;
            while (i--) {
              const currentName = names[i];
              let handlers = this.bindings[currentName];
              if (!handlers) {
                handlers = [];
                this.toggleEvent(currentName, true);
              }
              if (prepend2) {
                handlers = [wrappedCallback, ...handlers];
              } else {
                handlers = [...handlers, wrappedCallback];
              }
              this.bindings[currentName] = handlers;
            }
          }
          return this;
        }
        /**
         * Unbinds an event listener to a specific event by name.
         *
         * @method off
         * @param {String?} name Name of the event to unbind.
         * @param {Function?} callback Callback to unbind.
         * @return {Object} Current class instance.
         * @example
         * // Unbind specific callback
         * instance.off('event', handler);
         *
         * // Unbind all listeners by name
         * instance.off('event');
         *
         * // Unbind all events
         * instance.off();
         */
        off(name2, callback) {
          if (name2) {
            const names = name2.toLowerCase().split(" ");
            let i = names.length;
            while (i--) {
              const currentName = names[i];
              let handlers = this.bindings[currentName];
              if (!currentName) {
                each$d(this.bindings, (_value, bindingName) => {
                  this.toggleEvent(bindingName, false);
                  delete this.bindings[bindingName];
                });
                return this;
              }
              if (handlers) {
                if (!callback) {
                  handlers.length = 0;
                } else {
                  const filteredHandlers = partition$2(handlers, (handler) => handler.func === callback);
                  handlers = filteredHandlers.fail;
                  this.bindings[currentName] = handlers;
                  each$e(filteredHandlers.pass, (handler) => {
                    handler.removed = true;
                  });
                }
                if (!handlers.length) {
                  this.toggleEvent(name2, false);
                  delete this.bindings[currentName];
                }
              }
            }
          } else {
            each$d(this.bindings, (_value, name3) => {
              this.toggleEvent(name3, false);
            });
            this.bindings = {};
          }
          return this;
        }
        /**
         * Binds an event listener to a specific event by name
         * and automatically unbind the event once the callback fires.
         *
         * @method once
         * @param {String} name Event name or space separated list of events to bind.
         * @param {Function} callback Callback to be executed when the event occurs.
         * @param {Boolean} prepend Optional flag if the event should be prepended. Use this with care.
         * @return {Object} Current class instance.
         * @example
         * instance.once('event', (e) => {
         *   // Callback logic
         * });
         */
        once(name2, callback, prepend2) {
          return this.on(name2, callback, prepend2, { once: true });
        }
        /**
         * Returns true/false if the dispatcher has a event of the specified name.
         *
         * @method has
         * @param {String} name Name of the event to check for.
         * @return {Boolean} true/false if the event exists or not.
         */
        has(name2) {
          name2 = name2.toLowerCase();
          const binding = this.bindings[name2];
          return !(!binding || binding.length === 0);
        }
      }
      const getEventDispatcher = (obj) => {
        if (!obj._eventDispatcher) {
          obj._eventDispatcher = new EventDispatcher({
            scope: obj,
            toggleEvent: (name2, state) => {
              if (EventDispatcher.isNative(name2) && obj.toggleNativeEvent) {
                obj.toggleNativeEvent(name2, state);
              }
            }
          });
        }
        return obj._eventDispatcher;
      };
      const Observable = {
        /**
         * Fires the specified event by name. Consult the
         * <a href="https://www.tiny.cloud/docs/tinymce/8/events/">event reference</a> for more details on each event.
         * <br>
         * <em>Deprecated in TinyMCE 6.0 and has been marked for removal in TinyMCE 9.0. Use <code>dispatch</code> instead.</em>
         *
         * @method fire
         * @param {String} name Name of the event to fire.
         * @param {Object?} args Event arguments.
         * @param {Boolean?} bubble True/false if the event is to be bubbled.
         * @return {Object} Event args instance passed in.
         * @deprecated Use dispatch() instead
         * @example
         * instance.fire('event', {...});
         */
        fire(name2, args, bubble) {
          logFeatureDeprecationWarning("fire");
          return this.dispatch(name2, args, bubble);
        },
        /**
         * Dispatches the specified event by name. Consult the
         * <a href="https://www.tiny.cloud/docs/tinymce/8/events/">event reference</a> for more details on each event.
         *
         * @method dispatch
         * @param {String} name Name of the event to dispatch.
         * @param {Object?} args Event arguments.
         * @param {Boolean?} bubble True/false if the event is to be bubbled.
         * @return {Object} Event args instance passed in.
         * @example
         * instance.dispatch('event', {...});
         */
        dispatch(name2, args, bubble) {
          const self = this;
          if (self.removed && name2 !== "remove" && name2 !== "detach") {
            return normalize$3(name2.toLowerCase(), args ?? {}, self);
          }
          const dispatcherArgs = getEventDispatcher(self).dispatch(name2, args);
          if (bubble !== false && self.parent) {
            let parent2 = self.parent();
            while (parent2 && !dispatcherArgs.isPropagationStopped()) {
              parent2.dispatch(name2, dispatcherArgs, false);
              parent2 = parent2.parent ? parent2.parent() : void 0;
            }
          }
          return dispatcherArgs;
        },
        /**
         * Binds an event listener to a specific event by name. Consult the
         * <a href="https://www.tiny.cloud/docs/tinymce/8/events/">event reference</a> for more details on each event.
         *
         * @method on
         * @param {String} name Event name or space separated list of events to bind.
         * @param {Function} callback Callback to be executed when the event occurs.
         * @param {Boolean} prepend Optional flag if the event should be prepended. Use this with care.
         * @return {Object} Current class instance.
         * @example
         * instance.on('event', (e) => {
         *   // Callback logic
         * });
         */
        on(name2, callback, prepend2) {
          return getEventDispatcher(this).on(name2, callback, prepend2);
        },
        /**
         * Unbinds an event listener to a specific event by name. Consult the
         * <a href="https://www.tiny.cloud/docs/tinymce/8/events/">event reference</a> for more details on each event.
         *
         * @method off
         * @param {String?} name Name of the event to unbind.
         * @param {Function?} callback Callback to unbind.
         * @return {Object} Current class instance.
         * @example
         * // Unbind specific callback
         * instance.off('event', handler);
         *
         * // Unbind all listeners by name
         * instance.off('event');
         *
         * // Unbind all events
         * instance.off();
         */
        off(name2, callback) {
          return getEventDispatcher(this).off(name2, callback);
        },
        /**
         * Bind the event callback and once it fires the callback is removed. Consult the
         * <a href="https://www.tiny.cloud/docs/tinymce/8/events/">event reference</a> for more details on each event.
         *
         * @method once
         * @param {String} name Name of the event to bind.
         * @param {Function} callback Callback to bind only once.
         * @return {Object} Current class instance.
         */
        once(name2, callback) {
          return getEventDispatcher(this).once(name2, callback);
        },
        /**
         * Returns true/false if the object has a event of the specified name.
         *
         * @method hasEventListeners
         * @param {String} name Name of the event to check for.
         * @return {Boolean} true/false if the event exists or not.
         */
        hasEventListeners(name2) {
          return getEventDispatcher(this).has(name2);
        }
      };
      const DOM$2 = DOMUtils.DOM;
      let customEventRootDelegates;
      const getEventTarget = (editor, eventName) => {
        if (eventName === "selectionchange") {
          return editor.getDoc();
        }
        if (!editor.inline && /^(?:mouse|touch|click|contextmenu|drop|dragover|dragend)/.test(eventName)) {
          return editor.getDoc().documentElement;
        }
        const eventRoot = getEventRoot(editor);
        if (eventRoot) {
          if (!editor.eventRoot) {
            editor.eventRoot = DOM$2.select(eventRoot)[0];
          }
          return editor.eventRoot;
        }
        return editor.getBody();
      };
      const isListening = (editor) => !editor.hidden && !isDisabled(editor);
      const fireEvent = (editor, eventName, e) => {
        if (isListening(editor)) {
          editor.dispatch(eventName, e);
        } else if (isDisabled(editor)) {
          processDisabledEvents(editor, e);
        }
      };
      const bindEventDelegate = (editor, eventName) => {
        if (!editor.delegates) {
          editor.delegates = {};
        }
        if (editor.delegates[eventName] || editor.removed) {
          return;
        }
        const eventRootElm = getEventTarget(editor, eventName);
        if (getEventRoot(editor)) {
          if (!customEventRootDelegates) {
            customEventRootDelegates = {};
            editor.editorManager.on("removeEditor", () => {
              if (!editor.editorManager.activeEditor) {
                if (customEventRootDelegates) {
                  each$d(customEventRootDelegates, (_value, name2) => {
                    editor.dom.unbind(getEventTarget(editor, name2));
                  });
                  customEventRootDelegates = null;
                }
              }
            });
          }
          if (customEventRootDelegates[eventName]) {
            return;
          }
          const delegate = (e) => {
            const target = e.target;
            const editors2 = editor.editorManager.get();
            let i = editors2.length;
            while (i--) {
              const body = editors2[i].getBody();
              if (body === target || DOM$2.isChildOf(target, body)) {
                fireEvent(editors2[i], eventName, e);
              }
            }
          };
          customEventRootDelegates[eventName] = delegate;
          DOM$2.bind(eventRootElm, eventName, delegate);
        } else {
          const delegate = (e) => {
            fireEvent(editor, eventName, e);
          };
          DOM$2.bind(eventRootElm, eventName, delegate);
          editor.delegates[eventName] = delegate;
        }
      };
      const EditorObservable = {
        ...Observable,
        /**
         * Bind any pending event delegates. This gets executed after the target body/document is created.
         *
         * @private
         */
        bindPendingEventDelegates() {
          const self = this;
          Tools.each(self._pendingNativeEvents, (name2) => {
            bindEventDelegate(self, name2);
          });
        },
        /**
         * Toggles a native event on/off this is called by the EventDispatcher when
         * the first native event handler is added and when the last native event handler is removed.
         *
         * @private
         */
        toggleNativeEvent(name2, state) {
          const self = this;
          if (name2 === "focus" || name2 === "blur") {
            return;
          }
          if (self.removed) {
            return;
          }
          if (state) {
            if (self.initialized) {
              bindEventDelegate(self, name2);
            } else {
              if (!self._pendingNativeEvents) {
                self._pendingNativeEvents = [name2];
              } else {
                self._pendingNativeEvents.push(name2);
              }
            }
          } else if (self.initialized && self.delegates) {
            self.dom.unbind(getEventTarget(self, name2), name2, self.delegates[name2]);
            delete self.delegates[name2];
          }
        },
        /**
         * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc.
         *
         * @private
         */
        unbindAllNativeEvents() {
          const self = this;
          const body = self.getBody();
          const dom2 = self.dom;
          if (self.delegates) {
            each$d(self.delegates, (value2, name2) => {
              self.dom.unbind(getEventTarget(self, name2), name2, value2);
            });
            delete self.delegates;
          }
          if (!self.inline && body && dom2) {
            body.onload = null;
            dom2.unbind(self.getWin());
            dom2.unbind(self.getDoc());
          }
          if (dom2) {
            dom2.unbind(body);
            dom2.unbind(self.getContainer());
          }
        }
      };
      const stringListProcessor = (value2) => {
        if (isString(value2)) {
          return { value: value2.split(/[ ,]/), valid: true };
        } else if (isArrayOf(value2, isString)) {
          return { value: value2, valid: true };
        } else {
          return { valid: false, message: `The value must be a string[] or a comma/space separated string.` };
        }
      };
      const getBuiltInProcessor = (type2) => {
        const validator = (() => {
          switch (type2) {
            case "array":
              return isArray$1;
            case "boolean":
              return isBoolean;
            case "function":
              return isFunction;
            case "number":
              return isNumber;
            case "object":
              return isObject;
            case "string":
              return isString;
            case "string[]":
              return stringListProcessor;
            case "object[]":
              return (val) => isArrayOf(val, isObject);
            case "regexp":
              return (val) => is$5(val, RegExp);
            default:
              return always;
          }
        })();
        return (value2) => processValue(value2, validator, `The value must be a ${type2}.`);
      };
      const isBuiltInSpec = (spec) => isString(spec.processor);
      const getErrorMessage = (message, result) => {
        const additionalText = isEmpty$5(result.message) ? "" : `. ${result.message}`;
        return message + additionalText;
      };
      const isValidResult = (result) => result.valid;
      const processValue = (value2, processor, message = "") => {
        const result = processor(value2);
        if (isBoolean(result)) {
          return result ? { value: value2, valid: true } : { valid: false, message };
        } else {
          return result;
        }
      };
      const processDefaultValue = (name2, defaultValue, processor) => {
        if (!isUndefined(defaultValue)) {
          const result = processValue(defaultValue, processor);
          if (isValidResult(result)) {
            return result.value;
          } else {
            console.error(getErrorMessage(`Invalid default value passed for the "${name2}" option`, result));
          }
        }
        return void 0;
      };
      const create$4 = (editor, initialOptions, rawInitialOptions = initialOptions) => {
        const registry2 = {};
        const values2 = {};
        const setValue = (name2, value2, processor) => {
          const result = processValue(value2, processor);
          if (isValidResult(result)) {
            values2[name2] = result.value;
            return true;
          } else {
            console.warn(getErrorMessage(`Invalid value passed for the ${name2} option`, result));
            return false;
          }
        };
        const register2 = (name2, spec) => {
          const processor = isBuiltInSpec(spec) ? getBuiltInProcessor(spec.processor) : spec.processor;
          const defaultValue = processDefaultValue(name2, spec.default, processor);
          registry2[name2] = {
            ...spec,
            default: defaultValue,
            processor
          };
          const initValue = get$a(values2, name2).orThunk(() => get$a(initialOptions, name2));
          initValue.each((value2) => setValue(name2, value2, processor));
        };
        const isRegistered = (name2) => has$2(registry2, name2);
        const get2 = (name2) => get$a(values2, name2).orThunk(() => get$a(registry2, name2).map((spec) => spec.default)).getOrUndefined();
        const set2 = (name2, value2) => {
          if (!isRegistered(name2)) {
            console.warn(`"${name2}" is not a registered option. Ensure the option has been registered before setting a value.`);
            return false;
          } else {
            const spec = registry2[name2];
            if (spec.immutable) {
              console.error(`"${name2}" is an immutable option and cannot be updated`);
              return false;
            } else {
              return setValue(name2, value2, spec.processor);
            }
          }
        };
        const unset = (name2) => {
          const registered = isRegistered(name2);
          if (registered) {
            delete values2[name2];
          }
          return registered;
        };
        const isSet = (name2) => has$2(values2, name2);
        const debug = () => {
          try {
            console.log(JSON.parse(JSON.stringify(rawInitialOptions, (_key, value2) => {
              if (isBoolean(value2) || isNumber(value2) || isString(value2) || isNull(value2) || isArray$1(value2) || isPlainObject(value2)) {
                return value2;
              }
              return Object.prototype.toString.call(value2);
            })));
          } catch (error2) {
            console.error(error2);
          }
        };
        return {
          register: register2,
          isRegistered,
          get: get2,
          set: set2,
          unset,
          isSet,
          debug
        };
      };
      const setContentEditable = (elm, state) => {
        elm.dom.contentEditable = state ? "true" : "false";
      };
      const toggleReadOnly = (editor, state) => {
        const body = SugarElement.fromDom(editor.getBody());
        if (state) {
          editor.readonly = true;
          if (editor.hasEditableRoot()) {
            setContentEditable(body, true);
          }
          disableEditor(editor);
        } else {
          editor.readonly = false;
          enableEditor(editor);
        }
      };
      const isReadOnly = (editor) => editor.readonly;
      const rollbackChange = (editor) => {
        const undoLevel = editor.undoManager.add();
        if (isNonNullable(undoLevel)) {
          editor.undoManager.undo();
          editor.undoManager.reset();
        }
      };
      const hasContentMutations = (mutations) => exists(mutations, (mutation) => mutation.type === "characterData" || mutation.type === "childList");
      const registerReadOnlyInputBlockers = (editor) => {
        const handleMutations = (mutations) => {
          if (isReadOnly(editor) && hasContentMutations(mutations)) {
            rollbackChange(editor);
          }
        };
        const observer = new MutationObserver(handleMutations);
        editor.on("beforeinput paste cut dragend dragover draggesture dragdrop drop drag", (e) => {
          if (isReadOnly(editor)) {
            e.preventDefault();
          }
        });
        editor.on("BeforeExecCommand", (e) => {
          if ((e.command === "Undo" || e.command === "Redo") && isReadOnly(editor)) {
            e.preventDefault();
          }
        });
        editor.on("compositionstart", () => {
          if (isReadOnly(editor)) {
            observer.observe(editor.getBody(), {
              characterData: true,
              childList: true,
              subtree: true
            });
          }
        });
        editor.on("compositionend", () => {
          if (isReadOnly(editor)) {
            const records = observer.takeRecords();
            handleMutations(records);
          }
          observer.disconnect();
        });
      };
      const defaultModes = ["design", "readonly"];
      const switchToMode = (editor, activeMode, availableModes, mode) => {
        const oldMode = availableModes[activeMode.get()];
        const newMode = availableModes[mode];
        try {
          newMode.activate();
        } catch (e) {
          console.error(`problem while activating editor mode ${mode}:`, e);
          return;
        }
        oldMode.deactivate();
        if (oldMode.editorReadOnly !== newMode.editorReadOnly) {
          toggleReadOnly(editor, newMode.editorReadOnly);
        }
        activeMode.set(mode);
        fireSwitchMode(editor, mode);
      };
      const setMode = (editor, availableModes, activeMode, mode) => {
        if (mode === activeMode.get() || editor.initialized && isDisabled(editor)) {
          return;
        } else if (!has$2(availableModes, mode)) {
          throw new Error(`Editor mode '${mode}' is invalid`);
        }
        if (editor.initialized) {
          switchToMode(editor, activeMode, availableModes, mode);
        } else {
          editor.on("init", () => switchToMode(editor, activeMode, availableModes, mode));
        }
      };
      const registerMode = (availableModes, mode, api2) => {
        if (contains$2(defaultModes, mode)) {
          throw new Error(`Cannot override default mode ${mode}`);
        }
        return {
          ...availableModes,
          [mode]: {
            ...api2,
            deactivate: () => {
              try {
                api2.deactivate();
              } catch (e) {
                console.error(`problem while deactivating editor mode ${mode}:`, e);
              }
            }
          }
        };
      };
      const create$3 = (editor) => {
        const activeMode = Cell("design");
        const availableModes = Cell({
          design: {
            activate: noop,
            deactivate: noop,
            editorReadOnly: false
          },
          readonly: {
            activate: noop,
            deactivate: noop,
            editorReadOnly: true
          }
        });
        registerReadOnlyInputBlockers(editor);
        registerEventsAndFilters$1(editor);
        return {
          isReadOnly: () => isReadOnly(editor),
          set: (mode) => setMode(editor, availableModes.get(), activeMode, mode),
          get: () => activeMode.get(),
          register: (mode, api2) => {
            availableModes.set(registerMode(availableModes.get(), mode, api2));
          }
        };
      };
      const each$2 = Tools.each, explode = Tools.explode;
      const keyCodeLookup = {
        f1: 112,
        f2: 113,
        f3: 114,
        f4: 115,
        f5: 116,
        f6: 117,
        f7: 118,
        f8: 119,
        f9: 120,
        f10: 121,
        f11: 122,
        f12: 123
      };
      const modifierNames = Tools.makeMap("alt,ctrl,shift,meta,access");
      const isModifier = (key) => key in modifierNames;
      const parseShortcut = (pattern) => {
        const shortcut = {};
        const isMac = Env.os.isMacOS() || Env.os.isiOS();
        each$2(explode(pattern.toLowerCase(), "+"), (value2) => {
          if (isModifier(value2)) {
            shortcut[value2] = true;
          } else {
            if (/^[0-9]{2,}$/.test(value2)) {
              shortcut.keyCode = parseInt(value2, 10);
            } else {
              shortcut.charCode = value2.charCodeAt(0);
              shortcut.keyCode = keyCodeLookup[value2] || value2.toUpperCase().charCodeAt(0);
            }
          }
        });
        const id = [shortcut.keyCode];
        let key;
        for (key in modifierNames) {
          if (shortcut[key]) {
            id.push(key);
          } else {
            shortcut[key] = false;
          }
        }
        shortcut.id = id.join(",");
        if (shortcut.access) {
          shortcut.alt = true;
          if (isMac) {
            shortcut.ctrl = true;
          } else {
            shortcut.shift = true;
          }
        }
        if (shortcut.meta) {
          if (isMac) {
            shortcut.meta = true;
          } else {
            shortcut.ctrl = true;
            shortcut.meta = false;
          }
        }
        return shortcut;
      };
      class Shortcuts {
        editor;
        shortcuts = {};
        pendingPatterns = [];
        constructor(editor) {
          this.editor = editor;
          const self = this;
          editor.on("keyup keypress keydown", (e) => {
            if ((self.hasModifier(e) || self.isFunctionKey(e)) && !e.isDefaultPrevented()) {
              each$2(self.shortcuts, (shortcut) => {
                if (self.matchShortcut(e, shortcut)) {
                  self.pendingPatterns = shortcut.subpatterns.slice(0);
                  if (e.type === "keydown") {
                    self.executeShortcutAction(shortcut);
                  }
                }
              });
              if (self.matchShortcut(e, self.pendingPatterns[0])) {
                if (self.pendingPatterns.length === 1) {
                  if (e.type === "keydown") {
                    self.executeShortcutAction(self.pendingPatterns[0]);
                  }
                }
                self.pendingPatterns.shift();
              }
            }
          });
        }
        /**
         * Adds a keyboard shortcut for some command or function.
         *
         * @method add
         * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
         * @param {String} desc Text description for the command.
         * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
         * @param {Object} scope Optional scope to execute the function in.
         * @return {Boolean} true/false state if the shortcut was added or not.
         */
        add(pattern, desc, cmdFunc, scope) {
          const self = this;
          const func = self.normalizeCommandFunc(cmdFunc);
          each$2(explode(Tools.trim(pattern)), (pattern2) => {
            const shortcut = self.createShortcut(pattern2, desc, func, scope);
            self.shortcuts[shortcut.id] = shortcut;
          });
          return true;
        }
        /**
         * Remove a keyboard shortcut by pattern.
         *
         * @method remove
         * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
         * @return {Boolean} true/false state if the shortcut was removed or not.
         */
        remove(pattern) {
          const shortcut = this.createShortcut(pattern);
          if (this.shortcuts[shortcut.id]) {
            delete this.shortcuts[shortcut.id];
            return true;
          }
          return false;
        }
        normalizeCommandFunc(cmdFunc) {
          const self = this;
          const cmd = cmdFunc;
          if (typeof cmd === "string") {
            return () => {
              self.editor.execCommand(cmd, false, null);
            };
          } else if (Tools.isArray(cmd)) {
            return () => {
              self.editor.execCommand(cmd[0], cmd[1], cmd[2]);
            };
          } else {
            return cmd;
          }
        }
        createShortcut(pattern, desc, cmdFunc, scope) {
          const shortcuts = Tools.map(explode(pattern, ">"), parseShortcut);
          shortcuts[shortcuts.length - 1] = Tools.extend(shortcuts[shortcuts.length - 1], {
            func: cmdFunc,
            scope: scope || this.editor
          });
          return Tools.extend(shortcuts[0], {
            desc: this.editor.translate(desc),
            subpatterns: shortcuts.slice(1)
          });
        }
        hasModifier(e) {
          return e.altKey || e.ctrlKey || e.metaKey;
        }
        isFunctionKey(e) {
          return e.type === "keydown" && e.keyCode >= 112 && e.keyCode <= 123;
        }
        matchShortcut(e, shortcut) {
          if (!shortcut) {
            return false;
          }
          if (shortcut.ctrl !== e.ctrlKey || shortcut.meta !== e.metaKey) {
            return false;
          }
          if (shortcut.alt !== e.altKey || shortcut.shift !== e.shiftKey) {
            return false;
          }
          if (e.keyCode === shortcut.keyCode || e.charCode && e.charCode === shortcut.charCode) {
            e.preventDefault();
            return true;
          }
          return false;
        }
        executeShortcutAction(shortcut) {
          return shortcut.func ? shortcut.func.call(shortcut.scope) : null;
        }
      }
      const registry = () => {
        const bridge = create$6();
        return {
          /**
           * Registers a new auto completer component. When a configured string pattern
           * is matched in the content while typing, the autocompleter will be triggered.
           * Emoticons and Charmap use an autocompleter.
           * <br>
           * For information on creating an autocompleter, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/autocompleter/">
           * UI Components - Autocompleter</a>.
           *
           * @method addAutocompleter
           * @param {String} name Unique name identifying this autocomplete configuration.
           * @param {InlineContent.AutocompleterSpec} obj The autocomplete configuration object.
           */
          addAutocompleter: bridge.addAutocompleter,
          /**
           * Registers a new toolbar button that executes a command when clicked or activated
           * via keyboard navigation controls.
           * <br>
           * For information on creating a basic toolbar button, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-basic-toolbar-button/">
           * UI Components - Types of toolbar buttons: Basic button</a>.
           *
           * @method addButton
           * @param {String} name Unique name identifying the button, this button name will be used in the toolbar configuration to reference the button.
           * @param {Toolbar.ToolbarButtonSpec} obj the button configuration object.
           */
          addButton: bridge.addButton,
          /**
           * Registers a new contextual form item.
           * Similar to a context menu item, a contextual form is an item with an input
           * form element appearing when a content predicate is matched. An example
           * of a contextual form is the link plugin when the configuration
           * { link_context_toolbar: true } is used. When the cursor is on a link, a
           * contextual input form appears allowing for quick changes to the url field.
           * <br>
           * For information on creating context forms, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/contextform/">
           * UI Components - Context forms</a>.
           *
           * @method addContextForm
           * @param {String} name Unique name identifying the new contextual form item.
           * @param {Toolbar.ContextFormSpec} obj the context form configuration object.
           */
          addContextForm: bridge.addContextForm,
          /**
           * Registers a new context menu section that only appears when a content predicate is matched,
           * for example, the cursor is inside a table.
           * <br>
           * For information on creating context menus, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/contextmenu/">
           * UI Components - Context Menu</a>.
           *
           * @method addContextMenu
           * @param {String} name Unique name identifying the new context menu.
           * @param {Menu.ContextMenuSpec} obj The context menu configuration object.
           */
          addContextMenu: bridge.addContextMenu,
          /**
           * Registers a new context toolbar that only appears when a content predicate is matched for example
           * the cursor is on an image element.
           * <br>
           * For information on creating context toolbars, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/contexttoolbar/">
           * UI Components - Context Toolbar</a>.
           *
           * @method addContextToolbar
           * @param {String} name Unique name identifying the new context toolbar.
           * @param {Toolbar.ContextToolbarSpec} obj The context menu configuration object.
           */
          addContextToolbar: bridge.addContextToolbar,
          /**
           * Registers a new SVG icon. The icon name reference can be configured by any
           * TinyMCE UI components that can display an icon. The icon is only available
           * to the editor instance it was configured for.
           *
           * @method addIcon
           * @param {String} name Unique name identifying the new icon.
           * @param {String} svgData The SVG data string the browser will use to render the SVG icon.
           * @example
           * //To add a simple triangle icon:
           * editor.ui.registry.addIcon('triangleUp', '<svg height="24" width="24"><path d="M12 0 L24 24 L0 24 Z" /></svg>');
           */
          addIcon: bridge.addIcon,
          /**
           * Registers a new menu button. Adds a toolbar button that opens a menu when
           * clicked. The menu can be populated by items created by addMenuItem,
           * addNestedMenuItem or addToggleMenuItem.
           * <br>
           * For information on creating a toolbar menu button, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-menu-toolbar-button/">
           * UI Components - Types of toolbar buttons: Menu button</a>.
           *
           * @method addMenuButton
           * @param {String} name Unique name identifying the new menu button.
           * @param {Toolbar.ToolbarMenuButtonSpec} obj The menu button configuration object.
           */
          addMenuButton: bridge.addMenuButton,
          /**
           * Registers a new menu item that executes a command when clicked or activated
           * via keyboard navigation controls.
           * <br>
           * For information on creating a basic menu item, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/creating-custom-menu-items/">
           * UI Components - Custom menu items: Basic menu items</a>.
           *
           * @method addMenuItem
           * @param {String} name Unique name identifying the new menu item.
           * @param {Menu.MenuItemSpec} obj The menu item configuration object.
           */
          addMenuItem: bridge.addMenuItem,
          /**
           * Registers a new menu item that reveals a submenu when clicked or activated
           * by keyboard navigation controls.The submenu can be populated by items
           * created by addMenuItem, addNestedMenuItem or addToggleMenuItem.
           * <br>
           * For information on creating a nested menu item, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-nested-menu-items/">
           * UI Components - Custom menu items: Nested menu items</a>.
           *
           * @method addNestedMenuItem
           * @param {String} name Unique name identifying the new nested menu item.
           * @param {Menu.NestedMenuItemSpec} obj The nested menu item configuration object.
           */
          addNestedMenuItem: bridge.addNestedMenuItem,
          /**
           * Registers a new sidebar container.
           * This sidebar container is attached to the right side of the editor and
           * can be toggled open or closed. When registered, a new toolbar toggle
           * button with the same sidebar name is created. Additionally there is a
           * ToggleSidebar command and a 'ToggleSidebar' event that can used to
           * manage the sidebar open/closed state. The tinycomments plugin uses a
           * sidebar for its Ui components.
           * <br>
           * For information on creating a custom sidebar, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/customsidebar/">
           * UI Components - Custom sidebar</a>.
           *
           * @method addSidebar
           * @param {String} name Unique name identifying the new sidebar.
           * @param {Sidebar.SidebarSpec} obj The sidebar configuration object.
           */
          addSidebar: bridge.addSidebar,
          /**
           * Registers a new split button for the toolbar. The list styles plugin uses
           * a split button to simplify its functionality.
           * <br>
           * For information on creating a split toolbar button, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-split-toolbar-button/">
           * UI Components - Types of toolbar buttons: Split button</a>.
           *
           * @method addSplitButton
           * @param {String} name Unique name identifying the new split button.
           * @param {Toolbar.ToolbarSplitButtonSpec} obj The split button configuration object.
           */
          addSplitButton: bridge.addSplitButton,
          /**
           * Registers a new toggle button for the toolbar. A toggle buttons state can
           * be set in the configuration.
           * <br>
           * For information on creating a toggle toolbar button, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-toggle-toolbar-button/">
           * UI Components - Types of toolbar buttons: Toggle button</a>.
           *
           * @method addToggleButton
           * @param {String} name Unique name identifying the new split button.
           * @param {Toolbar.ToolbarToggleButtonSpec} obj The toggle button configuration object.
           */
          addToggleButton: bridge.addToggleButton,
          /**
           * Registers a new group toolbar button for the toolbar. Renders a toolbar button that opens a floating toolbar when
           * clicked.
           * <br>
           * <strong>Note:</strong> Group toolbar buttons can only be used when using the floating toolbar mode.
           * <br>
           * For information on creating a group toolbar button, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-group-toolbar-button/">
           * UI Components - Types of toolbar buttons: Group toolbar button</a>.
           *
           * @method addGroupToolbarButton
           * @param {String} name Unique name identifying the new group toolbar button.
           * @param {Toolbar.GroupToolbarButtonSpec} obj The group toolbar button configuration object.
           */
          addGroupToolbarButton: bridge.addGroupToolbarButton,
          /**
           * Registers a new menu item that will act like a toggle button,
           * showing a tick in the menu item to represent state.
           * <br>
           * For information on creating a toggle menu item, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-toggle-menu-items/">
           * UI Components - Custom menu items: Toggle menu items</a>.
           *
           * @method addToggleMenuItem
           * @param {String} name Unique name identifying the new menu item.
           * @param {Menu.ToggleMenuItemSpec} obj The menu item configuration object.
           */
          addToggleMenuItem: bridge.addToggleMenuItem,
          /**
           * Registers a new view container.
           * This view container is hidden (off) by default and attached next to the main view.
           * It can be toggled on or off.
           * When it is on, the main editor view is hidden and the specific view is shown.
           * When it is off, the specific view is hidden and the main view is shown.
           * There is also a ToggleView command.
           * The ToggleView command can toggle the view visibility.
           * The ToggleView command can be queried for its current state.
           * <br>
           * For information on creating a custom view, see:
           * <a href="https://www.tiny.cloud/docs/tinymce/8/custom-view/">
           * UI Components - Custom view</a>.
           *
           * @method addView
           * @param {String} name Unique name identifying the new view.
           * @param {View.ViewSpec} obj The view configuration object.
           */
          addView: bridge.addView,
          /**
           * Registers a new context configuration in the registry.
           * The registry stores all context configurations.
           * The buttons in editor configuration object can contain a context property.
           * These button specifications can use the registered contexts to determine
           * whether to enable or disable the buttons based on the current context.
           *
           * @method addContext
           * @param {String} name Unique name identifying the new context configuration.
           * @param {Function} pred A predicate function that determines if the context is active
           */
          addContext: bridge.addContext,
          /* note getAll is an internal method and may not be supported in future revisions */
          getAll: bridge.getAll
        };
      };
      const DOM$1 = DOMUtils.DOM;
      const extend = Tools.extend, each$1 = Tools.each;
      class Editor {
        baseUri;
        /**
         * Editor instance id, normally the same as the div/textarea that was replaced.
         *
         * @property id
         * @type String
         */
        id;
        /**
         * A uuid string to uniquely identify an editor across any page.
         *
         * @property editorUid
         * @type String
         */
        editorUid;
        /**
         * Name/Value object containing plugin instances.
         *
         * @property plugins
         * @type Object
         * @example
         * // Execute a method inside a plugin directly
         * tinymce.activeEditor.plugins.someplugin.someMethod();
         */
        plugins = {};
        /**
         * URI object to document configured for the TinyMCE instance.
         *
         * @property documentBaseURI
         * @type tinymce.util.URI
         * @example
         * // Get relative URL from the location of document_base_url
         * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
         *
         * // Get absolute URL from the location of document_base_url
         * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
         */
        documentBaseURI;
        /**
         * URI object to current document that holds the TinyMCE editor instance.
         *
         * @property baseURI
         * @type tinymce.util.URI
         * @example
         * // Get relative URL from the location of the API
         * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
         *
         * // Get absolute URL from the location of the API
         * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
         */
        baseURI;
        /**
         * Array with CSS files to load into the iframe.
         *
         * @property contentCSS
         * @type Array
         */
        contentCSS = [];
        /**
         * Array of CSS styles to add to head of document when the editor loads.
         *
         * @property contentStyles
         * @type Array
         */
        contentStyles = [];
        /**
         * Editor ui components
         *
         * @property ui
         * @type tinymce.editor.ui.Ui
         */
        ui;
        /**
         * Editor mode API
         *
         * @property mode
         * @type tinymce.EditorMode
         */
        mode;
        /**
         * Editor options API
         *
         * @property options
         * @type tinymce.EditorOptions
         */
        options;
        /**
         * Editor upload API
         *
         * @property editorUpload
         * @type tinymce.EditorUpload
         */
        editorUpload;
        /**
         * Editor user lookup API
         *
         * @property userLookup
         * @type tinymce.UserLookup
         */
        userLookup;
        shortcuts;
        loadedCSS = {};
        editorCommands;
        suffix;
        editorManager;
        hidden;
        inline;
        hasVisual;
        isNotDirty = false;
        // Arguments set later, for example by InitContentBody.ts
        // Note that these may technically be undefined up until PreInit (or similar) has fired,
        // however the types are aimed at an initialised editor for ease of use.
        annotator;
        bodyElement;
        bookmark;
        // Note: Intentionally any so as to not expose Optional
        composing = false;
        container;
        contentAreaContainer;
        contentDocument;
        contentWindow;
        delegates;
        destroyed = false;
        dom;
        editorContainer;
        eventRoot;
        formatter;
        formElement;
        formEventDelegate;
        hasHiddenInput = false;
        iframeElement = null;
        iframeHTML;
        initialized = false;
        notificationManager;
        orgDisplay;
        orgVisibility;
        parser;
        quirks;
        readonly = false;
        removed = false;
        schema;
        selection;
        serializer;
        startContent = "";
        targetElm;
        theme;
        model;
        undoManager;
        windowManager;
        licenseKeyManager;
        _beforeUnload;
        _eventDispatcher;
        _nodeChangeDispatcher;
        _pendingNativeEvents = [];
        _selectionOverrides;
        _skinLoaded = false;
        _editableRoot = true;
        // EditorObservable patches
        bindPendingEventDelegates;
        toggleNativeEvent;
        unbindAllNativeEvents;
        fire;
        dispatch;
        on;
        off;
        once;
        hasEventListeners;
        /**
         * Constructs a editor instance by id.
         *
         * @constructor
         * @method Editor
         * @param {String} id Unique id for the editor.
         * @param {Object} options Options for the editor.
         * @param {tinymce.EditorManager} editorManager EditorManager instance.
         */
        constructor(id, options, editorManager) {
          this.editorManager = editorManager;
          extend(this, EditorObservable);
          const self = this;
          this.id = id;
          this.editorUid = uuidV4();
          this.hidden = false;
          const normalizedOptions = normalizeOptions(editorManager.defaultOptions, options);
          this.options = create$4(self, normalizedOptions, options);
          register$7(self);
          this.userLookup = createUserLookup(this);
          const getOption2 = this.options.get;
          if (getOption2("deprecation_warnings")) {
            logWarnings(options, normalizedOptions);
          }
          const suffix = getOption2("suffix");
          if (suffix) {
            editorManager.suffix = suffix;
          }
          this.suffix = editorManager.suffix;
          const baseUrl = getOption2("base_url");
          if (baseUrl) {
            editorManager._setBaseUrl(baseUrl);
          }
          this.baseUri = editorManager.baseURI;
          const referrerPolicy = getReferrerPolicy(self);
          if (referrerPolicy) {
            ScriptLoader.ScriptLoader._setReferrerPolicy(referrerPolicy);
            DOMUtils.DOM.styleSheetLoader._setReferrerPolicy(referrerPolicy);
          }
          ScriptLoader.ScriptLoader._setCrossOrigin((url) => {
            const crossOrigin = getCrossOrigin(self);
            return crossOrigin(url, "script");
          });
          DOMUtils.DOM.styleSheetLoader._setCrossOrigin((url) => {
            const crossOrigin = getCrossOrigin(self);
            return crossOrigin(url, "stylesheet");
          });
          const contentCssCors = hasContentCssCors(self);
          if (isNonNullable(contentCssCors)) {
            DOMUtils.DOM.styleSheetLoader._setContentCssCors(contentCssCors);
          }
          AddOnManager.languageLoad = getOption2("language_load");
          AddOnManager.baseURL = editorManager.baseURL;
          this.setDirty(false);
          this.documentBaseURI = new URI(getDocumentBaseUrl(self), {
            base_uri: this.baseUri
          });
          this.baseURI = this.baseUri;
          this.inline = isInline$2(self);
          this.hasVisual = isVisualAidsEnabled(self);
          this.shortcuts = new Shortcuts(this);
          this.editorCommands = new EditorCommands(this);
          registerCommands(this);
          const cacheSuffix = getOption2("cache_suffix");
          if (cacheSuffix) {
            Env.cacheSuffix = cacheSuffix.replace(/^[\?\&]+/, "");
          }
          this.ui = {
            registry: registry(),
            styleSheetLoader: void 0,
            show: noop,
            hide: noop,
            setEnabled: noop,
            isEnabled: always
          };
          this.mode = create$3(self);
          Object.defineProperty(this, "editorUid", {
            writable: false,
            configurable: false,
            enumerable: true
          });
          editorManager.dispatch("SetupEditor", { editor: this });
          const setupCallback = getSetupCallback(self);
          if (isFunction(setupCallback)) {
            setupCallback.call(self, self);
          }
        }
        /**
         * Renders the editor/adds it to the page.
         *
         * @method render
         */
        render() {
          render(this);
        }
        /**
         * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
         * it will also place DOM focus inside the editor.
         *
         * @method focus
         * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor.
         */
        focus(skipFocus) {
          this.execCommand("mceFocus", false, skipFocus);
        }
        /**
         * Returns true/false if the editor has real keyboard focus.
         *
         * @method hasFocus
         * @return {Boolean} Current focus state of the editor.
         */
        hasFocus() {
          return hasFocus(this);
        }
        /**
         * Translates the specified string by replacing variables with language pack items it will also check if there is
         * a key matching the input.
         *
         * @method translate
         * @param {String} text String to translate by the language pack data.
         * @return {String} Translated string.
         */
        translate(text2) {
          return I18n.translate(text2);
        }
        getParam(name2, defaultVal, type2) {
          const options = this.options;
          if (!options.isRegistered(name2)) {
            if (isNonNullable(type2)) {
              options.register(name2, { processor: type2, default: defaultVal });
            } else {
              options.register(name2, { processor: always, default: defaultVal });
            }
          }
          return !options.isSet(name2) && !isUndefined(defaultVal) ? defaultVal : options.get(name2);
        }
        /**
         * Checks that the plugin is in the editor configuration and can optionally check if the plugin has been loaded.
         *
         * @method hasPlugin
         * @param {String} name The name of the plugin, as specified for the TinyMCE `plugins` option.
         * @param {Boolean} loaded If `true`, will also check that the plugin has been loaded.
         * @return {Boolean} If `loaded` is `true`, returns `true` if the plugin is in the configuration and has been loaded. If `loaded` is `false`, returns `true` if the plugin is in the configuration, regardless of plugin load status.
         * @example
         * // Returns `true` if the Comments plugin is in the editor configuration and has loaded successfully:
         * tinymce.activeEditor.hasPlugin('tinycomments', true);
         * // Returns `true` if the Table plugin is in the editor configuration, regardless of whether or not it loads:
         * tinymce.activeEditor.hasPlugin('table');
         */
        hasPlugin(name2, loaded) {
          const hasPlugin = contains$2(getPlugins(this), name2);
          if (hasPlugin) {
            return loaded ? PluginManager.get(name2) !== void 0 : true;
          } else {
            return false;
          }
        }
        /**
         * Dispatches out a onNodeChange event to all observers. This method should be called when you
         * need to update the UI states or element path etc.
         *
         * @method nodeChanged
         * @param {Object} args Optional args to pass to NodeChange event handlers.
         */
        nodeChanged(args) {
          this._nodeChangeDispatcher.nodeChanged(args);
        }
        addCommand(name2, callback, scope) {
          this.editorCommands.addCommand(name2, callback, scope);
        }
        addQueryStateHandler(name2, callback, scope) {
          this.editorCommands.addQueryStateHandler(name2, callback, scope);
        }
        addQueryValueHandler(name2, callback, scope) {
          this.editorCommands.addQueryValueHandler(name2, callback, scope);
        }
        /**
         * Adds a keyboard shortcut for some command or function.
         *
         * @method addShortcut
         * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
         * @param {String} desc Text description for the command.
         * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
         * @param {Object} scope Optional scope to execute the function in.
         * @return {Boolean} true/false state if the shortcut was added or not.
         * @example
         * editor.addShortcut('ctrl+a', 'description of the shortcut', () => {});
         * editor.addShortcut('ctrl+alt+a', 'description of the shortcut', () => {});
         * // "meta" maps to Command on Mac and Ctrl on PC
         * editor.addShortcut('meta+a', 'description of the shortcut', () => {});
         * // "access" maps to Control+Option on Mac and shift+alt on PC
         * editor.addShortcut('access+a', 'description of the shortcut', () => {});
         *
         * editor.addShortcut('meta+access+c', 'Opens the code editor dialog.', () => {
         *   editor.execCommand('mceCodeEditor');
         * });
         *
         * editor.addShortcut('meta+shift+32', 'Inserts "Hello, World!" for meta+shift+space', () => {
         *   editor.execCommand('mceInsertContent', false, 'Hello, World!');
         * });
         */
        addShortcut(pattern, desc, cmdFunc, scope) {
          this.shortcuts.add(pattern, desc, cmdFunc, scope);
        }
        /**
         * Executes a registered command on the current instance. A list of available commands can be found in
         * the tinymce command identifiers documentation.
         *
         * @method execCommand
         * @param {String} cmd Command name to execute, for example mceLink or Bold.
         * @param {Boolean} ui Specifies if a UI (dialog) should be presented or not.
         * @param {Object/Array/String/Number/Boolean} value Optional command value, this can be anything.
         * @param {Object} args Optional arguments object.
         * @return {Boolean} true or false if the command was supported or not.
         */
        execCommand(cmd, ui, value2, args) {
          return this.editorCommands.execCommand(cmd, ui, value2, args);
        }
        /**
         * Returns a command specific state, for example if bold is enabled or not.
         *
         * @method queryCommandState
         * @param {String} cmd Command to query state from.
         * @return {Boolean} Command specific state, for example if bold is enabled or not.
         */
        queryCommandState(cmd) {
          return this.editorCommands.queryCommandState(cmd);
        }
        /**
         * Returns a command specific value, for example the current font size.
         *
         * @method queryCommandValue
         * @param {String} cmd Command to query value from.
         * @return {String} Command value, for example the current font size or an empty string (`""`) if the query command is not found.
         */
        queryCommandValue(cmd) {
          return this.editorCommands.queryCommandValue(cmd);
        }
        /**
         * Returns true/false if the command is supported or not.
         *
         * @method queryCommandSupported
         * @param {String} cmd Command that we check support for.
         * @return {Boolean} true/false if the command is supported or not.
         */
        queryCommandSupported(cmd) {
          return this.editorCommands.queryCommandSupported(cmd);
        }
        /**
         * Shows the editor and hides any textarea/div that the editor is supposed to replace.
         *
         * @method show
         */
        show() {
          const self = this;
          if (self.hidden) {
            self.hidden = false;
            if (self.inline) {
              self.getBody().contentEditable = "true";
            } else {
              DOM$1.show(self.getContainer());
              DOM$1.hide(self.id);
            }
            self.load();
            self.dispatch("show");
          }
        }
        /**
         * Hides the editor and shows any textarea/div that the editor is supposed to replace.
         *
         * @method hide
         */
        hide() {
          const self = this;
          if (!self.hidden) {
            self.save();
            if (self.inline) {
              self.getBody().contentEditable = "false";
              if (self === self.editorManager.focusedEditor) {
                self.editorManager.focusedEditor = null;
              }
            } else {
              DOM$1.hide(self.getContainer());
              DOM$1.setStyle(self.id, "display", self.orgDisplay);
            }
            self.hidden = true;
            self.dispatch("hide");
          }
        }
        /**
         * Returns true/false if the editor is hidden or not.
         *
         * @method isHidden
         * @return {Boolean} True/false if the editor is hidden or not.
         */
        isHidden() {
          return this.hidden;
        }
        /**
         * Sets the progress state, this will display a throbber/progess for the editor.
         * This is ideal for asynchronous operations like an AJAX save call.
         *
         * @method setProgressState
         * @param {Boolean} state Boolean state if the progress should be shown or hidden.
         * @param {Number} time Optional time to wait before the progress gets shown.
         * @return {Boolean} Same as the input state.
         * @example
         * // Show progress for the active editor
         * tinymce.activeEditor.setProgressState(true);
         *
         * // Hide progress for the active editor
         * tinymce.activeEditor.setProgressState(false);
         *
         * // Show progress after 3 seconds
         * tinymce.activeEditor.setProgressState(true, 3000);
         */
        setProgressState(state, time) {
          this.dispatch("ProgressState", { state, time });
        }
        /**
         * Loads contents from the textarea, input or other element that got converted into an editor instance.
         * This method will move the contents from that textarea, input or other element into the editor by using setContent
         * so all events etc that method has will get dispatched as well.
         *
         * @method load
         * @param {Object} args Optional content object, this gets passed around through the whole load process.
         */
        load(args = {}) {
          const self = this;
          const elm = self.getElement();
          if (self.removed) {
            return;
          }
          if (elm) {
            const loadArgs = {
              ...args,
              load: true
            };
            const value2 = isTextareaOrInput(elm) ? elm.value : elm.innerHTML;
            self.setContent(value2, loadArgs);
            if (!loadArgs.no_events) {
              self.dispatch("LoadContent", {
                ...loadArgs,
                element: elm
              });
            }
          }
        }
        /**
         * Saves the contents from an editor out to the textarea or div element that got converted into an editor instance.
         * This method will move the HTML contents from the editor into that textarea or div by getContent
         * so all events etc that method has will get dispatched as well.
         *
         * @method save
         * @param {Object} args Optional content object, this gets passed around through the whole save process.
         * @return {String} HTML string that got set into the textarea/div.
         */
        save(args = {}) {
          const self = this;
          let elm = self.getElement();
          if (!elm || !self.initialized || self.removed) {
            return "";
          }
          const getArgs = {
            ...args,
            save: true,
            element: elm
          };
          let html2 = self.getContent(getArgs);
          const saveArgs = { ...getArgs, content: html2 };
          if (!saveArgs.no_events) {
            self.dispatch("SaveContent", saveArgs);
          }
          if (saveArgs.format === "raw") {
            self.dispatch("RawSaveContent", saveArgs);
          }
          html2 = saveArgs.content;
          if (!isTextareaOrInput(elm)) {
            if (args.is_removing || !self.inline) {
              elm.innerHTML = html2;
            }
            const form = DOM$1.getParent(self.id, "form");
            if (form) {
              each$1(form.elements, (elm2) => {
                if (elm2.name === self.id) {
                  elm2.value = html2;
                  return false;
                } else {
                  return true;
                }
              });
            }
          } else {
            elm.value = html2;
          }
          saveArgs.element = getArgs.element = elm = null;
          if (saveArgs.set_dirty !== false) {
            self.setDirty(false);
          }
          return html2;
        }
        setContent(content, args) {
          setContent(this, content, args);
        }
        getContent(args) {
          return getContent(this, args);
        }
        /**
         * Inserts content at caret position.
         *
         * @method insertContent
         * @param {String} content Content to insert.
         * @param {Object} args Optional args to pass to insert call.
         */
        insertContent(content, args) {
          if (args) {
            content = extend({ content }, args);
          }
          this.execCommand("mceInsertContent", false, content);
        }
        /**
         * Resets the editors content, undo/redo history and dirty state. If `initialContent` isn't specified, then
         * the editor is reset back to the initial start content.
         *
         * @method resetContent
         * @param {String} initialContent An optional string to use as the initial content of the editor.
         */
        resetContent(initialContent) {
          if (initialContent === void 0) {
            setContent(this, this.startContent, { initial: true, format: "raw" });
          } else {
            setContent(this, initialContent, { initial: true });
          }
          this.undoManager.reset();
          this.setDirty(false);
          this.nodeChanged();
        }
        /**
         * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
         *
         * The dirty state is automatically set to `true` when the user modifies editor content after initialization or the
         * last `editor.save()` call. This includes changes made using undo or redo.
         *
         * @method isDirty
         * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
         * @example
         * if (tinymce.activeEditor.isDirty()) {
         *   alert("You must save your contents.");
         * }
         */
        isDirty() {
          return !this.isNotDirty;
        }
        /**
         * Explicitly sets the dirty state. This will fire the dirty event if the editor dirty state is changed from false to true
         * by invoking this method.
         *
         * @method setDirty
         * @param {Boolean} state True/false if the editor is considered dirty.
         * @example
         * const ajaxSave = () => {
         *   const editor = tinymce.get('elm1');
         *
         *   // Save contents using some XHR call
         *   alert(editor.getContent());
         *
         *   editor.setDirty(false); // Force not dirty state
         * }
         */
        setDirty(state) {
          const oldState = !this.isNotDirty;
          this.isNotDirty = !state;
          if (state && state !== oldState) {
            this.dispatch("dirty");
          }
        }
        /**
         * Returns the container element of the editor. The container element includes
         * all the elements added to the page for the editor. Such as UI, iframe, etc.
         *
         * @method getContainer
         * @return {Element} HTML DOM element for the editor container.
         */
        getContainer() {
          const self = this;
          if (!self.container) {
            self.container = self.editorContainer || DOM$1.get(self.id + "_parent");
          }
          return self.container;
        }
        /**
         * Returns the content area container element of the editor. This element
         * holds the iframe or the editable element.
         *
         * @method getContentAreaContainer
         * @return {Element} HTML DOM element for the editor area container.
         */
        getContentAreaContainer() {
          return this.contentAreaContainer;
        }
        /**
         * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
         *
         * @method getElement
         * @return {Element} HTML DOM element for the replaced element.
         */
        getElement() {
          if (!this.targetElm) {
            this.targetElm = DOM$1.get(this.id);
          }
          return this.targetElm;
        }
        /**
         * Returns the iframes window object.
         *
         * @method getWin
         * @return {Window} Iframe DOM window object.
         */
        getWin() {
          const self = this;
          if (!self.contentWindow) {
            const elm = self.iframeElement;
            if (elm) {
              self.contentWindow = elm.contentWindow;
            }
          }
          return self.contentWindow;
        }
        /**
         * Returns the iframes document object.
         *
         * @method getDoc
         * @return {Document} Iframe DOM document object.
         */
        getDoc() {
          const self = this;
          if (!self.contentDocument) {
            const win = self.getWin();
            if (win) {
              self.contentDocument = win.document;
            }
          }
          return self.contentDocument;
        }
        /**
         * Returns the root element of the editable area.
         * For a non-inline iframe-based editor, returns the iframe's body element.
         *
         * @method getBody
         * @return {Element} The root element of the editable area.
         */
        getBody() {
          const doc = this.getDoc();
          return this.bodyElement ?? doc?.body ?? null;
        }
        /**
         * URL converter function this gets executed each time a user adds an img, a or
         * any other element that has a URL in it. This will be called both by the DOM and HTML
         * manipulation functions.
         *
         * @method convertURL
         * @param {String} url URL to convert.
         * @param {String} name Attribute name src, href etc.
         * @param {String/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
         * @return {String} Converted URL string.
         */
        convertURL(url, name2, elm) {
          const self = this, getOption2 = self.options.get;
          const urlConverterCallback = getUrlConverterCallback(self);
          if (isFunction(urlConverterCallback)) {
            return urlConverterCallback.call(self, url, elm, true, name2);
          }
          if (!getOption2("convert_urls") || elm === "link" || isObject(elm) && elm.nodeName === "LINK" || url.indexOf("file:") === 0 || url.length === 0) {
            return url;
          }
          const urlObject = new URI(url);
          if (urlObject.protocol !== "http" && urlObject.protocol !== "https" && urlObject.protocol !== "") {
            return url;
          }
          if (getOption2("relative_urls")) {
            return self.documentBaseURI.toRelative(url);
          }
          url = self.documentBaseURI.toAbsolute(url, getOption2("remove_script_host"));
          return url;
        }
        /**
         * Adds visual aids for tables, anchors, etc so they can be more easily edited inside the editor.
         *
         * @method addVisual
         * @param {Element} elm Optional root element to loop though to find tables, etc that needs the visual aid.
         */
        addVisual(elm) {
          addVisual(this, elm);
        }
        /**
         * Changes the editable state of the editor's root element.
         *
         * @method setEditableRoot
         * @param {Boolean} state State to set true for editable and false for non-editable.
         */
        setEditableRoot(state) {
          setEditableRoot(this, state);
        }
        /**
         * Returns the current editable state of the editor's root element.
         *
         * @method hasEditableRoot
         * @return {Boolean} True if the root element is editable, false if it is not editable.
         */
        hasEditableRoot() {
          return hasEditableRoot(this);
        }
        /**
         * Removes the editor from the dom and tinymce collection.
         *
         * @method remove
         */
        remove() {
          remove$1(this);
        }
        /**
         * Destroys the editor instance by removing all events, element references or other resources
         * that could leak memory. This method will be called automatically when the page is unloaded
         * but you can also call it directly if you know what you are doing.
         *
         * @method destroy
         * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
         */
        destroy(automatic) {
          destroy(this, automatic);
        }
        /**
         * Uploads all data uri/blob uri images in the editor contents to server.
         *
         * @method uploadImages
         * @return {Promise} Promise instance with images and status for each image.
         */
        uploadImages() {
          return this.editorUpload.uploadImages();
        }
        // Internal functions
        _scanForImages() {
          return this.editorUpload.scanForImages();
        }
      }
      const DOM = DOMUtils.DOM;
      const each = Tools.each;
      let boundGlobalEvents = false;
      let beforeUnloadDelegate;
      let editors = [];
      const globalEventDelegate = (e) => {
        const type2 = e.type;
        each(EditorManager.get(), (editor) => {
          switch (type2) {
            case "scroll":
              editor.dispatch("ScrollWindow", e);
              break;
            case "resize":
              editor.dispatch("ResizeWindow", e);
              break;
          }
        });
      };
      const toggleGlobalEvents = (state) => {
        if (state !== boundGlobalEvents) {
          const DOM2 = DOMUtils.DOM;
          if (state) {
            DOM2.bind(window, "resize", globalEventDelegate);
            DOM2.bind(window, "scroll", globalEventDelegate);
          } else {
            DOM2.unbind(window, "resize", globalEventDelegate);
            DOM2.unbind(window, "scroll", globalEventDelegate);
          }
          boundGlobalEvents = state;
        }
      };
      const removeEditorFromList = (targetEditor) => {
        const oldEditors = editors;
        editors = filter$5(editors, (editor) => {
          return targetEditor !== editor;
        });
        if (EditorManager.activeEditor === targetEditor) {
          EditorManager.activeEditor = editors.length > 0 ? editors[0] : null;
        }
        if (EditorManager.focusedEditor === targetEditor) {
          EditorManager.focusedEditor = null;
        }
        return oldEditors.length !== editors.length;
      };
      const purgeDestroyedEditor = (editor) => {
        if (editor && editor.initialized && !(editor.getContainer() || editor.getBody()).parentNode) {
          removeEditorFromList(editor);
          editor.unbindAllNativeEvents();
          editor.destroy(true);
          editor.removed = true;
        }
      };
      const isQuirksMode = document.compatMode !== "CSS1Compat";
      const EditorManager = {
        ...Observable,
        baseURI: null,
        baseURL: null,
        /**
         * Object containing the options that will be passed by default to the <code>init</code> method upon each initialization of an editor. These defaults will be shallow merged with other options passed to <code>init</code>.
         *
         * @property defaultOptions
         * @type Object
         */
        defaultOptions: {},
        documentBaseURL: null,
        suffix: null,
        /**
         * A uuid string to anonymously identify the page tinymce is loaded in
         *
         * @property pageUid
         * @type String
         */
        pageUid: uuidV4(),
        /**
         * Major version of TinyMCE build.
         *
         * @property majorVersion
         * @type String
         */
        majorVersion: "8",
        /**
         * Minor version of TinyMCE build.
         *
         * @property minorVersion
         * @type String
         */
        minorVersion: "2.0",
        /**
         * Release date of TinyMCE build.
         *
         * @property releaseDate
         * @type String
         */
        releaseDate: "2025-10-23",
        /**
         * Collection of language pack data.
         *
         * @property i18n
         * @type Object
         */
        i18n: I18n,
        /**
         * Currently active editor instance.
         *
         * @property activeEditor
         * @type tinymce.Editor
         * @example
         * tinyMCE.activeEditor.selection.getContent();
         * tinymce.EditorManager.activeEditor.selection.getContent();
         */
        activeEditor: null,
        focusedEditor: null,
        setup() {
          const self = this;
          let baseURL = "";
          let suffix = "";
          let documentBaseURL = URI.getDocumentBaseUrl(document.location);
          if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) {
            documentBaseURL = documentBaseURL.replace(/[\?#].*$/, "").replace(/[\/\\][^\/]+$/, "");
            if (!/[\/\\]$/.test(documentBaseURL)) {
              documentBaseURL += "/";
            }
          }
          const preInit2 = window.tinymce || window.tinyMCEPreInit;
          if (preInit2) {
            baseURL = preInit2.base || preInit2.baseURL;
            suffix = preInit2.suffix;
          } else {
            const scripts = document.getElementsByTagName("script");
            for (let i = 0; i < scripts.length; i++) {
              const src = scripts[i].src || "";
              if (src === "") {
                continue;
              }
              const srcScript = src.substring(src.lastIndexOf("/"));
              if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
                if (srcScript.indexOf(".min") !== -1) {
                  suffix = ".min";
                }
                baseURL = src.substring(0, src.lastIndexOf("/"));
                break;
              }
            }
            if (!baseURL && document.currentScript) {
              const src = document.currentScript.src;
              if (src.indexOf(".min") !== -1) {
                suffix = ".min";
              }
              baseURL = src.substring(0, src.lastIndexOf("/"));
            }
          }
          self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);
          self.documentBaseURL = documentBaseURL;
          self.baseURI = new URI(self.baseURL);
          self.suffix = suffix;
          setup$C(self);
          each$e(["majorVersion", "minorVersion", "releaseDate", "pageUid", "_addLicenseKeyManager"], (property) => Object.defineProperty(self, property, {
            writable: false,
            configurable: false,
            enumerable: true
          }));
        },
        /**
         * Overrides the default options for editor instances. The <code>overrideDefaults</code> method replaces the <code>defaultOptions</code>, including any set by a previous call to the <code>overrideDefaults</code> method.
         * <br /><br />
         * When using the Tiny Cloud, some of these defaults are required for the cloud-based editor to function.
         * <br /><br />
         * Therefore, when using <code>overrideDefaults</code> with the cloud-based editor, any newly integrated options must be combined with the options in <code>tinymce.defaultOptions</code>.
         *
         * @method overrideDefaults
         * @param {Object} defaultOptions Default options object.
         * @example
         * const customOptions = {
         *   toolbar_sticky: true
         * };
         *
         * tinymce.overrideDefaults({
         *   ...tinymce.defaultOptions,
         *   ...customOptions
         * });
         */
        overrideDefaults(defaultOptions) {
          const baseUrl = defaultOptions.base_url;
          if (baseUrl) {
            this._setBaseUrl(baseUrl);
          }
          const suffix = defaultOptions.suffix;
          if (suffix) {
            this.suffix = suffix;
          }
          this.defaultOptions = defaultOptions;
          const pluginBaseUrls = defaultOptions.plugin_base_urls;
          if (pluginBaseUrls !== void 0) {
            each$d(pluginBaseUrls, (pluginBaseUrl, pluginName) => {
              AddOnManager.PluginManager.urls[pluginName] = pluginBaseUrl;
            });
          }
        },
        /**
         * Initializes a set of editors. This method will create editors based on various settings.
         * <br /><br />
         * For information on basic usage of <code>init</code>, see: <a href="https://www.tiny.cloud/docs/tinymce/8/basic-setup/">Basic setup</a>.
         *
         * @method init
         * @param {Object} options Options object to be passed to each editor instance.
         * @return {Promise} Promise that gets resolved with an array of editors when all editor instances are initialized.
         * @example
         * // Initializes a editor using the longer method
         * tinymce.EditorManager.init({
         *    some_settings : 'some value'
         * });
         *
         * // Initializes a editor instance using the shorter version and with a promise
         * tinymce.init({
         *   some_settings : 'some value'
         * }).then((editors) => {
         *   ...
         * });
         */
        init(options) {
          const self = this;
          let result;
          const invalidInlineTargets = Tools.makeMap("area base basefont br col frame hr img input isindex link meta param embed source wbr track colgroup option table tbody tfoot thead tr th td script noscript style textarea video audio iframe object menu", " ");
          const isInvalidInlineTarget = (options2, elm) => options2.inline && elm.tagName.toLowerCase() in invalidInlineTargets;
          const createId = (elm) => {
            let id = elm.id;
            if (!id) {
              id = get$a(elm, "name").filter((name2) => !DOM.get(name2)).getOrThunk(DOM.uniqueId);
              elm.setAttribute("id", id);
            }
            return id;
          };
          const execCallback = (name2) => {
            const callback = options[name2];
            if (!callback) {
              return;
            }
            return callback.apply(self, []);
          };
          const findTargets = (options2) => {
            if (Env.browser.isIE() || Env.browser.isEdge()) {
              initError("TinyMCE does not support the browser you are using. For a list of supported browsers please see: https://www.tiny.cloud/docs/tinymce/8/support/#supportedwebbrowsers");
              return [];
            } else if (isQuirksMode) {
              initError("Failed to initialize the editor as the document is not in standards mode. TinyMCE requires standards mode.");
              return [];
            } else if (isString(options2.selector)) {
              return DOM.select(options2.selector);
            } else if (isNonNullable(options2.target)) {
              return [options2.target];
            } else {
              return [];
            }
          };
          let provideResults = (editors2) => {
            result = editors2;
          };
          const initEditors = () => {
            let initCount = 0;
            const editors2 = [];
            let targets;
            const createEditor = (id, options2, targetElm) => {
              const editor = new Editor(id, options2, self);
              editors2.push(editor);
              editor.on("init", () => {
                if (++initCount === targets.length) {
                  provideResults(editors2);
                }
              });
              editor.targetElm = editor.targetElm || targetElm;
              editor.render();
            };
            DOM.unbind(window, "ready", initEditors);
            execCallback("onpageload");
            targets = unique$1(findTargets(options));
            Tools.each(targets, (elm) => {
              purgeDestroyedEditor(self.get(elm.id));
            });
            targets = Tools.grep(targets, (elm) => {
              return !self.get(elm.id);
            });
            if (targets.length === 0) {
              provideResults([]);
            } else {
              each(targets, (elm) => {
                if (isInvalidInlineTarget(options, elm)) {
                  initError("Could not initialize inline editor on invalid inline target element", elm);
                } else {
                  createEditor(createId(elm), options, elm);
                }
              });
            }
          };
          DOM.bind(window, "ready", initEditors);
          return new Promise((resolve2) => {
            if (result) {
              resolve2(result);
            } else {
              provideResults = (editors2) => {
                resolve2(editors2);
              };
            }
          });
        },
        /**
         * Returns an editor instance for a given id.
         *
         * @method get
         * @param {String/Number} id The id or index of the editor instance to return.
         * @return {tinymce.Editor/Array} Editor instance or an array of editor instances.
         * @example
         * // Adds an onclick event to an editor by id
         * tinymce.get('mytextbox').on('click', (e) => {
         *   ed.windowManager.alert('Hello world!');
         * });
         *
         * // Adds an onclick event to an editor by index
         * tinymce.get(0).on('click', (e) => {
         *   ed.windowManager.alert('Hello world!');
         * });
         *
         * // Adds an onclick event to an editor by id (longer version)
         * tinymce.EditorManager.get('mytextbox').on('click', (e) => {
         *   ed.windowManager.alert('Hello world!');
         * });
         */
        get(id) {
          if (arguments.length === 0) {
            return editors.slice(0);
          } else if (isString(id)) {
            return find$2(editors, (editor) => {
              return editor.id === id;
            }).getOr(null);
          } else if (isNumber(id)) {
            return editors[id] ? editors[id] : null;
          } else {
            return null;
          }
        },
        /**
         * Adds an editor instance to the editor collection. This will also set it as the active editor.
         *
         * @method add
         * @param {tinymce.Editor} editor Editor instance to add to the collection.
         * @return {tinymce.Editor} The same instance that got passed in.
         */
        add(editor) {
          const self = this;
          const existingEditor = self.get(editor.id);
          if (existingEditor === editor) {
            return editor;
          }
          if (existingEditor === null) {
            editors.push(editor);
          }
          toggleGlobalEvents(true);
          self.activeEditor = editor;
          self.dispatch("AddEditor", { editor });
          if (!beforeUnloadDelegate) {
            beforeUnloadDelegate = (e) => {
              const event = self.dispatch("BeforeUnload");
              if (event.returnValue) {
                e.preventDefault();
                e.returnValue = event.returnValue;
                return event.returnValue;
              }
            };
            window.addEventListener("beforeunload", beforeUnloadDelegate);
          }
          return editor;
        },
        /**
         * Creates an editor instance and adds it to the EditorManager collection.
         *
         * @method createEditor
         * @param {String} id Instance id to use for editor.
         * @param {Object} options Editor instance options.
         * @return {tinymce.Editor} Editor instance that got created.
         */
        createEditor(id, options) {
          return this.add(new Editor(id, options, this));
        },
        /**
         * Removes a editor or editors form page.
         *
         * @example
         * // Remove all editors bound to divs
         * tinymce.remove('div');
         *
         * // Remove all editors bound to textareas
         * tinymce.remove('textarea');
         *
         * // Remove all editors
         * tinymce.remove();
         *
         * // Remove specific instance by id
         * tinymce.remove('#id');
         *
         * @method remove
         * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove.
         * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
         */
        remove(selector) {
          const self = this;
          let editor;
          if (!selector) {
            for (let i = editors.length - 1; i >= 0; i--) {
              self.remove(editors[i]);
            }
            return;
          }
          if (isString(selector)) {
            each(DOM.select(selector), (elm) => {
              editor = self.get(elm.id);
              if (editor) {
                self.remove(editor);
              }
            });
            return;
          }
          editor = selector;
          if (isNull(self.get(editor.id))) {
            return null;
          }
          if (removeEditorFromList(editor)) {
            self.dispatch("RemoveEditor", { editor });
          }
          if (editors.length === 0) {
            window.removeEventListener("beforeunload", beforeUnloadDelegate);
          }
          editor.remove();
          toggleGlobalEvents(editors.length > 0);
          return editor;
        },
        /**
         * Executes a specific command on the currently active editor.
         *
         * @method execCommand
         * @param {String} cmd Command to perform for example Bold.
         * @param {Boolean} ui Optional boolean state if a UI should be presented for the command or not.
         * @param {Object/String/Number/Boolean} value Optional value parameter like for example an URL to a link.
         * @return {Boolean} true/false if the command was executed or not.
         */
        execCommand(cmd, ui, value2) {
          const self = this;
          const editorId = isObject(value2) ? value2.id ?? value2.index : value2;
          switch (cmd) {
            case "mceAddEditor": {
              if (!self.get(editorId)) {
                const editorOptions = value2.options;
                new Editor(editorId, editorOptions, self).render();
              }
              return true;
            }
            case "mceRemoveEditor": {
              const editor = self.get(editorId);
              if (editor) {
                editor.remove();
              }
              return true;
            }
            case "mceToggleEditor": {
              const editor = self.get(editorId);
              if (!editor) {
                self.execCommand("mceAddEditor", false, value2);
                return true;
              }
              if (editor.isHidden()) {
                editor.show();
              } else {
                editor.hide();
              }
              return true;
            }
          }
          if (self.activeEditor) {
            return self.activeEditor.execCommand(cmd, ui, value2);
          }
          return false;
        },
        /**
         * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
         *
         * @method triggerSave
         * @example
         * // Saves all contents
         * tinyMCE.triggerSave();
         */
        triggerSave: () => {
          each(editors, (editor) => {
            editor.save();
          });
        },
        /**
         * Adds a language pack, this gets called by the loaded language files like en.js.
         *
         * @method addI18n
         * @param {String} code Optional language code.
         * @param {Object} items Name/value object with translations.
         */
        addI18n: (code, items) => {
          I18n.add(code, items);
        },
        /**
         * Translates the specified string using the language pack items.
         *
         * @method translate
         * @param {String/Array/Object} text String to translate
         * @return {String} Translated string.
         */
        translate: (text2) => {
          return I18n.translate(text2);
        },
        /**
         * Sets the active editor instance and fires the deactivate/activate events.
         *
         * @method setActive
         * @param {tinymce.Editor} editor Editor instance to set as the active instance.
         */
        setActive(editor) {
          const activeEditor = this.activeEditor;
          if (this.activeEditor !== editor) {
            if (activeEditor) {
              activeEditor.dispatch("deactivate", { relatedTarget: editor });
            }
            editor.dispatch("activate", { relatedTarget: activeEditor });
          }
          this.activeEditor = editor;
        },
        _setBaseUrl(baseUrl) {
          this.baseURL = new URI(this.documentBaseURL).toAbsolute(baseUrl.replace(/\/+$/, ""));
          this.baseURI = new URI(this.baseURL);
        },
        _addLicenseKeyManager: (addOn) => LicenseKeyManagerLoader.add(addOn)
      };
      EditorManager.setup();
      const setup = () => {
        const dataValue = value$1();
        const FakeClipboardItem = (items) => ({
          items,
          types: keys(items),
          getType: (type2) => get$a(items, type2).getOrUndefined()
        });
        const write2 = (data2) => {
          dataValue.set(data2);
        };
        const read2 = () => dataValue.get().getOrUndefined();
        const clear2 = dataValue.clear;
        return {
          FakeClipboardItem,
          write: write2,
          read: read2,
          clear: clear2
        };
      };
      const FakeClipboard = setup();
      const min = Math.min, max = Math.max, round = Math.round;
      const relativePosition = (rect, targetRect, rel) => {
        let x = targetRect.x;
        let y = targetRect.y;
        const w = rect.w;
        const h = rect.h;
        const targetW = targetRect.w;
        const targetH = targetRect.h;
        const relChars = (rel || "").split("");
        if (relChars[0] === "b") {
          y += targetH;
        }
        if (relChars[1] === "r") {
          x += targetW;
        }
        if (relChars[0] === "c") {
          y += round(targetH / 2);
        }
        if (relChars[1] === "c") {
          x += round(targetW / 2);
        }
        if (relChars[3] === "b") {
          y -= h;
        }
        if (relChars[4] === "r") {
          x -= w;
        }
        if (relChars[3] === "c") {
          y -= round(h / 2);
        }
        if (relChars[4] === "c") {
          x -= round(w / 2);
        }
        return create$2(x, y, w, h);
      };
      const findBestRelativePosition = (rect, targetRect, constrainRect, rels) => {
        for (let i = 0; i < rels.length; i++) {
          const pos = relativePosition(rect, targetRect, rels[i]);
          if (pos.x >= constrainRect.x && pos.x + pos.w <= constrainRect.w + constrainRect.x && pos.y >= constrainRect.y && pos.y + pos.h <= constrainRect.h + constrainRect.y) {
            return rels[i];
          }
        }
        return null;
      };
      const inflate = (rect, w, h) => {
        return create$2(rect.x - w, rect.y - h, rect.w + w * 2, rect.h + h * 2);
      };
      const intersect = (rect, cropRect) => {
        const x1 = max(rect.x, cropRect.x);
        const y1 = max(rect.y, cropRect.y);
        const x2 = min(rect.x + rect.w, cropRect.x + cropRect.w);
        const y2 = min(rect.y + rect.h, cropRect.y + cropRect.h);
        if (x2 - x1 < 0 || y2 - y1 < 0) {
          return null;
        }
        return create$2(x1, y1, x2 - x1, y2 - y1);
      };
      const clamp = (rect, clampRect, fixedSize) => {
        let x1 = rect.x;
        let y1 = rect.y;
        let x2 = rect.x + rect.w;
        let y2 = rect.y + rect.h;
        const cx2 = clampRect.x + clampRect.w;
        const cy2 = clampRect.y + clampRect.h;
        const underflowX1 = max(0, clampRect.x - x1);
        const underflowY1 = max(0, clampRect.y - y1);
        const overflowX2 = max(0, x2 - cx2);
        const overflowY2 = max(0, y2 - cy2);
        x1 += underflowX1;
        y1 += underflowY1;
        if (fixedSize) {
          x2 += underflowX1;
          y2 += underflowY1;
          x1 -= overflowX2;
          y1 -= overflowY2;
        }
        x2 -= overflowX2;
        y2 -= overflowY2;
        return create$2(x1, y1, x2 - x1, y2 - y1);
      };
      const create$2 = (x, y, w, h) => {
        return { x, y, w, h };
      };
      const fromClientRect = (clientRect) => {
        return create$2(clientRect.left, clientRect.top, clientRect.width, clientRect.height);
      };
      const Rect = {
        inflate,
        relativePosition,
        findBestRelativePosition,
        intersect,
        clamp,
        create: create$2,
        fromClientRect
      };
      const awaiter = (resolveCb, rejectCb, timeout = 1e3) => {
        let done = false;
        let timer = null;
        const complete = (completer) => (...args) => {
          if (!done) {
            done = true;
            if (timer !== null) {
              window.clearTimeout(timer);
              timer = null;
            }
            completer.apply(null, args);
          }
        };
        const resolve2 = complete(resolveCb);
        const reject = complete(rejectCb);
        const start2 = (...args) => {
          if (!done && timer === null) {
            timer = window.setTimeout(() => reject.apply(null, args), timeout);
          }
        };
        return {
          start: start2,
          resolve: resolve2,
          reject
        };
      };
      const create$1 = () => {
        const tasks = {};
        const resultFns = {};
        const resources = {};
        const load = (id, url) => {
          const loadErrMsg = `Script at URL "${url}" failed to load`;
          const runErrMsg = `Script at URL "${url}" did not call \`tinymce.Resource.add('${id}', data)\` within 1 second`;
          if (tasks[id] !== void 0) {
            return tasks[id];
          } else {
            const task = new Promise((resolve2, reject) => {
              const waiter = awaiter(resolve2, reject);
              resultFns[id] = waiter.resolve;
              ScriptLoader.ScriptLoader.loadScript(url).then(() => waiter.start(runErrMsg), () => waiter.reject(loadErrMsg));
            });
            tasks[id] = task;
            return task;
          }
        };
        const add2 = (id, data2) => {
          if (resultFns[id] !== void 0) {
            resultFns[id](data2);
            delete resultFns[id];
          }
          tasks[id] = Promise.resolve(data2);
          resources[id] = data2;
        };
        const has2 = (id) => {
          return id in resources;
        };
        const unload = (id) => {
          delete tasks[id];
          delete resources[id];
        };
        const get2 = (id) => resources[id];
        return {
          load,
          add: add2,
          has: has2,
          get: get2,
          unload
        };
      };
      const Resource = create$1();
      const create = () => (() => {
        let data2 = {};
        let keys2 = [];
        const storage = {
          getItem: (key) => {
            const item = data2[key];
            return item ? item : null;
          },
          setItem: (key, value2) => {
            keys2.push(key);
            data2[key] = String(value2);
          },
          key: (index) => {
            return keys2[index];
          },
          removeItem: (key) => {
            keys2 = keys2.filter((k) => k === key);
            delete data2[key];
          },
          clear: () => {
            keys2 = [];
            data2 = {};
          },
          length: 0
        };
        Object.defineProperty(storage, "length", {
          get: () => keys2.length,
          configurable: false,
          enumerable: false
        });
        return storage;
      })();
      let localStorage;
      try {
        const test = "__storage_test__";
        localStorage = window.localStorage;
        localStorage.setItem(test, test);
        localStorage.removeItem(test);
      } catch {
        localStorage = create();
      }
      var LocalStorage = localStorage;
      const publicApi = {
        geom: {
          Rect
        },
        util: {
          Delay,
          Tools,
          VK,
          URI,
          EventDispatcher,
          Observable,
          I18n,
          LocalStorage,
          ImageUploader
        },
        dom: {
          EventUtils,
          TreeWalker: DomTreeWalker,
          TextSeeker,
          DOMUtils,
          ScriptLoader,
          RangeUtils,
          Serializer: DomSerializer,
          StyleSheetLoader,
          ControlSelection,
          BookmarkManager,
          Selection: EditorSelection,
          Event: EventUtils.Event
        },
        html: {
          Styles,
          Entities,
          Node: AstNode,
          Schema,
          DomParser,
          Writer,
          Serializer: HtmlSerializer
        },
        Env,
        AddOnManager,
        Annotator,
        Formatter,
        UndoManager,
        EditorCommands,
        WindowManager,
        NotificationManager,
        EditorObservable,
        Shortcuts,
        Editor,
        FocusManager,
        EditorManager,
        // Global instances
        DOM: DOMUtils.DOM,
        ScriptLoader: ScriptLoader.ScriptLoader,
        PluginManager,
        ThemeManager,
        ModelManager,
        IconManager,
        Resource,
        FakeClipboard,
        // Global utility functions
        trim: Tools.trim,
        isArray: Tools.isArray,
        is: Tools.is,
        toArray: Tools.toArray,
        makeMap: Tools.makeMap,
        each: Tools.each,
        map: Tools.map,
        grep: Tools.grep,
        inArray: Tools.inArray,
        extend: Tools.extend,
        walk: Tools.walk,
        resolve: Tools.resolve,
        explode: Tools.explode,
        _addCacheSuffix: Tools._addCacheSuffix
      };
      const tinymce$1 = Tools.extend(EditorManager, publicApi);
      const exportToModuleLoaders = (tinymce2) => {
        if (typeof module === "object") {
          try {
            module.exports = tinymce2;
          } catch {
          }
        }
      };
      const exportToWindowGlobal = (tinymce2) => {
        window.tinymce = tinymce2;
        window.tinyMCE = tinymce2;
      };
      exportToWindowGlobal(tinymce$1);
      exportToModuleLoaders(tinymce$1);
    })();
  }
});
export default require_tinymce();
/*! Bundled license information:

tinymce/tinymce.js:
  (*! @license DOMPurify 3.2.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.6/LICENSE *)
*/
//# sourceMappingURL=tinymce.js.map
