1/** 2 * TinyMCE version 6.6.2 (2023-08-0
vendor: 243,886 bytes, lines 2-7018
29) 3 */ 4 5(function () { 6 'use strict'; 7 8 var typeOf$1 = function (x) { 9 if (x === null) { 10 return 'null'; 11 } 12 if (x === undefined) { 13 return 'undefined'; 14 } 15 var t = typeof x; 16 if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) { 17 return 'array'; 18 } 19 if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) { 20 return 'string'; 21 } 22 return t; 23 }; 24 var isEquatableType = function (x) { 25 return [ 26 'undefined', 27 'boolean', 28 'number', 29 'string', 30 'function', 31 'xml', 32 'null' 33 ].indexOf(x) !== -1; 34 }; 35 36 var sort$1 = function (xs, compareFn) { 37 var clone = Array.prototype.slice.call(xs); 38 return clone.sort(compareFn); 39 }; 40 41 var contramap = function (eqa, f) { 42 return eq$2(function (x, y) { 43 return eqa.eq(f(x), f(y)); 44 }); 45 }; 46 var eq$2 = function (f) { 47 return { eq: f }; 48 }; 49 var tripleEq = eq$2(function (x, y) { 50 return x === y; 51 }); 52 var eqString = tripleEq; 53 var eqArray = function (eqa) { 54 return eq$2(function (x, y) { 55 if (x.length !== y.length) { 56 return false; 57 } 58 var len = x.length; 59 for (var i = 0; i < len; i++) { 60 if (!eqa.eq(x[i], y[i])) { 61 return false; 62 } 63 } 64 return true; 65 }); 66 }; 67 var eqSortedArray = function (eqa, compareFn) { 68 return contramap(eqArray(eqa), function (xs) { 69 return sort$1(xs, compareFn); 70 }); 71 }; 72 var eqRecord = function (eqa) { 73 return eq$2(function (x, y) { 74 var kx = Object.keys(x); 75 var ky = Object.keys(y); 76 if (!eqSortedArray(eqString).eq(kx, ky)) { 77 return false; 78 } 79 var len = kx.length; 80 for (var i = 0; i < len; i++) { 81 var q = kx[i]; 82 if (!eqa.eq(x[q], y[q])) { 83 return false; 84 } 85 } 86 return true; 87 }); 88 }; 89 var eqAny = eq$2(function (x, y) { 90 if (x === y) { 91 return true; 92 } 93 var tx = typeOf$1(x); 94 var ty = typeOf$1(y); 95 if (tx !== ty) { 96 return false; 97 } 98 if (isEquatableType(tx)) { 99 return x === y; 100 } else if (tx === 'array') { 101 return eqArray(eqAny).eq(x, y); 102 } else if (tx === 'object') { 103 return eqRecord(eqAny).eq(x, y); 104 } 105 return false; 106 }); 107 108 const getPrototypeOf$2 = Object.getPrototypeOf; 109 const hasProto = (v, constructor, predicate) => { 110 var _a; 111 if (predicate(v, constructor.prototype)) { 112 return true; 113 } else { 114 return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name; 115 } 116 }; 117 const typeOf = x => { 118 const t = typeof x; 119 if (x === null) { 120 return 'null'; 121 } else if (t === 'object' && Array.isArray(x)) { 122 return 'array'; 123 } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) { 124 return 'string'; 125 } else { 126 return t; 127 } 128 }; 129 const isType$1 = type => value => typeOf(value) === type; 130 const isSimpleType = type => value => typeof value === type; 131 const eq$1 = t => a => t === a; 132 const is$4 = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf$2(o) === proto); 133 const isString = isType$1('string'); 134 const isObject = isType$1('object'); 135 const isPlainObject = value => is$4(value, Object); 136 const isArray$1 = isType$1('array'); 137 const isNull = eq$1(null); 138 const isBoolean = isSimpleType('boolean'); 139 const isUndefined = eq$1(undefined); 140 const isNullable = a => a === null || a === undefined; 141 const isNonNullable = a => !isNullable(a); 142 const isFunction = isSimpleType('function'); 143 const isNumber = isSimpleType('number'); 144 const isArrayOf = (value, pred) => { 145 if (isArray$1(value)) { 146 for (let i = 0, len = value.length; i < len; ++i) { 147 if (!pred(value[i])) { 148 return false; 149 } 150 } 151 return true; 152 } 153 return false; 154 }; 155 156 const noop = () => { 157 }; 158 const compose = (fa, fb) => { 159 return (...args) => { 160 return fa(fb.apply(null, args)); 161 }; 162 }; 163 const compose1 = (fbc, fab) => a => fbc(fab(a)); 164 const constant = value => { 165 return () => { 166 return value; 167 }; 168 }; 169 const identity = x => { 170 return x; 171 }; 172 const tripleEquals = (a, b) => { 173 return a === b; 174 }; 175 function curry(fn, ...initialArgs) { 176 return (...restArgs) => { 177 const all = initialArgs.concat(restArgs); 178 return fn.apply(null, all); 179 }; 180 } 181 const not = f => t => !f(t); 182 const die = msg => { 183 return () => { 184 throw new Error(msg); 185 }; 186 }; 187 const apply$1 = f => { 188 return f(); 189 }; 190 const call = f => { 191 f(); 192 }; 193 const never = constant(false); 194 const always = constant(true); 195 196 class Optional { 197 constructor(tag, value) { 198 this.tag = tag; 199 this.value = value; 200 } 201 static some(value) { 202 return new Optional(true, value); 203 } 204 static none() { 205 return Optional.singletonNone; 206 } 207 fold(onNone, onSome) { 208 if (this.tag) { 209 return onSome(this.value); 210 } else { 211 return onNone(); 212 } 213 } 214 isSome() { 215 return this.tag; 216 } 217 isNone() { 218 return !this.tag; 219 } 220 map(mapper) { 221 if (this.tag) { 222 return Optional.some(mapper(this.value)); 223 } else { 224 return Optional.none(); 225 } 226 } 227 bind(binder) { 228 if (this.tag) { 229 return binder(this.value); 230 } else { 231 return Optional.none(); 232 } 233 } 234 exists(predicate) { 235 return this.tag && predicate(this.value); 236 } 237 forall(predicate) { 238 return !this.tag || predicate(this.value); 239 } 240 filter(predicate) { 241 if (!this.tag || predicate(this.value)) { 242 return this; 243 } else { 244 return Optional.none(); 245 } 246 } 247 getOr(replacement) { 248 return this.tag ? this.value : replacement; 249 } 250 or(replacement) { 251 return this.tag ? this : replacement; 252 } 253 getOrThunk(thunk) { 254 return this.tag ? this.value : thunk(); 255 } 256 orThunk(thunk) { 257 return this.tag ? this : thunk(); 258 } 259 getOrDie(message) { 260 if (!this.tag) { 261 throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None'); 262 } else { 263 return this.value; 264 } 265 } 266 static from(value) { 267 return isNonNullable(value) ? Optional.some(value) : Optional.none(); 268 } 269 getOrNull() { 270 return this.tag ? this.value : null; 271 } 272 getOrUndefined() { 273 return this.value; 274 } 275 each(worker) { 276 if (this.tag) { 277 worker(this.value); 278 } 279 } 280 toArray() { 281 return this.tag ? [this.value] : []; 282 } 283 toString() { 284 return this.tag ? `some(${ this.value })` : 'none()'; 285 } 286 } 287 Optional.singletonNone = new Optional(false); 288 289 const nativeSlice = Array.prototype.slice; 290 const nativeIndexOf = Array.prototype.indexOf; 291 const nativePush = Array.prototype.push; 292 const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t); 293 const indexOf$1 = (xs, x) => { 294 const r = rawIndexOf(xs, x); 295 return r === -1 ? Optional.none() : Optional.some(r); 296 }; 297 const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1; 298 const exists = (xs, pred) => { 299 for (let i = 0, len = xs.length; i < len; i++) { 300 const x = xs[i]; 301 if (pred(x, i)) { 302 return true; 303 } 304 } 305 return false; 306 }; 307 const map$3 = (xs, f) => { 308 const len = xs.length; 309 const r = new Array(len); 310 for (let i = 0; i < len; i++) { 311 const x = xs[i]; 312 r[i] = f(x, i); 313 } 314 return r; 315 }; 316 const each$e = (xs, f) => { 317 for (let i = 0, len = xs.length; i < len; i++) { 318 const x = xs[i]; 319 f(x, i); 320 } 321 }; 322 const eachr = (xs, f) => { 323 for (let i = xs.length - 1; i >= 0; i--) { 324 const x = xs[i]; 325 f(x, i); 326 } 327 }; 328 const partition$2 = (xs, pred) => { 329 const pass = []; 330 const fail = []; 331 for (let i = 0, len = xs.length; i < len; i++) { 332 const x = xs[i]; 333 const arr = pred(x, i) ? pass : fail; 334 arr.push(x); 335 } 336 return { 337 pass, 338 fail 339 }; 340 }; 341 const filter$5 = (xs, pred) => { 342 const r = []; 343 for (let i = 0, len = xs.length; i < len; i++) { 344 const x = xs[i]; 345 if (pred(x, i)) { 346 r.push(x); 347 } 348 } 349 return r; 350 }; 351 const foldr = (xs, f, acc) => { 352 eachr(xs, (x, i) => { 353 acc = f(acc, x, i); 354 }); 355 return acc; 356 }; 357 const foldl = (xs, f, acc) => { 358 each$e(xs, (x, i) => { 359 acc = f(acc, x, i); 360 }); 361 return acc; 362 }; 363 const findUntil$1 = (xs, pred, until) => { 364 for (let i = 0, len = xs.length; i < len; i++) { 365 const x = xs[i]; 366 if (pred(x, i)) { 367 return Optional.some(x); 368 } else if (until(x, i)) { 369 break; 370 } 371 } 372 return Optional.none(); 373 }; 374 const find$2 = (xs, pred) => { 375 return findUntil$1(xs, pred, never); 376 }; 377 const findIndex$2 = (xs, pred) => { 378 for (let i = 0, len = xs.length; i < len; i++) { 379 const x = xs[i]; 380 if (pred(x, i)) { 381 return Optional.some(i); 382 } 383 } 384 return Optional.none(); 385 }; 386 const flatten = xs => { 387 const r = []; 388 for (let i = 0, len = xs.length; i < len; ++i) { 389 if (!isArray$1(xs[i])) { 390 throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs); 391 } 392 nativePush.apply(r, xs[i]); 393 } 394 return r; 395 }; 396 const bind$3 = (xs, f) => flatten(map$3(xs, f)); 397 const forall = (xs, pred) => { 398 for (let i = 0, len = xs.length; i < len; ++i) { 399 const x = xs[i]; 400 if (pred(x, i) !== true) { 401 return false; 402 } 403 } 404 return true; 405 }; 406 const reverse = xs => { 407 const r = nativeSlice.call(xs, 0); 408 r.reverse(); 409 return r; 410 }; 411 const difference = (a1, a2) => filter$5(a1, x => !contains$2(a2, x)); 412 const mapToObject = (xs, f) => { 413 const r = {}; 414 for (let i = 0, len = xs.length; i < len; i++) { 415 const x = xs[i]; 416 r[String(x)] = f(x, i); 417 } 418 return r; 419 }; 420 const sort = (xs, comparator) => { 421 const copy = nativeSlice.call(xs, 0); 422 copy.sort(comparator); 423 return copy; 424 }; 425 const get$b = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none(); 426 const head = xs => get$b(xs, 0); 427 const last$3 = xs => get$b(xs, xs.length - 1); 428 const from = isFunction(Array.from) ? Array.from : x => nativeSlice.call(x); 429 const findMap = (arr, f) => { 430 for (let i = 0; i < arr.length; i++) { 431 const r = f(arr[i], i); 432 if (r.isSome()) { 433 return r; 434 } 435 } 436 return Optional.none(); 437 }; 438 const unique$1 = (xs, comparator) => { 439 const r = []; 440 const isDuplicated = isFunction(comparator) ? x => exists(r, i => comparator(i, x)) : x => contains$2(r, x); 441 for (let i = 0, len = xs.length; i < len; i++) { 442 const x = xs[i]; 443 if (!isDuplicated(x)) { 444 r.push(x); 445 } 446 } 447 return r; 448 }; 449 450 const keys = Object.keys; 451 const hasOwnProperty$2 = Object.hasOwnProperty; 452 const each$d = (obj, f) => { 453 const props = keys(obj); 454 for (let k = 0, len = props.length; k < len; k++) { 455 const i = props[k]; 456 const x = obj[i]; 457 f(x, i); 458 } 459 }; 460 const map$2 = (obj, f) => { 461 return tupleMap(obj, (x, i) => ({ 462 k: i, 463 v: f(x, i) 464 })); 465 }; 466 const tupleMap = (obj, f) => { 467 const r = {}; 468 each$d(obj, (x, i) => { 469 const tuple = f(x, i); 470 r[tuple.k] = tuple.v; 471 }); 472 return r; 473 }; 474 const objAcc = r => (x, i) => { 475 r[i] = x; 476 }; 477 const internalFilter = (obj, pred, onTrue, onFalse) => { 478 each$d(obj, (x, i) => { 479 (pred(x, i) ? onTrue : onFalse)(x, i); 480 }); 481 }; 482 const bifilter = (obj, pred) => { 483 const t = {}; 484 const f = {}; 485 internalFilter(obj, pred, objAcc(t), objAcc(f)); 486 return { 487 t, 488 f 489 }; 490 }; 491 const filter$4 = (obj, pred) => { 492 const t = {}; 493 internalFilter(obj, pred, objAcc(t), noop); 494 return t; 495 }; 496 const mapToArray = (obj, f) => { 497 const r = []; 498 each$d(obj, (value, name) => { 499 r.push(f(value, name)); 500 }); 501 return r; 502 }; 503 const values = obj => { 504 return mapToArray(obj, identity); 505 }; 506 const get$a = (obj, key) => { 507 return has$2(obj, key) ? Optional.from(obj[key]) : Optional.none(); 508 }; 509 const has$2 = (obj, key) => hasOwnProperty$2.call(obj, key); 510 const hasNonNullableKey = (obj, key) => has$2(obj, key) && obj[key] !== undefined && obj[key] !== null; 511 const equal$1 = (a1, a2, eq = eqAny) => eqRecord(eq).eq(a1, a2); 512 513 const stringArray = a => { 514 const all = {}; 515 each$e(a, key => { 516 all[key] = {}; 517 }); 518 return keys(all); 519 }; 520 521 const isArrayLike = o => o.length !== undefined; 522 const isArray = Array.isArray; 523 const toArray$1 = obj => { 524 if (!isArray(obj)) { 525 const array = []; 526 for (let i = 0, l = obj.length; i < l; i++) { 527 array[i] = obj[i]; 528 } 529 return array; 530 } else { 531 return obj; 532 } 533 }; 534 const each$c = (o, cb, s) => { 535 if (!o) { 536 return false; 537 } 538 s = s || o; 539 if (isArrayLike(o)) { 540 for (let n = 0, l = o.length; n < l; n++) { 541 if (cb.call(s, o[n], n, o) === false) { 542 return false; 543 } 544 } 545 } else { 546 for (const n in o) { 547 if (has$2(o, n)) { 548 if (cb.call(s, o[n], n, o) === false) { 549 return false; 550 } 551 } 552 } 553 } 554 return true; 555 }; 556 const map$1 = (array, callback) => { 557 const out = []; 558 each$c(array, (item, index) => { 559 out.push(callback(item, index, array)); 560 }); 561 return out; 562 }; 563 const filter$3 = (a, f) => { 564 const o = []; 565 each$c(a, (v, index) => { 566 if (!f || f(v, index, a)) { 567 o.push(v); 568 } 569 }); 570 return o; 571 }; 572 const indexOf = (a, v) => { 573 if (a) { 574 for (let i = 0, l = a.length; i < l; i++) { 575 if (a[i] === v) { 576 return i; 577 } 578 } 579 } 580 return -1; 581 }; 582 const reduce = (collection, iteratee, accumulator, thisArg) => { 583 let acc = isUndefined(accumulator) ? collection[0] : accumulator; 584 for (let i = 0; i < collection.length; i++) { 585 acc = iteratee.call(thisArg, acc, collection[i], i); 586 } 587 return acc; 588 }; 589 const findIndex$1 = (array, predicate, thisArg) => { 590 for (let i = 0, l = array.length; i < l; i++) { 591 if (predicate.call(thisArg, array[i], i, array)) { 592 return i; 593 } 594 } 595 return -1; 596 }; 597 const last$2 = collection => collection[collection.length - 1]; 598 599 const cached = f => { 600 let called = false; 601 let r; 602 return (...args) => { 603 if (!called) { 604 called = true; 605 r = f.apply(null, args); 606 } 607 return r; 608 }; 609 }; 610 611 const DeviceType = (os, browser, userAgent, mediaMatch) => { 612 const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true; 613 const isiPhone = os.isiOS() && !isiPad; 614 const isMobile = os.isiOS() || os.isAndroid(); 615 const isTouch = isMobile || mediaMatch('(pointer:coarse)'); 616 const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)'); 617 const isPhone = isiPhone || isMobile && !isTablet; 618 const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false; 619 const isDesktop = !isPhone && !isTablet && !iOSwebview; 620 return { 621 isiPad: constant(isiPad), 622 isiPhone: constant(isiPhone), 623 isTablet: constant(isTablet), 624 isPhone: constant(isPhone), 625 isTouch: constant(isTouch), 626 isAndroid: os.isAndroid, 627 isiOS: os.isiOS, 628 isWebView: constant(iOSwebview), 629 isDesktop: constant(isDesktop) 630 }; 631 }; 632 633 const firstMatch = (regexes, s) => { 634 for (let i = 0; i < regexes.length; i++) { 635 const x = regexes[i]; 636 if (x.test(s)) { 637 return x; 638 } 639 } 640 return undefined; 641 }; 642 const find$1 = (regexes, agent) => { 643 const r = firstMatch(regexes, agent); 644 if (!r) { 645 return { 646 major: 0, 647 minor: 0 648 }; 649 } 650 const group = i => { 651 return Number(agent.replace(r, '$' + i)); 652 }; 653 return nu$3(group(1), group(2)); 654 }; 655 const detect$5 = (versionRegexes, agent) => { 656 const cleanedAgent = String(agent).toLowerCase(); 657 if (versionRegexes.length === 0) { 658 return unknown$2(); 659 } 660 return find$1(versionRegexes, cleanedAgent); 661 }; 662 const unknown$2 = () => { 663 return nu$3(0, 0); 664 }; 665 const nu$3 = (major, minor) => { 666 return { 667 major, 668 minor 669 }; 670 }; 671 const Version = { 672 nu: nu$3, 673 detect: detect$5, 674 unknown: unknown$2 675 }; 676 677 const detectBrowser$1 = (browsers, userAgentData) => { 678 return findMap(userAgentData.brands, uaBrand => { 679 const lcBrand = uaBrand.brand.toLowerCase(); 680 return find$2(browsers, browser => { 681 var _a; 682 return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase()); 683 }).map(info => ({ 684 current: info.name, 685 version: Version.nu(parseInt(uaBrand.version, 10), 0) 686 })); 687 }); 688 }; 689 690 const detect$4 = (candidates, userAgent) => { 691 const agent = String(userAgent).toLowerCase(); 692 return find$2(candidates, candidate => { 693 return candidate.search(agent); 694 }); 695 }; 696 const detectBrowser = (browsers, userAgent) => { 697 return detect$4(browsers, userAgent).map(browser => { 698 const version = Version.detect(browser.versionRegexes, userAgent); 699 return { 700 current: browser.name, 701 version 702 }; 703 }); 704 }; 705 const detectOs = (oses, userAgent) => { 706 return detect$4(oses, userAgent).map(os => { 707 const version = Version.detect(os.versionRegexes, userAgent); 708 return { 709 current: os.name, 710 version 711 }; 712 }); 713 }; 714 715 const removeFromStart = (str, numChars) => { 716 return str.substring(numChars); 717 }; 718 719 const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr; 720 const removeLeading = (str, prefix) => { 721 return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str; 722 }; 723 const contains$1 = (str, substr, start = 0, end) => { 724 const idx = str.indexOf(substr, start); 725 if (idx !== -1) { 726 return isUndefined(end) ? true : idx + substr.length <= end; 727 } else { 728 return false; 729 } 730 }; 731 const startsWith = (str, prefix) => { 732 return checkRange(str, prefix, 0); 733 }; 734 const endsWith = (str, suffix) => { 735 return checkRange(str, suffix, str.length - suffix.length); 736 }; 737 const blank = r => s => s.replace(r, ''); 738 const trim$3 = blank(/^\s+|\s+$/g); 739 const lTrim = blank(/^\s+/g); 740 const rTrim = blank(/\s+$/g); 741 const isNotEmpty = s => s.length > 0; 742 const isEmpty$3 = s => !isNotEmpty(s); 743 const repeat = (s, count) => count <= 0 ? '' : new Array(count + 1).join(s); 744 const toInt = (value, radix = 10) => { 745 const num = parseInt(value, radix); 746 return isNaN(num) ? Optional.none() : Optional.some(num); 747 }; 748 749 const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/; 750 const checkContains = target => { 751 return uastring => { 752 return contains$1(uastring, target); 753 }; 754 }; 755 const browsers = [ 756 { 757 name: 'Edge', 758 versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/], 759 search: uastring => { 760 return contains$1(uastring, 'edge/') && contains$1(uastring, 'chrome') && contains$1(uastring, 'safari') && contains$1(uastring, 'applewebkit'); 761 } 762 }, 763 { 764 name: 'Chromium', 765 brand: 'Chromium', 766 versionRegexes: [ 767 /.*?chrome\/([0-9]+)\.([0-9]+).*/, 768 normalVersionRegex 769 ], 770 search: uastring => { 771 return contains$1(uastring, 'chrome') && !contains$1(uastring, 'chromeframe'); 772 } 773 }, 774 { 775 name: 'IE', 776 versionRegexes: [ 777 /.*?msie\ ?([0-9]+)\.([0-9]+).*/, 778 /.*?rv:([0-9]+)\.([0-9]+).*/ 779 ], 780 search: uastring => { 781 return contains$1(uastring, 'msie') || contains$1(uastring, 'trident'); 782 } 783 }, 784 { 785 name: 'Opera', 786 versionRegexes: [ 787 normalVersionRegex, 788 /.*?opera\/([0-9]+)\.([0-9]+).*/ 789 ], 790 search: checkContains('opera') 791 }, 792 { 793 name: 'Firefox', 794 versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/], 795 search: checkContains('firefox') 796 }, 797 { 798 name: 'Safari', 799 versionRegexes: [ 800 normalVersionRegex, 801 /.*?cpu os ([0-9]+)_([0-9]+).*/ 802 ], 803 search: uastring => { 804 return (contains$1(uastring, 'safari') || contains$1(uastring, 'mobile/')) && contains$1(uastring, 'applewebkit'); 805 } 806 } 807 ]; 808 const oses = [ 809 { 810 name: 'Windows', 811 search: checkContains('win'), 812 versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/] 813 }, 814 { 815 name: 'iOS', 816 search: uastring => { 817 return contains$1(uastring, 'iphone') || contains$1(uastring, 'ipad'); 818 }, 819 versionRegexes: [ 820 /.*?version\/\ ?([0-9]+)\.([0-9]+).*/, 821 /.*cpu os ([0-9]+)_([0-9]+).*/, 822 /.*cpu iphone os ([0-9]+)_([0-9]+).*/ 823 ] 824 }, 825 { 826 name: 'Android', 827 search: checkContains('android'), 828 versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/] 829 }, 830 { 831 name: 'macOS', 832 search: checkContains('mac os x'), 833 versionRegexes: [/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/] 834 }, 835 { 836 name: 'Linux', 837 search: checkContains('linux'), 838 versionRegexes: [] 839 }, 840 { 841 name: 'Solaris', 842 search: checkContains('sunos'), 843 versionRegexes: [] 844 }, 845 { 846 name: 'FreeBSD', 847 search: checkContains('freebsd'), 848 versionRegexes: [] 849 }, 850 { 851 name: 'ChromeOS', 852 search: checkContains('cros'), 853 versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/] 854 } 855 ]; 856 const PlatformInfo = { 857 browsers: constant(browsers), 858 oses: constant(oses) 859 }; 860 861 const edge = 'Edge'; 862 const chromium = 'Chromium'; 863 const ie = 'IE'; 864 const opera = 'Opera'; 865 const firefox = 'Firefox'; 866 const safari = 'Safari'; 867 const unknown$1 = () => { 868 return nu$2({ 869 current: undefined, 870 version: Version.unknown() 871 }); 872 }; 873 const nu$2 = info => { 874 const current = info.current; 875 const version = info.version; 876 const isBrowser = name => () => current === name; 877 return { 878 current, 879 version, 880 isEdge: isBrowser(edge), 881 isChromium: isBrowser(chromium), 882 isIE: isBrowser(ie), 883 isOpera: isBrowser(opera), 884 isFirefox: isBrowser(firefox), 885 isSafari: isBrowser(safari) 886 }; 887 }; 888 const Browser = { 889 unknown: unknown$1, 890 nu: nu$2, 891 edge: constant(edge), 892 chromium: constant(chromium), 893 ie: constant(ie), 894 opera: constant(opera), 895 firefox: constant(firefox), 896 safari: constant(safari) 897 }; 898 899 const windows = 'Windows'; 900 const ios = 'iOS'; 901 const android = 'Android'; 902 const linux = 'Linux'; 903 const macos = 'macOS'; 904 const solaris = 'Solaris'; 905 const freebsd = 'FreeBSD'; 906 const chromeos = 'ChromeOS'; 907 const unknown = () => { 908 return nu$1({ 909 current: undefined, 910 version: Version.unknown() 911 }); 912 }; 913 const nu$1 = info => { 914 const current = info.current; 915 const version = info.version; 916 const isOS = name => () => current === name; 917 return { 918 current, 919 version, 920 isWindows: isOS(windows), 921 isiOS: isOS(ios), 922 isAndroid: isOS(android), 923 isMacOS: isOS(macos), 924 isLinux: isOS(linux), 925 isSolaris: isOS(solaris), 926 isFreeBSD: isOS(freebsd), 927 isChromeOS: isOS(chromeos) 928 }; 929 }; 930 const OperatingSystem = { 931 unknown, 932 nu: nu$1, 933 windows: constant(windows), 934 ios: constant(ios), 935 android: constant(android), 936 linux: constant(linux), 937 macos: constant(macos), 938 solaris: constant(solaris), 939 freebsd: constant(freebsd), 940 chromeos: constant(chromeos) 941 }; 942 943 const detect$3 = (userAgent, userAgentDataOpt, mediaMatch) => { 944 const browsers = PlatformInfo.browsers(); 945 const oses = PlatformInfo.oses(); 946 const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu); 947 const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu); 948 const deviceType = DeviceType(os, browser, userAgent, mediaMatch); 949 return { 950 browser, 951 os, 952 deviceType 953 }; 954 }; 955 const PlatformDetection = { detect: detect$3 }; 956 957 const mediaMatch = query => window.matchMedia(query).matches; 958 let platform$4 = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch)); 959 const detect$2 = () => platform$4(); 960 961 const userAgent = navigator.userAgent; 962 const platform$3 = detect$2(); 963 const browser$2 = platform$3.browser; 964 const os$1 = platform$3.os; 965 const deviceType = platform$3.deviceType; 966 const windowsPhone = userAgent.indexOf('Windows Phone') !== -1; 967 const Env = { 968 transparentSrc: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 969 documentMode: browser$2.isIE() ? document.documentMode || 7 : 10, 970 cacheSuffix: null, 971 container: null, 972 canHaveCSP: !browser$2.isIE(), 973 windowsPhone, 974 browser: { 975 current: browser$2.current, 976 version: browser$2.version, 977 isChromium: browser$2.isChromium, 978 isEdge: browser$2.isEdge, 979 isFirefox: browser$2.isFirefox, 980 isIE: browser$2.isIE, 981 isOpera: browser$2.isOpera, 982 isSafari: browser$2.isSafari 983 }, 984 os: { 985 current: os$1.current, 986 version: os$1.version, 987 isAndroid: os$1.isAndroid, 988 isChromeOS: os$1.isChromeOS, 989 isFreeBSD: os$1.isFreeBSD, 990 isiOS: os$1.isiOS, 991 isLinux: os$1.isLinux, 992 isMacOS: os$1.isMacOS, 993 isSolaris: os$1.isSolaris, 994 isWindows: os$1.isWindows 995 }, 996 deviceType: { 997 isDesktop: deviceType.isDesktop, 998 isiPad: deviceType.isiPad, 999 isiPhone: deviceType.isiPhone, 1000 isPhone: deviceType.isPhone, 1001 isTablet: deviceType.isTablet, 1002 isTouch: deviceType.isTouch, 1003 isWebView: deviceType.isWebView 1004 } 1005 }; 1006 1007 const whiteSpaceRegExp$1 = /^\s*|\s*$/g; 1008 const trim$2 = str => { 1009 return isNullable(str) ? '' : ('' + str).replace(whiteSpaceRegExp$1, ''); 1010 }; 1011 const is$3 = (obj, type) => { 1012 if (!type) { 1013 return obj !== undefined; 1014 } 1015 if (type === 'array' && isArray(obj)) { 1016 return true; 1017 } 1018 return typeof obj === type; 1019 }; 1020 const makeMap$4 = (items, delim, map = {}) => { 1021 const resolvedItems = isString(items) ? items.split(delim || ',') : items || []; 1022 let i = resolvedItems.length; 1023 while (i--) { 1024 map[resolvedItems[i]] = {}; 1025 } 1026 return map; 1027 }; 1028 const hasOwnProperty$1 = has$2; 1029 const extend$3 = (obj, ...exts) => { 1030 for (let i = 0; i < exts.length; i++) { 1031 const ext = exts[i]; 1032 for (const name in ext) { 1033 if (has$2(ext, name)) { 1034 const value = ext[name]; 1035 if (value !== undefined) { 1036 obj[name] = value; 1037 } 1038 } 1039 } 1040 } 1041 return obj; 1042 }; 1043 const walk$4 = function (o, f, n, s) { 1044 s = s || this; 1045 if (o) { 1046 if (n) { 1047 o = o[n]; 1048 } 1049 each$c(o, (o, i) => { 1050 if (f.call(s, o, i, n) === false) { 1051 return false; 1052 } else { 1053 walk$4(o, f, n, s); 1054 return true; 1055 } 1056 }); 1057 } 1058 }; 1059 const resolve$3 = (n, o = window) => { 1060 const path = n.split('.'); 1061 for (let i = 0, l = path.length; i < l; i++) { 1062 o = o[path[i]]; 1063 if (!o) { 1064 break; 1065 } 1066 } 1067 return o; 1068 }; 1069 const explode$3 = (s, d) => { 1070 if (isArray$1(s)) { 1071 return s; 1072 } else if (s === '') { 1073 return []; 1074 } else { 1075 return map$1(s.split(d || ','), trim$2); 1076 } 1077 }; 1078 const _addCacheSuffix = url => { 1079 const cacheSuffix = Env.cacheSuffix; 1080 if (cacheSuffix) { 1081 url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix; 1082 } 1083 return url; 1084 }; 1085 const Tools = { 1086 trim: trim$2, 1087 isArray: isArray, 1088 is: is$3, 1089 toArray: toArray$1, 1090 makeMap: makeMap$4, 1091 each: each$c, 1092 map: map$1, 1093 grep: filter$3, 1094 inArray: indexOf, 1095 hasOwn: hasOwnProperty$1, 1096 extend: extend$3, 1097 walk: walk$4, 1098 resolve: resolve$3, 1099 explode: explode$3, 1100 _addCacheSuffix 1101 }; 1102 1103 const is$2 = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs)); 1104 const cat = arr => { 1105 const r = []; 1106 const push = x => { 1107 r.push(x); 1108 }; 1109 for (let i = 0; i < arr.length; i++) { 1110 arr[i].each(push); 1111 } 1112 return r; 1113 }; 1114 const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none(); 1115 const lift3 = (oa, ob, oc, f) => oa.isSome() && ob.isSome() && oc.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie(), oc.getOrDie())) : Optional.none(); 1116 const someIf = (b, a) => b ? Optional.some(a) : Optional.none(); 1117 1118 const Global = typeof window !== 'undefined' ? window : Function('return this;')(); 1119 1120 const path = (parts, scope) => { 1121 let o = scope !== undefined && scope !== null ? scope : Global; 1122 for (let i = 0; i < parts.length && o !== undefined && o !== null; ++i) { 1123 o = o[parts[i]]; 1124 } 1125 return o; 1126 }; 1127 const resolve$2 = (p, scope) => { 1128 const parts = p.split('.'); 1129 return path(parts, scope); 1130 }; 1131 1132 const unsafe = (name, scope) => { 1133 return resolve$2(name, scope); 1134 }; 1135 const getOrDie = (name, scope) => { 1136 const actual = unsafe(name, scope); 1137 if (actual === undefined || actual === null) { 1138 throw new Error(name + ' not available on this browser'); 1139 } 1140 return actual; 1141 }; 1142 1143 const getPrototypeOf$1 = Object.getPrototypeOf; 1144 const sandHTMLElement = scope => { 1145 return getOrDie('HTMLElement', scope); 1146 }; 1147 const isPrototypeOf = x => { 1148 const scope = resolve$2('ownerDocument.defaultView', x); 1149 return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\w*Element$/.test(getPrototypeOf$1(x).constructor.name)); 1150 }; 1151 1152 const COMMENT = 8; 1153 const DOCUMENT = 9; 1154 const DOCUMENT_FRAGMENT = 11; 1155 const ELEMENT = 1; 1156 const TEXT = 3; 1157 1158 const name = element => { 1159 const r = element.dom.nodeName; 1160 return r.toLowerCase(); 1161 }; 1162 const type$1 = element => element.dom.nodeType; 1163 const isType = t => element => type$1(element) === t; 1164 const isComment$1 = element => type$1(element) === COMMENT || name(element) === '#comment'; 1165 const isHTMLElement = element => isElement$7(element) && isPrototypeOf(element.dom); 1166 const isElement$7 = isType(ELEMENT); 1167 const isText$b = isType(TEXT); 1168 const isDocument$2 = isType(DOCUMENT); 1169 const isDocumentFragment$1 = isType(DOCUMENT_FRAGMENT); 1170 const isTag = tag => e => isElement$7(e) && name(e) === tag; 1171 1172 const rawSet = (dom, key, value) => { 1173 if (isString(value) || isBoolean(value) || isNumber(value)) { 1174 dom.setAttribute(key, value + ''); 1175 } else { 1176 console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom); 1177 throw new Error('Attribute value was not simple'); 1178 } 1179 }; 1180 const set$3 = (element, key, value) => { 1181 rawSet(element.dom, key, value); 1182 }; 1183 const setAll$1 = (element, attrs) => { 1184 const dom = element.dom; 1185 each$d(attrs, (v, k) => { 1186 rawSet(dom, k, v); 1187 }); 1188 }; 1189 const get$9 = (element, key) => { 1190 const v = element.dom.getAttribute(key); 1191 return v === null ? undefined : v; 1192 }; 1193 const getOpt = (element, key) => Optional.from(get$9(element, key)); 1194 const has$1 = (element, key) => { 1195 const dom = element.dom; 1196 return dom && dom.hasAttribute ? dom.hasAttribute(key) : false; 1197 }; 1198 const remove$a = (element, key) => { 1199 element.dom.removeAttribute(key); 1200 }; 1201 const hasNone = element => { 1202 const attrs = element.dom.attributes; 1203 return attrs === undefined || attrs === null || attrs.length === 0; 1204 }; 1205 const clone$4 = element => foldl(element.dom.attributes, (acc, attr) => { 1206 acc[attr.name] = attr.value; 1207 return acc; 1208 }, {}); 1209 1210 const read$4 = (element, attr) => { 1211 const value = get$9(element, attr); 1212 return value === undefined || value === '' ? [] : value.split(' '); 1213 }; 1214 const add$4 = (element, attr, id) => { 1215 const old = read$4(element, attr); 1216 const nu = old.concat([id]); 1217 set$3(element, attr, nu.join(' ')); 1218 return true; 1219 }; 1220 const remove$9 = (element, attr, id) => { 1221 const nu = filter$5(read$4(element, attr), v => v !== id); 1222 if (nu.length > 0) { 1223 set$3(element, attr, nu.join(' ')); 1224 } else { 1225 remove$a(element, attr); 1226 } 1227 return false; 1228 }; 1229 1230 const supports = element => element.dom.classList !== undefined; 1231 const get$8 = element => read$4(element, 'class'); 1232 const add$3 = (element, clazz) => add$4(element, 'class', clazz); 1233 const remove$8 = (element, clazz) => remove$9(element, 'class', clazz); 1234 const toggle$2 = (element, clazz) => { 1235 if (contains$2(get$8(element), clazz)) { 1236 return remove$8(element, clazz); 1237 } else { 1238 return add$3(element, clazz); 1239 } 1240 }; 1241 1242 const add$2 = (element, clazz) => { 1243 if (supports(element)) { 1244 element.dom.classList.add(clazz); 1245 } else { 1246 add$3(element, clazz); 1247 } 1248 }; 1249 const cleanClass = element => { 1250 const classList = supports(element) ? element.dom.classList : get$8(element); 1251 if (classList.length === 0) { 1252 remove$a(element, 'class'); 1253 } 1254 }; 1255 const remove$7 = (element, clazz) => { 1256 if (supports(element)) { 1257 const classList = element.dom.classList; 1258 classList.remove(clazz); 1259 } else { 1260 remove$8(element, clazz); 1261 } 1262 cleanClass(element); 1263 }; 1264 const toggle$1 = (element, clazz) => { 1265 const result = supports(element) ? element.dom.classList.toggle(clazz) : toggle$2(element, clazz); 1266 cleanClass(element); 1267 return result; 1268 }; 1269 const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz); 1270 1271 const fromHtml$1 = (html, scope) => { 1272 const doc = scope || document; 1273 const div = doc.createElement('div'); 1274 div.innerHTML = html; 1275 if (!div.hasChildNodes() || div.childNodes.length > 1) { 1276 const message = 'HTML does not have a single root node'; 1277 console.error(message, html); 1278 throw new Error(message); 1279 } 1280 return fromDom$2(div.childNodes[0]); 1281 }; 1282 const fromTag = (tag, scope) => { 1283 const doc = scope || document; 1284 const node = doc.createElement(tag); 1285 return fromDom$2(node); 1286 }; 1287 const fromText = (text, scope) => { 1288 const doc = scope || document; 1289 const node = doc.createTextNode(text); 1290 return fromDom$2(node); 1291 }; 1292 const fromDom$2 = node => { 1293 if (node === null || node === undefined) { 1294 throw new Error('Node cannot be null or undefined'); 1295 } 1296 return { dom: node }; 1297 }; 1298 const fromPoint$2 = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$2); 1299 const SugarElement = { 1300 fromHtml: fromHtml$1, 1301 fromTag, 1302 fromText, 1303 fromDom: fromDom$2, 1304 fromPoint: fromPoint$2 1305 }; 1306 1307 const toArray = (target, f) => { 1308 const r = []; 1309 const recurse = e => { 1310 r.push(e); 1311 return f(e); 1312 }; 1313 let cur = f(target); 1314 do { 1315 cur = cur.bind(recurse); 1316 } while (cur.isSome()); 1317 return r; 1318 }; 1319 1320 const is$1 = (element, selector) => { 1321 const dom = element.dom; 1322 if (dom.nodeType !== ELEMENT) { 1323 return false; 1324 } else { 1325 const elem = dom; 1326 if (elem.matches !== undefined) { 1327 return elem.matches(selector); 1328 } else if (elem.msMatchesSelector !== undefined) { 1329 return elem.msMatchesSelector(selector); 1330 } else if (elem.webkitMatchesSelector !== undefined) { 1331 return elem.webkitMatchesSelector(selector); 1332 } else if (elem.mozMatchesSelector !== undefined) { 1333 return elem.mozMatchesSelector(selector); 1334 } else { 1335 throw new Error('Browser lacks native selectors'); 1336 } 1337 } 1338 }; 1339 const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0; 1340 const all = (selector, scope) => { 1341 const base = scope === undefined ? document : scope.dom; 1342 return bypassSelector(base) ? [] : map$3(base.querySelectorAll(selector), SugarElement.fromDom); 1343 }; 1344 const one = (selector, scope) => { 1345 const base = scope === undefined ? document : scope.dom; 1346 return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom); 1347 }; 1348 1349 const eq = (e1, e2) => e1.dom === e2.dom; 1350 const contains = (e1, e2) => { 1351 const d1 = e1.dom; 1352 const d2 = e2.dom; 1353 return d1 === d2 ? false : d1.contains(d2); 1354 }; 1355 1356 const owner$1 = element => SugarElement.fromDom(element.dom.ownerDocument); 1357 const documentOrOwner = dos => isDocument$2(dos) ? dos : owner$1(dos); 1358 const documentElement = element => SugarElement.fromDom(documentOrOwner(element).dom.documentElement); 1359 const defaultView = element => SugarElement.fromDom(documentOrOwner(element).dom.defaultView); 1360 const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom); 1361 const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom); 1362 const parents$1 = (element, isRoot) => { 1363 const stop = isFunction(isRoot) ? isRoot : never; 1364 let dom = element.dom; 1365 const ret = []; 1366 while (dom.parentNode !== null && dom.parentNode !== undefined) { 1367 const rawParent = dom.parentNode; 1368 const p = SugarElement.fromDom(rawParent); 1369 ret.push(p); 1370 if (stop(p) === true) { 1371 break; 1372 } else { 1373 dom = rawParent; 1374 } 1375 } 1376 return ret; 1377 }; 1378 const siblings = element => { 1379 const filterSelf = elements => filter$5(elements, x => !eq(element, x)); 1380 return parent(element).map(children$1).map(filterSelf).getOr([]); 1381 }; 1382 const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom); 1383 const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom); 1384 const prevSiblings = element => reverse(toArray(element, prevSibling)); 1385 const nextSiblings = element => toArray(element, nextSibling); 1386 const children$1 = element => map$3(element.dom.childNodes, SugarElement.fromDom); 1387 const child$1 = (element, index) => { 1388 const cs = element.dom.childNodes; 1389 return Optional.from(cs[index]).map(SugarElement.fromDom); 1390 }; 1391 const firstChild = element => child$1(element, 0); 1392 const lastChild = element => child$1(element, element.dom.childNodes.length - 1); 1393 const childNodesCount = element => element.dom.childNodes.length; 1394 const hasChildNodes = element => element.dom.hasChildNodes(); 1395 1396 const getHead = doc => { 1397 const b = doc.dom.head; 1398 if (b === null || b === undefined) { 1399 throw new Error('Head is not available yet'); 1400 } 1401 return SugarElement.fromDom(b); 1402 }; 1403 1404 const isShadowRoot = dos => isDocumentFragment$1(dos) && isNonNullable(dos.dom.host); 1405 const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode); 1406 const isSupported$1 = constant(supported); 1407 const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner; 1408 const getStyleContainer = dos => isShadowRoot(dos) ? dos : getHead(documentOrOwner(dos)); 1409 const getContentContainer = dos => isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body); 1410 const getShadowRoot = e => { 1411 const r = getRootNode(e); 1412 return isShadowRoot(r) ? Optional.some(r) : Optional.none(); 1413 }; 1414 const getShadowHost = e => SugarElement.fromDom(e.dom.host); 1415 const getOriginalEventTarget = event => { 1416 if (isSupported$1() && isNonNullable(event.target)) { 1417 const el = SugarElement.fromDom(event.target); 1418 if (isElement$7(el) && isOpenShadowHost(el)) { 1419 if (event.composed && event.composedPath) { 1420 const composedPath = event.composedPath(); 1421 if (composedPath) { 1422 return head(composedPath); 1423 } 1424 } 1425 } 1426 } 1427 return Optional.from(event.target); 1428 }; 1429 const isOpenShadowHost = element => isNonNullable(element.dom.shadowRoot); 1430 1431 const inBody = element => { 1432 const dom = isText$b(element) ? element.dom.parentNode : element.dom; 1433 if (dom === undefined || dom === null || dom.ownerDocument === null) { 1434 return false; 1435 } 1436 const doc = dom.ownerDocument; 1437 return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost)); 1438 }; 1439 1440 var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => { 1441 if (is(scope, a)) { 1442 return Optional.some(scope); 1443 } else if (isFunction(isRoot) && isRoot(scope)) { 1444 return Optional.none(); 1445 } else { 1446 return ancestor(scope, a, isRoot); 1447 } 1448 }; 1449 1450 const ancestor$4 = (scope, predicate, isRoot) => { 1451 let element = scope.dom; 1452 const stop = isFunction(isRoot) ? isRoot : never; 1453 while (element.parentNode) { 1454 element = element.parentNode; 1455 const el = SugarElement.fromDom(element); 1456 if (predicate(el)) { 1457 return Optional.some(el); 1458 } else if (stop(el)) { 1459 break; 1460 } 1461 } 1462 return Optional.none(); 1463 }; 1464 const closest$4 = (scope, predicate, isRoot) => { 1465 const is = (s, test) => test(s); 1466 return ClosestOrAncestor(is, ancestor$4, scope, predicate, isRoot); 1467 }; 1468 const sibling$1 = (scope, predicate) => { 1469 const element = scope.dom; 1470 if (!element.parentNode) { 1471 return Optional.none(); 1472 } 1473 return child(SugarElement.fromDom(element.parentNode), x => !eq(scope, x) && predicate(x)); 1474 }; 1475 const child = (scope, predicate) => { 1476 const pred = node => predicate(SugarElement.fromDom(node)); 1477 const result = find$2(scope.dom.childNodes, pred); 1478 return result.map(SugarElement.fromDom); 1479 }; 1480 const descendant$1 = (scope, predicate) => { 1481 const descend = node => { 1482 for (let i = 0; i < node.childNodes.length; i++) { 1483 const child = SugarElement.fromDom(node.childNodes[i]); 1484 if (predicate(child)) { 1485 return Optional.some(child); 1486 } 1487 const res = descend(node.childNodes[i]); 1488 if (res.isSome()) { 1489 return res; 1490 } 1491 } 1492 return Optional.none(); 1493 }; 1494 return descend(scope.dom); 1495 }; 1496 1497 const ancestor$3 = (scope, selector, isRoot) => ancestor$4(scope, e => is$1(e, selector), isRoot); 1498 const descendant = (scope, selector) => one(selector, scope); 1499 const closest$3 = (scope, selector, isRoot) => { 1500 const is = (element, selector) => is$1(element, selector); 1501 return ClosestOrAncestor(is, ancestor$3, scope, selector, isRoot); 1502 }; 1503 1504 const closest$2 = target => closest$3(target, '[contenteditable]'); 1505 const isEditable$3 = (element, assumeEditable = false) => { 1506 if (inBody(element)) { 1507 return element.dom.isContentEditable; 1508 } else { 1509 return closest$2(element).fold(constant(assumeEditable), editable => getRaw$1(editable) === 'true'); 1510 } 1511 }; 1512 const getRaw$1 = element => element.dom.contentEditable; 1513 1514 const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue); 1515 1516 const internalSet = (dom, property, value) => { 1517 if (!isString(value)) { 1518 console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom); 1519 throw new Error('CSS value must be a string: ' + value); 1520 } 1521 if (isSupported(dom)) { 1522 dom.style.setProperty(property, value); 1523 } 1524 }; 1525 const internalRemove = (dom, property) => { 1526 if (isSupported(dom)) { 1527 dom.style.removeProperty(property); 1528 } 1529 }; 1530 const set$2 = (element, property, value) => { 1531 const dom = element.dom; 1532 internalSet(dom, property, value); 1533 }; 1534 const setAll = (element, css) => { 1535 const dom = element.dom; 1536 each$d(css, (v, k) => { 1537 internalSet(dom, k, v); 1538 }); 1539 }; 1540 const get$7 = (element, property) => { 1541 const dom = element.dom; 1542 const styles = window.getComputedStyle(dom); 1543 const r = styles.getPropertyValue(property); 1544 return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r; 1545 }; 1546 const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : ''; 1547 const getRaw = (element, property) => { 1548 const dom = element.dom; 1549 const raw = getUnsafeProperty(dom, property); 1550 return Optional.from(raw).filter(r => r.length > 0); 1551 }; 1552 const getAllRaw = element => { 1553 const css = {}; 1554 const dom = element.dom; 1555 if (isSupported(dom)) { 1556 for (let i = 0; i < dom.style.length; i++) { 1557 const ruleName = dom.style.item(i); 1558 css[ruleName] = dom.style[ruleName]; 1559 } 1560 } 1561 return css; 1562 }; 1563 const remove$6 = (element, property) => { 1564 const dom = element.dom; 1565 internalRemove(dom, property); 1566 if (is$2(getOpt(element, 'style').map(trim$3), '')) { 1567 remove$a(element, 'style'); 1568 } 1569 }; 1570 const reflow = e => e.dom.offsetWidth; 1571 1572 const before$3 = (marker, element) => { 1573 const parent$1 = parent(marker); 1574 parent$1.each(v => { 1575 v.dom.insertBefore(element.dom, marker.dom); 1576 }); 1577 }; 1578 const after$4 = (marker, element) => { 1579 const sibling = nextSibling(marker); 1580 sibling.fold(() => { 1581 const parent$1 = parent(marker); 1582 parent$1.each(v => { 1583 append$1(v, element); 1584 }); 1585 }, v => { 1586 before$3(v, element); 1587 }); 1588 }; 1589 const prepend = (parent, element) => { 1590 const firstChild$1 = firstChild(parent); 1591 firstChild$1.fold(() => { 1592 append$1(parent, element); 1593 }, v => { 1594 parent.dom.insertBefore(element.dom, v.dom); 1595 }); 1596 }; 1597 const append$1 = (parent, element) => { 1598 parent.dom.appendChild(element.dom); 1599 }; 1600 const wrap$2 = (element, wrapper) => { 1601 before$3(element, wrapper); 1602 append$1(wrapper, element); 1603 }; 1604 1605 const after$3 = (marker, elements) => { 1606 each$e(elements, (x, i) => { 1607 const e = i === 0 ? marker : elements[i - 1]; 1608 after$4(e, x); 1609 }); 1610 }; 1611 const append = (parent, elements) => { 1612 each$e(elements, x => { 1613 append$1(parent, x); 1614 }); 1615 }; 1616 1617 const empty = element => { 1618 element.dom.textContent = ''; 1619 each$e(children$1(element), rogue => { 1620 remove$5(rogue); 1621 }); 1622 }; 1623 const remove$5 = element => { 1624 const dom = element.dom; 1625 if (dom.parentNode !== null) { 1626 dom.parentNode.removeChild(dom); 1627 } 1628 }; 1629 const unwrap = wrapper => { 1630 const children = children$1(wrapper); 1631 if (children.length > 0) { 1632 after$3(wrapper, children); 1633 } 1634 remove$5(wrapper); 1635 }; 1636 1637 const fromHtml = (html, scope) => { 1638 const doc = scope || document; 1639 const div = doc.createElement('div'); 1640 div.innerHTML = html; 1641 return children$1(SugarElement.fromDom(div)); 1642 }; 1643 const fromDom$1 = nodes => map$3(nodes, SugarElement.fromDom); 1644 1645 const get$6 = element => element.dom.innerHTML; 1646 const set$1 = (element, content) => { 1647 const owner = owner$1(element); 1648 const docDom = owner.dom; 1649 const fragment = SugarElement.fromDom(docDom.createDocumentFragment()); 1650 const contentElements = fromHtml(content, docDom); 1651 append(fragment, contentElements); 1652 empty(element); 1653 append$1(element, fragment); 1654 }; 1655 const getOuter = element => { 1656 const container = SugarElement.fromTag('div'); 1657 const clone = SugarElement.fromDom(element.dom.cloneNode(true)); 1658 append$1(container, clone); 1659 return get$6(container); 1660 }; 1661 1662 const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({ 1663 target, 1664 x, 1665 y, 1666 stop, 1667 prevent, 1668 kill, 1669 raw 1670 }); 1671 const fromRawEvent = rawEvent => { 1672 const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target)); 1673 const stop = () => rawEvent.stopPropagation(); 1674 const prevent = () => rawEvent.preventDefault(); 1675 const kill = compose(prevent, stop); 1676 return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent); 1677 }; 1678 const handle$1 = (filter, handler) => rawEvent => { 1679 if (filter(rawEvent)) { 1680 handler(fromRawEvent(rawEvent)); 1681 } 1682 }; 1683 const binder = (element, event, filter, handler, useCapture) => { 1684 const wrapped = handle$1(filter, handler); 1685 element.dom.addEventListener(event, wrapped, useCapture); 1686 return { unbind: curry(unbind, element, event, wrapped, useCapture) }; 1687 }; 1688 const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false); 1689 const unbind = (element, event, handler, useCapture) => { 1690 element.dom.removeEventListener(event, handler, useCapture); 1691 }; 1692 1693 const r = (left, top) => { 1694 const translate = (x, y) => r(left + x, top + y); 1695 return { 1696 left, 1697 top, 1698 translate 1699 }; 1700 }; 1701 const SugarPosition = r; 1702 1703 const boxPosition = dom => { 1704 const box = dom.getBoundingClientRect(); 1705 return SugarPosition(box.left, box.top); 1706 }; 1707 const firstDefinedOrZero = (a, b) => { 1708 if (a !== undefined) { 1709 return a; 1710 } else { 1711 return b !== undefined ? b : 0; 1712 } 1713 }; 1714 const absolute = element => { 1715 const doc = element.dom.ownerDocument; 1716 const body = doc.body; 1717 const win = doc.defaultView; 1718 const html = doc.documentElement; 1719 if (body === element.dom) { 1720 return SugarPosition(body.offsetLeft, body.offsetTop); 1721 } 1722 const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop); 1723 const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft); 1724 const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop); 1725 const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft); 1726 return viewport(element).translate(scrollLeft - clientLeft, scrollTop - clientTop); 1727 }; 1728 const viewport = element => { 1729 const dom = element.dom; 1730 const doc = dom.ownerDocument; 1731 const body = doc.body; 1732 if (body === dom) { 1733 return SugarPosition(body.offsetLeft, body.offsetTop); 1734 } 1735 if (!inBody(element)) { 1736 return SugarPosition(0, 0); 1737 } 1738 return boxPosition(dom); 1739 }; 1740 1741 const get$5 = _DOC => { 1742 const doc = _DOC !== undefined ? _DOC.dom : document; 1743 const x = doc.body.scrollLeft || doc.documentElement.scrollLeft; 1744 const y = doc.body.scrollTop || doc.documentElement.scrollTop; 1745 return SugarPosition(x, y); 1746 }; 1747 const intoView = (element, alignToTop) => { 1748 const isSafari = detect$2().browser.isSafari(); 1749 if (isSafari && isFunction(element.dom.scrollIntoViewIfNeeded)) { 1750 element.dom.scrollIntoViewIfNeeded(false); 1751 } else { 1752 element.dom.scrollIntoView(alignToTop); 1753 } 1754 }; 1755 1756 const get$4 = _win => { 1757 const win = _win === undefined ? window : _win; 1758 if (detect$2().browser.isFirefox()) { 1759 return Optional.none(); 1760 } else { 1761 return Optional.from(win.visualViewport); 1762 } 1763 }; 1764 const bounds = (x, y, width, height) => ({ 1765 x, 1766 y, 1767 width, 1768 height, 1769 right: x + width, 1770 bottom: y + height 1771 }); 1772 const getBounds = _win => { 1773 const win = _win === undefined ? window : _win; 1774 const doc = win.document; 1775 const scroll = get$5(SugarElement.fromDom(doc)); 1776 return get$4(win).fold(() => { 1777 const html = win.document.documentElement; 1778 const width = html.clientWidth; 1779 const height = html.clientHeight; 1780 return bounds(scroll.left, scroll.top, width, height); 1781 }, visualViewport => bounds(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height)); 1782 }; 1783 1784 const children = (scope, predicate) => filter$5(children$1(scope), predicate); 1785 const descendants$1 = (scope, predicate) => { 1786 let result = []; 1787 each$e(children$1(scope), x => { 1788 if (predicate(x)) { 1789 result = result.concat([x]); 1790 } 1791 result = result.concat(descendants$1(x, predicate)); 1792 }); 1793 return result; 1794 }; 1795 1796 const descendants = (scope, selector) => all(selector, scope); 1797 1798 const ancestor$2 = (scope, selector, isRoot) => ancestor$3(scope, selector, isRoot).isSome(); 1799 1800 class DomTreeWalker { 1801 constructor(startNode, rootNode) { 1802 this.node = startNode; 1803 this.rootNode = rootNode; 1804 this.current = this.current.bind(this); 1805 this.next = this.next.bind(this); 1806 this.prev = this.prev.bind(this); 1807 this.prev2 = this.prev2.bind(this); 1808 } 1809 current() { 1810 return this.node; 1811 } 1812 next(shallow) { 1813 this.node = this.findSibling(this.node, 'firstChild', 'nextSibling', shallow); 1814 return this.node; 1815 } 1816 prev(shallow) { 1817 this.node = this.findSibling(this.node, 'lastChild', 'previousSibling', shallow); 1818 return this.node; 1819 } 1820 prev2(shallow) { 1821 this.node = this.findPreviousNode(this.node, shallow); 1822 return this.node; 1823 } 1824 findSibling(node, startName, siblingName, shallow) { 1825 if (node) { 1826 if (!shallow && node[startName]) { 1827 return node[startName]; 1828 } 1829 if (node !== this.rootNode) { 1830 let sibling = node[siblingName]; 1831 if (sibling) { 1832 return sibling; 1833 } 1834 for (let parent = node.parentNode; parent && parent !== this.rootNode; parent = parent.parentNode) { 1835 sibling = parent[siblingName]; 1836 if (sibling) { 1837 return sibling; 1838 } 1839 } 1840 } 1841 } 1842 return undefined; 1843 } 1844 findPreviousNode(node, shallow) { 1845 if (node) { 1846 const sibling = node.previousSibling; 1847 if (this.rootNode && sibling === this.rootNode) { 1848 return; 1849 } 1850 if (sibling) { 1851 if (!shallow) { 1852 for (let child = sibling.lastChild; child; child = child.lastChild) { 1853 if (!child.lastChild) { 1854 return child; 1855 } 1856 } 1857 } 1858 return sibling; 1859 } 1860 const parent = node.parentNode; 1861 if (parent && parent !== this.rootNode) { 1862 return parent; 1863 } 1864 } 1865 return undefined; 1866 } 1867 } 1868 1869 const isNodeType = type => { 1870 return node => { 1871 return !!node && node.nodeType === type; 1872 }; 1873 }; 1874 const isRestrictedNode = node => !!node && !Object.getPrototypeOf(node); 1875 const isElement$6 = isNodeType(1); 1876 const matchNodeName = name => { 1877 const lowerCasedName = name.toLowerCase(); 1878 return node => isNonNullable(node) && node.nodeName.toLowerCase() === lowerCasedName; 1879 }; 1880 const matchNodeNames = names => { 1881 const lowerCasedNames = names.map(s => s.toLowerCase()); 1882 return node => { 1883 if (node && node.nodeName) { 1884 const nodeName = node.nodeName.toLowerCase(); 1885 return contains$2(lowerCasedNames, nodeName); 1886 } 1887 return false; 1888 }; 1889 }; 1890 const matchStyleValues = (name, values) => { 1891 const items = values.toLowerCase().split(' '); 1892 return node => { 1893 if (isElement$6(node)) { 1894 const win = node.ownerDocument.defaultView; 1895 if (win) { 1896 for (let i = 0; i < items.length; i++) { 1897 const computed = win.getComputedStyle(node, null); 1898 const cssValue = computed ? computed.getPropertyValue(name) : null; 1899 if (cssValue === items[i]) { 1900 return true; 1901 } 1902 } 1903 } 1904 } 1905 return false; 1906 }; 1907 }; 1908 const hasAttribute = attrName => { 1909 return node => { 1910 return isElement$6(node) && node.hasAttribute(attrName); 1911 }; 1912 }; 1913 const hasAttributeValue = (attrName, attrValue) => { 1914 return node => { 1915 return isElement$6(node) && node.getAttribute(attrName) === attrValue; 1916 }; 1917 }; 1918 const isBogus$2 = node => isElement$6(node) && node.hasAttribute('data-mce-bogus'); 1919 const isBogusAll$1 = node => isElement$6(node) && node.getAttribute('data-mce-bogus') === 'all'; 1920 const isTable$2 = node => isElement$6(node) && node.tagName === 'TABLE'; 1921 const hasContentEditableState = value => { 1922 return node => { 1923 if (isElement$6(node)) { 1924 if (node.contentEditable === value) { 1925 return true; 1926 } 1927 if (node.getAttribute('data-mce-contenteditable') === value) { 1928 return true; 1929 } 1930 } 1931 return false; 1932 }; 1933 }; 1934 const isTextareaOrInput = matchNodeNames([ 1935 'textarea', 1936 'input' 1937 ]); 1938 const isText$a = isNodeType(3); 1939 const isCData = isNodeType(4); 1940 const isPi = isNodeType(7); 1941 const isComment = isNodeType(8); 1942 const isDocument$1 = isNodeType(9); 1943 const isDocumentFragment = isNodeType(11); 1944 const isBr$6 = matchNodeName('br'); 1945 const isImg = matchNodeName('img'); 1946 const isContentEditableTrue$3 = hasContentEditableState('true'); 1947 const isContentEditableFalse$b = hasContentEditableState('false'); 1948 const isTableCell$3 = matchNodeNames([ 1949 'td', 1950 'th' 1951 ]); 1952 const isTableCellOrCaption = matchNodeNames([ 1953 'td', 1954 'th', 1955 'caption' 1956 ]); 1957 const isMedia$2 = matchNodeNames([ 1958 'video', 1959 'audio', 1960 'object', 1961 'embed' 1962 ]); 1963 const isListItem$2 = matchNodeName('li'); 1964 const isDetails = matchNodeName('details'); 1965 const isSummary = matchNodeName('summary'); 1966 1967 const zeroWidth = '\uFEFF'; 1968 const nbsp = '\xA0'; 1969 const isZwsp$2 = char => char === zeroWidth; 1970 const removeZwsp = s => s.replace(/\uFEFF/g, ''); 1971 1972 const NodeValue = (is, name) => { 1973 const get = element => { 1974 if (!is(element)) { 1975 throw new Error('Can only get ' + name + ' value of a ' + name + ' node'); 1976 } 1977 return getOption(element).getOr(''); 1978 }; 1979 const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none(); 1980 const set = (element, value) => { 1981 if (!is(element)) { 1982 throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node'); 1983 } 1984 element.dom.nodeValue = value; 1985 }; 1986 return { 1987 get, 1988 getOption, 1989 set 1990 }; 1991 }; 1992 1993 const api$1 = NodeValue(isText$b, 'text'); 1994 const get$3 = element => api$1.get(element); 1995 const getOption = element => api$1.getOption(element); 1996 const set = (element, value) => api$1.set(element, value); 1997 1998 const blocks = [ 1999 'article', 2000 'aside', 2001 'details', 2002 'div', 2003 'dt', 2004 'figcaption', 2005 'footer', 2006 'form', 2007 'fieldset', 2008 'header', 2009 'hgroup', 2010 'html', 2011 'main', 2012 'nav', 2013 'section', 2014 'summary', 2015 'body', 2016 'p', 2017 'dl', 2018 'multicol', 2019 'dd', 2020 'figure', 2021 'address', 2022 'center', 2023 'blockquote', 2024 'h1', 2025 'h2', 2026 'h3', 2027 'h4', 2028 'h5', 2029 'h6', 2030 'listing', 2031 'xmp', 2032 'pre', 2033 'plaintext', 2034 'menu', 2035 'dir', 2036 'ul', 2037 'ol', 2038 'li', 2039 'hr', 2040 'table', 2041 'tbody', 2042 'thead', 2043 'tfoot', 2044 'th', 2045 'tr', 2046 'td', 2047 'caption' 2048 ]; 2049 const tableCells = [ 2050 'td', 2051 'th' 2052 ]; 2053 const tableSections = [ 2054 'thead', 2055 'tbody', 2056 'tfoot' 2057 ]; 2058 const textBlocks = [ 2059 'h1', 2060 'h2', 2061 'h3', 2062 'h4', 2063 'h5', 2064 'h6', 2065 'p', 2066 'div', 2067 'address', 2068 'pre', 2069 'form', 2070 'blockquote', 2071 'center', 2072 'dir', 2073 'fieldset', 2074 'header', 2075 'footer', 2076 'article', 2077 'section', 2078 'hgroup', 2079 'aside', 2080 'nav', 2081 'figure' 2082 ]; 2083 const headings = [ 2084 'h1', 2085 'h2', 2086 'h3', 2087 'h4', 2088 'h5', 2089 'h6' 2090 ]; 2091 const listItems$1 = [ 2092 'li', 2093 'dd', 2094 'dt' 2095 ]; 2096 const lists = [ 2097 'ul', 2098 'ol', 2099 'dl' 2100 ]; 2101 const wsElements = [ 2102 'pre', 2103 'script', 2104 'textarea', 2105 'style' 2106 ]; 2107 const wrapBlockElements = ['pre'].concat(headings); 2108 const lazyLookup = items => { 2109 let lookup; 2110 return node => { 2111 lookup = lookup ? lookup : mapToObject(items, always); 2112 return has$2(lookup, name(node)); 2113 }; 2114 }; 2115 const isBlock$2 = lazyLookup(blocks); 2116 const isTable$1 = node => name(node) === 'table'; 2117 const isInline$1 = node => isElement$7(node) && !isBlock$2(node); 2118 const isBr$5 = node => isElement$7(node) && name(node) === 'br'; 2119 const isTextBlock$2 = lazyLookup(textBlocks); 2120 const isList = lazyLookup(lists); 2121 const isListItem$1 = lazyLookup(listItems$1); 2122 const isTableSection = lazyLookup(tableSections); 2123 const isTableCell$2 = lazyLookup(tableCells); 2124 const isWsPreserveElement = lazyLookup(wsElements); 2125 const isWrapBlockElement = lazyLookup(wrapBlockElements); 2126 const isWrapElement = node => isWrapBlockElement(node) || isInline$1(node); 2127 2128 const getLastChildren$1 = elm => { 2129 const children = []; 2130 let rawNode = elm.dom; 2131 while (rawNode) { 2132 children.push(SugarElement.fromDom(rawNode)); 2133 rawNode = rawNode.lastChild; 2134 } 2135 return children; 2136 }; 2137 const removeTrailingBr = elm => { 2138 const allBrs = descendants(elm, 'br'); 2139 const brs = filter$5(getLastChildren$1(elm).slice(-1), isBr$5); 2140 if (allBrs.length === brs.length) { 2141 each$e(brs, remove$5); 2142 } 2143 }; 2144 const createPaddingBr = () => { 2145 const br = SugarElement.fromTag('br'); 2146 set$3(br, 'data-mce-bogus', '1'); 2147 return br; 2148 }; 2149 const fillWithPaddingBr = elm => { 2150 empty(elm); 2151 append$1(elm, createPaddingBr()); 2152 }; 2153 const trimBlockTrailingBr = elm => { 2154 lastChild(elm).each(lastChild => { 2155 prevSibling(lastChild).each(lastChildPrevSibling => { 2156 if (isBlock$2(elm) && isBr$5(lastChild) && isBlock$2(lastChildPrevSibling)) { 2157 remove$5(lastChild); 2158 } 2159 }); 2160 }); 2161 }; 2162 2163 const ZWSP$1 = zeroWidth; 2164 const isZwsp$1 = isZwsp$2; 2165 const trim$1 = removeZwsp; 2166 2167 const isElement$5 = isElement$6; 2168 const isText$9 = isText$a; 2169 const isCaretContainerBlock$1 = node => { 2170 if (isText$9(node)) { 2171 node = node.parentNode; 2172 } 2173 return isElement$5(node) && node.hasAttribute('data-mce-caret'); 2174 }; 2175 const isCaretContainerInline = node => isText$9(node) && isZwsp$1(node.data); 2176 const isCaretContainer$2 = node => isCaretContainerBlock$1(node) || isCaretContainerInline(node); 2177 const hasContent = node => node.firstChild !== node.lastChild || !isBr$6(node.firstChild); 2178 const insertInline$1 = (node, before) => { 2179 var _a; 2180 const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document; 2181 const textNode = doc.createTextNode(ZWSP$1); 2182 const parentNode = node.parentNode; 2183 if (!before) { 2184 const sibling = node.nextSibling; 2185 if (isText$9(sibling)) { 2186 if (isCaretContainer$2(sibling)) { 2187 return sibling; 2188 } 2189 if (startsWithCaretContainer$1(sibling)) { 2190 sibling.splitText(1); 2191 return sibling; 2192 } 2193 } 2194 if (node.nextSibling) { 2195 parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(textNode, node.nextSibling); 2196 } else { 2197 parentNode === null || parentNode === void 0 ? void 0 : parentNode.appendChild(textNode); 2198 } 2199 } else { 2200 const sibling = node.previousSibling; 2201 if (isText$9(sibling)) { 2202 if (isCaretContainer$2(sibling)) { 2203 return sibling; 2204 } 2205 if (endsWithCaretContainer$1(sibling)) { 2206 return sibling.splitText(sibling.data.length - 1); 2207 } 2208 } 2209 parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(textNode, node); 2210 } 2211 return textNode; 2212 }; 2213 const isBeforeInline = pos => { 2214 const container = pos.container(); 2215 if (!isText$a(container)) { 2216 return false; 2217 } 2218 return container.data.charAt(pos.offset()) === ZWSP$1 || pos.isAtStart() && isCaretContainerInline(container.previousSibling); 2219 }; 2220 const isAfterInline = pos => { 2221 const container = pos.container(); 2222 if (!isText$a(container)) { 2223 return false; 2224 } 2225 return container.data.charAt(pos.offset() - 1) === ZWSP$1 || pos.isAtEnd() && isCaretContainerInline(container.nextSibling); 2226 }; 2227 const insertBlock = (blockName, node, before) => { 2228 var _a; 2229 const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document; 2230 const blockNode = doc.createElement(blockName); 2231 blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after'); 2232 blockNode.setAttribute('data-mce-bogus', 'all'); 2233 blockNode.appendChild(createPaddingBr().dom); 2234 const parentNode = node.parentNode; 2235 if (!before) { 2236 if (node.nextSibling) { 2237 parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(blockNode, node.nextSibling); 2238 } else { 2239 parentNode === null || parentNode === void 0 ? void 0 : parentNode.appendChild(blockNode); 2240 } 2241 } else { 2242 parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(blockNode, node); 2243 } 2244 return blockNode; 2245 }; 2246 const startsWithCaretContainer$1 = node => isText$9(node) && node.data[0] === ZWSP$1; 2247 const endsWithCaretContainer$1 = node => isText$9(node) && node.data[node.data.length - 1] === ZWSP$1; 2248 const trimBogusBr = elm => { 2249 var _a; 2250 const brs = elm.getElementsByTagName('br'); 2251 const lastBr = brs[brs.length - 1]; 2252 if (isBogus$2(lastBr)) { 2253 (_a = lastBr.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(lastBr); 2254 } 2255 }; 2256 const showCaretContainerBlock = caretContainer => { 2257 if (caretContainer && caretContainer.hasAttribute('data-mce-caret')) { 2258 trimBogusBr(caretContainer); 2259 caretContainer.removeAttribute('data-mce-caret'); 2260 caretContainer.removeAttribute('data-mce-bogus'); 2261 caretContainer.removeAttribute('style'); 2262 caretContainer.removeAttribute('data-mce-style'); 2263 caretContainer.removeAttribute('_moz_abspos'); 2264 return caretContainer; 2265 } 2266 return null; 2267 }; 2268 const isRangeInCaretContainerBlock = range => isCaretContainerBlock$1(range.startContainer); 2269 2270 const isContentEditableTrue$2 = isContentEditableTrue$3; 2271 const isContentEditableFalse$a = isContentEditableFalse$b; 2272 const isBr$4 = isBr$6; 2273 const isText$8 = isText$a; 2274 const isInvalidTextElement = matchNodeNames([ 2275 'script', 2276 'style', 2277 'textarea' 2278 ]); 2279 const isAtomicInline = matchNodeNames([ 2280 'img', 2281 'input', 2282 'textarea', 2283 'hr', 2284 'iframe', 2285 'video', 2286 'audio', 2287 'object', 2288 'embed' 2289 ]); 2290 const isTable = matchNodeNames(['table']); 2291 const isCaretContainer$1 = isCaretContainer$2; 2292 const isCaretCandidate$3 = node => { 2293 if (isCaretContainer$1(node)) { 2294 return false; 2295 } 2296 if (isText$8(node)) { 2297 return !isInvalidTextElement(node.parentNode); 2298 } 2299 return isAtomicInline(node) || isBr$4(node) || isTable(node) || isNonUiContentEditableFalse(node); 2300 }; 2301 const isUnselectable = node => isElement$6(node) && node.getAttribute('unselectable') === 'true'; 2302 const isNonUiContentEditableFalse = node => !isUnselectable(node) && isContentEditableFalse$a(node); 2303 const isInEditable = (node, root) => { 2304 for (let tempNode = node.parentNode; tempNode && tempNode !== root; tempNode = tempNode.parentNode) { 2305 if (isNonUiContentEditableFalse(tempNode)) { 2306 return false; 2307 } 2308 if (isContentEditableTrue$2(tempNode)) { 2309 return true; 2310 } 2311 } 2312 return true; 2313 }; 2314 const isAtomicContentEditableFalse = node => { 2315 if (!isNonUiContentEditableFalse(node)) { 2316 return false; 2317 } 2318 return !foldl(from(node.getElementsByTagName('*')), (result, elm) => { 2319 return result || isContentEditableTrue$2(elm); 2320 }, false); 2321 }; 2322 const isAtomic$1 = node => isAtomicInline(node) || isAtomicContentEditableFalse(node); 2323 const isEditableCaretCandidate$1 = (node, root) => isCaretCandidate$3(node) && isInEditable(node, root); 2324 2325 const whiteSpaceRegExp = /^[ \t\r\n]*$/; 2326 const isWhitespaceText = text => whiteSpaceRegExp.test(text); 2327 const isZwsp = text => { 2328 for (const c of text) { 2329 if (!isZwsp$2(c)) { 2330 return false; 2331 } 2332 } 2333 return true; 2334 }; 2335 const isCollapsibleWhitespace$1 = c => ' \f\t\x0B'.indexOf(c) !== -1; 2336 const isNewLineChar = c => c === '\n' || c === '\r'; 2337 const isNewline = (text, idx) => idx < text.length && idx >= 0 ? isNewLineChar(text[idx]) : false; 2338 const normalize$4 = (text, tabSpaces = 4, isStartOfContent = true, isEndOfContent = true) => { 2339 const tabSpace = repeat(' ', tabSpaces); 2340 const normalizedText = text.replace(/\t/g, tabSpace); 2341 const result = foldl(normalizedText, (acc, c) => { 2342 if (isCollapsibleWhitespace$1(c) || c === nbsp) { 2343 if (acc.pcIsSpace || acc.str === '' && isStartOfContent || acc.str.length === normalizedText.length - 1 && isEndOfContent || isNewline(normalizedText, acc.str.length + 1)) { 2344 return { 2345 pcIsSpace: false, 2346 str: acc.str + nbsp 2347 }; 2348 } else { 2349 return { 2350 pcIsSpace: true, 2351 str: acc.str + ' ' 2352 }; 2353 } 2354 } else { 2355 return { 2356 pcIsSpace: isNewLineChar(c), 2357 str: acc.str + c 2358 }; 2359 } 2360 }, { 2361 pcIsSpace: false, 2362 str: '' 2363 }); 2364 return result.str; 2365 }; 2366 2367 const hasWhitespacePreserveParent = (node, rootNode) => { 2368 const rootElement = SugarElement.fromDom(rootNode); 2369 const startNode = SugarElement.fromDom(node); 2370 return ancestor$2(startNode, 'pre,code', curry(eq, rootElement)); 2371 }; 2372 const isWhitespace$1 = (node, rootNode) => { 2373 return isText$a(node) && isWhitespaceText(node.data) && !hasWhitespacePreserveParent(node, rootNode); 2374 }; 2375 const isNamedAnchor = node => { 2376 return isElement$6(node) && node.nodeName === 'A' && !node.hasAttribute('href') && (node.hasAttribute('name') || node.hasAttribute('id')); 2377 }; 2378 const isContent$1 = (node, rootNode) => { 2379 return isCaretCandidate$3(node) && !isWhitespace$1(node, rootNode) || isNamedAnchor(node) || isBookmark(node); 2380 }; 2381 const isBookmark = hasAttribute('data-mce-bookmark'); 2382 const isBogus$1 = hasAttribute('data-mce-bogus'); 2383 const isBogusAll = hasAttributeValue('data-mce-bogus', 'all'); 2384 const isEmptyNode = (targetNode, skipBogus) => { 2385 let brCount = 0; 2386 if (isContent$1(targetNode, targetNode)) { 2387 return false; 2388 } else { 2389 let node = targetNode.firstChild; 2390 if (!node) { 2391 return true; 2392 } 2393 const walker = new DomTreeWalker(node, targetNode); 2394 do { 2395 if (skipBogus) { 2396 if (isBogusAll(node)) { 2397 node = walker.next(true); 2398 continue; 2399 } 2400 if (isBogus$1(node)) { 2401 node = walker.next(); 2402 continue; 2403 } 2404 } 2405 if (isBr$6(node)) { 2406 brCount++; 2407 node = walker.next(); 2408 continue; 2409 } 2410 if (isContent$1(node, targetNode)) { 2411 return false; 2412 } 2413 node = walker.next(); 2414 } while (node); 2415 return brCount <= 1; 2416 } 2417 }; 2418 const isEmpty$2 = (elm, skipBogus = true) => isEmptyNode(elm.dom, skipBogus); 2419 2420 const transparentBlockAttr = 'data-mce-block'; 2421 const elementNames = map => filter$5(keys(map), key => !/[A-Z]/.test(key)); 2422 const makeSelectorFromSchemaMap = map => elementNames(map).join(','); 2423 const updateTransparent = (blocksSelector, transparent) => { 2424 if (isNonNullable(transparent.querySelector(blocksSelector))) { 2425 transparent.setAttribute(transparentBlockAttr, 'true'); 2426 if (transparent.getAttribute('data-mce-selected') === 'inline-boundary') { 2427 transparent.removeAttribute('data-mce-selected'); 2428 } 2429 return true; 2430 } else { 2431 transparent.removeAttribute(transparentBlockAttr); 2432 return false; 2433 } 2434 }; 2435 const updateBlockStateOnChildren = (schema, scope) => { 2436 const transparentSelector = makeSelectorFromSchemaMap(schema.getTransparentElements()); 2437 const blocksSelector = makeSelectorFromSchemaMap(schema.getBlockElements()); 2438 return filter$5(scope.querySelectorAll(transparentSelector), transparent => updateTransparent(blocksSelector, transparent)); 2439 }; 2440 const trimEdge = (el, leftSide) => { 2441 var _a; 2442 const childPropertyName = leftSide ? 'lastChild' : 'firstChild'; 2443 for (let child = el[childPropertyName]; child; child = child[childPropertyName]) { 2444 if (isEmpty$2(SugarElement.fromDom(child))) { 2445 (_a = child.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(child); 2446 return; 2447 } 2448 } 2449 }; 2450 const split$2 = (parentElm, splitElm) => { 2451 const range = document.createRange(); 2452 const parentNode = parentElm.parentNode; 2453 if (parentNode) { 2454 range.setStartBefore(parentElm); 2455 range.setEndBefore(splitElm); 2456 const beforeFragment = range.extractContents(); 2457 trimEdge(beforeFragment, true); 2458 range.setStartAfter(splitElm); 2459 range.setEndAfter(parentElm); 2460 const afterFragment = range.extractContents(); 2461 trimEdge(afterFragment, false); 2462 if (!isEmpty$2(SugarElement.fromDom(beforeFragment))) { 2463 parentNode.insertBefore(beforeFragment, parentElm); 2464 } 2465 if (!isEmpty$2(SugarElement.fromDom(splitElm))) { 2466 parentNode.insertBefore(splitElm, parentElm); 2467 } 2468 if (!isEmpty$2(SugarElement.fromDom(afterFragment))) { 2469 parentNode.insertBefore(afterFragment, parentElm); 2470 } 2471 parentNode.removeChild(parentElm); 2472 } 2473 }; 2474 const splitInvalidChildren = (schema, scope, transparentBlocks) => { 2475 const blocksElements = schema.getBlockElements(); 2476 const rootNode = SugarElement.fromDom(scope); 2477 const isBlock = el => name(el) in blocksElements; 2478 const isRoot = el => eq(el, rootNode); 2479 each$e(fromDom$1(transparentBlocks), transparentBlock => { 2480 ancestor$4(transparentBlock, isBlock, isRoot).each(parentBlock => { 2481 const invalidChildren = children(transparentBlock, el => isBlock(el) && !schema.isValidChild(name(parentBlock), name(el))); 2482 if (invalidChildren.length > 0) { 2483 const stateScope = parentElement(parentBlock); 2484 each$e(invalidChildren, child => { 2485 ancestor$4(child, isBlock, isRoot).each(parentBlock => { 2486 split$2(parentBlock.dom, child.dom); 2487 }); 2488 }); 2489 stateScope.each(scope => updateBlockStateOnChildren(schema, scope.dom)); 2490 } 2491 }); 2492 }); 2493 }; 2494 const unwrapInvalidChildren = (schema, scope, transparentBlocks) => { 2495 each$e([ 2496 ...transparentBlocks, 2497 ...isTransparentBlock(schema, scope) ? [scope] : [] 2498 ], block => each$e(descendants(SugarElement.fromDom(block), block.nodeName.toLowerCase()), elm => { 2499 if (isTransparentInline(schema, elm.dom)) { 2500 unwrap(elm); 2501 } 2502 })); 2503 }; 2504 const updateChildren = (schema, scope) => { 2505 const transparentBlocks = updateBlockStateOnChildren(schema, scope); 2506 splitInvalidChildren(schema, scope, transparentBlocks); 2507 unwrapInvalidChildren(schema, scope, transparentBlocks); 2508 }; 2509 const updateElement = (schema, target) => { 2510 if (isTransparentElement(schema, target)) { 2511 const blocksSelector = makeSelectorFromSchemaMap(schema.getBlockElements()); 2512 updateTransparent(blocksSelector, target); 2513 } 2514 }; 2515 const updateCaret = (schema, root, caretParent) => { 2516 const isRoot = el => eq(el, SugarElement.fromDom(root)); 2517 const parents = parents$1(SugarElement.fromDom(caretParent), isRoot); 2518 get$b(parents, parents.length - 2).filter(isElement$7).fold(() => updateChildren(schema, root), scope => updateChildren(schema, scope.dom)); 2519 }; 2520 const hasBlockAttr = el => el.hasAttribute(transparentBlockAttr); 2521 const isTransparentElementName = (schema, name) => has$2(schema.getTransparentElements(), name); 2522 const isTransparentElement = (schema, node) => isElement$6(node) && isTransparentElementName(schema, node.nodeName); 2523 const isTransparentBlock = (schema, node) => isTransparentElement(schema, node) && hasBlockAttr(node); 2524 const isTransparentInline = (schema, node) => isTransparentElement(schema, node) && !hasBlockAttr(node); 2525 const isTransparentAstBlock = (schema, node) => node.type === 1 && isTransparentElementName(schema, node.name) && isString(node.attr(transparentBlockAttr)); 2526 2527 const browser$1 = detect$2().browser; 2528 const firstElement = nodes => find$2(nodes, isElement$7); 2529 const getTableCaptionDeltaY = elm => { 2530 if (browser$1.isFirefox() && name(elm) === 'table') { 2531 return firstElement(children$1(elm)).filter(elm => { 2532 return name(elm) === 'caption'; 2533 }).bind(caption => { 2534 return firstElement(nextSiblings(caption)).map(body => { 2535 const bodyTop = body.dom.offsetTop; 2536 const captionTop = caption.dom.offsetTop; 2537 const captionHeight = caption.dom.offsetHeight; 2538 return bodyTop <= captionTop ? -captionHeight : 0; 2539 }); 2540 }).getOr(0); 2541 } else { 2542 return 0; 2543 } 2544 }; 2545 const hasChild = (elm, child) => elm.children && contains$2(elm.children, child); 2546 const getPos = (body, elm, rootElm) => { 2547 let x = 0, y = 0; 2548 const doc = body.ownerDocument; 2549 rootElm = rootElm ? rootElm : body; 2550 if (elm) { 2551 if (rootElm === body && elm.getBoundingClientRect && get$7(SugarElement.fromDom(body), 'position') === 'static') { 2552 const pos = elm.getBoundingClientRect(); 2553 x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - doc.documentElement.clientLeft; 2554 y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - doc.documentElement.clientTop; 2555 return { 2556 x, 2557 y 2558 }; 2559 } 2560 let offsetParent = elm; 2561 while (offsetParent && offsetParent !== rootElm && offsetParent.nodeType && !hasChild(offsetParent, rootElm)) { 2562 const castOffsetParent = offsetParent; 2563 x += castOffsetParent.offsetLeft || 0; 2564 y += castOffsetParent.offsetTop || 0; 2565 offsetParent = castOffsetParent.offsetParent; 2566 } 2567 offsetParent = elm.parentNode; 2568 while (offsetParent && offsetParent !== rootElm && offsetParent.nodeType && !hasChild(offsetParent, rootElm)) { 2569 x -= offsetParent.scrollLeft || 0; 2570 y -= offsetParent.scrollTop || 0; 2571 offsetParent = offsetParent.parentNode; 2572 } 2573 y += getTableCaptionDeltaY(SugarElement.fromDom(elm)); 2574 } 2575 return { 2576 x, 2577 y 2578 }; 2579 }; 2580 2581 const StyleSheetLoader = (documentOrShadowRoot, settings = {}) => { 2582 let idCount = 0; 2583 const loadedStates = {}; 2584 const edos = SugarElement.fromDom(documentOrShadowRoot); 2585 const doc = documentOrOwner(edos); 2586 const _setReferrerPolicy = referrerPolicy => { 2587 settings.referrerPolicy = referrerPolicy; 2588 }; 2589 const _setContentCssCors = contentCssCors => { 2590 settings.contentCssCors = contentCssCors; 2591 }; 2592 const addStyle = element => { 2593 append$1(getStyleContainer(edos), element); 2594 }; 2595 const removeStyle = id => { 2596 const styleContainer = getStyleContainer(edos); 2597 descendant(styleContainer, '#' + id).each(remove$5); 2598 }; 2599 const getOrCreateState = url => get$a(loadedStates, url).getOrThunk(() => ({ 2600 id: 'mce-u' + idCount++, 2601 passed: [], 2602 failed: [], 2603 count: 0 2604 })); 2605 const load = url => new Promise((success, failure) => { 2606 let link; 2607 const urlWithSuffix = Tools._addCacheSuffix(url); 2608 const state = getOrCreateState(urlWithSuffix); 2609 loadedStates[urlWithSuffix] = state; 2610 state.count++; 2611 const resolve = (callbacks, status) => { 2612 each$e(callbacks, call); 2613 state.status = status; 2614 state.passed = []; 2615 state.failed = []; 2616 if (link) { 2617 link.onload = null; 2618 link.onerror = null; 2619 link = null; 2620 } 2621 }; 2622 const passed = () => resolve(state.passed, 2); 2623 const failed = () => resolve(state.failed, 3); 2624 if (success) { 2625 state.passed.push(success); 2626 } 2627 if (failure) { 2628 state.failed.push(failure); 2629 } 2630 if (state.status === 1) { 2631 return; 2632 } 2633 if (state.status === 2) { 2634 passed(); 2635 return; 2636 } 2637 if (state.status === 3) { 2638 failed(); 2639 return; 2640 } 2641 state.status = 1; 2642 const linkElem = SugarElement.fromTag('link', doc.dom); 2643 setAll$1(linkElem, { 2644 rel: 'stylesheet', 2645 type: 'text/css', 2646 id: state.id 2647 }); 2648 if (settings.contentCssCors) { 2649 set$3(linkElem, 'crossOrigin', 'anonymous'); 2650 } 2651 if (settings.referrerPolicy) { 2652 set$3(linkElem, 'referrerpolicy', settings.referrerPolicy); 2653 } 2654 link = linkElem.dom; 2655 link.onload = passed; 2656 link.onerror = failed; 2657 addStyle(linkElem); 2658 set$3(linkElem, 'href', urlWithSuffix); 2659 }); 2660 const loadAll = urls => { 2661 const loadedUrls = Promise.allSettled(map$3(urls, url => load(url).then(constant(url)))); 2662 return loadedUrls.then(results => { 2663 const parts = partition$2(results, r => r.status === 'fulfilled'); 2664 if (parts.fail.length > 0) { 2665 return Promise.reject(map$3(parts.fail, result => result.reason)); 2666 } else { 2667 return map$3(parts.pass, result => result.value); 2668 } 2669 }); 2670 }; 2671 const unload = url => { 2672 const urlWithSuffix = Tools._addCacheSuffix(url); 2673 get$a(loadedStates, urlWithSuffix).each(state => { 2674 const count = --state.count; 2675 if (count === 0) { 2676 delete loadedStates[urlWithSuffix]; 2677 removeStyle(state.id); 2678 } 2679 }); 2680 }; 2681 const unloadAll = urls => { 2682 each$e(urls, url => { 2683 unload(url); 2684 }); 2685 }; 2686 return { 2687 load, 2688 loadAll, 2689 unload, 2690 unloadAll, 2691 _setReferrerPolicy, 2692 _setContentCssCors 2693 }; 2694 }; 2695 2696 const create$d = () => { 2697 const map = new WeakMap(); 2698 const forElement = (referenceElement, settings) => { 2699 const root = getRootNode(referenceElement); 2700 const rootDom = root.dom; 2701 return Optional.from(map.get(rootDom)).getOrThunk(() => { 2702 const sl = StyleSheetLoader(rootDom, settings); 2703 map.set(rootDom, sl); 2704 return sl; 2705 }); 2706 }; 2707 return { forElement }; 2708 }; 2709 const instance = create$d(); 2710 2711 const isSpan = node => node.nodeName.toLowerCase() === 'span'; 2712 const isInlineContent = (node, root) => isNonNullable(node) && (isContent$1(node, root) || isInline$1(SugarElement.fromDom(node))); 2713 const surroundedByInlineContent = (node, root) => { 2714 const prev = new DomTreeWalker(node, root).prev(false); 2715 const next = new DomTreeWalker(node, root).next(false); 2716 const prevIsInline = isUndefined(prev) || isInlineContent(prev, root); 2717 const nextIsInline = isUndefined(next) || isInlineContent(next, root); 2718 return prevIsInline && nextIsInline; 2719 }; 2720 const isBookmarkNode$2 = node => isSpan(node) && node.getAttribute('data-mce-type') === 'bookmark'; 2721 const isKeepTextNode = (node, root) => isText$a(node) && node.data.length > 0 && surroundedByInlineContent(node, root); 2722 const isKeepElement = node => isElement$6(node) ? node.childNodes.length > 0 : false; 2723 const isDocument = node => isDocumentFragment(node) || isDocument$1(node); 2724 const trimNode = (dom, node, root) => { 2725 var _a; 2726 const rootNode = root || node; 2727 if (isElement$6(node) && isBookmarkNode$2(node)) { 2728 return node; 2729 } 2730 const children = node.childNodes; 2731 for (let i = children.length - 1; i >= 0; i--) { 2732 trimNode(dom, children[i], rootNode); 2733 } 2734 if (isElement$6(node)) { 2735 const currentChildren = node.childNodes; 2736 if (currentChildren.length === 1 && isBookmarkNode$2(currentChildren[0])) { 2737 (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(currentChildren[0], node); 2738 } 2739 } 2740 if (!isDocument(node) && !isContent$1(node, rootNode) && !isKeepElement(node) && !isKeepTextNode(node, rootNode)) { 2741 dom.remove(node); 2742 } 2743 return node; 2744 }; 2745 2746 const makeMap$3 = Tools.makeMap; 2747 const attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g; 2748 const textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g; 2749 const rawCharsRegExp = /[<>&\"\']/g; 2750 const entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi; 2751 const asciiMap = { 2752 128: '\u20AC', 2753 130: '\u201A', 2754 131: '\u0192', 2755 132: '\u201E', 2756 133: '\u2026', 2757 134: '\u2020', 2758 135: '\u2021', 2759 136: '\u02c6', 2760 137: '\u2030', 2761 138: '\u0160', 2762 139: '\u2039', 2763 140: '\u0152', 2764 142: '\u017d', 2765 145: '\u2018', 2766 146: '\u2019', 2767 147: '\u201C', 2768 148: '\u201D', 2769 149: '\u2022', 2770 150: '\u2013', 2771 151: '\u2014', 2772 152: '\u02DC', 2773 153: '\u2122', 2774 154: '\u0161', 2775 155: '\u203A', 2776 156: '\u0153', 2777 158: '\u017e', 2778 159: '\u0178' 2779 }; 2780 const baseEntities = { 2781 '"': '"', 2782 '\'': ''', 2783 '<': '<', 2784 '>': '>', 2785 '&': '&', 2786 '`': '`' 2787 }; 2788 const reverseEntities = { 2789 '<': '<', 2790 '>': '>', 2791 '&': '&', 2792 '"': '"', 2793 ''': `'` 2794 }; 2795 const nativeDecode = text => { 2796 const elm = SugarElement.fromTag('div').dom; 2797 elm.innerHTML = text; 2798 return elm.textContent || elm.innerText || text; 2799 }; 2800 const buildEntitiesLookup = (items, radix) => { 2801 const lookup = {}; 2802 if (items) { 2803 const itemList = items.split(','); 2804 radix = radix || 10; 2805 for (let i = 0; i < itemList.length; i += 2) { 2806 const chr = String.fromCharCode(parseInt(itemList[i], radix)); 2807 if (!baseEntities[chr]) { 2808 const entity = '&' + itemList[i + 1] + ';'; 2809 lookup[chr] = entity; 2810 lookup[entity] = chr; 2811 } 2812 } 2813 return lookup; 2814 } else { 2815 return undefined; 2816 } 2817 }; 2818 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); 2819 const encodeRaw = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => { 2820 return baseEntities[chr] || chr; 2821 }); 2822 const encodeAllRaw = text => ('' + text).replace(rawCharsRegExp, chr => { 2823 return baseEntities[chr] || chr; 2824 }); 2825 const encodeNumeric = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => { 2826 if (chr.length > 1) { 2827 return '&#' + ((chr.charCodeAt(0) - 55296) * 1024 + (chr.charCodeAt(1) - 56320) + 65536) + ';'; 2828 } 2829 return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';'; 2830 }); 2831 const encodeNamed = (text, attr, entities) => { 2832 const resolveEntities = entities || namedEntities; 2833 return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => { 2834 return baseEntities[chr] || resolveEntities[chr] || chr; 2835 }); 2836 }; 2837 const getEncodeFunc = (name, entities) => { 2838 const entitiesMap = buildEntitiesLookup(entities) || namedEntities; 2839 const encodeNamedAndNumeric = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => { 2840 if (baseEntities[chr] !== undefined) { 2841 return baseEntities[chr]; 2842 } 2843 if (entitiesMap[chr] !== undefined) { 2844 return entitiesMap[chr]; 2845 } 2846 if (chr.length > 1) { 2847 return '&#' + ((chr.charCodeAt(0) - 55296) * 1024 + (chr.charCodeAt(1) - 56320) + 65536) + ';'; 2848 } 2849 return '&#' + chr.charCodeAt(0) + ';'; 2850 }); 2851 const encodeCustomNamed = (text, attr) => { 2852 return encodeNamed(text, attr, entitiesMap); 2853 }; 2854 const nameMap = makeMap$3(name.replace(/\+/g, ',')); 2855 if (nameMap.named && nameMap.numeric) { 2856 return encodeNamedAndNumeric; 2857 } 2858 if (nameMap.named) { 2859 if (entities) { 2860 return encodeCustomNamed; 2861 } 2862 return encodeNamed; 2863 } 2864 if (nameMap.numeric) { 2865 return encodeNumeric; 2866 } 2867 return encodeRaw; 2868 }; 2869 const decode = text => text.replace(entityRegExp, (all, numeric) => { 2870 if (numeric) { 2871 if (numeric.charAt(0).toLowerCase() === 'x') { 2872 numeric = parseInt(numeric.substr(1), 16); 2873 } else { 2874 numeric = parseInt(numeric, 10); 2875 } 2876 if (numeric > 65535) { 2877 numeric -= 65536; 2878 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (numeric & 1023)); 2879 } 2880 return asciiMap[numeric] || String.fromCharCode(numeric); 2881 } 2882 return reverseEntities[all] || namedEntities[all] || nativeDecode(all); 2883 }); 2884 const Entities = { 2885 encodeRaw, 2886 encodeAllRaw, 2887 encodeNumeric, 2888 encodeNamed, 2889 getEncodeFunc, 2890 decode 2891 }; 2892 2893 const lookupCache = {}; 2894 const mapCache = {}; 2895 const dummyObj = {}; 2896 const makeMap$2 = Tools.makeMap, each$b = Tools.each, extend$2 = Tools.extend, explode$2 = Tools.explode, inArray = Tools.inArray; 2897 const split$1 = (items, delim) => { 2898 items = Tools.trim(items); 2899 return items ? items.split(delim || ' ') : []; 2900 }; 2901 const createMap = (defaultValue, extendWith = {}) => { 2902 const value = makeMap$2(defaultValue, ' ', makeMap$2(defaultValue.toUpperCase(), ' ')); 2903 return extend$2(value, extendWith); 2904 }; 2905 const getTextRootBlockElements = schema => createMap('td th li dt dd figcaption caption details summary', schema.getTextBlockElements()); 2906 const compileSchema = type => { 2907 const schema = {}; 2908 let globalAttributes, blockContent; 2909 let phrasingContent, flowContent; 2910 const add = (name, attributes = '', children = '') => { 2911 const childNames = split$1(children); 2912 const names = split$1(name); 2913 let ni = names.length; 2914 while (ni--) { 2915 const attributesOrder = split$1([ 2916 globalAttributes, 2917 attributes 2918 ].join(' ')); 2919 schema[names[ni]] = { 2920 attributes: mapToObject(attributesOrder, () => ({})), 2921 attributesOrder, 2922 children: mapToObject(childNames, constant(dummyObj)) 2923 }; 2924 } 2925 }; 2926 const addAttrs = (name, attributes) => { 2927 const names = split$1(name); 2928 const attrs = split$1(attributes); 2929 let ni = names.length; 2930 while (ni--) { 2931 const schemaItem = schema[names[ni]]; 2932 for (let i = 0, l = attrs.length; i < l; i++) { 2933 schemaItem.attributes[attrs[i]] = {}; 2934 schemaItem.attributesOrder.push(attrs[i]); 2935 } 2936 } 2937 }; 2938 if (lookupCache[type]) { 2939 return lookupCache[type]; 2940 } 2941 globalAttributes = 'id accesskey class dir lang style tabindex title role'; 2942 blockContent = 'address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul'; 2943 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'; 2944 if (type !== 'html4') { 2945 const transparentContent = 'a ins del canvas map'; 2946 globalAttributes += ' contenteditable contextmenu draggable dropzone ' + 'hidden spellcheck translate'; 2947 blockContent += ' article aside details dialog figure main header footer hgroup section nav ' + transparentContent; 2948 phrasingContent += ' audio canvas command datalist mark meter output picture ' + 'progress time wbr video ruby bdi keygen'; 2949 } 2950 if (type !== 'html5-strict') { 2951 globalAttributes += ' xml:lang'; 2952 const html4PhrasingContent = 'acronym applet basefont big font strike tt'; 2953 phrasingContent = [ 2954 phrasingContent, 2955 html4PhrasingContent 2956 ].join(' '); 2957 each$b(split$1(html4PhrasingContent), name => { 2958 add(name, '', phrasingContent); 2959 }); 2960 const html4BlockContent = 'center dir isindex noframes'; 2961 blockContent = [ 2962 blockContent, 2963 html4BlockContent 2964 ].join(' '); 2965 flowContent = [ 2966 blockContent, 2967 phrasingContent 2968 ].join(' '); 2969 each$b(split$1(html4BlockContent), name => { 2970 add(name, '', flowContent); 2971 }); 2972 } 2973 flowContent = flowContent || [ 2974 blockContent, 2975 phrasingContent 2976 ].join(' '); 2977 add('html', 'manifest', 'head body'); 2978 add('head', '', 'base command link meta noscript script style title'); 2979 add('title hr noscript br'); 2980 add('base', 'href target'); 2981 add('link', 'href rel media hreflang type sizes hreflang'); 2982 add('meta', 'name http-equiv content charset'); 2983 add('style', 'media type scoped'); 2984 add('script', 'src async defer type charset'); 2985 add('body', 'onafterprint onbeforeprint onbeforeunload onblur onerror onfocus ' + 'onhashchange onload onmessage onoffline ononline onpagehide onpageshow ' + 'onpopstate onresize onscroll onstorage onunload', flowContent); 2986 add('dd div', '', flowContent); 2987 add('address dt caption', '', type === 'html4' ? phrasingContent : flowContent); 2988 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); 2989 add('blockquote', 'cite', flowContent); 2990 add('ol', 'reversed start type', 'li'); 2991 add('ul', '', 'li'); 2992 add('li', 'value', flowContent); 2993 add('dl', '', 'dt dd'); 2994 add('a', 'href target rel media hreflang type', type === 'html4' ? phrasingContent : flowContent); 2995 add('q', 'cite', phrasingContent); 2996 add('ins del', 'cite datetime', flowContent); 2997 add('img', 'src sizes srcset alt usemap ismap width height'); 2998 add('iframe', 'src name width height', flowContent); 2999 add('embed', 'src type width height'); 3000 add('object', 'data type typemustmatch name usemap form width height', [ 3001 flowContent, 3002 'param' 3003 ].join(' ')); 3004 add('param', 'name value'); 3005 add('map', 'name', [ 3006 flowContent, 3007 'area' 3008 ].join(' ')); 3009 add('area', 'alt coords shape href target rel media hreflang type'); 3010 add('table', 'border', 'caption colgroup thead tfoot tbody tr' + (type === 'html4' ? ' col' : '')); 3011 add('colgroup', 'span', 'col'); 3012 add('col', 'span'); 3013 add('tbody thead tfoot', '', 'tr'); 3014 add('tr', '', 'td th'); 3015 add('td', 'colspan rowspan headers', flowContent); 3016 add('th', 'colspan rowspan headers scope abbr', flowContent); 3017 add('form', 'accept-charset action autocomplete enctype method name novalidate target', flowContent); 3018 add('fieldset', 'disabled form name', [ 3019 flowContent, 3020 'legend' 3021 ].join(' ')); 3022 add('label', 'form for', phrasingContent); 3023 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'); 3024 add('button', 'disabled form formaction formenctype formmethod formnovalidate formtarget name type value', type === 'html4' ? flowContent : phrasingContent); 3025 add('select', 'disabled form multiple name required size', 'option optgroup'); 3026 add('optgroup', 'disabled label', 'option'); 3027 add('option', 'disabled label selected value'); 3028 add('textarea', 'cols dirname disabled form maxlength name readonly required rows wrap'); 3029 add('menu', 'type label', [ 3030 flowContent, 3031 'li' 3032 ].join(' ')); 3033 add('noscript', '', flowContent); 3034 if (type !== 'html4') { 3035 add('wbr'); 3036 add('ruby', '', [ 3037 phrasingContent, 3038 'rt rp' 3039 ].join(' ')); 3040 add('figcaption', '', flowContent); 3041 add('mark rt rp summary bdi', '', phrasingContent); 3042 add('canvas', 'width height', flowContent); 3043 add('video', 'src crossorigin poster preload autoplay mediagroup loop ' + 'muted controls width height buffered', [ 3044 flowContent, 3045 'track source' 3046 ].join(' ')); 3047 add('audio', 'src crossorigin preload autoplay mediagroup loop muted controls ' + 'buffered volume', [ 3048 flowContent, 3049 'track source' 3050 ].join(' ')); 3051 add('picture', '', 'img source'); 3052 add('source', 'src srcset type media sizes'); 3053 add('track', 'kind src srclang label default'); 3054 add('datalist', '', [ 3055 phrasingContent, 3056 'option' 3057 ].join(' ')); 3058 add('article section nav aside main header footer', '', flowContent); 3059 add('hgroup', '', 'h1 h2 h3 h4 h5 h6'); 3060 add('figure', '', [ 3061 flowContent, 3062 'figcaption' 3063 ].join(' ')); 3064 add('time', 'datetime', phrasingContent); 3065 add('dialog', 'open', flowContent); 3066 add('command', 'type label icon disabled checked radiogroup command'); 3067 add('output', 'for form name', phrasingContent); 3068 add('progress', 'value max', phrasingContent); 3069 add('meter', 'value min max low high optimum', phrasingContent); 3070 add('details', 'open', [ 3071 flowContent, 3072 'summary' 3073 ].join(' ')); 3074 add('keygen', 'autofocus challenge disabled form keytype name'); 3075 } 3076 if (type !== 'html5-strict') { 3077 addAttrs('script', 'language xml:space'); 3078 addAttrs('style', 'xml:space'); 3079 addAttrs('object', 'declare classid code codebase codetype archive standby align border hspace vspace'); 3080 addAttrs('embed', 'align name hspace vspace'); 3081 addAttrs('param', 'valuetype type'); 3082 addAttrs('a', 'charset name rev shape coords'); 3083 addAttrs('br', 'clear'); 3084 addAttrs('applet', 'codebase archive code object alt name width height align hspace vspace'); 3085 addAttrs('img', 'name longdesc align border hspace vspace'); 3086 addAttrs('iframe', 'longdesc frameborder marginwidth marginheight scrolling align'); 3087 addAttrs('font basefont', 'size color face'); 3088 addAttrs('input', 'usemap align'); 3089 addAttrs('select'); 3090 addAttrs('textarea'); 3091 addAttrs('h1 h2 h3 h4 h5 h6 div p legend caption', 'align'); 3092 addAttrs('ul', 'type compact'); 3093 addAttrs('li', 'type'); 3094 addAttrs('ol dl menu dir', 'compact'); 3095 addAttrs('pre', 'width xml:space'); 3096 addAttrs('hr', 'align noshade size width'); 3097 addAttrs('isindex', 'prompt'); 3098 addAttrs('table', 'summary width frame rules cellspacing cellpadding align bgcolor'); 3099 addAttrs('col', 'width align char charoff valign'); 3100 addAttrs('colgroup', 'width align char charoff valign'); 3101 addAttrs('thead', 'align char charoff valign'); 3102 addAttrs('tr', 'align char charoff valign bgcolor'); 3103 addAttrs('th', 'axis align char charoff valign nowrap bgcolor width height'); 3104 addAttrs('form', 'accept'); 3105 addAttrs('td', 'abbr axis scope align char charoff valign nowrap bgcolor width height'); 3106 addAttrs('tfoot', 'align char charoff valign'); 3107 addAttrs('tbody', 'align char charoff valign'); 3108 addAttrs('area', 'nohref'); 3109 addAttrs('body', 'background bgcolor text link vlink alink'); 3110 } 3111 if (type !== 'html4') { 3112 addAttrs('input button select textarea', 'autofocus'); 3113 addAttrs('input textarea', 'placeholder'); 3114 addAttrs('a', 'download'); 3115 addAttrs('link script img', 'crossorigin'); 3116 addAttrs('img', 'loading'); 3117 addAttrs('iframe', 'sandbox seamless allow allowfullscreen loading'); 3118 } 3119 if (type !== 'html4') { 3120 each$e([ 3121 schema.video, 3122 schema.audio 3123 ], item => { 3124 delete item.children.audio; 3125 delete item.children.video; 3126 }); 3127 } 3128 each$b(split$1('a form meter progress dfn'), name => { 3129 if (schema[name]) { 3130 delete schema[name].children[name]; 3131 } 3132 }); 3133 delete schema.caption.children.table; 3134 delete schema.script; 3135 lookupCache[type] = schema; 3136 return schema; 3137 }; 3138 const compileElementMap = (value, mode) => { 3139 if (value) { 3140 const styles = {}; 3141 if (isString(value)) { 3142 value = { '*': value }; 3143 } 3144 each$b(value, (value, key) => { 3145 styles[key] = styles[key.toUpperCase()] = mode === 'map' ? makeMap$2(value, /[, ]/) : explode$2(value, /[, ]/); 3146 }); 3147 return styles; 3148 } else { 3149 return undefined; 3150 } 3151 }; 3152 const Schema = (settings = {}) => { 3153 var _a; 3154 const elements = {}; 3155 const children = {}; 3156 let patternElements = []; 3157 const customElementsMap = {}; 3158 const specialElements = {}; 3159 const createLookupTable = (option, defaultValue, extendWith) => { 3160 const value = settings[option]; 3161 if (!value) { 3162 let newValue = mapCache[option]; 3163 if (!newValue) { 3164 newValue = createMap(defaultValue, extendWith); 3165 mapCache[option] = newValue; 3166 } 3167 return newValue; 3168 } else { 3169 return makeMap$2(value, /[, ]/, makeMap$2(value.toUpperCase(), /[, ]/)); 3170 } 3171 }; 3172 const schemaType = (_a = settings.schema) !== null && _a !== void 0 ? _a : 'html5'; 3173 const schemaItems = compileSchema(schemaType); 3174 if (settings.verify_html === false) { 3175 settings.valid_elements = '*[*]'; 3176 } 3177 const validStyles = compileElementMap(settings.valid_styles); 3178 const invalidStyles = compileElementMap(settings.invalid_styles, 'map'); 3179 const validClasses = compileElementMap(settings.valid_classes, 'map'); 3180 const whitespaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object code'); 3181 const selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr'); 3182 const voidElementsMap = createLookupTable('void_elements', 'area base basefont br col frame hr img input isindex link ' + 'meta param embed source wbr track'); 3183 const boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' + 'noshade nowrap readonly selected autoplay loop controls allowfullscreen'); 3184 const nonEmptyOrMoveCaretBeforeOnEnter = 'td th iframe video audio object script code'; 3185 const nonEmptyElementsMap = createLookupTable('non_empty_elements', nonEmptyOrMoveCaretBeforeOnEnter + ' pre', voidElementsMap); 3186 const moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', nonEmptyOrMoveCaretBeforeOnEnter + ' table', voidElementsMap); 3187 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'); 3188 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); 3189 const textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font s strike u var cite ' + 'dfn code mark q sup sub samp'); 3190 const transparentElementsMap = createLookupTable('transparent_elements', 'a ins del canvas map'); 3191 each$b('script noscript iframe noframes noembed title style textarea xmp plaintext'.split(' '), name => { 3192 specialElements[name] = new RegExp('</' + name + '[^>]*>', 'gi'); 3193 }); 3194 const patternToRegExp = str => new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$'); 3195 const addValidElements = validElements => { 3196 const elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)])?$/; 3197 const attrRuleRegExp = /^([!\-])?(\w+[\\:]:\w+|[^=~<]+)?(?:([=~<])(.*))?$/; 3198 const hasPatternsRegExp = /[*?+]/; 3199 if (validElements) { 3200 const validElementsArr = split$1(validElements, ','); 3201 let globalAttributes; 3202 let globalAttributesOrder; 3203 if (elements['@']) { 3204 globalAttributes = elements['@'].attributes; 3205 globalAttributesOrder = elements['@'].attributesOrder; 3206 } 3207 for (let ei = 0, el = validElementsArr.length; ei < el; ei++) { 3208 let matches = elementRuleRegExp.exec(validElementsArr[ei]); 3209 if (matches) { 3210 const prefix = matches[1]; 3211 const elementName = matches[2]; 3212 const outputName = matches[3]; 3213 const attrData = matches[5]; 3214 const attributes = {}; 3215 const attributesOrder = []; 3216 const element = { 3217 attributes, 3218 attributesOrder 3219 }; 3220 if (prefix === '#') { 3221 element.paddEmpty = true; 3222 } 3223 if (prefix === '-') { 3224 element.removeEmpty = true; 3225 } 3226 if (matches[4] === '!') { 3227 element.removeEmptyAttrs = true; 3228 } 3229 if (globalAttributes) { 3230 each$d(globalAttributes, (value, key) => { 3231 attributes[key] = value; 3232 }); 3233 if (globalAttributesOrder) { 3234 attributesOrder.push(...globalAttributesOrder); 3235 } 3236 } 3237 if (attrData) { 3238 const attrDatas = split$1(attrData, '|'); 3239 for (let ai = 0, al = attrDatas.length; ai < al; ai++) { 3240 matches = attrRuleRegExp.exec(attrDatas[ai]); 3241 if (matches) { 3242 const attr = {}; 3243 const attrType = matches[1]; 3244 const attrName = matches[2].replace(/[\\:]:/g, ':'); 3245 const attrPrefix = matches[3]; 3246 const value = matches[4]; 3247 if (attrType === '!') { 3248 element.attributesRequired = element.attributesRequired || []; 3249 element.attributesRequired.push(attrName); 3250 attr.required = true; 3251 } 3252 if (attrType === '-') { 3253 delete attributes[attrName]; 3254 attributesOrder.splice(inArray(attributesOrder, attrName), 1); 3255 continue; 3256 } 3257 if (attrPrefix) { 3258 if (attrPrefix === '=') { 3259 element.attributesDefault = element.attributesDefault || []; 3260 element.attributesDefault.push({ 3261 name: attrName, 3262 value 3263 }); 3264 attr.defaultValue = value; 3265 } 3266 if (attrPrefix === '~') { 3267 element.attributesForced = element.attributesForced || []; 3268 element.attributesForced.push({ 3269 name: attrName, 3270 value 3271 }); 3272 attr.forcedValue = value; 3273 } 3274 if (attrPrefix === '<') { 3275 attr.validValues = makeMap$2(value, '?'); 3276 } 3277 } 3278 if (hasPatternsRegExp.test(attrName)) { 3279 const attrPattern = attr; 3280 element.attributePatterns = element.attributePatterns || []; 3281 attrPattern.pattern = patternToRegExp(attrName); 3282 element.attributePatterns.push(attrPattern); 3283 } else { 3284 if (!attributes[attrName]) { 3285 attributesOrder.push(attrName); 3286 } 3287 attributes[attrName] = attr; 3288 } 3289 } 3290 } 3291 } 3292 if (!globalAttributes && elementName === '@') { 3293 globalAttributes = attributes; 3294 globalAttributesOrder = attributesOrder; 3295 } 3296 if (outputName) { 3297 element.outputName = elementName; 3298 elements[outputName] = element; 3299 } 3300 if (hasPatternsRegExp.test(elementName)) { 3301 const patternElement = element; 3302 patternElement.pattern = patternToRegExp(elementName); 3303 patternElements.push(patternElement); 3304 } else { 3305 elements[elementName] = element; 3306 } 3307 } 3308 } 3309 } 3310 }; 3311 const setValidElements = validElements => { 3312 patternElements = []; 3313 each$e(keys(elements), name => { 3314 delete elements[name]; 3315 }); 3316 addValidElements(validElements); 3317 each$b(schemaItems, (element, name) => { 3318 children[name] = element.children; 3319 }); 3320 }; 3321 const addCustomElements = customElements => { 3322 const customElementRegExp = /^(~)?(.+)$/; 3323 if (customElements) { 3324 delete mapCache.text_block_elements; 3325 delete mapCache.block_elements; 3326 each$b(split$1(customElements, ','), rule => { 3327 const matches = customElementRegExp.exec(rule); 3328 if (matches) { 3329 const inline = matches[1] === '~'; 3330 const cloneName = inline ? 'span' : 'div'; 3331 const name = matches[2]; 3332 children[name] = children[cloneName]; 3333 customElementsMap[name] = cloneName; 3334 nonEmptyElementsMap[name.toUpperCase()] = {}; 3335 nonEmptyElementsMap[name] = {}; 3336 if (!inline) { 3337 blockElementsMap[name.toUpperCase()] = {}; 3338 blockElementsMap[name] = {}; 3339 } 3340 if (!elements[name]) { 3341 let customRule = elements[cloneName]; 3342 customRule = extend$2({}, customRule); 3343 delete customRule.removeEmptyAttrs; 3344 delete customRule.removeEmpty; 3345 elements[name] = customRule; 3346 } 3347 each$b(children, (element, elmName) => { 3348 if (element[cloneName]) { 3349 children[elmName] = element = extend$2({}, children[elmName]); 3350 element[name] = element[cloneName]; 3351 } 3352 }); 3353 } 3354 }); 3355 } 3356 }; 3357 const addValidChildren = validChildren => { 3358 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]+)\[([^\]]+)]$/; 3359 delete lookupCache[schemaType]; 3360 if (validChildren) { 3361 each$b(split$1(validChildren, ','), rule => { 3362 const matches = childRuleRegExp.exec(rule); 3363 if (matches) { 3364 const prefix = matches[1]; 3365 let parent; 3366 if (prefix) { 3367 parent = children[matches[2]]; 3368 } else { 3369 parent = children[matches[2]] = { '#comment': {} }; 3370 } 3371 parent = children[matches[2]]; 3372 each$b(split$1(matches[3], '|'), child => { 3373 if (prefix === '-') { 3374 delete parent[child]; 3375 } else { 3376 parent[child] = {}; 3377 } 3378 }); 3379 } 3380 }); 3381 } 3382 }; 3383 const getElementRule = name => { 3384 const element = elements[name]; 3385 if (element) { 3386 return element; 3387 } 3388 let i = patternElements.length; 3389 while (i--) { 3390 const patternElement = patternElements[i]; 3391 if (patternElement.pattern.test(name)) { 3392 return patternElement; 3393 } 3394 } 3395 return undefined; 3396 }; 3397 if (!settings.valid_elements) { 3398 each$b(schemaItems, (element, name) => { 3399 elements[name] = { 3400 attributes: element.attributes, 3401 attributesOrder: element.attributesOrder 3402 }; 3403 children[name] = element.children; 3404 }); 3405 each$b(split$1('strong/b em/i'), item => { 3406 const items = split$1(item, '/'); 3407 elements[items[1]].outputName = items[0]; 3408 }); 3409 each$b(textInlineElementsMap, (_val, name) => { 3410 if (elements[name]) { 3411 if (settings.padd_empty_block_inline_children) { 3412 elements[name].paddInEmptyBlock = true; 3413 } 3414 elements[name].removeEmpty = true; 3415 } 3416 }); 3417 each$b(split$1('ol ul blockquote a table tbody'), name => { 3418 if (elements[name]) { 3419 elements[name].removeEmpty = true; 3420 } 3421 }); 3422 each$b(split$1('p h1 h2 h3 h4 h5 h6 th td pre div address caption li summary'), name => { 3423 if (elements[name]) { 3424 elements[name].paddEmpty = true; 3425 } 3426 }); 3427 each$b(split$1('span'), name => { 3428 elements[name].removeEmptyAttrs = true; 3429 }); 3430 } else { 3431 setValidElements(settings.valid_elements); 3432 } 3433 addCustomElements(settings.custom_elements); 3434 addValidChildren(settings.valid_children); 3435 addValidElements(settings.extended_valid_elements); 3436 addValidChildren('+ol[ul|ol],+ul[ul|ol]'); 3437 each$b({ 3438 dd: 'dl', 3439 dt: 'dl', 3440 li: 'ul ol', 3441 td: 'tr', 3442 th: 'tr', 3443 tr: 'tbody thead tfoot', 3444 tbody: 'table', 3445 thead: 'table', 3446 tfoot: 'table', 3447 legend: 'fieldset', 3448 area: 'map', 3449 param: 'video audio object' 3450 }, (parents, item) => { 3451 if (elements[item]) { 3452 elements[item].parentsRequired = split$1(parents); 3453 } 3454 }); 3455 if (settings.invalid_elements) { 3456 each$b(explode$2(settings.invalid_elements), item => { 3457 if (elements[item]) { 3458 delete elements[item]; 3459 } 3460 }); 3461 } 3462 if (!getElementRule('span')) { 3463 addValidElements('span[!data-mce-type|*]'); 3464 } 3465 const getValidStyles = constant(validStyles); 3466 const getInvalidStyles = constant(invalidStyles); 3467 const getValidClasses = constant(validClasses); 3468 const getBoolAttrs = constant(boolAttrMap); 3469 const getBlockElements = constant(blockElementsMap); 3470 const getTextBlockElements = constant(textBlockElementsMap); 3471 const getTextInlineElements = constant(textInlineElementsMap); 3472 const getVoidElements = constant(Object.seal(voidElementsMap)); 3473 const getSelfClosingElements = constant(selfClosingElementsMap); 3474 const getNonEmptyElements = constant(nonEmptyElementsMap); 3475 const getMoveCaretBeforeOnEnterElements = constant(moveCaretBeforeOnEnterElementsMap); 3476 const getWhitespaceElements = constant(whitespaceElementsMap); 3477 const getTransparentElements = constant(transparentElementsMap); 3478 const getSpecialElements = constant(Object.seal(specialElements)); 3479 const isValidChild = (name, child) => { 3480 const parent = children[name.toLowerCase()]; 3481 return !!(parent && parent[child.toLowerCase()]); 3482 }; 3483 const isValid = (name, attr) => { 3484 const rule = getElementRule(name); 3485 if (rule) { 3486 if (attr) { 3487 if (rule.attributes[attr]) { 3488 return true; 3489 } 3490 const attrPatterns = rule.attributePatterns; 3491 if (attrPatterns) { 3492 let i = attrPatterns.length; 3493 while (i--) { 3494 if (attrPatterns[i].pattern.test(attr)) { 3495 return true; 3496 } 3497 } 3498 } 3499 } else { 3500 return true; 3501 } 3502 } 3503 return false; 3504 }; 3505 const getCustomElements = constant(customElementsMap); 3506 return { 3507 type: schemaType, 3508 children, 3509 elements, 3510 getValidStyles, 3511 getValidClasses, 3512 getBlockElements, 3513 getInvalidStyles, 3514 getVoidElements, 3515 getTextBlockElements, 3516 getTextInlineElements, 3517 getBoolAttrs, 3518 getElementRule, 3519 getSelfClosingElements, 3520 getNonEmptyElements, 3521 getMoveCaretBeforeOnEnterElements, 3522 getWhitespaceElements, 3523 getTransparentElements, 3524 getSpecialElements, 3525 isValidChild, 3526 isValid, 3527 getCustomElements, 3528 addValidElements, 3529 setValidElements, 3530 addCustomElements, 3531 addValidChildren 3532 }; 3533 }; 3534 3535 const Styles = (settings = {}, schema) => { 3536 const urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi; 3537 const styleRegExp = /\s*([^:]+):\s*([^;]+);?/g; 3538 const trimRightRegExp = /\s+$/; 3539 const encodingLookup = {}; 3540 let validStyles; 3541 let invalidStyles; 3542 const invisibleChar = zeroWidth; 3543 if (schema) { 3544 validStyles = schema.getValidStyles(); 3545 invalidStyles = schema.getInvalidStyles(); 3546 } 3547 const encodingItems = (`\\" \\' \\; \\: ; : ` + invisibleChar).split(' '); 3548 for (let i = 0; i < encodingItems.length; i++) { 3549 encodingLookup[encodingItems[i]] = invisibleChar + i; 3550 encodingLookup[invisibleChar + i] = encodingItems[i]; 3551 } 3552 const self = { 3553 parse: css => { 3554 const styles = {}; 3555 let isEncoded = false; 3556 const urlConverter = settings.url_converter; 3557 const urlConverterScope = settings.url_converter_scope || self; 3558 const compress = (prefix, suffix, noJoin) => { 3559 const top = styles[prefix + '-top' + suffix]; 3560 if (!top) { 3561 return; 3562 } 3563 const right = styles[prefix + '-right' + suffix]; 3564 if (!right) { 3565 return; 3566 } 3567 const bottom = styles[prefix + '-bottom' + suffix]; 3568 if (!bottom) { 3569 return; 3570 } 3571 const left = styles[prefix + '-left' + suffix]; 3572 if (!left) { 3573 return; 3574 } 3575 const box = [ 3576 top, 3577 right, 3578 bottom, 3579 left 3580 ]; 3581 let i = box.length - 1; 3582 while (i--) { 3583 if (box[i] !== box[i + 1]) { 3584 break; 3585 } 3586 } 3587 if (i > -1 && noJoin) { 3588 return; 3589 } 3590 styles[prefix + suffix] = i === -1 ? box[0] : box.join(' '); 3591 delete styles[prefix + '-top' + suffix]; 3592 delete styles[prefix + '-right' + suffix]; 3593 delete styles[prefix + '-bottom' + suffix]; 3594 delete styles[prefix + '-left' + suffix]; 3595 }; 3596 const canCompress = key => { 3597 const value = styles[key]; 3598 if (!value) { 3599 return; 3600 } 3601 const values = value.split(' '); 3602 let i = values.length; 3603 while (i--) { 3604 if (values[i] !== values[0]) { 3605 return false; 3606 } 3607 } 3608 styles[key] = values[0]; 3609 return true; 3610 }; 3611 const compress2 = (target, a, b, c) => { 3612 if (!canCompress(a)) { 3613 return; 3614 } 3615 if (!canCompress(b)) { 3616 return; 3617 } 3618 if (!canCompress(c)) { 3619 return; 3620 } 3621 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c]; 3622 delete styles[a]; 3623 delete styles[b]; 3624 delete styles[c]; 3625 }; 3626 const encode = str => { 3627 isEncoded = true; 3628 return encodingLookup[str]; 3629 }; 3630 const decode = (str, keepSlashes) => { 3631 if (isEncoded) { 3632 str = str.replace(/\uFEFF[0-9]/g, str => { 3633 return encodingLookup[str]; 3634 }); 3635 } 3636 if (!keepSlashes) { 3637 str = str.replace(/\\([\'\";:])/g, '$1'); 3638 } 3639 return str; 3640 }; 3641 const decodeSingleHexSequence = escSeq => { 3642 return String.fromCharCode(parseInt(escSeq.slice(1), 16)); 3643 }; 3644 const decodeHexSequences = value => { 3645 return value.replace(/\\[0-9a-f]+/gi, decodeSingleHexSequence); 3646 }; 3647 const processUrl = (match, url, url2, url3, str, str2) => { 3648 str = str || str2; 3649 if (str) { 3650 str = decode(str); 3651 return `'` + str.replace(/\'/g, `\\'`) + `'`; 3652 } 3653 url = decode(url || url2 || url3 || ''); 3654 if (!settings.allow_script_urls) { 3655 const scriptUrl = url.replace(/[\s\r\n]+/g, ''); 3656 if (/(java|vb)script:/i.test(scriptUrl)) { 3657 return ''; 3658 } 3659 if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) { 3660 return ''; 3661 } 3662 } 3663 if (urlConverter) { 3664 url = urlConverter.call(urlConverterScope, url, 'style'); 3665 } 3666 return `url('` + url.replace(/\'/g, `\\'`) + `')`; 3667 }; 3668 if (css) { 3669 css = css.replace(/[\u0000-\u001F]/g, ''); 3670 css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, str => { 3671 return str.replace(/[;:]/g, encode); 3672 }); 3673 let matches; 3674 while (matches = styleRegExp.exec(css)) { 3675 styleRegExp.lastIndex = matches.index + matches[0].length; 3676 let name = matches[1].replace(trimRightRegExp, '').toLowerCase(); 3677 let value = matches[2].replace(trimRightRegExp, ''); 3678 if (name && value) { 3679 name = decodeHexSequences(name); 3680 value = decodeHexSequences(value); 3681 if (name.indexOf(invisibleChar) !== -1 || name.indexOf('"') !== -1) { 3682 continue; 3683 } 3684 if (!settings.allow_script_urls && (name === 'behavior' || /expression\s*\(|\/\*|\*\//.test(value))) { 3685 continue; 3686 } 3687 if (name === 'font-weight' && value === '700') { 3688 value = 'bold'; 3689 } else if (name === 'color' || name === 'background-color') { 3690 value = value.toLowerCase(); 3691 } 3692 value = value.replace(urlOrStrRegExp, processUrl); 3693 styles[name] = isEncoded ? decode(value, true) : value; 3694 } 3695 } 3696 compress('border', '', true); 3697 compress('border', '-width'); 3698 compress('border', '-color'); 3699 compress('border', '-style'); 3700 compress('padding', ''); 3701 compress('margin', ''); 3702 compress2('border', 'border-width', 'border-style', 'border-color'); 3703 if (styles.border === 'medium none') { 3704 delete styles.border; 3705 } 3706 if (styles['border-image'] === 'none') { 3707 delete styles['border-image']; 3708 } 3709 } 3710 return styles; 3711 }, 3712 serialize: (styles, elementName) => { 3713 let css = ''; 3714 const serializeStyles = (elemName, validStyleList) => { 3715 const styleList = validStyleList[elemName]; 3716 if (styleList) { 3717 for (let i = 0, l = styleList.length; i < l; i++) { 3718 const name = styleList[i]; 3719 const value = styles[name]; 3720 if (value) { 3721 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';'; 3722 } 3723 } 3724 } 3725 }; 3726 const isValid = (name, elemName) => { 3727 if (!invalidStyles || !elemName) { 3728 return true; 3729 } 3730 let styleMap = invalidStyles['*']; 3731 if (styleMap && styleMap[name]) { 3732 return false; 3733 } 3734 styleMap = invalidStyles[elemName]; 3735 return !(styleMap && styleMap[name]); 3736 }; 3737 if (elementName && validStyles) { 3738 serializeStyles('*', validStyles); 3739 serializeStyles(elementName, validStyles); 3740 } else { 3741 each$d(styles, (value, name) => { 3742 if (value && isValid(name, elementName)) { 3743 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';'; 3744 } 3745 }); 3746 } 3747 return css; 3748 } 3749 }; 3750 return self; 3751 }; 3752 3753 const deprecated = { 3754 keyLocation: true, 3755 layerX: true, 3756 layerY: true, 3757 returnValue: true, 3758 webkitMovementX: true, 3759 webkitMovementY: true, 3760 keyIdentifier: true, 3761 mozPressure: true 3762 }; 3763 const isNativeEvent = event => event instanceof Event || isFunction(event.initEvent); 3764 const hasIsDefaultPrevented = event => event.isDefaultPrevented === always || event.isDefaultPrevented === never; 3765 const needsNormalizing = event => isNullable(event.preventDefault) || isNativeEvent(event); 3766 const clone$3 = (originalEvent, data) => { 3767 const event = data !== null && data !== void 0 ? data : {}; 3768 for (const name in originalEvent) { 3769 if (!has$2(deprecated, name)) { 3770 event[name] = originalEvent[name]; 3771 } 3772 } 3773 if (isNonNullable(originalEvent.composedPath)) { 3774 event.composedPath = () => originalEvent.composedPath(); 3775 } 3776 return event; 3777 }; 3778 const normalize$3 = (type, originalEvent, fallbackTarget, data) => { 3779 var _a; 3780 const event = clone$3(originalEvent, data); 3781 event.type = type; 3782 if (isNullable(event.target)) { 3783 event.target = (_a = event.srcElement) !== null && _a !== void 0 ? _a : fallbackTarget; 3784 } 3785 if (needsNormalizing(originalEvent)) { 3786 event.preventDefault = () => { 3787 event.defaultPrevented = true; 3788 event.isDefaultPrevented = always; 3789 if (isFunction(originalEvent.preventDefault)) { 3790 originalEvent.preventDefault(); 3791 } 3792 }; 3793 event.stopPropagation = () => { 3794 event.cancelBubble = true; 3795 event.isPropagationStopped = always; 3796 if (isFunction(originalEvent.stopPropagation)) { 3797 originalEvent.stopPropagation(); 3798 } 3799 }; 3800 event.stopImmediatePropagation = () => { 3801 event.isImmediatePropagationStopped = always; 3802 event.stopPropagation(); 3803 }; 3804 if (!hasIsDefaultPrevented(event)) { 3805 event.isDefaultPrevented = event.defaultPrevented === true ? always : never; 3806 event.isPropagationStopped = event.cancelBubble === true ? always : never; 3807 event.isImmediatePropagationStopped = never; 3808 } 3809 } 3810 return event; 3811 }; 3812 3813 const eventExpandoPrefix = 'mce-data-'; 3814 const mouseEventRe = /^(?:mouse|contextmenu)|click/; 3815 const addEvent = (target, name, callback, capture) => { 3816 target.addEventListener(name, callback, capture || false); 3817 }; 3818 const removeEvent = (target, name, callback, capture) => { 3819 target.removeEventListener(name, callback, capture || false); 3820 }; 3821 const isMouseEvent = event => isNonNullable(event) && mouseEventRe.test(event.type); 3822 const fix = (originalEvent, data) => { 3823 const event = normalize$3(originalEvent.type, originalEvent, document, data); 3824 if (isMouseEvent(originalEvent) && isUndefined(originalEvent.pageX) && !isUndefined(originalEvent.clientX)) { 3825 const eventDoc = event.target.ownerDocument || document; 3826 const doc = eventDoc.documentElement; 3827 const body = eventDoc.body; 3828 const mouseEvent = event; 3829 mouseEvent.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); 3830 mouseEvent.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); 3831 } 3832 return event; 3833 }; 3834 const bindOnReady = (win, callback, eventUtils) => { 3835 const doc = win.document, event = { type: 'ready' }; 3836 if (eventUtils.domLoaded) { 3837 callback(event); 3838 return; 3839 } 3840 const isDocReady = () => { 3841 return doc.readyState === 'complete' || doc.readyState === 'interactive' && doc.body; 3842 }; 3843 const readyHandler = () => { 3844 removeEvent(win, 'DOMContentLoaded', readyHandler); 3845 removeEvent(win, 'load', readyHandler); 3846 if (!eventUtils.domLoaded) { 3847 eventUtils.domLoaded = true; 3848 callback(event); 3849 } 3850 win = null; 3851 }; 3852 if (isDocReady()) { 3853 readyHandler(); 3854 } else { 3855 addEvent(win, 'DOMContentLoaded', readyHandler); 3856 } 3857 if (!eventUtils.domLoaded) { 3858 addEvent(win, 'load', readyHandler); 3859 } 3860 }; 3861 class EventUtils { 3862 constructor() { 3863 this.domLoaded = false; 3864 this.events = {}; 3865 this.count = 1; 3866 this.expando = eventExpandoPrefix + (+new Date()).toString(32); 3867 this.hasFocusIn = 'onfocusin' in document.documentElement; 3868 this.count = 1; 3869 } 3870 bind(target, names, callback, scope) { 3871 const self = this; 3872 let callbackList; 3873 const win = window; 3874 const defaultNativeHandler = evt => { 3875 self.executeHandlers(fix(evt || win.event), id); 3876 }; 3877 if (!target || isText$a(target) || isComment(target)) { 3878 return callback; 3879 } 3880 let id; 3881 if (!target[self.expando]) { 3882 id = self.count++; 3883 target[self.expando] = id; 3884 self.events[id] = {}; 3885 } else { 3886 id = target[self.expando]; 3887 } 3888 scope = scope || target; 3889 const namesList = names.split(' '); 3890 let i = namesList.length; 3891 while (i--) { 3892 let name = namesList[i]; 3893 let nativeHandler = defaultNativeHandler; 3894 let capture = false; 3895 let fakeName = false; 3896 if (name === 'DOMContentLoaded') { 3897 name = 'ready'; 3898 } 3899 if (self.domLoaded && name === 'ready' && target.readyState === 'complete') { 3900 callback.call(scope, fix({ type: name })); 3901 continue; 3902 } 3903 if (!self.hasFocusIn && (name === 'focusin' || name === 'focusout')) { 3904 capture = true; 3905 fakeName = name === 'focusin' ? 'focus' : 'blur'; 3906 nativeHandler = evt => { 3907 const event = fix(evt || win.event); 3908 event.type = event.type === 'focus' ? 'focusin' : 'focusout'; 3909 self.executeHandlers(event, id); 3910 }; 3911 } 3912 callbackList = self.events[id][name]; 3913 if (!callbackList) { 3914 self.events[id][name] = callbackList = [{ 3915 func: callback, 3916 scope 3917 }]; 3918 callbackList.fakeName = fakeName; 3919 callbackList.capture = capture; 3920 callbackList.nativeHandler = nativeHandler; 3921 if (name === 'ready') { 3922 bindOnReady(target, nativeHandler, self); 3923 } else { 3924 addEvent(target, fakeName || name, nativeHandler, capture); 3925 } 3926 } else { 3927 if (name === 'ready' && self.domLoaded) { 3928 callback(fix({ type: name })); 3929 } else { 3930 callbackList.push({ 3931 func: callback, 3932 scope 3933 }); 3934 } 3935 } 3936 } 3937 target = callbackList = null; 3938 return callback; 3939 } 3940 unbind(target, names, callback) { 3941 if (!target || isText$a(target) || isComment(target)) { 3942 return this; 3943 } 3944 const id = target[this.expando]; 3945 if (id) { 3946 let eventMap = this.events[id]; 3947 if (names) { 3948 const namesList = names.split(' '); 3949 let i = namesList.length; 3950 while (i--) { 3951 const name = namesList[i]; 3952 const callbackList = eventMap[name]; 3953 if (callbackList) { 3954 if (callback) { 3955 let ci = callbackList.length; 3956 while (ci--) { 3957 if (callbackList[ci].func === callback) { 3958 const nativeHandler = callbackList.nativeHandler; 3959 const fakeName = callbackList.fakeName, capture = callbackList.capture; 3960 const newCallbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1)); 3961 newCallbackList.nativeHandler = nativeHandler; 3962 newCallbackList.fakeName = fakeName; 3963 newCallbackList.capture = capture; 3964 eventMap[name] = newCallbackList; 3965 } 3966 } 3967 } 3968 if (!callback || callbackList.length === 0) { 3969 delete eventMap[name]; 3970 removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture); 3971 } 3972 } 3973 } 3974 } else { 3975 each$d(eventMap, (callbackList, name) => { 3976 removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture); 3977 }); 3978 eventMap = {}; 3979 } 3980 for (const name in eventMap) { 3981 if (has$2(eventMap, name)) { 3982 return this; 3983 } 3984 } 3985 delete this.events[id]; 3986 try { 3987 delete target[this.expando]; 3988 } catch (ex) { 3989 target[this.expando] = null; 3990 } 3991 } 3992 return this; 3993 } 3994 fire(target, name, args) { 3995 return this.dispatch(target, name, args); 3996 } 3997 dispatch(target, name, args) { 3998 if (!target || isText$a(target) || isComment(target)) { 3999 return this; 4000 } 4001 const event = fix({ 4002 type: name, 4003 target 4004 }, args); 4005 do { 4006 const id = target[this.expando]; 4007 if (id) { 4008 this.executeHandlers(event, id); 4009 } 4010 target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow; 4011 } while (target && !event.isPropagationStopped()); 4012 return this; 4013 } 4014 clean(target) { 4015 if (!target || isText$a(target) || isComment(target)) { 4016 return this; 4017 } 4018 if (target[this.expando]) { 4019 this.unbind(target); 4020 } 4021 if (!target.getElementsByTagName) { 4022 target = target.document; 4023 } 4024 if (target && target.getElementsByTagName) { 4025 this.unbind(target); 4026 const children = target.getElementsByTagName('*'); 4027 let i = children.length; 4028 while (i--) { 4029 target = children[i]; 4030 if (target[this.expando]) { 4031 this.unbind(target); 4032 } 4033 } 4034 } 4035 return this; 4036 } 4037 destroy() { 4038 this.events = {}; 4039 } 4040 cancel(e) { 4041 if (e) { 4042 e.preventDefault(); 4043 e.stopImmediatePropagation(); 4044 } 4045 return false; 4046 } 4047 executeHandlers(evt, id) { 4048 const container = this.events[id]; 4049 const callbackList = container && container[evt.type]; 4050 if (callbackList) { 4051 for (let i = 0, l = callbackList.length; i < l; i++) { 4052 const callback = callbackList[i]; 4053 if (callback && callback.func.call(callback.scope, evt) === false) { 4054 evt.preventDefault(); 4055 } 4056 if (evt.isImmediatePropagationStopped()) { 4057 return; 4058 } 4059 } 4060 } 4061 } 4062 } 4063 EventUtils.Event = new EventUtils(); 4064 4065 const each$a = Tools.each; 4066 const grep = Tools.grep; 4067 const internalStyleName = 'data-mce-style'; 4068 const numericalCssMap = Tools.makeMap('fill-opacity font-weight line-height opacity orphans widows z-index zoom', ' '); 4069 const legacySetAttribute = (elm, name, value) => { 4070 if (isNullable(value) || value === '') { 4071 remove$a(elm, name); 4072 } else { 4073 set$3(elm, name, value); 4074 } 4075 }; 4076 const camelCaseToHyphens = name => name.replace(/[A-Z]/g, v => '-' + v.toLowerCase()); 4077 const findNodeIndex = (node, normalized) => { 4078 let idx = 0; 4079 if (node) { 4080 for (let lastNodeType = node.nodeType, tempNode = node.previousSibling; tempNode; tempNode = tempNode.previousSibling) { 4081 const nodeType = tempNode.nodeType; 4082 if (normalized && isText$a(tempNode)) { 4083 if (nodeType === lastNodeType || !tempNode.data.length) { 4084 continue; 4085 } 4086 } 4087 idx++; 4088 lastNodeType = nodeType; 4089 } 4090 } 4091 return idx; 4092 }; 4093 const updateInternalStyleAttr = (styles, elm) => { 4094 const rawValue = get$9(elm, 'style'); 4095 const value = styles.serialize(styles.parse(rawValue), name(elm)); 4096 legacySetAttribute(elm, internalStyleName, value); 4097 }; 4098 const convertStyleToString = (cssValue, cssName) => { 4099 if (isNumber(cssValue)) { 4100 return has$2(numericalCssMap, cssName) ? cssValue + '' : cssValue + 'px'; 4101 } else { 4102 return cssValue; 4103 } 4104 }; 4105 const applyStyle$1 = ($elm, cssName, cssValue) => { 4106 const normalizedName = camelCaseToHyphens(cssName); 4107 if (isNullable(cssValue) || cssValue === '') { 4108 remove$6($elm, normalizedName); 4109 } else { 4110 set$2($elm, normalizedName, convertStyleToString(cssValue, normalizedName)); 4111 } 4112 }; 4113 const setupAttrHooks = (styles, settings, getContext) => { 4114 const keepValues = settings.keep_values; 4115 const keepUrlHook = { 4116 set: (elm, value, name) => { 4117 const sugarElm = SugarElement.fromDom(elm); 4118 if (isFunction(settings.url_converter) && isNonNullable(value)) { 4119 value = settings.url_converter.call(settings.url_converter_scope || getContext(), String(value), name, elm); 4120 } 4121 const internalName = 'data-mce-' + name; 4122 legacySetAttribute(sugarElm, internalName, value); 4123 legacySetAttribute(sugarElm, name, value); 4124 }, 4125 get: (elm, name) => { 4126 const sugarElm = SugarElement.fromDom(elm); 4127 return get$9(sugarElm, 'data-mce-' + name) || get$9(sugarElm, name); 4128 } 4129 }; 4130 const attrHooks = { 4131 style: { 4132 set: (elm, value) => { 4133 const sugarElm = SugarElement.fromDom(elm); 4134 if (keepValues) { 4135 legacySetAttribute(sugarElm, internalStyleName, value); 4136 } 4137 remove$a(sugarElm, 'style'); 4138 if (isString(value)) { 4139 setAll(sugarElm, styles.parse(value)); 4140 } 4141 }, 4142 get: elm => { 4143 const sugarElm = SugarElement.fromDom(elm); 4144 const value = get$9(sugarElm, internalStyleName) || get$9(sugarElm, 'style'); 4145 return styles.serialize(styles.parse(value), name(sugarElm)); 4146 } 4147 } 4148 }; 4149 if (keepValues) { 4150 attrHooks.href = attrHooks.src = keepUrlHook; 4151 } 4152 return attrHooks; 4153 }; 4154 const DOMUtils = (doc, settings = {}) => { 4155 const addedStyles = {}; 4156 const win = window; 4157 const files = {}; 4158 let counter = 0; 4159 const stdMode = true; 4160 const boxModel = true; 4161 const styleSheetLoader = instance.forElement(SugarElement.fromDom(doc), { 4162 contentCssCors: settings.contentCssCors, 4163 referrerPolicy: settings.referrerPolicy 4164 }); 4165 const boundEvents = []; 4166 const schema = settings.schema ? settings.schema : Schema({}); 4167 const styles = Styles({ 4168 url_converter: settings.url_converter, 4169 url_converter_scope: settings.url_converter_scope 4170 }, settings.schema); 4171 const events = settings.ownEvents ? new EventUtils() : EventUtils.Event; 4172 const blockElementsMap = schema.getBlockElements(); 4173 const isBlock = node => { 4174 if (isString(node)) { 4175 return has$2(blockElementsMap, node); 4176 } else { 4177 return isElement$6(node) && (has$2(blockElementsMap, node.nodeName) || isTransparentBlock(schema, node)); 4178 } 4179 }; 4180 const get = elm => elm && doc && isString(elm) ? doc.getElementById(elm) : elm; 4181 const _get = elm => { 4182 const value = get(elm); 4183 return isNonNullable(value) ? SugarElement.fromDom(value) : null; 4184 }; 4185 const getAttrib = (elm, name, defaultVal = '') => { 4186 let value; 4187 const $elm = _get(elm); 4188 if (isNonNullable($elm) && isElement$7($elm)) { 4189 const hook = attrHooks[name]; 4190 if (hook && hook.get) { 4191 value = hook.get($elm.dom, name); 4192 } else { 4193 value = get$9($elm, name); 4194 } 4195 } 4196 return isNonNullable(value) ? value : defaultVal; 4197 }; 4198 const getAttribs = elm => { 4199 const node = get(elm); 4200 return isNullable(node) ? [] : node.attributes; 4201 }; 4202 const setAttrib = (elm, name, value) => { 4203 run(elm, e => { 4204 if (isElement$6(e)) { 4205 const $elm = SugarElement.fromDom(e); 4206 const val = value === '' ? null : value; 4207 const originalValue = get$9($elm, name); 4208 const hook = attrHooks[name]; 4209 if (hook && hook.set) { 4210 hook.set($elm.dom, val, name); 4211 } else { 4212 legacySetAttribute($elm, name, val); 4213 } 4214 if (originalValue !== val && settings.onSetAttrib) { 4215 settings.onSetAttrib({ 4216 attrElm: $elm.dom, 4217 attrName: name, 4218 attrValue: val 4219 }); 4220 } 4221 } 4222 }); 4223 }; 4224 const clone = (node, deep) => { 4225 return node.cloneNode(deep); 4226 }; 4227 const getRoot = () => settings.root_element || doc.body; 4228 const getViewPort = argWin => { 4229 const vp = getBounds(argWin); 4230 return { 4231 x: vp.x, 4232 y: vp.y, 4233 w: vp.width, 4234 h: vp.height 4235 }; 4236 }; 4237 const getPos$1 = (elm, rootElm) => getPos(doc.body, get(elm), rootElm); 4238 const setStyle = (elm, name, value) => { 4239 run(elm, e => { 4240 const $elm = SugarElement.fromDom(e); 4241 applyStyle$1($elm, name, value); 4242 if (settings.update_styles) { 4243 updateInternalStyleAttr(styles, $elm); 4244 } 4245 }); 4246 }; 4247 const setStyles = (elm, stylesArg) => { 4248 run(elm, e => { 4249 const $elm = SugarElement.fromDom(e); 4250 each$d(stylesArg, (v, n) => { 4251 applyStyle$1($elm, n, v); 4252 }); 4253 if (settings.update_styles) { 4254 updateInternalStyleAttr(styles, $elm); 4255 } 4256 }); 4257 }; 4258 const getStyle = (elm, name, computed) => { 4259 const $elm = get(elm); 4260 if (isNullable($elm) || !isElement$6($elm)) { 4261 return undefined; 4262 } 4263 if (computed) { 4264 return get$7(SugarElement.fromDom($elm), camelCaseToHyphens(name)); 4265 } else { 4266 name = name.replace(/-(\D)/g, (a, b) => b.toUpperCase()); 4267 if (name === 'float') { 4268 name = 'cssFloat'; 4269 } 4270 return $elm.style ? $elm.style[name] : undefined; 4271 } 4272 }; 4273 const getSize = elm => { 4274 const $elm = get(elm); 4275 if (!$elm) { 4276 return { 4277 w: 0, 4278 h: 0 4279 }; 4280 } 4281 let w = getStyle($elm, 'width'); 4282 let h = getStyle($elm, 'height'); 4283 if (!w || w.indexOf('px') === -1) { 4284 w = '0'; 4285 } 4286 if (!h || h.indexOf('px') === -1) { 4287 h = '0'; 4288 } 4289 return { 4290 w: parseInt(w, 10) || $elm.offsetWidth || $elm.clientWidth, 4291 h: parseInt(h, 10) || $elm.offsetHeight || $elm.clientHeight 4292 }; 4293 }; 4294 const getRect = elm => { 4295 const $elm = get(elm); 4296 const pos = getPos$1($elm); 4297 const size = getSize($elm); 4298 return { 4299 x: pos.x, 4300 y: pos.y, 4301 w: size.w, 4302 h: size.h 4303 }; 4304 }; 4305 const is = (elm, selector) => { 4306 if (!elm) { 4307 return false; 4308 } 4309 const elms = isArray$1(elm) ? elm : [elm]; 4310 return exists(elms, e => { 4311 return is$1(SugarElement.fromDom(e), selector); 4312 }); 4313 }; 4314 const getParents = (elm, selector, root, collect) => { 4315 const result = []; 4316 let node = get(elm); 4317 collect = collect === undefined; 4318 const resolvedRoot = root || (getRoot().nodeName !== 'BODY' ? getRoot().parentNode : null); 4319 if (isString(selector)) { 4320 if (selector === '*') { 4321 selector = isElement$6; 4322 } else { 4323 const selectorVal = selector; 4324 selector = node => is(node, selectorVal); 4325 } 4326 } 4327 while (node) { 4328 if (node === resolvedRoot || isNullable(node.nodeType) || isDocument$1(node) || isDocumentFragment(node)) { 4329 break; 4330 } 4331 if (!selector || selector(node)) { 4332 if (collect) { 4333 result.push(node); 4334 } else { 4335 return [node]; 4336 } 4337 } 4338 node = node.parentNode; 4339 } 4340 return collect ? result : null; 4341 }; 4342 const getParent = (node, selector, root) => { 4343 const parents = getParents(node, selector, root, false); 4344 return parents && parents.length > 0 ? parents[0] : null; 4345 }; 4346 const _findSib = (node, selector, name) => { 4347 let func = selector; 4348 if (node) { 4349 if (isString(selector)) { 4350 func = node => { 4351 return is(node, selector); 4352 }; 4353 } 4354 for (let tempNode = node[name]; tempNode; tempNode = tempNode[name]) { 4355 if (isFunction(func) && func(tempNode)) { 4356 return tempNode; 4357 } 4358 } 4359 } 4360 return null; 4361 }; 4362 const getNext = (node, selector) => _findSib(node, selector, 'nextSibling'); 4363 const getPrev = (node, selector) => _findSib(node, selector, 'previousSibling'); 4364 const isParentNode = node => isFunction(node.querySelectorAll); 4365 const select = (selector, scope) => { 4366 var _a, _b; 4367 const elm = (_b = (_a = get(scope)) !== null && _a !== void 0 ? _a : settings.root_element) !== null && _b !== void 0 ? _b : doc; 4368 return isParentNode(elm) ? from(elm.querySelectorAll(selector)) : []; 4369 }; 4370 const run = function (elm, func, scope) { 4371 const context = scope !== null && scope !== void 0 ? scope : this; 4372 if (isArray$1(elm)) { 4373 const result = []; 4374 each$a(elm, (e, i) => { 4375 const node = get(e); 4376 if (node) { 4377 result.push(func.call(context, node, i)); 4378 } 4379 }); 4380 return result; 4381 } else { 4382 const node = get(elm); 4383 return !node ? false : func.call(context, node); 4384 } 4385 }; 4386 const setAttribs = (elm, attrs) => { 4387 run(elm, $elm => { 4388 each$d(attrs, (value, name) => { 4389 setAttrib($elm, name, value); 4390 }); 4391 }); 4392 }; 4393 const setHTML = (elm, html) => { 4394 run(elm, e => { 4395 const $elm = SugarElement.fromDom(e); 4396 set$1($elm, html); 4397 }); 4398 }; 4399 const add = (parentElm, name, attrs, html, create) => run(parentElm, parentElm => { 4400 const newElm = isString(name) ? doc.createElement(name) : name; 4401 if (isNonNullable(attrs)) { 4402 setAttribs(newElm, attrs); 4403 } 4404 if (html) { 4405 if (!isString(html) && html.nodeType) { 4406 newElm.appendChild(html); 4407 } else if (isString(html)) { 4408 setHTML(newElm, html); 4409 } 4410 } 4411 return !create ? parentElm.appendChild(newElm) : newElm; 4412 }); 4413 const create = (name, attrs, html) => add(doc.createElement(name), name, attrs, html, true); 4414 const decode = Entities.decode; 4415 const encode = Entities.encodeAllRaw; 4416 const createHTML = (name, attrs, html = '') => { 4417 let outHtml = '<' + name; 4418 for (const key in attrs) { 4419 if (hasNonNullableKey(attrs, key)) { 4420 outHtml += ' ' + key + '="' + encode(attrs[key]) + '"'; 4421 } 4422 } 4423 if (isEmpty$3(html) && has$2(schema.getVoidElements(), name)) { 4424 return outHtml + ' />'; 4425 } else { 4426 return outHtml + '>' + html + '</' + name + '>'; 4427 } 4428 }; 4429 const createFragment = html => { 4430 const container = doc.createElement('div'); 4431 const frag = doc.createDocumentFragment(); 4432 frag.appendChild(container); 4433 if (html) { 4434 container.innerHTML = html; 4435 } 4436 let node; 4437 while (node = container.firstChild) { 4438 frag.appendChild(node); 4439 } 4440 frag.removeChild(container); 4441 return frag; 4442 }; 4443 const remove = (node, keepChildren) => { 4444 return run(node, n => { 4445 const $node = SugarElement.fromDom(n); 4446 if (keepChildren) { 4447 each$e(children$1($node), child => { 4448 if (isText$b(child) && child.dom.length === 0) { 4449 remove$5(child); 4450 } else { 4451 before$3($node, child); 4452 } 4453 }); 4454 } 4455 remove$5($node); 4456 return $node.dom; 4457 }); 4458 }; 4459 const removeAllAttribs = e => run(e, e => { 4460 const attrs = e.attributes; 4461 for (let i = attrs.length - 1; i >= 0; i--) { 4462 e.removeAttributeNode(attrs.item(i)); 4463 } 4464 }); 4465 const parseStyle = cssText => styles.parse(cssText); 4466 const serializeStyle = (stylesArg, name) => styles.serialize(stylesArg, name); 4467 const addStyle = cssText => { 4468 if (self !== DOMUtils.DOM && doc === document) { 4469 if (addedStyles[cssText]) { 4470 return; 4471 } 4472 addedStyles[cssText] = true; 4473 } 4474 let styleElm = doc.getElementById('mceDefaultStyles'); 4475 if (!styleElm) { 4476 styleElm = doc.createElement('style'); 4477 styleElm.id = 'mceDefaultStyles'; 4478 styleElm.type = 'text/css'; 4479 const head = doc.head; 4480 if (head.firstChild) { 4481 head.insertBefore(styleElm, head.firstChild); 4482 } else { 4483 head.appendChild(styleElm); 4484 } 4485 } 4486 if (styleElm.styleSheet) { 4487 styleElm.styleSheet.cssText += cssText; 4488 } else { 4489 styleElm.appendChild(doc.createTextNode(cssText)); 4490 } 4491 }; 4492 const loadCSS = urls => { 4493 if (!urls) { 4494 urls = ''; 4495 } 4496 each$e(urls.split(','), url => { 4497 files[url] = true; 4498 styleSheetLoader.load(url).catch(noop); 4499 }); 4500 }; 4501 const toggleClass = (elm, cls, state) => { 4502 run(elm, e => { 4503 if (isElement$6(e)) { 4504 const $elm = SugarElement.fromDom(e); 4505 const classes = cls.split(' '); 4506 each$e(classes, c => { 4507 if (isNonNullable(state)) { 4508 const fn = state ? add$2 : remove$7; 4509 fn($elm, c); 4510 } else { 4511 toggle$1($elm, c); 4512 } 4513 }); 4514 } 4515 }); 4516 }; 4517 const addClass = (elm, cls) => { 4518 toggleClass(elm, cls, true); 4519 }; 4520 const removeClass = (elm, cls) => { 4521 toggleClass(elm, cls, false); 4522 }; 4523 const hasClass = (elm, cls) => { 4524 const $elm = _get(elm); 4525 const classes = cls.split(' '); 4526 return isNonNullable($elm) && forall(classes, c => has($elm, c)); 4527 }; 4528 const show = elm => { 4529 run(elm, e => remove$6(SugarElement.fromDom(e), 'display')); 4530 }; 4531 const hide = elm => { 4532 run(elm, e => set$2(SugarElement.fromDom(e), 'display', 'none')); 4533 }; 4534 const isHidden = elm => { 4535 const $elm = _get(elm); 4536 return isNonNullable($elm) && is$2(getRaw($elm, 'display'), 'none'); 4537 }; 4538 const uniqueId = prefix => (!prefix ? 'mce_' : prefix) + counter++; 4539 const getOuterHTML = elm => { 4540 const $elm = _get(elm); 4541 if (isNonNullable($elm)) { 4542 return isElement$6($elm.dom) ? $elm.dom.outerHTML : getOuter($elm); 4543 } else { 4544 return ''; 4545 } 4546 }; 4547 const setOuterHTML = (elm, html) => { 4548 run(elm, $elm => { 4549 if (isElement$6($elm)) { 4550 $elm.outerHTML = html; 4551 } 4552 }); 4553 }; 4554 const insertAfter = (node, reference) => { 4555 const referenceNode = get(reference); 4556 return run(node, node => { 4557 const parent = referenceNode === null || referenceNode === void 0 ? void 0 : referenceNode.parentNode; 4558 const nextSibling = referenceNode === null || referenceNode === void 0 ? void 0 : referenceNode.nextSibling; 4559 if (parent) { 4560 if (nextSibling) { 4561 parent.insertBefore(node, nextSibling); 4562 } else { 4563 parent.appendChild(node); 4564 } 4565 } 4566 return node; 4567 }); 4568 }; 4569 const replace = (newElm, oldElm, keepChildren) => run(oldElm, elm => { 4570 var _a; 4571 const replacee = isArray$1(oldElm) ? newElm.cloneNode(true) : newElm; 4572 if (keepChildren) { 4573 each$a(grep(elm.childNodes), node => { 4574 replacee.appendChild(node); 4575 }); 4576 } 4577 (_a = elm.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(replacee, elm); 4578 return elm; 4579 }); 4580 const rename = (elm, name) => { 4581 if (elm.nodeName !== name.toUpperCase()) { 4582 const newElm = create(name); 4583 each$a(getAttribs(elm), attrNode => { 4584 setAttrib(newElm, attrNode.nodeName, getAttrib(elm, attrNode.nodeName)); 4585 }); 4586 replace(newElm, elm, true); 4587 return newElm; 4588 } else { 4589 return elm; 4590 } 4591 }; 4592 const findCommonAncestor = (a, b) => { 4593 let ps = a; 4594 while (ps) { 4595 let pe = b; 4596 while (pe && ps !== pe) { 4597 pe = pe.parentNode; 4598 } 4599 if (ps === pe) { 4600 break; 4601 } 4602 ps = ps.parentNode; 4603 } 4604 if (!ps && a.ownerDocument) { 4605 return a.ownerDocument.documentElement; 4606 } else { 4607 return ps; 4608 } 4609 }; 4610 const isNonEmptyElement = node => { 4611 if (isElement$6(node)) { 4612 const isNamedAnchor = node.nodeName.toLowerCase() === 'a' && !getAttrib(node, 'href') && getAttrib(node, 'id'); 4613 if (getAttrib(node, 'name') || getAttrib(node, 'data-mce-bookmark') || isNamedAnchor) { 4614 return true; 4615 } 4616 } 4617 return false; 4618 }; 4619 const isEmpty = (node, elements, options) => { 4620 let brCount = 0; 4621 if (isNonEmptyElement(node)) { 4622 return false; 4623 } 4624 const firstChild = node.firstChild; 4625 if (firstChild) { 4626 const walker = new DomTreeWalker(firstChild, node); 4627 const whitespaceElements = schema ? schema.getWhitespaceElements() : {}; 4628 const nonEmptyElements = elements || (schema ? schema.getNonEmptyElements() : null); 4629 let tempNode = firstChild; 4630 do { 4631 if (isElement$6(tempNode)) { 4632 const bogusVal = tempNode.getAttribute('data-mce-bogus'); 4633 if (bogusVal) { 4634 tempNode = walker.next(bogusVal === 'all'); 4635 continue; 4636 } 4637 const name = tempNode.nodeName.toLowerCase(); 4638 if (nonEmptyElements && nonEmptyElements[name]) { 4639 if (name === 'br') { 4640 brCount++; 4641 tempNode = walker.next(); 4642 continue; 4643 } 4644 return false; 4645 } 4646 if (isNonEmptyElement(tempNode)) { 4647 return false; 4648 } 4649 } 4650 if (isComment(tempNode)) { 4651 return false; 4652 } 4653 if (isText$a(tempNode) && !isWhitespaceText(tempNode.data) && (!(options === null || options === void 0 ? void 0 : options.includeZwsp) || !isZwsp(tempNode.data))) { 4654 return false; 4655 } 4656 if (isText$a(tempNode) && tempNode.parentNode && whitespaceElements[tempNode.parentNode.nodeName] && isWhitespaceText(tempNode.data)) { 4657 return false; 4658 } 4659 tempNode = walker.next(); 4660 } while (tempNode); 4661 } 4662 return brCount <= 1; 4663 }; 4664 const createRng = () => doc.createRange(); 4665 const split = (parentElm, splitElm, replacementElm) => { 4666 let range = createRng(); 4667 let beforeFragment; 4668 let afterFragment; 4669 if (parentElm && splitElm && parentElm.parentNode && splitElm.parentNode) { 4670 const parentNode = parentElm.parentNode; 4671 range.setStart(parentNode, findNodeIndex(parentElm)); 4672 range.setEnd(splitElm.parentNode, findNodeIndex(splitElm)); 4673 beforeFragment = range.extractContents(); 4674 range = createRng(); 4675 range.setStart(splitElm.parentNode, findNodeIndex(splitElm) + 1); 4676 range.setEnd(parentNode, findNodeIndex(parentElm) + 1); 4677 afterFragment = range.extractContents(); 4678 parentNode.insertBefore(trimNode(self, beforeFragment), parentElm); 4679 if (replacementElm) { 4680 parentNode.insertBefore(replacementElm, parentElm); 4681 } else { 4682 parentNode.insertBefore(splitElm, parentElm); 4683 } 4684 parentNode.insertBefore(trimNode(self, afterFragment), parentElm); 4685 remove(parentElm); 4686 return replacementElm || splitElm; 4687 } else { 4688 return undefined; 4689 } 4690 }; 4691 const bind = (target, name, func, scope) => { 4692 if (isArray$1(target)) { 4693 let i = target.length; 4694 const rv = []; 4695 while (i--) { 4696 rv[i] = bind(target[i], name, func, scope); 4697 } 4698 return rv; 4699 } else { 4700 if (settings.collect && (target === doc || target === win)) { 4701 boundEvents.push([ 4702 target, 4703 name, 4704 func, 4705 scope 4706 ]); 4707 } 4708 return events.bind(target, name, func, scope || self); 4709 } 4710 }; 4711 const unbind = (target, name, func) => { 4712 if (isArray$1(target)) { 4713 let i = target.length; 4714 const rv = []; 4715 while (i--) { 4716 rv[i] = unbind(target[i], name, func); 4717 } 4718 return rv; 4719 } else { 4720 if (boundEvents.length > 0 && (target === doc || target === win)) { 4721 let i = boundEvents.length; 4722 while (i--) { 4723 const [boundTarget, boundName, boundFunc] = boundEvents[i]; 4724 if (target === boundTarget && (!name || name === boundName) && (!func || func === boundFunc)) { 4725 events.unbind(boundTarget, boundName, boundFunc); 4726 } 4727 } 4728 } 4729 return events.unbind(target, name, func); 4730 } 4731 }; 4732 const dispatch = (target, name, evt) => events.dispatch(target, name, evt); 4733 const fire = (target, name, evt) => events.dispatch(target, name, evt); 4734 const getContentEditable = node => { 4735 if (node && isElement$6(node)) { 4736 const contentEditable = node.getAttribute('data-mce-contenteditable'); 4737 if (contentEditable && contentEditable !== 'inherit') { 4738 return contentEditable; 4739 } 4740 return node.contentEditable !== 'inherit' ? node.contentEditable : null; 4741 } else { 4742 return null; 4743 } 4744 }; 4745 const getContentEditableParent = node => { 4746 const root = getRoot(); 4747 let state = null; 4748 for (let tempNode = node; tempNode && tempNode !== root; tempNode = tempNode.parentNode) { 4749 state = getContentEditable(tempNode); 4750 if (state !== null) { 4751 break; 4752 } 4753 } 4754 return state; 4755 }; 4756 const isEditable = node => { 4757 if (isNonNullable(node)) { 4758 const scope = isElement$6(node) ? node : node.parentElement; 4759 return isNonNullable(scope) && isEditable$3(SugarElement.fromDom(scope)); 4760 } else { 4761 return false; 4762 } 4763 }; 4764 const destroy = () => { 4765 if (boundEvents.length > 0) { 4766 let i = boundEvents.length; 4767 while (i--) { 4768 const [boundTarget, boundName, boundFunc] = boundEvents[i]; 4769 events.unbind(boundTarget, boundName, boundFunc); 4770 } 4771 } 4772 each$d(files, (_, url) => { 4773 styleSheetLoader.unload(url); 4774 delete files[url]; 4775 }); 4776 }; 4777 const isChildOf = (node, parent) => { 4778 return node === parent || parent.contains(node); 4779 }; 4780 const dumpRng = r => 'startContainer: ' + r.startContainer.nodeName + ', startOffset: ' + r.startOffset + ', endContainer: ' + r.endContainer.nodeName + ', endOffset: ' + r.endOffset; 4781 const self = { 4782 doc, 4783 settings, 4784 win, 4785 files, 4786 stdMode, 4787 boxModel, 4788 styleSheetLoader, 4789 boundEvents, 4790 styles, 4791 schema, 4792 events, 4793 isBlock: isBlock, 4794 root: null, 4795 clone, 4796 getRoot, 4797 getViewPort, 4798 getRect, 4799 getSize, 4800 getParent, 4801 getParents: getParents, 4802 get, 4803 getNext, 4804 getPrev, 4805 select, 4806 is, 4807 add, 4808 create, 4809 createHTML, 4810 createFragment, 4811 remove, 4812 setStyle, 4813 getStyle: getStyle, 4814 setStyles, 4815 removeAllAttribs, 4816 setAttrib, 4817 setAttribs, 4818 getAttrib, 4819 getPos: getPos$1, 4820 parseStyle, 4821 serializeStyle, 4822 addStyle, 4823 loadCSS, 4824 addClass, 4825 removeClass, 4826 hasClass, 4827 toggleClass, 4828 show, 4829 hide, 4830 isHidden, 4831 uniqueId, 4832 setHTML, 4833 getOuterHTML, 4834 setOuterHTML, 4835 decode, 4836 encode, 4837 insertAfter, 4838 replace, 4839 rename, 4840 findCommonAncestor, 4841 run, 4842 getAttribs, 4843 isEmpty, 4844 createRng, 4845 nodeIndex: findNodeIndex, 4846 split, 4847 bind: bind, 4848 unbind: unbind, 4849 fire, 4850 dispatch, 4851 getContentEditable, 4852 getContentEditableParent, 4853 isEditable, 4854 destroy, 4855 isChildOf, 4856 dumpRng 4857 }; 4858 const attrHooks = setupAttrHooks(styles, settings, constant(self)); 4859 return self; 4860 }; 4861 DOMUtils.DOM = DOMUtils(document); 4862 DOMUtils.nodeIndex = findNodeIndex; 4863 4864 const DOM$b = DOMUtils.DOM; 4865 const QUEUED = 0; 4866 const LOADING = 1; 4867 const LOADED = 2; 4868 const FAILED = 3; 4869 class ScriptLoader { 4870 constructor(settings = {}) { 4871 this.states = {}; 4872 this.queue = []; 4873 this.scriptLoadedCallbacks = {}; 4874 this.queueLoadedCallbacks = []; 4875 this.loading = false; 4876 this.settings = settings; 4877 } 4878 _setReferrerPolicy(referrerPolicy) { 4879 this.settings.referrerPolicy = referrerPolicy; 4880 } 4881 loadScript(url) { 4882 return new Promise((resolve, reject) => { 4883 const dom = DOM$b; 4884 let elm; 4885 const cleanup = () => { 4886 dom.remove(id); 4887 if (elm) { 4888 elm.onerror = elm.onload = elm = null; 4889 } 4890 }; 4891 const done = () => { 4892 cleanup(); 4893 resolve(); 4894 }; 4895 const error = () => { 4896 cleanup(); 4897 reject('Failed to load script: ' + url); 4898 }; 4899 const id = dom.uniqueId(); 4900 elm = document.createElement('script'); 4901 elm.id = id; 4902 elm.type = 'text/javascript'; 4903 elm.src = Tools._addCacheSuffix(url); 4904 if (this.settings.referrerPolicy) { 4905 dom.setAttrib(elm, 'referrerpolicy', this.settings.referrerPolicy); 4906 } 4907 elm.onload = done; 4908 elm.onerror = error; 4909 (document.getElementsByTagName('head')[0] || document.body).appendChild(elm); 4910 }); 4911 } 4912 isDone(url) { 4913 return this.states[url] === LOADED; 4914 } 4915 markDone(url) { 4916 this.states[url] = LOADED; 4917 } 4918 add(url) { 4919 const self = this; 4920 self.queue.push(url); 4921 const state = self.states[url]; 4922 if (state === undefined) { 4923 self.states[url] = QUEUED; 4924 } 4925 return new Promise((resolve, reject) => { 4926 if (!self.scriptLoadedCallbacks[url]) { 4927 self.scriptLoadedCallbacks[url] = []; 4928 } 4929 self.scriptLoadedCallbacks[url].push({ 4930 resolve, 4931 reject 4932 }); 4933 }); 4934 } 4935 load(url) { 4936 return this.add(url); 4937 } 4938 remove(url) { 4939 delete this.states[url]; 4940 delete this.scriptLoadedCallbacks[url]; 4941 } 4942 loadQueue() { 4943 const queue = this.queue; 4944 this.queue = []; 4945 return this.loadScripts(queue); 4946 } 4947 loadScripts(scripts) { 4948 const self = this; 4949 const execCallbacks = (name, url) => { 4950 get$a(self.scriptLoadedCallbacks, url).each(callbacks => { 4951 each$e(callbacks, callback => callback[name](url)); 4952 }); 4953 delete self.scriptLoadedCallbacks[url]; 4954 }; 4955 const processResults = results => { 4956 const failures = filter$5(results, result => result.status === 'rejected'); 4957 if (failures.length > 0) { 4958 return Promise.reject(bind$3(failures, ({reason}) => isArray$1(reason) ? reason : [reason])); 4959 } else { 4960 return Promise.resolve(); 4961 } 4962 }; 4963 const load = urls => Promise.allSettled(map$3(urls, url => { 4964 if (self.states[url] === LOADED) { 4965 execCallbacks('resolve', url); 4966 return Promise.resolve(); 4967 } else if (self.states[url] === FAILED) { 4968 execCallbacks('reject', url); 4969 return Promise.reject(url); 4970 } else { 4971 self.states[url] = LOADING; 4972 return self.loadScript(url).then(() => { 4973 self.states[url] = LOADED; 4974 execCallbacks('resolve', url); 4975 const queue = self.queue; 4976 if (queue.length > 0) { 4977 self.queue = []; 4978 return load(queue).then(processResults); 4979 } else { 4980 return Promise.resolve(); 4981 } 4982 }, () => { 4983 self.states[url] = FAILED; 4984 execCallbacks('reject', url); 4985 return Promise.reject(url); 4986 }); 4987 } 4988 })); 4989 const processQueue = urls => { 4990 self.loading = true; 4991 return load(urls).then(results => { 4992 self.loading = false; 4993 const nextQueuedItem = self.queueLoadedCallbacks.shift(); 4994 Optional.from(nextQueuedItem).each(call); 4995 return processResults(results); 4996 }); 4997 }; 4998 const uniqueScripts = stringArray(scripts); 4999 if (self.loading) { 5000 return new Promise((resolve, reject) => { 5001 self.queueLoadedCallbacks.push(() => { 5002 processQueue(uniqueScripts).then(resolve, reject); 5003 }); 5004 }); 5005 } else { 5006 return processQueue(uniqueScripts); 5007 } 5008 } 5009 } 5010 ScriptLoader.ScriptLoader = new ScriptLoader(); 5011 5012 const Cell = initial => { 5013 let value = initial; 5014 const get = () => { 5015 return value; 5016 }; 5017 const set = v => { 5018 value = v; 5019 }; 5020 return { 5021 get, 5022 set 5023 }; 5024 }; 5025 5026 const isRaw = str => isObject(str) && has$2(str, 'raw'); 5027 const isTokenised = str => isArray$1(str) && str.length > 1; 5028 const data = {}; 5029 const currentCode = Cell('en'); 5030 const getLanguageData = () => get$a(data, currentCode.get()); 5031 const getData$1 = () => map$2(data, value => ({ ...value })); 5032 const setCode = newCode => { 5033 if (newCode) { 5034 currentCode.set(newCode); 5035 } 5036 }; 5037 const getCode = () => currentCode.get(); 5038 const add$1 = (code, items) => { 5039 let langData = data[code]; 5040 if (!langData) { 5041 data[code] = langData = {}; 5042 } 5043 each$d(items, (translation, name) => { 5044 langData[name.toLowerCase()] = translation; 5045 }); 5046 }; 5047 const translate = text => { 5048 const langData = getLanguageData().getOr({}); 5049 const toString = obj => { 5050 if (isFunction(obj)) { 5051 return Object.prototype.toString.call(obj); 5052 } 5053 return !isEmpty(obj) ? '' + obj : ''; 5054 }; 5055 const isEmpty = text => text === '' || text === null || text === undefined; 5056 const getLangData = text => { 5057 const textstr = toString(text); 5058 return get$a(langData, textstr.toLowerCase()).map(toString).getOr(textstr); 5059 }; 5060 const removeContext = str => str.replace(/{context:\w+}$/, ''); 5061 if (isEmpty(text)) { 5062 return ''; 5063 } 5064 if (isRaw(text)) { 5065 return toString(text.raw); 5066 } 5067 if (isTokenised(text)) { 5068 const values = text.slice(1); 5069 const substitued = getLangData(text[0]).replace(/\{([0-9]+)\}/g, ($1, $2) => has$2(values, $2) ? toString(values[$2]) : $1); 5070 return removeContext(substitued); 5071 } 5072 return removeContext(getLangData(text)); 5073 }; 5074 const isRtl$1 = () => getLanguageData().bind(items => get$a(items, '_dir')).exists(dir => dir === 'rtl'); 5075 const hasCode = code => has$2(data, code); 5076 const I18n = { 5077 getData: getData$1, 5078 setCode, 5079 getCode, 5080 add: add$1, 5081 translate, 5082 isRtl: isRtl$1, 5083 hasCode 5084 }; 5085 5086 const AddOnManager = () => { 5087 const items = []; 5088 const urls = {}; 5089 const lookup = {}; 5090 const _listeners = []; 5091 const runListeners = (name, state) => { 5092 const matchedListeners = filter$5(_listeners, listener => listener.name === name && listener.state === state); 5093 each$e(matchedListeners, listener => listener.resolve()); 5094 }; 5095 const isLoaded = name => has$2(urls, name); 5096 const isAdded = name => has$2(lookup, name); 5097 const get = name => { 5098 if (lookup[name]) { 5099 return lookup[name].instance; 5100 } 5101 return undefined; 5102 }; 5103 const loadLanguagePack = (name, languages) => { 5104 const language = I18n.getCode(); 5105 const wrappedLanguages = ',' + (languages || '') + ','; 5106 if (!language || languages && wrappedLanguages.indexOf(',' + language + ',') === -1) { 5107 return; 5108 } 5109 ScriptLoader.ScriptLoader.add(urls[name] + '/langs/' + language + '.js'); 5110 }; 5111 const requireLangPack = (name, languages) => { 5112 if (AddOnManager.languageLoad !== false) { 5113 if (isLoaded(name)) { 5114 loadLanguagePack(name, languages); 5115 } else { 5116 waitFor(name, 'loaded').then(() => loadLanguagePack(name, languages)); 5117 } 5118 } 5119 }; 5120 const add = (id, addOn) => { 5121 items.push(addOn); 5122 lookup[id] = { instance: addOn }; 5123 runListeners(id, 'added'); 5124 return addOn; 5125 }; 5126 const remove = name => { 5127 delete urls[name]; 5128 delete lookup[name]; 5129 }; 5130 const createUrl = (baseUrl, dep) => { 5131 if (isString(dep)) { 5132 return isString(baseUrl) ? { 5133 prefix: '', 5134 resource: dep, 5135 suffix: '' 5136 } : { 5137 prefix: baseUrl.prefix, 5138 resource: dep, 5139 suffix: baseUrl.suffix 5140 }; 5141 } else { 5142 return dep; 5143 } 5144 }; 5145 const load = (name, addOnUrl) => { 5146 if (urls[name]) { 5147 return Promise.resolve(); 5148 } 5149 let urlString = isString(addOnUrl) ? addOnUrl : addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix; 5150 if (urlString.indexOf('/') !== 0 && urlString.indexOf('://') === -1) { 5151 urlString = AddOnManager.baseURL + '/' + urlString; 5152 } 5153 urls[name] = urlString.substring(0, urlString.lastIndexOf('/')); 5154 const done = () => { 5155 runListeners(name, 'loaded'); 5156 return Promise.resolve(); 5157 }; 5158 if (lookup[name]) { 5159 return done(); 5160 } else { 5161 return ScriptLoader.ScriptLoader.add(urlString).then(done); 5162 } 5163 }; 5164 const waitFor = (name, state = 'added') => { 5165 if (state === 'added' && isAdded(name)) { 5166 return Promise.resolve(); 5167 } else if (state === 'loaded' && isLoaded(name)) { 5168 return Promise.resolve(); 5169 } else { 5170 return new Promise(resolve => { 5171 _listeners.push({ 5172 name, 5173 state, 5174 resolve 5175 }); 5176 }); 5177 } 5178 }; 5179 return { 5180 items, 5181 urls, 5182 lookup, 5183 get, 5184 requireLangPack, 5185 add, 5186 remove, 5187 createUrl, 5188 load, 5189 waitFor 5190 }; 5191 }; 5192 AddOnManager.languageLoad = true; 5193 AddOnManager.baseURL = ''; 5194 AddOnManager.PluginManager = AddOnManager(); 5195 AddOnManager.ThemeManager = AddOnManager(); 5196 AddOnManager.ModelManager = AddOnManager(); 5197 5198 const singleton = doRevoke => { 5199 const subject = Cell(Optional.none()); 5200 const revoke = () => subject.get().each(doRevoke); 5201 const clear = () => { 5202 revoke(); 5203 subject.set(Optional.none()); 5204 }; 5205 const isSet = () => subject.get().isSome(); 5206 const get = () => subject.get(); 5207 const set = s => { 5208 revoke(); 5209 subject.set(Optional.some(s)); 5210 }; 5211 return { 5212 clear, 5213 isSet, 5214 get, 5215 set 5216 }; 5217 }; 5218 const repeatable = delay => { 5219 const intervalId = Cell(Optional.none()); 5220 const revoke = () => intervalId.get().each(id => clearInterval(id)); 5221 const clear = () => { 5222 revoke(); 5223 intervalId.set(Optional.none()); 5224 }; 5225 const isSet = () => intervalId.get().isSome(); 5226 const get = () => intervalId.get(); 5227 const set = functionToRepeat => { 5228 revoke(); 5229 intervalId.set(Optional.some(setInterval(functionToRepeat, delay))); 5230 }; 5231 return { 5232 clear, 5233 isSet, 5234 get, 5235 set 5236 }; 5237 }; 5238 const value$2 = () => { 5239 const subject = singleton(noop); 5240 const on = f => subject.get().each(f); 5241 return { 5242 ...subject, 5243 on 5244 }; 5245 }; 5246 5247 const first$1 = (fn, rate) => { 5248 let timer = null; 5249 const cancel = () => { 5250 if (!isNull(timer)) { 5251 clearTimeout(timer); 5252 timer = null; 5253 } 5254 }; 5255 const throttle = (...args) => { 5256 if (isNull(timer)) { 5257 timer = setTimeout(() => { 5258 timer = null; 5259 fn.apply(null, args); 5260 }, rate); 5261 } 5262 }; 5263 return { 5264 cancel, 5265 throttle 5266 }; 5267 }; 5268 const last$1 = (fn, rate) => { 5269 let timer = null; 5270 const cancel = () => { 5271 if (!isNull(timer)) { 5272 clearTimeout(timer); 5273 timer = null; 5274 } 5275 }; 5276 const throttle = (...args) => { 5277 cancel(); 5278 timer = setTimeout(() => { 5279 timer = null; 5280 fn.apply(null, args); 5281 }, rate); 5282 }; 5283 return { 5284 cancel, 5285 throttle 5286 }; 5287 }; 5288 5289 const annotation = constant('mce-annotation'); 5290 const dataAnnotation = constant('data-mce-annotation'); 5291 const dataAnnotationId = constant('data-mce-annotation-uid'); 5292 const dataAnnotationActive = constant('data-mce-annotation-active'); 5293 const dataAnnotationClasses = constant('data-mce-annotation-classes'); 5294 const dataAnnotationAttributes = constant('data-mce-annotation-attrs'); 5295 5296 const isRoot$1 = root => node => eq(node, root); 5297 const identify = (editor, annotationName) => { 5298 const rng = editor.selection.getRng(); 5299 const start = SugarElement.fromDom(rng.startContainer); 5300 const root = SugarElement.fromDom(editor.getBody()); 5301 const selector = annotationName.fold(() => '.' + annotation(), an => `[${ dataAnnotation() }="${ an }"]`); 5302 const newStart = child$1(start, rng.startOffset).getOr(start); 5303 const closest = closest$3(newStart, selector, isRoot$1(root)); 5304 return closest.bind(c => getOpt(c, `${ dataAnnotationId() }`).bind(uid => getOpt(c, `${ dataAnnotation() }`).map(name => { 5305 const elements = findMarkers(editor, uid); 5306 return { 5307 uid, 5308 name, 5309 elements 5310 }; 5311 }))); 5312 }; 5313 const isAnnotation = elem => isElement$7(elem) && has(elem, annotation()); 5314 const isBogusElement = (elem, root) => has$1(elem, 'data-mce-bogus') || ancestor$2(elem, '[data-mce-bogus="all"]', isRoot$1(root)); 5315 const findMarkers = (editor, uid) => { 5316 const body = SugarElement.fromDom(editor.getBody()); 5317 const descendants$1 = descendants(body, `[${ dataAnnotationId() }="${ uid }"]`); 5318 return filter$5(descendants$1, descendant => !isBogusElement(descendant, body)); 5319 }; 5320 const findAll = (editor, name) => { 5321 const body = SugarElement.fromDom(editor.getBody()); 5322 const markers = descendants(body, `[${ dataAnnotation() }="${ name }"]`); 5323 const directory = {}; 5324 each$e(markers, m => { 5325 if (!isBogusElement(m, body)) { 5326 const uid = get$9(m, dataAnnotationId()); 5327 const nodesAlready = get$a(directory, uid).getOr([]); 5328 directory[uid] = nodesAlready.concat([m]); 5329 } 5330 }); 5331 return directory; 5332 }; 5333 5334 const setup$x = (editor, registry) => { 5335 const changeCallbacks = Cell({}); 5336 const initData = () => ({ 5337 listeners: [], 5338 previous: value$2() 5339 }); 5340 const withCallbacks = (name, f) => { 5341 updateCallbacks(name, data => { 5342 f(data); 5343 return data; 5344 }); 5345 }; 5346 const updateCallbacks = (name, f) => { 5347 const callbackMap = changeCallbacks.get(); 5348 const data = get$a(callbackMap, name).getOrThunk(initData); 5349 const outputData = f(data); 5350 callbackMap[name] = outputData; 5351 changeCallbacks.set(callbackMap); 5352 }; 5353 const fireCallbacks = (name, uid, elements) => { 5354 withCallbacks(name, data => { 5355 each$e(data.listeners, f => f(true, name, { 5356 uid, 5357 nodes: map$3(elements, elem => elem.dom) 5358 })); 5359 }); 5360 }; 5361 const fireNoAnnotation = name => { 5362 withCallbacks(name, data => { 5363 each$e(data.listeners, f => f(false, name)); 5364 }); 5365 }; 5366 const toggleActiveAttr = (uid, state) => { 5367 each$e(findMarkers(editor, uid), elem => { 5368 if (state) { 5369 set$3(elem, dataAnnotationActive(), 'true'); 5370 } else { 5371 remove$a(elem, dataAnnotationActive()); 5372 } 5373 }); 5374 }; 5375 const onNodeChange = last$1(() => { 5376 const annotations = sort(registry.getNames()); 5377 each$e(annotations, name => { 5378 updateCallbacks(name, data => { 5379 const prev = data.previous.get(); 5380 identify(editor, Optional.some(name)).fold(() => { 5381 prev.each(uid => { 5382 fireNoAnnotation(name); 5383 data.previous.clear(); 5384 toggleActiveAttr(uid, false); 5385 }); 5386 }, ({uid, name, elements}) => { 5387 if (!is$2(prev, uid)) { 5388 prev.each(uid => toggleActiveAttr(uid, false)); 5389 fireCallbacks(name, uid, elements); 5390 data.previous.set(uid); 5391 toggleActiveAttr(uid, true); 5392 } 5393 }); 5394 return { 5395 previous: data.previous, 5396 listeners: data.listeners 5397 }; 5398 }); 5399 }); 5400 }, 30); 5401 editor.on('remove', () => { 5402 onNodeChange.cancel(); 5403 }); 5404 editor.on('NodeChange', () => { 5405 onNodeChange.throttle(); 5406 }); 5407 const addListener = (name, f) => { 5408 updateCallbacks(name, data => ({ 5409 previous: data.previous, 5410 listeners: data.listeners.concat([f]) 5411 })); 5412 }; 5413 return { addListener }; 5414 }; 5415 5416 const setup$w = (editor, registry) => { 5417 const dataAnnotation$1 = dataAnnotation(); 5418 const identifyParserNode = node => Optional.from(node.attr(dataAnnotation$1)).bind(registry.lookup); 5419 const removeDirectAnnotation = node => { 5420 var _a, _b; 5421 node.attr(dataAnnotationId(), null); 5422 node.attr(dataAnnotation(), null); 5423 node.attr(dataAnnotationActive(), null); 5424 const customAttrNames = Optional.from(node.attr(dataAnnotationAttributes())).map(names => names.split(',')).getOr([]); 5425 const customClasses = Optional.from(node.attr(dataAnnotationClasses())).map(names => names.split(',')).getOr([]); 5426 each$e(customAttrNames, name => node.attr(name, null)); 5427 const classList = (_b = (_a = node.attr('class')) === null || _a === void 0 ? void 0 : _a.split(' ')) !== null && _b !== void 0 ? _b : []; 5428 const newClassList = difference(classList, [annotation()].concat(customClasses)); 5429 node.attr('class', newClassList.length > 0 ? newClassList.join(' ') : null); 5430 node.attr(dataAnnotationClasses(), null); 5431 node.attr(dataAnnotationAttributes(), null); 5432 }; 5433 editor.serializer.addTempAttr(dataAnnotationActive()); 5434 editor.serializer.addAttributeFilter(dataAnnotation$1, nodes => { 5435 for (const node of nodes) { 5436 identifyParserNode(node).each(settings => { 5437 if (settings.persistent === false) { 5438 if (node.name === 'span') { 5439 node.unwrap(); 5440 } else { 5441 removeDirectAnnotation(node); 5442 } 5443 } 5444 }); 5445 } 5446 }); 5447 }; 5448 5449 const create$c = () => { 5450 const annotations = {}; 5451 const register = (name, settings) => { 5452 annotations[name] = { 5453 name, 5454 settings 5455 }; 5456 }; 5457 const lookup = name => get$a(annotations, name).map(a => a.settings); 5458 const getNames = () => keys(annotations); 5459 return { 5460 register, 5461 lookup, 5462 getNames 5463 }; 5464 }; 5465 5466 let unique = 0; 5467 const generate$1 = prefix => { 5468 const date = new Date(); 5469 const time = date.getTime(); 5470 const random = Math.floor(Math.random() * 1000000000); 5471 unique++; 5472 return prefix + '_' + random + unique + String(time); 5473 }; 5474 5475 const add = (element, classes) => { 5476 each$e(classes, x => { 5477 add$2(element, x); 5478 }); 5479 }; 5480 const remove$4 = (element, classes) => { 5481 each$e(classes, x => { 5482 remove$7(element, x); 5483 }); 5484 }; 5485 5486 const clone$2 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep)); 5487 const shallow$1 = original => clone$2(original, false); 5488 const deep$1 = original => clone$2(original, true); 5489 const shallowAs = (original, tag) => { 5490 const nu = SugarElement.fromTag(tag); 5491 const attributes = clone$4(original); 5492 setAll$1(nu, attributes); 5493 return nu; 5494 }; 5495 const mutate = (original, tag) => { 5496 const nu = shallowAs(original, tag); 5497 after$4(original, nu); 5498 const children = children$1(original); 5499 append(nu, children); 5500 remove$5(original); 5501 return nu; 5502 }; 5503 5504 const TextWalker = (startNode, rootNode, isBoundary = never) => { 5505 const walker = new DomTreeWalker(startNode, rootNode); 5506 const walk = direction => { 5507 let next; 5508 do { 5509 next = walker[direction](); 5510 } while (next && !isText$a(next) && !isBoundary(next)); 5511 return Optional.from(next).filter(isText$a); 5512 }; 5513 return { 5514 current: () => Optional.from(walker.current()).filter(isText$a), 5515 next: () => walk('next'), 5516 prev: () => walk('prev'), 5517 prev2: () => walk('prev2') 5518 }; 5519 }; 5520 5521 const TextSeeker = (dom, isBoundary) => { 5522 const isBlockBoundary = isBoundary ? isBoundary : node => dom.isBlock(node) || isBr$6(node) || isContentEditableFalse$b(node); 5523 const walk = (node, offset, walker, process) => { 5524 if (isText$a(node)) { 5525 const newOffset = process(node, offset, node.data); 5526 if (newOffset !== -1) { 5527 return Optional.some({ 5528 container: node, 5529 offset: newOffset 5530 }); 5531 } 5532 } 5533 return walker().bind(next => walk(next.container, next.offset, walker, process)); 5534 }; 5535 const backwards = (node, offset, process, root) => { 5536 const walker = TextWalker(node, root !== null && root !== void 0 ? root : dom.getRoot(), isBlockBoundary); 5537 return walk(node, offset, () => walker.prev().map(prev => ({ 5538 container: prev, 5539 offset: prev.length 5540 })), process).getOrNull(); 5541 }; 5542 const forwards = (node, offset, process, root) => { 5543 const walker = TextWalker(node, root !== null && root !== void 0 ? root : dom.getRoot(), isBlockBoundary); 5544 return walk(node, offset, () => walker.next().map(next => ({ 5545 container: next, 5546 offset: 0 5547 })), process).getOrNull(); 5548 }; 5549 return { 5550 backwards, 5551 forwards 5552 }; 5553 }; 5554 5555 const round$2 = Math.round; 5556 const clone$1 = rect => { 5557 if (!rect) { 5558 return { 5559 left: 0, 5560 top: 0, 5561 bottom: 0, 5562 right: 0, 5563 width: 0, 5564 height: 0 5565 }; 5566 } 5567 return { 5568 left: round$2(rect.left), 5569 top: round$2(rect.top), 5570 bottom: round$2(rect.bottom), 5571 right: round$2(rect.right), 5572 width: round$2(rect.width), 5573 height: round$2(rect.height) 5574 }; 5575 }; 5576 const collapse = (rect, toStart) => { 5577 rect = clone$1(rect); 5578 if (toStart) { 5579 rect.right = rect.left; 5580 } else { 5581 rect.left = rect.left + rect.width; 5582 rect.right = rect.left; 5583 } 5584 rect.width = 0; 5585 return rect; 5586 }; 5587 const isEqual = (rect1, rect2) => rect1.left === rect2.left && rect1.top === rect2.top && rect1.bottom === rect2.bottom && rect1.right === rect2.right; 5588 const isValidOverflow = (overflowY, rect1, rect2) => overflowY >= 0 && overflowY <= Math.min(rect1.height, rect2.height) / 2; 5589 const isAbove$1 = (rect1, rect2) => { 5590 const halfHeight = Math.min(rect2.height / 2, rect1.height / 2); 5591 if (rect1.bottom - halfHeight < rect2.top) { 5592 return true; 5593 } 5594 if (rect1.top > rect2.bottom) { 5595 return false; 5596 } 5597 return isValidOverflow(rect2.top - rect1.bottom, rect1, rect2); 5598 }; 5599 const isBelow$1 = (rect1, rect2) => { 5600 if (rect1.top > rect2.bottom) { 5601 return true; 5602 } 5603 if (rect1.bottom < rect2.top) { 5604 return false; 5605 } 5606 return isValidOverflow(rect2.bottom - rect1.top, rect1, rect2); 5607 }; 5608 const containsXY = (rect, clientX, clientY) => clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom; 5609 const boundingClientRectFromRects = rects => { 5610 return foldl(rects, (acc, rect) => { 5611 return acc.fold(() => Optional.some(rect), prevRect => { 5612 const left = Math.min(rect.left, prevRect.left); 5613 const top = Math.min(rect.top, prevRect.top); 5614 const right = Math.max(rect.right, prevRect.right); 5615 const bottom = Math.max(rect.bottom, prevRect.bottom); 5616 return Optional.some({ 5617 top, 5618 right, 5619 bottom, 5620 left, 5621 width: right - left, 5622 height: bottom - top 5623 }); 5624 }); 5625 }, Optional.none()); 5626 }; 5627 const distanceToRectEdgeFromXY = (rect, x, y) => { 5628 const cx = Math.max(Math.min(x, rect.left + rect.width), rect.left); 5629 const cy = Math.max(Math.min(y, rect.top + rect.height), rect.top); 5630 return Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)); 5631 }; 5632 const overlapY = (r1, r2) => Math.max(0, Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top)); 5633 5634 const clamp$2 = (value, min, max) => Math.min(Math.max(value, min), max); 5635 5636 const getSelectedNode = range => { 5637 const startContainer = range.startContainer, startOffset = range.startOffset; 5638 if (startContainer === range.endContainer && startContainer.hasChildNodes() && range.endOffset === startOffset + 1) { 5639 return startContainer.childNodes[startOffset]; 5640 } 5641 return null; 5642 }; 5643 const getNode$1 = (container, offset) => { 5644 if (isElement$6(container) && container.hasChildNodes()) { 5645 const childNodes = container.childNodes; 5646 const safeOffset = clamp$2(offset, 0, childNodes.length - 1); 5647 return childNodes[safeOffset]; 5648 } else { 5649 return container; 5650 } 5651 }; 5652 const getNodeUnsafe = (container, offset) => { 5653 if (offset < 0 && isElement$6(container) && container.hasChildNodes()) { 5654 return undefined; 5655 } else { 5656 return getNode$1(container, offset); 5657 } 5658 }; 5659 5660 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]'); 5661 const isExtendingChar = ch => isString(ch) && ch.charCodeAt(0) >= 768 && extendingChars.test(ch); 5662 5663 const or = (...args) => { 5664 return x => { 5665 for (let i = 0; i < args.length; i++) { 5666 if (args[i](x)) { 5667 return true; 5668 } 5669 } 5670 return false; 5671 }; 5672 }; 5673 const and = (...args) => { 5674 return x => { 5675 for (let i = 0; i < args.length; i++) { 5676 if (!args[i](x)) { 5677 return false; 5678 } 5679 } 5680 return true; 5681 }; 5682 }; 5683 5684 const isElement$4 = isElement$6; 5685 const isCaretCandidate$2 = isCaretCandidate$3; 5686 const isBlock$1 = matchStyleValues('display', 'block table'); 5687 const isFloated = matchStyleValues('float', 'left right'); 5688 const isValidElementCaretCandidate = and(isElement$4, isCaretCandidate$2, not(isFloated)); 5689 const isNotPre = not(matchStyleValues('white-space', 'pre pre-line pre-wrap')); 5690 const isText$7 = isText$a; 5691 const isBr$3 = isBr$6; 5692 const nodeIndex$1 = DOMUtils.nodeIndex; 5693 const resolveIndex$1 = getNodeUnsafe; 5694 const createRange$1 = doc => doc ? doc.createRange() : DOMUtils.DOM.createRng(); 5695 const isWhiteSpace$1 = chr => isString(chr) && /[\r\n\t ]/.test(chr); 5696 const isRange = rng => !!rng.setStart && !!rng.setEnd; 5697 const isHiddenWhiteSpaceRange = range => { 5698 const container = range.startContainer; 5699 const offset = range.startOffset; 5700 if (isWhiteSpace$1(range.toString()) && isNotPre(container.parentNode) && isText$a(container)) { 5701 const text = container.data; 5702 if (isWhiteSpace$1(text[offset - 1]) || isWhiteSpace$1(text[offset + 1])) { 5703 return true; 5704 } 5705 } 5706 return false; 5707 }; 5708 const getBrClientRect = brNode => { 5709 const doc = brNode.ownerDocument; 5710 const rng = createRange$1(doc); 5711 const nbsp$1 = doc.createTextNode(nbsp); 5712 const parentNode = brNode.parentNode; 5713 parentNode.insertBefore(nbsp$1, brNode); 5714 rng.setStart(nbsp$1, 0); 5715 rng.setEnd(nbsp$1, 1); 5716 const clientRect = clone$1(rng.getBoundingClientRect()); 5717 parentNode.removeChild(nbsp$1); 5718 return clientRect; 5719 }; 5720 const getBoundingClientRectWebKitText = rng => { 5721 const sc = rng.startContainer; 5722 const ec = rng.endContainer; 5723 const so = rng.startOffset; 5724 const eo = rng.endOffset; 5725 if (sc === ec && isText$a(ec) && so === 0 && eo === 1) { 5726 const newRng = rng.cloneRange(); 5727 newRng.setEndAfter(ec); 5728 return getBoundingClientRect$1(newRng); 5729 } else { 5730 return null; 5731 } 5732 }; 5733 const isZeroRect = r => r.left === 0 && r.right === 0 && r.top === 0 && r.bottom === 0; 5734 const getBoundingClientRect$1 = item => { 5735 var _a; 5736 let clientRect; 5737 const clientRects = item.getClientRects(); 5738 if (clientRects.length > 0) { 5739 clientRect = clone$1(clientRects[0]); 5740 } else { 5741 clientRect = clone$1(item.getBoundingClientRect()); 5742 } 5743 if (!isRange(item) && isBr$3(item) && isZeroRect(clientRect)) { 5744 return getBrClientRect(item); 5745 } 5746 if (isZeroRect(clientRect) && isRange(item)) { 5747 return (_a = getBoundingClientRectWebKitText(item)) !== null && _a !== void 0 ? _a : clientRect; 5748 } 5749 return clientRect; 5750 }; 5751 const collapseAndInflateWidth = (clientRect, toStart) => { 5752 const newClientRect = collapse(clientRect, toStart); 5753 newClientRect.width = 1; 5754 newClientRect.right = newClientRect.left + 1; 5755 return newClientRect; 5756 }; 5757 const getCaretPositionClientRects = caretPosition => { 5758 const clientRects = []; 5759 const addUniqueAndValidRect = clientRect => { 5760 if (clientRect.height === 0) { 5761 return; 5762 } 5763 if (clientRects.length > 0) { 5764 if (isEqual(clientRect, clientRects[clientRects.length - 1])) { 5765 return; 5766 } 5767 } 5768 clientRects.push(clientRect); 5769 }; 5770 const addCharacterOffset = (container, offset) => { 5771 const range = createRange$1(container.ownerDocument); 5772 if (offset < container.data.length) { 5773 if (isExtendingChar(container.data[offset])) { 5774 return; 5775 } 5776 if (isExtendingChar(container.data[offset - 1])) { 5777 range.setStart(container, offset); 5778 range.setEnd(container, offset + 1); 5779 if (!isHiddenWhiteSpaceRange(range)) { 5780 addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), false)); 5781 return; 5782 } 5783 } 5784 } 5785 if (offset > 0) { 5786 range.setStart(container, offset - 1); 5787 range.setEnd(container, offset); 5788 if (!isHiddenWhiteSpaceRange(range)) { 5789 addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), false)); 5790 } 5791 } 5792 if (offset < container.data.length) { 5793 range.setStart(container, offset); 5794 range.setEnd(container, offset + 1); 5795 if (!isHiddenWhiteSpaceRange(range)) { 5796 addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), true)); 5797 } 5798 } 5799 }; 5800 const container = caretPosition.container(); 5801 const offset = caretPosition.offset(); 5802 if (isText$7(container)) { 5803 addCharacterOffset(container, offset); 5804 return clientRects; 5805 } 5806 if (isElement$4(container)) { 5807 if (caretPosition.isAtEnd()) { 5808 const node = resolveIndex$1(container, offset); 5809 if (isText$7(node)) { 5810 addCharacterOffset(node, node.data.length); 5811 } 5812 if (isValidElementCaretCandidate(node) && !isBr$3(node)) { 5813 addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), false)); 5814 } 5815 } else { 5816 const node = resolveIndex$1(container, offset); 5817 if (isText$7(node)) { 5818 addCharacterOffset(node, 0); 5819 } 5820 if (isValidElementCaretCandidate(node) && caretPosition.isAtEnd()) { 5821 addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), false)); 5822 return clientRects; 5823 } 5824 const beforeNode = resolveIndex$1(caretPosition.container(), caretPosition.offset() - 1); 5825 if (isValidElementCaretCandidate(beforeNode) && !isBr$3(beforeNode)) { 5826 if (isBlock$1(beforeNode) || isBlock$1(node) || !isValidElementCaretCandidate(node)) { 5827 addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(beforeNode), false)); 5828 } 5829 } 5830 if (isValidElementCaretCandidate(node)) { 5831 addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), true)); 5832 } 5833 } 5834 } 5835 return clientRects; 5836 }; 5837 const CaretPosition = (container, offset, clientRects) => { 5838 const isAtStart = () => { 5839 if (isText$7(container)) { 5840 return offset === 0; 5841 } 5842 return offset === 0; 5843 }; 5844 const isAtEnd = () => { 5845 if (isText$7(container)) { 5846 return offset >= container.data.length; 5847 } 5848 return offset >= container.childNodes.length; 5849 }; 5850 const toRange = () => { 5851 const range = createRange$1(container.ownerDocument); 5852 range.setStart(container, offset); 5853 range.setEnd(container, offset); 5854 return range; 5855 }; 5856 const getClientRects = () => { 5857 if (!clientRects) { 5858 clientRects = getCaretPositionClientRects(CaretPosition(container, offset)); 5859 } 5860 return clientRects; 5861 }; 5862 const isVisible = () => getClientRects().length > 0; 5863 const isEqual = caretPosition => caretPosition && container === caretPosition.container() && offset === caretPosition.offset(); 5864 const getNode = before => resolveIndex$1(container, before ? offset - 1 : offset); 5865 return { 5866 container: constant(container), 5867 offset: constant(offset), 5868 toRange, 5869 getClientRects, 5870 isVisible, 5871 isAtStart, 5872 isAtEnd, 5873 isEqual, 5874 getNode 5875 }; 5876 }; 5877 CaretPosition.fromRangeStart = range => CaretPosition(range.startContainer, range.startOffset); 5878 CaretPosition.fromRangeEnd = range => CaretPosition(range.endContainer, range.endOffset); 5879 CaretPosition.after = node => CaretPosition(node.parentNode, nodeIndex$1(node) + 1); 5880 CaretPosition.before = node => CaretPosition(node.parentNode, nodeIndex$1(node)); 5881 CaretPosition.isAbove = (pos1, pos2) => lift2(head(pos2.getClientRects()), last$3(pos1.getClientRects()), isAbove$1).getOr(false); 5882 CaretPosition.isBelow = (pos1, pos2) => lift2(last$3(pos2.getClientRects()), head(pos1.getClientRects()), isBelow$1).getOr(false); 5883 CaretPosition.isAtStart = pos => pos ? pos.isAtStart() : false; 5884 CaretPosition.isAtEnd = pos => pos ? pos.isAtEnd() : false; 5885 CaretPosition.isTextPosition = pos => pos ? isText$a(pos.container()) : false; 5886 CaretPosition.isElementPosition = pos => !CaretPosition.isTextPosition(pos); 5887 5888 const trimEmptyTextNode$1 = (dom, node) => { 5889 if (isText$a(node) && node.data.length === 0) { 5890 dom.remove(node); 5891 } 5892 }; 5893 const insertNode = (dom, rng, node) => { 5894 rng.insertNode(node); 5895 trimEmptyTextNode$1(dom, node.previousSibling); 5896 trimEmptyTextNode$1(dom, node.nextSibling); 5897 }; 5898 const insertFragment = (dom, rng, frag) => { 5899 const firstChild = Optional.from(frag.firstChild); 5900 const lastChild = Optional.from(frag.lastChild); 5901 rng.insertNode(frag); 5902 firstChild.each(child => trimEmptyTextNode$1(dom, child.previousSibling)); 5903 lastChild.each(child => trimEmptyTextNode$1(dom, child.nextSibling)); 5904 }; 5905 const rangeInsertNode = (dom, rng, node) => { 5906 if (isDocumentFragment(node)) { 5907 insertFragment(dom, rng, node); 5908 } else { 5909 insertNode(dom, rng, node); 5910 } 5911 }; 5912 5913 const isText$6 = isText$a; 5914 const isBogus = isBogus$2; 5915 const nodeIndex = DOMUtils.nodeIndex; 5916 const normalizedParent = node => { 5917 const parentNode = node.parentNode; 5918 if (isBogus(parentNode)) { 5919 return normalizedParent(parentNode); 5920 } 5921 return parentNode; 5922 }; 5923 const getChildNodes = node => { 5924 if (!node) { 5925 return []; 5926 } 5927 return reduce(node.childNodes, (result, node) => { 5928 if (isBogus(node) && node.nodeName !== 'BR') { 5929 result = result.concat(getChildNodes(node)); 5930 } else { 5931 result.push(node); 5932 } 5933 return result; 5934 }, []); 5935 }; 5936 const normalizedTextOffset = (node, offset) => { 5937 let tempNode = node; 5938 while (tempNode = tempNode.previousSibling) { 5939 if (!isText$6(tempNode)) { 5940 break; 5941 } 5942 offset += tempNode.data.length; 5943 } 5944 return offset; 5945 }; 5946 const equal = a => b => a === b; 5947 const normalizedNodeIndex = node => { 5948 let nodes, index; 5949 nodes = getChildNodes(normalizedParent(node)); 5950 index = findIndex$1(nodes, equal(node), node); 5951 nodes = nodes.slice(0, index + 1); 5952 const numTextFragments = reduce(nodes, (result, node, i) => { 5953 if (isText$6(node) && isText$6(nodes[i - 1])) { 5954 result++; 5955 } 5956 return result; 5957 }, 0); 5958 nodes = filter$3(nodes, matchNodeNames([node.nodeName])); 5959 index = findIndex$1(nodes, equal(node), node); 5960 return index - numTextFragments; 5961 }; 5962 const createPathItem = node => { 5963 const name = isText$6(node) ? 'text()' : node.nodeName.toLowerCase(); 5964 return name + '[' + normalizedNodeIndex(node) + ']'; 5965 }; 5966 const parentsUntil$1 = (root, node, predicate) => { 5967 const parents = []; 5968 for (let tempNode = node.parentNode; tempNode && tempNode !== root; tempNode = tempNode.parentNode) { 5969 if (predicate && predicate(tempNode)) { 5970 break; 5971 } 5972 parents.push(tempNode); 5973 } 5974 return parents; 5975 }; 5976 const create$b = (root, caretPosition) => { 5977 let path = []; 5978 let container = caretPosition.container(); 5979 let offset = caretPosition.offset(); 5980 let outputOffset; 5981 if (isText$6(container)) { 5982 outputOffset = normalizedTextOffset(container, offset); 5983 } else { 5984 const childNodes = container.childNodes; 5985 if (offset >= childNodes.length) { 5986 outputOffset = 'after'; 5987 offset = childNodes.length - 1; 5988 } else { 5989 outputOffset = 'before'; 5990 } 5991 container = childNodes[offset]; 5992 } 5993 path.push(createPathItem(container)); 5994 let parents = parentsUntil$1(root, container); 5995 parents = filter$3(parents, not(isBogus$2)); 5996 path = path.concat(map$1(parents, node => { 5997 return createPathItem(node); 5998 })); 5999 return path.reverse().join('/') + ',' + outputOffset; 6000 }; 6001 const resolvePathItem = (node, name, index) => { 6002 let nodes = getChildNodes(node); 6003 nodes = filter$3(nodes, (node, index) => { 6004 return !isText$6(node) || !isText$6(nodes[index - 1]); 6005 }); 6006 nodes = filter$3(nodes, matchNodeNames([name])); 6007 return nodes[index]; 6008 }; 6009 const findTextPosition = (container, offset) => { 6010 let node = container; 6011 let targetOffset = 0; 6012 while (isText$6(node)) { 6013 const dataLen = node.data.length; 6014 if (offset >= targetOffset && offset <= targetOffset + dataLen) { 6015 container = node; 6016 offset = offset - targetOffset; 6017 break; 6018 } 6019 if (!isText$6(node.nextSibling)) { 6020 container = node; 6021 offset = dataLen; 6022 break; 6023 } 6024 targetOffset += dataLen; 6025 node = node.nextSibling; 6026 } 6027 if (isText$6(container) && offset > container.data.length) { 6028 offset = container.data.length; 6029 } 6030 return CaretPosition(container, offset); 6031 }; 6032 const resolve$1 = (root, path) => { 6033 if (!path) { 6034 return null; 6035 } 6036 const parts = path.split(','); 6037 const paths = parts[0].split('/'); 6038 const offset = parts.length > 1 ? parts[1] : 'before'; 6039 const container = reduce(paths, (result, value) => { 6040 const match = /([\w\-\(\)]+)\[([0-9]+)\]/.exec(value); 6041 if (!match) { 6042 return null; 6043 } 6044 if (match[1] === 'text()') { 6045 match[1] = '#text'; 6046 } 6047 return resolvePathItem(result, match[1], parseInt(match[2], 10)); 6048 }, root); 6049 if (!container) { 6050 return null; 6051 } 6052 if (!isText$6(container) && container.parentNode) { 6053 let nodeOffset; 6054 if (offset === 'after') { 6055 nodeOffset = nodeIndex(container) + 1; 6056 } else { 6057 nodeOffset = nodeIndex(container); 6058 } 6059 return CaretPosition(container.parentNode, nodeOffset); 6060 } 6061 return findTextPosition(container, parseInt(offset, 10)); 6062 }; 6063 6064 const isContentEditableFalse$9 = isContentEditableFalse$b; 6065 const getNormalizedTextOffset$1 = (trim, container, offset) => { 6066 let trimmedOffset = trim(container.data.slice(0, offset)).length; 6067 for (let node = container.previousSibling; node && isText$a(node); node = node.previousSibling) { 6068 trimmedOffset += trim(node.data).length; 6069 } 6070 return trimmedOffset; 6071 }; 6072 const getPoint = (dom, trim, normalized, rng, start) => { 6073 const container = start ? rng.startContainer : rng.endContainer; 6074 let offset = start ? rng.startOffset : rng.endOffset; 6075 const point = []; 6076 const root = dom.getRoot(); 6077 if (isText$a(container)) { 6078 point.push(normalized ? getNormalizedTextOffset$1(trim, container, offset) : offset); 6079 } else { 6080 let after = 0; 6081 const childNodes = container.childNodes; 6082 if (offset >= childNodes.length && childNodes.length) { 6083 after = 1; 6084 offset = Math.max(0, childNodes.length - 1); 6085 } 6086 point.push(dom.nodeIndex(childNodes[offset], normalized) + after); 6087 } 6088 for (let node = container; node && node !== root; node = node.parentNode) { 6089 point.push(dom.nodeIndex(node, normalized)); 6090 } 6091 return point; 6092 }; 6093 const getLocation = (trim, selection, normalized, rng) => { 6094 const dom = selection.dom; 6095 const start = getPoint(dom, trim, normalized, rng, true); 6096 const forward = selection.isForward(); 6097 const fakeCaret = isRangeInCaretContainerBlock(rng) ? { isFakeCaret: true } : {}; 6098 if (!selection.isCollapsed()) { 6099 const end = getPoint(dom, trim, normalized, rng, false); 6100 return { 6101 start, 6102 end, 6103 forward, 6104 ...fakeCaret 6105 }; 6106 } else { 6107 return { 6108 start, 6109 forward, 6110 ...fakeCaret 6111 }; 6112 } 6113 }; 6114 const findIndex = (dom, name, element) => { 6115 let count = 0; 6116 Tools.each(dom.select(name), node => { 6117 if (node.getAttribute('data-mce-bogus') === 'all') { 6118 return; 6119 } else if (node === element) { 6120 return false; 6121 } else { 6122 count++; 6123 return; 6124 } 6125 }); 6126 return count; 6127 }; 6128 const moveEndPoint$1 = (rng, start) => { 6129 let container = start ? rng.startContainer : rng.endContainer; 6130 let offset = start ? rng.startOffset : rng.endOffset; 6131 if (isElement$6(container) && container.nodeName === 'TR') { 6132 const childNodes = container.childNodes; 6133 container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)]; 6134 if (container) { 6135 offset = start ? 0 : container.childNodes.length; 6136 if (start) { 6137 rng.setStart(container, offset); 6138 } else { 6139 rng.setEnd(container, offset); 6140 } 6141 } 6142 } 6143 }; 6144 const normalizeTableCellSelection = rng => { 6145 moveEndPoint$1(rng, true); 6146 moveEndPoint$1(rng, false); 6147 return rng; 6148 }; 6149 const findSibling = (node, offset) => { 6150 if (isElement$6(node)) { 6151 node = getNode$1(node, offset); 6152 if (isContentEditableFalse$9(node)) { 6153 return node; 6154 } 6155 } 6156 if (isCaretContainer$2(node)) { 6157 if (isText$a(node) && isCaretContainerBlock$1(node)) { 6158 node = node.parentNode; 6159 } 6160 let sibling = node.previousSibling; 6161 if (isContentEditableFalse$9(sibling)) { 6162 return sibling; 6163 } 6164 sibling = node.nextSibling; 6165 if (isContentEditableFalse$9(sibling)) { 6166 return sibling; 6167 } 6168 } 6169 return undefined; 6170 }; 6171 const findAdjacentContentEditableFalseElm = rng => { 6172 return findSibling(rng.startContainer, rng.startOffset) || findSibling(rng.endContainer, rng.endOffset); 6173 }; 6174 const getOffsetBookmark = (trim, normalized, selection) => { 6175 const element = selection.getNode(); 6176 const rng = selection.getRng(); 6177 if (element.nodeName === 'IMG' || isContentEditableFalse$9(element)) { 6178 const name = element.nodeName; 6179 return { 6180 name, 6181 index: findIndex(selection.dom, name, element) 6182 }; 6183 } 6184 const sibling = findAdjacentContentEditableFalseElm(rng); 6185 if (sibling) { 6186 const name = sibling.tagName; 6187 return { 6188 name, 6189 index: findIndex(selection.dom, name, sibling) 6190 }; 6191 } 6192 return getLocation(trim, selection, normalized, rng); 6193 }; 6194 const getCaretBookmark = selection => { 6195 const rng = selection.getRng(); 6196 return { 6197 start: create$b(selection.dom.getRoot(), CaretPosition.fromRangeStart(rng)), 6198 end: create$b(selection.dom.getRoot(), CaretPosition.fromRangeEnd(rng)), 6199 forward: selection.isForward() 6200 }; 6201 }; 6202 const getRangeBookmark = selection => { 6203 return { 6204 rng: selection.getRng(), 6205 forward: selection.isForward() 6206 }; 6207 }; 6208 const createBookmarkSpan = (dom, id, filled) => { 6209 const args = { 6210 'data-mce-type': 'bookmark', 6211 id, 6212 'style': 'overflow:hidden;line-height:0px' 6213 }; 6214 return filled ? dom.create('span', args, '') : dom.create('span', args); 6215 }; 6216 const getPersistentBookmark = (selection, filled) => { 6217 const dom = selection.dom; 6218 let rng = selection.getRng(); 6219 const id = dom.uniqueId(); 6220 const collapsed = selection.isCollapsed(); 6221 const element = selection.getNode(); 6222 const name = element.nodeName; 6223 const forward = selection.isForward(); 6224 if (name === 'IMG') { 6225 return { 6226 name, 6227 index: findIndex(dom, name, element) 6228 }; 6229 } 6230 const rng2 = normalizeTableCellSelection(rng.cloneRange()); 6231 if (!collapsed) { 6232 rng2.collapse(false); 6233 const endBookmarkNode = createBookmarkSpan(dom, id + '_end', filled); 6234 rangeInsertNode(dom, rng2, endBookmarkNode); 6235 } 6236 rng = normalizeTableCellSelection(rng); 6237 rng.collapse(true); 6238 const startBookmarkNode = createBookmarkSpan(dom, id + '_start', filled); 6239 rangeInsertNode(dom, rng, startBookmarkNode); 6240 selection.moveToBookmark({ 6241 id, 6242 keep: true, 6243 forward 6244 }); 6245 return { 6246 id, 6247 forward 6248 }; 6249 }; 6250 const getBookmark$2 = (selection, type, normalized = false) => { 6251 if (type === 2) { 6252 return getOffsetBookmark(trim$1, normalized, selection); 6253 } else if (type === 3) { 6254 return getCaretBookmark(selection); 6255 } else if (type) { 6256 return getRangeBookmark(selection); 6257 } else { 6258 return getPersistentBookmark(selection, false); 6259 } 6260 }; 6261 const getUndoBookmark = curry(getOffsetBookmark, identity, true); 6262 6263 const value$1 = value => { 6264 const applyHelper = fn => fn(value); 6265 const constHelper = constant(value); 6266 const outputHelper = () => output; 6267 const output = { 6268 tag: true, 6269 inner: value, 6270 fold: (_onError, onValue) => onValue(value), 6271 isValue: always, 6272 isError: never, 6273 map: mapper => Result.value(mapper(value)), 6274 mapError: outputHelper, 6275 bind: applyHelper, 6276 exists: applyHelper, 6277 forall: applyHelper, 6278 getOr: constHelper, 6279 or: outputHelper, 6280 getOrThunk: constHelper, 6281 orThunk: outputHelper, 6282 getOrDie: constHelper, 6283 each: fn => { 6284 fn(value); 6285 }, 6286 toOptional: () => Optional.some(value) 6287 }; 6288 return output; 6289 }; 6290 const error = error => { 6291 const outputHelper = () => output; 6292 const output = { 6293 tag: false, 6294 inner: error, 6295 fold: (onError, _onValue) => onError(error), 6296 isValue: never, 6297 isError: always, 6298 map: outputHelper, 6299 mapError: mapper => Result.error(mapper(error)), 6300 bind: outputHelper, 6301 exists: never, 6302 forall: always, 6303 getOr: identity, 6304 or: identity, 6305 getOrThunk: apply$1, 6306 orThunk: apply$1, 6307 getOrDie: die(String(error)), 6308 each: noop, 6309 toOptional: Optional.none 6310 }; 6311 return output; 6312 }; 6313 const fromOption = (optional, err) => optional.fold(() => error(err), value$1); 6314 const Result = { 6315 value: value$1, 6316 error, 6317 fromOption 6318 }; 6319 6320 const generate = cases => { 6321 if (!isArray$1(cases)) { 6322 throw new Error('cases must be an array'); 6323 } 6324 if (cases.length === 0) { 6325 throw new Error('there must be at least one case'); 6326 } 6327 const constructors = []; 6328 const adt = {}; 6329 each$e(cases, (acase, count) => { 6330 const keys$1 = keys(acase); 6331 if (keys$1.length !== 1) { 6332 throw new Error('one and only one name per case'); 6333 } 6334 const key = keys$1[0]; 6335 const value = acase[key]; 6336 if (adt[key] !== undefined) { 6337 throw new Error('duplicate key detected:' + key); 6338 } else if (key === 'cata') { 6339 throw new Error('cannot have a case named cata (sorry)'); 6340 } else if (!isArray$1(value)) { 6341 throw new Error('case arguments must be an array'); 6342 } 6343 constructors.push(key); 6344 adt[key] = (...args) => { 6345 const argLength = args.length; 6346 if (argLength !== value.length) { 6347 throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength); 6348 } 6349 const match = branches => { 6350 const branchKeys = keys(branches); 6351 if (constructors.length !== branchKeys.length) { 6352 throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\nActual: ' + branchKeys.join(',')); 6353 } 6354 const allReqd = forall(constructors, reqKey => { 6355 return contains$2(branchKeys, reqKey); 6356 }); 6357 if (!allReqd) { 6358 throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\nRequired: ' + constructors.join(', ')); 6359 } 6360 return branches[key].apply(null, args); 6361 }; 6362 return { 6363 fold: (...foldArgs) => { 6364 if (foldArgs.length !== cases.length) { 6365 throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + foldArgs.length); 6366 } 6367 const target = foldArgs[count]; 6368 return target.apply(null, args); 6369 }, 6370 match, 6371 log: label => { 6372 console.log(label, { 6373 constructors, 6374 constructor: key, 6375 params: args 6376 }); 6377 } 6378 }; 6379 }; 6380 }); 6381 return adt; 6382 }; 6383 const Adt = { generate }; 6384 6385 Adt.generate([ 6386 { 6387 bothErrors: [ 6388 'error1', 6389 'error2' 6390 ] 6391 }, 6392 { 6393 firstError: [ 6394 'error1', 6395 'value2' 6396 ] 6397 }, 6398 { 6399 secondError: [ 6400 'value1', 6401 'error2' 6402 ] 6403 }, 6404 { 6405 bothValues: [ 6406 'value1', 6407 'value2' 6408 ] 6409 } 6410 ]); 6411 const partition$1 = results => { 6412 const errors = []; 6413 const values = []; 6414 each$e(results, result => { 6415 result.fold(err => { 6416 errors.push(err); 6417 }, value => { 6418 values.push(value); 6419 }); 6420 }); 6421 return { 6422 errors, 6423 values 6424 }; 6425 }; 6426 6427 const isInlinePattern = pattern => pattern.type === 'inline-command' || pattern.type === 'inline-format'; 6428 const isBlockPattern = pattern => pattern.type === 'block-command' || pattern.type === 'block-format'; 6429 const normalizePattern = pattern => { 6430 const err = message => Result.error({ 6431 message, 6432 pattern 6433 }); 6434 const formatOrCmd = (name, onFormat, onCommand) => { 6435 if (pattern.format !== undefined) { 6436 let formats; 6437 if (isArray$1(pattern.format)) { 6438 if (!forall(pattern.format, isString)) { 6439 return err(name + ' pattern has non-string items in the `format` array'); 6440 } 6441 formats = pattern.format; 6442 } else if (isString(pattern.format)) { 6443 formats = [pattern.format]; 6444 } else { 6445 return err(name + ' pattern has non-string `format` parameter'); 6446 } 6447 return Result.value(onFormat(formats)); 6448 } else if (pattern.cmd !== undefined) { 6449 if (!isString(pattern.cmd)) { 6450 return err(name + ' pattern has non-string `cmd` parameter'); 6451 } 6452 return Result.value(onCommand(pattern.cmd, pattern.value)); 6453 } else { 6454 return err(name + ' pattern is missing both `format` and `cmd` parameters'); 6455 } 6456 }; 6457 if (!isObject(pattern)) { 6458 return err('Raw pattern is not an object'); 6459 } 6460 if (!isString(pattern.start)) { 6461 return err('Raw pattern is missing `start` parameter'); 6462 } 6463 if (pattern.end !== undefined) { 6464 if (!isString(pattern.end)) { 6465 return err('Inline pattern has non-string `end` parameter'); 6466 } 6467 if (pattern.start.length === 0 && pattern.end.length === 0) { 6468 return err('Inline pattern has empty `start` and `end` parameters'); 6469 } 6470 let start = pattern.start; 6471 let end = pattern.end; 6472 if (end.length === 0) { 6473 end = start; 6474 start = ''; 6475 } 6476 return formatOrCmd('Inline', format => ({ 6477 type: 'inline-format', 6478 start, 6479 end, 6480 format 6481 }), (cmd, value) => ({ 6482 type: 'inline-command', 6483 start, 6484 end, 6485 cmd, 6486 value 6487 })); 6488 } else if (pattern.replacement !== undefined) { 6489 if (!isString(pattern.replacement)) { 6490 return err('Replacement pattern has non-string `replacement` parameter'); 6491 } 6492 if (pattern.start.length === 0) { 6493 return err('Replacement pattern has empty `start` parameter'); 6494 } 6495 return Result.value({ 6496 type: 'inline-command', 6497 start: '', 6498 end: pattern.start, 6499 cmd: 'mceInsertContent', 6500 value: pattern.replacement 6501 }); 6502 } else { 6503 if (pattern.start.length === 0) { 6504 return err('Block pattern has empty `start` parameter'); 6505 } 6506 return formatOrCmd('Block', formats => ({ 6507 type: 'block-format', 6508 start: pattern.start, 6509 format: formats[0] 6510 }), (command, commandValue) => ({ 6511 type: 'block-command', 6512 start: pattern.start, 6513 cmd: command, 6514 value: commandValue 6515 })); 6516 } 6517 }; 6518 const getBlockPatterns = patterns => filter$5(patterns, isBlockPattern); 6519 const getInlinePatterns = patterns => filter$5(patterns, isInlinePattern); 6520 const createPatternSet = (patterns, dynamicPatternsLookup) => ({ 6521 inlinePatterns: getInlinePatterns(patterns), 6522 blockPatterns: getBlockPatterns(patterns), 6523 dynamicPatternsLookup 6524 }); 6525 const fromRawPatterns = patterns => { 6526 const normalized = partition$1(map$3(patterns, normalizePattern)); 6527 each$e(normalized.errors, err => console.error(err.message, err.pattern)); 6528 return normalized.values; 6529 }; 6530 const fromRawPatternsLookup = lookupFn => { 6531 return ctx => { 6532 const rawPatterns = lookupFn(ctx); 6533 return fromRawPatterns(rawPatterns); 6534 }; 6535 }; 6536 6537 const deviceDetection$1 = detect$2().deviceType; 6538 const isTouch = deviceDetection$1.isTouch(); 6539 const DOM$a = DOMUtils.DOM; 6540 const getHash = value => { 6541 const items = value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','); 6542 return foldl(items, (output, item) => { 6543 const arr = item.split('='); 6544 const key = arr[0]; 6545 const val = arr.length > 1 ? arr[1] : key; 6546 output[trim$3(key)] = trim$3(val); 6547 return output; 6548 }, {}); 6549 }; 6550 const isRegExp = x => is$4(x, RegExp); 6551 const option = name => editor => editor.options.get(name); 6552 const stringOrObjectProcessor = value => isString(value) || isObject(value); 6553 const bodyOptionProcessor = (editor, defaultValue = '') => value => { 6554 const valid = isString(value); 6555 if (valid) { 6556 if (value.indexOf('=') !== -1) { 6557 const bodyObj = getHash(value); 6558 return { 6559 value: get$a(bodyObj, editor.id).getOr(defaultValue), 6560 valid 6561 }; 6562 } else { 6563 return { 6564 value, 6565 valid 6566 }; 6567 } 6568 } else { 6569 return { 6570 valid: false, 6571 message: 'Must be a string.' 6572 }; 6573 } 6574 }; 6575 const register$7 = editor => { 6576 const registerOption = editor.options.register; 6577 registerOption('id', { 6578 processor: 'string', 6579 default: editor.id 6580 }); 6581 registerOption('selector', { processor: 'string' }); 6582 registerOption('target', { processor: 'object' }); 6583 registerOption('suffix', { processor: 'string' }); 6584 registerOption('cache_suffix', { processor: 'string' }); 6585 registerOption('base_url', { processor: 'string' }); 6586 registerOption('referrer_policy', { 6587 processor: 'string', 6588 default: '' 6589 }); 6590 registerOption('language_load', { 6591 processor: 'boolean', 6592 default: true 6593 }); 6594 registerOption('inline', { 6595 processor: 'boolean', 6596 default: false 6597 }); 6598 registerOption('iframe_attrs', { 6599 processor: 'object', 6600 default: {} 6601 }); 6602 registerOption('doctype', { 6603 processor: 'string', 6604 default: '<!DOCTYPE html>' 6605 }); 6606 registerOption('document_base_url', { 6607 processor: 'string', 6608 default: editor.documentBaseUrl 6609 }); 6610 registerOption('body_id', { 6611 processor: bodyOptionProcessor(editor, 'tinymce'), 6612 default: 'tinymce' 6613 }); 6614 registerOption('body_class', { 6615 processor: bodyOptionProcessor(editor), 6616 default: '' 6617 }); 6618 registerOption('content_security_policy', { 6619 processor: 'string', 6620 default: '' 6621 }); 6622 registerOption('br_in_pre', { 6623 processor: 'boolean', 6624 default: true 6625 }); 6626 registerOption('forced_root_block', { 6627 processor: value => { 6628 const valid = isString(value) && isNotEmpty(value); 6629 if (valid) { 6630 return { 6631 value, 6632 valid 6633 }; 6634 } else { 6635 return { 6636 valid: false, 6637 message: 'Must be a non-empty string.' 6638 }; 6639 } 6640 }, 6641 default: 'p' 6642 }); 6643 registerOption('forced_root_block_attrs', { 6644 processor: 'object', 6645 default: {} 6646 }); 6647 registerOption('newline_behavior', { 6648 processor: value => { 6649 const valid = contains$2([ 6650 'block', 6651 'linebreak', 6652 'invert', 6653 'default' 6654 ], value); 6655 return valid ? { 6656 value, 6657 valid 6658 } : { 6659 valid: false, 6660 message: 'Must be one of: block, linebreak, invert or default.' 6661 }; 6662 }, 6663 default: 'default' 6664 }); 6665 registerOption('br_newline_selector', { 6666 processor: 'string', 6667 default: '.mce-toc h2,figcaption,caption' 6668 }); 6669 registerOption('no_newline_selector', { 6670 processor: 'string', 6671 default: '' 6672 }); 6673 registerOption('keep_styles', { 6674 processor: 'boolean', 6675 default: true 6676 }); 6677 registerOption('end_container_on_empty_block', { 6678 processor: value => { 6679 if (isBoolean(value)) { 6680 return { 6681 valid: true, 6682 value 6683 }; 6684 } else if (isString(value)) { 6685 return { 6686 valid: true, 6687 value 6688 }; 6689 } else { 6690 return { 6691 valid: false, 6692 message: 'Must be boolean or a string' 6693 }; 6694 } 6695 }, 6696 default: 'blockquote' 6697 }); 6698 registerOption('font_size_style_values', { 6699 processor: 'string', 6700 default: 'xx-small,x-small,small,medium,large,x-large,xx-large' 6701 }); 6702 registerOption('font_size_legacy_values', { 6703 processor: 'string', 6704 default: 'xx-small,small,medium,large,x-large,xx-large,300%' 6705 }); 6706 registerOption('font_size_classes', { 6707 processor: 'string', 6708 default: '' 6709 }); 6710 registerOption('automatic_uploads', { 6711 processor: 'boolean', 6712 default: true 6713 }); 6714 registerOption('images_reuse_filename', { 6715 processor: 'boolean', 6716 default: false 6717 }); 6718 registerOption('images_replace_blob_uris', { 6719 processor: 'boolean', 6720 default: true 6721 }); 6722 registerOption('icons', { 6723 processor: 'string', 6724 default: '' 6725 }); 6726 registerOption('icons_url', { 6727 processor: 'string', 6728 default: '' 6729 }); 6730 registerOption('images_upload_url', { 6731 processor: 'string', 6732 default: '' 6733 }); 6734 registerOption('images_upload_base_path', { 6735 processor: 'string', 6736 default: '' 6737 }); 6738 registerOption('images_upload_credentials', { 6739 processor: 'boolean', 6740 default: false 6741 }); 6742 registerOption('images_upload_handler', { processor: 'function' }); 6743 registerOption('language', { 6744 processor: 'string', 6745 default: 'en' 6746 }); 6747 registerOption('language_url', { 6748 processor: 'string', 6749 default: '' 6750 }); 6751 registerOption('entity_encoding', { 6752 processor: 'string', 6753 default: 'named' 6754 }); 6755 registerOption('indent', { 6756 processor: 'boolean', 6757 default: true 6758 }); 6759 registerOption('indent_before', { 6760 processor: 'string', 6761 default: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + 'tfoot,tbody,tr,section,details,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist' 6762 }); 6763 registerOption('indent_after', { 6764 processor: 'string', 6765 default: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + 'tfoot,tbody,tr,section,details,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist' 6766 }); 6767 registerOption('indent_use_margin', { 6768 processor: 'boolean', 6769 default: false 6770 }); 6771 registerOption('indentation', { 6772 processor: 'string', 6773 default: '40px' 6774 }); 6775 registerOption('content_css', { 6776 processor: value => { 6777 const valid = value === false || isString(value) || isArrayOf(value, isString); 6778 if (valid) { 6779 if (isString(value)) { 6780 return { 6781 value: map$3(value.split(','), trim$3), 6782 valid 6783 }; 6784 } else if (isArray$1(value)) { 6785 return { 6786 value, 6787 valid 6788 }; 6789 } else if (value === false) { 6790 return { 6791 value: [], 6792 valid 6793 }; 6794 } else { 6795 return { 6796 value, 6797 valid 6798 }; 6799 } 6800 } else { 6801 return { 6802 valid: false, 6803 message: 'Must be false, a string or an array of strings.' 6804 }; 6805 } 6806 }, 6807 default: isInline(editor) ? [] : ['default'] 6808 }); 6809 registerOption('content_style', { processor: 'string' }); 6810 registerOption('content_css_cors', { 6811 processor: 'boolean', 6812 default: false 6813 }); 6814 registerOption('font_css', { 6815 processor: value => { 6816 const valid = isString(value) || isArrayOf(value, isString); 6817 if (valid) { 6818 const newValue = isArray$1(value) ? value : map$3(value.split(','), trim$3); 6819 return { 6820 value: newValue, 6821 valid 6822 }; 6823 } else { 6824 return { 6825 valid: false, 6826 message: 'Must be a string or an array of strings.' 6827 }; 6828 } 6829 }, 6830 default: [] 6831 }); 6832 registerOption('inline_boundaries', { 6833 processor: 'boolean', 6834 default: true 6835 }); 6836 registerOption('inline_boundaries_selector', { 6837 processor: 'string', 6838 default: 'a[href],code,span.mce-annotation' 6839 }); 6840 registerOption('object_resizing', { 6841 processor: value => { 6842 const valid = isBoolean(value) || isString(value); 6843 if (valid) { 6844 if (value === false || deviceDetection$1.isiPhone() || deviceDetection$1.isiPad()) { 6845 return { 6846 value: '', 6847 valid 6848 }; 6849 } else { 6850 return { 6851 value: value === true ? 'table,img,figure.image,div,video,iframe' : value, 6852 valid 6853 }; 6854 } 6855 } else { 6856 return { 6857 valid: false, 6858 message: 'Must be boolean or a string' 6859 }; 6860 } 6861 }, 6862 default: !isTouch 6863 }); 6864 registerOption('resize_img_proportional', { 6865 processor: 'boolean', 6866 default: true 6867 }); 6868 registerOption('event_root', { processor: 'object' }); 6869 registerOption('service_message', { processor: 'string' }); 6870 registerOption('theme', { 6871 processor: value => value === false || isString(value) || isFunction(value), 6872 default: 'silver' 6873 }); 6874 registerOption('theme_url', { processor: 'string' }); 6875 registerOption('formats', { processor: 'object' }); 6876 registerOption('format_empty_lines', { 6877 processor: 'boolean', 6878 default: false 6879 }); 6880 registerOption('format_noneditable_selector', { 6881 processor: 'string', 6882 default: '' 6883 }); 6884 registerOption('preview_styles', { 6885 processor: value => { 6886 const valid = value === false || isString(value); 6887 if (valid) { 6888 return { 6889 value: value === false ? '' : value, 6890 valid 6891 }; 6892 } else { 6893 return { 6894 valid: false, 6895 message: 'Must be false or a string' 6896 }; 6897 } 6898 }, 6899 default: 'font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow' 6900 }); 6901 registerOption('custom_ui_selector', { 6902 processor: 'string', 6903 default: '' 6904 }); 6905 registerOption('hidden_input', { 6906 processor: 'boolean', 6907 default: true 6908 }); 6909 registerOption('submit_patch', { 6910 processor: 'boolean', 6911 default: true 6912 }); 6913 registerOption('encoding', { processor: 'string' }); 6914 registerOption('add_form_submit_trigger', { 6915 processor: 'boolean', 6916 default: true 6917 }); 6918 registerOption('add_unload_trigger', { 6919 processor: 'boolean', 6920 default: true 6921 }); 6922 registerOption('custom_undo_redo_levels', { 6923 processor: 'number', 6924 default: 0 6925 }); 6926 registerOption('disable_nodechange', { 6927 processor: 'boolean', 6928 default: false 6929 }); 6930 registerOption('readonly', { 6931 processor: 'boolean', 6932 default: false 6933 }); 6934 registerOption('editable_root', { 6935 processor: 'boolean', 6936 default: true 6937 }); 6938 registerOption('plugins', { 6939 processor: 'string[]', 6940 default: [] 6941 }); 6942 registerOption('external_plugins', { processor: 'object' }); 6943 registerOption('forced_plugins', { processor: 'string[]' }); 6944 registerOption('model', { 6945 processor: 'string', 6946 default: editor.hasPlugin('rtc') ? 'plugin' : 'dom' 6947 }); 6948 registerOption('model_url', { processor: 'string' }); 6949 registerOption('block_unsupported_drop', { 6950 processor: 'boolean', 6951 default: true 6952 }); 6953 registerOption('visual', { 6954 processor: 'boolean', 6955 default: true 6956 }); 6957 registerOption('visual_table_class', { 6958 processor: 'string', 6959 default: 'mce-item-table' 6960 }); 6961 registerOption('visual_anchor_class', { 6962 processor: 'string', 6963 default: 'mce-item-anchor' 6964 }); 6965 registerOption('iframe_aria_text', { 6966 processor: 'string', 6967 default: 'Rich Text Area. Press ALT-0 for help.' 6968 }); 6969 registerOption('setup', { processor: 'function' }); 6970 registerOption('init_instance_callback', { processor: 'function' }); 6971 registerOption('url_converter', { 6972 processor: 'function', 6973 default: editor.convertURL 6974 }); 6975 registerOption('url_converter_scope', { 6976 processor: 'object', 6977 default: editor 6978 }); 6979 registerOption('urlconverter_callback', { processor: 'function' }); 6980 registerOption('allow_conditional_comments', { 6981 processor: 'boolean', 6982 default: false 6983 }); 6984 registerOption('allow_html_data_urls', { 6985 processor: 'boolean', 6986 default: false 6987 }); 6988 registerOption('allow_svg_data_urls', { processor: 'boolean' }); 6989 registerOption('allow_html_in_named_anchor', { 6990 processor: 'boolean', 6991 default: false 6992 }); 6993 registerOption('allow_script_urls', { 6994 processor: 'boolean', 6995 default: false 6996 }); 6997 registerOption('allow_unsafe_link_target', { 6998 processor: 'boolean', 6999 default: false 7000 }); 7001 registerOption('convert_fonts_to_spans', { 7002 processor: 'boolean', 7003 default: true, 7004 deprecated: true 7005 }); 7006 registerOption('fix_list_elements', { 7007 processor: 'boolean', 7008 default: false 7009 }); 7010 registerOption('preserve_cdata', { 7011 processor: 'boolean', 7012 default: false 7013 }); 7014 registerOption('remove_trailing_brs', { 7015 processor: 'boolean', 7016 default: true 7017 }); 7018 registerOption('
7018pad_empty_with_br', { 7019 processor: 'boolean', 7020 default: fals
vendor: 209,358 bytes, lines 7020-12343
7020e 7021 }); 7022 registerOption('inline_styles', { 7023 processor: 'boolean', 7024 default: true, 7025 deprecated: true 7026 }); 7027 registerOption('element_format', { 7028 processor: 'string', 7029 default: 'html' 7030 }); 7031 registerOption('entities', { processor: 'string' }); 7032 registerOption('schema', { 7033 processor: 'string', 7034 default: 'html5' 7035 }); 7036 registerOption('convert_urls', { 7037 processor: 'boolean', 7038 default: true 7039 }); 7040 registerOption('relative_urls', { 7041 processor: 'boolean', 7042 default: true 7043 }); 7044 registerOption('remove_script_host', { 7045 processor: 'boolean', 7046 default: true 7047 }); 7048 registerOption('custom_elements', { processor: 'string' }); 7049 registerOption('extended_valid_elements', { processor: 'string' }); 7050 registerOption('invalid_elements', { processor: 'string' }); 7051 registerOption('invalid_styles', { processor: stringOrObjectProcessor }); 7052 registerOption('valid_children', { processor: 'string' }); 7053 registerOption('valid_classes', { processor: stringOrObjectProcessor }); 7054 registerOption('valid_elements', { processor: 'string' }); 7055 registerOption('valid_styles', { processor: stringOrObjectProcessor }); 7056 registerOption('verify_html', { 7057 processor: 'boolean', 7058 default: true 7059 }); 7060 registerOption('auto_focus', { processor: value => isString(value) || value === true }); 7061 registerOption('browser_spellcheck', { 7062 processor: 'boolean', 7063 default: false 7064 }); 7065 registerOption('protect', { processor: 'array' }); 7066 registerOption('images_file_types', { 7067 processor: 'string', 7068 default: 'jpeg,jpg,jpe,jfi,jif,jfif,png,gif,bmp,webp' 7069 }); 7070 registerOption('deprecation_warnings', { 7071 processor: 'boolean', 7072 default: true 7073 }); 7074 registerOption('a11y_advanced_options', { 7075 processor: 'boolean', 7076 default: false 7077 }); 7078 registerOption('api_key', { processor: 'string' }); 7079 registerOption('paste_block_drop', { 7080 processor: 'boolean', 7081 default: false 7082 }); 7083 registerOption('paste_data_images', { 7084 processor: 'boolean', 7085 default: true 7086 }); 7087 registerOption('paste_preprocess', { processor: 'function' }); 7088 registerOption('paste_postprocess', { processor: 'function' }); 7089 registerOption('paste_webkit_styles', { 7090 processor: 'string', 7091 default: 'none' 7092 }); 7093 registerOption('paste_remove_styles_if_webkit', { 7094 processor: 'boolean', 7095 default: true 7096 }); 7097 registerOption('paste_merge_formats', { 7098 processor: 'boolean', 7099 default: true 7100 }); 7101 registerOption('smart_paste', { 7102 processor: 'boolean', 7103 default: true 7104 }); 7105 registerOption('paste_as_text', { 7106 processor: 'boolean', 7107 default: false 7108 }); 7109 registerOption('paste_tab_spaces', { 7110 processor: 'number', 7111 default: 4 7112 }); 7113 registerOption('text_patterns', { 7114 processor: value => { 7115 if (isArrayOf(value, isObject) || value === false) { 7116 const patterns = value === false ? [] : value; 7117 return { 7118 value: fromRawPatterns(patterns), 7119 valid: true 7120 }; 7121 } else { 7122 return { 7123 valid: false, 7124 message: 'Must be an array of objects or false.' 7125 }; 7126 } 7127 }, 7128 default: [ 7129 { 7130 start: '*', 7131 end: '*', 7132 format: 'italic' 7133 }, 7134 { 7135 start: '**', 7136 end: '**', 7137 format: 'bold' 7138 }, 7139 { 7140 start: '#', 7141 format: 'h1' 7142 }, 7143 { 7144 start: '##', 7145 format: 'h2' 7146 }, 7147 { 7148 start: '###', 7149 format: 'h3' 7150 }, 7151 { 7152 start: '####', 7153 format: 'h4' 7154 }, 7155 { 7156 start: '#####', 7157 format: 'h5' 7158 }, 7159 { 7160 start: '######', 7161 format: 'h6' 7162 }, 7163 { 7164 start: '1. ', 7165 cmd: 'InsertOrderedList' 7166 }, 7167 { 7168 start: '* ', 7169 cmd: 'InsertUnorderedList' 7170 }, 7171 { 7172 start: '- ', 7173 cmd: 'InsertUnorderedList' 7174 } 7175 ] 7176 }); 7177 registerOption('text_patterns_lookup', { 7178 processor: value => { 7179 if (isFunction(value)) { 7180 return { 7181 value: fromRawPatternsLookup(value), 7182 valid: true 7183 }; 7184 } else { 7185 return { 7186 valid: false, 7187 message: 'Must be a single function' 7188 }; 7189 } 7190 }, 7191 default: _ctx => [] 7192 }); 7193 registerOption('noneditable_class', { 7194 processor: 'string', 7195 default: 'mceNonEditable' 7196 }); 7197 registerOption('editable_class', { 7198 processor: 'string', 7199 default: 'mceEditable' 7200 }); 7201 registerOption('noneditable_regexp', { 7202 processor: value => { 7203 if (isArrayOf(value, isRegExp)) { 7204 return { 7205 value, 7206 valid: true 7207 }; 7208 } else if (isRegExp(value)) { 7209 return { 7210 value: [value], 7211 valid: true 7212 }; 7213 } else { 7214 return { 7215 valid: false, 7216 message: 'Must be a RegExp or an array of RegExp.' 7217 }; 7218 } 7219 }, 7220 default: [] 7221 }); 7222 registerOption('table_tab_navigation', { 7223 processor: 'boolean', 7224 default: true 7225 }); 7226 registerOption('highlight_on_focus', { 7227 processor: 'boolean', 7228 default: false 7229 }); 7230 registerOption('xss_sanitization', { 7231 processor: 'boolean', 7232 default: true 7233 }); 7234 registerOption('details_initial_state', { 7235 processor: value => { 7236 const valid = contains$2([ 7237 'inherited', 7238 'collapsed', 7239 'expanded' 7240 ], value); 7241 return valid ? { 7242 value, 7243 valid 7244 } : { 7245 valid: false, 7246 message: 'Must be one of: inherited, collapsed, or expanded.' 7247 }; 7248 }, 7249 default: 'inherited' 7250 }); 7251 registerOption('details_serialized_state', { 7252 processor: value => { 7253 const valid = contains$2([ 7254 'inherited', 7255 'collapsed', 7256 'expanded' 7257 ], value); 7258 return valid ? { 7259 value, 7260 valid 7261 } : { 7262 valid: false, 7263 message: 'Must be one of: inherited, collapsed, or expanded.' 7264 }; 7265 }, 7266 default: 'inherited' 7267 }); 7268 registerOption('init_content_sync', { 7269 processor: 'boolean', 7270 default: false 7271 }); 7272 registerOption('newdocument_content', { 7273 processor: 'string', 7274 default: '' 7275 }); 7276 editor.on('ScriptsLoaded', () => { 7277 registerOption('directionality', { 7278 processor: 'string', 7279 default: I18n.isRtl() ? 'rtl' : undefined 7280 }); 7281 registerOption('placeholder', { 7282 processor: 'string', 7283 default: DOM$a.getAttrib(editor.getElement(), 'placeholder') 7284 }); 7285 }); 7286 }; 7287 const getIframeAttrs = option('iframe_attrs'); 7288 const getDocType = option('doctype'); 7289 const getDocumentBaseUrl = option('document_base_url'); 7290 const getBodyId = option('body_id'); 7291 const getBodyClass = option('body_class'); 7292 const getContentSecurityPolicy = option('content_security_policy'); 7293 const shouldPutBrInPre$1 = option('br_in_pre'); 7294 const getForcedRootBlock = option('forced_root_block'); 7295 const getForcedRootBlockAttrs = option('forced_root_block_attrs'); 7296 const getNewlineBehavior = option('newline_behavior'); 7297 const getBrNewLineSelector = option('br_newline_selector'); 7298 const getNoNewLineSelector = option('no_newline_selector'); 7299 const shouldKeepStyles = option('keep_styles'); 7300 const shouldEndContainerOnEmptyBlock = option('end_container_on_empty_block'); 7301 const isAutomaticUploadsEnabled = option('automatic_uploads'); 7302 const shouldReuseFileName = option('images_reuse_filename'); 7303 const shouldReplaceBlobUris = option('images_replace_blob_uris'); 7304 const getIconPackName = option('icons'); 7305 const getIconsUrl = option('icons_url'); 7306 const getImageUploadUrl = option('images_upload_url'); 7307 const getImageUploadBasePath = option('images_upload_base_path'); 7308 const getImagesUploadCredentials = option('images_upload_credentials'); 7309 const getImagesUploadHandler = option('images_upload_handler'); 7310 const shouldUseContentCssCors = option('content_css_cors'); 7311 const getReferrerPolicy = option('referrer_policy'); 7312 const getLanguageCode = option('language'); 7313 const getLanguageUrl = option('language_url'); 7314 const shouldIndentUseMargin = option('indent_use_margin'); 7315 const getIndentation = option('indentation'); 7316 const getContentCss = option('content_css'); 7317 const getContentStyle = option('content_style'); 7318 const getFontCss = option('font_css'); 7319 const getDirectionality = option('directionality'); 7320 const getInlineBoundarySelector = option('inline_boundaries_selector'); 7321 const getObjectResizing = option('object_resizing'); 7322 const getResizeImgProportional = option('resize_img_proportional'); 7323 const getPlaceholder = option('placeholder'); 7324 const getEventRoot = option('event_root'); 7325 const getServiceMessage = option('service_message'); 7326 const getTheme = option('theme'); 7327 const getThemeUrl = option('theme_url'); 7328 const getModel = option('model'); 7329 const getModelUrl = option('model_url'); 7330 const isInlineBoundariesEnabled = option('inline_boundaries'); 7331 const getFormats = option('formats'); 7332 const getPreviewStyles = option('preview_styles'); 7333 const canFormatEmptyLines = option('format_empty_lines'); 7334 const getFormatNoneditableSelector = option('format_noneditable_selector'); 7335 const getCustomUiSelector = option('custom_ui_selector'); 7336 const isInline = option('inline'); 7337 const hasHiddenInput = option('hidden_input'); 7338 const shouldPatchSubmit = option('submit_patch'); 7339 const shouldAddFormSubmitTrigger = option('add_form_submit_trigger'); 7340 const shouldAddUnloadTrigger = option('add_unload_trigger'); 7341 const getCustomUndoRedoLevels = option('custom_undo_redo_levels'); 7342 const shouldDisableNodeChange = option('disable_nodechange'); 7343 const isReadOnly$1 = option('readonly'); 7344 const hasEditableRoot$1 = option('editable_root'); 7345 const hasContentCssCors = option('content_css_cors'); 7346 const getPlugins = option('plugins'); 7347 const getExternalPlugins$1 = option('external_plugins'); 7348 const shouldBlockUnsupportedDrop = option('block_unsupported_drop'); 7349 const isVisualAidsEnabled = option('visual'); 7350 const getVisualAidsTableClass = option('visual_table_class'); 7351 const getVisualAidsAnchorClass = option('visual_anchor_class'); 7352 const getIframeAriaText = option('iframe_aria_text'); 7353 const getSetupCallback = option('setup'); 7354 const getInitInstanceCallback = option('init_instance_callback'); 7355 const getUrlConverterCallback = option('urlconverter_callback'); 7356 const getAutoFocus = option('auto_focus'); 7357 const shouldBrowserSpellcheck = option('browser_spellcheck'); 7358 const getProtect = option('protect'); 7359 const shouldPasteBlockDrop = option('paste_block_drop'); 7360 const shouldPasteDataImages = option('paste_data_images'); 7361 const getPastePreProcess = option('paste_preprocess'); 7362 const getPastePostProcess = option('paste_postprocess'); 7363 const getNewDocumentContent = option('newdocument_content'); 7364 const getPasteWebkitStyles = option('paste_webkit_styles'); 7365 const shouldPasteRemoveWebKitStyles = option('paste_remove_styles_if_webkit'); 7366 const shouldPasteMergeFormats = option('paste_merge_formats'); 7367 const isSmartPasteEnabled = option('smart_paste'); 7368 const isPasteAsTextEnabled = option('paste_as_text'); 7369 const getPasteTabSpaces = option('paste_tab_spaces'); 7370 const shouldAllowHtmlDataUrls = option('allow_html_data_urls'); 7371 const getTextPatterns = option('text_patterns'); 7372 const getTextPatternsLookup = option('text_patterns_lookup'); 7373 const getNonEditableClass = option('noneditable_class'); 7374 const getEditableClass = option('editable_class'); 7375 const getNonEditableRegExps = option('noneditable_regexp'); 7376 const shouldPreserveCData = option('preserve_cdata'); 7377 const shouldHighlightOnFocus = option('highlight_on_focus'); 7378 const shouldSanitizeXss = option('xss_sanitization'); 7379 const shouldUseDocumentWrite = option('init_content_sync'); 7380 const hasTextPatternsLookup = editor => editor.options.isSet('text_patterns_lookup'); 7381 const getFontStyleValues = editor => Tools.explode(editor.options.get('font_size_style_values')); 7382 const getFontSizeClasses = editor => Tools.explode(editor.options.get('font_size_classes')); 7383 const isEncodingXml = editor => editor.options.get('encoding') === 'xml'; 7384 const getAllowedImageFileTypes = editor => Tools.explode(editor.options.get('images_file_types')); 7385 const hasTableTabNavigation = option('table_tab_navigation'); 7386 const getDetailsInitialState = option('details_initial_state'); 7387 const getDetailsSerializedState = option('details_serialized_state'); 7388 7389 const isElement$3 = isElement$6; 7390 const isText$5 = isText$a; 7391 const removeNode$1 = node => { 7392 const parentNode = node.parentNode; 7393 if (parentNode) { 7394 parentNode.removeChild(node); 7395 } 7396 }; 7397 const trimCount = text => { 7398 const trimmedText = trim$1(text); 7399 return { 7400 count: text.length - trimmedText.length, 7401 text: trimmedText 7402 }; 7403 }; 7404 const deleteZwspChars = caretContainer => { 7405 let idx; 7406 while ((idx = caretContainer.data.lastIndexOf(ZWSP$1)) !== -1) { 7407 caretContainer.deleteData(idx, 1); 7408 } 7409 }; 7410 const removeUnchanged = (caretContainer, pos) => { 7411 remove$3(caretContainer); 7412 return pos; 7413 }; 7414 const removeTextAndReposition = (caretContainer, pos) => { 7415 const before = trimCount(caretContainer.data.substr(0, pos.offset())); 7416 const after = trimCount(caretContainer.data.substr(pos.offset())); 7417 const text = before.text + after.text; 7418 if (text.length > 0) { 7419 deleteZwspChars(caretContainer); 7420 return CaretPosition(caretContainer, pos.offset() - before.count); 7421 } else { 7422 return pos; 7423 } 7424 }; 7425 const removeElementAndReposition = (caretContainer, pos) => { 7426 const parentNode = pos.container(); 7427 const newPosition = indexOf$1(from(parentNode.childNodes), caretContainer).map(index => { 7428 return index < pos.offset() ? CaretPosition(parentNode, pos.offset() - 1) : pos; 7429 }).getOr(pos); 7430 remove$3(caretContainer); 7431 return newPosition; 7432 }; 7433 const removeTextCaretContainer = (caretContainer, pos) => isText$5(caretContainer) && pos.container() === caretContainer ? removeTextAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos); 7434 const removeElementCaretContainer = (caretContainer, pos) => pos.container() === caretContainer.parentNode ? removeElementAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos); 7435 const removeAndReposition = (container, pos) => CaretPosition.isTextPosition(pos) ? removeTextCaretContainer(container, pos) : removeElementCaretContainer(container, pos); 7436 const remove$3 = caretContainerNode => { 7437 if (isElement$3(caretContainerNode) && isCaretContainer$2(caretContainerNode)) { 7438 if (hasContent(caretContainerNode)) { 7439 caretContainerNode.removeAttribute('data-mce-caret'); 7440 } else { 7441 removeNode$1(caretContainerNode); 7442 } 7443 } 7444 if (isText$5(caretContainerNode)) { 7445 deleteZwspChars(caretContainerNode); 7446 if (caretContainerNode.data.length === 0) { 7447 removeNode$1(caretContainerNode); 7448 } 7449 } 7450 }; 7451 7452 const isContentEditableFalse$8 = isContentEditableFalse$b; 7453 const isMedia$1 = isMedia$2; 7454 const isTableCell$1 = isTableCell$3; 7455 const inlineFakeCaretSelector = '*[contentEditable=false],video,audio,embed,object'; 7456 const getAbsoluteClientRect = (root, element, before) => { 7457 const clientRect = collapse(element.getBoundingClientRect(), before); 7458 let scrollX; 7459 let scrollY; 7460 if (root.tagName === 'BODY') { 7461 const docElm = root.ownerDocument.documentElement; 7462 scrollX = root.scrollLeft || docElm.scrollLeft; 7463 scrollY = root.scrollTop || docElm.scrollTop; 7464 } else { 7465 const rootRect = root.getBoundingClientRect(); 7466 scrollX = root.scrollLeft - rootRect.left; 7467 scrollY = root.scrollTop - rootRect.top; 7468 } 7469 clientRect.left += scrollX; 7470 clientRect.right += scrollX; 7471 clientRect.top += scrollY; 7472 clientRect.bottom += scrollY; 7473 clientRect.width = 1; 7474 let margin = element.offsetWidth - element.clientWidth; 7475 if (margin > 0) { 7476 if (before) { 7477 margin *= -1; 7478 } 7479 clientRect.left += margin; 7480 clientRect.right += margin; 7481 } 7482 return clientRect; 7483 }; 7484 const trimInlineCaretContainers = root => { 7485 var _a, _b; 7486 const fakeCaretTargetNodes = descendants(SugarElement.fromDom(root), inlineFakeCaretSelector); 7487 for (let i = 0; i < fakeCaretTargetNodes.length; i++) { 7488 const node = fakeCaretTargetNodes[i].dom; 7489 let sibling = node.previousSibling; 7490 if (endsWithCaretContainer$1(sibling)) { 7491 const data = sibling.data; 7492 if (data.length === 1) { 7493 (_a = sibling.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(sibling); 7494 } else { 7495 sibling.deleteData(data.length - 1, 1); 7496 } 7497 } 7498 sibling = node.nextSibling; 7499 if (startsWithCaretContainer$1(sibling)) { 7500 const data = sibling.data; 7501 if (data.length === 1) { 7502 (_b = sibling.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(sibling); 7503 } else { 7504 sibling.deleteData(0, 1); 7505 } 7506 } 7507 } 7508 }; 7509 const FakeCaret = (editor, root, isBlock, hasFocus) => { 7510 const lastVisualCaret = value$2(); 7511 let cursorInterval; 7512 let caretContainerNode; 7513 const caretBlock = getForcedRootBlock(editor); 7514 const dom = editor.dom; 7515 const show = (before, element) => { 7516 let rng; 7517 hide(); 7518 if (isTableCell$1(element)) { 7519 return null; 7520 } 7521 if (isBlock(element)) { 7522 const caretContainer = insertBlock(caretBlock, element, before); 7523 const clientRect = getAbsoluteClientRect(root, element, before); 7524 dom.setStyle(caretContainer, 'top', clientRect.top); 7525 caretContainerNode = caretContainer; 7526 const caret = dom.create('div', { 7527 'class': 'mce-visual-caret', 7528 'data-mce-bogus': 'all' 7529 }); 7530 dom.setStyles(caret, { ...clientRect }); 7531 dom.add(root, caret); 7532 lastVisualCaret.set({ 7533 caret, 7534 element, 7535 before 7536 }); 7537 if (before) { 7538 dom.addClass(caret, 'mce-visual-caret-before'); 7539 } 7540 startBlink(); 7541 rng = element.ownerDocument.createRange(); 7542 rng.setStart(caretContainer, 0); 7543 rng.setEnd(caretContainer, 0); 7544 } else { 7545 caretContainerNode = insertInline$1(element, before); 7546 rng = element.ownerDocument.createRange(); 7547 if (isInlineFakeCaretTarget(caretContainerNode.nextSibling)) { 7548 rng.setStart(caretContainerNode, 0); 7549 rng.setEnd(caretContainerNode, 0); 7550 } else { 7551 rng.setStart(caretContainerNode, 1); 7552 rng.setEnd(caretContainerNode, 1); 7553 } 7554 return rng; 7555 } 7556 return rng; 7557 }; 7558 const hide = () => { 7559 trimInlineCaretContainers(root); 7560 if (caretContainerNode) { 7561 remove$3(caretContainerNode); 7562 caretContainerNode = null; 7563 } 7564 lastVisualCaret.on(caretState => { 7565 dom.remove(caretState.caret); 7566 lastVisualCaret.clear(); 7567 }); 7568 if (cursorInterval) { 7569 clearInterval(cursorInterval); 7570 cursorInterval = undefined; 7571 } 7572 }; 7573 const startBlink = () => { 7574 cursorInterval = setInterval(() => { 7575 lastVisualCaret.on(caretState => { 7576 if (hasFocus()) { 7577 dom.toggleClass(caretState.caret, 'mce-visual-caret-hidden'); 7578 } else { 7579 dom.addClass(caretState.caret, 'mce-visual-caret-hidden'); 7580 } 7581 }); 7582 }, 500); 7583 }; 7584 const reposition = () => { 7585 lastVisualCaret.on(caretState => { 7586 const clientRect = getAbsoluteClientRect(root, caretState.element, caretState.before); 7587 dom.setStyles(caretState.caret, { ...clientRect }); 7588 }); 7589 }; 7590 const destroy = () => clearInterval(cursorInterval); 7591 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;' + '}'; 7592 return { 7593 show, 7594 hide, 7595 getCss, 7596 reposition, 7597 destroy 7598 }; 7599 }; 7600 const isFakeCaretTableBrowser = () => Env.browser.isFirefox(); 7601 const isInlineFakeCaretTarget = node => isContentEditableFalse$8(node) || isMedia$1(node); 7602 const isFakeCaretTarget = node => { 7603 const isTarget = isInlineFakeCaretTarget(node) || isTable$2(node) && isFakeCaretTableBrowser(); 7604 return isTarget && parentElement(SugarElement.fromDom(node)).exists(isEditable$3); 7605 }; 7606 7607 const isContentEditableTrue$1 = isContentEditableTrue$3; 7608 const isContentEditableFalse$7 = isContentEditableFalse$b; 7609 const isMedia = isMedia$2; 7610 const isBlockLike = matchStyleValues('display', 'block table table-cell table-caption list-item'); 7611 const isCaretContainer = isCaretContainer$2; 7612 const isCaretContainerBlock = isCaretContainerBlock$1; 7613 const isElement$2 = isElement$6; 7614 const isText$4 = isText$a; 7615 const isCaretCandidate$1 = isCaretCandidate$3; 7616 const isForwards = direction => direction > 0; 7617 const isBackwards = direction => direction < 0; 7618 const skipCaretContainers = (walk, shallow) => { 7619 let node; 7620 while (node = walk(shallow)) { 7621 if (!isCaretContainerBlock(node)) { 7622 return node; 7623 } 7624 } 7625 return null; 7626 }; 7627 const findNode = (node, direction, predicateFn, rootNode, shallow) => { 7628 const walker = new DomTreeWalker(node, rootNode); 7629 const isCefOrCaretContainer = isContentEditableFalse$7(node) || isCaretContainerBlock(node); 7630 let tempNode; 7631 if (isBackwards(direction)) { 7632 if (isCefOrCaretContainer) { 7633 tempNode = skipCaretContainers(walker.prev.bind(walker), true); 7634 if (predicateFn(tempNode)) { 7635 return tempNode; 7636 } 7637 } 7638 while (tempNode = skipCaretContainers(walker.prev.bind(walker), shallow)) { 7639 if (predicateFn(tempNode)) { 7640 return tempNode; 7641 } 7642 } 7643 } 7644 if (isForwards(direction)) { 7645 if (isCefOrCaretContainer) { 7646 tempNode = skipCaretContainers(walker.next.bind(walker), true); 7647 if (predicateFn(tempNode)) { 7648 return tempNode; 7649 } 7650 } 7651 while (tempNode = skipCaretContainers(walker.next.bind(walker), shallow)) { 7652 if (predicateFn(tempNode)) { 7653 return tempNode; 7654 } 7655 } 7656 } 7657 return null; 7658 }; 7659 const getEditingHost = (node, rootNode) => { 7660 const isCETrue = node => isContentEditableTrue$1(node.dom); 7661 const isRoot = node => node.dom === rootNode; 7662 return ancestor$4(SugarElement.fromDom(node), isCETrue, isRoot).map(elm => elm.dom).getOr(rootNode); 7663 }; 7664 const getParentBlock$3 = (node, rootNode) => { 7665 while (node && node !== rootNode) { 7666 if (isBlockLike(node)) { 7667 return node; 7668 } 7669 node = node.parentNode; 7670 } 7671 return null; 7672 }; 7673 const isInSameBlock = (caretPosition1, caretPosition2, rootNode) => getParentBlock$3(caretPosition1.container(), rootNode) === getParentBlock$3(caretPosition2.container(), rootNode); 7674 const getChildNodeAtRelativeOffset = (relativeOffset, caretPosition) => { 7675 if (!caretPosition) { 7676 return Optional.none(); 7677 } 7678 const container = caretPosition.container(); 7679 const offset = caretPosition.offset(); 7680 if (!isElement$2(container)) { 7681 return Optional.none(); 7682 } 7683 return Optional.from(container.childNodes[offset + relativeOffset]); 7684 }; 7685 const beforeAfter = (before, node) => { 7686 var _a; 7687 const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document; 7688 const range = doc.createRange(); 7689 if (before) { 7690 range.setStartBefore(node); 7691 range.setEndBefore(node); 7692 } else { 7693 range.setStartAfter(node); 7694 range.setEndAfter(node); 7695 } 7696 return range; 7697 }; 7698 const isNodesInSameBlock = (root, node1, node2) => getParentBlock$3(node1, root) === getParentBlock$3(node2, root); 7699 const lean = (left, root, node) => { 7700 const siblingName = left ? 'previousSibling' : 'nextSibling'; 7701 let tempNode = node; 7702 while (tempNode && tempNode !== root) { 7703 let sibling = tempNode[siblingName]; 7704 if (sibling && isCaretContainer(sibling)) { 7705 sibling = sibling[siblingName]; 7706 } 7707 if (isContentEditableFalse$7(sibling) || isMedia(sibling)) { 7708 if (isNodesInSameBlock(root, sibling, tempNode)) { 7709 return sibling; 7710 } 7711 break; 7712 } 7713 if (isCaretCandidate$1(sibling)) { 7714 break; 7715 } 7716 tempNode = tempNode.parentNode; 7717 } 7718 return null; 7719 }; 7720 const before$2 = curry(beforeAfter, true); 7721 const after$2 = curry(beforeAfter, false); 7722 const normalizeRange = (direction, root, range) => { 7723 let node; 7724 const leanLeft = curry(lean, true, root); 7725 const leanRight = curry(lean, false, root); 7726 const container = range.startContainer; 7727 const offset = range.startOffset; 7728 if (isCaretContainerBlock$1(container)) { 7729 const block = isText$4(container) ? container.parentNode : container; 7730 const location = block.getAttribute('data-mce-caret'); 7731 if (location === 'before') { 7732 node = block.nextSibling; 7733 if (isFakeCaretTarget(node)) { 7734 return before$2(node); 7735 } 7736 } 7737 if (location === 'after') { 7738 node = block.previousSibling; 7739 if (isFakeCaretTarget(node)) { 7740 return after$2(node); 7741 } 7742 } 7743 } 7744 if (!range.collapsed) { 7745 return range; 7746 } 7747 if (isText$a(container)) { 7748 if (isCaretContainer(container)) { 7749 if (direction === 1) { 7750 node = leanRight(container); 7751 if (node) { 7752 return before$2(node); 7753 } 7754 node = leanLeft(container); 7755 if (node) { 7756 return after$2(node); 7757 } 7758 } 7759 if (direction === -1) { 7760 node = leanLeft(container); 7761 if (node) { 7762 return after$2(node); 7763 } 7764 node = leanRight(container); 7765 if (node) { 7766 return before$2(node); 7767 } 7768 } 7769 return range; 7770 } 7771 if (endsWithCaretContainer$1(container) && offset >= container.data.length - 1) { 7772 if (direction === 1) { 7773 node = leanRight(container); 7774 if (node) { 7775 return before$2(node); 7776 } 7777 } 7778 return range; 7779 } 7780 if (startsWithCaretContainer$1(container) && offset <= 1) { 7781 if (direction === -1) { 7782 node = leanLeft(container); 7783 if (node) { 7784 return after$2(node); 7785 } 7786 } 7787 return range; 7788 } 7789 if (offset === container.data.length) { 7790 node = leanRight(container); 7791 if (node) { 7792 return before$2(node); 7793 } 7794 return range; 7795 } 7796 if (offset === 0) { 7797 node = leanLeft(container); 7798 if (node) { 7799 return after$2(node); 7800 } 7801 return range; 7802 } 7803 } 7804 return range; 7805 }; 7806 const getRelativeCefElm = (forward, caretPosition) => getChildNodeAtRelativeOffset(forward ? 0 : -1, caretPosition).filter(isContentEditableFalse$7); 7807 const getNormalizedRangeEndPoint = (direction, root, range) => { 7808 const normalizedRange = normalizeRange(direction, root, range); 7809 return direction === -1 ? CaretPosition.fromRangeStart(normalizedRange) : CaretPosition.fromRangeEnd(normalizedRange); 7810 }; 7811 const getElementFromPosition = pos => Optional.from(pos.getNode()).map(SugarElement.fromDom); 7812 const getElementFromPrevPosition = pos => Optional.from(pos.getNode(true)).map(SugarElement.fromDom); 7813 const getVisualCaretPosition = (walkFn, caretPosition) => { 7814 let pos = caretPosition; 7815 while (pos = walkFn(pos)) { 7816 if (pos.isVisible()) { 7817 return pos; 7818 } 7819 } 7820 return pos; 7821 }; 7822 const isMoveInsideSameBlock = (from, to) => { 7823 const inSameBlock = isInSameBlock(from, to); 7824 if (!inSameBlock && isBr$6(from.getNode())) { 7825 return true; 7826 } 7827 return inSameBlock; 7828 }; 7829 7830 var HDirection; 7831 (function (HDirection) { 7832 HDirection[HDirection['Backwards'] = -1] = 'Backwards'; 7833 HDirection[HDirection['Forwards'] = 1] = 'Forwards'; 7834 }(HDirection || (HDirection = {}))); 7835 const isContentEditableFalse$6 = isContentEditableFalse$b; 7836 const isText$3 = isText$a; 7837 const isElement$1 = isElement$6; 7838 const isBr$2 = isBr$6; 7839 const isCaretCandidate = isCaretCandidate$3; 7840 const isAtomic = isAtomic$1; 7841 const isEditableCaretCandidate = isEditableCaretCandidate$1; 7842 const getParents$3 = (node, root) => { 7843 const parents = []; 7844 let tempNode = node; 7845 while (tempNode && tempNode !== root) { 7846 parents.push(tempNode); 7847 tempNode = tempNode.parentNode; 7848 } 7849 return parents; 7850 }; 7851 const nodeAtIndex = (container, offset) => { 7852 if (container.hasChildNodes() && offset < container.childNodes.length) { 7853 return container.childNodes[offset]; 7854 } 7855 return null; 7856 }; 7857 const getCaretCandidatePosition = (direction, node) => { 7858 if (isForwards(direction)) { 7859 if (isCaretCandidate(node.previousSibling) && !isText$3(node.previousSibling)) { 7860 return CaretPosition.before(node); 7861 } 7862 if (isText$3(node)) { 7863 return CaretPosition(node, 0); 7864 } 7865 } 7866 if (isBackwards(direction)) { 7867 if (isCaretCandidate(node.nextSibling) && !isText$3(node.nextSibling)) { 7868 return CaretPosition.after(node); 7869 } 7870 if (isText$3(node)) { 7871 return CaretPosition(node, node.data.length); 7872 } 7873 } 7874 if (isBackwards(direction)) { 7875 if (isBr$2(node)) { 7876 return CaretPosition.before(node); 7877 } 7878 return CaretPosition.after(node); 7879 } 7880 return CaretPosition.before(node); 7881 }; 7882 const moveForwardFromBr = (root, nextNode) => { 7883 const nextSibling = nextNode.nextSibling; 7884 if (nextSibling && isCaretCandidate(nextSibling)) { 7885 if (isText$3(nextSibling)) { 7886 return CaretPosition(nextSibling, 0); 7887 } else { 7888 return CaretPosition.before(nextSibling); 7889 } 7890 } else { 7891 return findCaretPosition$1(HDirection.Forwards, CaretPosition.after(nextNode), root); 7892 } 7893 }; 7894 const findCaretPosition$1 = (direction, startPos, root) => { 7895 let node; 7896 let nextNode; 7897 let innerNode; 7898 let caretPosition; 7899 if (!isElement$1(root) || !startPos) { 7900 return null; 7901 } 7902 if (startPos.isEqual(CaretPosition.after(root)) && root.lastChild) { 7903 caretPosition = CaretPosition.after(root.lastChild); 7904 if (isBackwards(direction) && isCaretCandidate(root.lastChild) && isElement$1(root.lastChild)) { 7905 return isBr$2(root.lastChild) ? CaretPosition.before(root.lastChild) : caretPosition; 7906 } 7907 } else { 7908 caretPosition = startPos; 7909 } 7910 const container = caretPosition.container(); 7911 let offset = caretPosition.offset(); 7912 if (isText$3(container)) { 7913 if (isBackwards(direction) && offset > 0) { 7914 return CaretPosition(container, --offset); 7915 } 7916 if (isForwards(direction) && offset < container.length) { 7917 return CaretPosition(container, ++offset); 7918 } 7919 node = container; 7920 } else { 7921 if (isBackwards(direction) && offset > 0) { 7922 nextNode = nodeAtIndex(container, offset - 1); 7923 if (isCaretCandidate(nextNode)) { 7924 if (!isAtomic(nextNode)) { 7925 innerNode = findNode(nextNode, direction, isEditableCaretCandidate, nextNode); 7926 if (innerNode) { 7927 if (isText$3(innerNode)) { 7928 return CaretPosition(innerNode, innerNode.data.length); 7929 } 7930 return CaretPosition.after(innerNode); 7931 } 7932 } 7933 if (isText$3(nextNode)) { 7934 return CaretPosition(nextNode, nextNode.data.length); 7935 } 7936 return CaretPosition.before(nextNode); 7937 } 7938 } 7939 if (isForwards(direction) && offset < container.childNodes.length) { 7940 nextNode = nodeAtIndex(container, offset); 7941 if (isCaretCandidate(nextNode)) { 7942 if (isBr$2(nextNode)) { 7943 return moveForwardFromBr(root, nextNode); 7944 } 7945 if (!isAtomic(nextNode)) { 7946 innerNode = findNode(nextNode, direction, isEditableCaretCandidate, nextNode); 7947 if (innerNode) { 7948 if (isText$3(innerNode)) { 7949 return CaretPosition(innerNode, 0); 7950 } 7951 return CaretPosition.before(innerNode); 7952 } 7953 } 7954 if (isText$3(nextNode)) { 7955 return CaretPosition(nextNode, 0); 7956 } 7957 return CaretPosition.after(nextNode); 7958 } 7959 } 7960 node = nextNode ? nextNode : caretPosition.getNode(); 7961 } 7962 if (node && (isForwards(direction) && caretPosition.isAtEnd() || isBackwards(direction) && caretPosition.isAtStart())) { 7963 node = findNode(node, direction, always, root, true); 7964 if (isEditableCaretCandidate(node, root)) { 7965 return getCaretCandidatePosition(direction, node); 7966 } 7967 } 7968 nextNode = node ? findNode(node, direction, isEditableCaretCandidate, root) : node; 7969 const rootContentEditableFalseElm = last$2(filter$5(getParents$3(container, root), isContentEditableFalse$6)); 7970 if (rootContentEditableFalseElm && (!nextNode || !rootContentEditableFalseElm.contains(nextNode))) { 7971 if (isForwards(direction)) { 7972 caretPosition = CaretPosition.after(rootContentEditableFalseElm); 7973 } else { 7974 caretPosition = CaretPosition.before(rootContentEditableFalseElm); 7975 } 7976 return caretPosition; 7977 } 7978 if (nextNode) { 7979 return getCaretCandidatePosition(direction, nextNode); 7980 } 7981 return null; 7982 }; 7983 const CaretWalker = root => ({ 7984 next: caretPosition => { 7985 return findCaretPosition$1(HDirection.Forwards, caretPosition, root); 7986 }, 7987 prev: caretPosition => { 7988 return findCaretPosition$1(HDirection.Backwards, caretPosition, root); 7989 } 7990 }); 7991 7992 const walkToPositionIn = (forward, root, start) => { 7993 const position = forward ? CaretPosition.before(start) : CaretPosition.after(start); 7994 return fromPosition(forward, root, position); 7995 }; 7996 const afterElement = node => isBr$6(node) ? CaretPosition.before(node) : CaretPosition.after(node); 7997 const isBeforeOrStart = position => { 7998 if (CaretPosition.isTextPosition(position)) { 7999 return position.offset() === 0; 8000 } else { 8001 return isCaretCandidate$3(position.getNode()); 8002 } 8003 }; 8004 const isAfterOrEnd = position => { 8005 if (CaretPosition.isTextPosition(position)) { 8006 const container = position.container(); 8007 return position.offset() === container.data.length; 8008 } else { 8009 return isCaretCandidate$3(position.getNode(true)); 8010 } 8011 }; 8012 const isBeforeAfterSameElement = (from, to) => !CaretPosition.isTextPosition(from) && !CaretPosition.isTextPosition(to) && from.getNode() === to.getNode(true); 8013 const isAtBr = position => !CaretPosition.isTextPosition(position) && isBr$6(position.getNode()); 8014 const shouldSkipPosition = (forward, from, to) => { 8015 if (forward) { 8016 return !isBeforeAfterSameElement(from, to) && !isAtBr(from) && isAfterOrEnd(from) && isBeforeOrStart(to); 8017 } else { 8018 return !isBeforeAfterSameElement(to, from) && isBeforeOrStart(from) && isAfterOrEnd(to); 8019 } 8020 }; 8021 const fromPosition = (forward, root, pos) => { 8022 const walker = CaretWalker(root); 8023 return Optional.from(forward ? walker.next(pos) : walker.prev(pos)); 8024 }; 8025 const navigate = (forward, root, from) => fromPosition(forward, root, from).bind(to => { 8026 if (isInSameBlock(from, to, root) && shouldSkipPosition(forward, from, to)) { 8027 return fromPosition(forward, root, to); 8028 } else { 8029 return Optional.some(to); 8030 } 8031 }); 8032 const navigateIgnore = (forward, root, from, ignoreFilter) => navigate(forward, root, from).bind(pos => ignoreFilter(pos) ? navigateIgnore(forward, root, pos, ignoreFilter) : Optional.some(pos)); 8033 const positionIn = (forward, element) => { 8034 const startNode = forward ? element.firstChild : element.lastChild; 8035 if (isText$a(startNode)) { 8036 return Optional.some(CaretPosition(startNode, forward ? 0 : startNode.data.length)); 8037 } else if (startNode) { 8038 if (isCaretCandidate$3(startNode)) { 8039 return Optional.some(forward ? CaretPosition.before(startNode) : afterElement(startNode)); 8040 } else { 8041 return walkToPositionIn(forward, element, startNode); 8042 } 8043 } else { 8044 return Optional.none(); 8045 } 8046 }; 8047 const nextPosition = curry(fromPosition, true); 8048 const prevPosition = curry(fromPosition, false); 8049 const firstPositionIn = curry(positionIn, true); 8050 const lastPositionIn = curry(positionIn, false); 8051 8052 const CARET_ID = '_mce_caret'; 8053 const isCaretNode = node => isElement$6(node) && node.id === CARET_ID; 8054 const getParentCaretContainer = (body, node) => { 8055 let currentNode = node; 8056 while (currentNode && currentNode !== body) { 8057 if (isCaretNode(currentNode)) { 8058 return currentNode; 8059 } 8060 currentNode = currentNode.parentNode; 8061 } 8062 return null; 8063 }; 8064 8065 const isStringPathBookmark = bookmark => isString(bookmark.start); 8066 const isRangeBookmark = bookmark => has$2(bookmark, 'rng'); 8067 const isIdBookmark = bookmark => has$2(bookmark, 'id'); 8068 const isIndexBookmark = bookmark => has$2(bookmark, 'name'); 8069 const isPathBookmark = bookmark => Tools.isArray(bookmark.start); 8070 8071 const isForwardBookmark = bookmark => !isIndexBookmark(bookmark) && isBoolean(bookmark.forward) ? bookmark.forward : true; 8072 const addBogus = (dom, node) => { 8073 if (isElement$6(node) && dom.isBlock(node) && !node.innerHTML) { 8074 node.innerHTML = '<br data-mce-bogus="1" />'; 8075 } 8076 return node; 8077 }; 8078 const resolveCaretPositionBookmark = (dom, bookmark) => { 8079 const startPos = Optional.from(resolve$1(dom.getRoot(), bookmark.start)); 8080 const endPos = Optional.from(resolve$1(dom.getRoot(), bookmark.end)); 8081 return lift2(startPos, endPos, (start, end) => { 8082 const range = dom.createRng(); 8083 range.setStart(start.container(), start.offset()); 8084 range.setEnd(end.container(), end.offset()); 8085 return { 8086 range, 8087 forward: isForwardBookmark(bookmark) 8088 }; 8089 }); 8090 }; 8091 const insertZwsp = (node, rng) => { 8092 var _a; 8093 const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document; 8094 const textNode = doc.createTextNode(ZWSP$1); 8095 node.appendChild(textNode); 8096 rng.setStart(textNode, 0); 8097 rng.setEnd(textNode, 0); 8098 }; 8099 const isEmpty$1 = node => !node.hasChildNodes(); 8100 const tryFindRangePosition = (node, rng) => lastPositionIn(node).fold(never, pos => { 8101 rng.setStart(pos.container(), pos.offset()); 8102 rng.setEnd(pos.container(), pos.offset()); 8103 return true; 8104 }); 8105 const padEmptyCaretContainer = (root, node, rng) => { 8106 if (isEmpty$1(node) && getParentCaretContainer(root, node)) { 8107 insertZwsp(node, rng); 8108 return true; 8109 } else { 8110 return false; 8111 } 8112 }; 8113 const setEndPoint = (dom, start, bookmark, rng) => { 8114 const point = bookmark[start ? 'start' : 'end']; 8115 const root = dom.getRoot(); 8116 if (point) { 8117 let node = root; 8118 let offset = point[0]; 8119 for (let i = point.length - 1; node && i >= 1; i--) { 8120 const children = node.childNodes; 8121 if (padEmptyCaretContainer(root, node, rng)) { 8122 return true; 8123 } 8124 if (point[i] > children.length - 1) { 8125 if (padEmptyCaretContainer(root, node, rng)) { 8126 return true; 8127 } 8128 return tryFindRangePosition(node, rng); 8129 } 8130 node = children[point[i]]; 8131 } 8132 if (isText$a(node)) { 8133 offset = Math.min(point[0], node.data.length); 8134 } 8135 if (isElement$6(node)) { 8136 offset = Math.min(point[0], node.childNodes.length); 8137 } 8138 if (start) { 8139 rng.setStart(node, offset); 8140 } else { 8141 rng.setEnd(node, offset); 8142 } 8143 } 8144 return true; 8145 }; 8146 const isValidTextNode = node => isText$a(node) && node.data.length > 0; 8147 const restoreEndPoint = (dom, suffix, bookmark) => { 8148 const marker = dom.get(bookmark.id + '_' + suffix); 8149 const markerParent = marker === null || marker === void 0 ? void 0 : marker.parentNode; 8150 const keep = bookmark.keep; 8151 if (marker && markerParent) { 8152 let container; 8153 let offset; 8154 if (suffix === 'start') { 8155 if (!keep) { 8156 container = markerParent; 8157 offset = dom.nodeIndex(marker); 8158 } else { 8159 if (marker.hasChildNodes()) { 8160 container = marker.firstChild; 8161 offset = 1; 8162 } else if (isValidTextNode(marker.nextSibling)) { 8163 container = marker.nextSibling; 8164 offset = 0; 8165 } else if (isValidTextNode(marker.previousSibling)) { 8166 container = marker.previousSibling; 8167 offset = marker.previousSibling.data.length; 8168 } else { 8169 container = markerParent; 8170 offset = dom.nodeIndex(marker) + 1; 8171 } 8172 } 8173 } else { 8174 if (!keep) { 8175 container = markerParent; 8176 offset = dom.nodeIndex(marker); 8177 } else { 8178 if (marker.hasChildNodes()) { 8179 container = marker.firstChild; 8180 offset = 1; 8181 } else if (isValidTextNode(marker.previousSibling)) { 8182 container = marker.previousSibling; 8183 offset = marker.previousSibling.data.length; 8184 } else { 8185 container = markerParent; 8186 offset = dom.nodeIndex(marker); 8187 } 8188 } 8189 } 8190 if (!keep) { 8191 const prev = marker.previousSibling; 8192 const next = marker.nextSibling; 8193 Tools.each(Tools.grep(marker.childNodes), node => { 8194 if (isText$a(node)) { 8195 node.data = node.data.replace(/\uFEFF/g, ''); 8196 } 8197 }); 8198 let otherMarker; 8199 while (otherMarker = dom.get(bookmark.id + '_' + suffix)) { 8200 dom.remove(otherMarker, true); 8201 } 8202 if (isText$a(next) && isText$a(prev) && !Env.browser.isOpera()) { 8203 const idx = prev.data.length; 8204 prev.appendData(next.data); 8205 dom.remove(next); 8206 container = prev; 8207 offset = idx; 8208 } 8209 } 8210 return Optional.some(CaretPosition(container, offset)); 8211 } else { 8212 return Optional.none(); 8213 } 8214 }; 8215 const resolvePaths = (dom, bookmark) => { 8216 const range = dom.createRng(); 8217 if (setEndPoint(dom, true, bookmark, range) && setEndPoint(dom, false, bookmark, range)) { 8218 return Optional.some({ 8219 range, 8220 forward: isForwardBookmark(bookmark) 8221 }); 8222 } else { 8223 return Optional.none(); 8224 } 8225 }; 8226 const resolveId = (dom, bookmark) => { 8227 const startPos = restoreEndPoint(dom, 'start', bookmark); 8228 const endPos = restoreEndPoint(dom, 'end', bookmark); 8229 return lift2(startPos, endPos.or(startPos), (spos, epos) => { 8230 const range = dom.createRng(); 8231 range.setStart(addBogus(dom, spos.container()), spos.offset()); 8232 range.setEnd(addBogus(dom, epos.container()), epos.offset()); 8233 return { 8234 range, 8235 forward: isForwardBookmark(bookmark) 8236 }; 8237 }); 8238 }; 8239 const resolveIndex = (dom, bookmark) => Optional.from(dom.select(bookmark.name)[bookmark.index]).map(elm => { 8240 const range = dom.createRng(); 8241 range.selectNode(elm); 8242 return { 8243 range, 8244 forward: true 8245 }; 8246 }); 8247 const resolve = (selection, bookmark) => { 8248 const dom = selection.dom; 8249 if (bookmark) { 8250 if (isPathBookmark(bookmark)) { 8251 return resolvePaths(dom, bookmark); 8252 } else if (isStringPathBookmark(bookmark)) { 8253 return resolveCaretPositionBookmark(dom, bookmark); 8254 } else if (isIdBookmark(bookmark)) { 8255 return resolveId(dom, bookmark); 8256 } else if (isIndexBookmark(bookmark)) { 8257 return resolveIndex(dom, bookmark); 8258 } else if (isRangeBookmark(bookmark)) { 8259 return Optional.some({ 8260 range: bookmark.rng, 8261 forward: isForwardBookmark(bookmark) 8262 }); 8263 } 8264 } 8265 return Optional.none(); 8266 }; 8267 8268 const getBookmark$1 = (selection, type, normalized) => { 8269 return getBookmark$2(selection, type, normalized); 8270 }; 8271 const moveToBookmark = (selection, bookmark) => { 8272 resolve(selection, bookmark).each(({range, forward}) => { 8273 selection.setRng(range, forward); 8274 }); 8275 }; 8276 const isBookmarkNode$1 = node => { 8277 return isElement$6(node) && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark'; 8278 }; 8279 8280 const is = expected => actual => expected === actual; 8281 const isNbsp = is(nbsp); 8282 const isWhiteSpace = chr => chr !== '' && ' \f\n\r\t\x0B'.indexOf(chr) !== -1; 8283 const isContent = chr => !isWhiteSpace(chr) && !isNbsp(chr) && !isZwsp$2(chr); 8284 8285 const hexColour = value => ({ value: normalizeHex(value) }); 8286 const normalizeHex = hex => removeLeading(hex, '#').toUpperCase(); 8287 const toHex = component => { 8288 const hex = component.toString(16); 8289 return (hex.length === 1 ? '0' + hex : hex).toUpperCase(); 8290 }; 8291 const fromRgba = rgbaColour => { 8292 const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue); 8293 return hexColour(value); 8294 }; 8295 8296 const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/i; 8297 const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?(?:\.\d+)?)\s*\)\s*$/i; 8298 const rgbaColour = (red, green, blue, alpha) => ({ 8299 red, 8300 green, 8301 blue, 8302 alpha 8303 }); 8304 const fromStringValues = (red, green, blue, alpha) => { 8305 const r = parseInt(red, 10); 8306 const g = parseInt(green, 10); 8307 const b = parseInt(blue, 10); 8308 const a = parseFloat(alpha); 8309 return rgbaColour(r, g, b, a); 8310 }; 8311 const fromString = rgbaString => { 8312 if (rgbaString === 'transparent') { 8313 return Optional.some(rgbaColour(0, 0, 0, 0)); 8314 } 8315 const rgbMatch = rgbRegex.exec(rgbaString); 8316 if (rgbMatch !== null) { 8317 return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], '1')); 8318 } 8319 const rgbaMatch = rgbaRegex.exec(rgbaString); 8320 if (rgbaMatch !== null) { 8321 return Optional.some(fromStringValues(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], rgbaMatch[4])); 8322 } 8323 return Optional.none(); 8324 }; 8325 8326 const rgbaToHexString = color => fromString(color).map(fromRgba).map(h => '#' + h.value).getOr(color); 8327 8328 const getRanges$1 = selection => { 8329 const ranges = []; 8330 if (selection) { 8331 for (let i = 0; i < selection.rangeCount; i++) { 8332 ranges.push(selection.getRangeAt(i)); 8333 } 8334 } 8335 return ranges; 8336 }; 8337 const getSelectedNodes = ranges => { 8338 return bind$3(ranges, range => { 8339 const node = getSelectedNode(range); 8340 return node ? [SugarElement.fromDom(node)] : []; 8341 }); 8342 }; 8343 const hasMultipleRanges = selection => { 8344 return getRanges$1(selection).length > 1; 8345 }; 8346 8347 const getCellsFromRanges = ranges => filter$5(getSelectedNodes(ranges), isTableCell$2); 8348 const getCellsFromElement = elm => descendants(elm, 'td[data-mce-selected],th[data-mce-selected]'); 8349 const getCellsFromElementOrRanges = (ranges, element) => { 8350 const selectedCells = getCellsFromElement(element); 8351 return selectedCells.length > 0 ? selectedCells : getCellsFromRanges(ranges); 8352 }; 8353 const getCellsFromEditor = editor => getCellsFromElementOrRanges(getRanges$1(editor.selection.getSel()), SugarElement.fromDom(editor.getBody())); 8354 const getClosestTable = (cell, isRoot) => ancestor$3(cell, 'table', isRoot); 8355 8356 const getStartNode = rng => { 8357 const sc = rng.startContainer, so = rng.startOffset; 8358 if (isText$a(sc)) { 8359 return so === 0 ? Optional.some(SugarElement.fromDom(sc)) : Optional.none(); 8360 } else { 8361 return Optional.from(sc.childNodes[so]).map(SugarElement.fromDom); 8362 } 8363 }; 8364 const getEndNode = rng => { 8365 const ec = rng.endContainer, eo = rng.endOffset; 8366 if (isText$a(ec)) { 8367 return eo === ec.data.length ? Optional.some(SugarElement.fromDom(ec)) : Optional.none(); 8368 } else { 8369 return Optional.from(ec.childNodes[eo - 1]).map(SugarElement.fromDom); 8370 } 8371 }; 8372 const getFirstChildren = node => { 8373 return firstChild(node).fold(constant([node]), child => { 8374 return [node].concat(getFirstChildren(child)); 8375 }); 8376 }; 8377 const getLastChildren = node => { 8378 return lastChild(node).fold(constant([node]), child => { 8379 if (name(child) === 'br') { 8380 return prevSibling(child).map(sibling => { 8381 return [node].concat(getLastChildren(sibling)); 8382 }).getOr([]); 8383 } else { 8384 return [node].concat(getLastChildren(child)); 8385 } 8386 }); 8387 }; 8388 const hasAllContentsSelected = (elm, rng) => { 8389 return lift2(getStartNode(rng), getEndNode(rng), (startNode, endNode) => { 8390 const start = find$2(getFirstChildren(elm), curry(eq, startNode)); 8391 const end = find$2(getLastChildren(elm), curry(eq, endNode)); 8392 return start.isSome() && end.isSome(); 8393 }).getOr(false); 8394 }; 8395 const moveEndPoint = (dom, rng, node, start) => { 8396 const root = node; 8397 const walker = new DomTreeWalker(node, root); 8398 const moveCaretBeforeOnEnterElementsMap = filter$4(dom.schema.getMoveCaretBeforeOnEnterElements(), (_, name) => !contains$2([ 8399 'td', 8400 'th', 8401 'table' 8402 ], name.toLowerCase())); 8403 let currentNode = node; 8404 do { 8405 if (isText$a(currentNode) && Tools.trim(currentNode.data).length !== 0) { 8406 if (start) { 8407 rng.setStart(currentNode, 0); 8408 } else { 8409 rng.setEnd(currentNode, currentNode.data.length); 8410 } 8411 return; 8412 } 8413 if (moveCaretBeforeOnEnterElementsMap[currentNode.nodeName]) { 8414 if (start) { 8415 rng.setStartBefore(currentNode); 8416 } else { 8417 if (currentNode.nodeName === 'BR') { 8418 rng.setEndBefore(currentNode); 8419 } else { 8420 rng.setEndAfter(currentNode); 8421 } 8422 } 8423 return; 8424 } 8425 } while (currentNode = start ? walker.next() : walker.prev()); 8426 if (root.nodeName === 'BODY') { 8427 if (start) { 8428 rng.setStart(root, 0); 8429 } else { 8430 rng.setEnd(root, root.childNodes.length); 8431 } 8432 } 8433 }; 8434 const hasAnyRanges = editor => { 8435 const sel = editor.selection.getSel(); 8436 return isNonNullable(sel) && sel.rangeCount > 0; 8437 }; 8438 const runOnRanges = (editor, executor) => { 8439 const fakeSelectionNodes = getCellsFromEditor(editor); 8440 if (fakeSelectionNodes.length > 0) { 8441 each$e(fakeSelectionNodes, elem => { 8442 const node = elem.dom; 8443 const fakeNodeRng = editor.dom.createRng(); 8444 fakeNodeRng.setStartBefore(node); 8445 fakeNodeRng.setEndAfter(node); 8446 executor(fakeNodeRng, true); 8447 }); 8448 } else { 8449 executor(editor.selection.getRng(), false); 8450 } 8451 }; 8452 const preserve = (selection, fillBookmark, executor) => { 8453 const bookmark = getPersistentBookmark(selection, fillBookmark); 8454 executor(bookmark); 8455 selection.moveToBookmark(bookmark); 8456 }; 8457 8458 const isNode = node => isNumber(node === null || node === void 0 ? void 0 : node.nodeType); 8459 const isElementNode$1 = node => isElement$6(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$2(node); 8460 const isElementDirectlySelected = (dom, node) => { 8461 if (isElementNode$1(node) && !/^(TD|TH)$/.test(node.nodeName)) { 8462 const selectedAttr = dom.getAttrib(node, 'data-mce-selected'); 8463 const value = parseInt(selectedAttr, 10); 8464 return !isNaN(value) && value > 0; 8465 } else { 8466 return false; 8467 } 8468 }; 8469 const isEditable$2 = elm => elm.isContentEditable === true; 8470 const preserveSelection = (editor, action, shouldMoveStart) => { 8471 const {selection, dom} = editor; 8472 const selectedNodeBeforeAction = selection.getNode(); 8473 const isSelectedBeforeNodeNoneditable = isContentEditableFalse$b(selectedNodeBeforeAction); 8474 preserve(selection, true, () => { 8475 action(); 8476 }); 8477 const isBeforeNodeStillNoneditable = isSelectedBeforeNodeNoneditable && isContentEditableFalse$b(selectedNodeBeforeAction); 8478 if (isBeforeNodeStillNoneditable && dom.isChildOf(selectedNodeBeforeAction, editor.getBody())) { 8479 editor.selection.select(selectedNodeBeforeAction); 8480 } else if (shouldMoveStart(selection.getStart())) { 8481 moveStartToNearestText(dom, selection); 8482 } 8483 }; 8484 const moveStartToNearestText = (dom, selection) => { 8485 var _a, _b; 8486 const rng = selection.getRng(); 8487 const {startContainer, startOffset} = rng; 8488 const selectedNode = selection.getNode(); 8489 if (isElementDirectlySelected(dom, selectedNode)) { 8490 return; 8491 } 8492 if (isElement$6(startContainer)) { 8493 const nodes = startContainer.childNodes; 8494 const root = dom.getRoot(); 8495 let walker; 8496 if (startOffset < nodes.length) { 8497 const startNode = nodes[startOffset]; 8498 walker = new DomTreeWalker(startNode, (_a = dom.getParent(startNode, dom.isBlock)) !== null && _a !== void 0 ? _a : root); 8499 } else { 8500 const startNode = nodes[nodes.length - 1]; 8501 walker = new DomTreeWalker(startNode, (_b = dom.getParent(startNode, dom.isBlock)) !== null && _b !== void 0 ? _b : root); 8502 walker.next(true); 8503 } 8504 for (let node = walker.current(); node; node = walker.next()) { 8505 if (dom.getContentEditable(node) === 'false') { 8506 return; 8507 } else if (isText$a(node) && !isWhiteSpaceNode$1(node)) { 8508 rng.setStart(node, 0); 8509 selection.setRng(rng); 8510 return; 8511 } 8512 } 8513 } 8514 }; 8515 const getNonWhiteSpaceSibling = (node, next, inc) => { 8516 if (node) { 8517 const nextName = next ? 'nextSibling' : 'previousSibling'; 8518 for (node = inc ? node : node[nextName]; node; node = node[nextName]) { 8519 if (isElement$6(node) || !isWhiteSpaceNode$1(node)) { 8520 return node; 8521 } 8522 } 8523 } 8524 return undefined; 8525 }; 8526 const isTextBlock$1 = (schema, node) => !!schema.getTextBlockElements()[node.nodeName.toLowerCase()] || isTransparentBlock(schema, node); 8527 const isValid = (ed, parent, child) => { 8528 return ed.schema.isValidChild(parent, child); 8529 }; 8530 const isWhiteSpaceNode$1 = (node, allowSpaces = false) => { 8531 if (isNonNullable(node) && isText$a(node)) { 8532 const data = allowSpaces ? node.data.replace(/ /g, '\xA0') : node.data; 8533 return isWhitespaceText(data); 8534 } else { 8535 return false; 8536 } 8537 }; 8538 const isEmptyTextNode$1 = node => { 8539 return isNonNullable(node) && isText$a(node) && node.length === 0; 8540 }; 8541 const isWrapNoneditableTarget = (editor, node) => { 8542 const baseDataSelector = '[data-mce-cef-wrappable]'; 8543 const formatNoneditableSelector = getFormatNoneditableSelector(editor); 8544 const selector = isEmpty$3(formatNoneditableSelector) ? baseDataSelector : `${ baseDataSelector },${ formatNoneditableSelector }`; 8545 return is$1(SugarElement.fromDom(node), selector); 8546 }; 8547 const isWrappableNoneditable = (editor, node) => { 8548 const dom = editor.dom; 8549 return isElementNode$1(node) && dom.getContentEditable(node) === 'false' && isWrapNoneditableTarget(editor, node) && dom.select('[contenteditable="true"]', node).length === 0; 8550 }; 8551 const replaceVars = (value, vars) => { 8552 if (isFunction(value)) { 8553 return value(vars); 8554 } else if (isNonNullable(vars)) { 8555 value = value.replace(/%(\w+)/g, (str, name) => { 8556 return vars[name] || str; 8557 }); 8558 } 8559 return value; 8560 }; 8561 const isEq$5 = (str1, str2) => { 8562 str1 = str1 || ''; 8563 str2 = str2 || ''; 8564 str1 = '' + (str1.nodeName || str1); 8565 str2 = '' + (str2.nodeName || str2); 8566 return str1.toLowerCase() === str2.toLowerCase(); 8567 }; 8568 const normalizeStyleValue = (value, name) => { 8569 if (isNullable(value)) { 8570 return null; 8571 } else { 8572 let strValue = String(value); 8573 if (name === 'color' || name === 'backgroundColor') { 8574 strValue = rgbaToHexString(strValue); 8575 } 8576 if (name === 'fontWeight' && value === 700) { 8577 strValue = 'bold'; 8578 } 8579 if (name === 'fontFamily') { 8580 strValue = strValue.replace(/[\'\"]/g, '').replace(/,\s+/g, ','); 8581 } 8582 return strValue; 8583 } 8584 }; 8585 const getStyle = (dom, node, name) => { 8586 const style = dom.getStyle(node, name); 8587 return normalizeStyleValue(style, name); 8588 }; 8589 const getTextDecoration = (dom, node) => { 8590 let decoration; 8591 dom.getParent(node, n => { 8592 if (isElement$6(n)) { 8593 decoration = dom.getStyle(n, 'text-decoration'); 8594 return !!decoration && decoration !== 'none'; 8595 } else { 8596 return false; 8597 } 8598 }); 8599 return decoration; 8600 }; 8601 const getParents$2 = (dom, node, selector) => { 8602 return dom.getParents(node, selector, dom.getRoot()); 8603 }; 8604 const isFormatPredicate = (editor, formatName, predicate) => { 8605 const formats = editor.formatter.get(formatName); 8606 return isNonNullable(formats) && exists(formats, predicate); 8607 }; 8608 const isVariableFormatName = (editor, formatName) => { 8609 const hasVariableValues = format => { 8610 const isVariableValue = val => isFunction(val) || val.length > 1 && val.charAt(0) === '%'; 8611 return exists([ 8612 'styles', 8613 'attributes' 8614 ], key => get$a(format, key).exists(field => { 8615 const fieldValues = isArray$1(field) ? field : values(field); 8616 return exists(fieldValues, isVariableValue); 8617 })); 8618 }; 8619 return isFormatPredicate(editor, formatName, hasVariableValues); 8620 }; 8621 const areSimilarFormats = (editor, formatName, otherFormatName) => { 8622 const validKeys = [ 8623 'inline', 8624 'block', 8625 'selector', 8626 'attributes', 8627 'styles', 8628 'classes' 8629 ]; 8630 const filterObj = format => filter$4(format, (_, key) => exists(validKeys, validKey => validKey === key)); 8631 return isFormatPredicate(editor, formatName, fmt1 => { 8632 const filteredFmt1 = filterObj(fmt1); 8633 return isFormatPredicate(editor, otherFormatName, fmt2 => { 8634 const filteredFmt2 = filterObj(fmt2); 8635 return equal$1(filteredFmt1, filteredFmt2); 8636 }); 8637 }); 8638 }; 8639 const isBlockFormat = format => hasNonNullableKey(format, 'block'); 8640 const isWrappingBlockFormat = format => isBlockFormat(format) && format.wrapper === true; 8641 const isNonWrappingBlockFormat = format => isBlockFormat(format) && format.wrapper !== true; 8642 const isSelectorFormat = format => hasNonNullableKey(format, 'selector'); 8643 const isInlineFormat = format => hasNonNullableKey(format, 'inline'); 8644 const isMixedFormat = format => isSelectorFormat(format) && isInlineFormat(format) && is$2(get$a(format, 'mixed'), true); 8645 const shouldExpandToSelector = format => isSelectorFormat(format) && format.expand !== false && !isInlineFormat(format); 8646 8647 const isBookmarkNode = isBookmarkNode$1; 8648 const getParents$1 = getParents$2; 8649 const isWhiteSpaceNode = isWhiteSpaceNode$1; 8650 const isTextBlock = isTextBlock$1; 8651 const isBogusBr = node => { 8652 return isBr$6(node) && node.getAttribute('data-mce-bogus') && !node.nextSibling; 8653 }; 8654 const findParentContentEditable = (dom, node) => { 8655 let parent = node; 8656 while (parent) { 8657 if (isElement$6(parent) && dom.getContentEditable(parent)) { 8658 return dom.getContentEditable(parent) === 'false' ? parent : node; 8659 } 8660 parent = parent.parentNode; 8661 } 8662 return node; 8663 }; 8664 const walkText = (start, node, offset, predicate) => { 8665 const str = node.data; 8666 if (start) { 8667 for (let i = offset; i > 0; i--) { 8668 if (predicate(str.charAt(i - 1))) { 8669 return i; 8670 } 8671 } 8672 } else { 8673 for (let i = offset; i < str.length; i++) { 8674 if (predicate(str.charAt(i))) { 8675 return i; 8676 } 8677 } 8678 } 8679 return -1; 8680 }; 8681 const findSpace = (start, node, offset) => walkText(start, node, offset, c => isNbsp(c) || isWhiteSpace(c)); 8682 const findContent = (start, node, offset) => walkText(start, node, offset, isContent); 8683 const findWordEndPoint = (dom, body, container, offset, start, includeTrailingSpaces) => { 8684 let lastTextNode; 8685 const rootNode = dom.getParent(container, dom.isBlock) || body; 8686 const walk = (container, offset, pred) => { 8687 const textSeeker = TextSeeker(dom); 8688 const walker = start ? textSeeker.backwards : textSeeker.forwards; 8689 return Optional.from(walker(container, offset, (text, textOffset) => { 8690 if (isBookmarkNode(text.parentNode)) { 8691 return -1; 8692 } else { 8693 lastTextNode = text; 8694 return pred(start, text, textOffset); 8695 } 8696 }, rootNode)); 8697 }; 8698 const spaceResult = walk(container, offset, findSpace); 8699 return spaceResult.bind(result => includeTrailingSpaces ? walk(result.container, result.offset + (start ? -1 : 0), findContent) : Optional.some(result)).orThunk(() => lastTextNode ? Optional.some({ 8700 container: lastTextNode, 8701 offset: start ? 0 : lastTextNode.length 8702 }) : Optional.none()); 8703 }; 8704 const findSelectorEndPoint = (dom, formatList, rng, container, siblingName) => { 8705 const sibling = container[siblingName]; 8706 if (isText$a(container) && isEmpty$3(container.data) && sibling) { 8707 container = sibling; 8708 } 8709 const parents = getParents$1(dom, container); 8710 for (let i = 0; i < parents.length; i++) { 8711 for (let y = 0; y < formatList.length; y++) { 8712 const curFormat = formatList[y]; 8713 if (isNonNullable(curFormat.collapsed) && curFormat.collapsed !== rng.collapsed) { 8714 continue; 8715 } 8716 if (isSelectorFormat(curFormat) && dom.is(parents[i], curFormat.selector)) { 8717 return parents[i]; 8718 } 8719 } 8720 } 8721 return container; 8722 }; 8723 const findBlockEndPoint = (dom, formatList, container, siblingName) => { 8724 var _a; 8725 let node = container; 8726 const root = dom.getRoot(); 8727 const format = formatList[0]; 8728 if (isBlockFormat(format)) { 8729 node = format.wrapper ? null : dom.getParent(container, format.block, root); 8730 } 8731 if (!node) { 8732 const scopeRoot = (_a = dom.getParent(container, 'LI,TD,TH')) !== null && _a !== void 0 ? _a : root; 8733 node = dom.getParent(isText$a(container) ? container.parentNode : container, node => node !== root && isTextBlock(dom.schema, node), scopeRoot); 8734 } 8735 if (node && isBlockFormat(format) && format.wrapper) { 8736 node = getParents$1(dom, node, 'ul,ol').reverse()[0] || node; 8737 } 8738 if (!node) { 8739 node = container; 8740 while (node && node[siblingName] && !dom.isBlock(node[siblingName])) { 8741 node = node[siblingName]; 8742 if (isEq$5(node, 'br')) { 8743 break; 8744 } 8745 } 8746 } 8747 return node || container; 8748 }; 8749 const isAtBlockBoundary$1 = (dom, root, container, siblingName) => { 8750 const parent = container.parentNode; 8751 if (isNonNullable(container[siblingName])) { 8752 return false; 8753 } else if (parent === root || isNullable(parent) || dom.isBlock(parent)) { 8754 return true; 8755 } else { 8756 return isAtBlockBoundary$1(dom, root, parent, siblingName); 8757 } 8758 }; 8759 const findParentContainer = (dom, formatList, container, offset, start) => { 8760 let parent = container; 8761 const siblingName = start ? 'previousSibling' : 'nextSibling'; 8762 const root = dom.getRoot(); 8763 if (isText$a(container) && !isWhiteSpaceNode(container)) { 8764 if (start ? offset > 0 : offset < container.data.length) { 8765 return container; 8766 } 8767 } 8768 while (parent) { 8769 if (!formatList[0].block_expand && dom.isBlock(parent)) { 8770 return parent; 8771 } 8772 for (let sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) { 8773 const allowSpaces = isText$a(sibling) && !isAtBlockBoundary$1(dom, root, sibling, siblingName); 8774 if (!isBookmarkNode(sibling) && !isBogusBr(sibling) && !isWhiteSpaceNode(sibling, allowSpaces)) { 8775 return parent; 8776 } 8777 } 8778 if (parent === root || parent.parentNode === root) { 8779 container = parent; 8780 break; 8781 } 8782 parent = parent.parentNode; 8783 } 8784 return container; 8785 }; 8786 const isSelfOrParentBookmark = container => isBookmarkNode(container.parentNode) || isBookmarkNode(container); 8787 const expandRng = (dom, rng, formatList, includeTrailingSpace = false) => { 8788 let {startContainer, startOffset, endContainer, endOffset} = rng; 8789 const format = formatList[0]; 8790 if (isElement$6(startContainer) && startContainer.hasChildNodes()) { 8791 startContainer = getNode$1(startContainer, startOffset); 8792 if (isText$a(startContainer)) { 8793 startOffset = 0; 8794 } 8795 } 8796 if (isElement$6(endContainer) && endContainer.hasChildNodes()) { 8797 endContainer = getNode$1(endContainer, rng.collapsed ? endOffset : endOffset - 1); 8798 if (isText$a(endContainer)) { 8799 endOffset = endContainer.data.length; 8800 } 8801 } 8802 startContainer = findParentContentEditable(dom, startContainer); 8803 endContainer = findParentContentEditable(dom, endContainer); 8804 if (isSelfOrParentBookmark(startContainer)) { 8805 startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode; 8806 if (rng.collapsed) { 8807 startContainer = startContainer.previousSibling || startContainer; 8808 } else { 8809 startContainer = startContainer.nextSibling || startContainer; 8810 } 8811 if (isText$a(startContainer)) { 8812 startOffset = rng.collapsed ? startContainer.length : 0; 8813 } 8814 } 8815 if (isSelfOrParentBookmark(endContainer)) { 8816 endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode; 8817 if (rng.collapsed) { 8818 endContainer = endContainer.nextSibling || endContainer; 8819 } else { 8820 endContainer = endContainer.previousSibling || endContainer; 8821 } 8822 if (isText$a(endContainer)) { 8823 endOffset = rng.collapsed ? 0 : endContainer.length; 8824 } 8825 } 8826 if (rng.collapsed) { 8827 const startPoint = findWordEndPoint(dom, dom.getRoot(), startContainer, startOffset, true, includeTrailingSpace); 8828 startPoint.each(({container, offset}) => { 8829 startContainer = container; 8830 startOffset = offset; 8831 }); 8832 const endPoint = findWordEndPoint(dom, dom.getRoot(), endContainer, endOffset, false, includeTrailingSpace); 8833 endPoint.each(({container, offset}) => { 8834 endContainer = container; 8835 endOffset = offset; 8836 }); 8837 } 8838 if (isInlineFormat(format) || format.block_expand) { 8839 if (!isInlineFormat(format) || (!isText$a(startContainer) || startOffset === 0)) { 8840 startContainer = findParentContainer(dom, formatList, startContainer, startOffset, true); 8841 } 8842 if (!isInlineFormat(format) || (!isText$a(endContainer) || endOffset === endContainer.data.length)) { 8843 endContainer = findParentContainer(dom, formatList, endContainer, endOffset, false); 8844 } 8845 } 8846 if (shouldExpandToSelector(format)) { 8847 startContainer = findSelectorEndPoint(dom, formatList, rng, startContainer, 'previousSibling'); 8848 endContainer = findSelectorEndPoint(dom, formatList, rng, endContainer, 'nextSibling'); 8849 } 8850 if (isBlockFormat(format) || isSelectorFormat(format)) { 8851 startContainer = findBlockEndPoint(dom, formatList, startContainer, 'previousSibling'); 8852 endContainer = findBlockEndPoint(dom, formatList, endContainer, 'nextSibling'); 8853 if (isBlockFormat(format)) { 8854 if (!dom.isBlock(startContainer)) { 8855 startContainer = findParentContainer(dom, formatList, startContainer, startOffset, true); 8856 } 8857 if (!dom.isBlock(endContainer)) { 8858 endContainer = findParentContainer(dom, formatList, endContainer, endOffset, false); 8859 } 8860 } 8861 } 8862 if (isElement$6(startContainer) && startContainer.parentNode) { 8863 startOffset = dom.nodeIndex(startContainer); 8864 startContainer = startContainer.parentNode; 8865 } 8866 if (isElement$6(endContainer) && endContainer.parentNode) { 8867 endOffset = dom.nodeIndex(endContainer) + 1; 8868 endContainer = endContainer.parentNode; 8869 } 8870 return { 8871 startContainer, 8872 startOffset, 8873 endContainer, 8874 endOffset 8875 }; 8876 }; 8877 8878 const walk$3 = (dom, rng, callback) => { 8879 var _a; 8880 const startOffset = rng.startOffset; 8881 const startContainer = getNode$1(rng.startContainer, startOffset); 8882 const endOffset = rng.endOffset; 8883 const endContainer = getNode$1(rng.endContainer, endOffset - 1); 8884 const exclude = nodes => { 8885 const firstNode = nodes[0]; 8886 if (isText$a(firstNode) && firstNode === startContainer && startOffset >= firstNode.data.length) { 8887 nodes.splice(0, 1); 8888 } 8889 const lastNode = nodes[nodes.length - 1]; 8890 if (endOffset === 0 && nodes.length > 0 && lastNode === endContainer && isText$a(lastNode)) { 8891 nodes.splice(nodes.length - 1, 1); 8892 } 8893 return nodes; 8894 }; 8895 const collectSiblings = (node, name, endNode) => { 8896 const siblings = []; 8897 for (; node && node !== endNode; node = node[name]) { 8898 siblings.push(node); 8899 } 8900 return siblings; 8901 }; 8902 const findEndPoint = (node, root) => dom.getParent(node, node => node.parentNode === root, root); 8903 const walkBoundary = (startNode, endNode, next) => { 8904 const siblingName = next ? 'nextSibling' : 'previousSibling'; 8905 for (let node = startNode, parent = node.parentNode; node && node !== endNode; node = parent) { 8906 parent = node.parentNode; 8907 const siblings = collectSiblings(node === startNode ? node : node[siblingName], siblingName); 8908 if (siblings.length) { 8909 if (!next) { 8910 siblings.reverse(); 8911 } 8912 callback(exclude(siblings)); 8913 } 8914 } 8915 }; 8916 if (startContainer === endContainer) { 8917 return callback(exclude([startContainer])); 8918 } 8919 const ancestor = (_a = dom.findCommonAncestor(startContainer, endContainer)) !== null && _a !== void 0 ? _a : dom.getRoot(); 8920 if (dom.isChildOf(startContainer, endContainer)) { 8921 return walkBoundary(startContainer, ancestor, true); 8922 } 8923 if (dom.isChildOf(endContainer, startContainer)) { 8924 return walkBoundary(endContainer, ancestor); 8925 } 8926 const startPoint = findEndPoint(startContainer, ancestor) || startContainer; 8927 const endPoint = findEndPoint(endContainer, ancestor) || endContainer; 8928 walkBoundary(startContainer, startPoint, true); 8929 const siblings = collectSiblings(startPoint === startContainer ? startPoint : startPoint.nextSibling, 'nextSibling', endPoint === endContainer ? endPoint.nextSibling : endPoint); 8930 if (siblings.length) { 8931 callback(exclude(siblings)); 8932 } 8933 walkBoundary(endContainer, endPoint); 8934 }; 8935 8936 const validBlocks = [ 8937 'pre[class*=language-][contenteditable="false"]', 8938 'figure.image', 8939 'div[data-ephox-embed-iri]', 8940 'div.tiny-pageembed', 8941 'div.mce-toc', 8942 'div[data-mce-toc]' 8943 ]; 8944 const isZeroWidth = elem => isText$b(elem) && get$3(elem) === ZWSP$1; 8945 const context = (editor, elem, wrapName, nodeName) => parent(elem).fold(() => 'skipping', parent => { 8946 if (nodeName === 'br' || isZeroWidth(elem)) { 8947 return 'valid'; 8948 } else if (isAnnotation(elem)) { 8949 return 'existing'; 8950 } else if (isCaretNode(elem.dom)) { 8951 return 'caret'; 8952 } else if (exists(validBlocks, selector => is$1(elem, selector))) { 8953 return 'valid-block'; 8954 } else if (!isValid(editor, wrapName, nodeName) || !isValid(editor, name(parent), wrapName)) { 8955 return 'invalid-child'; 8956 } else { 8957 return 'valid'; 8958 } 8959 }); 8960 8961 const applyWordGrab = (editor, rng) => { 8962 const r = expandRng(editor.dom, rng, [{ inline: 'span' }]); 8963 rng.setStart(r.startContainer, r.startOffset); 8964 rng.setEnd(r.endContainer, r.endOffset); 8965 editor.selection.setRng(rng); 8966 }; 8967 const applyAnnotation = (elem, masterUId, data, annotationName, decorate, directAnnotation) => { 8968 const {uid = masterUId, ...otherData} = data; 8969 add$2(elem, annotation()); 8970 set$3(elem, `${ dataAnnotationId() }`, uid); 8971 set$3(elem, `${ dataAnnotation() }`, annotationName); 8972 const {attributes = {}, classes = []} = decorate(uid, otherData); 8973 setAll$1(elem, attributes); 8974 add(elem, classes); 8975 if (directAnnotation) { 8976 if (classes.length > 0) { 8977 set$3(elem, `${ dataAnnotationClasses() }`, classes.join(',')); 8978 } 8979 const attributeNames = keys(attributes); 8980 if (attributeNames.length > 0) { 8981 set$3(elem, `${ dataAnnotationAttributes() }`, attributeNames.join(',')); 8982 } 8983 } 8984 }; 8985 const removeDirectAnnotation = elem => { 8986 remove$7(elem, annotation()); 8987 remove$a(elem, `${ dataAnnotationId() }`); 8988 remove$a(elem, `${ dataAnnotation() }`); 8989 remove$a(elem, `${ dataAnnotationActive() }`); 8990 const customAttrNames = getOpt(elem, `${ dataAnnotationAttributes() }`).map(names => names.split(',')).getOr([]); 8991 const customClasses = getOpt(elem, `${ dataAnnotationClasses() }`).map(names => names.split(',')).getOr([]); 8992 each$e(customAttrNames, name => remove$a(elem, name)); 8993 remove$4(elem, customClasses); 8994 remove$a(elem, `${ dataAnnotationClasses() }`); 8995 remove$a(elem, `${ dataAnnotationAttributes() }`); 8996 }; 8997 const makeAnnotation = (eDoc, uid, data, annotationName, decorate) => { 8998 const master = SugarElement.fromTag('span', eDoc); 8999 applyAnnotation(master, uid, data, annotationName, decorate, false); 9000 return master; 9001 }; 9002 const annotate = (editor, rng, uid, annotationName, decorate, data) => { 9003 const newWrappers = []; 9004 const master = makeAnnotation(editor.getDoc(), uid, data, annotationName, decorate); 9005 const wrapper = value$2(); 9006 const finishWrapper = () => { 9007 wrapper.clear(); 9008 }; 9009 const getOrOpenWrapper = () => wrapper.get().getOrThunk(() => { 9010 const nu = shallow$1(master); 9011 newWrappers.push(nu); 9012 wrapper.set(nu); 9013 return nu; 9014 }); 9015 const processElements = elems => { 9016 each$e(elems, processElement); 9017 }; 9018 const processElement = elem => { 9019 const ctx = context(editor, elem, 'span', name(elem)); 9020 switch (ctx) { 9021 case 'invalid-child': { 9022 finishWrapper(); 9023 const children = children$1(elem); 9024 processElements(children); 9025 finishWrapper(); 9026 break; 9027 } 9028 case 'valid-block': { 9029 finishWrapper(); 9030 applyAnnotation(elem, uid, data, annotationName, decorate, true); 9031 break; 9032 } 9033 case 'valid': { 9034 const w = getOrOpenWrapper(); 9035 wrap$2(elem, w); 9036 break; 9037 } 9038 } 9039 }; 9040 const processNodes = nodes => { 9041 const elems = map$3(nodes, SugarElement.fromDom); 9042 processElements(elems); 9043 }; 9044 walk$3(editor.dom, rng, nodes => { 9045 finishWrapper(); 9046 processNodes(nodes); 9047 }); 9048 return newWrappers; 9049 }; 9050 const annotateWithBookmark = (editor, name, settings, data) => { 9051 editor.undoManager.transact(() => { 9052 const selection = editor.selection; 9053 const initialRng = selection.getRng(); 9054 const hasFakeSelection = getCellsFromEditor(editor).length > 0; 9055 const masterUid = generate$1('mce-annotation'); 9056 if (initialRng.collapsed && !hasFakeSelection) { 9057 applyWordGrab(editor, initialRng); 9058 } 9059 if (selection.getRng().collapsed && !hasFakeSelection) { 9060 const wrapper = makeAnnotation(editor.getDoc(), masterUid, data, name, settings.decorate); 9061 set$1(wrapper, nbsp); 9062 selection.getRng().insertNode(wrapper.dom); 9063 selection.select(wrapper.dom); 9064 } else { 9065 preserve(selection, false, () => { 9066 runOnRanges(editor, selectionRng => { 9067 annotate(editor, selectionRng, masterUid, name, settings.decorate, data); 9068 }); 9069 }); 9070 } 9071 }); 9072 }; 9073 9074 const Annotator = editor => { 9075 const registry = create$c(); 9076 setup$w(editor, registry); 9077 const changes = setup$x(editor, registry); 9078 const isSpan = isTag('span'); 9079 const removeAnnotations = elements => { 9080 each$e(elements, element => { 9081 if (isSpan(element)) { 9082 unwrap(element); 9083 } else { 9084 removeDirectAnnotation(element); 9085 } 9086 }); 9087 }; 9088 return { 9089 register: (name, settings) => { 9090 registry.register(name, settings); 9091 }, 9092 annotate: (name, data) => { 9093 registry.lookup(name).each(settings => { 9094 annotateWithBookmark(editor, name, settings, data); 9095 }); 9096 }, 9097 annotationChanged: (name, callback) => { 9098 changes.addListener(name, callback); 9099 }, 9100 remove: name => { 9101 identify(editor, Optional.some(name)).each(({elements}) => { 9102 const bookmark = editor.selection.getBookmark(); 9103 removeAnnotations(elements); 9104 editor.selection.moveToBookmark(bookmark); 9105 }); 9106 }, 9107 removeAll: name => { 9108 const bookmark = editor.selection.getBookmark(); 9109 each$d(findAll(editor, name), (elements, _) => { 9110 removeAnnotations(elements); 9111 }); 9112 editor.selection.moveToBookmark(bookmark); 9113 }, 9114 getAll: name => { 9115 const directory = findAll(editor, name); 9116 return map$2(directory, elems => map$3(elems, elem => elem.dom)); 9117 } 9118 }; 9119 }; 9120 9121 const BookmarkManager = selection => { 9122 return { 9123 getBookmark: curry(getBookmark$1, selection), 9124 moveToBookmark: curry(moveToBookmark, selection) 9125 }; 9126 }; 9127 BookmarkManager.isBookmarkNode = isBookmarkNode$1; 9128 9129 const isXYWithinRange = (clientX, clientY, range) => { 9130 if (range.collapsed) { 9131 return false; 9132 } else { 9133 return exists(range.getClientRects(), rect => containsXY(rect, clientX, clientY)); 9134 } 9135 }; 9136 9137 const firePreProcess = (editor, args) => editor.dispatch('PreProcess', args); 9138 const firePostProcess = (editor, args) => editor.dispatch('PostProcess', args); 9139 const fireRemove = editor => { 9140 editor.dispatch('remove'); 9141 }; 9142 const fireDetach = editor => { 9143 editor.dispatch('detach'); 9144 }; 9145 const fireSwitchMode = (editor, mode) => { 9146 editor.dispatch('SwitchMode', { mode }); 9147 }; 9148 const fireObjectResizeStart = (editor, target, width, height, origin) => { 9149 editor.dispatch('ObjectResizeStart', { 9150 target, 9151 width, 9152 height, 9153 origin 9154 }); 9155 }; 9156 const fireObjectResized = (editor, target, width, height, origin) => { 9157 editor.dispatch('ObjectResized', { 9158 target, 9159 width, 9160 height, 9161 origin 9162 }); 9163 }; 9164 const firePreInit = editor => { 9165 editor.dispatch('PreInit'); 9166 }; 9167 const firePostRender = editor => { 9168 editor.dispatch('PostRender'); 9169 }; 9170 const fireInit = editor => { 9171 editor.dispatch('Init'); 9172 }; 9173 const firePlaceholderToggle = (editor, state) => { 9174 editor.dispatch('PlaceholderToggle', { state }); 9175 }; 9176 const fireError = (editor, errorType, error) => { 9177 editor.dispatch(errorType, error); 9178 }; 9179 const fireFormatApply = (editor, format, node, vars) => { 9180 editor.dispatch('FormatApply', { 9181 format, 9182 node, 9183 vars 9184 }); 9185 }; 9186 const fireFormatRemove = (editor, format, node, vars) => { 9187 editor.dispatch('FormatRemove', { 9188 format, 9189 node, 9190 vars 9191 }); 9192 }; 9193 const fireBeforeSetContent = (editor, args) => editor.dispatch('BeforeSetContent', args); 9194 const fireSetContent = (editor, args) => editor.dispatch('SetContent', args); 9195 const fireBeforeGetContent = (editor, args) => editor.dispatch('BeforeGetContent', args); 9196 const fireGetContent = (editor, args) => editor.dispatch('GetContent', args); 9197 const fireAutocompleterStart = (editor, args) => { 9198 editor.dispatch('AutocompleterStart', args); 9199 }; 9200 const fireAutocompleterUpdate = (editor, args) => { 9201 editor.dispatch('AutocompleterUpdate', args); 9202 }; 9203 const fireAutocompleterEnd = editor => { 9204 editor.dispatch('AutocompleterEnd'); 9205 }; 9206 const firePastePreProcess = (editor, html, internal) => editor.dispatch('PastePreProcess', { 9207 content: html, 9208 internal 9209 }); 9210 const firePastePostProcess = (editor, node, internal) => editor.dispatch('PastePostProcess', { 9211 node, 9212 internal 9213 }); 9214 const firePastePlainTextToggle = (editor, state) => editor.dispatch('PastePlainTextToggle', { state }); 9215 const fireEditableRootStateChange = (editor, state) => editor.dispatch('EditableRootStateChange', { state }); 9216 9217 const VK = { 9218 BACKSPACE: 8, 9219 DELETE: 46, 9220 DOWN: 40, 9221 ENTER: 13, 9222 ESC: 27, 9223 LEFT: 37, 9224 RIGHT: 39, 9225 SPACEBAR: 32, 9226 TAB: 9, 9227 UP: 38, 9228 PAGE_UP: 33, 9229 PAGE_DOWN: 34, 9230 END: 35, 9231 HOME: 36, 9232 modifierPressed: e => { 9233 return e.shiftKey || e.ctrlKey || e.altKey || VK.metaKeyPressed(e); 9234 }, 9235 metaKeyPressed: e => { 9236 return Env.os.isMacOS() || Env.os.isiOS() ? e.metaKey : e.ctrlKey && !e.altKey; 9237 } 9238 }; 9239 9240 const elementSelectionAttr = 'data-mce-selected'; 9241 const controlElmSelector = 'table,img,figure.image,hr,video,span.mce-preview-object,details'; 9242 const abs = Math.abs; 9243 const round$1 = Math.round; 9244 const resizeHandles = { 9245 nw: [ 9246 0, 9247 0, 9248 -1, 9249 -1 9250 ], 9251 ne: [ 9252 1, 9253 0, 9254 1, 9255 -1 9256 ], 9257 se: [ 9258 1, 9259 1, 9260 1, 9261 1 9262 ], 9263 sw: [ 9264 0, 9265 1, 9266 -1, 9267 1 9268 ] 9269 }; 9270 const isTouchEvent = evt => evt.type === 'longpress' || evt.type.indexOf('touch') === 0; 9271 const ControlSelection = (selection, editor) => { 9272 const dom = editor.dom; 9273 const editableDoc = editor.getDoc(); 9274 const rootDocument = document; 9275 const rootElement = editor.getBody(); 9276 let selectedElm, selectedElmGhost, resizeHelper, selectedHandle, resizeBackdrop; 9277 let startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted; 9278 let width; 9279 let height; 9280 let startScrollWidth; 9281 let startScrollHeight; 9282 const isImage = elm => isNonNullable(elm) && (isImg(elm) || dom.is(elm, 'figure.image')); 9283 const isMedia = elm => isMedia$2(elm) || dom.hasClass(elm, 'mce-preview-object'); 9284 const isEventOnImageOutsideRange = (evt, range) => { 9285 if (isTouchEvent(evt)) { 9286 const touch = evt.touches[0]; 9287 return isImage(evt.target) && !isXYWithinRange(touch.clientX, touch.clientY, range); 9288 } else { 9289 return isImage(evt.target) && !isXYWithinRange(evt.clientX, evt.clientY, range); 9290 } 9291 }; 9292 const contextMenuSelectImage = evt => { 9293 const target = evt.target; 9294 if (isEventOnImageOutsideRange(evt, editor.selection.getRng()) && !evt.isDefaultPrevented()) { 9295 editor.selection.select(target); 9296 } 9297 }; 9298 const getResizeTargets = elm => { 9299 if (dom.hasClass(elm, 'mce-preview-object') && isNonNullable(elm.firstElementChild)) { 9300 return [ 9301 elm, 9302 elm.firstElementChild 9303 ]; 9304 } else if (dom.is(elm, 'figure.image')) { 9305 return [elm.querySelector('img')]; 9306 } else { 9307 return [elm]; 9308 } 9309 }; 9310 const isResizable = elm => { 9311 const selector = getObjectResizing(editor); 9312 if (!selector) { 9313 return false; 9314 } 9315 if (elm.getAttribute('data-mce-resize') === 'false') { 9316 return false; 9317 } 9318 if (elm === editor.getBody()) { 9319 return false; 9320 } 9321 if (dom.hasClass(elm, 'mce-preview-object') && isNonNullable(elm.firstElementChild)) { 9322 return is$1(SugarElement.fromDom(elm.firstElementChild), selector); 9323 } else { 9324 return is$1(SugarElement.fromDom(elm), selector); 9325 } 9326 }; 9327 const createGhostElement = elm => { 9328 if (isMedia(elm)) { 9329 return dom.create('img', { src: Env.transparentSrc }); 9330 } else { 9331 return elm.cloneNode(true); 9332 } 9333 }; 9334 const setSizeProp = (element, name, value) => { 9335 if (isNonNullable(value)) { 9336 const targets = getResizeTargets(element); 9337 each$e(targets, target => { 9338 if (target.style[name] || !editor.schema.isValid(target.nodeName.toLowerCase(), name)) { 9339 dom.setStyle(target, name, value); 9340 } else { 9341 dom.setAttrib(target, name, '' + value); 9342 } 9343 }); 9344 } 9345 }; 9346 const setGhostElmSize = (ghostElm, width, height) => { 9347 setSizeProp(ghostElm, 'width', width); 9348 setSizeProp(ghostElm, 'height', height); 9349 }; 9350 const resizeGhostElement = e => { 9351 let deltaX, deltaY, proportional; 9352 let resizeHelperX, resizeHelperY; 9353 deltaX = e.screenX - startX; 9354 deltaY = e.screenY - startY; 9355 width = deltaX * selectedHandle[2] + startW; 9356 height = deltaY * selectedHandle[3] + startH; 9357 width = width < 5 ? 5 : width; 9358 height = height < 5 ? 5 : height; 9359 if ((isImage(selectedElm) || isMedia(selectedElm)) && getResizeImgProportional(editor) !== false) { 9360 proportional = !VK.modifierPressed(e); 9361 } else { 9362 proportional = VK.modifierPressed(e); 9363 } 9364 if (proportional) { 9365 if (abs(deltaX) > abs(deltaY)) { 9366 height = round$1(width * ratio); 9367 width = round$1(height / ratio); 9368 } else { 9369 width = round$1(height / ratio); 9370 height = round$1(width * ratio); 9371 } 9372 } 9373 setGhostElmSize(selectedElmGhost, width, height); 9374 resizeHelperX = selectedHandle.startPos.x + deltaX; 9375 resizeHelperY = selectedHandle.startPos.y + deltaY; 9376 resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0; 9377 resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0; 9378 dom.setStyles(resizeHelper, { 9379 left: resizeHelperX, 9380 top: resizeHelperY, 9381 display: 'block' 9382 }); 9383 resizeHelper.innerHTML = width + ' × ' + height; 9384 if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) { 9385 dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width)); 9386 } 9387 if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) { 9388 dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height)); 9389 } 9390 deltaX = rootElement.scrollWidth - startScrollWidth; 9391 deltaY = rootElement.scrollHeight - startScrollHeight; 9392 if (deltaX + deltaY !== 0) { 9393 dom.setStyles(resizeHelper, { 9394 left: resizeHelperX - deltaX, 9395 top: resizeHelperY - deltaY 9396 }); 9397 } 9398 if (!resizeStarted) { 9399 fireObjectResizeStart(editor, selectedElm, startW, startH, 'corner-' + selectedHandle.name); 9400 resizeStarted = true; 9401 } 9402 }; 9403 const endGhostResize = () => { 9404 const wasResizeStarted = resizeStarted; 9405 resizeStarted = false; 9406 if (wasResizeStarted) { 9407 setSizeProp(selectedElm, 'width', width); 9408 setSizeProp(selectedElm, 'height', height); 9409 } 9410 dom.unbind(editableDoc, 'mousemove', resizeGhostElement); 9411 dom.unbind(editableDoc, 'mouseup', endGhostResize); 9412 if (rootDocument !== editableDoc) { 9413 dom.unbind(rootDocument, 'mousemove', resizeGhostElement); 9414 dom.unbind(rootDocument, 'mouseup', endGhostResize); 9415 } 9416 dom.remove(selectedElmGhost); 9417 dom.remove(resizeHelper); 9418 dom.remove(resizeBackdrop); 9419 showResizeRect(selectedElm); 9420 if (wasResizeStarted) { 9421 fireObjectResized(editor, selectedElm, width, height, 'corner-' + selectedHandle.name); 9422 dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style')); 9423 } 9424 editor.nodeChanged(); 9425 }; 9426 const showResizeRect = targetElm => { 9427 unbindResizeHandleEvents(); 9428 const position = dom.getPos(targetElm, rootElement); 9429 const selectedElmX = position.x; 9430 const selectedElmY = position.y; 9431 const rect = targetElm.getBoundingClientRect(); 9432 const targetWidth = rect.width || rect.right - rect.left; 9433 const targetHeight = rect.height || rect.bottom - rect.top; 9434 if (selectedElm !== targetElm) { 9435 hideResizeRect(); 9436 selectedElm = targetElm; 9437 width = height = 0; 9438 } 9439 const e = editor.dispatch('ObjectSelected', { target: targetElm }); 9440 if (isResizable(targetElm) && !e.isDefaultPrevented()) { 9441 each$d(resizeHandles, (handle, name) => { 9442 const startDrag = e => { 9443 const target = getResizeTargets(selectedElm)[0]; 9444 startX = e.screenX; 9445 startY = e.screenY; 9446 startW = target.clientWidth; 9447 startH = target.clientHeight; 9448 ratio = startH / startW; 9449 selectedHandle = handle; 9450 selectedHandle.name = name; 9451 selectedHandle.startPos = { 9452 x: targetWidth * handle[0] + selectedElmX, 9453 y: targetHeight * handle[1] + selectedElmY 9454 }; 9455 startScrollWidth = rootElement.scrollWidth; 9456 startScrollHeight = rootElement.scrollHeight; 9457 resizeBackdrop = dom.add(rootElement, 'div', { 9458 'class': 'mce-resize-backdrop', 9459 'data-mce-bogus': 'all' 9460 }); 9461 dom.setStyles(resizeBackdrop, { 9462 position: 'fixed', 9463 left: '0', 9464 top: '0', 9465 width: '100%', 9466 height: '100%' 9467 }); 9468 selectedElmGhost = createGhostElement(selectedElm); 9469 dom.addClass(selectedElmGhost, 'mce-clonedresizable'); 9470 dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all'); 9471 selectedElmGhost.contentEditable = 'false'; 9472 dom.setStyles(selectedElmGhost, { 9473 left: selectedElmX, 9474 top: selectedElmY, 9475 margin: 0 9476 }); 9477 setGhostElmSize(selectedElmGhost, targetWidth, targetHeight); 9478 selectedElmGhost.removeAttribute(elementSelectionAttr); 9479 rootElement.appendChild(selectedElmGhost); 9480 dom.bind(editableDoc, 'mousemove', resizeGhostElement); 9481 dom.bind(editableDoc, 'mouseup', endGhostResize); 9482 if (rootDocument !== editableDoc) { 9483 dom.bind(rootDocument, 'mousemove', resizeGhostElement); 9484 dom.bind(rootDocument, 'mouseup', endGhostResize); 9485 } 9486 resizeHelper = dom.add(rootElement, 'div', { 9487 'class': 'mce-resize-helper', 9488 'data-mce-bogus': 'all' 9489 }, startW + ' × ' + startH); 9490 }; 9491 let handleElm = dom.get('mceResizeHandle' + name); 9492 if (handleElm) { 9493 dom.remove(handleElm); 9494 } 9495 handleElm = dom.add(rootElement, 'div', { 9496 'id': 'mceResizeHandle' + name, 9497 'data-mce-bogus': 'all', 9498 'class': 'mce-resizehandle', 9499 'unselectable': true, 9500 'style': 'cursor:' + name + '-resize; margin:0; padding:0' 9501 }); 9502 dom.bind(handleElm, 'mousedown', e => { 9503 e.stopImmediatePropagation(); 9504 e.preventDefault(); 9505 startDrag(e); 9506 }); 9507 handle.elm = handleElm; 9508 dom.setStyles(handleElm, { 9509 left: targetWidth * handle[0] + selectedElmX - handleElm.offsetWidth / 2, 9510 top: targetHeight * handle[1] + selectedElmY - handleElm.offsetHeight / 2 9511 }); 9512 }); 9513 } else { 9514 hideResizeRect(false); 9515 } 9516 }; 9517 const throttledShowResizeRect = first$1(showResizeRect, 0); 9518 const hideResizeRect = (removeSelected = true) => { 9519 throttledShowResizeRect.cancel(); 9520 unbindResizeHandleEvents(); 9521 if (selectedElm && removeSelected) { 9522 selectedElm.removeAttribute(elementSelectionAttr); 9523 } 9524 each$d(resizeHandles, (value, name) => { 9525 const handleElm = dom.get('mceResizeHandle' + name); 9526 if (handleElm) { 9527 dom.unbind(handleElm); 9528 dom.remove(handleElm); 9529 } 9530 }); 9531 }; 9532 const isChildOrEqual = (node, parent) => dom.isChildOf(node, parent); 9533 const updateResizeRect = e => { 9534 if (resizeStarted || editor.removed || editor.composing) { 9535 return; 9536 } 9537 const targetElm = e.type === 'mousedown' ? e.target : selection.getNode(); 9538 const controlElm = closest$3(SugarElement.fromDom(targetElm), controlElmSelector).map(e => e.dom).filter(e => dom.isEditable(e.parentElement)).getOrUndefined(); 9539 const selectedValue = isNonNullable(controlElm) ? dom.getAttrib(controlElm, elementSelectionAttr, '1') : '1'; 9540 each$e(dom.select(`img[${ elementSelectionAttr }],hr[${ elementSelectionAttr }]`), img => { 9541 img.removeAttribute(elementSelectionAttr); 9542 }); 9543 if (isNonNullable(controlElm) && isChildOrEqual(controlElm, rootElement) && editor.hasFocus()) { 9544 disableGeckoResize(); 9545 const startElm = selection.getStart(true); 9546 if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) { 9547 dom.setAttrib(controlElm, elementSelectionAttr, selectedValue); 9548 throttledShowResizeRect.throttle(controlElm); 9549 return; 9550 } 9551 } 9552 hideResizeRect(); 9553 }; 9554 const unbindResizeHandleEvents = () => { 9555 each$d(resizeHandles, handle => { 9556 if (handle.elm) { 9557 dom.unbind(handle.elm); 9558 delete handle.elm; 9559 } 9560 }); 9561 }; 9562 const disableGeckoResize = () => { 9563 try { 9564 editor.getDoc().execCommand('enableObjectResizing', false, 'false'); 9565 } catch (ex) { 9566 } 9567 }; 9568 editor.on('init', () => { 9569 disableGeckoResize(); 9570 editor.on('NodeChange ResizeEditor ResizeWindow ResizeContent drop', updateResizeRect); 9571 editor.on('keyup compositionend', e => { 9572 if (selectedElm && selectedElm.nodeName === 'TABLE') { 9573 updateResizeRect(e); 9574 } 9575 }); 9576 editor.on('hide blur', hideResizeRect); 9577 editor.on('contextmenu longpress', contextMenuSelectImage, true); 9578 }); 9579 editor.on('remove', unbindResizeHandleEvents); 9580 const destroy = () => { 9581 throttledShowResizeRect.cancel(); 9582 selectedElm = selectedElmGhost = resizeBackdrop = null; 9583 }; 9584 return { 9585 isResizable, 9586 showResizeRect, 9587 hideResizeRect, 9588 updateResizeRect, 9589 destroy 9590 }; 9591 }; 9592 9593 const setStart = (rng, situ) => { 9594 situ.fold(e => { 9595 rng.setStartBefore(e.dom); 9596 }, (e, o) => { 9597 rng.setStart(e.dom, o); 9598 }, e => { 9599 rng.setStartAfter(e.dom); 9600 }); 9601 }; 9602 const setFinish = (rng, situ) => { 9603 situ.fold(e => { 9604 rng.setEndBefore(e.dom); 9605 }, (e, o) => { 9606 rng.setEnd(e.dom, o); 9607 }, e => { 9608 rng.setEndAfter(e.dom); 9609 }); 9610 }; 9611 const relativeToNative = (win, startSitu, finishSitu) => { 9612 const range = win.document.createRange(); 9613 setStart(range, startSitu); 9614 setFinish(range, finishSitu); 9615 return range; 9616 }; 9617 const exactToNative = (win, start, soffset, finish, foffset) => { 9618 const rng = win.document.createRange(); 9619 rng.setStart(start.dom, soffset); 9620 rng.setEnd(finish.dom, foffset); 9621 return rng; 9622 }; 9623 9624 const adt$3 = Adt.generate([ 9625 { 9626 ltr: [ 9627 'start', 9628 'soffset', 9629 'finish', 9630 'foffset' 9631 ] 9632 }, 9633 { 9634 rtl: [ 9635 'start', 9636 'soffset', 9637 'finish', 9638 'foffset' 9639 ] 9640 } 9641 ]); 9642 const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset); 9643 const getRanges = (win, selection) => selection.match({ 9644 domRange: rng => { 9645 return { 9646 ltr: constant(rng), 9647 rtl: Optional.none 9648 }; 9649 }, 9650 relative: (startSitu, finishSitu) => { 9651 return { 9652 ltr: cached(() => relativeToNative(win, startSitu, finishSitu)), 9653 rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu))) 9654 }; 9655 }, 9656 exact: (start, soffset, finish, foffset) => { 9657 return { 9658 ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)), 9659 rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, soffset))) 9660 }; 9661 } 9662 }); 9663 const doDiagnose = (win, ranges) => { 9664 const rng = ranges.ltr(); 9665 if (rng.collapsed) { 9666 const reversed = ranges.rtl().filter(rev => rev.collapsed === false); 9667 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)); 9668 } else { 9669 return fromRange(win, adt$3.ltr, rng); 9670 } 9671 }; 9672 const diagnose = (win, selection) => { 9673 const ranges = getRanges(win, selection); 9674 return doDiagnose(win, ranges); 9675 }; 9676 adt$3.ltr; 9677 adt$3.rtl; 9678 9679 const create$a = (start, soffset, finish, foffset) => ({ 9680 start, 9681 soffset, 9682 finish, 9683 foffset 9684 }); 9685 const SimRange = { create: create$a }; 9686 9687 const caretPositionFromPoint = (doc, x, y) => { 9688 var _a, _b; 9689 return Optional.from((_b = (_a = doc.dom).caretPositionFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)).bind(pos => { 9690 if (pos.offsetNode === null) { 9691 return Optional.none(); 9692 } 9693 const r = doc.dom.createRange(); 9694 r.setStart(pos.offsetNode, pos.offset); 9695 r.collapse(); 9696 return Optional.some(r); 9697 }); 9698 }; 9699 const caretRangeFromPoint = (doc, x, y) => { 9700 var _a, _b; 9701 return Optional.from((_b = (_a = doc.dom).caretRangeFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)); 9702 }; 9703 const availableSearch = (() => { 9704 if (document.caretPositionFromPoint) { 9705 return caretPositionFromPoint; 9706 } else if (document.caretRangeFromPoint) { 9707 return caretRangeFromPoint; 9708 } else { 9709 return Optional.none; 9710 } 9711 })(); 9712 const fromPoint$1 = (win, x, y) => { 9713 const doc = SugarElement.fromDom(win.document); 9714 return availableSearch(doc, x, y).map(rng => SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset)); 9715 }; 9716 9717 const adt$2 = Adt.generate([ 9718 { before: ['element'] }, 9719 { 9720 on: [ 9721 'element', 9722 'offset' 9723 ] 9724 }, 9725 { after: ['element'] } 9726 ]); 9727 const cata = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter); 9728 const getStart$2 = situ => situ.fold(identity, identity, identity); 9729 const before$1 = adt$2.before; 9730 const on = adt$2.on; 9731 const after$1 = adt$2.after; 9732 const Situ = { 9733 before: before$1, 9734 on, 9735 after: after$1, 9736 cata, 9737 getStart: getStart$2 9738 }; 9739 9740 const adt$1 = Adt.generate([ 9741 { domRange: ['rng'] }, 9742 { 9743 relative: [ 9744 'startSitu', 9745 'finishSitu' 9746 ] 9747 }, 9748 { 9749 exact: [ 9750 'start', 9751 'soffset', 9752 'finish', 9753 'foffset' 9754 ] 9755 } 9756 ]); 9757 const exactFromRange = simRange => adt$1.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset); 9758 const getStart$1 = selection => selection.match({ 9759 domRange: rng => SugarElement.fromDom(rng.startContainer), 9760 relative: (startSitu, _finishSitu) => Situ.getStart(startSitu), 9761 exact: (start, _soffset, _finish, _foffset) => start 9762 }); 9763 const domRange = adt$1.domRange; 9764 const relative = adt$1.relative; 9765 const exact = adt$1.exact; 9766 const getWin = selection => { 9767 const start = getStart$1(selection); 9768 return defaultView(start); 9769 }; 9770 const range = SimRange.create; 9771 const SimSelection = { 9772 domRange, 9773 relative, 9774 exact, 9775 exactFromRange, 9776 getWin, 9777 range 9778 }; 9779 9780 const beforeSpecial = (element, offset) => { 9781 const name$1 = name(element); 9782 if ('input' === name$1) { 9783 return Situ.after(element); 9784 } else if (!contains$2([ 9785 'br', 9786 'img' 9787 ], name$1)) { 9788 return Situ.on(element, offset); 9789 } else { 9790 return offset === 0 ? Situ.before(element) : Situ.after(element); 9791 } 9792 }; 9793 const preprocessRelative = (startSitu, finishSitu) => { 9794 const start = startSitu.fold(Situ.before, beforeSpecial, Situ.after); 9795 const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after); 9796 return SimSelection.relative(start, finish); 9797 }; 9798 const preprocessExact = (start, soffset, finish, foffset) => { 9799 const startSitu = beforeSpecial(start, soffset); 9800 const finishSitu = beforeSpecial(finish, foffset); 9801 return SimSelection.relative(startSitu, finishSitu); 9802 }; 9803 const preprocess = selection => selection.match({ 9804 domRange: rng => { 9805 const start = SugarElement.fromDom(rng.startContainer); 9806 const finish = SugarElement.fromDom(rng.endContainer); 9807 return preprocessExact(start, rng.startOffset, finish, rng.endOffset); 9808 }, 9809 relative: preprocessRelative, 9810 exact: preprocessExact 9811 }); 9812 9813 const fromElements = (elements, scope) => { 9814 const doc = scope || document; 9815 const fragment = doc.createDocumentFragment(); 9816 each$e(elements, element => { 9817 fragment.appendChild(element.dom); 9818 }); 9819 return SugarElement.fromDom(fragment); 9820 }; 9821 9822 const toNative = selection => { 9823 const win = SimSelection.getWin(selection).dom; 9824 const getDomRange = (start, soffset, finish, foffset) => exactToNative(win, start, soffset, finish, foffset); 9825 const filtered = preprocess(selection); 9826 return diagnose(win, filtered).match({ 9827 ltr: getDomRange, 9828 rtl: getDomRange 9829 }); 9830 }; 9831 const getAtPoint = (win, x, y) => fromPoint$1(win, x, y); 9832 9833 const fromPoint = (clientX, clientY, doc) => { 9834 const win = defaultView(SugarElement.fromDom(doc)); 9835 return getAtPoint(win.dom, clientX, clientY).map(simRange => { 9836 const rng = doc.createRange(); 9837 rng.setStart(simRange.start.dom, simRange.soffset); 9838 rng.setEnd(simRange.finish.dom, simRange.foffset); 9839 return rng; 9840 }).getOrUndefined(); 9841 }; 9842 9843 const isEq$4 = (rng1, rng2) => { 9844 return isNonNullable(rng1) && isNonNullable(rng2) && (rng1.startContainer === rng2.startContainer && rng1.startOffset === rng2.startOffset) && (rng1.endContainer === rng2.endContainer && rng1.endOffset === rng2.endOffset); 9845 }; 9846 9847 const findParent = (node, rootNode, predicate) => { 9848 let currentNode = node; 9849 while (currentNode && currentNode !== rootNode) { 9850 if (predicate(currentNode)) { 9851 return currentNode; 9852 } 9853 currentNode = currentNode.parentNode; 9854 } 9855 return null; 9856 }; 9857 const hasParent$1 = (node, rootNode, predicate) => findParent(node, rootNode, predicate) !== null; 9858 const hasParentWithName = (node, rootNode, name) => hasParent$1(node, rootNode, node => node.nodeName === name); 9859 const isCeFalseCaretContainer = (node, rootNode) => isCaretContainer$2(node) && !hasParent$1(node, rootNode, isCaretNode); 9860 const hasBrBeforeAfter = (dom, node, left) => { 9861 const parentNode = node.parentNode; 9862 if (parentNode) { 9863 const walker = new DomTreeWalker(node, dom.getParent(parentNode, dom.isBlock) || dom.getRoot()); 9864 let currentNode; 9865 while (currentNode = walker[left ? 'prev' : 'next']()) { 9866 if (isBr$6(currentNode)) { 9867 return true; 9868 } 9869 } 9870 } 9871 return false; 9872 }; 9873 const isPrevNode = (node, name) => { 9874 var _a; 9875 return ((_a = node.previousSibling) === null || _a === void 0 ? void 0 : _a.nodeName) === name; 9876 }; 9877 const hasContentEditableFalseParent = (root, node) => { 9878 let currentNode = node; 9879 while (currentNode && currentNode !== root) { 9880 if (isContentEditableFalse$b(currentNode)) { 9881 return true; 9882 } 9883 currentNode = currentNode.parentNode; 9884 } 9885 return false; 9886 }; 9887 const findTextNodeRelative = (dom, isAfterNode, collapsed, left, startNode) => { 9888 const body = dom.getRoot(); 9889 const nonEmptyElementsMap = dom.schema.getNonEmptyElements(); 9890 const parentNode = startNode.parentNode; 9891 let lastInlineElement; 9892 let node; 9893 if (!parentNode) { 9894 return Optional.none(); 9895 } 9896 const parentBlockContainer = dom.getParent(parentNode, dom.isBlock) || body; 9897 if (left && isBr$6(startNode) && isAfterNode && dom.isEmpty(parentBlockContainer)) { 9898 return Optional.some(CaretPosition(parentNode, dom.nodeIndex(startNode))); 9899 } 9900 const walker = new DomTreeWalker(startNode, parentBlockContainer); 9901 while (node = walker[left ? 'prev' : 'next']()) { 9902 if (dom.getContentEditableParent(node) === 'false' || isCeFalseCaretContainer(node, body)) { 9903 return Optional.none(); 9904 } 9905 if (isText$a(node) && node.data.length > 0) { 9906 if (!hasParentWithName(node, body, 'A')) { 9907 return Optional.some(CaretPosition(node, left ? node.data.length : 0)); 9908 } 9909 return Optional.none(); 9910 } 9911 if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) { 9912 return Optional.none(); 9913 } 9914 lastInlineElement = node; 9915 } 9916 if (isComment(lastInlineElement)) { 9917 return Optional.none(); 9918 } 9919 if (collapsed && lastInlineElement) { 9920 return Optional.some(CaretPosition(lastInlineElement, 0)); 9921 } 9922 return Optional.none(); 9923 }; 9924 const normalizeEndPoint = (dom, collapsed, start, rng) => { 9925 const body = dom.getRoot(); 9926 let node; 9927 let normalized = false; 9928 let container = start ? rng.startContainer : rng.endContainer; 9929 let offset = start ? rng.startOffset : rng.endOffset; 9930 const isAfterNode = isElement$6(container) && offset === container.childNodes.length; 9931 const nonEmptyElementsMap = dom.schema.getNonEmptyElements(); 9932 let directionLeft = start; 9933 if (isCaretContainer$2(container)) { 9934 return Optional.none(); 9935 } 9936 if (isElement$6(container) && offset > container.childNodes.length - 1) { 9937 directionLeft = false; 9938 } 9939 if (isDocument$1(container)) { 9940 container = body; 9941 offset = 0; 9942 } 9943 if (container === body) { 9944 if (directionLeft) { 9945 node = container.childNodes[offset > 0 ? offset - 1 : 0]; 9946 if (node) { 9947 if (isCaretContainer$2(node)) { 9948 return Optional.none(); 9949 } 9950 if (nonEmptyElementsMap[node.nodeName] || isTable$2(node)) { 9951 return Optional.none(); 9952 } 9953 } 9954 } 9955 if (container.hasChildNodes()) { 9956 offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1); 9957 container = container.childNodes[offset]; 9958 offset = isText$a(container) && isAfterNode ? container.data.length : 0; 9959 if (!collapsed && container === body.lastChild && isTable$2(container)) { 9960 return Optional.none(); 9961 } 9962 if (hasContentEditableFalseParent(body, container) || isCaretContainer$2(container)) { 9963 return Optional.none(); 9964 } 9965 if (container.hasChildNodes() && !isTable$2(container)) { 9966 node = container; 9967 const walker = new DomTreeWalker(container, body); 9968 do { 9969 if (isContentEditableFalse$b(node) || isCaretContainer$2(node)) { 9970 normalized = false; 9971 break; 9972 } 9973 if (isText$a(node) && node.data.length > 0) { 9974 offset = directionLeft ? 0 : node.data.length; 9975 container = node; 9976 normalized = true; 9977 break; 9978 } 9979 if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCellOrCaption(node)) { 9980 offset = dom.nodeIndex(node); 9981 container = node.parentNode; 9982 if (!directionLeft) { 9983 offset++; 9984 } 9985 normalized = true; 9986 break; 9987 } 9988 } while (node = directionLeft ? walker.next() : walker.prev()); 9989 } 9990 } 9991 } 9992 if (collapsed) { 9993 if (isText$a(container) && offset === 0) { 9994 findTextNodeRelative(dom, isAfterNode, collapsed, true, container).each(pos => { 9995 container = pos.container(); 9996 offset = pos.offset(); 9997 normalized = true; 9998 }); 9999 } 10000 if (isElement$6(container)) { 10001 node = container.childNodes[offset]; 10002 if (!node) { 10003 node = container.childNodes[offset - 1]; 10004 } 10005 if (node && isBr$6(node) && !isPrevNode(node, 'A') && !hasBrBeforeAfter(dom, node, false) && !hasBrBeforeAfter(dom, node, true)) { 10006 findTextNodeRelative(dom, isAfterNode, collapsed, true, node).each(pos => { 10007 container = pos.container(); 10008 offset = pos.offset(); 10009 normalized = true; 10010 }); 10011 } 10012 } 10013 } 10014 if (directionLeft && !collapsed && isText$a(container) && offset === container.data.length) { 10015 findTextNodeRelative(dom, isAfterNode, collapsed, false, container).each(pos => { 10016 container = pos.container(); 10017 offset = pos.offset(); 10018 normalized = true; 10019 }); 10020 } 10021 return normalized && container ? Optional.some(CaretPosition(container, offset)) : Optional.none(); 10022 }; 10023 const normalize$2 = (dom, rng) => { 10024 const collapsed = rng.collapsed, normRng = rng.cloneRange(); 10025 const startPos = CaretPosition.fromRangeStart(rng); 10026 normalizeEndPoint(dom, collapsed, true, normRng).each(pos => { 10027 if (!collapsed || !CaretPosition.isAbove(startPos, pos)) { 10028 normRng.setStart(pos.container(), pos.offset()); 10029 } 10030 }); 10031 if (!collapsed) { 10032 normalizeEndPoint(dom, collapsed, false, normRng).each(pos => { 10033 normRng.setEnd(pos.container(), pos.offset()); 10034 }); 10035 } 10036 if (collapsed) { 10037 normRng.collapse(true); 10038 } 10039 return isEq$4(rng, normRng) ? Optional.none() : Optional.some(normRng); 10040 }; 10041 10042 const splitText = (node, offset) => { 10043 return node.splitText(offset); 10044 }; 10045 const split = rng => { 10046 let startContainer = rng.startContainer, startOffset = rng.startOffset, endContainer = rng.endContainer, endOffset = rng.endOffset; 10047 if (startContainer === endContainer && isText$a(startContainer)) { 10048 if (startOffset > 0 && startOffset < startContainer.data.length) { 10049 endContainer = splitText(startContainer, startOffset); 10050 startContainer = endContainer.previousSibling; 10051 if (endOffset > startOffset) { 10052 endOffset = endOffset - startOffset; 10053 const newContainer = splitText(endContainer, endOffset).previousSibling; 10054 startContainer = endContainer = newContainer; 10055 endOffset = newContainer.data.length; 10056 startOffset = 0; 10057 } else { 10058 endOffset = 0; 10059 } 10060 } 10061 } else { 10062 if (isText$a(startContainer) && startOffset > 0 && startOffset < startContainer.data.length) { 10063 startContainer = splitText(startContainer, startOffset); 10064 startOffset = 0; 10065 } 10066 if (isText$a(endContainer) && endOffset > 0 && endOffset < endContainer.data.length) { 10067 const newContainer = splitText(endContainer, endOffset).previousSibling; 10068 endContainer = newContainer; 10069 endOffset = newContainer.data.length; 10070 } 10071 } 10072 return { 10073 startContainer, 10074 startOffset, 10075 endContainer, 10076 endOffset 10077 }; 10078 }; 10079 10080 const RangeUtils = dom => { 10081 const walk = (rng, callback) => { 10082 return walk$3(dom, rng, callback); 10083 }; 10084 const split$1 = split; 10085 const normalize = rng => { 10086 return normalize$2(dom, rng).fold(never, normalizedRng => { 10087 rng.setStart(normalizedRng.startContainer, normalizedRng.startOffset); 10088 rng.setEnd(normalizedRng.endContainer, normalizedRng.endOffset); 10089 return true; 10090 }); 10091 }; 10092 const expand = (rng, options = { type: 'word' }) => { 10093 if (options.type === 'word') { 10094 const rangeLike = expandRng(dom, rng, [{ inline: 'span' }]); 10095 const newRange = dom.createRng(); 10096 newRange.setStart(rangeLike.startContainer, rangeLike.startOffset); 10097 newRange.setEnd(rangeLike.endContainer, rangeLike.endOffset); 10098 return newRange; 10099 } 10100 return rng; 10101 }; 10102 return { 10103 walk, 10104 split: split$1, 10105 expand, 10106 normalize 10107 }; 10108 }; 10109 RangeUtils.compareRanges = isEq$4; 10110 RangeUtils.getCaretRangeFromPoint = fromPoint; 10111 RangeUtils.getSelectedNode = getSelectedNode; 10112 RangeUtils.getNode = getNode$1; 10113 10114 const Dimension = (name, getOffset) => { 10115 const set = (element, h) => { 10116 if (!isNumber(h) && !h.match(/^[0-9]+$/)) { 10117 throw new Error(name + '.set accepts only positive integer values. Value was ' + h); 10118 } 10119 const dom = element.dom; 10120 if (isSupported(dom)) { 10121 dom.style[name] = h + 'px'; 10122 } 10123 }; 10124 const get = element => { 10125 const r = getOffset(element); 10126 if (r <= 0 || r === null) { 10127 const css = get$7(element, name); 10128 return parseFloat(css) || 0; 10129 } 10130 return r; 10131 }; 10132 const getOuter = get; 10133 const aggregate = (element, properties) => foldl(properties, (acc, property) => { 10134 const val = get$7(element, property); 10135 const value = val === undefined ? 0 : parseInt(val, 10); 10136 return isNaN(value) ? acc : acc + value; 10137 }, 0); 10138 const max = (element, value, properties) => { 10139 const cumulativeInclusions = aggregate(element, properties); 10140 const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0; 10141 return absoluteMax; 10142 }; 10143 return { 10144 set, 10145 get, 10146 getOuter, 10147 aggregate, 10148 max 10149 }; 10150 }; 10151 10152 const api = Dimension('height', element => { 10153 const dom = element.dom; 10154 return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight; 10155 }); 10156 const get$2 = element => api.get(element); 10157 10158 const getDocument = () => SugarElement.fromDom(document); 10159 10160 const walkUp = (navigation, doc) => { 10161 const frame = navigation.view(doc); 10162 return frame.fold(constant([]), f => { 10163 const parent = navigation.owner(f); 10164 const rest = walkUp(navigation, parent); 10165 return [f].concat(rest); 10166 }); 10167 }; 10168 const pathTo = (element, navigation) => { 10169 const d = navigation.owner(element); 10170 return walkUp(navigation, d); 10171 }; 10172 10173 const view = doc => { 10174 var _a; 10175 const element = doc.dom === document ? Optional.none() : Optional.from((_a = doc.dom.defaultView) === null || _a === void 0 ? void 0 : _a.frameElement); 10176 return element.map(SugarElement.fromDom); 10177 }; 10178 const owner = element => documentOrOwner(element); 10179 10180 var Navigation = /*#__PURE__*/Object.freeze({ 10181 __proto__: null, 10182 view: view, 10183 owner: owner 10184 }); 10185 10186 const find = element => { 10187 const doc = getDocument(); 10188 const scroll = get$5(doc); 10189 const frames = pathTo(element, Navigation); 10190 const offset = viewport(element); 10191 const r = foldr(frames, (b, a) => { 10192 const loc = viewport(a); 10193 return { 10194 left: b.left + loc.left, 10195 top: b.top + loc.top 10196 }; 10197 }, { 10198 left: 0, 10199 top: 0 10200 }); 10201 return SugarPosition(r.left + offset.left + scroll.left, r.top + offset.top + scroll.top); 10202 }; 10203 10204 const excludeFromDescend = element => name(element) === 'textarea'; 10205 const fireScrollIntoViewEvent = (editor, data) => { 10206 const scrollEvent = editor.dispatch('ScrollIntoView', data); 10207 return scrollEvent.isDefaultPrevented(); 10208 }; 10209 const fireAfterScrollIntoViewEvent = (editor, data) => { 10210 editor.dispatch('AfterScrollIntoView', data); 10211 }; 10212 const descend = (element, offset) => { 10213 const children = children$1(element); 10214 if (children.length === 0 || excludeFromDescend(element)) { 10215 return { 10216 element, 10217 offset 10218 }; 10219 } else if (offset < children.length && !excludeFromDescend(children[offset])) { 10220 return { 10221 element: children[offset], 10222 offset: 0 10223 }; 10224 } else { 10225 const last = children[children.length - 1]; 10226 if (excludeFromDescend(last)) { 10227 return { 10228 element, 10229 offset 10230 }; 10231 } else { 10232 if (name(last) === 'img') { 10233 return { 10234 element: last, 10235 offset: 1 10236 }; 10237 } else if (isText$b(last)) { 10238 return { 10239 element: last, 10240 offset: get$3(last).length 10241 }; 10242 } else { 10243 return { 10244 element: last, 10245 offset: children$1(last).length 10246 }; 10247 } 10248 } 10249 } 10250 }; 10251 const markerInfo = (element, cleanupFun) => { 10252 const pos = absolute(element); 10253 const height = get$2(element); 10254 return { 10255 element, 10256 bottom: pos.top + height, 10257 height, 10258 pos, 10259 cleanup: cleanupFun 10260 }; 10261 }; 10262 const createMarker$1 = (element, offset) => { 10263 const startPoint = descend(element, offset); 10264 const span = SugarElement.fromHtml('<span data-mce-bogus="all" style="display: inline-block;">' + ZWSP$1 + '</span>'); 10265 before$3(startPoint.element, span); 10266 return markerInfo(span, () => remove$5(span)); 10267 }; 10268 const elementMarker = element => markerInfo(SugarElement.fromDom(element), noop); 10269 const withMarker = (editor, f, rng, alignToTop) => { 10270 preserveWith(editor, (_s, _e) => applyWithMarker(editor, f, rng, alignToTop), rng); 10271 }; 10272 const withScrollEvents = (editor, doc, f, marker, alignToTop) => { 10273 const data = { 10274 elm: marker.element.dom, 10275 alignToTop 10276 }; 10277 if (fireScrollIntoViewEvent(editor, data)) { 10278 return; 10279 } 10280 const scrollTop = get$5(doc).top; 10281 f(doc, scrollTop, marker, alignToTop); 10282 fireAfterScrollIntoViewEvent(editor, data); 10283 }; 10284 const applyWithMarker = (editor, f, rng, alignToTop) => { 10285 const body = SugarElement.fromDom(editor.getBody()); 10286 const doc = SugarElement.fromDom(editor.getDoc()); 10287 reflow(body); 10288 const marker = createMarker$1(SugarElement.fromDom(rng.startContainer), rng.startOffset); 10289 withScrollEvents(editor, doc, f, marker, alignToTop); 10290 marker.cleanup(); 10291 }; 10292 const withElement = (editor, element, f, alignToTop) => { 10293 const doc = SugarElement.fromDom(editor.getDoc()); 10294 withScrollEvents(editor, doc, f, elementMarker(element), alignToTop); 10295 }; 10296 const preserveWith = (editor, f, rng) => { 10297 const startElement = rng.startContainer; 10298 const startOffset = rng.startOffset; 10299 const endElement = rng.endContainer; 10300 const endOffset = rng.endOffset; 10301 f(SugarElement.fromDom(startElement), SugarElement.fromDom(endElement)); 10302 const newRng = editor.dom.createRng(); 10303 newRng.setStart(startElement, startOffset); 10304 newRng.setEnd(endElement, endOffset); 10305 editor.selection.setRng(rng); 10306 }; 10307 const scrollToMarker = (marker, alignToTop) => marker.element.dom.scrollIntoView({ block: alignToTop ? 'start' : 'end' }); 10308 const intoWindowIfNeeded = (scrollTop, viewHeight, marker, alignToTop) => { 10309 const viewportBottom = viewHeight + scrollTop; 10310 const markerTop = marker.pos.top; 10311 const markerBottom = marker.bottom; 10312 const largerThanViewport = markerBottom - markerTop >= viewHeight; 10313 if (markerTop < scrollTop) { 10314 scrollToMarker(marker, alignToTop !== false); 10315 } else if (markerTop > viewportBottom) { 10316 const align = largerThanViewport ? alignToTop !== false : alignToTop === true; 10317 scrollToMarker(marker, align); 10318 } else if (markerBottom > viewportBottom && !largerThanViewport) { 10319 scrollToMarker(marker, alignToTop === true); 10320 } 10321 }; 10322 const intoWindow = (doc, scrollTop, marker, alignToTop) => { 10323 const viewHeight = defaultView(doc).dom.innerHeight; 10324 intoWindowIfNeeded(scrollTop, viewHeight, marker, alignToTop); 10325 }; 10326 const intoFrame = (doc, scrollTop, marker, alignToTop) => { 10327 const frameViewHeight = defaultView(doc).dom.innerHeight; 10328 intoWindowIfNeeded(scrollTop, frameViewHeight, marker, alignToTop); 10329 const op = find(marker.element); 10330 const viewportBounds = getBounds(window); 10331 if (op.top < viewportBounds.y) { 10332 intoView(marker.element, alignToTop !== false); 10333 } else if (op.top > viewportBounds.bottom) { 10334 intoView(marker.element, alignToTop === true); 10335 } 10336 }; 10337 const rangeIntoWindow = (editor, rng, alignToTop) => withMarker(editor, intoWindow, rng, alignToTop); 10338 const elementIntoWindow = (editor, element, alignToTop) => withElement(editor, element, intoWindow, alignToTop); 10339 const rangeIntoFrame = (editor, rng, alignToTop) => withMarker(editor, intoFrame, rng, alignToTop); 10340 const elementIntoFrame = (editor, element, alignToTop) => withElement(editor, element, intoFrame, alignToTop); 10341 const scrollElementIntoView = (editor, element, alignToTop) => { 10342 const scroller = editor.inline ? elementIntoWindow : elementIntoFrame; 10343 scroller(editor, element, alignToTop); 10344 }; 10345 const scrollRangeIntoView = (editor, rng, alignToTop) => { 10346 const scroller = editor.inline ? rangeIntoWindow : rangeIntoFrame; 10347 scroller(editor, rng, alignToTop); 10348 }; 10349 10350 const focus$1 = element => element.dom.focus(); 10351 const hasFocus$1 = element => { 10352 const root = getRootNode(element).dom; 10353 return element.dom === root.activeElement; 10354 }; 10355 const active$1 = (root = getDocument()) => Optional.from(root.dom.activeElement).map(SugarElement.fromDom); 10356 const search = element => active$1(getRootNode(element)).filter(e => element.dom.contains(e.dom)); 10357 10358 const clamp$1 = (offset, element) => { 10359 const max = isText$b(element) ? get$3(element).length : children$1(element).length + 1; 10360 if (offset > max) { 10361 return max; 10362 } else if (offset < 0) { 10363 return 0; 10364 } 10365 return offset; 10366 }; 10367 const normalizeRng = rng => SimSelection.range(rng.start, clamp$1(rng.soffset, rng.start), rng.finish, clamp$1(rng.foffset, rng.finish)); 10368 const isOrContains = (root, elm) => !isRestrictedNode(elm.dom) && (contains(root, elm) || eq(root, elm)); 10369 const isRngInRoot = root => rng => isOrContains(root, rng.start) && isOrContains(root, rng.finish); 10370 const shouldStore = editor => editor.inline || Env.browser.isFirefox(); 10371 const nativeRangeToSelectionRange = r => SimSelection.range(SugarElement.fromDom(r.startContainer), r.startOffset, SugarElement.fromDom(r.endContainer), r.endOffset); 10372 const readRange = win => { 10373 const selection = win.getSelection(); 10374 const rng = !selection || selection.rangeCount === 0 ? Optional.none() : Optional.from(selection.getRangeAt(0)); 10375 return rng.map(nativeRangeToSelectionRange); 10376 }; 10377 const getBookmark = root => { 10378 const win = defaultView(root); 10379 return readRange(win.dom).filter(isRngInRoot(root)); 10380 }; 10381 const validate = (root, bookmark) => Optional.from(bookmark).filter(isRngInRoot(root)).map(normalizeRng); 10382 const bookmarkToNativeRng = bookmark => { 10383 const rng = document.createRange(); 10384 try { 10385 rng.setStart(bookmark.start.dom, bookmark.soffset); 10386 rng.setEnd(bookmark.finish.dom, bookmark.foffset); 10387 return Optional.some(rng); 10388 } catch (_) { 10389 return Optional.none(); 10390 } 10391 }; 10392 const store = editor => { 10393 const newBookmark = shouldStore(editor) ? getBookmark(SugarElement.fromDom(editor.getBody())) : Optional.none(); 10394 editor.bookmark = newBookmark.isSome() ? newBookmark : editor.bookmark; 10395 }; 10396 const getRng = editor => { 10397 const bookmark = editor.bookmark ? editor.bookmark : Optional.none(); 10398 return bookmark.bind(x => validate(SugarElement.fromDom(editor.getBody()), x)).bind(bookmarkToNativeRng); 10399 }; 10400 const restore = editor => { 10401 getRng(editor).each(rng => editor.selection.setRng(rng)); 10402 }; 10403 10404 const isEditorUIElement$1 = elm => { 10405 const className = elm.className.toString(); 10406 return className.indexOf('tox-') !== -1 || className.indexOf('mce-') !== -1; 10407 }; 10408 const FocusManager = { isEditorUIElement: isEditorUIElement$1 }; 10409 10410 const wrappedSetTimeout = (callback, time) => { 10411 if (!isNumber(time)) { 10412 time = 0; 10413 } 10414 return setTimeout(callback, time); 10415 }; 10416 const wrappedSetInterval = (callback, time) => { 10417 if (!isNumber(time)) { 10418 time = 0; 10419 } 10420 return setInterval(callback, time); 10421 }; 10422 const Delay = { 10423 setEditorTimeout: (editor, callback, time) => { 10424 return wrappedSetTimeout(() => { 10425 if (!editor.removed) { 10426 callback(); 10427 } 10428 }, time); 10429 }, 10430 setEditorInterval: (editor, callback, time) => { 10431 const timer = wrappedSetInterval(() => { 10432 if (!editor.removed) { 10433 callback(); 10434 } else { 10435 clearInterval(timer); 10436 } 10437 }, time); 10438 return timer; 10439 } 10440 }; 10441 10442 const isManualNodeChange = e => { 10443 return e.type === 'nodechange' && e.selectionChange; 10444 }; 10445 const registerPageMouseUp = (editor, throttledStore) => { 10446 const mouseUpPage = () => { 10447 throttledStore.throttle(); 10448 }; 10449 DOMUtils.DOM.bind(document, 'mouseup', mouseUpPage); 10450 editor.on('remove', () => { 10451 DOMUtils.DOM.unbind(document, 'mouseup', mouseUpPage); 10452 }); 10453 }; 10454 const registerMouseUp = (editor, throttledStore) => { 10455 editor.on('mouseup touchend', _e => { 10456 throttledStore.throttle(); 10457 }); 10458 }; 10459 const registerEditorEvents = (editor, throttledStore) => { 10460 registerMouseUp(editor, throttledStore); 10461 editor.on('keyup NodeChange AfterSetSelectionRange', e => { 10462 if (!isManualNodeChange(e)) { 10463 store(editor); 10464 } 10465 }); 10466 }; 10467 const register$6 = editor => { 10468 const throttledStore = first$1(() => { 10469 store(editor); 10470 }, 0); 10471 editor.on('init', () => { 10472 if (editor.inline) { 10473 registerPageMouseUp(editor, throttledStore); 10474 } 10475 registerEditorEvents(editor, throttledStore); 10476 }); 10477 editor.on('remove', () => { 10478 throttledStore.cancel(); 10479 }); 10480 }; 10481 10482 let documentFocusInHandler; 10483 const DOM$9 = DOMUtils.DOM; 10484 const isEditorUIElement = elm => { 10485 return isElement$6(elm) && FocusManager.isEditorUIElement(elm); 10486 }; 10487 const isEditorContentAreaElement = elm => { 10488 const classList = elm.classList; 10489 if (classList !== undefined) { 10490 return classList.contains('tox-edit-area') || classList.contains('tox-edit-area__iframe') || classList.contains('mce-content-body'); 10491 } else { 10492 return false; 10493 } 10494 }; 10495 const isUIElement = (editor, elm) => { 10496 const customSelector = getCustomUiSelector(editor); 10497 const parent = DOM$9.getParent(elm, elm => { 10498 return isEditorUIElement(elm) || (customSelector ? editor.dom.is(elm, customSelector) : false); 10499 }); 10500 return parent !== null; 10501 }; 10502 const getActiveElement = editor => { 10503 try { 10504 const root = getRootNode(SugarElement.fromDom(editor.getElement())); 10505 return active$1(root).fold(() => document.body, x => x.dom); 10506 } catch (ex) { 10507 return document.body; 10508 } 10509 }; 10510 const registerEvents$1 = (editorManager, e) => { 10511 const editor = e.editor; 10512 register$6(editor); 10513 const toggleContentAreaOnFocus = (editor, fn) => { 10514 if (shouldHighlightOnFocus(editor) && editor.inline !== true) { 10515 const contentArea = SugarElement.fromDom(editor.getContainer()); 10516 fn(contentArea, 'tox-edit-focus'); 10517 } 10518 }; 10519 editor.on('focusin', () => { 10520 const focusedEditor = editorManager.focusedEditor; 10521 if (isEditorContentAreaElement(getActiveElement(editor))) { 10522 toggleContentAreaOnFocus(editor, add$2); 10523 } 10524 if (focusedEditor !== editor) { 10525 if (focusedEditor) { 10526 focusedEditor.dispatch('blur', { focusedEditor: editor }); 10527 } 10528 editorManager.setActive(editor); 10529 editorManager.focusedEditor = editor; 10530 editor.dispatch('focus', { blurredEditor: focusedEditor }); 10531 editor.focus(true); 10532 } 10533 }); 10534 editor.on('focusout', () => { 10535 Delay.setEditorTimeout(editor, () => { 10536 const focusedEditor = editorManager.focusedEditor; 10537 if (!isEditorContentAreaElement(getActiveElement(editor)) || focusedEditor !== editor) { 10538 toggleContentAreaOnFocus(editor, remove$7); 10539 } 10540 if (!isUIElement(editor, getActiveElement(editor)) && focusedEditor === editor) { 10541 editor.dispatch('blur', { focusedEditor: null }); 10542 editorManager.focusedEditor = null; 10543 } 10544 }); 10545 }); 10546 if (!documentFocusInHandler) { 10547 documentFocusInHandler = e => { 10548 const activeEditor = editorManager.activeEditor; 10549 if (activeEditor) { 10550 getOriginalEventTarget(e).each(target => { 10551 const elem = target; 10552 if (elem.ownerDocument === document) { 10553 if (elem !== document.body && !isUIElement(activeEditor, elem) && editorManager.focusedEditor === activeEditor) { 10554 activeEditor.dispatch('blur', { focusedEditor: null }); 10555 editorManager.focusedEditor = null; 10556 } 10557 } 10558 }); 10559 } 10560 }; 10561 DOM$9.bind(document, 'focusin', documentFocusInHandler); 10562 } 10563 }; 10564 const unregisterDocumentEvents = (editorManager, e) => { 10565 if (editorManager.focusedEditor === e.editor) { 10566 editorManager.focusedEditor = null; 10567 } 10568 if (!editorManager.activeEditor && documentFocusInHandler) { 10569 DOM$9.unbind(document, 'focusin', documentFocusInHandler); 10570 documentFocusInHandler = null; 10571 } 10572 }; 10573 const setup$v = editorManager => { 10574 editorManager.on('AddEditor', curry(registerEvents$1, editorManager)); 10575 editorManager.on('RemoveEditor', curry(unregisterDocumentEvents, editorManager)); 10576 }; 10577 10578 const getContentEditableHost = (editor, node) => editor.dom.getParent(node, node => editor.dom.getContentEditable(node) === 'true'); 10579 const getCollapsedNode = rng => rng.collapsed ? Optional.from(getNode$1(rng.startContainer, rng.startOffset)).map(SugarElement.fromDom) : Optional.none(); 10580 const getFocusInElement = (root, rng) => getCollapsedNode(rng).bind(node => { 10581 if (isTableSection(node)) { 10582 return Optional.some(node); 10583 } else if (!contains(root, node)) { 10584 return Optional.some(root); 10585 } else { 10586 return Optional.none(); 10587 } 10588 }); 10589 const normalizeSelection = (editor, rng) => { 10590 getFocusInElement(SugarElement.fromDom(editor.getBody()), rng).bind(elm => { 10591 return firstPositionIn(elm.dom); 10592 }).fold(() => { 10593 editor.selection.normalize(); 10594 }, caretPos => editor.selection.setRng(caretPos.toRange())); 10595 }; 10596 const focusBody = body => { 10597 if (body.setActive) { 10598 try { 10599 body.setActive(); 10600 } catch (ex) { 10601 body.focus(); 10602 } 10603 } else { 10604 body.focus(); 10605 } 10606 }; 10607 const hasElementFocus = elm => hasFocus$1(elm) || search(elm).isSome(); 10608 const hasIframeFocus = editor => isNonNullable(editor.iframeElement) && hasFocus$1(SugarElement.fromDom(editor.iframeElement)); 10609 const hasInlineFocus = editor => { 10610 const rawBody = editor.getBody(); 10611 return rawBody && hasElementFocus(SugarElement.fromDom(rawBody)); 10612 }; 10613 const hasUiFocus = editor => { 10614 const dos = getRootNode(SugarElement.fromDom(editor.getElement())); 10615 return active$1(dos).filter(elem => !isEditorContentAreaElement(elem.dom) && isUIElement(editor, elem.dom)).isSome(); 10616 }; 10617 const hasFocus = editor => editor.inline ? hasInlineFocus(editor) : hasIframeFocus(editor); 10618 const hasEditorOrUiFocus = editor => hasFocus(editor) || hasUiFocus(editor); 10619 const focusEditor = editor => { 10620 const selection = editor.selection; 10621 const body = editor.getBody(); 10622 let rng = selection.getRng(); 10623 editor.quirks.refreshContentEditable(); 10624 if (isNonNullable(editor.bookmark) && !hasFocus(editor)) { 10625 getRng(editor).each(bookmarkRng => { 10626 editor.selection.setRng(bookmarkRng); 10627 rng = bookmarkRng; 10628 }); 10629 } 10630 const contentEditableHost = getContentEditableHost(editor, selection.getNode()); 10631 if (contentEditableHost && editor.dom.isChildOf(contentEditableHost, body)) { 10632 focusBody(contentEditableHost); 10633 normalizeSelection(editor, rng); 10634 activateEditor(editor); 10635 return; 10636 } 10637 if (!editor.inline) { 10638 if (!Env.browser.isOpera()) { 10639 focusBody(body); 10640 } 10641 editor.getWin().focus(); 10642 } 10643 if (Env.browser.isFirefox() || editor.inline) { 10644 focusBody(body); 10645 normalizeSelection(editor, rng); 10646 } 10647 activateEditor(editor); 10648 }; 10649 const activateEditor = editor => editor.editorManager.setActive(editor); 10650 const focus = (editor, skipFocus) => { 10651 if (editor.removed) { 10652 return; 10653 } 10654 if (skipFocus) { 10655 activateEditor(editor); 10656 } else { 10657 focusEditor(editor); 10658 } 10659 }; 10660 10661 const getEndpointElement = (root, rng, start, real, resolve) => { 10662 const container = start ? rng.startContainer : rng.endContainer; 10663 const offset = start ? rng.startOffset : rng.endOffset; 10664 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); 10665 }; 10666 const getStart = (root, rng, real = false) => getEndpointElement(root, rng, true, real, (elm, offset) => Math.min(childNodesCount(elm), offset)); 10667 const getEnd$1 = (root, rng, real = false) => getEndpointElement(root, rng, false, real, (elm, offset) => offset > 0 ? offset - 1 : offset); 10668 const skipEmptyTextNodes = (node, forwards) => { 10669 const orig = node; 10670 while (node && isText$a(node) && node.length === 0) { 10671 node = forwards ? node.nextSibling : node.previousSibling; 10672 } 10673 return node || orig; 10674 }; 10675 const getNode = (root, rng) => { 10676 if (!rng) { 10677 return root; 10678 } 10679 let startContainer = rng.startContainer; 10680 let endContainer = rng.endContainer; 10681 const startOffset = rng.startOffset; 10682 const endOffset = rng.endOffset; 10683 let node = rng.commonAncestorContainer; 10684 if (!rng.collapsed) { 10685 if (startContainer === endContainer) { 10686 if (endOffset - startOffset < 2) { 10687 if (startContainer.hasChildNodes()) { 10688 node = startContainer.childNodes[startOffset]; 10689 } 10690 } 10691 } 10692 if (isText$a(startContainer) && isText$a(endContainer)) { 10693 if (startContainer.length === startOffset) { 10694 startContainer = skipEmptyTextNodes(startContainer.nextSibling, true); 10695 } else { 10696 startContainer = startContainer.parentNode; 10697 } 10698 if (endOffset === 0) { 10699 endContainer = skipEmptyTextNodes(endContainer.previousSibling, false); 10700 } else { 10701 endContainer = endContainer.parentNode; 10702 } 10703 if (startContainer && startContainer === endContainer) { 10704 node = startContainer; 10705 } 10706 } 10707 } 10708 const elm = isText$a(node) ? node.parentNode : node; 10709 return isElement$6(elm) ? elm : root; 10710 }; 10711 const getSelectedBlocks = (dom, rng, startElm, endElm) => { 10712 const selectedBlocks = []; 10713 const root = dom.getRoot(); 10714 const start = dom.getParent(startElm || getStart(root, rng, rng.collapsed), dom.isBlock); 10715 const end = dom.getParent(endElm || getEnd$1(root, rng, rng.collapsed), dom.isBlock); 10716 if (start && start !== root) { 10717 selectedBlocks.push(start); 10718 } 10719 if (start && end && start !== end) { 10720 let node; 10721 const walker = new DomTreeWalker(start, root); 10722 while ((node = walker.next()) && node !== end) { 10723 if (dom.isBlock(node)) { 10724 selectedBlocks.push(node); 10725 } 10726 } 10727 } 10728 if (end && start !== end && end !== root) { 10729 selectedBlocks.push(end); 10730 } 10731 return selectedBlocks; 10732 }; 10733 const select = (dom, node, content) => Optional.from(node).bind(node => Optional.from(node.parentNode).map(parent => { 10734 const idx = dom.nodeIndex(node); 10735 const rng = dom.createRng(); 10736 rng.setStart(parent, idx); 10737 rng.setEnd(parent, idx + 1); 10738 if (content) { 10739 moveEndPoint(dom, rng, node, true); 10740 moveEndPoint(dom, rng, node, false); 10741 } 10742 return rng; 10743 })); 10744 10745 const processRanges = (editor, ranges) => map$3(ranges, range => { 10746 const evt = editor.dispatch('GetSelectionRange', { range }); 10747 return evt.range !== range ? evt.range : range; 10748 }); 10749 10750 const getEnd = element => name(element) === 'img' ? 1 : getOption(element).fold(() => children$1(element).length, v => v.length); 10751 const isTextNodeWithCursorPosition = el => getOption(el).filter(text => text.trim().length !== 0 || text.indexOf(nbsp) > -1).isSome(); 10752 const isContentEditableFalse$5 = elem => isHTMLElement(elem) && get$9(elem, 'contenteditable') === 'false'; 10753 const elementsWithCursorPosition = [ 10754 'img', 10755 'br' 10756 ]; 10757 const isCursorPosition = elem => { 10758 const hasCursorPosition = isTextNodeWithCursorPosition(elem); 10759 return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem)) || isContentEditableFalse$5(elem); 10760 }; 10761 10762 const first = element => descendant$1(element, isCursorPosition); 10763 const last = element => descendantRtl(element, isCursorPosition); 10764 const descendantRtl = (scope, predicate) => { 10765 const descend = element => { 10766 const children = children$1(element); 10767 for (let i = children.length - 1; i >= 0; i--) { 10768 const child = children[i]; 10769 if (predicate(child)) { 10770 return Optional.some(child); 10771 } 10772 const res = descend(child); 10773 if (res.isSome()) { 10774 return res; 10775 } 10776 } 10777 return Optional.none(); 10778 }; 10779 return descend(scope); 10780 }; 10781 10782 const autocompleteSelector = '[data-mce-autocompleter]'; 10783 const create$9 = (editor, range) => { 10784 if (findIn(SugarElement.fromDom(editor.getBody())).isNone()) { 10785 const wrapper = SugarElement.fromHtml('<span data-mce-autocompleter="1" data-mce-bogus="1"></span>', editor.getDoc()); 10786 append$1(wrapper, SugarElement.fromDom(range.extractContents())); 10787 range.insertNode(wrapper.dom); 10788 parent(wrapper).each(elm => elm.dom.normalize()); 10789 last(wrapper).map(last => { 10790 editor.selection.setCursorLocation(last.dom, getEnd(last)); 10791 }); 10792 } 10793 }; 10794 const detect$1 = elm => closest$3(elm, autocompleteSelector); 10795 const findIn = elm => descendant(elm, autocompleteSelector); 10796 const remove$2 = (editor, elm) => findIn(elm).each(wrapper => { 10797 const bookmark = editor.selection.getBookmark(); 10798 unwrap(wrapper); 10799 editor.selection.moveToBookmark(bookmark); 10800 }); 10801 10802 const typeLookup = { 10803 '#text': 3, 10804 '#comment': 8, 10805 '#cdata': 4, 10806 '#pi': 7, 10807 '#doctype': 10, 10808 '#document-fragment': 11 10809 }; 10810 const walk$2 = (node, root, prev) => { 10811 const startName = prev ? 'lastChild' : 'firstChild'; 10812 const siblingName = prev ? 'prev' : 'next'; 10813 if (node[startName]) { 10814 return node[startName]; 10815 } 10816 if (node !== root) { 10817 let sibling = node[siblingName]; 10818 if (sibling) { 10819 return sibling; 10820 } 10821 for (let parent = node.parent; parent && parent !== root; parent = parent.parent) { 10822 sibling = parent[siblingName]; 10823 if (sibling) { 10824 return sibling; 10825 } 10826 } 10827 } 10828 return undefined; 10829 }; 10830 const isEmptyTextNode = node => { 10831 var _a; 10832 const text = (_a = node.value) !== null && _a !== void 0 ? _a : ''; 10833 if (!isWhitespaceText(text)) { 10834 return false; 10835 } 10836 const parentNode = node.parent; 10837 if (parentNode && (parentNode.name !== 'span' || parentNode.attr('style')) && /^[ ]+$/.test(text)) { 10838 return false; 10839 } 10840 return true; 10841 }; 10842 const isNonEmptyElement = node => { 10843 const isNamedAnchor = node.name === 'a' && !node.attr('href') && node.attr('id'); 10844 return node.attr('name') || node.attr('id') && !node.firstChild || node.attr('data-mce-bookmark') || isNamedAnchor; 10845 }; 10846 class AstNode { 10847 static create(name, attrs) { 10848 const node = new AstNode(name, typeLookup[name] || 1); 10849 if (attrs) { 10850 each$d(attrs, (value, attrName) => { 10851 node.attr(attrName, value); 10852 }); 10853 } 10854 return node; 10855 } 10856 constructor(name, type) { 10857 this.name = name; 10858 this.type = type; 10859 if (type === 1) { 10860 this.attributes = []; 10861 this.attributes.map = {}; 10862 } 10863 } 10864 replace(node) { 10865 const self = this; 10866 if (node.parent) { 10867 node.remove(); 10868 } 10869 self.insert(node, self); 10870 self.remove(); 10871 return self; 10872 } 10873 attr(name, value) { 10874 const self = this; 10875 if (!isString(name)) { 10876 if (isNonNullable(name)) { 10877 each$d(name, (value, key) => { 10878 self.attr(key, value); 10879 }); 10880 } 10881 return self; 10882 } 10883 const attrs = self.attributes; 10884 if (attrs) { 10885 if (value !== undefined) { 10886 if (value === null) { 10887 if (name in attrs.map) { 10888 delete attrs.map[name]; 10889 let i = attrs.length; 10890 while (i--) { 10891 if (attrs[i].name === name) { 10892 attrs.splice(i, 1); 10893 return self; 10894 } 10895 } 10896 } 10897 return self; 10898 } 10899 if (name in attrs.map) { 10900 let i = attrs.length; 10901 while (i--) { 10902 if (attrs[i].name === name) { 10903 attrs[i].value = value; 10904 break; 10905 } 10906 } 10907 } else { 10908 attrs.push({ 10909 name, 10910 value 10911 }); 10912 } 10913 attrs.map[name] = value; 10914 return self; 10915 } 10916 return attrs.map[name]; 10917 } 10918 return undefined; 10919 } 10920 clone() { 10921 const self = this; 10922 const clone = new AstNode(self.name, self.type); 10923 const selfAttrs = self.attributes; 10924 if (selfAttrs) { 10925 const cloneAttrs = []; 10926 cloneAttrs.map = {}; 10927 for (let i = 0, l = selfAttrs.length; i < l; i++) { 10928 const selfAttr = selfAttrs[i]; 10929 if (selfAttr.name !== 'id') { 10930 cloneAttrs[cloneAttrs.length] = { 10931 name: selfAttr.name, 10932 value: selfAttr.value 10933 }; 10934 cloneAttrs.map[selfAttr.name] = selfAttr.value; 10935 } 10936 } 10937 clone.attributes = cloneAttrs; 10938 } 10939 clone.value = self.value; 10940 return clone; 10941 } 10942 wrap(wrapper) { 10943 const self = this; 10944 if (self.parent) { 10945 self.parent.insert(wrapper, self); 10946 wrapper.append(self); 10947 } 10948 return self; 10949 } 10950 unwrap() { 10951 const self = this; 10952 for (let node = self.firstChild; node;) { 10953 const next = node.next; 10954 self.insert(node, self, true); 10955 node = next; 10956 } 10957 self.remove(); 10958 } 10959 remove() { 10960 const self = this, parent = self.parent, next = self.next, prev = self.prev; 10961 if (parent) { 10962 if (parent.firstChild === self) { 10963 parent.firstChild = next; 10964 if (next) { 10965 next.prev = null; 10966 } 10967 } else if (prev) { 10968 prev.next = next; 10969 } 10970 if (parent.lastChild === self) { 10971 parent.lastChild = prev; 10972 if (prev) { 10973 prev.next = null; 10974 } 10975 } else if (next) { 10976 next.prev = prev; 10977 } 10978 self.parent = self.next = self.prev = null; 10979 } 10980 return self; 10981 } 10982 append(node) { 10983 const self = this; 10984 if (node.parent) { 10985 node.remove(); 10986 } 10987 const last = self.lastChild; 10988 if (last) { 10989 last.next = node; 10990 node.prev = last; 10991 self.lastChild = node; 10992 } else { 10993 self.lastChild = self.firstChild = node; 10994 } 10995 node.parent = self; 10996 return node; 10997 } 10998 insert(node, refNode, before) { 10999 if (node.parent) { 11000 node.remove(); 11001 } 11002 const parent = refNode.parent || this; 11003 if (before) { 11004 if (refNode === parent.firstChild) { 11005 parent.firstChild = node; 11006 } else if (refNode.prev) { 11007 refNode.prev.next = node; 11008 } 11009 node.prev = refNode.prev; 11010 node.next = refNode; 11011 refNode.prev = node; 11012 } else { 11013 if (refNode === parent.lastChild) { 11014 parent.lastChild = node; 11015 } else if (refNode.next) { 11016 refNode.next.prev = node; 11017 } 11018 node.next = refNode.next; 11019 node.prev = refNode; 11020 refNode.next = node; 11021 } 11022 node.parent = parent; 11023 return node; 11024 } 11025 getAll(name) { 11026 const self = this; 11027 const collection = []; 11028 for (let node = self.firstChild; node; node = walk$2(node, self)) { 11029 if (node.name === name) { 11030 collection.push(node); 11031 } 11032 } 11033 return collection; 11034 } 11035 children() { 11036 const self = this; 11037 const collection = []; 11038 for (let node = self.firstChild; node; node = node.next) { 11039 collection.push(node); 11040 } 11041 return collection; 11042 } 11043 empty() { 11044 const self = this; 11045 if (self.firstChild) { 11046 const nodes = []; 11047 for (let node = self.firstChild; node; node = walk$2(node, self)) { 11048 nodes.push(node); 11049 } 11050 let i = nodes.length; 11051 while (i--) { 11052 const node = nodes[i]; 11053 node.parent = node.firstChild = node.lastChild = node.next = node.prev = null; 11054 } 11055 } 11056 self.firstChild = self.lastChild = null; 11057 return self; 11058 } 11059 isEmpty(elements, whitespace = {}, predicate) { 11060 var _a; 11061 const self = this; 11062 let node = self.firstChild; 11063 if (isNonEmptyElement(self)) { 11064 return false; 11065 } 11066 if (node) { 11067 do { 11068 if (node.type === 1) { 11069 if (node.attr('data-mce-bogus')) { 11070 continue; 11071 } 11072 if (elements[node.name]) { 11073 return false; 11074 } 11075 if (isNonEmptyElement(node)) { 11076 return false; 11077 } 11078 } 11079 if (node.type === 8) { 11080 return false; 11081 } 11082 if (node.type === 3 && !isEmptyTextNode(node)) { 11083 return false; 11084 } 11085 if (node.type === 3 && node.parent && whitespace[node.parent.name] && isWhitespaceText((_a = node.value) !== null && _a !== void 0 ? _a : '')) { 11086 return false; 11087 } 11088 if (predicate && predicate(node)) { 11089 return false; 11090 } 11091 } while (node = walk$2(node, self)); 11092 } 11093 return true; 11094 } 11095 walk(prev) { 11096 return walk$2(this, null, prev); 11097 } 11098 } 11099 11100 const isConditionalComment = (html, startIndex) => /^\s*\[if [\w\W]+\]>.*<!\[endif\](--!?)?>/.test(html.substr(startIndex)); 11101 const findCommentEndIndex = (html, isBogus, startIndex = 0) => { 11102 const lcHtml = html.toLowerCase(); 11103 if (lcHtml.indexOf('[if ', startIndex) !== -1 && isConditionalComment(lcHtml, startIndex)) { 11104 const endIfIndex = lcHtml.indexOf('[endif]', startIndex); 11105 return lcHtml.indexOf('>', endIfIndex); 11106 } else { 11107 if (isBogus) { 11108 const endIndex = lcHtml.indexOf('>', startIndex); 11109 return endIndex !== -1 ? endIndex : lcHtml.length; 11110 } else { 11111 const endCommentRegexp = /--!?>/g; 11112 endCommentRegexp.lastIndex = startIndex; 11113 const match = endCommentRegexp.exec(html); 11114 return match ? match.index + match[0].length : lcHtml.length; 11115 } 11116 } 11117 }; 11118 const findMatchingEndTagIndex = (schema, html, startIndex) => { 11119 const startTagRegExp = /<([!?\/])?([A-Za-z0-9\-_:.]+)/g; 11120 const endTagRegExp = /(?:\s(?:[^'">]+(?:"[^"]*"|'[^']*'))*[^"'>]*(?:"[^">]*|'[^'>]*)?|\s*|\/)>/g; 11121 const voidElements = schema.getVoidElements(); 11122 let count = 1, index = startIndex; 11123 while (count !== 0) { 11124 startTagRegExp.lastIndex = index; 11125 while (true) { 11126 const startMatch = startTagRegExp.exec(html); 11127 if (startMatch === null) { 11128 return index; 11129 } else if (startMatch[1] === '!') { 11130 if (startsWith(startMatch[2], '--')) { 11131 index = findCommentEndIndex(html, false, startMatch.index + '!--'.length); 11132 } else { 11133 index = findCommentEndIndex(html, true, startMatch.index + 1); 11134 } 11135 break; 11136 } else { 11137 endTagRegExp.lastIndex = startTagRegExp.lastIndex; 11138 const endMatch = endTagRegExp.exec(html); 11139 if (isNull(endMatch) || endMatch.index !== startTagRegExp.lastIndex) { 11140 continue; 11141 } 11142 if (startMatch[1] === '/') { 11143 count -= 1; 11144 } else if (!has$2(voidElements, startMatch[2])) { 11145 count += 1; 11146 } 11147 index = startTagRegExp.lastIndex + endMatch[0].length; 11148 break; 11149 } 11150 } 11151 } 11152 return index; 11153 }; 11154 const trimHtml$1 = (tempAttrs, html) => { 11155 const trimContentRegExp = new RegExp(['\\s?(' + tempAttrs.join('|') + ')="[^"]+"'].join('|'), 'gi'); 11156 return html.replace(trimContentRegExp, ''); 11157 }; 11158 const trimInternal = (serializer, html) => { 11159 const bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g; 11160 const schema = serializer.schema; 11161 let content = trimHtml$1(serializer.getTempAttrs(), html); 11162 const voidElements = schema.getVoidElements(); 11163 let matches; 11164 while (matches = bogusAllRegExp.exec(content)) { 11165 const index = bogusAllRegExp.lastIndex; 11166 const matchLength = matches[0].length; 11167 let endTagIndex; 11168 if (voidElements[matches[1]]) { 11169 endTagIndex = index; 11170 } else { 11171 endTagIndex = findMatchingEndTagIndex(schema, content, index); 11172 } 11173 content = content.substring(0, index - matchLength) + content.substring(endTagIndex); 11174 bogusAllRegExp.lastIndex = index - matchLength; 11175 } 11176 return trim$1(content); 11177 }; 11178 const trimExternal = trimInternal; 11179 11180 const cleanupBogusElements = parent => { 11181 const bogusElements = descendants(parent, '[data-mce-bogus]'); 11182 each$e(bogusElements, elem => { 11183 const bogusValue = get$9(elem, 'data-mce-bogus'); 11184 if (bogusValue === 'all') { 11185 remove$5(elem); 11186 } else if (isBr$5(elem)) { 11187 before$3(elem, SugarElement.fromText(zeroWidth)); 11188 remove$5(elem); 11189 } else { 11190 unwrap(elem); 11191 } 11192 }); 11193 }; 11194 const cleanupInputNames = parent => { 11195 const inputs = descendants(parent, 'input'); 11196 each$e(inputs, input => { 11197 remove$a(input, 'name'); 11198 }); 11199 }; 11200 11201 const trimEmptyContents = (editor, html) => { 11202 const blockName = getForcedRootBlock(editor); 11203 const emptyRegExp = new RegExp(`^(<${ blockName }[^>]*>( | |\\s|\u00a0|<br \\/>|)<\\/${ blockName }>[\r\n]*|<br \\/>[\r\n]*)$`); 11204 return html.replace(emptyRegExp, ''); 11205 }; 11206 const getPlainTextContent = (editor, body) => { 11207 const doc = editor.getDoc(); 11208 const dos = getRootNode(SugarElement.fromDom(editor.getBody())); 11209 const offscreenDiv = SugarElement.fromTag('div', doc); 11210 set$3(offscreenDiv, 'data-mce-bogus', 'all'); 11211 setAll(offscreenDiv, { 11212 position: 'fixed', 11213 left: '-9999999px', 11214 top: '0' 11215 }); 11216 set$1(offscreenDiv, body.innerHTML); 11217 cleanupBogusElements(offscreenDiv); 11218 cleanupInputNames(offscreenDiv); 11219 const root = getContentContainer(dos); 11220 append$1(root, offscreenDiv); 11221 const content = trim$1(offscreenDiv.dom.innerText); 11222 remove$5(offscreenDiv); 11223 return content; 11224 }; 11225 const getContentFromBody = (editor, args, body) => { 11226 let content; 11227 if (args.format === 'raw') { 11228 content = Tools.trim(trimExternal(editor.serializer, body.innerHTML)); 11229 } else if (args.format === 'text') { 11230 content = getPlainTextContent(editor, body); 11231 } else if (args.format === 'tree') { 11232 content = editor.serializer.serialize(body, args); 11233 } else { 11234 content = trimEmptyContents(editor, editor.serializer.serialize(body, args)); 11235 } 11236 const shouldTrim = args.format !== 'text' && !isWsPreserveElement(SugarElement.fromDom(body)); 11237 return shouldTrim && isString(content) ? Tools.trim(content) : content; 11238 }; 11239 const getContentInternal = (editor, args) => Optional.from(editor.getBody()).fold(constant(args.format === 'tree' ? new AstNode('body', 11) : ''), body => getContentFromBody(editor, args, body)); 11240 11241 const makeMap$1 = Tools.makeMap; 11242 const Writer = settings => { 11243 const html = []; 11244 settings = settings || {}; 11245 const indent = settings.indent; 11246 const indentBefore = makeMap$1(settings.indent_before || ''); 11247 const indentAfter = makeMap$1(settings.indent_after || ''); 11248 const encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities); 11249 const htmlOutput = settings.element_format !== 'xhtml'; 11250 return { 11251 start: (name, attrs, empty) => { 11252 if (indent && indentBefore[name] && html.length > 0) { 11253 const value = html[html.length - 1]; 11254 if (value.length > 0 && value !== '\n') { 11255 html.push('\n'); 11256 } 11257 } 11258 html.push('<', name); 11259 if (attrs) { 11260 for (let i = 0, l = attrs.length; i < l; i++) { 11261 const attr = attrs[i]; 11262 html.push(' ', attr.name, '="', encode(attr.value, true), '"'); 11263 } 11264 } 11265 if (!empty || htmlOutput) { 11266 html[html.length] = '>'; 11267 } else { 11268 html[html.length] = ' />'; 11269 } 11270 if (empty && indent && indentAfter[name] && html.length > 0) { 11271 const value = html[html.length - 1]; 11272 if (value.length > 0 && value !== '\n') { 11273 html.push('\n'); 11274 } 11275 } 11276 }, 11277 end: name => { 11278 let value; 11279 html.push('</', name, '>'); 11280 if (indent && indentAfter[name] && html.length > 0) { 11281 value = html[html.length - 1]; 11282 if (value.length > 0 && value !== '\n') { 11283 html.push('\n'); 11284 } 11285 } 11286 }, 11287 text: (text, raw) => { 11288 if (text.length > 0) { 11289 html[html.length] = raw ? text : encode(text); 11290 } 11291 }, 11292 cdata: text => { 11293 html.push('<![CDATA[', text, ']]>'); 11294 }, 11295 comment: text => { 11296 html.push('<!--', text, '-->'); 11297 }, 11298 pi: (name, text) => { 11299 if (text) { 11300 html.push('<?', name, ' ', encode(text), '?>'); 11301 } else { 11302 html.push('<?', name, '?>'); 11303 } 11304 if (indent) { 11305 html.push('\n'); 11306 } 11307 }, 11308 doctype: text => { 11309 html.push('<!DOCTYPE', text, '>', indent ? '\n' : ''); 11310 }, 11311 reset: () => { 11312 html.length = 0; 11313 }, 11314 getContent: () => { 11315 return html.join('').replace(/\n$/, ''); 11316 } 11317 }; 11318 }; 11319 11320 const HtmlSerializer = (settings = {}, schema = Schema()) => { 11321 const writer = Writer(settings); 11322 settings.validate = 'validate' in settings ? settings.validate : true; 11323 const serialize = node => { 11324 const validate = settings.validate; 11325 const handlers = { 11326 3: node => { 11327 var _a; 11328 writer.text((_a = node.value) !== null && _a !== void 0 ? _a : '', node.raw); 11329 }, 11330 8: node => { 11331 var _a; 11332 writer.comment((_a = node.value) !== null && _a !== void 0 ? _a : ''); 11333 }, 11334 7: node => { 11335 writer.pi(node.name, node.value); 11336 }, 11337 10: node => { 11338 var _a; 11339 writer.doctype((_a = node.value) !== null && _a !== void 0 ? _a : ''); 11340 }, 11341 4: node => { 11342 var _a; 11343 writer.cdata((_a = node.value) !== null && _a !== void 0 ? _a : ''); 11344 }, 11345 11: node => { 11346 let tempNode = node; 11347 if (tempNode = tempNode.firstChild) { 11348 do { 11349 walk(tempNode); 11350 } while (tempNode = tempNode.next); 11351 } 11352 } 11353 }; 11354 writer.reset(); 11355 const walk = node => { 11356 var _a; 11357 const handler = handlers[node.type]; 11358 if (!handler) { 11359 const name = node.name; 11360 const isEmpty = name in schema.getVoidElements(); 11361 let attrs = node.attributes; 11362 if (validate && attrs && attrs.length > 1) { 11363 const sortedAttrs = []; 11364 sortedAttrs.map = {}; 11365 const elementRule = schema.getElementRule(node.name); 11366 if (elementRule) { 11367 for (let i = 0, l = elementRule.attributesOrder.length; i < l; i++) { 11368 const attrName = elementRule.attributesOrder[i]; 11369 if (attrName in attrs.map) { 11370 const attrValue = attrs.map[attrName]; 11371 sortedAttrs.map[attrName] = attrValue; 11372 sortedAttrs.push({ 11373 name: attrName, 11374 value: attrValue 11375 }); 11376 } 11377 } 11378 for (let i = 0, l = attrs.length; i < l; i++) { 11379 const attrName = attrs[i].name; 11380 if (!(attrName in sortedAttrs.map)) { 11381 const attrValue = attrs.map[attrName]; 11382 sortedAttrs.map[attrName] = attrValue; 11383 sortedAttrs.push({ 11384 name: attrName, 11385 value: attrValue 11386 }); 11387 } 11388 } 11389 attrs = sortedAttrs; 11390 } 11391 } 11392 writer.start(name, attrs, isEmpty); 11393 if (!isEmpty) { 11394 let child = node.firstChild; 11395 if (child) { 11396 if ((name === 'pre' || name === 'textarea') && child.type === 3 && ((_a = child.value) === null || _a === void 0 ? void 0 : _a[0]) === '\n') { 11397 writer.text('\n', true); 11398 } 11399 do { 11400 walk(child); 11401 } while (child = child.next); 11402 } 11403 writer.end(name); 11404 } 11405 } else { 11406 handler(node); 11407 } 11408 }; 11409 if (node.type === 1 && !settings.inner) { 11410 walk(node); 11411 } else if (node.type === 3) { 11412 handlers[3](node); 11413 } else { 11414 handlers[11](node); 11415 } 11416 return writer.getContent(); 11417 }; 11418 return { serialize }; 11419 }; 11420 11421 const nonInheritableStyles = new Set(); 11422 (() => { 11423 const nonInheritableStylesArr = [ 11424 'margin', 11425 'margin-left', 11426 'margin-right', 11427 'margin-top', 11428 'margin-bottom', 11429 'padding', 11430 'padding-left', 11431 'padding-right', 11432 'padding-top', 11433 'padding-bottom', 11434 'border', 11435 'border-width', 11436 'border-style', 11437 'border-color', 11438 'background', 11439 'background-attachment', 11440 'background-clip', 11441 'background-color', 11442 'background-image', 11443 'background-origin', 11444 'background-position', 11445 'background-repeat', 11446 'background-size', 11447 'float', 11448 'position', 11449 'left', 11450 'right', 11451 'top', 11452 'bottom', 11453 'z-index', 11454 'display', 11455 'transform', 11456 'width', 11457 'max-width', 11458 'min-width', 11459 'height', 11460 'max-height', 11461 'min-height', 11462 'overflow', 11463 'overflow-x', 11464 'overflow-y', 11465 'text-overflow', 11466 'vertical-align', 11467 'transition', 11468 'transition-delay', 11469 'transition-duration', 11470 'transition-property', 11471 'transition-timing-function' 11472 ]; 11473 each$e(nonInheritableStylesArr, style => { 11474 nonInheritableStyles.add(style); 11475 }); 11476 })(); 11477 const shorthandStyleProps = [ 11478 'font', 11479 'text-decoration', 11480 'text-emphasis' 11481 ]; 11482 const getStyleProps = (dom, node) => keys(dom.parseStyle(dom.getAttrib(node, 'style'))); 11483 const isNonInheritableStyle = style => nonInheritableStyles.has(style); 11484 const hasInheritableStyles = (dom, node) => forall(getStyleProps(dom, node), style => !isNonInheritableStyle(style)); 11485 const getLonghandStyleProps = styles => filter$5(styles, style => exists(shorthandStyleProps, prop => startsWith(style, prop))); 11486 const hasStyleConflict = (dom, node, parentNode) => { 11487 const nodeStyleProps = getStyleProps(dom, node); 11488 const parentNodeStyleProps = getStyleProps(dom, parentNode); 11489 const valueMismatch = prop => { 11490 var _a, _b; 11491 const nodeValue = (_a = dom.getStyle(node, prop)) !== null && _a !== void 0 ? _a : ''; 11492 const parentValue = (_b = dom.getStyle(parentNode, prop)) !== null && _b !== void 0 ? _b : ''; 11493 return isNotEmpty(nodeValue) && isNotEmpty(parentValue) && nodeValue !== parentValue; 11494 }; 11495 return exists(nodeStyleProps, nodeStyleProp => { 11496 const propExists = props => exists(props, prop => prop === nodeStyleProp); 11497 if (!propExists(parentNodeStyleProps) && propExists(shorthandStyleProps)) { 11498 const longhandProps = getLonghandStyleProps(parentNodeStyleProps); 11499 return exists(longhandProps, valueMismatch); 11500 } else { 11501 return valueMismatch(nodeStyleProp); 11502 } 11503 }); 11504 }; 11505 11506 const isChar = (forward, predicate, pos) => Optional.from(pos.container()).filter(isText$a).exists(text => { 11507 const delta = forward ? 0 : -1; 11508 return predicate(text.data.charAt(pos.offset() + delta)); 11509 }); 11510 const isBeforeSpace = curry(isChar, true, isWhiteSpace); 11511 const isAfterSpace = curry(isChar, false, isWhiteSpace); 11512 const isEmptyText = pos => { 11513 const container = pos.container(); 11514 return isText$a(container) && (container.data.length === 0 || isZwsp$1(container.data) && BookmarkManager.isBookmarkNode(container.parentNode)); 11515 }; 11516 const matchesElementPosition = (before, predicate) => pos => getChildNodeAtRelativeOffset(before ? 0 : -1, pos).filter(predicate).isSome(); 11517 const isImageBlock = node => isImg(node) && get$7(SugarElement.fromDom(node), 'display') === 'block'; 11518 const isCefNode = node => isContentEditableFalse$b(node) && !isBogusAll$1(node); 11519 const isBeforeImageBlock = matchesElementPosition(true, isImageBlock); 11520 const isAfterImageBlock = matchesElementPosition(false, isImageBlock); 11521 const isBeforeMedia = matchesElementPosition(true, isMedia$2); 11522 const isAfterMedia = matchesElementPosition(false, isMedia$2); 11523 const isBeforeTable = matchesElementPosition(true, isTable$2); 11524 const isAfterTable = matchesElementPosition(false, isTable$2); 11525 const isBeforeContentEditableFalse = matchesElementPosition(true, isCefNode); 11526 const isAfterContentEditableFalse = matchesElementPosition(false, isCefNode); 11527 11528 const dropLast = xs => xs.slice(0, -1); 11529 const parentsUntil = (start, root, predicate) => { 11530 if (contains(root, start)) { 11531 return dropLast(parents$1(start, elm => { 11532 return predicate(elm) || eq(elm, root); 11533 })); 11534 } else { 11535 return []; 11536 } 11537 }; 11538 const parents = (start, root) => parentsUntil(start, root, never); 11539 const parentsAndSelf = (start, root) => [start].concat(parents(start, root)); 11540 11541 const navigateIgnoreEmptyTextNodes = (forward, root, from) => navigateIgnore(forward, root, from, isEmptyText); 11542 const getClosestBlock$1 = (root, pos) => find$2(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2); 11543 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())))); 11544 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()); 11545 const isAtStartOfBlock = curry(isAtBlockBoundary, false); 11546 const isAtEndOfBlock = curry(isAtBlockBoundary, true); 11547 const isBeforeBlock = curry(isAtBeforeAfterBlockBoundary, false); 11548 const isAfterBlock = curry(isAtBeforeAfterBlockBoundary, true); 11549 11550 const isBr$1 = pos => getElementFromPosition(pos).exists(isBr$5); 11551 const findBr = (forward, root, pos) => { 11552 const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2); 11553 const scope = head(parentBlocks).getOr(root); 11554 return fromPosition(forward, scope.dom, pos).filter(isBr$1); 11555 }; 11556 const isBeforeBr$1 = (root, pos) => getElementFromPosition(pos).exists(isBr$5) || findBr(true, root, pos).isSome(); 11557 const isAfterBr = (root, pos) => getElementFromPrevPosition(pos).exists(isBr$5) || findBr(false, root, pos).isSome(); 11558 const findPreviousBr = curry(findBr, false); 11559 const findNextBr = curry(findBr, true); 11560 11561 const isInMiddleOfText = pos => CaretPosition.isTextPosition(pos) && !pos.isAtStart() && !pos.isAtEnd(); 11562 const getClosestBlock = (root, pos) => { 11563 const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2); 11564 return head(parentBlocks).getOr(root); 11565 }; 11566 const hasSpaceBefore = (root, pos) => { 11567 if (isInMiddleOfText(pos)) { 11568 return isAfterSpace(pos); 11569 } else { 11570 return isAfterSpace(pos) || prevPosition(getClosestBlock(root, pos).dom, pos).exists(isAfterSpace); 11571 } 11572 }; 11573 const hasSpaceAfter = (root, pos) => { 11574 if (isInMiddleOfText(pos)) { 11575 return isBeforeSpace(pos); 11576 } else { 11577 return isBeforeSpace(pos) || nextPosition(getClosestBlock(root, pos).dom, pos).exists(isBeforeSpace); 11578 } 11579 }; 11580 const isPreValue = value => contains$2([ 11581 'pre', 11582 'pre-wrap' 11583 ], value); 11584 const isInPre = pos => getElementFromPosition(pos).bind(elm => closest$4(elm, isElement$7)).exists(elm => isPreValue(get$7(elm, 'white-space'))); 11585 const isAtBeginningOfBody = (root, pos) => prevPosition(root.dom, pos).isNone(); 11586 const isAtEndOfBody = (root, pos) => nextPosition(root.dom, pos).isNone(); 11587 const isAtLineBoundary = (root, pos) => isAtBeginningOfBody(root, pos) || isAtEndOfBody(root, pos) || isAtStartOfBlock(root, pos) || isAtEndOfBlock(root, pos) || isAfterBr(root, pos) || isBeforeBr$1(root, pos); 11588 const isCefBlock = node => isNonNullable(node) && isContentEditableFalse$b(node) && isBlockLike(node); 11589 const isSiblingCefBlock = (root, direction) => container => { 11590 return isCefBlock(new DomTreeWalker(container, root)[direction]()); 11591 }; 11592 const isBeforeCefBlock = (root, pos) => { 11593 const nextPos = nextPosition(root.dom, pos).getOr(pos); 11594 const isNextCefBlock = isSiblingCefBlock(root.dom, 'next'); 11595 return pos.isAtEnd() && (isNextCefBlock(pos.container()) || isNextCefBlock(nextPos.container())); 11596 }; 11597 const isAfterCefBlock = (root, pos) => { 11598 const prevPos = prevPosition(root.dom, pos).getOr(pos); 11599 const isPrevCefBlock = isSiblingCefBlock(root.dom, 'prev'); 11600 return pos.isAtStart() && (isPrevCefBlock(pos.container()) || isPrevCefBlock(prevPos.container())); 11601 }; 11602 const needsToHaveNbsp = (root, pos) => { 11603 if (isInPre(pos)) { 11604 return false; 11605 } else { 11606 return isAtLineBoundary(root, pos) || hasSpaceBefore(root, pos) || hasSpaceAfter(root, pos); 11607 } 11608 }; 11609 const needsToBeNbspLeft = (root, pos) => { 11610 if (isInPre(pos)) { 11611 return false; 11612 } else { 11613 return isAtStartOfBlock(root, pos) || isBeforeBlock(root, pos) || isAfterBr(root, pos) || hasSpaceBefore(root, pos) || isAfterCefBlock(root, pos); 11614 } 11615 }; 11616 const leanRight = pos => { 11617 const container = pos.container(); 11618 const offset = pos.offset(); 11619 if (isText$a(container) && offset < container.data.length) { 11620 return CaretPosition(container, offset + 1); 11621 } else { 11622 return pos; 11623 } 11624 }; 11625 const needsToBeNbspRight = (root, pos) => { 11626 if (isInPre(pos)) { 11627 return false; 11628 } else { 11629 return isAtEndOfBlock(root, pos) || isAfterBlock(root, pos) || isBeforeBr$1(root, pos) || hasSpaceAfter(root, pos) || isBeforeCefBlock(root, pos); 11630 } 11631 }; 11632 const needsToBeNbsp = (root, pos) => needsToBeNbspLeft(root, pos) || needsToBeNbspRight(root, leanRight(pos)); 11633 const isNbspAt = (text, offset) => isNbsp(text.charAt(offset)); 11634 const isWhiteSpaceAt = (text, offset) => isWhiteSpace(text.charAt(offset)); 11635 const hasNbsp = pos => { 11636 const container = pos.container(); 11637 return isText$a(container) && contains$1(container.data, nbsp); 11638 }; 11639 const normalizeNbspMiddle = text => { 11640 const chars = text.split(''); 11641 return map$3(chars, (chr, i) => { 11642 if (isNbsp(chr) && i > 0 && i < chars.length - 1 && isContent(chars[i - 1]) && isContent(chars[i + 1])) { 11643 return ' '; 11644 } else { 11645 return chr; 11646 } 11647 }).join(''); 11648 }; 11649 const normalizeNbspAtStart = (root, node, makeNbsp) => { 11650 const text = node.data; 11651 const firstPos = CaretPosition(node, 0); 11652 if (!makeNbsp && isNbspAt(text, 0) && !needsToBeNbsp(root, firstPos)) { 11653 node.data = ' ' + text.slice(1); 11654 return true; 11655 } else if (makeNbsp && isWhiteSpaceAt(text, 0) && needsToBeNbspLeft(root, firstPos)) { 11656 node.data = nbsp + text.slice(1); 11657 return true; 11658 } else { 11659 return false; 11660 } 11661 }; 11662 const normalizeNbspInMiddleOfTextNode = node => { 11663 const text = node.data; 11664 const newText = normalizeNbspMiddle(text); 11665 if (newText !== text) { 11666 node.data = newText; 11667 return true; 11668 } else { 11669 return false; 11670 } 11671 }; 11672 const normalizeNbspAtEnd = (root, node, makeNbsp) => { 11673 const text = node.data; 11674 const lastPos = CaretPosition(node, text.length - 1); 11675 if (!makeNbsp && isNbspAt(text, text.length - 1) && !needsToBeNbsp(root, lastPos)) { 11676 node.data = text.slice(0, -1) + ' '; 11677 return true; 11678 } else if (makeNbsp && isWhiteSpaceAt(text, text.length - 1) && needsToBeNbspRight(root, lastPos)) { 11679 node.data = text.slice(0, -1) + nbsp; 11680 return true; 11681 } else { 11682 return false; 11683 } 11684 }; 11685 const normalizeNbsps = (root, pos) => { 11686 const container = pos.container(); 11687 if (!isText$a(container)) { 11688 return Optional.none(); 11689 } 11690 if (hasNbsp(pos)) { 11691 const normalized = normalizeNbspAtStart(root, container, false) || normalizeNbspInMiddleOfTextNode(container) || normalizeNbspAtEnd(root, container, false); 11692 return someIf(normalized, pos); 11693 } else if (needsToBeNbsp(root, pos)) { 11694 const normalized = normalizeNbspAtStart(root, container, true) || normalizeNbspAtEnd(root, container, true); 11695 return someIf(normalized, pos); 11696 } else { 11697 return Optional.none(); 11698 } 11699 }; 11700 const normalizeNbspsInEditor = editor => { 11701 const root = SugarElement.fromDom(editor.getBody()); 11702 if (editor.selection.isCollapsed()) { 11703 normalizeNbsps(root, CaretPosition.fromRangeStart(editor.selection.getRng())).each(pos => { 11704 editor.selection.setRng(pos.toRange()); 11705 }); 11706 } 11707 }; 11708 11709 const normalize$1 = (node, offset, count) => { 11710 if (count === 0) { 11711 return; 11712 } 11713 const elm = SugarElement.fromDom(node); 11714 const root = ancestor$4(elm, isBlock$2).getOr(elm); 11715 const whitespace = node.data.slice(offset, offset + count); 11716 const isEndOfContent = offset + count >= node.data.length && needsToBeNbspRight(root, CaretPosition(node, node.data.length)); 11717 const isStartOfContent = offset === 0 && needsToBeNbspLeft(root, CaretPosition(node, 0)); 11718 node.replaceData(offset, count, normalize$4(whitespace, 4, isStartOfContent, isEndOfContent)); 11719 }; 11720 const normalizeWhitespaceAfter = (node, offset) => { 11721 const content = node.data.slice(offset); 11722 const whitespaceCount = content.length - lTrim(content).length; 11723 normalize$1(node, offset, whitespaceCount); 11724 }; 11725 const normalizeWhitespaceBefore = (node, offset) => { 11726 const content = node.data.slice(0, offset); 11727 const whitespaceCount = content.length - rTrim(content).length; 11728 normalize$1(node, offset - whitespaceCount, whitespaceCount); 11729 }; 11730 const mergeTextNodes = (prevNode, nextNode, normalizeWhitespace, mergeToPrev = true) => { 11731 const whitespaceOffset = rTrim(prevNode.data).length; 11732 const newNode = mergeToPrev ? prevNode : nextNode; 11733 const removeNode = mergeToPrev ? nextNode : prevNode; 11734 if (mergeToPrev) { 11735 newNode.appendData(removeNode.data); 11736 } else { 11737 newNode.insertData(0, removeNode.data); 11738 } 11739 remove$5(SugarElement.fromDom(removeNode)); 11740 if (normalizeWhitespace) { 11741 normalizeWhitespaceAfter(newNode, whitespaceOffset); 11742 } 11743 return newNode; 11744 }; 11745 11746 const needsReposition = (pos, elm) => { 11747 const container = pos.container(); 11748 const offset = pos.offset(); 11749 return !CaretPosition.isTextPosition(pos) && container === elm.parentNode && offset > CaretPosition.before(elm).offset(); 11750 }; 11751 const reposition = (elm, pos) => needsReposition(pos, elm) ? CaretPosition(pos.container(), pos.offset() - 1) : pos; 11752 const beforeOrStartOf = node => isText$a(node) ? CaretPosition(node, 0) : CaretPosition.before(node); 11753 const afterOrEndOf = node => isText$a(node) ? CaretPosition(node, node.data.length) : CaretPosition.after(node); 11754 const getPreviousSiblingCaretPosition = elm => { 11755 if (isCaretCandidate$3(elm.previousSibling)) { 11756 return Optional.some(afterOrEndOf(elm.previousSibling)); 11757 } else { 11758 return elm.previousSibling ? lastPositionIn(elm.previousSibling) : Optional.none(); 11759 } 11760 }; 11761 const getNextSiblingCaretPosition = elm => { 11762 if (isCaretCandidate$3(elm.nextSibling)) { 11763 return Optional.some(beforeOrStartOf(elm.nextSibling)); 11764 } else { 11765 return elm.nextSibling ? firstPositionIn(elm.nextSibling) : Optional.none(); 11766 } 11767 }; 11768 const findCaretPositionBackwardsFromElm = (rootElement, elm) => { 11769 return Optional.from(elm.previousSibling ? elm.previousSibling : elm.parentNode).bind(node => prevPosition(rootElement, CaretPosition.before(node))).orThunk(() => nextPosition(rootElement, CaretPosition.after(elm))); 11770 }; 11771 const findCaretPositionForwardsFromElm = (rootElement, elm) => nextPosition(rootElement, CaretPosition.after(elm)).orThunk(() => prevPosition(rootElement, CaretPosition.before(elm))); 11772 const findCaretPositionBackwards = (rootElement, elm) => getPreviousSiblingCaretPosition(elm).orThunk(() => getNextSiblingCaretPosition(elm)).orThunk(() => findCaretPositionBackwardsFromElm(rootElement, elm)); 11773 const findCaretPositionForward = (rootElement, elm) => getNextSiblingCaretPosition(elm).orThunk(() => getPreviousSiblingCaretPosition(elm)).orThunk(() => findCaretPositionForwardsFromElm(rootElement, elm)); 11774 const findCaretPosition = (forward, rootElement, elm) => forward ? findCaretPositionForward(rootElement, elm) : findCaretPositionBackwards(rootElement, elm); 11775 const findCaretPosOutsideElmAfterDelete = (forward, rootElement, elm) => findCaretPosition(forward, rootElement, elm).map(curry(reposition, elm)); 11776 const setSelection$1 = (editor, forward, pos) => { 11777 pos.fold(() => { 11778 editor.focus(); 11779 }, pos => { 11780 editor.selection.setRng(pos.toRange(), forward); 11781 }); 11782 }; 11783 const eqRawNode = rawNode => elm => elm.dom === rawNode; 11784 const isBlock = (editor, elm) => elm && has$2(editor.schema.getBlockElements(), name(elm)); 11785 const paddEmptyBlock = elm => { 11786 if (isEmpty$2(elm)) { 11787 const br = SugarElement.fromHtml('<br data-mce-bogus="1">'); 11788 empty(elm); 11789 append$1(elm, br); 11790 return Optional.some(CaretPosition.before(br.dom)); 11791 } else { 11792 return Optional.none(); 11793 } 11794 }; 11795 const deleteNormalized = (elm, afterDeletePosOpt, normalizeWhitespace) => { 11796 const prevTextOpt = prevSibling(elm).filter(isText$b); 11797 const nextTextOpt = nextSibling(elm).filter(isText$b); 11798 remove$5(elm); 11799 return lift3(prevTextOpt, nextTextOpt, afterDeletePosOpt, (prev, next, pos) => { 11800 const prevNode = prev.dom, nextNode = next.dom; 11801 const offset = prevNode.data.length; 11802 mergeTextNodes(prevNode, nextNode, normalizeWhitespace); 11803 return pos.container() === nextNode ? CaretPosition(prevNode, offset) : pos; 11804 }).orThunk(() => { 11805 if (normalizeWhitespace) { 11806 prevTextOpt.each(elm => normalizeWhitespaceBefore(elm.dom, elm.dom.length)); 11807 nextTextOpt.each(elm => normalizeWhitespaceAfter(elm.dom, 0)); 11808 } 11809 return afterDeletePosOpt; 11810 }); 11811 }; 11812 const isInlineElement = (editor, element) => has$2(editor.schema.getTextInlineElements(), name(element)); 11813 const deleteElement$2 = (editor, forward, elm, moveCaret = true) => { 11814 const afterDeletePos = findCaretPosOutsideElmAfterDelete(forward, editor.getBody(), elm.dom); 11815 const parentBlock = ancestor$4(elm, curry(isBlock, editor), eqRawNode(editor.getBody())); 11816 const normalizedAfterDeletePos = deleteNormalized(elm, afterDeletePos, isInlineElement(editor, elm)); 11817 if (editor.dom.isEmpty(editor.getBody())) { 11818 editor.setContent(''); 11819 editor.selection.setCursorLocation(); 11820 } else { 11821 parentBlock.bind(paddEmptyBlock).fold(() => { 11822 if (moveCaret) { 11823 setSelection$1(editor, forward, normalizedAfterDeletePos); 11824 } 11825 }, paddPos => { 11826 if (moveCaret) { 11827 setSelection$1(editor, forward, Optional.some(paddPos)); 11828 } 11829 }); 11830 } 11831 }; 11832 11833 const strongRtl = /[\u0591-\u07FF\uFB1D-\uFDFF\uFE70-\uFEFC]/; 11834 const hasStrongRtl = text => strongRtl.test(text); 11835 11836 const isInlineTarget = (editor, elm) => is$1(SugarElement.fromDom(elm), getInlineBoundarySelector(editor)) && !isTransparentBlock(editor.schema, elm) && editor.dom.isEditable(elm); 11837 const isRtl = element => { 11838 var _a; 11839 return DOMUtils.DOM.getStyle(element, 'direction', true) === 'rtl' || hasStrongRtl((_a = element.textContent) !== null && _a !== void 0 ? _a : ''); 11840 }; 11841 const findInlineParents = (isInlineTarget, rootNode, pos) => filter$5(DOMUtils.DOM.getParents(pos.container(), '*', rootNode), isInlineTarget); 11842 const findRootInline = (isInlineTarget, rootNode, pos) => { 11843 const parents = findInlineParents(isInlineTarget, rootNode, pos); 11844 return Optional.from(parents[parents.length - 1]); 11845 }; 11846 const hasSameParentBlock = (rootNode, node1, node2) => { 11847 const block1 = getParentBlock$3(node1, rootNode); 11848 const block2 = getParentBlock$3(node2, rootNode); 11849 return isNonNullable(block1) && block1 === block2; 11850 }; 11851 const isAtZwsp = pos => isBeforeInline(pos) || isAfterInline(pos); 11852 const normalizePosition = (forward, pos) => { 11853 const container = pos.container(), offset = pos.offset(); 11854 if (forward) { 11855 if (isCaretContainerInline(container)) { 11856 if (isText$a(container.nextSibling)) { 11857 return CaretPosition(container.nextSibling, 0); 11858 } else { 11859 return CaretPosition.after(container); 11860 } 11861 } else { 11862 return isBeforeInline(pos) ? CaretPosition(container, offset + 1) : pos; 11863 } 11864 } else { 11865 if (isCaretContainerInline(container)) { 11866 if (isText$a(container.previousSibling)) { 11867 return CaretPosition(container.previousSibling, container.previousSibling.data.length); 11868 } else { 11869 return CaretPosition.before(container); 11870 } 11871 } else { 11872 return isAfterInline(pos) ? CaretPosition(container, offset - 1) : pos; 11873 } 11874 } 11875 }; 11876 const normalizeForwards = curry(normalizePosition, true); 11877 const normalizeBackwards = curry(normalizePosition, false); 11878 11879 const execCommandIgnoreInputEvents = (editor, command) => { 11880 const inputBlocker = e => e.stopImmediatePropagation(); 11881 editor.on('beforeinput input', inputBlocker, true); 11882 editor.getDoc().execCommand(command); 11883 editor.off('beforeinput input', inputBlocker); 11884 }; 11885 const execEditorDeleteCommand = editor => { 11886 editor.execCommand('delete'); 11887 }; 11888 const execNativeDeleteCommand = editor => execCommandIgnoreInputEvents(editor, 'Delete'); 11889 const execNativeForwardDeleteCommand = editor => execCommandIgnoreInputEvents(editor, 'ForwardDelete'); 11890 const isBeforeRoot = rootNode => elm => is$2(parent(elm), rootNode, eq); 11891 const isTextBlockOrListItem = element => isTextBlock$2(element) || isListItem$1(element); 11892 const getParentBlock$2 = (rootNode, elm) => { 11893 if (contains(rootNode, elm)) { 11894 return closest$4(elm, isTextBlockOrListItem, isBeforeRoot(rootNode)); 11895 } else { 11896 return Optional.none(); 11897 } 11898 }; 11899 const paddEmptyBody = (editor, moveSelection = true) => { 11900 if (editor.dom.isEmpty(editor.getBody())) { 11901 editor.setContent('', { no_selection: !moveSelection }); 11902 } 11903 }; 11904 const willDeleteLastPositionInElement = (forward, fromPos, elm) => lift2(firstPositionIn(elm), lastPositionIn(elm), (firstPos, lastPos) => { 11905 const normalizedFirstPos = normalizePosition(true, firstPos); 11906 const normalizedLastPos = normalizePosition(false, lastPos); 11907 const normalizedFromPos = normalizePosition(false, fromPos); 11908 if (forward) { 11909 return nextPosition(elm, normalizedFromPos).exists(nextPos => nextPos.isEqual(normalizedLastPos) && fromPos.isEqual(normalizedFirstPos)); 11910 } else { 11911 return prevPosition(elm, normalizedFromPos).exists(prevPos => prevPos.isEqual(normalizedFirstPos) && fromPos.isEqual(normalizedLastPos)); 11912 } 11913 }).getOr(true); 11914 const freefallRtl = root => { 11915 const child = isComment$1(root) ? prevSibling(root) : lastChild(root); 11916 return child.bind(freefallRtl).orThunk(() => Optional.some(root)); 11917 }; 11918 const deleteRangeContents = (editor, rng, root, moveSelection = true) => { 11919 var _a; 11920 rng.deleteContents(); 11921 const lastNode = freefallRtl(root).getOr(root); 11922 const lastBlock = SugarElement.fromDom((_a = editor.dom.getParent(lastNode.dom, editor.dom.isBlock)) !== null && _a !== void 0 ? _a : root.dom); 11923 if (lastBlock.dom === editor.getBody()) { 11924 paddEmptyBody(editor, moveSelection); 11925 } else if (isEmpty$2(lastBlock)) { 11926 fillWithPaddingBr(lastBlock); 11927 if (moveSelection) { 11928 editor.selection.setCursorLocation(lastBlock.dom, 0); 11929 } 11930 } 11931 if (!eq(root, lastBlock)) { 11932 const additionalCleanupNodes = is$2(parent(lastBlock), root) ? [] : siblings(lastBlock); 11933 each$e(additionalCleanupNodes.concat(children$1(root)), node => { 11934 if (!eq(node, lastBlock) && !contains(node, lastBlock) && isEmpty$2(node)) { 11935 remove$5(node); 11936 } 11937 }); 11938 } 11939 }; 11940 11941 const isRootFromElement = root => cur => eq(root, cur); 11942 const getTableCells = table => descendants(table, 'td,th'); 11943 const getTableDetailsFromRange = (rng, isRoot) => { 11944 const getTable = node => getClosestTable(SugarElement.fromDom(node), isRoot); 11945 const startTable = getTable(rng.startContainer); 11946 const endTable = getTable(rng.endContainer); 11947 const isStartInTable = startTable.isSome(); 11948 const isEndInTable = endTable.isSome(); 11949 const isSameTable = lift2(startTable, endTable, eq).getOr(false); 11950 const isMultiTable = !isSameTable && isStartInTable && isEndInTable; 11951 return { 11952 startTable, 11953 endTable, 11954 isStartInTable, 11955 isEndInTable, 11956 isSameTable, 11957 isMultiTable 11958 }; 11959 }; 11960 11961 const tableCellRng = (start, end) => ({ 11962 start, 11963 end 11964 }); 11965 const tableSelection = (rng, table, cells) => ({ 11966 rng, 11967 table, 11968 cells 11969 }); 11970 const deleteAction = Adt.generate([ 11971 { 11972 singleCellTable: [ 11973 'rng', 11974 'cell' 11975 ] 11976 }, 11977 { fullTable: ['table'] }, 11978 { 11979 partialTable: [ 11980 'cells', 11981 'outsideDetails' 11982 ] 11983 }, 11984 { 11985 multiTable: [ 11986 'startTableCells', 11987 'endTableCells', 11988 'betweenRng' 11989 ] 11990 } 11991 ]); 11992 const getClosestCell$1 = (container, isRoot) => closest$3(SugarElement.fromDom(container), 'td,th', isRoot); 11993 const isExpandedCellRng = cellRng => !eq(cellRng.start, cellRng.end); 11994 const getTableFromCellRng = (cellRng, isRoot) => getClosestTable(cellRng.start, isRoot).bind(startParentTable => getClosestTable(cellRng.end, isRoot).bind(endParentTable => someIf(eq(startParentTable, endParentTable), startParentTable))); 11995 const isSingleCellTable = (cellRng, isRoot) => !isExpandedCellRng(cellRng) && getTableFromCellRng(cellRng, isRoot).exists(table => { 11996 const rows = table.dom.rows; 11997 return rows.length === 1 && rows[0].cells.length === 1; 11998 }); 11999 const getCellRng = (rng, isRoot) => { 12000 const startCell = getClosestCell$1(rng.startContainer, isRoot); 12001 const endCell = getClosestCell$1(rng.endContainer, isRoot); 12002 return lift2(startCell, endCell, tableCellRng); 12003 }; 12004 const getCellRangeFromStartTable = isRoot => startCell => getClosestTable(startCell, isRoot).bind(table => last$3(getTableCells(table)).map(endCell => tableCellRng(startCell, endCell))); 12005 const getCellRangeFromEndTable = isRoot => endCell => getClosestTable(endCell, isRoot).bind(table => head(getTableCells(table)).map(startCell => tableCellRng(startCell, endCell))); 12006 const getTableSelectionFromCellRng = isRoot => cellRng => getTableFromCellRng(cellRng, isRoot).map(table => tableSelection(cellRng, table, getTableCells(table))); 12007 const getTableSelections = (cellRng, selectionDetails, rng, isRoot) => { 12008 if (rng.collapsed || !cellRng.forall(isExpandedCellRng)) { 12009 return Optional.none(); 12010 } else if (selectionDetails.isSameTable) { 12011 const sameTableSelection = cellRng.bind(getTableSelectionFromCellRng(isRoot)); 12012 return Optional.some({ 12013 start: sameTableSelection, 12014 end: sameTableSelection 12015 }); 12016 } else { 12017 const startCell = getClosestCell$1(rng.startContainer, isRoot); 12018 const endCell = getClosestCell$1(rng.endContainer, isRoot); 12019 const startTableSelection = startCell.bind(getCellRangeFromStartTable(isRoot)).bind(getTableSelectionFromCellRng(isRoot)); 12020 const endTableSelection = endCell.bind(getCellRangeFromEndTable(isRoot)).bind(getTableSelectionFromCellRng(isRoot)); 12021 return Optional.some({ 12022 start: startTableSelection, 12023 end: endTableSelection 12024 }); 12025 } 12026 }; 12027 const getCellIndex = (cells, cell) => findIndex$2(cells, x => eq(x, cell)); 12028 const getSelectedCells = tableSelection => lift2(getCellIndex(tableSelection.cells, tableSelection.rng.start), getCellIndex(tableSelection.cells, tableSelection.rng.end), (startIndex, endIndex) => tableSelection.cells.slice(startIndex, endIndex + 1)); 12029 const isSingleCellTableContentSelected = (optCellRng, rng, isRoot) => optCellRng.exists(cellRng => isSingleCellTable(cellRng, isRoot) && hasAllContentsSelected(cellRng.start, rng)); 12030 const unselectCells = (rng, selectionDetails) => { 12031 const {startTable, endTable} = selectionDetails; 12032 const otherContentRng = rng.cloneRange(); 12033 startTable.each(table => otherContentRng.setStartAfter(table.dom)); 12034 endTable.each(table => otherContentRng.setEndBefore(table.dom)); 12035 return otherContentRng; 12036 }; 12037 const handleSingleTable = (cellRng, selectionDetails, rng, isRoot) => getTableSelections(cellRng, selectionDetails, rng, isRoot).bind(({start, end}) => start.or(end)).bind(tableSelection => { 12038 const {isSameTable} = selectionDetails; 12039 const selectedCells = getSelectedCells(tableSelection).getOr([]); 12040 if (isSameTable && tableSelection.cells.length === selectedCells.length) { 12041 return Optional.some(deleteAction.fullTable(tableSelection.table)); 12042 } else if (selectedCells.length > 0) { 12043 if (isSameTable) { 12044 return Optional.some(deleteAction.partialTable(selectedCells, Optional.none())); 12045 } else { 12046 const otherContentRng = unselectCells(rng, selectionDetails); 12047 return Optional.some(deleteAction.partialTable(selectedCells, Optional.some({ 12048 ...selectionDetails, 12049 rng: otherContentRng 12050 }))); 12051 } 12052 } else { 12053 return Optional.none(); 12054 } 12055 }); 12056 const handleMultiTable = (cellRng, selectionDetails, rng, isRoot) => getTableSelections(cellRng, selectionDetails, rng, isRoot).bind(({start, end}) => { 12057 const startTableSelectedCells = start.bind(getSelectedCells).getOr([]); 12058 const endTableSelectedCells = end.bind(getSelectedCells).getOr([]); 12059 if (startTableSelectedCells.length > 0 && endTableSelectedCells.length > 0) { 12060 const otherContentRng = unselectCells(rng, selectionDetails); 12061 return Optional.some(deleteAction.multiTable(startTableSelectedCells, endTableSelectedCells, otherContentRng)); 12062 } else { 12063 return Optional.none(); 12064 } 12065 }); 12066 const getActionFromRange = (root, rng) => { 12067 const isRoot = isRootFromElement(root); 12068 const optCellRng = getCellRng(rng, isRoot); 12069 const selectionDetails = getTableDetailsFromRange(rng, isRoot); 12070 if (isSingleCellTableContentSelected(optCellRng, rng, isRoot)) { 12071 return optCellRng.map(cellRng => deleteAction.singleCellTable(rng, cellRng.start)); 12072 } else if (selectionDetails.isMultiTable) { 12073 return handleMultiTable(optCellRng, selectionDetails, rng, isRoot); 12074 } else { 12075 return handleSingleTable(optCellRng, selectionDetails, rng, isRoot); 12076 } 12077 }; 12078 12079 const cleanCells = cells => each$e(cells, cell => { 12080 remove$a(cell, 'contenteditable'); 12081 fillWithPaddingBr(cell); 12082 }); 12083 const getOutsideBlock = (editor, container) => Optional.from(editor.dom.getParent(container, editor.dom.isBlock)).map(SugarElement.fromDom); 12084 const handleEmptyBlock = (editor, startInTable, emptyBlock) => { 12085 emptyBlock.each(block => { 12086 if (startInTable) { 12087 remove$5(block); 12088 } else { 12089 fillWithPaddingBr(block); 12090 editor.selection.setCursorLocation(block.dom, 0); 12091 } 12092 }); 12093 }; 12094 const deleteContentInsideCell = (editor, cell, rng, isFirstCellInSelection) => { 12095 const insideTableRng = rng.cloneRange(); 12096 if (isFirstCellInSelection) { 12097 insideTableRng.setStart(rng.startContainer, rng.startOffset); 12098 insideTableRng.setEndAfter(cell.dom.lastChild); 12099 } else { 12100 insideTableRng.setStartBefore(cell.dom.firstChild); 12101 insideTableRng.setEnd(rng.endContainer, rng.endOffset); 12102 } 12103 deleteCellContents(editor, insideTableRng, cell, false).each(action => action()); 12104 }; 12105 const collapseAndRestoreCellSelection = editor => { 12106 const selectedCells = getCellsFromEditor(editor); 12107 const selectedNode = SugarElement.fromDom(editor.selection.getNode()); 12108 if (isTableCell$3(selectedNode.dom) && isEmpty$2(selectedNode)) { 12109 editor.selection.setCursorLocation(selectedNode.dom, 0); 12110 } else { 12111 editor.selection.collapse(true); 12112 } 12113 if (selectedCells.length > 1 && exists(selectedCells, cell => eq(cell, selectedNode))) { 12114 set$3(selectedNode, 'data-mce-selected', '1'); 12115 } 12116 }; 12117 const emptySingleTableCells = (editor, cells, outsideDetails) => Optional.some(() => { 12118 const editorRng = editor.selection.getRng(); 12119 const cellsToClean = outsideDetails.bind(({rng, isStartInTable}) => { 12120 const outsideBlock = getOutsideBlock(editor, isStartInTable ? rng.endContainer : rng.startContainer); 12121 rng.deleteContents(); 12122 handleEmptyBlock(editor, isStartInTable, outsideBlock.filter(isEmpty$2)); 12123 const endPointCell = isStartInTable ? cells[0] : cells[cells.length - 1]; 12124 deleteContentInsideCell(editor, endPointCell, editorRng, isStartInTable); 12125 if (!isEmpty$2(endPointCell)) { 12126 return Optional.some(isStartInTable ? cells.slice(1) : cells.slice(0, -1)); 12127 } else { 12128 return Optional.none(); 12129 } 12130 }).getOr(cells); 12131 cleanCells(cellsToClean); 12132 collapseAndRestoreCellSelection(editor); 12133 }); 12134 const emptyMultiTableCells = (editor, startTableCells, endTableCells, betweenRng) => Optional.some(() => { 12135 const rng = editor.selection.getRng(); 12136 const startCell = startTableCells[0]; 12137 const endCell = endTableCells[endTableCells.length - 1]; 12138 deleteContentInsideCell(editor, startCell, rng, true); 12139 deleteContentInsideCell(editor, endCell, rng, false); 12140 const startTableCellsToClean = isEmpty$2(startCell) ? startTableCells : startTableCells.slice(1); 12141 const endTableCellsToClean = isEmpty$2(endCell) ? endTableCells : endTableCells.slice(0, -1); 12142 cleanCells(startTableCellsToClean.concat(endTableCellsToClean)); 12143 betweenRng.deleteContents(); 12144 collapseAndRestoreCellSelection(editor); 12145 }); 12146 const deleteCellContents = (editor, rng, cell, moveSelection = true) => Optional.some(() => { 12147 deleteRangeContents(editor, rng, cell, moveSelection); 12148 }); 12149 const deleteTableElement = (editor, table) => Optional.some(() => deleteElement$2(editor, false, table)); 12150 const deleteCellRange = (editor, rootElm, rng) => getActionFromRange(rootElm, rng).bind(action => action.fold(curry(deleteCellContents, editor), curry(deleteTableElement, editor), curry(emptySingleTableCells, editor), curry(emptyMultiTableCells, editor))); 12151 const deleteCaptionRange = (editor, caption) => emptyElement(editor, caption); 12152 const deleteTableRange = (editor, rootElm, rng, startElm) => getParentCaption(rootElm, startElm).fold(() => deleteCellRange(editor, rootElm, rng), caption => deleteCaptionRange(editor, caption)); 12153 const deleteRange$3 = (editor, startElm, selectedCells) => { 12154 const rootNode = SugarElement.fromDom(editor.getBody()); 12155 const rng = editor.selection.getRng(); 12156 return selectedCells.length !== 0 ? emptySingleTableCells(editor, selectedCells, Optional.none()) : deleteTableRange(editor, rootNode, rng, startElm); 12157 }; 12158 const getParentCell = (rootElm, elm) => find$2(parentsAndSelf(elm, rootElm), isTableCell$2); 12159 const getParentCaption = (rootElm, elm) => find$2(parentsAndSelf(elm, rootElm), isTag('caption')); 12160 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))); 12161 const emptyElement = (editor, elm) => Optional.some(() => { 12162 fillWithPaddingBr(elm); 12163 editor.selection.setCursorLocation(elm.dom, 0); 12164 }); 12165 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); 12166 const emptyCaretCaption = (editor, elm) => emptyElement(editor, elm); 12167 const validateCaretCaption = (rootElm, fromCaption, to) => getParentCaption(rootElm, SugarElement.fromDom(to.getNode())).fold(() => Optional.some(noop), toCaption => someIf(!eq(toCaption, fromCaption), noop)); 12168 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)); 12169 const deleteCaretCells = (editor, forward, rootElm, startElm) => { 12170 const from = CaretPosition.fromRangeStart(editor.selection.getRng()); 12171 return getParentCell(rootElm, startElm).bind(fromCell => isEmpty$2(fromCell) ? emptyElement(editor, fromCell) : deleteBetweenCells(editor, rootElm, forward, fromCell, from)); 12172 }; 12173 const deleteCaretCaption = (editor, forward, rootElm, fromCaption) => { 12174 const from = CaretPosition.fromRangeStart(editor.selection.getRng()); 12175 return isEmpty$2(fromCaption) ? emptyElement(editor, fromCaption) : deleteCaretInsideCaption(editor, rootElm, forward, fromCaption, from); 12176 }; 12177 const isNearTable = (forward, pos) => forward ? isBeforeTable(pos) : isAfterTable(pos); 12178 const isBeforeOrAfterTable = (editor, forward) => { 12179 const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng()); 12180 return isNearTable(forward, fromPos) || fromPosition(forward, editor.getBody(), fromPos).exists(pos => isNearTable(forward, pos)); 12181 }; 12182 const deleteCaret$3 = (editor, forward, startElm) => { 12183 const rootElm = SugarElement.fromDom(editor.getBody()); 12184 return getParentCaption(rootElm, startElm).fold(() => deleteCaretCells(editor, forward, rootElm, startElm).orThunk(() => someIf(isBeforeOrAfterTable(editor, forward), noop)), fromCaption => deleteCaretCaption(editor, forward, rootElm, fromCaption)); 12185 }; 12186 const backspaceDelete$9 = (editor, forward) => { 12187 const startElm = SugarElement.fromDom(editor.selection.getStart(true)); 12188 const cells = getCellsFromEditor(editor); 12189 return editor.selection.isCollapsed() && cells.length === 0 ? deleteCaret$3(editor, forward, startElm) : deleteRange$3(editor, startElm, cells); 12190 }; 12191 12192 const getContentEditableRoot$1 = (root, node) => { 12193 let tempNode = node; 12194 while (tempNode && tempNode !== root) { 12195 if (isContentEditableTrue$3(tempNode) || isContentEditableFalse$b(tempNode)) { 12196 return tempNode; 12197 } 12198 tempNode = tempNode.parentNode; 12199 } 12200 return null; 12201 }; 12202 12203 const internalAttributesPrefixes = [ 12204 'data-ephox-', 12205 'data-mce-', 12206 'data-alloy-', 12207 'data-snooker-', 12208 '_' 12209 ]; 12210 const each$9 = Tools.each; 12211 const ElementUtils = editor => { 12212 const dom = editor.dom; 12213 const internalAttributes = new Set(editor.serializer.getTempAttrs()); 12214 const compare = (node1, node2) => { 12215 if (node1.nodeName !== node2.nodeName || node1.nodeType !== node2.nodeType) { 12216 return false; 12217 } 12218 const getAttribs = node => { 12219 const attribs = {}; 12220 each$9(dom.getAttribs(node), attr => { 12221 const name = attr.nodeName.toLowerCase(); 12222 if (name !== 'style' && !isAttributeInternal(name)) { 12223 attribs[name] = dom.getAttrib(node, name); 12224 } 12225 }); 12226 return attribs; 12227 }; 12228 const compareObjects = (obj1, obj2) => { 12229 for (const name in obj1) { 12230 if (has$2(obj1, name)) { 12231 const value = obj2[name]; 12232 if (isUndefined(value)) { 12233 return false; 12234 } 12235 if (obj1[name] !== value) { 12236 return false; 12237 } 12238 delete obj2[name]; 12239 } 12240 } 12241 for (const name in obj2) { 12242 if (has$2(obj2, name)) { 12243 return false; 12244 } 12245 } 12246 return true; 12247 }; 12248 if (isElement$6(node1) && isElement$6(node2)) { 12249 if (!compareObjects(getAttribs(node1), getAttribs(node2))) { 12250 return false; 12251 } 12252 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) { 12253 return false; 12254 } 12255 } 12256 return !isBookmarkNode$1(node1) && !isBookmarkNode$1(node2); 12257 }; 12258 const isAttributeInternal = attributeName => exists(internalAttributesPrefixes, value => startsWith(attributeName, value)) || internalAttributes.has(attributeName); 12259 return { 12260 compare, 12261 isAttributeInternal 12262 }; 12263 }; 12264 12265 const traverse = (root, fn) => { 12266 let node = root; 12267 while (node = node.walk()) { 12268 fn(node); 12269 } 12270 }; 12271 const matchNode$1 = (nodeFilters, attributeFilters, node, matches) => { 12272 const name = node.name; 12273 for (let ni = 0, nl = nodeFilters.length; ni < nl; ni++) { 12274 const filter = nodeFilters[ni]; 12275 if (filter.name === name) { 12276 const match = matches.nodes[name]; 12277 if (match) { 12278 match.nodes.push(node); 12279 } else { 12280 matches.nodes[name] = { 12281 filter, 12282 nodes: [node] 12283 }; 12284 } 12285 } 12286 } 12287 if (node.attributes) { 12288 for (let ai = 0, al = attributeFilters.length; ai < al; ai++) { 12289 const filter = attributeFilters[ai]; 12290 const attrName = filter.name; 12291 if (attrName in node.attributes.map) { 12292 const match = matches.attributes[attrName]; 12293 if (match) { 12294 match.nodes.push(node); 12295 } else { 12296 matches.attributes[attrName] = { 12297 filter, 12298 nodes: [node] 12299 }; 12300 } 12301 } 12302 } 12303 } 12304 }; 12305 const findMatchingNodes = (nodeFilters, attributeFilters, node) => { 12306 const matches = { 12307 nodes: {}, 12308 attributes: {} 12309 }; 12310 if (node.firstChild) { 12311 traverse(node, childNode => { 12312 matchNode$1(nodeFilters, attributeFilters, childNode, matches); 12313 }); 12314 } 12315 return matches; 12316 }; 12317 const runFilters = (matches, args) => { 12318 const run = (matchRecord, filteringAttributes) => { 12319 each$d(matchRecord, match => { 12320 const nodes = from(match.nodes); 12321 each$e(match.filter.callbacks, callback => { 12322 for (let i = nodes.length - 1; i >= 0; i--) { 12323 const node = nodes[i]; 12324 const valueMatches = filteringAttributes ? node.attr(match.filter.name) !== undefined : node.name === match.filter.name; 12325 if (!valueMatches || isNullable(node.parent)) { 12326 nodes.splice(i, 1); 12327 } 12328 } 12329 if (nodes.length > 0) { 12330 callback(nodes, match.filter.name, args); 12331 } 12332 }); 12333 }); 12334 }; 12335 run(matches.nodes, false); 12336 run(matches.attributes, true); 12337 }; 12338 const filter$2 = (nodeFilters, attributeFilters, node, args = {}) => { 12339 const matches = findMatchingNodes(nodeFilters, attributeFilters, node); 12340 runFilters(matches, args); 12341 }; 12342 12343 const paddEmptyNode = (
12343settings, args, isBlock, node) => { 12344 const brPreferred = settings.pad_empty_with_br || args.insert; 12345 if (brPreferred && isBlock(node)) { 12346 const astNode = new AstNode('br', 1); 12347 if (args.insert) { 12348 astNode.attr('data-mce-bogus', '1'); 12349 }
vendor: 90,906 bytes, lines 12349-14605
12349 12350 node.empty().append(astNode); 12351 } else { 12352 node.empty().append(new AstNode('#text', 3)).value = nbsp; 12353 } 12354 }; 12355 const isPaddedWithNbsp = node => { 12356 var _a; 12357 return hasOnlyChild(node, '#text') && ((_a = node === null || node === void 0 ? void 0 : node.firstChild) === null || _a === void 0 ? void 0 : _a.value) === nbsp; 12358 }; 12359 const hasOnlyChild = (node, name) => { 12360 const firstChild = node === null || node === void 0 ? void 0 : node.firstChild; 12361 return isNonNullable(firstChild) && firstChild === node.lastChild && firstChild.name === name; 12362 }; 12363 const isPadded = (schema, node) => { 12364 const rule = schema.getElementRule(node.name); 12365 return (rule === null || rule === void 0 ? void 0 : rule.paddEmpty) === true; 12366 }; 12367 const isEmpty = (schema, nonEmptyElements, whitespaceElements, node) => node.isEmpty(nonEmptyElements, whitespaceElements, node => isPadded(schema, node)); 12368 const isLineBreakNode = (node, isBlock) => isNonNullable(node) && (isBlock(node) || node.name === 'br'); 12369 const findClosestEditingHost = scope => { 12370 let editableNode; 12371 for (let node = scope; node; node = node.parent) { 12372 const contentEditable = node.attr('contenteditable'); 12373 if (contentEditable === 'false') { 12374 break; 12375 } else if (contentEditable === 'true') { 12376 editableNode = node; 12377 } 12378 } 12379 return Optional.from(editableNode); 12380 }; 12381 12382 const removeOrUnwrapInvalidNode = (node, schema, originalNodeParent = node.parent) => { 12383 if (schema.getSpecialElements()[node.name]) { 12384 node.empty().remove(); 12385 } else { 12386 const children = node.children(); 12387 for (const childNode of children) { 12388 if (originalNodeParent && !schema.isValidChild(originalNodeParent.name, childNode.name)) { 12389 removeOrUnwrapInvalidNode(childNode, schema, originalNodeParent); 12390 } 12391 } 12392 node.unwrap(); 12393 } 12394 }; 12395 const cleanInvalidNodes = (nodes, schema, rootNode, onCreate = noop) => { 12396 const textBlockElements = schema.getTextBlockElements(); 12397 const nonEmptyElements = schema.getNonEmptyElements(); 12398 const whitespaceElements = schema.getWhitespaceElements(); 12399 const nonSplittableElements = Tools.makeMap('tr,td,th,tbody,thead,tfoot,table'); 12400 const fixed = new Set(); 12401 const isSplittableElement = node => node !== rootNode && !nonSplittableElements[node.name]; 12402 for (let ni = 0; ni < nodes.length; ni++) { 12403 const node = nodes[ni]; 12404 let parent; 12405 let newParent; 12406 let tempNode; 12407 if (!node.parent || fixed.has(node)) { 12408 continue; 12409 } 12410 if (textBlockElements[node.name] && node.parent.name === 'li') { 12411 let sibling = node.next; 12412 while (sibling) { 12413 if (textBlockElements[sibling.name]) { 12414 sibling.name = 'li'; 12415 fixed.add(sibling); 12416 node.parent.insert(sibling, node.parent); 12417 } else { 12418 break; 12419 } 12420 sibling = sibling.next; 12421 } 12422 node.unwrap(); 12423 continue; 12424 } 12425 const parents = [node]; 12426 for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && isSplittableElement(parent); parent = parent.parent) { 12427 parents.push(parent); 12428 } 12429 if (parent && parents.length > 1) { 12430 if (schema.isValidChild(parent.name, node.name)) { 12431 parents.reverse(); 12432 newParent = parents[0].clone(); 12433 onCreate(newParent); 12434 let currentNode = newParent; 12435 for (let i = 0; i < parents.length - 1; i++) { 12436 if (schema.isValidChild(currentNode.name, parents[i].name) && i > 0) { 12437 tempNode = parents[i].clone(); 12438 onCreate(tempNode); 12439 currentNode.append(tempNode); 12440 } else { 12441 tempNode = currentNode; 12442 } 12443 for (let childNode = parents[i].firstChild; childNode && childNode !== parents[i + 1];) { 12444 const nextNode = childNode.next; 12445 tempNode.append(childNode); 12446 childNode = nextNode; 12447 } 12448 currentNode = tempNode; 12449 } 12450 if (!isEmpty(schema, nonEmptyElements, whitespaceElements, newParent)) { 12451 parent.insert(newParent, parents[0], true); 12452 parent.insert(node, newParent); 12453 } else { 12454 parent.insert(node, parents[0], true); 12455 } 12456 parent = parents[0]; 12457 if (isEmpty(schema, nonEmptyElements, whitespaceElements, parent) || hasOnlyChild(parent, 'br')) { 12458 parent.empty().remove(); 12459 } 12460 } else { 12461 removeOrUnwrapInvalidNode(node, schema); 12462 } 12463 } else if (node.parent) { 12464 if (node.name === 'li') { 12465 let sibling = node.prev; 12466 if (sibling && (sibling.name === 'ul' || sibling.name === 'ol')) { 12467 sibling.append(node); 12468 continue; 12469 } 12470 sibling = node.next; 12471 if (sibling && (sibling.name === 'ul' || sibling.name === 'ol') && sibling.firstChild) { 12472 sibling.insert(node, sibling.firstChild, true); 12473 continue; 12474 } 12475 const wrapper = new AstNode('ul', 1); 12476 onCreate(wrapper); 12477 node.wrap(wrapper); 12478 continue; 12479 } 12480 if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) { 12481 const wrapper = new AstNode('div', 1); 12482 onCreate(wrapper); 12483 node.wrap(wrapper); 12484 } else { 12485 removeOrUnwrapInvalidNode(node, schema); 12486 } 12487 } 12488 } 12489 }; 12490 const hasClosest = (node, parentName) => { 12491 let tempNode = node; 12492 while (tempNode) { 12493 if (tempNode.name === parentName) { 12494 return true; 12495 } 12496 tempNode = tempNode.parent; 12497 } 12498 return false; 12499 }; 12500 const isInvalid = (schema, node, parent = node.parent) => { 12501 if (parent && schema.children[node.name] && !schema.isValidChild(parent.name, node.name)) { 12502 return true; 12503 } else if (parent && node.name === 'a' && hasClosest(parent, 'a')) { 12504 return true; 12505 } else { 12506 return false; 12507 } 12508 }; 12509 12510 const createRange = (sc, so, ec, eo) => { 12511 const rng = document.createRange(); 12512 rng.setStart(sc, so); 12513 rng.setEnd(ec, eo); 12514 return rng; 12515 }; 12516 const normalizeBlockSelectionRange = rng => { 12517 const startPos = CaretPosition.fromRangeStart(rng); 12518 const endPos = CaretPosition.fromRangeEnd(rng); 12519 const rootNode = rng.commonAncestorContainer; 12520 return fromPosition(false, rootNode, endPos).map(newEndPos => { 12521 if (!isInSameBlock(startPos, endPos, rootNode) && isInSameBlock(startPos, newEndPos, rootNode)) { 12522 return createRange(startPos.container(), startPos.offset(), newEndPos.container(), newEndPos.offset()); 12523 } else { 12524 return rng; 12525 } 12526 }).getOr(rng); 12527 }; 12528 const normalize = rng => rng.collapsed ? rng : normalizeBlockSelectionRange(rng); 12529 12530 const hasOnlyOneChild$1 = node => { 12531 return isNonNullable(node.firstChild) && node.firstChild === node.lastChild; 12532 }; 12533 const isPaddingNode = node => { 12534 return node.name === 'br' || node.value === nbsp; 12535 }; 12536 const isPaddedEmptyBlock = (schema, node) => { 12537 const blockElements = schema.getBlockElements(); 12538 return blockElements[node.name] && hasOnlyOneChild$1(node) && isPaddingNode(node.firstChild); 12539 }; 12540 const isEmptyFragmentElement = (schema, node) => { 12541 const nonEmptyElements = schema.getNonEmptyElements(); 12542 return isNonNullable(node) && (node.isEmpty(nonEmptyElements) || isPaddedEmptyBlock(schema, node)); 12543 }; 12544 const isListFragment = (schema, fragment) => { 12545 let firstChild = fragment.firstChild; 12546 let lastChild = fragment.lastChild; 12547 if (firstChild && firstChild.name === 'meta') { 12548 firstChild = firstChild.next; 12549 } 12550 if (lastChild && lastChild.attr('id') === 'mce_marker') { 12551 lastChild = lastChild.prev; 12552 } 12553 if (isEmptyFragmentElement(schema, lastChild)) { 12554 lastChild = lastChild === null || lastChild === void 0 ? void 0 : lastChild.prev; 12555 } 12556 if (!firstChild || firstChild !== lastChild) { 12557 return false; 12558 } 12559 return firstChild.name === 'ul' || firstChild.name === 'ol'; 12560 }; 12561 const cleanupDomFragment = domFragment => { 12562 var _a, _b; 12563 const firstChild = domFragment.firstChild; 12564 const lastChild = domFragment.lastChild; 12565 if (firstChild && firstChild.nodeName === 'META') { 12566 (_a = firstChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(firstChild); 12567 } 12568 if (lastChild && lastChild.id === 'mce_marker') { 12569 (_b = lastChild.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(lastChild); 12570 } 12571 return domFragment; 12572 }; 12573 const toDomFragment = (dom, serializer, fragment) => { 12574 const html = serializer.serialize(fragment); 12575 const domFragment = dom.createFragment(html); 12576 return cleanupDomFragment(domFragment); 12577 }; 12578 const listItems = elm => { 12579 var _a; 12580 return filter$5((_a = elm === null || elm === void 0 ? void 0 : elm.childNodes) !== null && _a !== void 0 ? _a : [], child => { 12581 return child.nodeName === 'LI'; 12582 }); 12583 }; 12584 const isPadding = node => { 12585 return node.data === nbsp || isBr$6(node); 12586 }; 12587 const isListItemPadded = node => { 12588 return isNonNullable(node === null || node === void 0 ? void 0 : node.firstChild) && node.firstChild === node.lastChild && isPadding(node.firstChild); 12589 }; 12590 const isEmptyOrPadded = elm => { 12591 return !elm.firstChild || isListItemPadded(elm); 12592 }; 12593 const trimListItems = elms => { 12594 return elms.length > 0 && isEmptyOrPadded(elms[elms.length - 1]) ? elms.slice(0, -1) : elms; 12595 }; 12596 const getParentLi = (dom, node) => { 12597 const parentBlock = dom.getParent(node, dom.isBlock); 12598 return parentBlock && parentBlock.nodeName === 'LI' ? parentBlock : null; 12599 }; 12600 const isParentBlockLi = (dom, node) => { 12601 return !!getParentLi(dom, node); 12602 }; 12603 const getSplit = (parentNode, rng) => { 12604 const beforeRng = rng.cloneRange(); 12605 const afterRng = rng.cloneRange(); 12606 beforeRng.setStartBefore(parentNode); 12607 afterRng.setEndAfter(parentNode); 12608 return [ 12609 beforeRng.cloneContents(), 12610 afterRng.cloneContents() 12611 ]; 12612 }; 12613 const findFirstIn = (node, rootNode) => { 12614 const caretPos = CaretPosition.before(node); 12615 const caretWalker = CaretWalker(rootNode); 12616 const newCaretPos = caretWalker.next(caretPos); 12617 return newCaretPos ? newCaretPos.toRange() : null; 12618 }; 12619 const findLastOf = (node, rootNode) => { 12620 const caretPos = CaretPosition.after(node); 12621 const caretWalker = CaretWalker(rootNode); 12622 const newCaretPos = caretWalker.prev(caretPos); 12623 return newCaretPos ? newCaretPos.toRange() : null; 12624 }; 12625 const insertMiddle = (target, elms, rootNode, rng) => { 12626 const parts = getSplit(target, rng); 12627 const parentElm = target.parentNode; 12628 if (parentElm) { 12629 parentElm.insertBefore(parts[0], target); 12630 Tools.each(elms, li => { 12631 parentElm.insertBefore(li, target); 12632 }); 12633 parentElm.insertBefore(parts[1], target); 12634 parentElm.removeChild(target); 12635 } 12636 return findLastOf(elms[elms.length - 1], rootNode); 12637 }; 12638 const insertBefore$1 = (target, elms, rootNode) => { 12639 const parentElm = target.parentNode; 12640 if (parentElm) { 12641 Tools.each(elms, elm => { 12642 parentElm.insertBefore(elm, target); 12643 }); 12644 } 12645 return findFirstIn(target, rootNode); 12646 }; 12647 const insertAfter$1 = (target, elms, rootNode, dom) => { 12648 dom.insertAfter(elms.reverse(), target); 12649 return findLastOf(elms[0], rootNode); 12650 }; 12651 const insertAtCaret$1 = (serializer, dom, rng, fragment) => { 12652 const domFragment = toDomFragment(dom, serializer, fragment); 12653 const liTarget = getParentLi(dom, rng.startContainer); 12654 const liElms = trimListItems(listItems(domFragment.firstChild)); 12655 const BEGINNING = 1, END = 2; 12656 const rootNode = dom.getRoot(); 12657 const isAt = location => { 12658 const caretPos = CaretPosition.fromRangeStart(rng); 12659 const caretWalker = CaretWalker(dom.getRoot()); 12660 const newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos); 12661 const newPosNode = newPos === null || newPos === void 0 ? void 0 : newPos.getNode(); 12662 return newPosNode ? getParentLi(dom, newPosNode) !== liTarget : true; 12663 }; 12664 if (!liTarget) { 12665 return null; 12666 } else if (isAt(BEGINNING)) { 12667 return insertBefore$1(liTarget, liElms, rootNode); 12668 } else if (isAt(END)) { 12669 return insertAfter$1(liTarget, liElms, rootNode, dom); 12670 } else { 12671 return insertMiddle(liTarget, liElms, rootNode, rng); 12672 } 12673 }; 12674 12675 const mergeableWrappedElements = ['pre']; 12676 const shouldPasteContentOnly = (dom, fragment, parentNode, root) => { 12677 var _a; 12678 const firstNode = fragment.firstChild; 12679 const lastNode = fragment.lastChild; 12680 const last = lastNode.attr('data-mce-type') === 'bookmark' ? lastNode.prev : lastNode; 12681 const isPastingSingleElement = firstNode === last; 12682 const isWrappedElement = contains$2(mergeableWrappedElements, firstNode.name); 12683 if (isPastingSingleElement && isWrappedElement) { 12684 const isContentEditable = firstNode.attr('contenteditable') !== 'false'; 12685 const isPastingInTheSameBlockTag = ((_a = dom.getParent(parentNode, dom.isBlock)) === null || _a === void 0 ? void 0 : _a.nodeName.toLowerCase()) === firstNode.name; 12686 const isPastingInContentEditable = Optional.from(getContentEditableRoot$1(root, parentNode)).forall(isContentEditableTrue$3); 12687 return isContentEditable && isPastingInTheSameBlockTag && isPastingInContentEditable; 12688 } else { 12689 return false; 12690 } 12691 }; 12692 const isTableCell = isTableCell$3; 12693 const isTableCellContentSelected = (dom, rng, cell) => { 12694 if (isNonNullable(cell)) { 12695 const endCell = dom.getParent(rng.endContainer, isTableCell); 12696 return cell === endCell && hasAllContentsSelected(SugarElement.fromDom(cell), rng); 12697 } else { 12698 return false; 12699 } 12700 }; 12701 const validInsertion = (editor, value, parentNode) => { 12702 var _a; 12703 if (parentNode.getAttribute('data-mce-bogus') === 'all') { 12704 (_a = parentNode.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(editor.dom.createFragment(value), parentNode); 12705 } else { 12706 const node = parentNode.firstChild; 12707 const node2 = parentNode.lastChild; 12708 if (!node || node === node2 && node.nodeName === 'BR') { 12709 editor.dom.setHTML(parentNode, value); 12710 } else { 12711 editor.selection.setContent(value, { no_events: true }); 12712 } 12713 } 12714 }; 12715 const trimBrsFromTableCell = (dom, elm) => { 12716 Optional.from(dom.getParent(elm, 'td,th')).map(SugarElement.fromDom).each(trimBlockTrailingBr); 12717 }; 12718 const reduceInlineTextElements = (editor, merge) => { 12719 const textInlineElements = editor.schema.getTextInlineElements(); 12720 const dom = editor.dom; 12721 if (merge) { 12722 const root = editor.getBody(); 12723 const elementUtils = ElementUtils(editor); 12724 Tools.each(dom.select('*[data-mce-fragment]'), node => { 12725 const isInline = isNonNullable(textInlineElements[node.nodeName.toLowerCase()]); 12726 if (isInline && hasInheritableStyles(dom, node)) { 12727 for (let parentNode = node.parentElement; isNonNullable(parentNode) && parentNode !== root; parentNode = parentNode.parentElement) { 12728 const styleConflict = hasStyleConflict(dom, node, parentNode); 12729 if (styleConflict) { 12730 break; 12731 } 12732 if (elementUtils.compare(parentNode, node)) { 12733 dom.remove(node, true); 12734 break; 12735 } 12736 } 12737 } 12738 }); 12739 } 12740 }; 12741 const markFragmentElements = fragment => { 12742 let node = fragment; 12743 while (node = node.walk()) { 12744 if (node.type === 1) { 12745 node.attr('data-mce-fragment', '1'); 12746 } 12747 } 12748 }; 12749 const unmarkFragmentElements = elm => { 12750 Tools.each(elm.getElementsByTagName('*'), elm => { 12751 elm.removeAttribute('data-mce-fragment'); 12752 }); 12753 }; 12754 const isPartOfFragment = node => { 12755 return !!node.getAttribute('data-mce-fragment'); 12756 }; 12757 const canHaveChildren = (editor, node) => { 12758 return isNonNullable(node) && !editor.schema.getVoidElements()[node.nodeName]; 12759 }; 12760 const moveSelectionToMarker = (editor, marker) => { 12761 var _a, _b, _c; 12762 let nextRng; 12763 const dom = editor.dom; 12764 const selection = editor.selection; 12765 if (!marker) { 12766 return; 12767 } 12768 selection.scrollIntoView(marker); 12769 const parentEditableElm = getContentEditableRoot$1(editor.getBody(), marker); 12770 if (parentEditableElm && dom.getContentEditable(parentEditableElm) === 'false') { 12771 dom.remove(marker); 12772 selection.select(parentEditableElm); 12773 return; 12774 } 12775 let rng = dom.createRng(); 12776 const node = marker.previousSibling; 12777 if (isText$a(node)) { 12778 rng.setStart(node, (_b = (_a = node.nodeValue) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0); 12779 const node2 = marker.nextSibling; 12780 if (isText$a(node2)) { 12781 node.appendData(node2.data); 12782 (_c = node2.parentNode) === null || _c === void 0 ? void 0 : _c.removeChild(node2); 12783 } 12784 } else { 12785 rng.setStartBefore(marker); 12786 rng.setEndBefore(marker); 12787 } 12788 const findNextCaretRng = rng => { 12789 let caretPos = CaretPosition.fromRangeStart(rng); 12790 const caretWalker = CaretWalker(editor.getBody()); 12791 caretPos = caretWalker.next(caretPos); 12792 return caretPos === null || caretPos === void 0 ? void 0 : caretPos.toRange(); 12793 }; 12794 const parentBlock = dom.getParent(marker, dom.isBlock); 12795 dom.remove(marker); 12796 if (parentBlock && dom.isEmpty(parentBlock)) { 12797 empty(SugarElement.fromDom(parentBlock)); 12798 rng.setStart(parentBlock, 0); 12799 rng.setEnd(parentBlock, 0); 12800 if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) { 12801 rng = nextRng; 12802 dom.remove(parentBlock); 12803 } else { 12804 dom.add(parentBlock, dom.create('br', { 'data-mce-bogus': '1' })); 12805 } 12806 } 12807 selection.setRng(rng); 12808 }; 12809 const deleteSelectedContent = editor => { 12810 const dom = editor.dom; 12811 const rng = normalize(editor.selection.getRng()); 12812 editor.selection.setRng(rng); 12813 const startCell = dom.getParent(rng.startContainer, isTableCell); 12814 if (isTableCellContentSelected(dom, rng, startCell)) { 12815 deleteCellContents(editor, rng, SugarElement.fromDom(startCell)); 12816 } else if (rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset === 1 && isText$a(rng.startContainer.childNodes[rng.startOffset])) { 12817 rng.deleteContents(); 12818 } else { 12819 editor.getDoc().execCommand('Delete', false); 12820 } 12821 }; 12822 const findMarkerNode = scope => { 12823 for (let markerNode = scope; markerNode; markerNode = markerNode.walk()) { 12824 if (markerNode.attr('id') === 'mce_marker') { 12825 return Optional.some(markerNode); 12826 } 12827 } 12828 return Optional.none(); 12829 }; 12830 const preventSplittingSummary = editor => { 12831 each$e(from(editor.getBody().querySelectorAll('details')), accordion => { 12832 const summaries = filter$5(from(accordion.children), node => node.nodeName === 'SUMMARY'); 12833 if (summaries.length > 1) { 12834 each$e(summaries.slice(1), summary => { 12835 const element = SugarElement.fromDom(summary); 12836 remove$7(element, 'mce-accordion-summary'); 12837 mutate(element, 'p'); 12838 }); 12839 } 12840 }); 12841 }; 12842 const insertHtmlAtCaret = (editor, value, details) => { 12843 var _a, _b; 12844 const selection = editor.selection; 12845 const dom = editor.dom; 12846 const parser = editor.parser; 12847 const merge = details.merge; 12848 const serializer = HtmlSerializer({ validate: true }, editor.schema); 12849 const bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark"></span>'; 12850 if (value.indexOf('{$caret}') === -1) { 12851 value += '{$caret}'; 12852 } 12853 value = value.replace(/\{\$caret\}/, bookmarkHtml); 12854 let rng = selection.getRng(); 12855 const caretElement = rng.startContainer; 12856 const body = editor.getBody(); 12857 if (caretElement === body && selection.isCollapsed()) { 12858 if (dom.isBlock(body.firstChild) && canHaveChildren(editor, body.firstChild) && dom.isEmpty(body.firstChild)) { 12859 rng = dom.createRng(); 12860 rng.setStart(body.firstChild, 0); 12861 rng.setEnd(body.firstChild, 0); 12862 selection.setRng(rng); 12863 } 12864 } 12865 if (!selection.isCollapsed()) { 12866 deleteSelectedContent(editor); 12867 } 12868 const parentNode = selection.getNode(); 12869 const parserArgs = { 12870 context: parentNode.nodeName.toLowerCase(), 12871 data: details.data, 12872 insert: true 12873 }; 12874 const fragment = parser.parse(value, parserArgs); 12875 if (details.paste === true && isListFragment(editor.schema, fragment) && isParentBlockLi(dom, parentNode)) { 12876 rng = insertAtCaret$1(serializer, dom, selection.getRng(), fragment); 12877 if (rng) { 12878 selection.setRng(rng); 12879 } 12880 return value; 12881 } 12882 if (details.paste === true && shouldPasteContentOnly(dom, fragment, parentNode, editor.getBody())) { 12883 (_a = fragment.firstChild) === null || _a === void 0 ? void 0 : _a.unwrap(); 12884 } 12885 markFragmentElements(fragment); 12886 let node = fragment.lastChild; 12887 if (node && node.attr('id') === 'mce_marker') { 12888 const marker = node; 12889 for (node = node.prev; node; node = node.walk(true)) { 12890 if (node.type === 3 || !dom.isBlock(node.name)) { 12891 if (node.parent && editor.schema.isValidChild(node.parent.name, 'span')) { 12892 node.parent.insert(marker, node, node.name === 'br'); 12893 } 12894 break; 12895 } 12896 } 12897 } 12898 editor._selectionOverrides.showBlockCaretContainer(parentNode); 12899 if (!parserArgs.invalid) { 12900 value = serializer.serialize(fragment); 12901 validInsertion(editor, value, parentNode); 12902 } else { 12903 editor.selection.setContent(bookmarkHtml); 12904 let parentNode = selection.getNode(); 12905 let tempNode; 12906 const rootNode = editor.getBody(); 12907 if (isDocument$1(parentNode)) { 12908 parentNode = tempNode = rootNode; 12909 } else { 12910 tempNode = parentNode; 12911 } 12912 while (tempNode && tempNode !== rootNode) { 12913 parentNode = tempNode; 12914 tempNode = tempNode.parentNode; 12915 } 12916 value = parentNode === rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode); 12917 const root = parser.parse(value); 12918 const markerNode = findMarkerNode(root); 12919 const editingHost = markerNode.bind(findClosestEditingHost).getOr(root); 12920 markerNode.each(marker => marker.replace(fragment)); 12921 const toExtract = fragment.children(); 12922 const parent = (_b = fragment.parent) !== null && _b !== void 0 ? _b : root; 12923 fragment.unwrap(); 12924 const invalidChildren = filter$5(toExtract, node => isInvalid(editor.schema, node, parent)); 12925 cleanInvalidNodes(invalidChildren, editor.schema, editingHost); 12926 filter$2(parser.getNodeFilters(), parser.getAttributeFilters(), root); 12927 value = serializer.serialize(root); 12928 if (parentNode === rootNode) { 12929 dom.setHTML(rootNode, value); 12930 } else { 12931 dom.setOuterHTML(parentNode, value); 12932 } 12933 } 12934 reduceInlineTextElements(editor, merge); 12935 moveSelectionToMarker(editor, dom.get('mce_marker')); 12936 unmarkFragmentElements(editor.getBody()); 12937 trimBrsFromTableCell(dom, selection.getStart()); 12938 preventSplittingSummary(editor); 12939 updateCaret(editor.schema, editor.getBody(), selection.getStart()); 12940 return value; 12941 }; 12942 12943 const isTreeNode = content => content instanceof AstNode; 12944 12945 const moveSelection = editor => { 12946 if (hasFocus(editor)) { 12947 firstPositionIn(editor.getBody()).each(pos => { 12948 const node = pos.getNode(); 12949 const caretPos = isTable$2(node) ? firstPositionIn(node).getOr(pos) : pos; 12950 editor.selection.setRng(caretPos.toRange()); 12951 }); 12952 } 12953 }; 12954 const setEditorHtml = (editor, html, noSelection) => { 12955 editor.dom.setHTML(editor.getBody(), html); 12956 if (noSelection !== true) { 12957 moveSelection(editor); 12958 } 12959 }; 12960 const setContentString = (editor, body, content, args) => { 12961 if (content.length === 0 || /^\s+$/.test(content)) { 12962 const padd = '<br data-mce-bogus="1">'; 12963 if (body.nodeName === 'TABLE') { 12964 content = '<tr><td>' + padd + '</td></tr>'; 12965 } else if (/^(UL|OL)$/.test(body.nodeName)) { 12966 content = '<li>' + padd + '</li>'; 12967 } 12968 const forcedRootBlockName = getForcedRootBlock(editor); 12969 if (editor.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) { 12970 content = padd; 12971 content = editor.dom.createHTML(forcedRootBlockName, getForcedRootBlockAttrs(editor), content); 12972 } else if (!content) { 12973 content = padd; 12974 } 12975 setEditorHtml(editor, content, args.no_selection); 12976 return { 12977 content, 12978 html: content 12979 }; 12980 } else { 12981 if (args.format !== 'raw') { 12982 content = HtmlSerializer({ validate: false }, editor.schema).serialize(editor.parser.parse(content, { 12983 isRootContent: true, 12984 insert: true 12985 })); 12986 } 12987 const trimmedHtml = isWsPreserveElement(SugarElement.fromDom(body)) ? content : Tools.trim(content); 12988 setEditorHtml(editor, trimmedHtml, args.no_selection); 12989 return { 12990 content: trimmedHtml, 12991 html: trimmedHtml 12992 }; 12993 } 12994 }; 12995 const setContentTree = (editor, body, content, args) => { 12996 filter$2(editor.parser.getNodeFilters(), editor.parser.getAttributeFilters(), content); 12997 const html = HtmlSerializer({ validate: false }, editor.schema).serialize(content); 12998 const trimmedHtml = isWsPreserveElement(SugarElement.fromDom(body)) ? html : Tools.trim(html); 12999 setEditorHtml(editor, trimmedHtml, args.no_selection); 13000 return { 13001 content, 13002 html: trimmedHtml 13003 }; 13004 }; 13005 const setContentInternal = (editor, content, args) => { 13006 return Optional.from(editor.getBody()).map(body => { 13007 if (isTreeNode(content)) { 13008 return setContentTree(editor, body, content, args); 13009 } else { 13010 return setContentString(editor, body, content, args); 13011 } 13012 }).getOr({ 13013 content, 13014 html: isTreeNode(args.content) ? '' : args.content 13015 }); 13016 }; 13017 13018 const ancestor$1 = (scope, predicate, isRoot) => ancestor$4(scope, predicate, isRoot).isSome(); 13019 const sibling = (scope, predicate) => sibling$1(scope, predicate).isSome(); 13020 13021 const ensureIsRoot = isRoot => isFunction(isRoot) ? isRoot : never; 13022 const ancestor = (scope, transform, isRoot) => { 13023 let element = scope.dom; 13024 const stop = ensureIsRoot(isRoot); 13025 while (element.parentNode) { 13026 element = element.parentNode; 13027 const el = SugarElement.fromDom(element); 13028 const transformed = transform(el); 13029 if (transformed.isSome()) { 13030 return transformed; 13031 } else if (stop(el)) { 13032 break; 13033 } 13034 } 13035 return Optional.none(); 13036 }; 13037 const closest$1 = (scope, transform, isRoot) => { 13038 const current = transform(scope); 13039 const stop = ensureIsRoot(isRoot); 13040 return current.orThunk(() => stop(scope) ? Optional.none() : ancestor(scope, transform, stop)); 13041 }; 13042 13043 const isEq$3 = isEq$5; 13044 const matchesUnInheritedFormatSelector = (ed, node, name) => { 13045 const formatList = ed.formatter.get(name); 13046 if (formatList) { 13047 for (let i = 0; i < formatList.length; i++) { 13048 const format = formatList[i]; 13049 if (isSelectorFormat(format) && format.inherit === false && ed.dom.is(node, format.selector)) { 13050 return true; 13051 } 13052 } 13053 } 13054 return false; 13055 }; 13056 const matchParents = (editor, node, name, vars, similar) => { 13057 const root = editor.dom.getRoot(); 13058 if (node === root) { 13059 return false; 13060 } 13061 const matchedNode = editor.dom.getParent(node, elm => { 13062 if (matchesUnInheritedFormatSelector(editor, elm, name)) { 13063 return true; 13064 } 13065 return elm.parentNode === root || !!matchNode(editor, elm, name, vars, true); 13066 }); 13067 return !!matchNode(editor, matchedNode, name, vars, similar); 13068 }; 13069 const matchName = (dom, node, format) => { 13070 if (isInlineFormat(format) && isEq$3(node, format.inline)) { 13071 return true; 13072 } 13073 if (isBlockFormat(format) && isEq$3(node, format.block)) { 13074 return true; 13075 } 13076 if (isSelectorFormat(format)) { 13077 return isElement$6(node) && dom.is(node, format.selector); 13078 } 13079 return false; 13080 }; 13081 const matchItems = (dom, node, format, itemName, similar, vars) => { 13082 const items = format[itemName]; 13083 const matchAttributes = itemName === 'attributes'; 13084 if (isFunction(format.onmatch)) { 13085 return format.onmatch(node, format, itemName); 13086 } 13087 if (items) { 13088 if (!isArrayLike(items)) { 13089 for (const key in items) { 13090 if (has$2(items, key)) { 13091 const value = matchAttributes ? dom.getAttrib(node, key) : getStyle(dom, node, key); 13092 const expectedValue = replaceVars(items[key], vars); 13093 const isEmptyValue = isNullable(value) || isEmpty$3(value); 13094 if (isEmptyValue && isNullable(expectedValue)) { 13095 continue; 13096 } 13097 if (similar && isEmptyValue && !format.exact) { 13098 return false; 13099 } 13100 if ((!similar || format.exact) && !isEq$3(value, normalizeStyleValue(expectedValue, key))) { 13101 return false; 13102 } 13103 } 13104 } 13105 } else { 13106 for (let i = 0; i < items.length; i++) { 13107 if (matchAttributes ? dom.getAttrib(node, items[i]) : getStyle(dom, node, items[i])) { 13108 return true; 13109 } 13110 } 13111 } 13112 } 13113 return true; 13114 }; 13115 const matchNode = (ed, node, name, vars, similar) => { 13116 const formatList = ed.formatter.get(name); 13117 const dom = ed.dom; 13118 if (formatList && isElement$6(node)) { 13119 for (let i = 0; i < formatList.length; i++) { 13120 const format = formatList[i]; 13121 if (matchName(ed.dom, node, format) && matchItems(dom, node, format, 'attributes', similar, vars) && matchItems(dom, node, format, 'styles', similar, vars)) { 13122 const classes = format.classes; 13123 if (classes) { 13124 for (let x = 0; x < classes.length; x++) { 13125 if (!ed.dom.hasClass(node, replaceVars(classes[x], vars))) { 13126 return; 13127 } 13128 } 13129 } 13130 return format; 13131 } 13132 } 13133 } 13134 return undefined; 13135 }; 13136 const match$2 = (editor, name, vars, node, similar) => { 13137 if (node) { 13138 return matchParents(editor, node, name, vars, similar); 13139 } 13140 node = editor.selection.getNode(); 13141 if (matchParents(editor, node, name, vars, similar)) { 13142 return true; 13143 } 13144 const startNode = editor.selection.getStart(); 13145 if (startNode !== node) { 13146 if (matchParents(editor, startNode, name, vars, similar)) { 13147 return true; 13148 } 13149 } 13150 return false; 13151 }; 13152 const matchAll = (editor, names, vars) => { 13153 const matchedFormatNames = []; 13154 const checkedMap = {}; 13155 const startElement = editor.selection.getStart(); 13156 editor.dom.getParent(startElement, node => { 13157 for (let i = 0; i < names.length; i++) { 13158 const name = names[i]; 13159 if (!checkedMap[name] && matchNode(editor, node, name, vars)) { 13160 checkedMap[name] = true; 13161 matchedFormatNames.push(name); 13162 } 13163 } 13164 }, editor.dom.getRoot()); 13165 return matchedFormatNames; 13166 }; 13167 const closest = (editor, names) => { 13168 const isRoot = elm => eq(elm, SugarElement.fromDom(editor.getBody())); 13169 const match = (elm, name) => matchNode(editor, elm.dom, name) ? Optional.some(name) : Optional.none(); 13170 return Optional.from(editor.selection.getStart(true)).bind(rawElm => closest$1(SugarElement.fromDom(rawElm), elm => findMap(names, name => match(elm, name)), isRoot)).getOrNull(); 13171 }; 13172 const canApply = (editor, name) => { 13173 const formatList = editor.formatter.get(name); 13174 const dom = editor.dom; 13175 if (formatList && editor.selection.isEditable()) { 13176 const startNode = editor.selection.getStart(); 13177 const parents = getParents$2(dom, startNode); 13178 for (let x = formatList.length - 1; x >= 0; x--) { 13179 const format = formatList[x]; 13180 if (!isSelectorFormat(format)) { 13181 return true; 13182 } 13183 for (let i = parents.length - 1; i >= 0; i--) { 13184 if (dom.is(parents[i], format.selector)) { 13185 return true; 13186 } 13187 } 13188 } 13189 } 13190 return false; 13191 }; 13192 const matchAllOnNode = (editor, node, formatNames) => foldl(formatNames, (acc, name) => { 13193 const matchSimilar = isVariableFormatName(editor, name); 13194 if (editor.formatter.matchNode(node, name, {}, matchSimilar)) { 13195 return acc.concat([name]); 13196 } else { 13197 return acc; 13198 } 13199 }, []); 13200 13201 const ZWSP = ZWSP$1; 13202 const importNode = (ownerDocument, node) => { 13203 return ownerDocument.importNode(node, true); 13204 }; 13205 const getEmptyCaretContainers = node => { 13206 const nodes = []; 13207 let tempNode = node; 13208 while (tempNode) { 13209 if (isText$a(tempNode) && tempNode.data !== ZWSP || tempNode.childNodes.length > 1) { 13210 return []; 13211 } 13212 if (isElement$6(tempNode)) { 13213 nodes.push(tempNode); 13214 } 13215 tempNode = tempNode.firstChild; 13216 } 13217 return nodes; 13218 }; 13219 const isCaretContainerEmpty = node => { 13220 return getEmptyCaretContainers(node).length > 0; 13221 }; 13222 const findFirstTextNode = node => { 13223 if (node) { 13224 const walker = new DomTreeWalker(node, node); 13225 for (let tempNode = walker.current(); tempNode; tempNode = walker.next()) { 13226 if (isText$a(tempNode)) { 13227 return tempNode; 13228 } 13229 } 13230 } 13231 return null; 13232 }; 13233 const createCaretContainer = fill => { 13234 const caretContainer = SugarElement.fromTag('span'); 13235 setAll$1(caretContainer, { 13236 'id': CARET_ID, 13237 'data-mce-bogus': '1', 13238 'data-mce-type': 'format-caret' 13239 }); 13240 if (fill) { 13241 append$1(caretContainer, SugarElement.fromText(ZWSP)); 13242 } 13243 return caretContainer; 13244 }; 13245 const trimZwspFromCaretContainer = caretContainerNode => { 13246 const textNode = findFirstTextNode(caretContainerNode); 13247 if (textNode && textNode.data.charAt(0) === ZWSP) { 13248 textNode.deleteData(0, 1); 13249 } 13250 return textNode; 13251 }; 13252 const removeCaretContainerNode = (editor, node, moveCaret = true) => { 13253 const dom = editor.dom, selection = editor.selection; 13254 if (isCaretContainerEmpty(node)) { 13255 deleteElement$2(editor, false, SugarElement.fromDom(node), moveCaret); 13256 } else { 13257 const rng = selection.getRng(); 13258 const block = dom.getParent(node, dom.isBlock); 13259 const startContainer = rng.startContainer; 13260 const startOffset = rng.startOffset; 13261 const endContainer = rng.endContainer; 13262 const endOffset = rng.endOffset; 13263 const textNode = trimZwspFromCaretContainer(node); 13264 dom.remove(node, true); 13265 if (startContainer === textNode && startOffset > 0) { 13266 rng.setStart(textNode, startOffset - 1); 13267 } 13268 if (endContainer === textNode && endOffset > 0) { 13269 rng.setEnd(textNode, endOffset - 1); 13270 } 13271 if (block && dom.isEmpty(block)) { 13272 fillWithPaddingBr(SugarElement.fromDom(block)); 13273 } 13274 selection.setRng(rng); 13275 } 13276 }; 13277 const removeCaretContainer = (editor, node, moveCaret = true) => { 13278 const dom = editor.dom, selection = editor.selection; 13279 if (!node) { 13280 node = getParentCaretContainer(editor.getBody(), selection.getStart()); 13281 if (!node) { 13282 while (node = dom.get(CARET_ID)) { 13283 removeCaretContainerNode(editor, node, moveCaret); 13284 } 13285 } 13286 } else { 13287 removeCaretContainerNode(editor, node, moveCaret); 13288 } 13289 }; 13290 const insertCaretContainerNode = (editor, caretContainer, formatNode) => { 13291 var _a, _b; 13292 const dom = editor.dom; 13293 const block = dom.getParent(formatNode, curry(isTextBlock$1, editor.schema)); 13294 if (block && dom.isEmpty(block)) { 13295 (_a = formatNode.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(caretContainer, formatNode); 13296 } else { 13297 removeTrailingBr(SugarElement.fromDom(formatNode)); 13298 if (dom.isEmpty(formatNode)) { 13299 (_b = formatNode.parentNode) === null || _b === void 0 ? void 0 : _b.replaceChild(caretContainer, formatNode); 13300 } else { 13301 dom.insertAfter(caretContainer, formatNode); 13302 } 13303 } 13304 }; 13305 const appendNode = (parentNode, node) => { 13306 parentNode.appendChild(node); 13307 return node; 13308 }; 13309 const insertFormatNodesIntoCaretContainer = (formatNodes, caretContainer) => { 13310 var _a; 13311 const innerMostFormatNode = foldr(formatNodes, (parentNode, formatNode) => { 13312 return appendNode(parentNode, formatNode.cloneNode(false)); 13313 }, caretContainer); 13314 const doc = (_a = innerMostFormatNode.ownerDocument) !== null && _a !== void 0 ? _a : document; 13315 return appendNode(innerMostFormatNode, doc.createTextNode(ZWSP)); 13316 }; 13317 const cleanFormatNode = (editor, caretContainer, formatNode, name, vars, similar) => { 13318 const formatter = editor.formatter; 13319 const dom = editor.dom; 13320 const validFormats = filter$5(keys(formatter.get()), formatName => formatName !== name && !contains$1(formatName, 'removeformat')); 13321 const matchedFormats = matchAllOnNode(editor, formatNode, validFormats); 13322 const uniqueFormats = filter$5(matchedFormats, fmtName => !areSimilarFormats(editor, fmtName, name)); 13323 if (uniqueFormats.length > 0) { 13324 const clonedFormatNode = formatNode.cloneNode(false); 13325 dom.add(caretContainer, clonedFormatNode); 13326 formatter.remove(name, vars, clonedFormatNode, similar); 13327 dom.remove(clonedFormatNode); 13328 return Optional.some(clonedFormatNode); 13329 } else { 13330 return Optional.none(); 13331 } 13332 }; 13333 const applyCaretFormat = (editor, name, vars) => { 13334 let caretContainer; 13335 const selection = editor.selection; 13336 const formatList = editor.formatter.get(name); 13337 if (!formatList) { 13338 return; 13339 } 13340 const selectionRng = selection.getRng(); 13341 let offset = selectionRng.startOffset; 13342 const container = selectionRng.startContainer; 13343 const text = container.nodeValue; 13344 caretContainer = getParentCaretContainer(editor.getBody(), selection.getStart()); 13345 const wordcharRegex = /[^\s\u00a0\u00ad\u200b\ufeff]/; 13346 if (text && offset > 0 && offset < text.length && wordcharRegex.test(text.charAt(offset)) && wordcharRegex.test(text.charAt(offset - 1))) { 13347 const bookmark = selection.getBookmark(); 13348 selectionRng.collapse(true); 13349 let rng = expandRng(editor.dom, selectionRng, formatList); 13350 rng = split(rng); 13351 editor.formatter.apply(name, vars, rng); 13352 selection.moveToBookmark(bookmark); 13353 } else { 13354 let textNode = caretContainer ? findFirstTextNode(caretContainer) : null; 13355 if (!caretContainer || (textNode === null || textNode === void 0 ? void 0 : textNode.data) !== ZWSP) { 13356 caretContainer = importNode(editor.getDoc(), createCaretContainer(true).dom); 13357 textNode = caretContainer.firstChild; 13358 selectionRng.insertNode(caretContainer); 13359 offset = 1; 13360 editor.formatter.apply(name, vars, caretContainer); 13361 } else { 13362 editor.formatter.apply(name, vars, caretContainer); 13363 } 13364 selection.setCursorLocation(textNode, offset); 13365 } 13366 }; 13367 const removeCaretFormat = (editor, name, vars, similar) => { 13368 const dom = editor.dom; 13369 const selection = editor.selection; 13370 let hasContentAfter = false; 13371 const formatList = editor.formatter.get(name); 13372 if (!formatList) { 13373 return; 13374 } 13375 const rng = selection.getRng(); 13376 const container = rng.startContainer; 13377 const offset = rng.startOffset; 13378 let node = container; 13379 if (isText$a(container)) { 13380 if (offset !== container.data.length) { 13381 hasContentAfter = true; 13382 } 13383 node = node.parentNode; 13384 } 13385 const parents = []; 13386 let formatNode; 13387 while (node) { 13388 if (matchNode(editor, node, name, vars, similar)) { 13389 formatNode = node; 13390 break; 13391 } 13392 if (node.nextSibling) { 13393 hasContentAfter = true; 13394 } 13395 parents.push(node); 13396 node = node.parentNode; 13397 } 13398 if (!formatNode) { 13399 return; 13400 } 13401 if (hasContentAfter) { 13402 const bookmark = selection.getBookmark(); 13403 rng.collapse(true); 13404 let expandedRng = expandRng(dom, rng, formatList, true); 13405 expandedRng = split(expandedRng); 13406 editor.formatter.remove(name, vars, expandedRng, similar); 13407 selection.moveToBookmark(bookmark); 13408 } else { 13409 const caretContainer = getParentCaretContainer(editor.getBody(), formatNode); 13410 const newCaretContainer = createCaretContainer(false).dom; 13411 insertCaretContainerNode(editor, newCaretContainer, caretContainer !== null && caretContainer !== void 0 ? caretContainer : formatNode); 13412 const cleanedFormatNode = cleanFormatNode(editor, newCaretContainer, formatNode, name, vars, similar); 13413 const caretTextNode = insertFormatNodesIntoCaretContainer(parents.concat(cleanedFormatNode.toArray()), newCaretContainer); 13414 if (caretContainer) { 13415 removeCaretContainerNode(editor, caretContainer, false); 13416 } 13417 selection.setCursorLocation(caretTextNode, 1); 13418 if (dom.isEmpty(formatNode)) { 13419 dom.remove(formatNode); 13420 } 13421 } 13422 }; 13423 const disableCaretContainer = (editor, keyCode, moveCaret) => { 13424 const selection = editor.selection, body = editor.getBody(); 13425 removeCaretContainer(editor, null, moveCaret); 13426 if ((keyCode === 8 || keyCode === 46) && selection.isCollapsed() && selection.getStart().innerHTML === ZWSP) { 13427 removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart())); 13428 } 13429 if (keyCode === 37 || keyCode === 39) { 13430 removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart())); 13431 } 13432 }; 13433 const endsWithNbsp = element => isText$a(element) && endsWith(element.data, nbsp); 13434 const setup$u = editor => { 13435 editor.on('mouseup keydown', e => { 13436 disableCaretContainer(editor, e.keyCode, endsWithNbsp(editor.selection.getRng().endContainer)); 13437 }); 13438 }; 13439 const createCaretFormat = formatNodes => { 13440 const caretContainer = createCaretContainer(false); 13441 const innerMost = insertFormatNodesIntoCaretContainer(formatNodes, caretContainer.dom); 13442 return { 13443 caretContainer, 13444 caretPosition: CaretPosition(innerMost, 0) 13445 }; 13446 }; 13447 const replaceWithCaretFormat = (targetNode, formatNodes) => { 13448 const {caretContainer, caretPosition} = createCaretFormat(formatNodes); 13449 before$3(SugarElement.fromDom(targetNode), caretContainer); 13450 remove$5(SugarElement.fromDom(targetNode)); 13451 return caretPosition; 13452 }; 13453 const createCaretFormatAtStart$1 = (rng, formatNodes) => { 13454 const {caretContainer, caretPosition} = createCaretFormat(formatNodes); 13455 rng.insertNode(caretContainer.dom); 13456 return caretPosition; 13457 }; 13458 const isFormatElement = (editor, element) => { 13459 const inlineElements = editor.schema.getTextInlineElements(); 13460 return has$2(inlineElements, name(element)) && !isCaretNode(element.dom) && !isBogus$2(element.dom); 13461 }; 13462 const isEmptyCaretFormatElement = element => { 13463 return isCaretNode(element.dom) && isCaretContainerEmpty(element.dom); 13464 }; 13465 13466 const postProcessHooks = {}; 13467 const isPre = matchNodeNames(['pre']); 13468 const addPostProcessHook = (name, hook) => { 13469 const hooks = postProcessHooks[name]; 13470 if (!hooks) { 13471 postProcessHooks[name] = []; 13472 } 13473 postProcessHooks[name].push(hook); 13474 }; 13475 const postProcess$1 = (name, editor) => { 13476 if (has$2(postProcessHooks, name)) { 13477 each$e(postProcessHooks[name], hook => { 13478 hook(editor); 13479 }); 13480 } 13481 }; 13482 addPostProcessHook('pre', editor => { 13483 const rng = editor.selection.getRng(); 13484 const hasPreSibling = blocks => pre => { 13485 const prev = pre.previousSibling; 13486 return isPre(prev) && contains$2(blocks, prev); 13487 }; 13488 const joinPre = (pre1, pre2) => { 13489 const sPre2 = SugarElement.fromDom(pre2); 13490 const doc = documentOrOwner(sPre2).dom; 13491 remove$5(sPre2); 13492 append(SugarElement.fromDom(pre1), [ 13493 SugarElement.fromTag('br', doc), 13494 SugarElement.fromTag('br', doc), 13495 ...children$1(sPre2) 13496 ]); 13497 }; 13498 if (!rng.collapsed) { 13499 const blocks = editor.selection.getSelectedBlocks(); 13500 const preBlocks = filter$5(filter$5(blocks, isPre), hasPreSibling(blocks)); 13501 each$e(preBlocks, pre => { 13502 joinPre(pre.previousSibling, pre); 13503 }); 13504 } 13505 }); 13506 13507 const listItemStyles = [ 13508 'fontWeight', 13509 'fontStyle', 13510 'color', 13511 'fontSize', 13512 'fontFamily' 13513 ]; 13514 const hasListStyles = fmt => isObject(fmt.styles) && exists(keys(fmt.styles), name => contains$2(listItemStyles, name)); 13515 const findExpandedListItemFormat = formats => find$2(formats, fmt => isInlineFormat(fmt) && fmt.inline === 'span' && hasListStyles(fmt)); 13516 const getExpandedListItemFormat = (formatter, format) => { 13517 const formatList = formatter.get(format); 13518 return isArray$1(formatList) ? findExpandedListItemFormat(formatList) : Optional.none(); 13519 }; 13520 const isRngStartAtStartOfElement = (rng, elm) => prevPosition(elm, CaretPosition.fromRangeStart(rng)).isNone(); 13521 const isRngEndAtEndOfElement = (rng, elm) => { 13522 return nextPosition(elm, CaretPosition.fromRangeEnd(rng)).exists(pos => !isBr$6(pos.getNode()) || nextPosition(elm, pos).isSome()) === false; 13523 }; 13524 const isEditableListItem = dom => elm => isListItem$2(elm) && dom.isEditable(elm); 13525 const getFullySelectedBlocks = selection => { 13526 const blocks = selection.getSelectedBlocks(); 13527 const rng = selection.getRng(); 13528 if (selection.isCollapsed()) { 13529 return []; 13530 } 13531 if (blocks.length === 1) { 13532 return isRngStartAtStartOfElement(rng, blocks[0]) && isRngEndAtEndOfElement(rng, blocks[0]) ? blocks : []; 13533 } else { 13534 const first = head(blocks).filter(elm => isRngStartAtStartOfElement(rng, elm)).toArray(); 13535 const last = last$3(blocks).filter(elm => isRngEndAtEndOfElement(rng, elm)).toArray(); 13536 const middle = blocks.slice(1, -1); 13537 return first.concat(middle).concat(last); 13538 } 13539 }; 13540 const getFullySelectedListItems = selection => filter$5(getFullySelectedBlocks(selection), isEditableListItem(selection.dom)); 13541 const getPartiallySelectedListItems = selection => filter$5(selection.getSelectedBlocks(), isEditableListItem(selection.dom)); 13542 13543 const each$8 = Tools.each; 13544 const isElementNode = node => isElement$6(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$2(node); 13545 const findElementSibling = (node, siblingName) => { 13546 for (let sibling = node; sibling; sibling = sibling[siblingName]) { 13547 if (isText$a(sibling) && isNotEmpty(sibling.data)) { 13548 return node; 13549 } 13550 if (isElement$6(sibling) && !isBookmarkNode$1(sibling)) { 13551 return sibling; 13552 } 13553 } 13554 return node; 13555 }; 13556 const mergeSiblingsNodes = (editor, prev, next) => { 13557 const elementUtils = ElementUtils(editor); 13558 const isPrevEditable = isElement$6(prev) && isEditable$2(prev); 13559 const isNextEditable = isElement$6(next) && isEditable$2(next); 13560 if (isPrevEditable && isNextEditable) { 13561 const prevSibling = findElementSibling(prev, 'previousSibling'); 13562 const nextSibling = findElementSibling(next, 'nextSibling'); 13563 if (elementUtils.compare(prevSibling, nextSibling)) { 13564 for (let sibling = prevSibling.nextSibling; sibling && sibling !== nextSibling;) { 13565 const tmpSibling = sibling; 13566 sibling = sibling.nextSibling; 13567 prevSibling.appendChild(tmpSibling); 13568 } 13569 editor.dom.remove(nextSibling); 13570 Tools.each(Tools.grep(nextSibling.childNodes), node => { 13571 prevSibling.appendChild(node); 13572 }); 13573 return prevSibling; 13574 } 13575 } 13576 return next; 13577 }; 13578 const mergeSiblings = (editor, format, vars, node) => { 13579 var _a; 13580 if (node && format.merge_siblings !== false) { 13581 const newNode = (_a = mergeSiblingsNodes(editor, getNonWhiteSpaceSibling(node), node)) !== null && _a !== void 0 ? _a : node; 13582 mergeSiblingsNodes(editor, newNode, getNonWhiteSpaceSibling(newNode, true)); 13583 } 13584 }; 13585 const clearChildStyles = (dom, format, node) => { 13586 if (format.clear_child_styles) { 13587 const selector = format.links ? '*:not(a)' : '*'; 13588 each$8(dom.select(selector, node), childNode => { 13589 if (isElementNode(childNode) && isEditable$2(childNode)) { 13590 each$8(format.styles, (_value, name) => { 13591 dom.setStyle(childNode, name, ''); 13592 }); 13593 } 13594 }); 13595 } 13596 }; 13597 const processChildElements = (node, filter, process) => { 13598 each$8(node.childNodes, node => { 13599 if (isElementNode(node)) { 13600 if (filter(node)) { 13601 process(node); 13602 } 13603 if (node.hasChildNodes()) { 13604 processChildElements(node, filter, process); 13605 } 13606 } 13607 }); 13608 }; 13609 const unwrapEmptySpan = (dom, node) => { 13610 if (node.nodeName === 'SPAN' && dom.getAttribs(node).length === 0) { 13611 dom.remove(node, true); 13612 } 13613 }; 13614 const hasStyle = (dom, name) => node => !!(node && getStyle(dom, node, name)); 13615 const applyStyle = (dom, name, value) => node => { 13616 dom.setStyle(node, name, value); 13617 if (node.getAttribute('style') === '') { 13618 node.removeAttribute('style'); 13619 } 13620 unwrapEmptySpan(dom, node); 13621 }; 13622 13623 const removeResult = Adt.generate([ 13624 { keep: [] }, 13625 { rename: ['name'] }, 13626 { removed: [] } 13627 ]); 13628 const MCE_ATTR_RE = /^(src|href|style)$/; 13629 const each$7 = Tools.each; 13630 const isEq$2 = isEq$5; 13631 const isTableCellOrRow = node => /^(TR|TH|TD)$/.test(node.nodeName); 13632 const isChildOfInlineParent = (dom, node, parent) => dom.isChildOf(node, parent) && node !== parent && !dom.isBlock(parent); 13633 const getContainer = (ed, rng, start) => { 13634 let container = rng[start ? 'startContainer' : 'endContainer']; 13635 let offset = rng[start ? 'startOffset' : 'endOffset']; 13636 if (isElement$6(container)) { 13637 const lastIdx = container.childNodes.length - 1; 13638 if (!start && offset) { 13639 offset--; 13640 } 13641 container = container.childNodes[offset > lastIdx ? lastIdx : offset]; 13642 } 13643 if (isText$a(container) && start && offset >= container.data.length) { 13644 container = new DomTreeWalker(container, ed.getBody()).next() || container; 13645 } 13646 if (isText$a(container) && !start && offset === 0) { 13647 container = new DomTreeWalker(container, ed.getBody()).prev() || container; 13648 } 13649 return container; 13650 }; 13651 const normalizeTableSelection = (node, start) => { 13652 const prop = start ? 'firstChild' : 'lastChild'; 13653 const childNode = node[prop]; 13654 if (isTableCellOrRow(node) && childNode) { 13655 if (node.nodeName === 'TR') { 13656 return childNode[prop] || childNode; 13657 } else { 13658 return childNode; 13659 } 13660 } 13661 return node; 13662 }; 13663 const wrap$1 = (dom, node, name, attrs) => { 13664 var _a; 13665 const wrapper = dom.create(name, attrs); 13666 (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(wrapper, node); 13667 wrapper.appendChild(node); 13668 return wrapper; 13669 }; 13670 const wrapWithSiblings = (dom, node, next, name, attrs) => { 13671 const start = SugarElement.fromDom(node); 13672 const wrapper = SugarElement.fromDom(dom.create(name, attrs)); 13673 const siblings = next ? nextSiblings(start) : prevSiblings(start); 13674 append(wrapper, siblings); 13675 if (next) { 13676 before$3(start, wrapper); 13677 prepend(wrapper, start); 13678 } else { 13679 after$4(start, wrapper); 13680 append$1(wrapper, start); 13681 } 13682 return wrapper.dom; 13683 }; 13684 const isColorFormatAndAnchor = (node, format) => format.links && node.nodeName === 'A'; 13685 const removeNode = (ed, node, format) => { 13686 const parentNode = node.parentNode; 13687 let rootBlockElm; 13688 const dom = ed.dom; 13689 const forcedRootBlock = getForcedRootBlock(ed); 13690 if (isBlockFormat(format)) { 13691 if (parentNode === dom.getRoot()) { 13692 if (!format.list_block || !isEq$2(node, format.list_block)) { 13693 each$e(from(node.childNodes), node => { 13694 if (isValid(ed, forcedRootBlock, node.nodeName.toLowerCase())) { 13695 if (!rootBlockElm) { 13696 rootBlockElm = wrap$1(dom, node, forcedRootBlock); 13697 dom.setAttribs(rootBlockElm, getForcedRootBlockAttrs(ed)); 13698 } else { 13699 rootBlockElm.appendChild(node); 13700 } 13701 } else { 13702 rootBlockElm = null; 13703 } 13704 }); 13705 } 13706 } 13707 } 13708 if (isMixedFormat(format) && !isEq$2(format.inline, node)) { 13709 return; 13710 } 13711 dom.remove(node, true); 13712 }; 13713 const processFormatAttrOrStyle = (name, value, vars) => { 13714 if (isNumber(name)) { 13715 return { 13716 name: value, 13717 value: null 13718 }; 13719 } else { 13720 return { 13721 name, 13722 value: replaceVars(value, vars) 13723 }; 13724 } 13725 }; 13726 const removeEmptyStyleAttributeIfNeeded = (dom, elm) => { 13727 if (dom.getAttrib(elm, 'style') === '') { 13728 elm.removeAttribute('style'); 13729 elm.removeAttribute('data-mce-style'); 13730 } 13731 }; 13732 const removeStyles = (dom, elm, format, vars, compareNode) => { 13733 let stylesModified = false; 13734 each$7(format.styles, (value, name) => { 13735 const { 13736 name: styleName, 13737 value: styleValue 13738 } = processFormatAttrOrStyle(name, value, vars); 13739 const normalizedStyleValue = normalizeStyleValue(styleValue, styleName); 13740 if (format.remove_similar || isNull(styleValue) || !isElement$6(compareNode) || isEq$2(getStyle(dom, compareNode, styleName), normalizedStyleValue)) { 13741 dom.setStyle(elm, styleName, ''); 13742 } 13743 stylesModified = true; 13744 }); 13745 if (stylesModified) { 13746 removeEmptyStyleAttributeIfNeeded(dom, elm); 13747 } 13748 }; 13749 const removeListStyleFormats = (editor, name, vars) => { 13750 if (name === 'removeformat') { 13751 each$e(getPartiallySelectedListItems(editor.selection), li => { 13752 each$e(listItemStyles, name => editor.dom.setStyle(li, name, '')); 13753 removeEmptyStyleAttributeIfNeeded(editor.dom, li); 13754 }); 13755 } else { 13756 getExpandedListItemFormat(editor.formatter, name).each(liFmt => { 13757 each$e(getPartiallySelectedListItems(editor.selection), li => removeStyles(editor.dom, li, liFmt, vars, null)); 13758 }); 13759 } 13760 }; 13761 const removeNodeFormatInternal = (ed, format, vars, node, compareNode) => { 13762 const dom = ed.dom; 13763 const elementUtils = ElementUtils(ed); 13764 const schema = ed.schema; 13765 if (isInlineFormat(format) && isTransparentElementName(schema, format.inline) && isTransparentBlock(schema, node) && node.parentElement === ed.getBody()) { 13766 removeNode(ed, node, format); 13767 return removeResult.removed(); 13768 } 13769 if (!format.ceFalseOverride && node && dom.getContentEditableParent(node) === 'false') { 13770 return removeResult.keep(); 13771 } 13772 if (node && !matchName(dom, node, format) && !isColorFormatAndAnchor(node, format)) { 13773 return removeResult.keep(); 13774 } 13775 const elm = node; 13776 const preserveAttributes = format.preserve_attributes; 13777 if (isInlineFormat(format) && format.remove === 'all' && isArray$1(preserveAttributes)) { 13778 const attrsToPreserve = filter$5(dom.getAttribs(elm), attr => contains$2(preserveAttributes, attr.name.toLowerCase())); 13779 dom.removeAllAttribs(elm); 13780 each$e(attrsToPreserve, attr => dom.setAttrib(elm, attr.name, attr.value)); 13781 if (attrsToPreserve.length > 0) { 13782 return removeResult.rename('span'); 13783 } 13784 } 13785 if (format.remove !== 'all') { 13786 removeStyles(dom, elm, format, vars, compareNode); 13787 each$7(format.attributes, (value, name) => { 13788 const { 13789 name: attrName, 13790 value: attrValue 13791 } = processFormatAttrOrStyle(name, value, vars); 13792 if (format.remove_similar || isNull(attrValue) || !isElement$6(compareNode) || isEq$2(dom.getAttrib(compareNode, attrName), attrValue)) { 13793 if (attrName === 'class') { 13794 const currentValue = dom.getAttrib(elm, attrName); 13795 if (currentValue) { 13796 let valueOut = ''; 13797 each$e(currentValue.split(/\s+/), cls => { 13798 if (/mce\-\w+/.test(cls)) { 13799 valueOut += (valueOut ? ' ' : '') + cls; 13800 } 13801 }); 13802 if (valueOut) { 13803 dom.setAttrib(elm, attrName, valueOut); 13804 return; 13805 } 13806 } 13807 } 13808 if (MCE_ATTR_RE.test(attrName)) { 13809 elm.removeAttribute('data-mce-' + attrName); 13810 } 13811 if (attrName === 'style' && matchNodeNames(['li'])(elm) && dom.getStyle(elm, 'list-style-type') === 'none') { 13812 elm.removeAttribute(attrName); 13813 dom.setStyle(elm, 'list-style-type', 'none'); 13814 return; 13815 } 13816 if (attrName === 'class') { 13817 elm.removeAttribute('className'); 13818 } 13819 elm.removeAttribute(attrName); 13820 } 13821 }); 13822 each$7(format.classes, value => { 13823 value = replaceVars(value, vars); 13824 if (!isElement$6(compareNode) || dom.hasClass(compareNode, value)) { 13825 dom.removeClass(elm, value); 13826 } 13827 }); 13828 const attrs = dom.getAttribs(elm); 13829 for (let i = 0; i < attrs.length; i++) { 13830 const attrName = attrs[i].nodeName; 13831 if (!elementUtils.isAttributeInternal(attrName)) { 13832 return removeResult.keep(); 13833 } 13834 } 13835 } 13836 if (format.remove !== 'none') { 13837 removeNode(ed, elm, format); 13838 return removeResult.removed(); 13839 } 13840 return removeResult.keep(); 13841 }; 13842 const findFormatRoot = (editor, container, name, vars, similar) => { 13843 let formatRoot; 13844 if (container.parentNode) { 13845 each$e(getParents$2(editor.dom, container.parentNode).reverse(), parent => { 13846 if (!formatRoot && isElement$6(parent) && parent.id !== '_start' && parent.id !== '_end') { 13847 const format = matchNode(editor, parent, name, vars, similar); 13848 if (format && format.split !== false) { 13849 formatRoot = parent; 13850 } 13851 } 13852 }); 13853 } 13854 return formatRoot; 13855 }; 13856 const removeNodeFormatFromClone = (editor, format, vars, clone) => removeNodeFormatInternal(editor, format, vars, clone, clone).fold(constant(clone), newName => { 13857 const fragment = editor.dom.createFragment(); 13858 fragment.appendChild(clone); 13859 return editor.dom.rename(clone, newName); 13860 }, constant(null)); 13861 const wrapAndSplit = (editor, formatList, formatRoot, container, target, split, format, vars) => { 13862 var _a, _b; 13863 let lastClone; 13864 let firstClone; 13865 const dom = editor.dom; 13866 if (formatRoot) { 13867 const formatRootParent = formatRoot.parentNode; 13868 for (let parent = container.parentNode; parent && parent !== formatRootParent; parent = parent.parentNode) { 13869 let clone = dom.clone(parent, false); 13870 for (let i = 0; i < formatList.length; i++) { 13871 clone = removeNodeFormatFromClone(editor, formatList[i], vars, clone); 13872 if (clone === null) { 13873 break; 13874 } 13875 } 13876 if (clone) { 13877 if (lastClone) { 13878 clone.appendChild(lastClone); 13879 } 13880 if (!firstClone) { 13881 firstClone = clone; 13882 } 13883 lastClone = clone; 13884 } 13885 } 13886 if (split && (!format.mixed || !dom.isBlock(formatRoot))) { 13887 container = (_a = dom.split(formatRoot, container)) !== null && _a !== void 0 ? _a : container; 13888 } 13889 if (lastClone && firstClone) { 13890 (_b = target.parentNode) === null || _b === void 0 ? void 0 : _b.insertBefore(lastClone, target); 13891 firstClone.appendChild(target); 13892 if (isInlineFormat(format)) { 13893 mergeSiblings(editor, format, vars, lastClone); 13894 } 13895 } 13896 } 13897 return container; 13898 }; 13899 const removeFormatInternal = (ed, name, vars, node, similar) => { 13900 const formatList = ed.formatter.get(name); 13901 const format = formatList[0]; 13902 const dom = ed.dom; 13903 const selection = ed.selection; 13904 const splitToFormatRoot = container => { 13905 const formatRoot = findFormatRoot(ed, container, name, vars, similar); 13906 return wrapAndSplit(ed, formatList, formatRoot, container, container, true, format, vars); 13907 }; 13908 const isRemoveBookmarkNode = node => isBookmarkNode$1(node) && isElement$6(node) && (node.id === '_start' || node.id === '_end'); 13909 const removeFormatOnNode = node => exists(formatList, fmt => removeNodeFormat(ed, fmt, vars, node, node)); 13910 const process = node => { 13911 const children = from(node.childNodes); 13912 const removed = removeFormatOnNode(node); 13913 const currentNodeMatches = removed || exists(formatList, f => matchName(dom, node, f)); 13914 const parentNode = node.parentNode; 13915 if (!currentNodeMatches && isNonNullable(parentNode) && shouldExpandToSelector(format)) { 13916 removeFormatOnNode(parentNode); 13917 } 13918 if (format.deep) { 13919 if (children.length) { 13920 for (let i = 0; i < children.length; i++) { 13921 process(children[i]); 13922 } 13923 } 13924 } 13925 const textDecorations = [ 13926 'underline', 13927 'line-through', 13928 'overline' 13929 ]; 13930 each$e(textDecorations, decoration => { 13931 if (isElement$6(node) && ed.dom.getStyle(node, 'text-decoration') === decoration && node.parentNode && getTextDecoration(dom, node.parentNode) === decoration) { 13932 removeNodeFormat(ed, { 13933 deep: false, 13934 exact: true, 13935 inline: 'span', 13936 styles: { textDecoration: decoration } 13937 }, undefined, node); 13938 } 13939 }); 13940 }; 13941 const unwrap = start => { 13942 const node = dom.get(start ? '_start' : '_end'); 13943 if (node) { 13944 let out = node[start ? 'firstChild' : 'lastChild']; 13945 if (isRemoveBookmarkNode(out)) { 13946 out = out[start ? 'firstChild' : 'lastChild']; 13947 } 13948 if (isText$a(out) && out.data.length === 0) { 13949 out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling; 13950 } 13951 dom.remove(node, true); 13952 return out; 13953 } else { 13954 return null; 13955 } 13956 }; 13957 const removeRngStyle = rng => { 13958 let startContainer; 13959 let endContainer; 13960 let expandedRng = expandRng(dom, rng, formatList, rng.collapsed); 13961 if (format.split) { 13962 expandedRng = split(expandedRng); 13963 startContainer = getContainer(ed, expandedRng, true); 13964 endContainer = getContainer(ed, expandedRng); 13965 if (startContainer !== endContainer) { 13966 startContainer = normalizeTableSelection(startContainer, true); 13967 endContainer = normalizeTableSelection(endContainer, false); 13968 if (isChildOfInlineParent(dom, startContainer, endContainer)) { 13969 const marker = Optional.from(startContainer.firstChild).getOr(startContainer); 13970 splitToFormatRoot(wrapWithSiblings(dom, marker, true, 'span', { 13971 'id': '_start', 13972 'data-mce-type': 'bookmark' 13973 })); 13974 unwrap(true); 13975 return; 13976 } 13977 if (isChildOfInlineParent(dom, endContainer, startContainer)) { 13978 const marker = Optional.from(endContainer.lastChild).getOr(endContainer); 13979 splitToFormatRoot(wrapWithSiblings(dom, marker, false, 'span', { 13980 'id': '_end', 13981 'data-mce-type': 'bookmark' 13982 })); 13983 unwrap(false); 13984 return; 13985 } 13986 startContainer = wrap$1(dom, startContainer, 'span', { 13987 'id': '_start', 13988 'data-mce-type': 'bookmark' 13989 }); 13990 endContainer = wrap$1(dom, endContainer, 'span', { 13991 'id': '_end', 13992 'data-mce-type': 'bookmark' 13993 }); 13994 const newRng = dom.createRng(); 13995 newRng.setStartAfter(startContainer); 13996 newRng.setEndBefore(endContainer); 13997 walk$3(dom, newRng, nodes => { 13998 each$e(nodes, n => { 13999 if (!isBookmarkNode$1(n) && !isBookmarkNode$1(n.parentNode)) { 14000 splitToFormatRoot(n); 14001 } 14002 }); 14003 }); 14004 splitToFormatRoot(startContainer); 14005 splitToFormatRoot(endContainer); 14006 startContainer = unwrap(true); 14007 endContainer = unwrap(); 14008 } else { 14009 startContainer = endContainer = splitToFormatRoot(startContainer); 14010 } 14011 expandedRng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer; 14012 expandedRng.startOffset = dom.nodeIndex(startContainer); 14013 expandedRng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer; 14014 expandedRng.endOffset = dom.nodeIndex(endContainer) + 1; 14015 } 14016 walk$3(dom, expandedRng, nodes => { 14017 each$e(nodes, process); 14018 }); 14019 }; 14020 if (node) { 14021 if (isNode(node)) { 14022 const rng = dom.createRng(); 14023 rng.setStartBefore(node); 14024 rng.setEndAfter(node); 14025 removeRngStyle(rng); 14026 } else { 14027 removeRngStyle(node); 14028 } 14029 fireFormatRemove(ed, name, node, vars); 14030 return; 14031 } 14032 if (!selection.isCollapsed() || !isInlineFormat(format) || getCellsFromEditor(ed).length) { 14033 preserveSelection(ed, () => runOnRanges(ed, removeRngStyle), startNode => isInlineFormat(format) && match$2(ed, name, vars, startNode)); 14034 ed.nodeChanged(); 14035 } else { 14036 removeCaretFormat(ed, name, vars, similar); 14037 } 14038 removeListStyleFormats(ed, name, vars); 14039 fireFormatRemove(ed, name, node, vars); 14040 }; 14041 const removeFormat$1 = (ed, name, vars, node, similar) => { 14042 if (node || ed.selection.isEditable()) { 14043 removeFormatInternal(ed, name, vars, node, similar); 14044 } 14045 }; 14046 const removeNodeFormat = (editor, format, vars, node, compareNode) => { 14047 return removeNodeFormatInternal(editor, format, vars, node, compareNode).fold(never, newName => { 14048 editor.dom.rename(node, newName); 14049 return true; 14050 }, always); 14051 }; 14052 14053 const each$6 = Tools.each; 14054 const mergeTextDecorationsAndColor = (dom, format, vars, node) => { 14055 const processTextDecorationsAndColor = n => { 14056 if (isElement$6(n) && isElement$6(n.parentNode) && isEditable$2(n)) { 14057 const parentTextDecoration = getTextDecoration(dom, n.parentNode); 14058 if (dom.getStyle(n, 'color') && parentTextDecoration) { 14059 dom.setStyle(n, 'text-decoration', parentTextDecoration); 14060 } else if (dom.getStyle(n, 'text-decoration') === parentTextDecoration) { 14061 dom.setStyle(n, 'text-decoration', null); 14062 } 14063 } 14064 }; 14065 if (format.styles && (format.styles.color || format.styles.textDecoration)) { 14066 Tools.walk(node, processTextDecorationsAndColor, 'childNodes'); 14067 processTextDecorationsAndColor(node); 14068 } 14069 }; 14070 const mergeBackgroundColorAndFontSize = (dom, format, vars, node) => { 14071 if (format.styles && format.styles.backgroundColor) { 14072 const hasFontSize = hasStyle(dom, 'fontSize'); 14073 processChildElements(node, elm => hasFontSize(elm) && isEditable$2(elm), applyStyle(dom, 'backgroundColor', replaceVars(format.styles.backgroundColor, vars))); 14074 } 14075 }; 14076 const mergeSubSup = (dom, format, vars, node) => { 14077 if (isInlineFormat(format) && (format.inline === 'sub' || format.inline === 'sup')) { 14078 const hasFontSize = hasStyle(dom, 'fontSize'); 14079 processChildElements(node, elm => hasFontSize(elm) && isEditable$2(elm), applyStyle(dom, 'fontSize', '')); 14080 const inverseTagDescendants = filter$5(dom.select(format.inline === 'sup' ? 'sub' : 'sup', node), isEditable$2); 14081 dom.remove(inverseTagDescendants, true); 14082 } 14083 }; 14084 const mergeWithChildren = (editor, formatList, vars, node) => { 14085 each$6(formatList, format => { 14086 if (isInlineFormat(format)) { 14087 each$6(editor.dom.select(format.inline, node), child => { 14088 if (isElementNode(child)) { 14089 removeNodeFormat(editor, format, vars, child, format.exact ? child : null); 14090 } 14091 }); 14092 } 14093 clearChildStyles(editor.dom, format, node); 14094 }); 14095 }; 14096 const mergeWithParents = (editor, format, name, vars, node) => { 14097 const parentNode = node.parentNode; 14098 if (matchNode(editor, parentNode, name, vars)) { 14099 if (removeNodeFormat(editor, format, vars, node)) { 14100 return; 14101 } 14102 } 14103 if (format.merge_with_parents && parentNode) { 14104 editor.dom.getParent(parentNode, parent => { 14105 if (matchNode(editor, parent, name, vars)) { 14106 removeNodeFormat(editor, format, vars, node); 14107 return true; 14108 } else { 14109 return false; 14110 } 14111 }); 14112 } 14113 }; 14114 14115 const each$5 = Tools.each; 14116 const canFormatBR = (editor, format, node, parentName) => { 14117 if (canFormatEmptyLines(editor) && isInlineFormat(format) && node.parentNode) { 14118 const validBRParentElements = getTextRootBlockElements(editor.schema); 14119 const hasCaretNodeSibling = sibling(SugarElement.fromDom(node), sibling => isCaretNode(sibling.dom)); 14120 return hasNonNullableKey(validBRParentElements, parentName) && isEmpty$2(SugarElement.fromDom(node.parentNode), false) && !hasCaretNodeSibling; 14121 } else { 14122 return false; 14123 } 14124 }; 14125 const applyStyles = (dom, elm, format, vars) => { 14126 each$5(format.styles, (value, name) => { 14127 dom.setStyle(elm, name, replaceVars(value, vars)); 14128 }); 14129 if (format.styles) { 14130 const styleVal = dom.getAttrib(elm, 'style'); 14131 if (styleVal) { 14132 dom.setAttrib(elm, 'data-mce-style', styleVal); 14133 } 14134 } 14135 }; 14136 const applyFormatAction = (ed, name, vars, node) => { 14137 const formatList = ed.formatter.get(name); 14138 const format = formatList[0]; 14139 const isCollapsed = !node && ed.selection.isCollapsed(); 14140 const dom = ed.dom; 14141 const selection = ed.selection; 14142 const setElementFormat = (elm, fmt = format) => { 14143 if (isFunction(fmt.onformat)) { 14144 fmt.onformat(elm, fmt, vars, node); 14145 } 14146 applyStyles(dom, elm, fmt, vars); 14147 each$5(fmt.attributes, (value, name) => { 14148 dom.setAttrib(elm, name, replaceVars(value, vars)); 14149 }); 14150 each$5(fmt.classes, value => { 14151 const newValue = replaceVars(value, vars); 14152 if (!dom.hasClass(elm, newValue)) { 14153 dom.addClass(elm, newValue); 14154 } 14155 }); 14156 }; 14157 const applyNodeStyle = (formatList, node) => { 14158 let found = false; 14159 each$5(formatList, format => { 14160 if (!isSelectorFormat(format)) { 14161 return false; 14162 } 14163 if (dom.getContentEditable(node) === 'false' && !format.ceFalseOverride) { 14164 return true; 14165 } 14166 if (isNonNullable(format.collapsed) && format.collapsed !== isCollapsed) { 14167 return true; 14168 } 14169 if (dom.is(node, format.selector) && !isCaretNode(node)) { 14170 setElementFormat(node, format); 14171 found = true; 14172 return false; 14173 } 14174 return true; 14175 }); 14176 return found; 14177 }; 14178 const createWrapElement = wrapName => { 14179 if (isString(wrapName)) { 14180 const wrapElm = dom.create(wrapName); 14181 setElementFormat(wrapElm); 14182 return wrapElm; 14183 } else { 14184 return null; 14185 } 14186 }; 14187 const applyRngStyle = (dom, rng, nodeSpecific) => { 14188 const newWrappers = []; 14189 let contentEditable = true; 14190 const wrapName = format.inline || format.block; 14191 const wrapElm = createWrapElement(wrapName); 14192 const isMatchingWrappingBlock = node => isWrappingBlockFormat(format) && matchNode(ed, node, name, vars); 14193 const canRenameBlock = (node, parentName, isEditableDescendant) => { 14194 const isValidBlockFormatForNode = isNonWrappingBlockFormat(format) && isTextBlock$1(ed.schema, node) && isValid(ed, parentName, wrapName); 14195 return isEditableDescendant && isValidBlockFormatForNode; 14196 }; 14197 const canWrapNode = (node, parentName, isEditableDescendant, isWrappableNoneditableElm) => { 14198 const nodeName = node.nodeName.toLowerCase(); 14199 const isValidWrapNode = isValid(ed, wrapName, nodeName) && isValid(ed, parentName, wrapName); 14200 const isZwsp = !nodeSpecific && isText$a(node) && isZwsp$1(node.data); 14201 const isCaret = isCaretNode(node); 14202 const isCorrectFormatForNode = !isInlineFormat(format) || !dom.isBlock(node); 14203 return (isEditableDescendant || isWrappableNoneditableElm) && isValidWrapNode && !isZwsp && !isCaret && isCorrectFormatForNode; 14204 }; 14205 walk$3(dom, rng, nodes => { 14206 let currentWrapElm; 14207 const process = node => { 14208 let hasContentEditableState = false; 14209 let lastContentEditable = contentEditable; 14210 let isWrappableNoneditableElm = false; 14211 const parentNode = node.parentNode; 14212 const parentName = parentNode.nodeName.toLowerCase(); 14213 const contentEditableValue = dom.getContentEditable(node); 14214 if (isNonNullable(contentEditableValue)) { 14215 lastContentEditable = contentEditable; 14216 contentEditable = contentEditableValue === 'true'; 14217 hasContentEditableState = true; 14218 isWrappableNoneditableElm = isWrappableNoneditable(ed, node); 14219 } 14220 const isEditableDescendant = contentEditable && !hasContentEditableState; 14221 if (isBr$6(node) && !canFormatBR(ed, format, node, parentName)) { 14222 currentWrapElm = null; 14223 if (isBlockFormat(format)) { 14224 dom.remove(node); 14225 } 14226 return; 14227 } 14228 if (isMatchingWrappingBlock(node)) { 14229 currentWrapElm = null; 14230 return; 14231 } 14232 if (canRenameBlock(node, parentName, isEditableDescendant)) { 14233 const elm = dom.rename(node, wrapName); 14234 setElementFormat(elm); 14235 newWrappers.push(elm); 14236 currentWrapElm = null; 14237 return; 14238 } 14239 if (isSelectorFormat(format)) { 14240 let found = applyNodeStyle(formatList, node); 14241 if (!found && isNonNullable(parentNode) && shouldExpandToSelector(format)) { 14242 found = applyNodeStyle(formatList, parentNode); 14243 } 14244 if (!isInlineFormat(format) || found) { 14245 currentWrapElm = null; 14246 return; 14247 } 14248 } 14249 if (isNonNullable(wrapElm) && canWrapNode(node, parentName, isEditableDescendant, isWrappableNoneditableElm)) { 14250 if (!currentWrapElm) { 14251 currentWrapElm = dom.clone(wrapElm, false); 14252 parentNode.insertBefore(currentWrapElm, node); 14253 newWrappers.push(currentWrapElm); 14254 } 14255 if (isWrappableNoneditableElm && hasContentEditableState) { 14256 contentEditable = lastContentEditable; 14257 } 14258 currentWrapElm.appendChild(node); 14259 } else { 14260 currentWrapElm = null; 14261 each$e(from(node.childNodes), process); 14262 if (hasContentEditableState) { 14263 contentEditable = lastContentEditable; 14264 } 14265 currentWrapElm = null; 14266 } 14267 }; 14268 each$e(nodes, process); 14269 }); 14270 if (format.links === true) { 14271 each$e(newWrappers, node => { 14272 const process = node => { 14273 if (node.nodeName === 'A') { 14274 setElementFormat(node, format); 14275 } 14276 each$e(from(node.childNodes), process); 14277 }; 14278 process(node); 14279 }); 14280 } 14281 each$e(newWrappers, node => { 14282 const getChildCount = node => { 14283 let count = 0; 14284 each$e(node.childNodes, node => { 14285 if (!isEmptyTextNode$1(node) && !isBookmarkNode$1(node)) { 14286 count++; 14287 } 14288 }); 14289 return count; 14290 }; 14291 const mergeStyles = node => { 14292 const childElement = find$2(node.childNodes, isElementNode$1).filter(child => dom.getContentEditable(child) !== 'false' && matchName(dom, child, format)); 14293 return childElement.map(child => { 14294 const clone = dom.clone(child, false); 14295 setElementFormat(clone); 14296 dom.replace(clone, node, true); 14297 dom.remove(child, true); 14298 return clone; 14299 }).getOr(node); 14300 }; 14301 const childCount = getChildCount(node); 14302 if ((newWrappers.length > 1 || !dom.isBlock(node)) && childCount === 0) { 14303 dom.remove(node, true); 14304 return; 14305 } 14306 if (isInlineFormat(format) || isBlockFormat(format) && format.wrapper) { 14307 if (!format.exact && childCount === 1) { 14308 node = mergeStyles(node); 14309 } 14310 mergeWithChildren(ed, formatList, vars, node); 14311 mergeWithParents(ed, format, name, vars, node); 14312 mergeBackgroundColorAndFontSize(dom, format, vars, node); 14313 mergeTextDecorationsAndColor(dom, format, vars, node); 14314 mergeSubSup(dom, format, vars, node); 14315 mergeSiblings(ed, format, vars, node); 14316 } 14317 }); 14318 }; 14319 const targetNode = isNode(node) ? node : selection.getNode(); 14320 if (dom.getContentEditable(targetNode) === 'false' && !isWrappableNoneditable(ed, targetNode)) { 14321 node = targetNode; 14322 applyNodeStyle(formatList, node); 14323 fireFormatApply(ed, name, node, vars); 14324 return; 14325 } 14326 if (format) { 14327 if (node) { 14328 if (isNode(node)) { 14329 if (!applyNodeStyle(formatList, node)) { 14330 const rng = dom.createRng(); 14331 rng.setStartBefore(node); 14332 rng.setEndAfter(node); 14333 applyRngStyle(dom, expandRng(dom, rng, formatList), true); 14334 } 14335 } else { 14336 applyRngStyle(dom, node, true); 14337 } 14338 } else { 14339 if (!isCollapsed || !isInlineFormat(format) || getCellsFromEditor(ed).length) { 14340 selection.setRng(normalize(selection.getRng())); 14341 preserveSelection(ed, () => { 14342 runOnRanges(ed, (selectionRng, fake) => { 14343 const expandedRng = fake ? selectionRng : expandRng(dom, selectionRng, formatList); 14344 applyRngStyle(dom, expandedRng, false); 14345 }); 14346 }, always); 14347 ed.nodeChanged(); 14348 } else { 14349 applyCaretFormat(ed, name, vars); 14350 } 14351 getExpandedListItemFormat(ed.formatter, name).each(liFmt => { 14352 each$e(getFullySelectedListItems(ed.selection), li => applyStyles(dom, li, liFmt, vars)); 14353 }); 14354 } 14355 postProcess$1(name, ed); 14356 } 14357 fireFormatApply(ed, name, node, vars); 14358 }; 14359 const applyFormat$1 = (editor, name, vars, node) => { 14360 if (node || editor.selection.isEditable()) { 14361 applyFormatAction(editor, name, vars, node); 14362 } 14363 }; 14364 14365 const hasVars = value => has$2(value, 'vars'); 14366 const setup$t = (registeredFormatListeners, editor) => { 14367 registeredFormatListeners.set({}); 14368 editor.on('NodeChange', e => { 14369 updateAndFireChangeCallbacks(editor, e.element, registeredFormatListeners.get()); 14370 }); 14371 editor.on('FormatApply FormatRemove', e => { 14372 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)); 14373 updateAndFireChangeCallbacks(editor, element, registeredFormatListeners.get()); 14374 }); 14375 }; 14376 const fallbackElement = editor => editor.selection.getStart(); 14377 const matchingNode = (editor, parents, format, similar, vars) => { 14378 const isMatchingNode = node => { 14379 const matchingFormat = editor.formatter.matchNode(node, format, vars !== null && vars !== void 0 ? vars : {}, similar); 14380 return !isUndefined(matchingFormat); 14381 }; 14382 const isUnableToMatch = node => { 14383 if (matchesUnInheritedFormatSelector(editor, node, format)) { 14384 return true; 14385 } else { 14386 if (!similar) { 14387 return isNonNullable(editor.formatter.matchNode(node, format, vars, true)); 14388 } else { 14389 return false; 14390 } 14391 } 14392 }; 14393 return findUntil$1(parents, isMatchingNode, isUnableToMatch); 14394 }; 14395 const getParents = (editor, elm) => { 14396 const element = elm !== null && elm !== void 0 ? elm : fallbackElement(editor); 14397 return filter$5(getParents$2(editor.dom, element), node => isElement$6(node) && !isBogus$2(node)); 14398 }; 14399 const updateAndFireChangeCallbacks = (editor, elm, registeredCallbacks) => { 14400 const parents = getParents(editor, elm); 14401 each$d(registeredCallbacks, (data, format) => { 14402 const runIfChanged = spec => { 14403 const match = matchingNode(editor, parents, format, spec.similar, hasVars(spec) ? spec.vars : undefined); 14404 const isSet = match.isSome(); 14405 if (spec.state.get() !== isSet) { 14406 spec.state.set(isSet); 14407 const node = match.getOr(elm); 14408 if (hasVars(spec)) { 14409 spec.callback(isSet, { 14410 node, 14411 format, 14412 parents 14413 }); 14414 } else { 14415 each$e(spec.callbacks, callback => callback(isSet, { 14416 node, 14417 format, 14418 parents 14419 })); 14420 } 14421 } 14422 }; 14423 each$e([ 14424 data.withSimilar, 14425 data.withoutSimilar 14426 ], runIfChanged); 14427 each$e(data.withVars, runIfChanged); 14428 }); 14429 }; 14430 const addListeners = (editor, registeredFormatListeners, formats, callback, similar, vars) => { 14431 const formatChangeItems = registeredFormatListeners.get(); 14432 each$e(formats.split(','), format => { 14433 const group = get$a(formatChangeItems, format).getOrThunk(() => { 14434 const base = { 14435 withSimilar: { 14436 state: Cell(false), 14437 similar: true, 14438 callbacks: [] 14439 }, 14440 withoutSimilar: { 14441 state: Cell(false), 14442 similar: false, 14443 callbacks: [] 14444 }, 14445 withVars: [] 14446 }; 14447 formatChangeItems[format] = base; 14448 return base; 14449 }); 14450 const getCurrent = () => { 14451 const parents = getParents(editor); 14452 return matchingNode(editor, parents, format, similar, vars).isSome(); 14453 }; 14454 if (isUndefined(vars)) { 14455 const toAppendTo = similar ? group.withSimilar : group.withoutSimilar; 14456 toAppendTo.callbacks.push(callback); 14457 if (toAppendTo.callbacks.length === 1) { 14458 toAppendTo.state.set(getCurrent()); 14459 } 14460 } else { 14461 group.withVars.push({ 14462 state: Cell(getCurrent()), 14463 similar, 14464 vars, 14465 callback 14466 }); 14467 } 14468 }); 14469 registeredFormatListeners.set(formatChangeItems); 14470 }; 14471 const removeListeners = (registeredFormatListeners, formats, callback) => { 14472 const formatChangeItems = registeredFormatListeners.get(); 14473 each$e(formats.split(','), format => get$a(formatChangeItems, format).each(group => { 14474 formatChangeItems[format] = { 14475 withSimilar: { 14476 ...group.withSimilar, 14477 callbacks: filter$5(group.withSimilar.callbacks, cb => cb !== callback) 14478 }, 14479 withoutSimilar: { 14480 ...group.withoutSimilar, 14481 callbacks: filter$5(group.withoutSimilar.callbacks, cb => cb !== callback) 14482 }, 14483 withVars: filter$5(group.withVars, item => item.callback !== callback) 14484 }; 14485 })); 14486 registeredFormatListeners.set(formatChangeItems); 14487 }; 14488 const formatChangedInternal = (editor, registeredFormatListeners, formats, callback, similar, vars) => { 14489 addListeners(editor, registeredFormatListeners, formats, callback, similar, vars); 14490 return { unbind: () => removeListeners(registeredFormatListeners, formats, callback) }; 14491 }; 14492 14493 const toggle = (editor, name, vars, node) => { 14494 const fmt = editor.formatter.get(name); 14495 if (fmt) { 14496 if (match$2(editor, name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) { 14497 removeFormat$1(editor, name, vars, node); 14498 } else { 14499 applyFormat$1(editor, name, vars, node); 14500 } 14501 } 14502 }; 14503 14504 const explode$1 = Tools.explode; 14505 const create$8 = () => { 14506 const filters = {}; 14507 const addFilter = (name, callback) => { 14508 each$e(explode$1(name), name => { 14509 if (!has$2(filters, name)) { 14510 filters[name] = { 14511 name, 14512 callbacks: [] 14513 }; 14514 } 14515 filters[name].callbacks.push(callback); 14516 }); 14517 }; 14518 const getFilters = () => values(filters); 14519 const removeFilter = (name, callback) => { 14520 each$e(explode$1(name), name => { 14521 if (has$2(filters, name)) { 14522 if (isNonNullable(callback)) { 14523 const filter = filters[name]; 14524 const newCallbacks = filter$5(filter.callbacks, c => c !== callback); 14525 if (newCallbacks.length > 0) { 14526 filter.callbacks = newCallbacks; 14527 } else { 14528 delete filters[name]; 14529 } 14530 } else { 14531 delete filters[name]; 14532 } 14533 } 14534 }); 14535 }; 14536 return { 14537 addFilter, 14538 getFilters, 14539 removeFilter 14540 }; 14541 }; 14542 14543 const removeAttrs = (node, names) => { 14544 each$e(names, name => { 14545 node.attr(name, null); 14546 }); 14547 }; 14548 const addFontToSpansFilter = (domParser, styles, fontSizes) => { 14549 domParser.addNodeFilter('font', nodes => { 14550 each$e(nodes, node => { 14551 const props = styles.parse(node.attr('style')); 14552 const color = node.attr('color'); 14553 const face = node.attr('face'); 14554 const size = node.attr('size'); 14555 if (color) { 14556 props.color = color; 14557 } 14558 if (face) { 14559 props['font-family'] = face; 14560 } 14561 if (size) { 14562 toInt(size).each(num => { 14563 props['font-size'] = fontSizes[num - 1]; 14564 }); 14565 } 14566 node.name = 'span'; 14567 node.attr('style', styles.serialize(props)); 14568 removeAttrs(node, [ 14569 'color', 14570 'face', 14571 'size' 14572 ]); 14573 }); 14574 }); 14575 }; 14576 const addStrikeFilter = (domParser, schema, styles) => { 14577 domParser.addNodeFilter('strike', nodes => { 14578 const convertToSTag = schema.type !== 'html4'; 14579 each$e(nodes, node => { 14580 if (convertToSTag) { 14581 node.name = 's'; 14582 } else { 14583 const props = styles.parse(node.attr('style')); 14584 props['text-decoration'] = 'line-through'; 14585 node.name = 'span'; 14586 node.attr('style', styles.serialize(props)); 14587 } 14588 }); 14589 }); 14590 }; 14591 const addFilters = (domParser, settings, schema) => { 14592 var _a; 14593 const styles = Styles(); 14594 if (settings.convert_fonts_to_spans) { 14595 addFontToSpansFilter(domParser, styles, Tools.explode((_a = settings.font_size_legacy_values) !== null && _a !== void 0 ? _a : '')); 14596 } 14597 addStrikeFilter(domParser, schema, styles); 14598 }; 14599 const register$5 = (domParser, settings, schema) => { 14600 if (settings.inline_styles) { 14601 addFilters(domParser, settings, schema); 14602 } 14603 }; 14604 14605 const addNodeFilter = (
vendor: 1,416 bytes, lines 14605-14635
14605settings, htmlParser, schema) => { 14606 htmlParser.addNodeFilter('br', (nodes, _, args) => { 14607 const blockElements = Tools.extend({}, schema.getBlockElements()); 14608 const nonEmptyElements = schema.getNonEmptyElements(); 14609 const whitespaceElements = schema.getWhitespaceElements(); 14610 blockElements.body = 1; 14611 const isBlock = node => node.name in blockElements || isTransparentAstBlock(schema, node); 14612 for (let i = 0, l = nodes.length; i < l; i++) { 14613 let node = nodes[i]; 14614 let parent = node.parent; 14615 if (parent && isBlock(parent) && node === parent.lastChild) { 14616 let prev = node.prev; 14617 while (prev) { 14618 const prevName = prev.name; 14619 if (prevName !== 'span' || prev.attr('data-mce-type') !== 'bookmark') { 14620 if (prevName === 'br') { 14621 node = null; 14622 } 14623 break; 14624 } 14625 prev = prev.prev; 14626 } 14627 if (node) { 14628 node.remove(); 14629 if (isEmpty(schema, nonEmptyElements, whitespaceElements, parent)) { 14630 const elementRule = schema.getElementRule(parent.name); 14631 if (elementRule) { 14632 if (elementRule.removeEmpty) { 14633 parent.remove(); 14634 } else if (elementRule.paddEmpty) { 14635 paddEmptyNode(settings,
vendor: 5,755 bytes, lines 14635-14790
14635args, isBlock, parent); 14636 } 14637 } 14638 } 14639 } 14640 } else { 14641 let lastParent = node; 14642 while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) { 14643 lastParent = parent; 14644 if (blockElements[parent.name]) { 14645 break; 14646 } 14647 parent = parent.parent; 14648 } 14649 if (lastParent === parent) { 14650 const textNode = new AstNode('#text', 3); 14651 textNode.value = nbsp; 14652 node.replace(textNode); 14653 } 14654 } 14655 } 14656 }); 14657 }; 14658 14659 const blobUriToBlob = url => fetch(url).then(res => res.ok ? res.blob() : Promise.reject()).catch(() => Promise.reject({ 14660 message: `Cannot convert ${ url } to Blob. Resource might not exist or is inaccessible.`, 14661 uriType: 'blob' 14662 })); 14663 const extractBase64Data = data => { 14664 const matches = /([a-z0-9+\/=\s]+)/i.exec(data); 14665 return matches ? matches[1] : ''; 14666 }; 14667 const parseDataUri = uri => { 14668 const [type, ...rest] = uri.split(','); 14669 const data = rest.join(','); 14670 const matches = /data:([^/]+\/[^;]+)(;.+)?/.exec(type); 14671 if (matches) { 14672 const base64Encoded = matches[2] === ';base64'; 14673 const extractedData = base64Encoded ? extractBase64Data(data) : decodeURIComponent(data); 14674 return Optional.some({ 14675 type: matches[1], 14676 data: extractedData, 14677 base64Encoded 14678 }); 14679 } else { 14680 return Optional.none(); 14681 } 14682 }; 14683 const buildBlob = (type, data, base64Encoded = true) => { 14684 let str = data; 14685 if (base64Encoded) { 14686 try { 14687 str = atob(data); 14688 } catch (e) { 14689 return Optional.none(); 14690 } 14691 } 14692 const arr = new Uint8Array(str.length); 14693 for (let i = 0; i < arr.length; i++) { 14694 arr[i] = str.charCodeAt(i); 14695 } 14696 return Optional.some(new Blob([arr], { type })); 14697 }; 14698 const dataUriToBlob = uri => { 14699 return new Promise((resolve, reject) => { 14700 parseDataUri(uri).bind(({type, data, base64Encoded}) => buildBlob(type, data, base64Encoded)).fold(() => reject('Invalid data URI'), resolve); 14701 }); 14702 }; 14703 const uriToBlob = url => { 14704 if (startsWith(url, 'blob:')) { 14705 return blobUriToBlob(url); 14706 } else if (startsWith(url, 'data:')) { 14707 return dataUriToBlob(url); 14708 } else { 14709 return Promise.reject('Unknown URI format'); 14710 } 14711 }; 14712 const blobToDataUri = blob => { 14713 return new Promise((resolve, reject) => { 14714 const reader = new FileReader(); 14715 reader.onloadend = () => { 14716 resolve(reader.result); 14717 }; 14718 reader.onerror = () => { 14719 var _a; 14720 reject((_a = reader.error) === null || _a === void 0 ? void 0 : _a.message); 14721 }; 14722 reader.readAsDataURL(blob); 14723 }); 14724 }; 14725 14726 let count$1 = 0; 14727 const uniqueId$1 = prefix => { 14728 return (prefix || 'blobid') + count$1++; 14729 }; 14730 const processDataUri = (dataUri, base64Only, generateBlobInfo) => { 14731 return parseDataUri(dataUri).bind(({data, type, base64Encoded}) => { 14732 if (base64Only && !base64Encoded) { 14733 return Optional.none(); 14734 } else { 14735 const base64 = base64Encoded ? data : btoa(data); 14736 return generateBlobInfo(base64, type); 14737 } 14738 }); 14739 }; 14740 const createBlobInfo$1 = (blobCache, blob, base64) => { 14741 const blobInfo = blobCache.create(uniqueId$1(), blob, base64); 14742 blobCache.add(blobInfo); 14743 return blobInfo; 14744 }; 14745 const dataUriToBlobInfo = (blobCache, dataUri, base64Only = false) => { 14746 return processDataUri(dataUri, base64Only, (base64, type) => Optional.from(blobCache.getByData(base64, type)).orThunk(() => buildBlob(type, base64).map(blob => createBlobInfo$1(blobCache, blob, base64)))); 14747 }; 14748 const imageToBlobInfo = (blobCache, imageSrc) => { 14749 const invalidDataUri = () => Promise.reject('Invalid data URI'); 14750 if (startsWith(imageSrc, 'blob:')) { 14751 const blobInfo = blobCache.getByUri(imageSrc); 14752 if (isNonNullable(blobInfo)) { 14753 return Promise.resolve(blobInfo); 14754 } else { 14755 return uriToBlob(imageSrc).then(blob => { 14756 return blobToDataUri(blob).then(dataUri => { 14757 return processDataUri(dataUri, false, base64 => { 14758 return Optional.some(createBlobInfo$1(blobCache, blob, base64)); 14759 }).getOrThunk(invalidDataUri); 14760 }); 14761 }); 14762 } 14763 } else if (startsWith(imageSrc, 'data:')) { 14764 return dataUriToBlobInfo(blobCache, imageSrc).fold(invalidDataUri, blobInfo => Promise.resolve(blobInfo)); 14765 } else { 14766 return Promise.reject('Unknown image data format'); 14767 } 14768 }; 14769 14770 const isBogusImage = img => isNonNullable(img.attr('data-mce-bogus')); 14771 const isInternalImageSource = img => img.attr('src') === Env.transparentSrc || isNonNullable(img.attr('data-mce-placeholder')); 14772 const registerBase64ImageFilter = (parser, settings) => { 14773 const {blob_cache: blobCache} = settings; 14774 if (blobCache) { 14775 const processImage = img => { 14776 const inputSrc = img.attr('src'); 14777 if (isInternalImageSource(img) || isBogusImage(img) || isNullable(inputSrc)) { 14778 return; 14779 } 14780 dataUriToBlobInfo(blobCache, inputSrc, true).each(blobInfo => { 14781 img.attr('src', blobInfo.blobUri()); 14782 }); 14783 }; 14784 parser.addAttributeFilter('src', nodes => each$e(nodes, processImage)); 14785 } 14786 }; 14787 const register$4 = (parser, settings) => { 14788 const schema = parser.schema; 14789 if (settings.remove_trailing_brs) { 14790 addNodeFilter(
14790settings,
vendor: 14,444 bytes, lines 14790-15353
14790parser, schema); 14791 } 14792 parser.addAttributeFilter('href', nodes => { 14793 let i = nodes.length; 14794 const appendRel = rel => { 14795 const parts = rel.split(' ').filter(p => p.length > 0); 14796 return parts.concat(['noopener']).sort().join(' '); 14797 }; 14798 const addNoOpener = rel => { 14799 const newRel = rel ? Tools.trim(rel) : ''; 14800 if (!/\b(noopener)\b/g.test(newRel)) { 14801 return appendRel(newRel); 14802 } else { 14803 return newRel; 14804 } 14805 }; 14806 if (!settings.allow_unsafe_link_target) { 14807 while (i--) { 14808 const node = nodes[i]; 14809 if (node.name === 'a' && node.attr('target') === '_blank') { 14810 node.attr('rel', addNoOpener(node.attr('rel'))); 14811 } 14812 } 14813 } 14814 }); 14815 if (!settings.allow_html_in_named_anchor) { 14816 parser.addAttributeFilter('id,name', nodes => { 14817 let i = nodes.length, sibling, prevSibling, parent, node; 14818 while (i--) { 14819 node = nodes[i]; 14820 if (node.name === 'a' && node.firstChild && !node.attr('href')) { 14821 parent = node.parent; 14822 sibling = node.lastChild; 14823 while (sibling && parent) { 14824 prevSibling = sibling.prev; 14825 parent.insert(sibling, node); 14826 sibling = prevSibling; 14827 } 14828 } 14829 } 14830 }); 14831 } 14832 if (settings.fix_list_elements) { 14833 parser.addNodeFilter('ul,ol', nodes => { 14834 let i = nodes.length, node, parentNode; 14835 while (i--) { 14836 node = nodes[i]; 14837 parentNode = node.parent; 14838 if (parentNode && (parentNode.name === 'ul' || parentNode.name === 'ol')) { 14839 if (node.prev && node.prev.name === 'li') { 14840 node.prev.append(node); 14841 } else { 14842 const li = new AstNode('li', 1); 14843 li.attr('style', 'list-style-type: none'); 14844 node.wrap(li); 14845 } 14846 } 14847 } 14848 }); 14849 } 14850 const validClasses = schema.getValidClasses(); 14851 if (settings.validate && validClasses) { 14852 parser.addAttributeFilter('class', nodes => { 14853 var _a; 14854 let i = nodes.length; 14855 while (i--) { 14856 const node = nodes[i]; 14857 const clazz = (_a = node.attr('class')) !== null && _a !== void 0 ? _a : ''; 14858 const classList = Tools.explode(clazz, ' '); 14859 let classValue = ''; 14860 for (let ci = 0; ci < classList.length; ci++) { 14861 const className = classList[ci]; 14862 let valid = false; 14863 let validClassesMap = validClasses['*']; 14864 if (validClassesMap && validClassesMap[className]) { 14865 valid = true; 14866 } 14867 validClassesMap = validClasses[node.name]; 14868 if (!valid && validClassesMap && validClassesMap[className]) { 14869 valid = true; 14870 } 14871 if (valid) { 14872 if (classValue) { 14873 classValue += ' '; 14874 } 14875 classValue += className; 14876 } 14877 } 14878 if (!classValue.length) { 14879 classValue = null; 14880 } 14881 node.attr('class', classValue); 14882 } 14883 }); 14884 } 14885 registerBase64ImageFilter(parser, settings); 14886 }; 14887 14888 function _typeof(obj) { 14889 '@babel/helpers - typeof'; 14890 return _typeof = 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator ? function (obj) { 14891 return typeof obj; 14892 } : function (obj) { 14893 return obj && 'function' == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj; 14894 }, _typeof(obj); 14895 } 14896 function _setPrototypeOf(o, p) { 14897 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { 14898 o.__proto__ = p; 14899 return o; 14900 }; 14901 return _setPrototypeOf(o, p); 14902 } 14903 function _isNativeReflectConstruct() { 14904 if (typeof Reflect === 'undefined' || !Reflect.construct) 14905 return false; 14906 if (Reflect.construct.sham) 14907 return false; 14908 if (typeof Proxy === 'function') 14909 return true; 14910 try { 14911 Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () { 14912 })); 14913 return true; 14914 } catch (e) { 14915 return false; 14916 } 14917 } 14918 function _construct(Parent, args, Class) { 14919 if (_isNativeReflectConstruct()) { 14920 _construct = Reflect.construct; 14921 } else { 14922 _construct = function _construct(Parent, args, Class) { 14923 var a = [null]; 14924 a.push.apply(a, args); 14925 var Constructor = Function.bind.apply(Parent, a); 14926 var instance = new Constructor(); 14927 if (Class) 14928 _setPrototypeOf(instance, Class.prototype); 14929 return instance; 14930 }; 14931 } 14932 return _construct.apply(null, arguments); 14933 } 14934 function _toConsumableArray(arr) { 14935 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); 14936 } 14937 function _arrayWithoutHoles(arr) { 14938 if (Array.isArray(arr)) 14939 return _arrayLikeToArray(arr); 14940 } 14941 function _iterableToArray(iter) { 14942 if (typeof Symbol !== 'undefined' && iter[Symbol.iterator] != null || iter['@@iterator'] != null) 14943 return Array.from(iter); 14944 } 14945 function _unsupportedIterableToArray(o, minLen) { 14946 if (!o) 14947 return; 14948 if (typeof o === 'string') 14949 return _arrayLikeToArray(o, minLen); 14950 var n = Object.prototype.toString.call(o).slice(8, -1); 14951 if (n === 'Object' && o.constructor) 14952 n = o.constructor.name; 14953 if (n === 'Map' || n === 'Set') 14954 return Array.from(o); 14955 if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) 14956 return _arrayLikeToArray(o, minLen); 14957 } 14958 function _arrayLikeToArray(arr, len) { 14959 if (len == null || len > arr.length) 14960 len = arr.length; 14961 for (var i = 0, arr2 = new Array(len); i < len; i++) 14962 arr2[i] = arr[i]; 14963 return arr2; 14964 } 14965 function _nonIterableSpread() { 14966 throw new TypeError('Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.'); 14967 } 14968 var hasOwnProperty = Object.hasOwnProperty, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen, getPrototypeOf = Object.getPrototypeOf, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; 14969 var freeze = Object.freeze, seal = Object.seal, create$7 = Object.create; 14970 var _ref = typeof Reflect !== 'undefined' && Reflect, apply = _ref.apply, construct = _ref.construct; 14971 if (!apply) { 14972 apply = function apply(fun, thisValue, args) { 14973 return fun.apply(thisValue, args); 14974 }; 14975 } 14976 if (!freeze) { 14977 freeze = function freeze(x) { 14978 return x; 14979 }; 14980 } 14981 if (!seal) { 14982 seal = function seal(x) { 14983 return x; 14984 }; 14985 } 14986 if (!construct) { 14987 construct = function construct(Func, args) { 14988 return _construct(Func, _toConsumableArray(args)); 14989 }; 14990 } 14991 var arrayForEach = unapply(Array.prototype.forEach); 14992 var arrayPop = unapply(Array.prototype.pop); 14993 var arrayPush = unapply(Array.prototype.push); 14994 var stringToLowerCase = unapply(String.prototype.toLowerCase); 14995 var stringMatch = unapply(String.prototype.match); 14996 var stringReplace = unapply(String.prototype.replace); 14997 var stringIndexOf = unapply(String.prototype.indexOf); 14998 var stringTrim = unapply(String.prototype.trim); 14999 var regExpTest = unapply(RegExp.prototype.test); 15000 var typeErrorCreate = unconstruct(TypeError); 15001 function unapply(func) { 15002 return function (thisArg) { 15003 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 15004 args[_key - 1] = arguments[_key]; 15005 } 15006 return apply(func, thisArg, args); 15007 }; 15008 } 15009 function unconstruct(func) { 15010 return function () { 15011 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 15012 args[_key2] = arguments[_key2]; 15013 } 15014 return construct(func, args); 15015 }; 15016 } 15017 function addToSet(set, array) { 15018 if (setPrototypeOf) { 15019 setPrototypeOf(set, null); 15020 } 15021 var l = array.length; 15022 while (l--) { 15023 var element = array[l]; 15024 if (typeof element === 'string') { 15025 var lcElement = stringToLowerCase(element); 15026 if (lcElement !== element) { 15027 if (!isFrozen(array)) { 15028 array[l] = lcElement; 15029 } 15030 element = lcElement; 15031 } 15032 } 15033 set[element] = true; 15034 } 15035 return set; 15036 } 15037 function clone(object) { 15038 var newObject = create$7(null); 15039 var property; 15040 for (property in object) { 15041 if (apply(hasOwnProperty, object, [property])) { 15042 newObject[property] = object[property]; 15043 } 15044 } 15045 return newObject; 15046 } 15047 function lookupGetter(object, prop) { 15048 while (object !== null) { 15049 var desc = getOwnPropertyDescriptor(object, prop); 15050 if (desc) { 15051 if (desc.get) { 15052 return unapply(desc.get); 15053 } 15054 if (typeof desc.value === 'function') { 15055 return unapply(desc.value); 15056 } 15057 } 15058 object = getPrototypeOf(object); 15059 } 15060 function fallbackValue(element) { 15061 console.warn('fallback value for', element); 15062 return null; 15063 } 15064 return fallbackValue; 15065 } 15066 var html$1 = freeze([ 15067 'a', 15068 'abbr', 15069 'acronym', 15070 'address', 15071 'area', 15072 'article', 15073 'aside', 15074 'audio', 15075 'b', 15076 'bdi', 15077 'bdo', 15078 'big', 15079 'blink', 15080 'blockquote', 15081 'body', 15082 'br', 15083 'button', 15084 'canvas', 15085 'caption', 15086 'center', 15087 'cite', 15088 'code', 15089 'col', 15090 'colgroup', 15091 'content', 15092 'data', 15093 'datalist', 15094 'dd', 15095 'decorator', 15096 'del', 15097 'details', 15098 'dfn', 15099 'dialog', 15100 'dir', 15101 'div', 15102 'dl', 15103 'dt', 15104 'element', 15105 'em', 15106 'fieldset', 15107 'figcaption', 15108 'figure', 15109 'font', 15110 'footer', 15111 'form', 15112 'h1', 15113 'h2', 15114 'h3', 15115 'h4', 15116 'h5', 15117 'h6', 15118 'head', 15119 'header', 15120 'hgroup', 15121 'hr', 15122 'html', 15123 'i', 15124 'img', 15125 'input', 15126 'ins', 15127 'kbd', 15128 'label', 15129 'legend', 15130 'li', 15131 'main', 15132 'map', 15133 'mark', 15134 'marquee', 15135 'menu', 15136 'menuitem', 15137 'meter', 15138 'nav', 15139 'nobr', 15140 'ol', 15141 'optgroup', 15142 'option', 15143 'output', 15144 'p', 15145 'picture', 15146 'pre', 15147 'progress', 15148 'q', 15149 'rp', 15150 'rt', 15151 'ruby', 15152 's', 15153 'samp', 15154 'section', 15155 'select', 15156 'shadow', 15157 'small', 15158 'source', 15159 'spacer', 15160 'span', 15161 'strike', 15162 'strong', 15163 'style', 15164 'sub', 15165 'summary', 15166 'sup', 15167 'table', 15168 'tbody', 15169 'td', 15170 'template', 15171 'textarea', 15172 'tfoot', 15173 'th', 15174 'thead', 15175 'time', 15176 'tr', 15177 'track', 15178 'tt', 15179 'u', 15180 'ul', 15181 'var', 15182 'video', 15183 'wbr' 15184 ]); 15185 var svg$1 = freeze([ 15186 'svg', 15187 'a', 15188 'altglyph', 15189 'altglyphdef', 15190 'altglyphitem', 15191 'animatecolor', 15192 'animatemotion', 15193 'animatetransform', 15194 'circle', 15195 'clippath', 15196 'defs', 15197 'desc', 15198 'ellipse', 15199 'filter', 15200 'font', 15201 'g', 15202 'glyph', 15203 'glyphref', 15204 'hkern', 15205 'image', 15206 'line', 15207 'lineargradient', 15208 'marker', 15209 'mask', 15210 'metadata', 15211 'mpath', 15212 'path', 15213 'pattern', 15214 'polygon', 15215 'polyline', 15216 'radialgradient', 15217 'rect', 15218 'stop', 15219 'style', 15220 'switch', 15221 'symbol', 15222 'text', 15223 'textpath', 15224 'title', 15225 'tref', 15226 'tspan', 15227 'view', 15228 'vkern' 15229 ]); 15230 var svgFilters = freeze([ 15231 'feBlend', 15232 'feColorMatrix', 15233 'feComponentTransfer', 15234 'feComposite', 15235 'feConvolveMatrix', 15236 'feDiffuseLighting', 15237 'feDisplacementMap', 15238 'feDistantLight', 15239 'feFlood', 15240 'feFuncA', 15241 'feFuncB', 15242 'feFuncG', 15243 'feFuncR', 15244 'feGaussianBlur', 15245 'feImage', 15246 'feMerge', 15247 'feMergeNode', 15248 'feMorphology', 15249 'feOffset', 15250 'fePointLight', 15251 'feSpecularLighting', 15252 'feSpotLight', 15253 'feTile', 15254 'feTurbulence' 15255 ]); 15256 var svgDisallowed = freeze([ 15257 'animate', 15258 'color-profile', 15259 'cursor', 15260 'discard', 15261 'fedropshadow', 15262 'font-face', 15263 'font-face-format', 15264 'font-face-name', 15265 'font-face-src', 15266 'font-face-uri', 15267 'foreignobject', 15268 'hatch', 15269 'hatchpath', 15270 'mesh', 15271 'meshgradient', 15272 'meshpatch', 15273 'meshrow', 15274 'missing-glyph', 15275 'script', 15276 'set', 15277 'solidcolor', 15278 'unknown', 15279 'use' 15280 ]); 15281 var mathMl$1 = freeze([ 15282 'math', 15283 'menclose', 15284 'merror', 15285 'mfenced', 15286 'mfrac', 15287 'mglyph', 15288 'mi', 15289 'mlabeledtr', 15290 'mmultiscripts', 15291 'mn', 15292 'mo', 15293 'mover', 15294 'mpadded', 15295 'mphantom', 15296 'mroot', 15297 'mrow', 15298 'ms', 15299 'mspace', 15300 'msqrt', 15301 'mstyle', 15302 'msub', 15303 'msup', 15304 'msubsup', 15305 'mtable', 15306 'mtd', 15307 'mtext', 15308 'mtr', 15309 'munder', 15310 'munderover' 15311 ]); 15312 var mathMlDisallowed = freeze([ 15313 'maction', 15314 'maligngroup', 15315 'malignmark', 15316 'mlongdiv', 15317 'mscarries', 15318 'mscarry', 15319 'msgroup', 15320 'mstack', 15321 'msline', 15322 'msrow', 15323 'semantics', 15324 'annotation', 15325 'annotation-xml', 15326 'mprescripts', 15327 'none' 15328 ]); 15329 var text = freeze(['#text']); 15330 var html = freeze([ 15331 'accept', 15332 'action', 15333 'align', 15334 'alt', 15335 'autocapitalize', 15336 'autocomplete', 15337 'autopictureinpicture', 15338 'autoplay', 15339 'background', 15340 'bgcolor', 15341 'border', 15342 'capture', 15343 'cellpadding', 15344 'cellspacing', 15345 'checked', 15346 'cite', 15347 'class', 15348 'clear', 15349 'color', 15350 'cols', 15351 'colspan', 15352 'controls', 15353 'controlslist',
vendor: 35,063 bytes, lines 15354-16373
15354 'coords', 15355 'crossorigin', 15356 'datetime', 15357 'decoding', 15358 'default', 15359 'dir', 15360 'disabled', 15361 'disablepictureinpicture', 15362 'disableremoteplayback', 15363 'download', 15364 'draggable', 15365 'enctype', 15366 'enterkeyhint', 15367 'face', 15368 'for', 15369 'headers', 15370 'height', 15371 'hidden', 15372 'high', 15373 'href', 15374 'hreflang', 15375 'id', 15376 'inputmode', 15377 'integrity', 15378 'ismap', 15379 'kind', 15380 'label', 15381 'lang', 15382 'list', 15383 'loading', 15384 'loop', 15385 'low', 15386 'max', 15387 'maxlength', 15388 'media', 15389 'method', 15390 'min', 15391 'minlength', 15392 'multiple', 15393 'muted', 15394 'name', 15395 'nonce', 15396 'noshade', 15397 'novalidate', 15398 'nowrap', 15399 'open', 15400 'optimum', 15401 'pattern', 15402 'placeholder', 15403 'playsinline', 15404 'poster', 15405 'preload', 15406 'pubdate', 15407 'radiogroup', 15408 'readonly', 15409 'rel', 15410 'required', 15411 'rev', 15412 'reversed', 15413 'role', 15414 'rows', 15415 'rowspan', 15416 'spellcheck', 15417 'scope', 15418 'selected', 15419 'shape', 15420 'size', 15421 'sizes', 15422 'span', 15423 'srclang', 15424 'start', 15425 'src', 15426 'srcset', 15427 'step', 15428 'style', 15429 'summary', 15430 'tabindex', 15431 'title', 15432 'translate', 15433 'type', 15434 'usemap', 15435 'valign', 15436 'value', 15437 'width', 15438 'xmlns', 15439 'slot' 15440 ]); 15441 var svg = freeze([ 15442 'accent-height', 15443 'accumulate', 15444 'additive', 15445 'alignment-baseline', 15446 'ascent', 15447 'attributename', 15448 'attributetype', 15449 'azimuth', 15450 'basefrequency', 15451 'baseline-shift', 15452 'begin', 15453 'bias', 15454 'by', 15455 'class', 15456 'clip', 15457 'clippathunits', 15458 'clip-path', 15459 'clip-rule', 15460 'color', 15461 'color-interpolation', 15462 'color-interpolation-filters', 15463 'color-profile', 15464 'color-rendering', 15465 'cx', 15466 'cy', 15467 'd', 15468 'dx', 15469 'dy', 15470 'diffuseconstant', 15471 'direction', 15472 'display', 15473 'divisor', 15474 'dur', 15475 'edgemode', 15476 'elevation', 15477 'end', 15478 'fill', 15479 'fill-opacity', 15480 'fill-rule', 15481 'filter', 15482 'filterunits', 15483 'flood-color', 15484 'flood-opacity', 15485 'font-family', 15486 'font-size', 15487 'font-size-adjust', 15488 'font-stretch', 15489 'font-style', 15490 'font-variant', 15491 'font-weight', 15492 'fx', 15493 'fy', 15494 'g1', 15495 'g2', 15496 'glyph-name', 15497 'glyphref', 15498 'gradientunits', 15499 'gradienttransform', 15500 'height', 15501 'href', 15502 'id', 15503 'image-rendering', 15504 'in', 15505 'in2', 15506 'k', 15507 'k1', 15508 'k2', 15509 'k3', 15510 'k4', 15511 'kerning', 15512 'keypoints', 15513 'keysplines', 15514 'keytimes', 15515 'lang', 15516 'lengthadjust', 15517 'letter-spacing', 15518 'kernelmatrix', 15519 'kernelunitlength', 15520 'lighting-color', 15521 'local', 15522 'marker-end', 15523 'marker-mid', 15524 'marker-start', 15525 'markerheight', 15526 'markerunits', 15527 'markerwidth', 15528 'maskcontentunits', 15529 'maskunits', 15530 'max', 15531 'mask', 15532 'media', 15533 'method', 15534 'mode', 15535 'min', 15536 'name', 15537 'numoctaves', 15538 'offset', 15539 'operator', 15540 'opacity', 15541 'order', 15542 'orient', 15543 'orientation', 15544 'origin', 15545 'overflow', 15546 'paint-order', 15547 'path', 15548 'pathlength', 15549 'patterncontentunits', 15550 'patterntransform', 15551 'patternunits', 15552 'points', 15553 'preservealpha', 15554 'preserveaspectratio', 15555 'primitiveunits', 15556 'r', 15557 'rx', 15558 'ry', 15559 'radius', 15560 'refx', 15561 'refy', 15562 'repeatcount', 15563 'repeatdur', 15564 'restart', 15565 'result', 15566 'rotate', 15567 'scale', 15568 'seed', 15569 'shape-rendering', 15570 'specularconstant', 15571 'specularexponent', 15572 'spreadmethod', 15573 'startoffset', 15574 'stddeviation', 15575 'stitchtiles', 15576 'stop-color', 15577 'stop-opacity', 15578 'stroke-dasharray', 15579 'stroke-dashoffset', 15580 'stroke-linecap', 15581 'stroke-linejoin', 15582 'stroke-miterlimit', 15583 'stroke-opacity', 15584 'stroke', 15585 'stroke-width', 15586 'style', 15587 'surfacescale', 15588 'systemlanguage', 15589 'tabindex', 15590 'targetx', 15591 'targety', 15592 'transform', 15593 'transform-origin', 15594 'text-anchor', 15595 'text-decoration', 15596 'text-rendering', 15597 'textlength', 15598 'type', 15599 'u1', 15600 'u2', 15601 'unicode', 15602 'values', 15603 'viewbox', 15604 'visibility', 15605 'version', 15606 'vert-adv-y', 15607 'vert-origin-x', 15608 'vert-origin-y', 15609 'width', 15610 'word-spacing', 15611 'wrap', 15612 'writing-mode', 15613 'xchannelselector', 15614 'ychannelselector', 15615 'x', 15616 'x1', 15617 'x2', 15618 'xmlns', 15619 'y', 15620 'y1', 15621 'y2', 15622 'z', 15623 'zoomandpan' 15624 ]); 15625 var mathMl = freeze([ 15626 'accent', 15627 'accentunder', 15628 'align', 15629 'bevelled', 15630 'close', 15631 'columnsalign', 15632 'columnlines', 15633 'columnspan', 15634 'denomalign', 15635 'depth', 15636 'dir', 15637 'display', 15638 'displaystyle', 15639 'encoding', 15640 'fence', 15641 'frame', 15642 'height', 15643 'href', 15644 'id', 15645 'largeop', 15646 'length', 15647 'linethickness', 15648 'lspace', 15649 'lquote', 15650 'mathbackground', 15651 'mathcolor', 15652 'mathsize', 15653 'mathvariant', 15654 'maxsize', 15655 'minsize', 15656 'movablelimits', 15657 'notation', 15658 'numalign', 15659 'open', 15660 'rowalign', 15661 'rowlines', 15662 'rowspacing', 15663 'rowspan', 15664 'rspace', 15665 'rquote', 15666 'scriptlevel', 15667 'scriptminsize', 15668 'scriptsizemultiplier', 15669 'selection', 15670 'separator', 15671 'separators', 15672 'stretchy', 15673 'subscriptshift', 15674 'supscriptshift', 15675 'symmetric', 15676 'voffset', 15677 'width', 15678 'xmlns' 15679 ]); 15680 var xml = freeze([ 15681 'xlink:href', 15682 'xml:id', 15683 'xlink:title', 15684 'xml:space', 15685 'xmlns:xlink' 15686 ]); 15687 var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); 15688 var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm); 15689 var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); 15690 var ARIA_ATTR = seal(/^aria-[\-\w]+$/); 15691 var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i); 15692 var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); 15693 var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g); 15694 var DOCTYPE_NAME = seal(/^html$/i); 15695 var getGlobal = function getGlobal() { 15696 return typeof window === 'undefined' ? null : window; 15697 }; 15698 var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { 15699 if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { 15700 return null; 15701 } 15702 var suffix = null; 15703 var ATTR_NAME = 'data-tt-policy-suffix'; 15704 if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { 15705 suffix = document.currentScript.getAttribute(ATTR_NAME); 15706 } 15707 var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); 15708 try { 15709 return trustedTypes.createPolicy(policyName, { 15710 createHTML: function createHTML(html) { 15711 return html; 15712 } 15713 }); 15714 } catch (_) { 15715 console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); 15716 return null; 15717 } 15718 }; 15719 function createDOMPurify() { 15720 var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); 15721 var DOMPurify = function DOMPurify(root) { 15722 return createDOMPurify(root); 15723 }; 15724 DOMPurify.version = '2.3.8'; 15725 DOMPurify.removed = []; 15726 if (!window || !window.document || window.document.nodeType !== 9) { 15727 DOMPurify.isSupported = false; 15728 return DOMPurify; 15729 } 15730 var originalDocument = window.document; 15731 var document = window.document; 15732 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; 15733 var ElementPrototype = Element.prototype; 15734 var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); 15735 var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling'); 15736 var getChildNodes = lookupGetter(ElementPrototype, 'childNodes'); 15737 var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); 15738 if (typeof HTMLTemplateElement === 'function') { 15739 var template = document.createElement('template'); 15740 if (template.content && template.content.ownerDocument) { 15741 document = template.content.ownerDocument; 15742 } 15743 } 15744 var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); 15745 var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : ''; 15746 var _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName; 15747 var importNode = originalDocument.importNode; 15748 var documentMode = {}; 15749 try { 15750 documentMode = clone(document).documentMode ? document.documentMode : {}; 15751 } catch (_) { 15752 } 15753 var hooks = {}; 15754 DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; 15755 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; 15756 var IS_ALLOWED_URI$1 = IS_ALLOWED_URI; 15757 var ALLOWED_TAGS = null; 15758 var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text))); 15759 var ALLOWED_ATTR = null; 15760 var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml))); 15761 var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, { 15762 tagNameCheck: { 15763 writable: true, 15764 configurable: false, 15765 enumerable: true, 15766 value: null 15767 }, 15768 attributeNameCheck: { 15769 writable: true, 15770 configurable: false, 15771 enumerable: true, 15772 value: null 15773 }, 15774 allowCustomizedBuiltInElements: { 15775 writable: true, 15776 configurable: false, 15777 enumerable: true, 15778 value: false 15779 } 15780 })); 15781 var FORBID_TAGS = null; 15782 var FORBID_ATTR = null; 15783 var ALLOW_ARIA_ATTR = true; 15784 var ALLOW_DATA_ATTR = true; 15785 var ALLOW_UNKNOWN_PROTOCOLS = false; 15786 var SAFE_FOR_TEMPLATES = false; 15787 var WHOLE_DOCUMENT = false; 15788 var SET_CONFIG = false; 15789 var FORCE_BODY = false; 15790 var RETURN_DOM = false; 15791 var RETURN_DOM_FRAGMENT = false; 15792 var RETURN_TRUSTED_TYPE = false; 15793 var SANITIZE_DOM = true; 15794 var KEEP_CONTENT = true; 15795 var IN_PLACE = false; 15796 var USE_PROFILES = {}; 15797 var FORBID_CONTENTS = null; 15798 var DEFAULT_FORBID_CONTENTS = addToSet({}, [ 15799 'annotation-xml', 15800 'audio', 15801 'colgroup', 15802 'desc', 15803 'foreignobject', 15804 'head', 15805 'iframe', 15806 'math', 15807 'mi', 15808 'mn', 15809 'mo', 15810 'ms', 15811 'mtext', 15812 'noembed', 15813 'noframes', 15814 'noscript', 15815 'plaintext', 15816 'script', 15817 'style', 15818 'svg', 15819 'template', 15820 'thead', 15821 'title', 15822 'video', 15823 'xmp' 15824 ]); 15825 var DATA_URI_TAGS = null; 15826 var DEFAULT_DATA_URI_TAGS = addToSet({}, [ 15827 'audio', 15828 'video', 15829 'img', 15830 'source', 15831 'image', 15832 'track' 15833 ]); 15834 var URI_SAFE_ATTRIBUTES = null; 15835 var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [ 15836 'alt', 15837 'class', 15838 'for', 15839 'id', 15840 'label', 15841 'name', 15842 'pattern', 15843 'placeholder', 15844 'role', 15845 'summary', 15846 'title', 15847 'value', 15848 'style', 15849 'xmlns' 15850 ]); 15851 var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; 15852 var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; 15853 var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; 15854 var NAMESPACE = HTML_NAMESPACE; 15855 var IS_EMPTY_INPUT = false; 15856 var PARSER_MEDIA_TYPE; 15857 var SUPPORTED_PARSER_MEDIA_TYPES = [ 15858 'application/xhtml+xml', 15859 'text/html' 15860 ]; 15861 var DEFAULT_PARSER_MEDIA_TYPE = 'text/html'; 15862 var transformCaseFunc; 15863 var CONFIG = null; 15864 var formElement = document.createElement('form'); 15865 var isRegexOrFunction = function isRegexOrFunction(testValue) { 15866 return testValue instanceof RegExp || testValue instanceof Function; 15867 }; 15868 var _parseConfig = function _parseConfig(cfg) { 15869 if (CONFIG && CONFIG === cfg) { 15870 return; 15871 } 15872 if (!cfg || _typeof(cfg) !== 'object') { 15873 cfg = {}; 15874 } 15875 cfg = clone(cfg); 15876 ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; 15877 ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; 15878 URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES; 15879 DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS; 15880 FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS; 15881 FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {}; 15882 FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {}; 15883 USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; 15884 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; 15885 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; 15886 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; 15887 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; 15888 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; 15889 RETURN_DOM = cfg.RETURN_DOM || false; 15890 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; 15891 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; 15892 FORCE_BODY = cfg.FORCE_BODY || false; 15893 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; 15894 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; 15895 IN_PLACE = cfg.IN_PLACE || false; 15896 IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1; 15897 NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE; 15898 if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) { 15899 CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck; 15900 } 15901 if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) { 15902 CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck; 15903 } 15904 if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') { 15905 CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements; 15906 } 15907 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; 15908 transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) { 15909 return x; 15910 } : stringToLowerCase; 15911 if (SAFE_FOR_TEMPLATES) { 15912 ALLOW_DATA_ATTR = false; 15913 } 15914 if (RETURN_DOM_FRAGMENT) { 15915 RETURN_DOM = true; 15916 } 15917 if (USE_PROFILES) { 15918 ALLOWED_TAGS = addToSet({}, _toConsumableArray(text)); 15919 ALLOWED_ATTR = []; 15920 if (USE_PROFILES.html === true) { 15921 addToSet(ALLOWED_TAGS, html$1); 15922 addToSet(ALLOWED_ATTR, html); 15923 } 15924 if (USE_PROFILES.svg === true) { 15925 addToSet(ALLOWED_TAGS, svg$1); 15926 addToSet(ALLOWED_ATTR, svg); 15927 addToSet(ALLOWED_ATTR, xml); 15928 } 15929 if (USE_PROFILES.svgFilters === true) { 15930 addToSet(ALLOWED_TAGS, svgFilters); 15931 addToSet(ALLOWED_ATTR, svg); 15932 addToSet(ALLOWED_ATTR, xml); 15933 } 15934 if (USE_PROFILES.mathMl === true) { 15935 addToSet(ALLOWED_TAGS, mathMl$1); 15936 addToSet(ALLOWED_ATTR, mathMl); 15937 addToSet(ALLOWED_ATTR, xml); 15938 } 15939 } 15940 if (cfg.ADD_TAGS) { 15941 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { 15942 ALLOWED_TAGS = clone(ALLOWED_TAGS); 15943 } 15944 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); 15945 } 15946 if (cfg.ADD_ATTR) { 15947 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { 15948 ALLOWED_ATTR = clone(ALLOWED_ATTR); 15949 } 15950 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); 15951 } 15952 if (cfg.ADD_URI_SAFE_ATTR) { 15953 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); 15954 } 15955 if (cfg.FORBID_CONTENTS) { 15956 if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) { 15957 FORBID_CONTENTS = clone(FORBID_CONTENTS); 15958 } 15959 addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS); 15960 } 15961 if (KEEP_CONTENT) { 15962 ALLOWED_TAGS['#text'] = true; 15963 } 15964 if (WHOLE_DOCUMENT) { 15965 addToSet(ALLOWED_TAGS, [ 15966 'html', 15967 'head', 15968 'body' 15969 ]); 15970 } 15971 if (ALLOWED_TAGS.table) { 15972 addToSet(ALLOWED_TAGS, ['tbody']); 15973 delete FORBID_TAGS.tbody; 15974 } 15975 if (freeze) { 15976 freeze(cfg); 15977 } 15978 CONFIG = cfg; 15979 }; 15980 var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [ 15981 'mi', 15982 'mo', 15983 'mn', 15984 'ms', 15985 'mtext' 15986 ]); 15987 var HTML_INTEGRATION_POINTS = addToSet({}, [ 15988 'foreignobject', 15989 'desc', 15990 'title', 15991 'annotation-xml' 15992 ]); 15993 var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [ 15994 'title', 15995 'style', 15996 'font', 15997 'a', 15998 'script' 15999 ]); 16000 var ALL_SVG_TAGS = addToSet({}, svg$1); 16001 addToSet(ALL_SVG_TAGS, svgFilters); 16002 addToSet(ALL_SVG_TAGS, svgDisallowed); 16003 var ALL_MATHML_TAGS = addToSet({}, mathMl$1); 16004 addToSet(ALL_MATHML_TAGS, mathMlDisallowed); 16005 var _checkValidNamespace = function _checkValidNamespace(element) { 16006 var parent = getParentNode(element); 16007 if (!parent || !parent.tagName) { 16008 parent = { 16009 namespaceURI: HTML_NAMESPACE, 16010 tagName: 'template' 16011 }; 16012 } 16013 var tagName = stringToLowerCase(element.tagName); 16014 var parentTagName = stringToLowerCase(parent.tagName); 16015 if (element.namespaceURI === SVG_NAMESPACE) { 16016 if (parent.namespaceURI === HTML_NAMESPACE) { 16017 return tagName === 'svg'; 16018 } 16019 if (parent.namespaceURI === MATHML_NAMESPACE) { 16020 return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); 16021 } 16022 return Boolean(ALL_SVG_TAGS[tagName]); 16023 } 16024 if (element.namespaceURI === MATHML_NAMESPACE) { 16025 if (parent.namespaceURI === HTML_NAMESPACE) { 16026 return tagName === 'math'; 16027 } 16028 if (parent.namespaceURI === SVG_NAMESPACE) { 16029 return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; 16030 } 16031 return Boolean(ALL_MATHML_TAGS[tagName]); 16032 } 16033 if (element.namespaceURI === HTML_NAMESPACE) { 16034 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { 16035 return false; 16036 } 16037 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { 16038 return false; 16039 } 16040 return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]); 16041 } 16042 return false; 16043 }; 16044 var _forceRemove = function _forceRemove(node) { 16045 arrayPush(DOMPurify.removed, { element: node }); 16046 try { 16047 node.parentNode.removeChild(node); 16048 } catch (_) { 16049 try { 16050 node.outerHTML = emptyHTML; 16051 } catch (_) { 16052 node.remove(); 16053 } 16054 } 16055 }; 16056 var _removeAttribute = function _removeAttribute(name, node) { 16057 try { 16058 arrayPush(DOMPurify.removed, { 16059 attribute: node.getAttributeNode(name), 16060 from: node 16061 }); 16062 } catch (_) { 16063 arrayPush(DOMPurify.removed, { 16064 attribute: null, 16065 from: node 16066 }); 16067 } 16068 node.removeAttribute(name); 16069 if (name === 'is' && !ALLOWED_ATTR[name]) { 16070 if (RETURN_DOM || RETURN_DOM_FRAGMENT) { 16071 try { 16072 _forceRemove(node); 16073 } catch (_) { 16074 } 16075 } else { 16076 try { 16077 node.setAttribute(name, ''); 16078 } catch (_) { 16079 } 16080 } 16081 } 16082 }; 16083 var _initDocument = function _initDocument(dirty) { 16084 var doc; 16085 var leadingWhitespace; 16086 if (FORCE_BODY) { 16087 dirty = '<remove></remove>' + dirty; 16088 } else { 16089 var matches = stringMatch(dirty, /^[\r\n\t ]+/); 16090 leadingWhitespace = matches && matches[0]; 16091 } 16092 if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') { 16093 dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>'; 16094 } 16095 var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; 16096 if (NAMESPACE === HTML_NAMESPACE) { 16097 try { 16098 doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE); 16099 } catch (_) { 16100 } 16101 } 16102 if (!doc || !doc.documentElement) { 16103 doc = implementation.createDocument(NAMESPACE, 'template', null); 16104 try { 16105 doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload; 16106 } catch (_) { 16107 } 16108 } 16109 var body = doc.body || doc.documentElement; 16110 if (dirty && leadingWhitespace) { 16111 body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null); 16112 } 16113 if (NAMESPACE === HTML_NAMESPACE) { 16114 return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; 16115 } 16116 return WHOLE_DOCUMENT ? doc.documentElement : body; 16117 }; 16118 var _createIterator = function _createIterator(root) { 16119 return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false); 16120 }; 16121 var _isClobbered = function _isClobbered(elm) { 16122 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'); 16123 }; 16124 var _isNode = function _isNode(object) { 16125 return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; 16126 }; 16127 var _executeHook = function _executeHook(entryPoint, currentNode, data) { 16128 if (!hooks[entryPoint]) { 16129 return; 16130 } 16131 arrayForEach(hooks[entryPoint], function (hook) { 16132 hook.call(DOMPurify, currentNode, data, CONFIG); 16133 }); 16134 }; 16135 var _sanitizeElements = function _sanitizeElements(currentNode) { 16136 var content; 16137 _executeHook('beforeSanitizeElements', currentNode, null); 16138 if (_isClobbered(currentNode)) { 16139 _forceRemove(currentNode); 16140 return true; 16141 } 16142 if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) { 16143 _forceRemove(currentNode); 16144 return true; 16145 } 16146 var tagName = transformCaseFunc(currentNode.nodeName); 16147 _executeHook('uponSanitizeElement', currentNode, { 16148 tagName: tagName, 16149 allowedTags: ALLOWED_TAGS 16150 }); 16151 if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { 16152 _forceRemove(currentNode); 16153 return true; 16154 } 16155 if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) { 16156 _forceRemove(currentNode); 16157 return true; 16158 } 16159 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { 16160 if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) { 16161 if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) 16162 return false; 16163 if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) 16164 return false; 16165 } 16166 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) { 16167 var parentNode = getParentNode(currentNode) || currentNode.parentNode; 16168 var childNodes = getChildNodes(currentNode) || currentNode.childNodes; 16169 if (childNodes && parentNode) { 16170 var childCount = childNodes.length; 16171 for (var i = childCount - 1; i >= 0; --i) { 16172 parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); 16173 } 16174 } 16175 } 16176 _forceRemove(currentNode); 16177 return true; 16178 } 16179 if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) { 16180 _forceRemove(currentNode); 16181 return true; 16182 } 16183 if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) { 16184 _forceRemove(currentNode); 16185 return true; 16186 } 16187 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) { 16188 content = currentNode.textContent; 16189 content = stringReplace(content, MUSTACHE_EXPR$1, ' '); 16190 content = stringReplace(content, ERB_EXPR$1, ' '); 16191 if (currentNode.textContent !== content) { 16192 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() }); 16193 currentNode.textContent = content; 16194 } 16195 } 16196 _executeHook('afterSanitizeElements', currentNode, null); 16197 return false; 16198 }; 16199 var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { 16200 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) { 16201 return false; 16202 } 16203 if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)); 16204 else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)); 16205 else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) { 16206 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))); 16207 else { 16208 return false; 16209 } 16210 } else if (URI_SAFE_ATTRIBUTES[lcName]); 16211 else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))); 16212 else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]); 16213 else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))); 16214 else if (!value); 16215 else { 16216 return false; 16217 } 16218 return true; 16219 }; 16220 var _basicCustomElementTest = function _basicCustomElementTest(tagName) { 16221 return tagName.indexOf('-') > 0; 16222 }; 16223 var _sanitizeAttributes = function _sanitizeAttributes(currentNode) { 16224 var attr; 16225 var value; 16226 var lcName; 16227 var l; 16228 _executeHook('beforeSanitizeAttributes', currentNode, null); 16229 var attributes = currentNode.attributes; 16230 if (!attributes) { 16231 return; 16232 } 16233 var hookEvent = { 16234 attrName: '', 16235 attrValue: '', 16236 keepAttr: true, 16237 allowedAttributes: ALLOWED_ATTR 16238 }; 16239 l = attributes.length; 16240 while (l--) { 16241 attr = attributes[l]; 16242 var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI; 16243 value = name === 'value' ? attr.value : stringTrim(attr.value); 16244 lcName = transformCaseFunc(name); 16245 var initValue = value; 16246 hookEvent.attrName = lcName; 16247 hookEvent.attrValue = value; 16248 hookEvent.keepAttr = true; 16249 hookEvent.forceKeepAttr = undefined; 16250 _executeHook('uponSanitizeAttribute', currentNode, hookEvent); 16251 value = hookEvent.attrValue; 16252 if (hookEvent.forceKeepAttr) { 16253 continue; 16254 } 16255 if (!hookEvent.keepAttr) { 16256 _removeAttribute(name, currentNode); 16257 continue; 16258 } 16259 if (regExpTest(/\/>/i, value)) { 16260 _removeAttribute(name, currentNode); 16261 continue; 16262 } 16263 if (SAFE_FOR_TEMPLATES) { 16264 value = stringReplace(value, MUSTACHE_EXPR$1, ' '); 16265 value = stringReplace(value, ERB_EXPR$1, ' '); 16266 } 16267 var lcTag = transformCaseFunc(currentNode.nodeName); 16268 if (!_isValidAttribute(lcTag, lcName, value)) { 16269 _removeAttribute(name, currentNode); 16270 continue; 16271 } 16272 if (value !== initValue) { 16273 try { 16274 if (namespaceURI) { 16275 currentNode.setAttributeNS(namespaceURI, name, value); 16276 } else { 16277 currentNode.setAttribute(name, value); 16278 } 16279 } catch (_) { 16280 _removeAttribute(name, currentNode); 16281 } 16282 } 16283 } 16284 _executeHook('afterSanitizeAttributes', currentNode, null); 16285 }; 16286 var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { 16287 var shadowNode; 16288 var shadowIterator = _createIterator(fragment); 16289 _executeHook('beforeSanitizeShadowDOM', fragment, null); 16290 while (shadowNode = shadowIterator.nextNode()) { 16291 _executeHook('uponSanitizeShadowNode', shadowNode, null); 16292 if (_sanitizeElements(shadowNode)) { 16293 continue; 16294 } 16295 if (shadowNode.content instanceof DocumentFragment) { 16296 _sanitizeShadowDOM(shadowNode.content); 16297 } 16298 _sanitizeAttributes(shadowNode); 16299 } 16300 _executeHook('afterSanitizeShadowDOM', fragment, null); 16301 }; 16302 DOMPurify.sanitize = function (dirty, cfg) { 16303 var body; 16304 var importedNode; 16305 var currentNode; 16306 var oldNode; 16307 var returnNode; 16308 IS_EMPTY_INPUT = !dirty; 16309 if (IS_EMPTY_INPUT) { 16310 dirty = '<!-->'; 16311 } 16312 if (typeof dirty !== 'string' && !_isNode(dirty)) { 16313 if (typeof dirty.toString !== 'function') { 16314 throw typeErrorCreate('toString is not a function'); 16315 } else { 16316 dirty = dirty.toString(); 16317 if (typeof dirty !== 'string') { 16318 throw typeErrorCreate('dirty is not a string, aborting'); 16319 } 16320 } 16321 } 16322 if (!DOMPurify.isSupported) { 16323 if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') { 16324 if (typeof dirty === 'string') { 16325 return window.toStaticHTML(dirty); 16326 } 16327 if (_isNode(dirty)) { 16328 return window.toStaticHTML(dirty.outerHTML); 16329 } 16330 } 16331 return dirty; 16332 } 16333 if (!SET_CONFIG) { 16334 _parseConfig(cfg); 16335 } 16336 DOMPurify.removed = []; 16337 if (typeof dirty === 'string') { 16338 IN_PLACE = false; 16339 } 16340 if (IN_PLACE) { 16341 if (dirty.nodeName) { 16342 var tagName = transformCaseFunc(dirty.nodeName); 16343 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { 16344 throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place'); 16345 } 16346 } 16347 } else if (dirty instanceof Node) { 16348 body = _initDocument('<!---->'); 16349 importedNode = body.ownerDocument.importNode(dirty, true); 16350 if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') { 16351 body = importedNode; 16352 } else if (importedNode.nodeName === 'HTML') { 16353 body = importedNode; 16354 } else { 16355 body.appendChild(importedNode); 16356 } 16357 } else { 16358 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) { 16359 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty; 16360 } 16361 body = _initDocument(dirty); 16362 if (!body) { 16363 return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : ''; 16364 } 16365 } 16366 if (body && FORCE_BODY) { 16367 _forceRemove(body.firstChild); 16368 } 16369 var nodeIterator = _createIterator(IN_PLACE ? dirty : body); 16370 while (currentNode = nodeIterator.nextNode()) { 16371 if (currentNode.nodeType === 3 && currentNode === oldNode) { 16372 continue; 16373 }
vendor: 22,996 bytes, lines 16374-17002
16374 if (_sanitizeElements(currentNode)) { 16375 continue; 16376 } 16377 if (currentNode.content instanceof DocumentFragment) { 16378 _sanitizeShadowDOM(currentNode.content); 16379 } 16380 _sanitizeAttributes(currentNode); 16381 oldNode = currentNode; 16382 } 16383 oldNode = null; 16384 if (IN_PLACE) { 16385 return dirty; 16386 } 16387 if (RETURN_DOM) { 16388 if (RETURN_DOM_FRAGMENT) { 16389 returnNode = createDocumentFragment.call(body.ownerDocument); 16390 while (body.firstChild) { 16391 returnNode.appendChild(body.firstChild); 16392 } 16393 } else { 16394 returnNode = body; 16395 } 16396 if (ALLOWED_ATTR.shadowroot) { 16397 returnNode = importNode.call(originalDocument, returnNode, true); 16398 } 16399 return returnNode; 16400 } 16401 var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML; 16402 if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) { 16403 serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML; 16404 } 16405 if (SAFE_FOR_TEMPLATES) { 16406 serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' '); 16407 serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' '); 16408 } 16409 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML; 16410 }; 16411 DOMPurify.setConfig = function (cfg) { 16412 _parseConfig(cfg); 16413 SET_CONFIG = true; 16414 }; 16415 DOMPurify.clearConfig = function () { 16416 CONFIG = null; 16417 SET_CONFIG = false; 16418 }; 16419 DOMPurify.isValidAttribute = function (tag, attr, value) { 16420 if (!CONFIG) { 16421 _parseConfig({}); 16422 } 16423 var lcTag = transformCaseFunc(tag); 16424 var lcName = transformCaseFunc(attr); 16425 return _isValidAttribute(lcTag, lcName, value); 16426 }; 16427 DOMPurify.addHook = function (entryPoint, hookFunction) { 16428 if (typeof hookFunction !== 'function') { 16429 return; 16430 } 16431 hooks[entryPoint] = hooks[entryPoint] || []; 16432 arrayPush(hooks[entryPoint], hookFunction); 16433 }; 16434 DOMPurify.removeHook = function (entryPoint) { 16435 if (hooks[entryPoint]) { 16436 return arrayPop(hooks[entryPoint]); 16437 } 16438 }; 16439 DOMPurify.removeHooks = function (entryPoint) { 16440 if (hooks[entryPoint]) { 16441 hooks[entryPoint] = []; 16442 } 16443 }; 16444 DOMPurify.removeAllHooks = function () { 16445 hooks = {}; 16446 }; 16447 return DOMPurify; 16448 } 16449 var purify = createDOMPurify(); 16450 16451 const each$4 = Tools.each, trim = Tools.trim; 16452 const queryParts = [ 16453 'source', 16454 'protocol', 16455 'authority', 16456 'userInfo', 16457 'user', 16458 'password', 16459 'host', 16460 'port', 16461 'relative', 16462 'path', 16463 'directory', 16464 'file', 16465 'query', 16466 'anchor' 16467 ]; 16468 const DEFAULT_PORTS = { 16469 ftp: 21, 16470 http: 80, 16471 https: 443, 16472 mailto: 25 16473 }; 16474 const safeSvgDataUrlElements = [ 16475 'img', 16476 'video' 16477 ]; 16478 const blockSvgDataUris = (allowSvgDataUrls, tagName) => { 16479 if (isNonNullable(allowSvgDataUrls)) { 16480 return !allowSvgDataUrls; 16481 } else { 16482 return isNonNullable(tagName) ? !contains$2(safeSvgDataUrlElements, tagName) : true; 16483 } 16484 }; 16485 const decodeUri = encodedUri => { 16486 try { 16487 return decodeURIComponent(encodedUri); 16488 } catch (ex) { 16489 return unescape(encodedUri); 16490 } 16491 }; 16492 const isInvalidUri = (settings, uri, tagName) => { 16493 const decodedUri = decodeUri(uri).replace(/\s/g, ''); 16494 if (settings.allow_script_urls) { 16495 return false; 16496 } else if (/((java|vb)script|mhtml):/i.test(decodedUri)) { 16497 return true; 16498 } else if (settings.allow_html_data_urls) { 16499 return false; 16500 } else if (/^data:image\//i.test(decodedUri)) { 16501 return blockSvgDataUris(settings.allow_svg_data_urls, tagName) && /^data:image\/svg\+xml/i.test(decodedUri); 16502 } else { 16503 return /^data:/i.test(decodedUri); 16504 } 16505 }; 16506 class URI { 16507 static parseDataUri(uri) { 16508 let type; 16509 const uriComponents = decodeURIComponent(uri).split(','); 16510 const matches = /data:([^;]+)/.exec(uriComponents[0]); 16511 if (matches) { 16512 type = matches[1]; 16513 } 16514 return { 16515 type, 16516 data: uriComponents[1] 16517 }; 16518 } 16519 static isDomSafe(uri, context, options = {}) { 16520 if (options.allow_script_urls) { 16521 return true; 16522 } else { 16523 const decodedUri = Entities.decode(uri).replace(/[\s\u0000-\u001F]+/g, ''); 16524 return !isInvalidUri(options, decodedUri, context); 16525 } 16526 } 16527 static getDocumentBaseUrl(loc) { 16528 var _a; 16529 let baseUrl; 16530 if (loc.protocol.indexOf('http') !== 0 && loc.protocol !== 'file:') { 16531 baseUrl = (_a = loc.href) !== null && _a !== void 0 ? _a : ''; 16532 } else { 16533 baseUrl = loc.protocol + '//' + loc.host + loc.pathname; 16534 } 16535 if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) { 16536 baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); 16537 if (!/[\/\\]$/.test(baseUrl)) { 16538 baseUrl += '/'; 16539 } 16540 } 16541 return baseUrl; 16542 } 16543 constructor(url, settings = {}) { 16544 this.path = ''; 16545 this.directory = ''; 16546 url = trim(url); 16547 this.settings = settings; 16548 const baseUri = settings.base_uri; 16549 const self = this; 16550 if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) { 16551 self.source = url; 16552 return; 16553 } 16554 const isProtocolRelative = url.indexOf('//') === 0; 16555 if (url.indexOf('/') === 0 && !isProtocolRelative) { 16556 url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url; 16557 } 16558 if (!/^[\w\-]*:?\/\//.test(url)) { 16559 const baseUrl = baseUri ? baseUri.path : new URI(document.location.href).directory; 16560 if ((baseUri === null || baseUri === void 0 ? void 0 : baseUri.protocol) === '') { 16561 url = '//mce_host' + self.toAbsPath(baseUrl, url); 16562 } else { 16563 const match = /([^#?]*)([#?]?.*)/.exec(url); 16564 if (match) { 16565 url = (baseUri && baseUri.protocol || 'http') + '://mce_host' + self.toAbsPath(baseUrl, match[1]) + match[2]; 16566 } 16567 } 16568 } 16569 url = url.replace(/@@/g, '(mce_at)'); 16570 const urlMatch = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?(\[[a-zA-Z0-9:.%]+\]|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url); 16571 if (urlMatch) { 16572 each$4(queryParts, (v, i) => { 16573 let part = urlMatch[i]; 16574 if (part) { 16575 part = part.replace(/\(mce_at\)/g, '@@'); 16576 } 16577 self[v] = part; 16578 }); 16579 } 16580 if (baseUri) { 16581 if (!self.protocol) { 16582 self.protocol = baseUri.protocol; 16583 } 16584 if (!self.userInfo) { 16585 self.userInfo = baseUri.userInfo; 16586 } 16587 if (!self.port && self.host === 'mce_host') { 16588 self.port = baseUri.port; 16589 } 16590 if (!self.host || self.host === 'mce_host') { 16591 self.host = baseUri.host; 16592 } 16593 self.source = ''; 16594 } 16595 if (isProtocolRelative) { 16596 self.protocol = ''; 16597 } 16598 } 16599 setPath(path) { 16600 const pathMatch = /^(.*?)\/?(\w+)?$/.exec(path); 16601 if (pathMatch) { 16602 this.path = pathMatch[0]; 16603 this.directory = pathMatch[1]; 16604 this.file = pathMatch[2]; 16605 } 16606 this.source = ''; 16607 this.getURI(); 16608 } 16609 toRelative(uri) { 16610 if (uri === './') { 16611 return uri; 16612 } 16613 const relativeUri = new URI(uri, { base_uri: this }); 16614 if (relativeUri.host !== 'mce_host' && this.host !== relativeUri.host && relativeUri.host || this.port !== relativeUri.port || this.protocol !== relativeUri.protocol && relativeUri.protocol !== '') { 16615 return relativeUri.getURI(); 16616 } 16617 const tu = this.getURI(), uu = relativeUri.getURI(); 16618 if (tu === uu || tu.charAt(tu.length - 1) === '/' && tu.substr(0, tu.length - 1) === uu) { 16619 return tu; 16620 } 16621 let output = this.toRelPath(this.path, relativeUri.path); 16622 if (relativeUri.query) { 16623 output += '?' + relativeUri.query; 16624 } 16625 if (relativeUri.anchor) { 16626 output += '#' + relativeUri.anchor; 16627 } 16628 return output; 16629 } 16630 toAbsolute(uri, noHost) { 16631 const absoluteUri = new URI(uri, { base_uri: this }); 16632 return absoluteUri.getURI(noHost && this.isSameOrigin(absoluteUri)); 16633 } 16634 isSameOrigin(uri) { 16635 if (this.host == uri.host && this.protocol == uri.protocol) { 16636 if (this.port == uri.port) { 16637 return true; 16638 } 16639 const defaultPort = this.protocol ? DEFAULT_PORTS[this.protocol] : null; 16640 if (defaultPort && (this.port || defaultPort) == (uri.port || defaultPort)) { 16641 return true; 16642 } 16643 } 16644 return false; 16645 } 16646 toRelPath(base, path) { 16647 let breakPoint = 0, out = '', i, l; 16648 const normalizedBase = base.substring(0, base.lastIndexOf('/')).split('/'); 16649 const items = path.split('/'); 16650 if (normalizedBase.length >= items.length) { 16651 for (i = 0, l = normalizedBase.length; i < l; i++) { 16652 if (i >= items.length || normalizedBase[i] !== items[i]) { 16653 breakPoint = i + 1; 16654 break; 16655 } 16656 } 16657 } 16658 if (normalizedBase.length < items.length) { 16659 for (i = 0, l = items.length; i < l; i++) { 16660 if (i >= normalizedBase.length || normalizedBase[i] !== items[i]) { 16661 breakPoint = i + 1; 16662 break; 16663 } 16664 } 16665 } 16666 if (breakPoint === 1) { 16667 return path; 16668 } 16669 for (i = 0, l = normalizedBase.length - (breakPoint - 1); i < l; i++) { 16670 out += '../'; 16671 } 16672 for (i = breakPoint - 1, l = items.length; i < l; i++) { 16673 if (i !== breakPoint - 1) { 16674 out += '/' + items[i]; 16675 } else { 16676 out += items[i]; 16677 } 16678 } 16679 return out; 16680 } 16681 toAbsPath(base, path) { 16682 let nb = 0; 16683 const tr = /\/$/.test(path) ? '/' : ''; 16684 const normalizedBase = base.split('/'); 16685 const normalizedPath = path.split('/'); 16686 const baseParts = []; 16687 each$4(normalizedBase, k => { 16688 if (k) { 16689 baseParts.push(k); 16690 } 16691 }); 16692 const pathParts = []; 16693 for (let i = normalizedPath.length - 1; i >= 0; i--) { 16694 if (normalizedPath[i].length === 0 || normalizedPath[i] === '.') { 16695 continue; 16696 } 16697 if (normalizedPath[i] === '..') { 16698 nb++; 16699 continue; 16700 } 16701 if (nb > 0) { 16702 nb--; 16703 continue; 16704 } 16705 pathParts.push(normalizedPath[i]); 16706 } 16707 const i = baseParts.length - nb; 16708 let outPath; 16709 if (i <= 0) { 16710 outPath = reverse(pathParts).join('/'); 16711 } else { 16712 outPath = baseParts.slice(0, i).join('/') + '/' + reverse(pathParts).join('/'); 16713 } 16714 if (outPath.indexOf('/') !== 0) { 16715 outPath = '/' + outPath; 16716 } 16717 if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) { 16718 outPath += tr; 16719 } 16720 return outPath; 16721 } 16722 getURI(noProtoHost = false) { 16723 let s; 16724 if (!this.source || noProtoHost) { 16725 s = ''; 16726 if (!noProtoHost) { 16727 if (this.protocol) { 16728 s += this.protocol + '://'; 16729 } else { 16730 s += '//'; 16731 } 16732 if (this.userInfo) { 16733 s += this.userInfo + '@'; 16734 } 16735 if (this.host) { 16736 s += this.host; 16737 } 16738 if (this.port) { 16739 s += ':' + this.port; 16740 } 16741 } 16742 if (this.path) { 16743 s += this.path; 16744 } 16745 if (this.query) { 16746 s += '?' + this.query; 16747 } 16748 if (this.anchor) { 16749 s += '#' + this.anchor; 16750 } 16751 this.source = s; 16752 } 16753 return this.source; 16754 } 16755 } 16756 16757 const filteredUrlAttrs = Tools.makeMap('src,href,data,background,action,formaction,poster,xlink:href'); 16758 const internalElementAttr = 'data-mce-type'; 16759 let uid = 0; 16760 const processNode = (node, settings, schema, evt) => { 16761 var _a, _b, _c, _d; 16762 const validate = settings.validate; 16763 const specialElements = schema.getSpecialElements(); 16764 if (node.nodeType === COMMENT && !settings.allow_conditional_comments && /^\[if/i.test((_a = node.nodeValue) !== null && _a !== void 0 ? _a : '')) { 16765 node.nodeValue = ' ' + node.nodeValue; 16766 } 16767 const lcTagName = (_b = evt === null || evt === void 0 ? void 0 : evt.tagName) !== null && _b !== void 0 ? _b : node.nodeName.toLowerCase(); 16768 if (node.nodeType !== ELEMENT || lcTagName === 'body') { 16769 return; 16770 } 16771 const element = SugarElement.fromDom(node); 16772 const isInternalElement = has$1(element, internalElementAttr); 16773 const bogus = get$9(element, 'data-mce-bogus'); 16774 if (!isInternalElement && isString(bogus)) { 16775 if (bogus === 'all') { 16776 remove$5(element); 16777 } else { 16778 unwrap(element); 16779 } 16780 return; 16781 } 16782 const rule = schema.getElementRule(lcTagName); 16783 if (validate && !rule) { 16784 if (has$2(specialElements, lcTagName)) { 16785 remove$5(element); 16786 } else { 16787 unwrap(element); 16788 } 16789 return; 16790 } else { 16791 if (isNonNullable(evt)) { 16792 evt.allowedTags[lcTagName] = true; 16793 } 16794 } 16795 if (validate && rule && !isInternalElement) { 16796 each$e((_c = rule.attributesForced) !== null && _c !== void 0 ? _c : [], attr => { 16797 set$3(element, attr.name, attr.value === '{$uid}' ? `mce_${ uid++ }` : attr.value); 16798 }); 16799 each$e((_d = rule.attributesDefault) !== null && _d !== void 0 ? _d : [], attr => { 16800 if (!has$1(element, attr.name)) { 16801 set$3(element, attr.name, attr.value === '{$uid}' ? `mce_${ uid++ }` : attr.value); 16802 } 16803 }); 16804 if (rule.attributesRequired && !exists(rule.attributesRequired, attr => has$1(element, attr))) { 16805 unwrap(element); 16806 return; 16807 } 16808 if (rule.removeEmptyAttrs && hasNone(element)) { 16809 unwrap(element); 16810 return; 16811 } 16812 if (rule.outputName && rule.outputName !== lcTagName) { 16813 mutate(element, rule.outputName); 16814 } 16815 } 16816 }; 16817 const shouldKeepAttribute = (settings, schema, tagName, attrName, attrValue) => !(attrName in filteredUrlAttrs && isInvalidUri(settings, attrValue, tagName)) && (!settings.validate || schema.isValid(tagName, attrName) || startsWith(attrName, 'data-') || startsWith(attrName, 'aria-')); 16818 const isRequiredAttributeOfInternalElement = (ele, attrName) => ele.hasAttribute(internalElementAttr) && (attrName === 'id' || attrName === 'class' || attrName === 'style'); 16819 const isBooleanAttribute = (attrName, schema) => attrName in schema.getBoolAttrs(); 16820 const filterAttributes = (ele, settings, schema) => { 16821 const {attributes} = ele; 16822 for (let i = attributes.length - 1; i >= 0; i--) { 16823 const attr = attributes[i]; 16824 const attrName = attr.name; 16825 const attrValue = attr.value; 16826 if (!shouldKeepAttribute(settings, schema, ele.tagName.toLowerCase(), attrName, attrValue) && !isRequiredAttributeOfInternalElement(ele, attrName)) { 16827 ele.removeAttribute(attrName); 16828 } else if (isBooleanAttribute(attrName, schema)) { 16829 ele.setAttribute(attrName, attrName); 16830 } 16831 } 16832 }; 16833 const setupPurify = (settings, schema) => { 16834 const purify$1 = purify(); 16835 purify$1.addHook('uponSanitizeElement', (ele, evt) => { 16836 processNode(ele, settings, schema, evt); 16837 }); 16838 purify$1.addHook('uponSanitizeAttribute', (ele, evt) => { 16839 const tagName = ele.tagName.toLowerCase(); 16840 const {attrName, attrValue} = evt; 16841 evt.keepAttr = shouldKeepAttribute(settings, schema, tagName, attrName, attrValue); 16842 if (evt.keepAttr) { 16843 evt.allowedAttributes[attrName] = true; 16844 if (isBooleanAttribute(attrName, schema)) { 16845 evt.attrValue = attrName; 16846 } 16847 if (settings.allow_svg_data_urls && startsWith(attrValue, 'data:image/svg+xml')) { 16848 evt.forceKeepAttr = true; 16849 } 16850 } else if (isRequiredAttributeOfInternalElement(ele, attrName)) { 16851 evt.forceKeepAttr = true; 16852 } 16853 }); 16854 return purify$1; 16855 }; 16856 const getPurifyConfig = (settings, mimeType) => { 16857 const basePurifyConfig = { 16858 IN_PLACE: true, 16859 ALLOW_UNKNOWN_PROTOCOLS: true, 16860 ALLOWED_TAGS: [ 16861 '#comment', 16862 '#cdata-section', 16863 'body' 16864 ], 16865 ALLOWED_ATTR: [] 16866 }; 16867 const config = { ...basePurifyConfig }; 16868 config.PARSER_MEDIA_TYPE = mimeType; 16869 if (settings.allow_script_urls) { 16870 config.ALLOWED_URI_REGEXP = /.*/; 16871 } else if (settings.allow_html_data_urls) { 16872 config.ALLOWED_URI_REGEXP = /^(?!(\w+script|mhtml):)/i; 16873 } 16874 return config; 16875 }; 16876 const getSanitizer = (settings, schema) => { 16877 if (settings.sanitize) { 16878 const purify = setupPurify(settings, schema); 16879 return (body, mimeType) => { 16880 purify.sanitize(body, getPurifyConfig(settings, mimeType)); 16881 purify.removed = []; 16882 }; 16883 } else { 16884 return (body, _) => { 16885 const nodeIterator = document.createNodeIterator(body, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT); 16886 let node; 16887 while (node = nodeIterator.nextNode()) { 16888 processNode(node, settings, schema); 16889 if (isElement$6(node)) { 16890 filterAttributes(node, settings, schema); 16891 } 16892 } 16893 }; 16894 } 16895 }; 16896 16897 const makeMap = Tools.makeMap, extend$1 = Tools.extend; 16898 const transferChildren = (parent, nativeParent, specialElements) => { 16899 const parentName = parent.name; 16900 const isSpecial = parentName in specialElements && parentName !== 'title' && parentName !== 'textarea'; 16901 const childNodes = nativeParent.childNodes; 16902 for (let ni = 0, nl = childNodes.length; ni < nl; ni++) { 16903 const nativeChild = childNodes[ni]; 16904 const child = new AstNode(nativeChild.nodeName.toLowerCase(), nativeChild.nodeType); 16905 if (isElement$6(nativeChild)) { 16906 const attributes = nativeChild.attributes; 16907 for (let ai = 0, al = attributes.length; ai < al; ai++) { 16908 const attr = attributes[ai]; 16909 child.attr(attr.name, attr.value); 16910 } 16911 } else if (isText$a(nativeChild)) { 16912 child.value = nativeChild.data; 16913 if (isSpecial) { 16914 child.raw = true; 16915 } 16916 } else if (isComment(nativeChild) || isCData(nativeChild) || isPi(nativeChild)) { 16917 child.value = nativeChild.data; 16918 } 16919 transferChildren(child, nativeChild, specialElements); 16920 parent.append(child); 16921 } 16922 }; 16923 const walkTree = (root, preprocessors, postprocessors) => { 16924 const traverseOrder = []; 16925 for (let node = root, lastNode = node; node; lastNode = node, node = node.walk()) { 16926 const tempNode = node; 16927 each$e(preprocessors, preprocess => preprocess(tempNode)); 16928 if (isNullable(tempNode.parent) && tempNode !== root) { 16929 node = lastNode; 16930 } else { 16931 traverseOrder.push(tempNode); 16932 } 16933 } 16934 for (let i = traverseOrder.length - 1; i >= 0; i--) { 16935 const node = traverseOrder[i]; 16936 each$e(postprocessors, postprocess => postprocess(node)); 16937 } 16938 }; 16939 const whitespaceCleaner = (root, schema, settings, args) => { 16940 const validate = settings.validate; 16941 const nonEmptyElements = schema.getNonEmptyElements(); 16942 const whitespaceElements = schema.getWhitespaceElements(); 16943 const blockElements = extend$1(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements()); 16944 const textRootBlockElements = getTextRootBlockElements(schema); 16945 const allWhiteSpaceRegExp = /[ \t\r\n]+/g; 16946 const startWhiteSpaceRegExp = /^[ \t\r\n]+/; 16947 const endWhiteSpaceRegExp = /[ \t\r\n]+$/; 16948 const hasWhitespaceParent = node => { 16949 let tempNode = node.parent; 16950 while (isNonNullable(tempNode)) { 16951 if (tempNode.name in whitespaceElements) { 16952 return true; 16953 } else { 16954 tempNode = tempNode.parent; 16955 } 16956 } 16957 return false; 16958 }; 16959 const isTextRootBlockEmpty = node => { 16960 let tempNode = node; 16961 while (isNonNullable(tempNode)) { 16962 if (tempNode.name in textRootBlockElements) { 16963 return isEmpty(schema, nonEmptyElements, whitespaceElements, tempNode); 16964 } else { 16965 tempNode = tempNode.parent; 16966 } 16967 } 16968 return false; 16969 }; 16970 const isBlock = node => node.name in blockElements || isTransparentAstBlock(schema, node); 16971 const isAtEdgeOfBlock = (node, start) => { 16972 const neighbour = start ? node.prev : node.next; 16973 if (isNonNullable(neighbour) || isNullable(node.parent)) { 16974 return false; 16975 } 16976 return isBlock(node.parent) && (node.parent !== root || args.isRootContent === true); 16977 }; 16978 const preprocess = node => { 16979 var _a; 16980 if (node.type === 3) { 16981 if (!hasWhitespaceParent(node)) { 16982 let text = (_a = node.value) !== null && _a !== void 0 ? _a : ''; 16983 text = text.replace(allWhiteSpaceRegExp, ' '); 16984 if (isLineBreakNode(node.prev, isBlock) || isAtEdgeOfBlock(node, true)) { 16985 text = text.replace(startWhiteSpaceRegExp, ''); 16986 } 16987 if (text.length === 0) { 16988 node.remove(); 16989 } else { 16990 node.value = text; 16991 } 16992 } 16993 } 16994 }; 16995 const postprocess = node => { 16996 var _a; 16997 if (node.type === 1) { 16998 const elementRule = schema.getElementRule(node.name); 16999 if (validate && elementRule) { 17000 const isNodeEmpty = isEmpty(schema, nonEmptyElements, whitespaceElements, node); 17001 if (elementRule.paddInEmptyBlock && isNodeEmpty && isTextRootBlockEmpty(node)) { 17002 paddEmptyNode(
17002settings, args, isBlock, node); 17003 } else if (elementRule.removeEmpty && isNodeEmpty) { 17004 if (isBlock(node)) { 17005 node.remove(); 17006 } else { 17007 node.unwrap(); 17008 } 17009 } else if (elementRule.paddEmpty && (isNodeEmpty || isPaddedWithNbsp(node))) { 17010 paddEmptyNode(settings,
vendor: 65,570 bytes, lines 17010-18685
17010args, isBlock, node); 17011 } 17012 } 17013 } else if (node.type === 3) { 17014 if (!hasWhitespaceParent(node)) { 17015 let text = (_a = node.value) !== null && _a !== void 0 ? _a : ''; 17016 if (node.next && isBlock(node.next) || isAtEdgeOfBlock(node, false)) { 17017 text = text.replace(endWhiteSpaceRegExp, ''); 17018 } 17019 if (text.length === 0) { 17020 node.remove(); 17021 } else { 17022 node.value = text; 17023 } 17024 } 17025 } 17026 }; 17027 return [ 17028 preprocess, 17029 postprocess 17030 ]; 17031 }; 17032 const getRootBlockName = (settings, args) => { 17033 var _a; 17034 const name = (_a = args.forced_root_block) !== null && _a !== void 0 ? _a : settings.forced_root_block; 17035 if (name === false) { 17036 return ''; 17037 } else if (name === true) { 17038 return 'p'; 17039 } else { 17040 return name; 17041 } 17042 }; 17043 const DomParser = (settings = {}, schema = Schema()) => { 17044 const nodeFilterRegistry = create$8(); 17045 const attributeFilterRegistry = create$8(); 17046 const defaultedSettings = { 17047 validate: true, 17048 root_name: 'body', 17049 sanitize: true, 17050 ...settings 17051 }; 17052 const parser = new DOMParser(); 17053 const sanitize = getSanitizer(defaultedSettings, schema); 17054 const parseAndSanitizeWithContext = (html, rootName, format = 'html') => { 17055 const mimeType = format === 'xhtml' ? 'application/xhtml+xml' : 'text/html'; 17056 const isSpecialRoot = has$2(schema.getSpecialElements(), rootName.toLowerCase()); 17057 const content = isSpecialRoot ? `<${ rootName }>${ html }</${ rootName }>` : html; 17058 const wrappedHtml = format === 'xhtml' ? `<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>${ content }</body></html>` : `<body>${ content }</body>`; 17059 const body = parser.parseFromString(wrappedHtml, mimeType).body; 17060 sanitize(body, mimeType); 17061 return isSpecialRoot ? body.firstChild : body; 17062 }; 17063 const addNodeFilter = nodeFilterRegistry.addFilter; 17064 const getNodeFilters = nodeFilterRegistry.getFilters; 17065 const removeNodeFilter = nodeFilterRegistry.removeFilter; 17066 const addAttributeFilter = attributeFilterRegistry.addFilter; 17067 const getAttributeFilters = attributeFilterRegistry.getFilters; 17068 const removeAttributeFilter = attributeFilterRegistry.removeFilter; 17069 const findInvalidChildren = (node, invalidChildren) => { 17070 if (isInvalid(schema, node)) { 17071 invalidChildren.push(node); 17072 } 17073 }; 17074 const isWrappableNode = (blockElements, node) => { 17075 const isInternalElement = isString(node.attr(internalElementAttr)); 17076 const isInlineElement = node.type === 1 && (!has$2(blockElements, node.name) && !isTransparentAstBlock(schema, node)); 17077 return node.type === 3 || isInlineElement && !isInternalElement; 17078 }; 17079 const addRootBlocks = (rootNode, rootBlockName) => { 17080 const blockElements = extend$1(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements()); 17081 const startWhiteSpaceRegExp = /^[ \t\r\n]+/; 17082 const endWhiteSpaceRegExp = /[ \t\r\n]+$/; 17083 let node = rootNode.firstChild, rootBlockNode = null; 17084 const trim = rootBlock => { 17085 var _a, _b; 17086 if (rootBlock) { 17087 node = rootBlock.firstChild; 17088 if (node && node.type === 3) { 17089 node.value = (_a = node.value) === null || _a === void 0 ? void 0 : _a.replace(startWhiteSpaceRegExp, ''); 17090 } 17091 node = rootBlock.lastChild; 17092 if (node && node.type === 3) { 17093 node.value = (_b = node.value) === null || _b === void 0 ? void 0 : _b.replace(endWhiteSpaceRegExp, ''); 17094 } 17095 } 17096 }; 17097 if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) { 17098 return; 17099 } 17100 while (node) { 17101 const next = node.next; 17102 if (isWrappableNode(blockElements, node)) { 17103 if (!rootBlockNode) { 17104 rootBlockNode = new AstNode(rootBlockName, 1); 17105 rootBlockNode.attr(defaultedSettings.forced_root_block_attrs); 17106 rootNode.insert(rootBlockNode, node); 17107 rootBlockNode.append(node); 17108 } else { 17109 rootBlockNode.append(node); 17110 } 17111 } else { 17112 trim(rootBlockNode); 17113 rootBlockNode = null; 17114 } 17115 node = next; 17116 } 17117 trim(rootBlockNode); 17118 }; 17119 const parse = (html, args = {}) => { 17120 var _a; 17121 const validate = defaultedSettings.validate; 17122 const rootName = (_a = args.context) !== null && _a !== void 0 ? _a : defaultedSettings.root_name; 17123 const element = parseAndSanitizeWithContext(html, rootName, args.format); 17124 updateChildren(schema, element); 17125 const rootNode = new AstNode(rootName, 11); 17126 transferChildren(rootNode, element, schema.getSpecialElements()); 17127 element.innerHTML = ''; 17128 const [whitespacePre, whitespacePost] = whitespaceCleaner(rootNode, schema, defaultedSettings, args); 17129 const invalidChildren = []; 17130 const invalidFinder = validate ? node => findInvalidChildren(node, invalidChildren) : noop; 17131 const matches = { 17132 nodes: {}, 17133 attributes: {} 17134 }; 17135 const matchFinder = node => matchNode$1(getNodeFilters(), getAttributeFilters(), node, matches); 17136 walkTree(rootNode, [ 17137 whitespacePre, 17138 matchFinder 17139 ], [ 17140 whitespacePost, 17141 invalidFinder 17142 ]); 17143 invalidChildren.reverse(); 17144 if (validate && invalidChildren.length > 0) { 17145 if (args.context) { 17146 const { 17147 pass: topLevelChildren, 17148 fail: otherChildren 17149 } = partition$2(invalidChildren, child => child.parent === rootNode); 17150 cleanInvalidNodes(otherChildren, schema, rootNode, matchFinder); 17151 args.invalid = topLevelChildren.length > 0; 17152 } else { 17153 cleanInvalidNodes(invalidChildren, schema, rootNode, matchFinder); 17154 } 17155 } 17156 const rootBlockName = getRootBlockName(defaultedSettings, args); 17157 if (rootBlockName && (rootNode.name === 'body' || args.isRootContent)) { 17158 addRootBlocks(rootNode, rootBlockName); 17159 } 17160 if (!args.invalid) { 17161 runFilters(matches, args); 17162 } 17163 return rootNode; 17164 }; 17165 const exports = { 17166 schema, 17167 addAttributeFilter, 17168 getAttributeFilters, 17169 removeAttributeFilter, 17170 addNodeFilter, 17171 getNodeFilters, 17172 removeNodeFilter, 17173 parse 17174 }; 17175 register$4(exports, defaultedSettings); 17176 register$5(exports, defaultedSettings, schema); 17177 return exports; 17178 }; 17179 17180 const serializeContent = content => isTreeNode(content) ? HtmlSerializer({ validate: false }).serialize(content) : content; 17181 const withSerializedContent = (content, fireEvent, sanitize) => { 17182 const serializedContent = serializeContent(content); 17183 const eventArgs = fireEvent(serializedContent); 17184 if (eventArgs.isDefaultPrevented()) { 17185 return eventArgs; 17186 } else if (isTreeNode(content)) { 17187 if (eventArgs.content !== serializedContent) { 17188 const rootNode = DomParser({ 17189 validate: false, 17190 forced_root_block: false, 17191 sanitize 17192 }).parse(eventArgs.content, { context: content.name }); 17193 return { 17194 ...eventArgs, 17195 content: rootNode 17196 }; 17197 } else { 17198 return { 17199 ...eventArgs, 17200 content 17201 }; 17202 } 17203 } else { 17204 return eventArgs; 17205 } 17206 }; 17207 const preProcessGetContent = (editor, args) => { 17208 if (args.no_events) { 17209 return Result.value(args); 17210 } else { 17211 const eventArgs = fireBeforeGetContent(editor, args); 17212 if (eventArgs.isDefaultPrevented()) { 17213 return Result.error(fireGetContent(editor, { 17214 content: '', 17215 ...eventArgs 17216 }).content); 17217 } else { 17218 return Result.value(eventArgs); 17219 } 17220 } 17221 }; 17222 const postProcessGetContent = (editor, content, args) => { 17223 if (args.no_events) { 17224 return content; 17225 } else { 17226 const processedEventArgs = withSerializedContent(content, content => fireGetContent(editor, { 17227 ...args, 17228 content 17229 }), shouldSanitizeXss(editor)); 17230 return processedEventArgs.content; 17231 } 17232 }; 17233 const preProcessSetContent = (editor, args) => { 17234 if (args.no_events) { 17235 return Result.value(args); 17236 } else { 17237 const processedEventArgs = withSerializedContent(args.content, content => fireBeforeSetContent(editor, { 17238 ...args, 17239 content 17240 }), shouldSanitizeXss(editor)); 17241 if (processedEventArgs.isDefaultPrevented()) { 17242 fireSetContent(editor, processedEventArgs); 17243 return Result.error(undefined); 17244 } else { 17245 return Result.value(processedEventArgs); 17246 } 17247 } 17248 }; 17249 const postProcessSetContent = (editor, content, args) => { 17250 if (!args.no_events) { 17251 fireSetContent(editor, { 17252 ...args, 17253 content 17254 }); 17255 } 17256 }; 17257 17258 const tableModel = (element, width, rows) => ({ 17259 element, 17260 width, 17261 rows 17262 }); 17263 const tableRow = (element, cells) => ({ 17264 element, 17265 cells 17266 }); 17267 const cellPosition = (x, y) => ({ 17268 x, 17269 y 17270 }); 17271 const getSpan = (td, key) => { 17272 return getOpt(td, key).bind(toInt).getOr(1); 17273 }; 17274 const fillout = (table, x, y, tr, td) => { 17275 const rowspan = getSpan(td, 'rowspan'); 17276 const colspan = getSpan(td, 'colspan'); 17277 const rows = table.rows; 17278 for (let y2 = y; y2 < y + rowspan; y2++) { 17279 if (!rows[y2]) { 17280 rows[y2] = tableRow(deep$1(tr), []); 17281 } 17282 for (let x2 = x; x2 < x + colspan; x2++) { 17283 const cells = rows[y2].cells; 17284 cells[x2] = y2 === y && x2 === x ? td : shallow$1(td); 17285 } 17286 } 17287 }; 17288 const cellExists = (table, x, y) => { 17289 const rows = table.rows; 17290 const cells = rows[y] ? rows[y].cells : []; 17291 return !!cells[x]; 17292 }; 17293 const skipCellsX = (table, x, y) => { 17294 while (cellExists(table, x, y)) { 17295 x++; 17296 } 17297 return x; 17298 }; 17299 const getWidth = rows => { 17300 return foldl(rows, (acc, row) => { 17301 return row.cells.length > acc ? row.cells.length : acc; 17302 }, 0); 17303 }; 17304 const findElementPos = (table, element) => { 17305 const rows = table.rows; 17306 for (let y = 0; y < rows.length; y++) { 17307 const cells = rows[y].cells; 17308 for (let x = 0; x < cells.length; x++) { 17309 if (eq(cells[x], element)) { 17310 return Optional.some(cellPosition(x, y)); 17311 } 17312 } 17313 } 17314 return Optional.none(); 17315 }; 17316 const extractRows = (table, sx, sy, ex, ey) => { 17317 const newRows = []; 17318 const rows = table.rows; 17319 for (let y = sy; y <= ey; y++) { 17320 const cells = rows[y].cells; 17321 const slice = sx < ex ? cells.slice(sx, ex + 1) : cells.slice(ex, sx + 1); 17322 newRows.push(tableRow(rows[y].element, slice)); 17323 } 17324 return newRows; 17325 }; 17326 const subTable = (table, startPos, endPos) => { 17327 const sx = startPos.x, sy = startPos.y; 17328 const ex = endPos.x, ey = endPos.y; 17329 const newRows = sy < ey ? extractRows(table, sx, sy, ex, ey) : extractRows(table, sx, ey, ex, sy); 17330 return tableModel(table.element, getWidth(newRows), newRows); 17331 }; 17332 const createDomTable = (table, rows) => { 17333 const tableElement = shallow$1(table.element); 17334 const tableBody = SugarElement.fromTag('tbody'); 17335 append(tableBody, rows); 17336 append$1(tableElement, tableBody); 17337 return tableElement; 17338 }; 17339 const modelRowsToDomRows = table => { 17340 return map$3(table.rows, row => { 17341 const cells = map$3(row.cells, cell => { 17342 const td = deep$1(cell); 17343 remove$a(td, 'colspan'); 17344 remove$a(td, 'rowspan'); 17345 return td; 17346 }); 17347 const tr = shallow$1(row.element); 17348 append(tr, cells); 17349 return tr; 17350 }); 17351 }; 17352 const fromDom = tableElm => { 17353 const table = tableModel(shallow$1(tableElm), 0, []); 17354 each$e(descendants(tableElm, 'tr'), (tr, y) => { 17355 each$e(descendants(tr, 'td,th'), (td, x) => { 17356 fillout(table, skipCellsX(table, x, y), y, tr, td); 17357 }); 17358 }); 17359 return tableModel(table.element, getWidth(table.rows), table.rows); 17360 }; 17361 const toDom = table => { 17362 return createDomTable(table, modelRowsToDomRows(table)); 17363 }; 17364 const subsection = (table, startElement, endElement) => { 17365 return findElementPos(table, startElement).bind(startPos => { 17366 return findElementPos(table, endElement).map(endPos => { 17367 return subTable(table, startPos, endPos); 17368 }); 17369 }); 17370 }; 17371 17372 const findParentListContainer = parents => find$2(parents, elm => name(elm) === 'ul' || name(elm) === 'ol'); 17373 const getFullySelectedListWrappers = (parents, rng) => find$2(parents, elm => name(elm) === 'li' && hasAllContentsSelected(elm, rng)).fold(constant([]), _li => findParentListContainer(parents).map(listCont => { 17374 const listElm = SugarElement.fromTag(name(listCont)); 17375 const listStyles = filter$4(getAllRaw(listCont), (_style, name) => startsWith(name, 'list-style')); 17376 setAll(listElm, listStyles); 17377 return [ 17378 SugarElement.fromTag('li'), 17379 listElm 17380 ]; 17381 }).getOr([])); 17382 const wrap = (innerElm, elms) => { 17383 const wrapped = foldl(elms, (acc, elm) => { 17384 append$1(elm, acc); 17385 return elm; 17386 }, innerElm); 17387 return elms.length > 0 ? fromElements([wrapped]) : wrapped; 17388 }; 17389 const directListWrappers = commonAnchorContainer => { 17390 if (isListItem$1(commonAnchorContainer)) { 17391 return parent(commonAnchorContainer).filter(isList).fold(constant([]), listElm => [ 17392 commonAnchorContainer, 17393 listElm 17394 ]); 17395 } else { 17396 return isList(commonAnchorContainer) ? [commonAnchorContainer] : []; 17397 } 17398 }; 17399 const getWrapElements = (rootNode, rng) => { 17400 const commonAnchorContainer = SugarElement.fromDom(rng.commonAncestorContainer); 17401 const parents = parentsAndSelf(commonAnchorContainer, rootNode); 17402 const wrapElements = filter$5(parents, isWrapElement); 17403 const listWrappers = getFullySelectedListWrappers(parents, rng); 17404 const allWrappers = wrapElements.concat(listWrappers.length ? listWrappers : directListWrappers(commonAnchorContainer)); 17405 return map$3(allWrappers, shallow$1); 17406 }; 17407 const emptyFragment = () => fromElements([]); 17408 const getFragmentFromRange = (rootNode, rng) => wrap(SugarElement.fromDom(rng.cloneContents()), getWrapElements(rootNode, rng)); 17409 const getParentTable = (rootElm, cell) => ancestor$3(cell, 'table', curry(eq, rootElm)); 17410 const getTableFragment = (rootNode, selectedTableCells) => getParentTable(rootNode, selectedTableCells[0]).bind(tableElm => { 17411 const firstCell = selectedTableCells[0]; 17412 const lastCell = selectedTableCells[selectedTableCells.length - 1]; 17413 const fullTableModel = fromDom(tableElm); 17414 return subsection(fullTableModel, firstCell, lastCell).map(sectionedTableModel => fromElements([toDom(sectionedTableModel)])); 17415 }).getOrThunk(emptyFragment); 17416 const getSelectionFragment = (rootNode, ranges) => ranges.length > 0 && ranges[0].collapsed ? emptyFragment() : getFragmentFromRange(rootNode, ranges[0]); 17417 const read$3 = (rootNode, ranges) => { 17418 const selectedCells = getCellsFromElementOrRanges(ranges, rootNode); 17419 return selectedCells.length > 0 ? getTableFragment(rootNode, selectedCells) : getSelectionFragment(rootNode, ranges); 17420 }; 17421 17422 const isCollapsibleWhitespace = (text, index) => index >= 0 && index < text.length && isWhiteSpace(text.charAt(index)); 17423 const getInnerText = bin => { 17424 return trim$1(bin.innerText); 17425 }; 17426 const getContextNodeName = parentBlockOpt => parentBlockOpt.map(block => block.nodeName).getOr('div').toLowerCase(); 17427 const getTextContent = editor => Optional.from(editor.selection.getRng()).map(rng => { 17428 var _a; 17429 const parentBlockOpt = Optional.from(editor.dom.getParent(rng.commonAncestorContainer, editor.dom.isBlock)); 17430 const body = editor.getBody(); 17431 const contextNodeName = getContextNodeName(parentBlockOpt); 17432 const rangeContentClone = SugarElement.fromDom(rng.cloneContents()); 17433 cleanupBogusElements(rangeContentClone); 17434 cleanupInputNames(rangeContentClone); 17435 const bin = editor.dom.add(body, contextNodeName, { 17436 'data-mce-bogus': 'all', 17437 'style': 'overflow: hidden; opacity: 0;' 17438 }, rangeContentClone.dom); 17439 const text = getInnerText(bin); 17440 const nonRenderedText = trim$1((_a = bin.textContent) !== null && _a !== void 0 ? _a : ''); 17441 editor.dom.remove(bin); 17442 if (isCollapsibleWhitespace(nonRenderedText, 0) || isCollapsibleWhitespace(nonRenderedText, nonRenderedText.length - 1)) { 17443 const parentBlock = parentBlockOpt.getOr(body); 17444 const parentBlockText = getInnerText(parentBlock); 17445 const textIndex = parentBlockText.indexOf(text); 17446 if (textIndex === -1) { 17447 return text; 17448 } else { 17449 const hasProceedingSpace = isCollapsibleWhitespace(parentBlockText, textIndex - 1); 17450 const hasTrailingSpace = isCollapsibleWhitespace(parentBlockText, textIndex + text.length); 17451 return (hasProceedingSpace ? ' ' : '') + text + (hasTrailingSpace ? ' ' : ''); 17452 } 17453 } else { 17454 return text; 17455 } 17456 }).getOr(''); 17457 const getSerializedContent = (editor, args) => { 17458 const rng = editor.selection.getRng(), tmpElm = editor.dom.create('body'); 17459 const sel = editor.selection.getSel(); 17460 const ranges = processRanges(editor, getRanges$1(sel)); 17461 const fragment = args.contextual ? read$3(SugarElement.fromDom(editor.getBody()), ranges).dom : rng.cloneContents(); 17462 if (fragment) { 17463 tmpElm.appendChild(fragment); 17464 } 17465 return editor.selection.serializer.serialize(tmpElm, args); 17466 }; 17467 const extractSelectedContent = (editor, args) => { 17468 if (args.format === 'text') { 17469 return getTextContent(editor); 17470 } else { 17471 const content = getSerializedContent(editor, args); 17472 if (args.format === 'tree') { 17473 return content; 17474 } else { 17475 return editor.selection.isCollapsed() ? '' : content; 17476 } 17477 } 17478 }; 17479 const setupArgs$3 = (args, format) => ({ 17480 ...args, 17481 format, 17482 get: true, 17483 selection: true, 17484 getInner: true 17485 }); 17486 const getSelectedContentInternal = (editor, format, args = {}) => { 17487 const defaultedArgs = setupArgs$3(args, format); 17488 return preProcessGetContent(editor, defaultedArgs).fold(identity, updatedArgs => { 17489 const content = extractSelectedContent(editor, updatedArgs); 17490 return postProcessGetContent(editor, content, updatedArgs); 17491 }); 17492 }; 17493 17494 const KEEP = 0, INSERT = 1, DELETE = 2; 17495 const diff = (left, right) => { 17496 const size = left.length + right.length + 2; 17497 const vDown = new Array(size); 17498 const vUp = new Array(size); 17499 const snake = (start, end, diag) => { 17500 return { 17501 start, 17502 end, 17503 diag 17504 }; 17505 }; 17506 const buildScript = (start1, end1, start2, end2, script) => { 17507 const middle = getMiddleSnake(start1, end1, start2, end2); 17508 if (middle === null || middle.start === end1 && middle.diag === end1 - end2 || middle.end === start1 && middle.diag === start1 - start2) { 17509 let i = start1; 17510 let j = start2; 17511 while (i < end1 || j < end2) { 17512 if (i < end1 && j < end2 && left[i] === right[j]) { 17513 script.push([ 17514 KEEP, 17515 left[i] 17516 ]); 17517 ++i; 17518 ++j; 17519 } else { 17520 if (end1 - start1 > end2 - start2) { 17521 script.push([ 17522 DELETE, 17523 left[i] 17524 ]); 17525 ++i; 17526 } else { 17527 script.push([ 17528 INSERT, 17529 right[j] 17530 ]); 17531 ++j; 17532 } 17533 } 17534 } 17535 } else { 17536 buildScript(start1, middle.start, start2, middle.start - middle.diag, script); 17537 for (let i2 = middle.start; i2 < middle.end; ++i2) { 17538 script.push([ 17539 KEEP, 17540 left[i2] 17541 ]); 17542 } 17543 buildScript(middle.end, end1, middle.end - middle.diag, end2, script); 17544 } 17545 }; 17546 const buildSnake = (start, diag, end1, end2) => { 17547 let end = start; 17548 while (end - diag < end2 && end < end1 && left[end] === right[end - diag]) { 17549 ++end; 17550 } 17551 return snake(start, end, diag); 17552 }; 17553 const getMiddleSnake = (start1, end1, start2, end2) => { 17554 const m = end1 - start1; 17555 const n = end2 - start2; 17556 if (m === 0 || n === 0) { 17557 return null; 17558 } 17559 const delta = m - n; 17560 const sum = n + m; 17561 const offset = (sum % 2 === 0 ? sum : sum + 1) / 2; 17562 vDown[1 + offset] = start1; 17563 vUp[1 + offset] = end1 + 1; 17564 let d, k, i, x, y; 17565 for (d = 0; d <= offset; ++d) { 17566 for (k = -d; k <= d; k += 2) { 17567 i = k + offset; 17568 if (k === -d || k !== d && vDown[i - 1] < vDown[i + 1]) { 17569 vDown[i] = vDown[i + 1]; 17570 } else { 17571 vDown[i] = vDown[i - 1] + 1; 17572 } 17573 x = vDown[i]; 17574 y = x - start1 + start2 - k; 17575 while (x < end1 && y < end2 && left[x] === right[y]) { 17576 vDown[i] = ++x; 17577 ++y; 17578 } 17579 if (delta % 2 !== 0 && delta - d <= k && k <= delta + d) { 17580 if (vUp[i - delta] <= vDown[i]) { 17581 return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2); 17582 } 17583 } 17584 } 17585 for (k = delta - d; k <= delta + d; k += 2) { 17586 i = k + offset - delta; 17587 if (k === delta - d || k !== delta + d && vUp[i + 1] <= vUp[i - 1]) { 17588 vUp[i] = vUp[i + 1] - 1; 17589 } else { 17590 vUp[i] = vUp[i - 1]; 17591 } 17592 x = vUp[i] - 1; 17593 y = x - start1 + start2 - k; 17594 while (x >= start1 && y >= start2 && left[x] === right[y]) { 17595 vUp[i] = x--; 17596 y--; 17597 } 17598 if (delta % 2 === 0 && -d <= k && k <= d) { 17599 if (vUp[i] <= vDown[i + delta]) { 17600 return buildSnake(vUp[i], k + start1 - start2, end1, end2); 17601 } 17602 } 17603 } 17604 } 17605 return null; 17606 }; 17607 const script = []; 17608 buildScript(0, left.length, 0, right.length, script); 17609 return script; 17610 }; 17611 17612 const getOuterHtml = elm => { 17613 if (isElement$6(elm)) { 17614 return elm.outerHTML; 17615 } else if (isText$a(elm)) { 17616 return Entities.encodeRaw(elm.data, false); 17617 } else if (isComment(elm)) { 17618 return '<!--' + elm.data + '-->'; 17619 } 17620 return ''; 17621 }; 17622 const createFragment = html => { 17623 let node; 17624 const container = document.createElement('div'); 17625 const frag = document.createDocumentFragment(); 17626 if (html) { 17627 container.innerHTML = html; 17628 } 17629 while (node = container.firstChild) { 17630 frag.appendChild(node); 17631 } 17632 return frag; 17633 }; 17634 const insertAt = (elm, html, index) => { 17635 const fragment = createFragment(html); 17636 if (elm.hasChildNodes() && index < elm.childNodes.length) { 17637 const target = elm.childNodes[index]; 17638 elm.insertBefore(fragment, target); 17639 } else { 17640 elm.appendChild(fragment); 17641 } 17642 }; 17643 const removeAt = (elm, index) => { 17644 if (elm.hasChildNodes() && index < elm.childNodes.length) { 17645 const target = elm.childNodes[index]; 17646 elm.removeChild(target); 17647 } 17648 }; 17649 const applyDiff = (diff, elm) => { 17650 let index = 0; 17651 each$e(diff, action => { 17652 if (action[0] === KEEP) { 17653 index++; 17654 } else if (action[0] === INSERT) { 17655 insertAt(elm, action[1], index); 17656 index++; 17657 } else if (action[0] === DELETE) { 17658 removeAt(elm, index); 17659 } 17660 }); 17661 }; 17662 const read$2 = elm => { 17663 return filter$5(map$3(from(elm.childNodes), getOuterHtml), item => { 17664 return item.length > 0; 17665 }); 17666 }; 17667 const write = (fragments, elm) => { 17668 const currentFragments = map$3(from(elm.childNodes), getOuterHtml); 17669 applyDiff(diff(currentFragments, fragments), elm); 17670 return elm; 17671 }; 17672 17673 const lazyTempDocument = cached(() => document.implementation.createHTMLDocument('undo')); 17674 const hasIframes = html => { 17675 return html.indexOf('</iframe>') !== -1; 17676 }; 17677 const createFragmentedLevel = fragments => { 17678 return { 17679 type: 'fragmented', 17680 fragments, 17681 content: '', 17682 bookmark: null, 17683 beforeBookmark: null 17684 }; 17685 }; 17686 const createCompleteLevel = content => { 17687 return { 17688 type: 'complete', 17689 fragments: null, 17690 content, 17691 bookmark: null, 17692 beforeBookmark: null 17693 }; 17694 }; 17695 const createFromEditor = editor => { 17696 const fragments = read$2(editor.getBody()); 17697 const trimmedFragments = bind$3(fragments, html => { 17698 const trimmed = trimInternal(editor.serializer, html); 17699 return trimmed.length > 0 ? [trimmed] : []; 17700 }); 17701 const content = trimmedFragments.join(''); 17702 return hasIframes(content) ? createFragmentedLevel(trimmedFragments) : createCompleteLevel(content); 17703 }; 17704 const applyToEditor = (editor, level, before) => { 17705 const bookmark = before ? level.beforeBookmark : level.bookmark; 17706 if (level.type === 'fragmented') { 17707 write(level.fragments, editor.getBody()); 17708 } else { 17709 editor.setContent(level.content, { 17710 format: 'raw', 17711 no_selection: isNonNullable(bookmark) && isPathBookmark(bookmark) ? !bookmark.isFakeCaret : true 17712 }); 17713 } 17714 if (bookmark) { 17715 editor.selection.moveToBookmark(bookmark); 17716 editor.selection.scrollIntoView(); 17717 } 17718 }; 17719 const getLevelContent = level => { 17720 return level.type === 'fragmented' ? level.fragments.join('') : level.content; 17721 }; 17722 const getCleanLevelContent = level => { 17723 const elm = SugarElement.fromTag('body', lazyTempDocument()); 17724 set$1(elm, getLevelContent(level)); 17725 each$e(descendants(elm, '*[data-mce-bogus]'), unwrap); 17726 return get$6(elm); 17727 }; 17728 const hasEqualContent = (level1, level2) => getLevelContent(level1) === getLevelContent(level2); 17729 const hasEqualCleanedContent = (level1, level2) => getCleanLevelContent(level1) === getCleanLevelContent(level2); 17730 const isEq$1 = (level1, level2) => { 17731 if (!level1 || !level2) { 17732 return false; 17733 } else if (hasEqualContent(level1, level2)) { 17734 return true; 17735 } else { 17736 return hasEqualCleanedContent(level1, level2); 17737 } 17738 }; 17739 17740 const isUnlocked = locks => locks.get() === 0; 17741 17742 const setTyping = (undoManager, typing, locks) => { 17743 if (isUnlocked(locks)) { 17744 undoManager.typing = typing; 17745 } 17746 }; 17747 const endTyping = (undoManager, locks) => { 17748 if (undoManager.typing) { 17749 setTyping(undoManager, false, locks); 17750 undoManager.add(); 17751 } 17752 }; 17753 const endTypingLevelIgnoreLocks = undoManager => { 17754 if (undoManager.typing) { 17755 undoManager.typing = false; 17756 undoManager.add(); 17757 } 17758 }; 17759 17760 const beforeChange$1 = (editor, locks, beforeBookmark) => { 17761 if (isUnlocked(locks)) { 17762 beforeBookmark.set(getUndoBookmark(editor.selection)); 17763 } 17764 }; 17765 const addUndoLevel$1 = (editor, undoManager, index, locks, beforeBookmark, level, event) => { 17766 const currentLevel = createFromEditor(editor); 17767 const newLevel = Tools.extend(level || {}, currentLevel); 17768 if (!isUnlocked(locks) || editor.removed) { 17769 return null; 17770 } 17771 const lastLevel = undoManager.data[index.get()]; 17772 if (editor.dispatch('BeforeAddUndo', { 17773 level: newLevel, 17774 lastLevel, 17775 originalEvent: event 17776 }).isDefaultPrevented()) { 17777 return null; 17778 } 17779 if (lastLevel && isEq$1(lastLevel, newLevel)) { 17780 return null; 17781 } 17782 if (undoManager.data[index.get()]) { 17783 beforeBookmark.get().each(bm => { 17784 undoManager.data[index.get()].beforeBookmark = bm; 17785 }); 17786 } 17787 const customUndoRedoLevels = getCustomUndoRedoLevels(editor); 17788 if (customUndoRedoLevels) { 17789 if (undoManager.data.length > customUndoRedoLevels) { 17790 for (let i = 0; i < undoManager.data.length - 1; i++) { 17791 undoManager.data[i] = undoManager.data[i + 1]; 17792 } 17793 undoManager.data.length--; 17794 index.set(undoManager.data.length); 17795 } 17796 } 17797 newLevel.bookmark = getUndoBookmark(editor.selection); 17798 if (index.get() < undoManager.data.length - 1) { 17799 undoManager.data.length = index.get() + 1; 17800 } 17801 undoManager.data.push(newLevel); 17802 index.set(undoManager.data.length - 1); 17803 const args = { 17804 level: newLevel, 17805 lastLevel, 17806 originalEvent: event 17807 }; 17808 if (index.get() > 0) { 17809 editor.setDirty(true); 17810 editor.dispatch('AddUndo', args); 17811 editor.dispatch('change', args); 17812 } else { 17813 editor.dispatch('AddUndo', args); 17814 } 17815 return newLevel; 17816 }; 17817 const clear$1 = (editor, undoManager, index) => { 17818 undoManager.data = []; 17819 index.set(0); 17820 undoManager.typing = false; 17821 editor.dispatch('ClearUndos'); 17822 }; 17823 const extra$1 = (editor, undoManager, index, callback1, callback2) => { 17824 if (undoManager.transact(callback1)) { 17825 const bookmark = undoManager.data[index.get()].bookmark; 17826 const lastLevel = undoManager.data[index.get() - 1]; 17827 applyToEditor(editor, lastLevel, true); 17828 if (undoManager.transact(callback2)) { 17829 undoManager.data[index.get() - 1].beforeBookmark = bookmark; 17830 } 17831 } 17832 }; 17833 const redo$1 = (editor, index, data) => { 17834 let level; 17835 if (index.get() < data.length - 1) { 17836 index.set(index.get() + 1); 17837 level = data[index.get()]; 17838 applyToEditor(editor, level, false); 17839 editor.setDirty(true); 17840 editor.dispatch('Redo', { level }); 17841 } 17842 return level; 17843 }; 17844 const undo$1 = (editor, undoManager, locks, index) => { 17845 let level; 17846 if (undoManager.typing) { 17847 undoManager.add(); 17848 undoManager.typing = false; 17849 setTyping(undoManager, false, locks); 17850 } 17851 if (index.get() > 0) { 17852 index.set(index.get() - 1); 17853 level = undoManager.data[index.get()]; 17854 applyToEditor(editor, level, true); 17855 editor.setDirty(true); 17856 editor.dispatch('Undo', { level }); 17857 } 17858 return level; 17859 }; 17860 const reset$1 = undoManager => { 17861 undoManager.clear(); 17862 undoManager.add(); 17863 }; 17864 const hasUndo$1 = (editor, undoManager, index) => index.get() > 0 || undoManager.typing && undoManager.data[0] && !isEq$1(createFromEditor(editor), undoManager.data[0]); 17865 const hasRedo$1 = (undoManager, index) => index.get() < undoManager.data.length - 1 && !undoManager.typing; 17866 const transact$1 = (undoManager, locks, callback) => { 17867 endTyping(undoManager, locks); 17868 undoManager.beforeChange(); 17869 undoManager.ignore(callback); 17870 return undoManager.add(); 17871 }; 17872 const ignore$1 = (locks, callback) => { 17873 try { 17874 locks.set(locks.get() + 1); 17875 callback(); 17876 } finally { 17877 locks.set(locks.get() - 1); 17878 } 17879 }; 17880 17881 const addVisualInternal = (editor, elm) => { 17882 const dom = editor.dom; 17883 const scope = isNonNullable(elm) ? elm : editor.getBody(); 17884 each$e(dom.select('table,a', scope), matchedElm => { 17885 switch (matchedElm.nodeName) { 17886 case 'TABLE': 17887 const cls = getVisualAidsTableClass(editor); 17888 const value = dom.getAttrib(matchedElm, 'border'); 17889 if ((!value || value === '0') && editor.hasVisual) { 17890 dom.addClass(matchedElm, cls); 17891 } else { 17892 dom.removeClass(matchedElm, cls); 17893 } 17894 break; 17895 case 'A': 17896 if (!dom.getAttrib(matchedElm, 'href')) { 17897 const value = dom.getAttrib(matchedElm, 'name') || matchedElm.id; 17898 const cls = getVisualAidsAnchorClass(editor); 17899 if (value && editor.hasVisual) { 17900 dom.addClass(matchedElm, cls); 17901 } else { 17902 dom.removeClass(matchedElm, cls); 17903 } 17904 } 17905 break; 17906 } 17907 }); 17908 editor.dispatch('VisualAid', { 17909 element: elm, 17910 hasVisual: editor.hasVisual 17911 }); 17912 }; 17913 17914 const makePlainAdaptor = editor => ({ 17915 init: { bindEvents: noop }, 17916 undoManager: { 17917 beforeChange: (locks, beforeBookmark) => beforeChange$1(editor, locks, beforeBookmark), 17918 add: (undoManager, index, locks, beforeBookmark, level, event) => addUndoLevel$1(editor, undoManager, index, locks, beforeBookmark, level, event), 17919 undo: (undoManager, locks, index) => undo$1(editor, undoManager, locks, index), 17920 redo: (index, data) => redo$1(editor, index, data), 17921 clear: (undoManager, index) => clear$1(editor, undoManager, index), 17922 reset: undoManager => reset$1(undoManager), 17923 hasUndo: (undoManager, index) => hasUndo$1(editor, undoManager, index), 17924 hasRedo: (undoManager, index) => hasRedo$1(undoManager, index), 17925 transact: (undoManager, locks, callback) => transact$1(undoManager, locks, callback), 17926 ignore: (locks, callback) => ignore$1(locks, callback), 17927 extra: (undoManager, index, callback1, callback2) => extra$1(editor, undoManager, index, callback1, callback2) 17928 }, 17929 formatter: { 17930 match: (name, vars, node, similar) => match$2(editor, name, vars, node, similar), 17931 matchAll: (names, vars) => matchAll(editor, names, vars), 17932 matchNode: (node, name, vars, similar) => matchNode(editor, node, name, vars, similar), 17933 canApply: name => canApply(editor, name), 17934 closest: names => closest(editor, names), 17935 apply: (name, vars, node) => applyFormat$1(editor, name, vars, node), 17936 remove: (name, vars, node, similar) => removeFormat$1(editor, name, vars, node, similar), 17937 toggle: (name, vars, node) => toggle(editor, name, vars, node), 17938 formatChanged: (registeredFormatListeners, formats, callback, similar, vars) => formatChangedInternal(editor, registeredFormatListeners, formats, callback, similar, vars) 17939 }, 17940 editor: { 17941 getContent: args => getContentInternal(editor, args), 17942 setContent: (content, args) => setContentInternal(editor, content, args), 17943 insertContent: (value, details) => insertHtmlAtCaret(editor, value, details), 17944 addVisual: elm => addVisualInternal(editor, elm) 17945 }, 17946 selection: { getContent: (format, args) => getSelectedContentInternal(editor, format, args) }, 17947 autocompleter: { 17948 addDecoration: range => create$9(editor, range), 17949 removeDecoration: () => remove$2(editor, SugarElement.fromDom(editor.getBody())) 17950 }, 17951 raw: { getModel: () => Optional.none() } 17952 }); 17953 const makeRtcAdaptor = rtcEditor => { 17954 const defaultVars = vars => isObject(vars) ? vars : {}; 17955 const {init, undoManager, formatter, editor, selection, autocompleter, raw} = rtcEditor; 17956 return { 17957 init: { bindEvents: init.bindEvents }, 17958 undoManager: { 17959 beforeChange: undoManager.beforeChange, 17960 add: undoManager.add, 17961 undo: undoManager.undo, 17962 redo: undoManager.redo, 17963 clear: undoManager.clear, 17964 reset: undoManager.reset, 17965 hasUndo: undoManager.hasUndo, 17966 hasRedo: undoManager.hasRedo, 17967 transact: (_undoManager, _locks, fn) => undoManager.transact(fn), 17968 ignore: (_locks, callback) => undoManager.ignore(callback), 17969 extra: (_undoManager, _index, callback1, callback2) => undoManager.extra(callback1, callback2) 17970 }, 17971 formatter: { 17972 match: (name, vars, _node, similar) => formatter.match(name, defaultVars(vars), similar), 17973 matchAll: formatter.matchAll, 17974 matchNode: formatter.matchNode, 17975 canApply: name => formatter.canApply(name), 17976 closest: names => formatter.closest(names), 17977 apply: (name, vars, _node) => formatter.apply(name, defaultVars(vars)), 17978 remove: (name, vars, _node, _similar) => formatter.remove(name, defaultVars(vars)), 17979 toggle: (name, vars, _node) => formatter.toggle(name, defaultVars(vars)), 17980 formatChanged: (_rfl, formats, callback, similar, vars) => formatter.formatChanged(formats, callback, similar, vars) 17981 }, 17982 editor: { 17983 getContent: args => editor.getContent(args), 17984 setContent: (content, args) => { 17985 return { 17986 content: editor.setContent(content, args), 17987 html: '' 17988 }; 17989 }, 17990 insertContent: (content, _details) => { 17991 editor.insertContent(content); 17992 return ''; 17993 }, 17994 addVisual: editor.addVisual 17995 }, 17996 selection: { getContent: (_format, args) => selection.getContent(args) }, 17997 autocompleter: { 17998 addDecoration: autocompleter.addDecoration, 17999 removeDecoration: autocompleter.removeDecoration 18000 }, 18001 raw: { getModel: () => Optional.some(raw.getRawModel()) } 18002 }; 18003 }; 18004 const makeNoopAdaptor = () => { 18005 const nul = constant(null); 18006 const empty = constant(''); 18007 return { 18008 init: { bindEvents: noop }, 18009 undoManager: { 18010 beforeChange: noop, 18011 add: nul, 18012 undo: nul, 18013 redo: nul, 18014 clear: noop, 18015 reset: noop, 18016 hasUndo: never, 18017 hasRedo: never, 18018 transact: nul, 18019 ignore: noop, 18020 extra: noop 18021 }, 18022 formatter: { 18023 match: never, 18024 matchAll: constant([]), 18025 matchNode: constant(undefined), 18026 canApply: never, 18027 closest: empty, 18028 apply: noop, 18029 remove: noop, 18030 toggle: noop, 18031 formatChanged: constant({ unbind: noop }) 18032 }, 18033 editor: { 18034 getContent: empty, 18035 setContent: constant({ 18036 content: '', 18037 html: '' 18038 }), 18039 insertContent: constant(''), 18040 addVisual: noop 18041 }, 18042 selection: { getContent: empty }, 18043 autocompleter: { 18044 addDecoration: noop, 18045 removeDecoration: noop 18046 }, 18047 raw: { getModel: constant(Optional.none()) } 18048 }; 18049 }; 18050 const isRtc = editor => has$2(editor.plugins, 'rtc'); 18051 const getRtcSetup = editor => get$a(editor.plugins, 'rtc').bind(rtcPlugin => Optional.from(rtcPlugin.setup)); 18052 const setup$s = editor => { 18053 const editorCast = editor; 18054 return getRtcSetup(editor).fold(() => { 18055 editorCast.rtcInstance = makePlainAdaptor(editor); 18056 return Optional.none(); 18057 }, setup => { 18058 editorCast.rtcInstance = makeNoopAdaptor(); 18059 return Optional.some(() => setup().then(rtcEditor => { 18060 editorCast.rtcInstance = makeRtcAdaptor(rtcEditor); 18061 return rtcEditor.rtc.isRemote; 18062 })); 18063 }); 18064 }; 18065 const getRtcInstanceWithFallback = editor => editor.rtcInstance ? editor.rtcInstance : makePlainAdaptor(editor); 18066 const getRtcInstanceWithError = editor => { 18067 const rtcInstance = editor.rtcInstance; 18068 if (!rtcInstance) { 18069 throw new Error('Failed to get RTC instance not yet initialized.'); 18070 } else { 18071 return rtcInstance; 18072 } 18073 }; 18074 const beforeChange = (editor, locks, beforeBookmark) => { 18075 getRtcInstanceWithError(editor).undoManager.beforeChange(locks, beforeBookmark); 18076 }; 18077 const addUndoLevel = (editor, undoManager, index, locks, beforeBookmark, level, event) => getRtcInstanceWithError(editor).undoManager.add(undoManager, index, locks, beforeBookmark, level, event); 18078 const undo = (editor, undoManager, locks, index) => getRtcInstanceWithError(editor).undoManager.undo(undoManager, locks, index); 18079 const redo = (editor, index, data) => getRtcInstanceWithError(editor).undoManager.redo(index, data); 18080 const clear = (editor, undoManager, index) => { 18081 getRtcInstanceWithError(editor).undoManager.clear(undoManager, index); 18082 }; 18083 const reset = (editor, undoManager) => { 18084 getRtcInstanceWithError(editor).undoManager.reset(undoManager); 18085 }; 18086 const hasUndo = (editor, undoManager, index) => getRtcInstanceWithError(editor).undoManager.hasUndo(undoManager, index); 18087 const hasRedo = (editor, undoManager, index) => getRtcInstanceWithError(editor).undoManager.hasRedo(undoManager, index); 18088 const transact = (editor, undoManager, locks, callback) => getRtcInstanceWithError(editor).undoManager.transact(undoManager, locks, callback); 18089 const ignore = (editor, locks, callback) => { 18090 getRtcInstanceWithError(editor).undoManager.ignore(locks, callback); 18091 }; 18092 const extra = (editor, undoManager, index, callback1, callback2) => { 18093 getRtcInstanceWithError(editor).undoManager.extra(undoManager, index, callback1, callback2); 18094 }; 18095 const matchFormat = (editor, name, vars, node, similar) => getRtcInstanceWithError(editor).formatter.match(name, vars, node, similar); 18096 const matchAllFormats = (editor, names, vars) => getRtcInstanceWithError(editor).formatter.matchAll(names, vars); 18097 const matchNodeFormat = (editor, node, name, vars, similar) => getRtcInstanceWithError(editor).formatter.matchNode(node, name, vars, similar); 18098 const canApplyFormat = (editor, name) => getRtcInstanceWithError(editor).formatter.canApply(name); 18099 const closestFormat = (editor, names) => getRtcInstanceWithError(editor).formatter.closest(names); 18100 const applyFormat = (editor, name, vars, node) => { 18101 getRtcInstanceWithError(editor).formatter.apply(name, vars, node); 18102 }; 18103 const removeFormat = (editor, name, vars, node, similar) => { 18104 getRtcInstanceWithError(editor).formatter.remove(name, vars, node, similar); 18105 }; 18106 const toggleFormat = (editor, name, vars, node) => { 18107 getRtcInstanceWithError(editor).formatter.toggle(name, vars, node); 18108 }; 18109 const formatChanged = (editor, registeredFormatListeners, formats, callback, similar, vars) => getRtcInstanceWithError(editor).formatter.formatChanged(registeredFormatListeners, formats, callback, similar, vars); 18110 const getContent$2 = (editor, args) => getRtcInstanceWithFallback(editor).editor.getContent(args); 18111 const setContent$2 = (editor, content, args) => getRtcInstanceWithFallback(editor).editor.setContent(content, args); 18112 const insertContent$1 = (editor, value, details) => getRtcInstanceWithFallback(editor).editor.insertContent(value, details); 18113 const getSelectedContent = (editor, format, args) => getRtcInstanceWithError(editor).selection.getContent(format, args); 18114 const addVisual$1 = (editor, elm) => getRtcInstanceWithError(editor).editor.addVisual(elm); 18115 const bindEvents = editor => getRtcInstanceWithError(editor).init.bindEvents(); 18116 const addAutocompleterDecoration = (editor, range) => getRtcInstanceWithError(editor).autocompleter.addDecoration(range); 18117 const removeAutocompleterDecoration = editor => getRtcInstanceWithError(editor).autocompleter.removeDecoration(); 18118 18119 const getContent$1 = (editor, args = {}) => { 18120 const format = args.format ? args.format : 'html'; 18121 return getSelectedContent(editor, format, args); 18122 }; 18123 18124 const removeEmpty = text => { 18125 if (text.dom.length === 0) { 18126 remove$5(text); 18127 return Optional.none(); 18128 } else { 18129 return Optional.some(text); 18130 } 18131 }; 18132 const walkPastBookmark = (node, start) => node.filter(elm => BookmarkManager.isBookmarkNode(elm.dom)).bind(start ? nextSibling : prevSibling); 18133 const merge$1 = (outer, inner, rng, start) => { 18134 const outerElm = outer.dom; 18135 const innerElm = inner.dom; 18136 const oldLength = start ? outerElm.length : innerElm.length; 18137 if (start) { 18138 mergeTextNodes(outerElm, innerElm, false, !start); 18139 rng.setStart(innerElm, oldLength); 18140 } else { 18141 mergeTextNodes(innerElm, outerElm, false, !start); 18142 rng.setEnd(innerElm, oldLength); 18143 } 18144 }; 18145 const normalizeTextIfRequired = (inner, start) => { 18146 parent(inner).each(root => { 18147 const text = inner.dom; 18148 if (start && needsToBeNbspLeft(root, CaretPosition(text, 0))) { 18149 normalizeWhitespaceAfter(text, 0); 18150 } else if (!start && needsToBeNbspRight(root, CaretPosition(text, text.length))) { 18151 normalizeWhitespaceBefore(text, text.length); 18152 } 18153 }); 18154 }; 18155 const mergeAndNormalizeText = (outerNode, innerNode, rng, start) => { 18156 outerNode.bind(outer => { 18157 const normalizer = start ? normalizeWhitespaceBefore : normalizeWhitespaceAfter; 18158 normalizer(outer.dom, start ? outer.dom.length : 0); 18159 return innerNode.filter(isText$b).map(inner => merge$1(outer, inner, rng, start)); 18160 }).orThunk(() => { 18161 const innerTextNode = walkPastBookmark(innerNode, start).or(innerNode).filter(isText$b); 18162 return innerTextNode.map(inner => normalizeTextIfRequired(inner, start)); 18163 }); 18164 }; 18165 const rngSetContent = (rng, fragment) => { 18166 const firstChild = Optional.from(fragment.firstChild).map(SugarElement.fromDom); 18167 const lastChild = Optional.from(fragment.lastChild).map(SugarElement.fromDom); 18168 rng.deleteContents(); 18169 rng.insertNode(fragment); 18170 const prevText = firstChild.bind(prevSibling).filter(isText$b).bind(removeEmpty); 18171 const nextText = lastChild.bind(nextSibling).filter(isText$b).bind(removeEmpty); 18172 mergeAndNormalizeText(prevText, firstChild, rng, true); 18173 mergeAndNormalizeText(nextText, lastChild, rng, false); 18174 rng.collapse(false); 18175 }; 18176 const setupArgs$2 = (args, content) => ({ 18177 format: 'html', 18178 ...args, 18179 set: true, 18180 selection: true, 18181 content 18182 }); 18183 const cleanContent = (editor, args) => { 18184 if (args.format !== 'raw') { 18185 const rng = editor.selection.getRng(); 18186 const contextBlock = editor.dom.getParent(rng.commonAncestorContainer, editor.dom.isBlock); 18187 const contextArgs = contextBlock ? { context: contextBlock.nodeName.toLowerCase() } : {}; 18188 const node = editor.parser.parse(args.content, { 18189 forced_root_block: false, 18190 ...contextArgs, 18191 ...args 18192 }); 18193 return HtmlSerializer({ validate: false }, editor.schema).serialize(node); 18194 } else { 18195 return args.content; 18196 } 18197 }; 18198 const setContent$1 = (editor, content, args = {}) => { 18199 const defaultedArgs = setupArgs$2(args, content); 18200 preProcessSetContent(editor, defaultedArgs).each(updatedArgs => { 18201 const cleanedContent = cleanContent(editor, updatedArgs); 18202 const rng = editor.selection.getRng(); 18203 rngSetContent(rng, rng.createContextualFragment(cleanedContent)); 18204 editor.selection.setRng(rng); 18205 scrollRangeIntoView(editor, rng); 18206 postProcessSetContent(editor, cleanedContent, updatedArgs); 18207 }); 18208 }; 18209 18210 const deleteFromCallbackMap = (callbackMap, selector, callback) => { 18211 if (has$2(callbackMap, selector)) { 18212 const newCallbacks = filter$5(callbackMap[selector], cb => cb !== callback); 18213 if (newCallbacks.length === 0) { 18214 delete callbackMap[selector]; 18215 } else { 18216 callbackMap[selector] = newCallbacks; 18217 } 18218 } 18219 }; 18220 var SelectorChanged = (dom, editor) => { 18221 let selectorChangedData; 18222 let currentSelectors; 18223 const findMatchingNode = (selector, nodes) => find$2(nodes, node => dom.is(node, selector)); 18224 const getParents = elem => dom.getParents(elem, undefined, dom.getRoot()); 18225 const setup = () => { 18226 selectorChangedData = {}; 18227 currentSelectors = {}; 18228 editor.on('NodeChange', e => { 18229 const node = e.element; 18230 const parents = getParents(node); 18231 const matchedSelectors = {}; 18232 each$d(selectorChangedData, (callbacks, selector) => { 18233 findMatchingNode(selector, parents).each(node => { 18234 if (!currentSelectors[selector]) { 18235 each$e(callbacks, callback => { 18236 callback(true, { 18237 node, 18238 selector, 18239 parents 18240 }); 18241 }); 18242 currentSelectors[selector] = callbacks; 18243 } 18244 matchedSelectors[selector] = callbacks; 18245 }); 18246 }); 18247 each$d(currentSelectors, (callbacks, selector) => { 18248 if (!matchedSelectors[selector]) { 18249 delete currentSelectors[selector]; 18250 each$e(callbacks, callback => { 18251 callback(false, { 18252 node, 18253 selector, 18254 parents 18255 }); 18256 }); 18257 } 18258 }); 18259 }); 18260 }; 18261 return { 18262 selectorChangedWithUnbind: (selector, callback) => { 18263 if (!selectorChangedData) { 18264 setup(); 18265 } 18266 if (!selectorChangedData[selector]) { 18267 selectorChangedData[selector] = []; 18268 } 18269 selectorChangedData[selector].push(callback); 18270 findMatchingNode(selector, getParents(editor.selection.getStart())).each(() => { 18271 currentSelectors[selector] = selectorChangedData[selector]; 18272 }); 18273 return { 18274 unbind: () => { 18275 deleteFromCallbackMap(selectorChangedData, selector, callback); 18276 deleteFromCallbackMap(currentSelectors, selector, callback); 18277 } 18278 }; 18279 } 18280 }; 18281 }; 18282 18283 const isAttachedToDom = node => { 18284 return !!(node && node.ownerDocument) && contains(SugarElement.fromDom(node.ownerDocument), SugarElement.fromDom(node)); 18285 }; 18286 const isValidRange = rng => { 18287 if (!rng) { 18288 return false; 18289 } else { 18290 return isAttachedToDom(rng.startContainer) && isAttachedToDom(rng.endContainer); 18291 } 18292 }; 18293 const EditorSelection = (dom, win, serializer, editor) => { 18294 let selectedRange; 18295 let explicitRange; 18296 const {selectorChangedWithUnbind} = SelectorChanged(dom, editor); 18297 const setCursorLocation = (node, offset) => { 18298 const rng = dom.createRng(); 18299 if (isNonNullable(node) && isNonNullable(offset)) { 18300 rng.setStart(node, offset); 18301 rng.setEnd(node, offset); 18302 setRng(rng); 18303 collapse(false); 18304 } else { 18305 moveEndPoint(dom, rng, editor.getBody(), true); 18306 setRng(rng); 18307 } 18308 }; 18309 const getContent = args => getContent$1(editor, args); 18310 const setContent = (content, args) => setContent$1(editor, content, args); 18311 const getStart$1 = real => getStart(editor.getBody(), getRng$1(), real); 18312 const getEnd = real => getEnd$1(editor.getBody(), getRng$1(), real); 18313 const getBookmark = (type, normalized) => bookmarkManager.getBookmark(type, normalized); 18314 const moveToBookmark = bookmark => bookmarkManager.moveToBookmark(bookmark); 18315 const select$1 = (node, content) => { 18316 select(dom, node, content).each(setRng); 18317 return node; 18318 }; 18319 const isCollapsed = () => { 18320 const rng = getRng$1(), sel = getSel(); 18321 if (!rng || rng.item) { 18322 return false; 18323 } 18324 if (rng.compareEndPoints) { 18325 return rng.compareEndPoints('StartToEnd', rng) === 0; 18326 } 18327 return !sel || rng.collapsed; 18328 }; 18329 const isEditable = () => { 18330 const rng = getRng$1(); 18331 const fakeSelectedElements = editor.getBody().querySelectorAll('[data-mce-selected="1"]'); 18332 if (fakeSelectedElements.length > 0) { 18333 return forall(fakeSelectedElements, el => dom.isEditable(el.parentElement)); 18334 } else if (rng.startContainer === rng.endContainer) { 18335 return dom.isEditable(rng.startContainer); 18336 } else { 18337 return dom.isEditable(rng.startContainer) && dom.isEditable(rng.endContainer); 18338 } 18339 }; 18340 const collapse = toStart => { 18341 const rng = getRng$1(); 18342 rng.collapse(!!toStart); 18343 setRng(rng); 18344 }; 18345 const getSel = () => win.getSelection ? win.getSelection() : win.document.selection; 18346 const getRng$1 = () => { 18347 let rng; 18348 const tryCompareBoundaryPoints = (how, sourceRange, destinationRange) => { 18349 try { 18350 return sourceRange.compareBoundaryPoints(how, destinationRange); 18351 } catch (ex) { 18352 return -1; 18353 } 18354 }; 18355 const doc = win.document; 18356 if (isNonNullable(editor.bookmark) && !hasFocus(editor)) { 18357 const bookmark = getRng(editor); 18358 if (bookmark.isSome()) { 18359 return bookmark.map(r => processRanges(editor, [r])[0]).getOr(doc.createRange()); 18360 } 18361 } 18362 try { 18363 const selection = getSel(); 18364 if (selection && !isRestrictedNode(selection.anchorNode)) { 18365 if (selection.rangeCount > 0) { 18366 rng = selection.getRangeAt(0); 18367 } else { 18368 rng = doc.createRange(); 18369 } 18370 rng = processRanges(editor, [rng])[0]; 18371 } 18372 } catch (ex) { 18373 } 18374 if (!rng) { 18375 rng = doc.createRange(); 18376 } 18377 if (isDocument$1(rng.startContainer) && rng.collapsed) { 18378 const elm = dom.getRoot(); 18379 rng.setStart(elm, 0); 18380 rng.setEnd(elm, 0); 18381 } 18382 if (selectedRange && explicitRange) { 18383 if (tryCompareBoundaryPoints(rng.START_TO_START, rng, selectedRange) === 0 && tryCompareBoundaryPoints(rng.END_TO_END, rng, selectedRange) === 0) { 18384 rng = explicitRange; 18385 } else { 18386 selectedRange = null; 18387 explicitRange = null; 18388 } 18389 } 18390 return rng; 18391 }; 18392 const setRng = (rng, forward) => { 18393 if (!isValidRange(rng)) { 18394 return; 18395 } 18396 const sel = getSel(); 18397 const evt = editor.dispatch('SetSelectionRange', { 18398 range: rng, 18399 forward 18400 }); 18401 rng = evt.range; 18402 if (sel) { 18403 explicitRange = rng; 18404 try { 18405 sel.removeAllRanges(); 18406 sel.addRange(rng); 18407 } catch (ex) { 18408 } 18409 if (forward === false && sel.extend) { 18410 sel.collapse(rng.endContainer, rng.endOffset); 18411 sel.extend(rng.startContainer, rng.startOffset); 18412 } 18413 selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null; 18414 } 18415 if (!rng.collapsed && rng.startContainer === rng.endContainer && (sel === null || sel === void 0 ? void 0 : sel.setBaseAndExtent)) { 18416 if (rng.endOffset - rng.startOffset < 2) { 18417 if (rng.startContainer.hasChildNodes()) { 18418 const node = rng.startContainer.childNodes[rng.startOffset]; 18419 if (node && node.nodeName === 'IMG') { 18420 sel.setBaseAndExtent(rng.startContainer, rng.startOffset, rng.endContainer, rng.endOffset); 18421 if (sel.anchorNode !== rng.startContainer || sel.focusNode !== rng.endContainer) { 18422 sel.setBaseAndExtent(node, 0, node, 1); 18423 } 18424 } 18425 } 18426 } 18427 } 18428 editor.dispatch('AfterSetSelectionRange', { 18429 range: rng, 18430 forward 18431 }); 18432 }; 18433 const setNode = elm => { 18434 setContent(dom.getOuterHTML(elm)); 18435 return elm; 18436 }; 18437 const getNode$1 = () => getNode(editor.getBody(), getRng$1()); 18438 const getSelectedBlocks$1 = (startElm, endElm) => getSelectedBlocks(dom, getRng$1(), startElm, endElm); 18439 const isForward = () => { 18440 const sel = getSel(); 18441 const anchorNode = sel === null || sel === void 0 ? void 0 : sel.anchorNode; 18442 const focusNode = sel === null || sel === void 0 ? void 0 : sel.focusNode; 18443 if (!sel || !anchorNode || !focusNode || isRestrictedNode(anchorNode) || isRestrictedNode(focusNode)) { 18444 return true; 18445 } 18446 const anchorRange = dom.createRng(); 18447 const focusRange = dom.createRng(); 18448 try { 18449 anchorRange.setStart(anchorNode, sel.anchorOffset); 18450 anchorRange.collapse(true); 18451 focusRange.setStart(focusNode, sel.focusOffset); 18452 focusRange.collapse(true); 18453 } catch (e) { 18454 return true; 18455 } 18456 return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0; 18457 }; 18458 const normalize = () => { 18459 const rng = getRng$1(); 18460 const sel = getSel(); 18461 if (!hasMultipleRanges(sel) && hasAnyRanges(editor)) { 18462 const normRng = normalize$2(dom, rng); 18463 normRng.each(normRng => { 18464 setRng(normRng, isForward()); 18465 }); 18466 return normRng.getOr(rng); 18467 } 18468 return rng; 18469 }; 18470 const selectorChanged = (selector, callback) => { 18471 selectorChangedWithUnbind(selector, callback); 18472 return exports; 18473 }; 18474 const getScrollContainer = () => { 18475 let scrollContainer; 18476 let node = dom.getRoot(); 18477 while (node && node.nodeName !== 'BODY') { 18478 if (node.scrollHeight > node.clientHeight) { 18479 scrollContainer = node; 18480 break; 18481 } 18482 node = node.parentNode; 18483 } 18484 return scrollContainer; 18485 }; 18486 const scrollIntoView = (elm, alignToTop) => { 18487 if (isNonNullable(elm)) { 18488 scrollElementIntoView(editor, elm, alignToTop); 18489 } else { 18490 scrollRangeIntoView(editor, getRng$1(), alignToTop); 18491 } 18492 }; 18493 const placeCaretAt = (clientX, clientY) => setRng(fromPoint(clientX, clientY, editor.getDoc())); 18494 const getBoundingClientRect = () => { 18495 const rng = getRng$1(); 18496 return rng.collapsed ? CaretPosition.fromRangeStart(rng).getClientRects()[0] : rng.getBoundingClientRect(); 18497 }; 18498 const destroy = () => { 18499 win = selectedRange = explicitRange = null; 18500 controlSelection.destroy(); 18501 }; 18502 const expand = (options = { type: 'word' }) => setRng(RangeUtils(dom).expand(getRng$1(), options)); 18503 const exports = { 18504 dom, 18505 win, 18506 serializer, 18507 editor, 18508 expand, 18509 collapse, 18510 setCursorLocation, 18511 getContent, 18512 setContent, 18513 getBookmark, 18514 moveToBookmark, 18515 select: select$1, 18516 isCollapsed, 18517 isEditable, 18518 isForward, 18519 setNode, 18520 getNode: getNode$1, 18521 getSel, 18522 setRng, 18523 getRng: getRng$1, 18524 getStart: getStart$1, 18525 getEnd, 18526 getSelectedBlocks: getSelectedBlocks$1, 18527 normalize, 18528 selectorChanged, 18529 selectorChangedWithUnbind, 18530 getScrollContainer, 18531 scrollIntoView, 18532 placeCaretAt, 18533 getBoundingClientRect, 18534 destroy 18535 }; 18536 const bookmarkManager = BookmarkManager(exports); 18537 const controlSelection = ControlSelection(exports, editor); 18538 exports.bookmarkManager = bookmarkManager; 18539 exports.controlSelection = controlSelection; 18540 return exports; 18541 }; 18542 18543 const register$3 = (htmlParser, settings, dom) => { 18544 htmlParser.addAttributeFilter('data-mce-tabindex', (nodes, name) => { 18545 let i = nodes.length; 18546 while (i--) { 18547 const node = nodes[i]; 18548 node.attr('tabindex', node.attr('data-mce-tabindex')); 18549 node.attr(name, null); 18550 } 18551 }); 18552 htmlParser.addAttributeFilter('src,href,style', (nodes, name) => { 18553 const internalName = 'data-mce-' + name; 18554 const urlConverter = settings.url_converter; 18555 const urlConverterScope = settings.url_converter_scope; 18556 let i = nodes.length; 18557 while (i--) { 18558 const node = nodes[i]; 18559 let value = node.attr(internalName); 18560 if (value !== undefined) { 18561 node.attr(name, value.length > 0 ? value : null); 18562 node.attr(internalName, null); 18563 } else { 18564 value = node.attr(name); 18565 if (name === 'style') { 18566 value = dom.serializeStyle(dom.parseStyle(value), node.name); 18567 } else if (urlConverter) { 18568 value = urlConverter.call(urlConverterScope, value, name, node.name); 18569 } 18570 node.attr(name, value.length > 0 ? value : null); 18571 } 18572 } 18573 }); 18574 htmlParser.addAttributeFilter('class', nodes => { 18575 let i = nodes.length; 18576 while (i--) { 18577 const node = nodes[i]; 18578 let value = node.attr('class'); 18579 if (value) { 18580 value = value.replace(/(?:^|\s)mce-item-\w+(?!\S)/g, ''); 18581 node.attr('class', value.length > 0 ? value : null); 18582 } 18583 } 18584 }); 18585 htmlParser.addAttributeFilter('data-mce-type', (nodes, name, args) => { 18586 let i = nodes.length; 18587 while (i--) { 18588 const node = nodes[i]; 18589 if (node.attr('data-mce-type') === 'bookmark' && !args.cleanup) { 18590 const hasChildren = Optional.from(node.firstChild).exists(firstChild => { 18591 var _a; 18592 return !isZwsp$1((_a = firstChild.value) !== null && _a !== void 0 ? _a : ''); 18593 }); 18594 if (hasChildren) { 18595 node.unwrap(); 18596 } else { 18597 node.remove(); 18598 } 18599 } 18600 } 18601 }); 18602 htmlParser.addNodeFilter('noscript', nodes => { 18603 var _a; 18604 let i = nodes.length; 18605 while (i--) { 18606 const node = nodes[i].firstChild; 18607 if (node) { 18608 node.value = Entities.decode((_a = node.value) !== null && _a !== void 0 ? _a : ''); 18609 } 18610 } 18611 }); 18612 htmlParser.addNodeFilter('script,style', (nodes, name) => { 18613 var _a; 18614 const trim = value => { 18615 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, ''); 18616 }; 18617 let i = nodes.length; 18618 while (i--) { 18619 const node = nodes[i]; 18620 const firstChild = node.firstChild; 18621 const value = (_a = firstChild === null || firstChild === void 0 ? void 0 : firstChild.value) !== null && _a !== void 0 ? _a : ''; 18622 if (name === 'script') { 18623 const type = node.attr('type'); 18624 if (type) { 18625 node.attr('type', type === 'mce-no/type' ? null : type.replace(/^mce\-/, '')); 18626 } 18627 if (settings.element_format === 'xhtml' && firstChild && value.length > 0) { 18628 firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>'; 18629 } 18630 } else { 18631 if (settings.element_format === 'xhtml' && firstChild && value.length > 0) { 18632 firstChild.value = '<!--\n' + trim(value) + '\n-->'; 18633 } 18634 } 18635 } 18636 }); 18637 htmlParser.addNodeFilter('#comment', nodes => { 18638 let i = nodes.length; 18639 while (i--) { 18640 const node = nodes[i]; 18641 const value = node.value; 18642 if (settings.preserve_cdata && (value === null || value === void 0 ? void 0 : value.indexOf('[CDATA[')) === 0) { 18643 node.name = '#cdata'; 18644 node.type = 4; 18645 node.value = dom.decode(value.replace(/^\[CDATA\[|\]\]$/g, '')); 18646 } else if ((value === null || value === void 0 ? void 0 : value.indexOf('mce:protected ')) === 0) { 18647 node.name = '#text'; 18648 node.type = 3; 18649 node.raw = true; 18650 node.value = unescape(value).substr(14); 18651 } 18652 } 18653 }); 18654 htmlParser.addNodeFilter('xml:namespace,input', (nodes, name) => { 18655 let i = nodes.length; 18656 while (i--) { 18657 const node = nodes[i]; 18658 if (node.type === 7) { 18659 node.remove(); 18660 } else if (node.type === 1) { 18661 if (name === 'input' && !node.attr('type')) { 18662 node.attr('type', 'text'); 18663 } 18664 } 18665 } 18666 }); 18667 htmlParser.addAttributeFilter('data-mce-type', nodes => { 18668 each$e(nodes, node => { 18669 if (node.attr('data-mce-type') === 'format-caret') { 18670 if (node.isEmpty(htmlParser.schema.getNonEmptyElements())) { 18671 node.remove(); 18672 } else { 18673 node.unwrap(); 18674 } 18675 } 18676 }); 18677 }); 18678 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) => { 18679 let i = nodes.length; 18680 while (i--) { 18681 nodes[i].attr(name, null); 18682 } 18683 }); 18684 if (settings.remove_trailing_brs) { 18685 addNodeFilter(
vendor: 2,413 bytes, lines 18685-18761
18685settings, htmlParser, htmlParser.schema); 18686 } 18687 }; 18688 const trimTrailingBr = rootNode => { 18689 const isBr = node => { 18690 return (node === null || node === void 0 ? void 0 : node.name) === 'br'; 18691 }; 18692 const brNode1 = rootNode.lastChild; 18693 if (isBr(brNode1)) { 18694 const brNode2 = brNode1.prev; 18695 if (isBr(brNode2)) { 18696 brNode1.remove(); 18697 brNode2.remove(); 18698 } 18699 } 18700 }; 18701 18702 const preProcess$1 = (editor, node, args) => { 18703 let oldDoc; 18704 const dom = editor.dom; 18705 let clonedNode = node.cloneNode(true); 18706 const impl = document.implementation; 18707 if (impl.createHTMLDocument) { 18708 const doc = impl.createHTMLDocument(''); 18709 Tools.each(clonedNode.nodeName === 'BODY' ? clonedNode.childNodes : [clonedNode], node => { 18710 doc.body.appendChild(doc.importNode(node, true)); 18711 }); 18712 if (clonedNode.nodeName !== 'BODY') { 18713 clonedNode = doc.body.firstChild; 18714 } else { 18715 clonedNode = doc.body; 18716 } 18717 oldDoc = dom.doc; 18718 dom.doc = doc; 18719 } 18720 firePreProcess(editor, { 18721 ...args, 18722 node: clonedNode 18723 }); 18724 if (oldDoc) { 18725 dom.doc = oldDoc; 18726 } 18727 return clonedNode; 18728 }; 18729 const shouldFireEvent = (editor, args) => { 18730 return isNonNullable(editor) && editor.hasEventListeners('PreProcess') && !args.no_events; 18731 }; 18732 const process$1 = (editor, node, args) => { 18733 return shouldFireEvent(editor, args) ? preProcess$1(editor, node, args) : node; 18734 }; 18735 18736 const addTempAttr = (htmlParser, tempAttrs, name) => { 18737 if (Tools.inArray(tempAttrs, name) === -1) { 18738 htmlParser.addAttributeFilter(name, (nodes, name) => { 18739 let i = nodes.length; 18740 while (i--) { 18741 nodes[i].attr(name, null); 18742 } 18743 }); 18744 tempAttrs.push(name); 18745 } 18746 }; 18747 const postProcess = (editor, args, content) => { 18748 if (!args.no_events && editor) { 18749 const outArgs = firePostProcess(editor, { 18750 ...args, 18751 content 18752 }); 18753 return outArgs.content; 18754 } else { 18755 return content; 18756 } 18757 }; 18758 const getHtmlFromNode = (dom, node, args) => { 18759 const html = trim$1(args.getInner ? node.innerHTML : dom.getOuterHTML(node)); 18760 return args.selection || isWsPreserveElement(SugarElement.fromDom(node)) ? html : Tools.trim(html); 18761 };
18762 const parseHtml = (htmlParser, html, args) => { 18763 const parserArgs = args.selection ? { 18764 forced_root_block: false, 18765 ...args 18766 } : args; 18767 const rootNode = htmlParser.parse(html, parserArgs); 18768 trimTrailingBr(rootNode); 18769 return rootNode; 18770 }; 18771 const serializeNode = (settings, schema, node) => { 18772 const htmlSerializer = HtmlSerializer(settings, schema); 18773 return htmlSerializer.serialize(node); 18774 }; 18775 const toHtml = (editor, settings, schema, rootNode, args) => { 18776 const content = serializeNode(settings, schema, rootNode); 18777 return postProcess(editor, args, content); 18778 }; 18779 const DomSerializerImpl = (settings, editor) => { 18780 const tempAttrs = ['data-mce-selected']; 18781 const defaultedSettings = { 18782 entity_encoding: 'named', 18783 remove_trailing_brs: true, 18784 pad_empty_with_br: fals
vendor: 365,104 bytes, lines 18784-28055
18784e, 18785 ...settings 18786 }; 18787 const dom = editor && editor.dom ? editor.dom : DOMUtils.DOM; 18788 const schema = editor && editor.schema ? editor.schema : Schema(defaultedSettings); 18789 const htmlParser = DomParser(defaultedSettings, schema); 18790 register$3(htmlParser, defaultedSettings, dom); 18791 const serialize = (node, parserArgs = {}) => { 18792 const args = { 18793 format: 'html', 18794 ...parserArgs 18795 }; 18796 const targetNode = process$1(editor, node, args); 18797 const html = getHtmlFromNode(dom, targetNode, args); 18798 const rootNode = parseHtml(htmlParser, html, args); 18799 return args.format === 'tree' ? rootNode : toHtml(editor, defaultedSettings, schema, rootNode, args); 18800 }; 18801 return { 18802 schema, 18803 addNodeFilter: htmlParser.addNodeFilter, 18804 addAttributeFilter: htmlParser.addAttributeFilter, 18805 serialize: serialize, 18806 addRules: schema.addValidElements, 18807 setRules: schema.setValidElements, 18808 addTempAttr: curry(addTempAttr, htmlParser, tempAttrs), 18809 getTempAttrs: constant(tempAttrs), 18810 getNodeFilters: htmlParser.getNodeFilters, 18811 getAttributeFilters: htmlParser.getAttributeFilters, 18812 removeNodeFilter: htmlParser.removeNodeFilter, 18813 removeAttributeFilter: htmlParser.removeAttributeFilter 18814 }; 18815 }; 18816 18817 const DomSerializer = (settings, editor) => { 18818 const domSerializer = DomSerializerImpl(settings, editor); 18819 return { 18820 schema: domSerializer.schema, 18821 addNodeFilter: domSerializer.addNodeFilter, 18822 addAttributeFilter: domSerializer.addAttributeFilter, 18823 serialize: domSerializer.serialize, 18824 addRules: domSerializer.addRules, 18825 setRules: domSerializer.setRules, 18826 addTempAttr: domSerializer.addTempAttr, 18827 getTempAttrs: domSerializer.getTempAttrs, 18828 getNodeFilters: domSerializer.getNodeFilters, 18829 getAttributeFilters: domSerializer.getAttributeFilters, 18830 removeNodeFilter: domSerializer.removeNodeFilter, 18831 removeAttributeFilter: domSerializer.removeAttributeFilter 18832 }; 18833 }; 18834 18835 const defaultFormat$1 = 'html'; 18836 const setupArgs$1 = (args, format) => ({ 18837 ...args, 18838 format, 18839 get: true, 18840 getInner: true 18841 }); 18842 const getContent = (editor, args = {}) => { 18843 const format = args.format ? args.format : defaultFormat$1; 18844 const defaultedArgs = setupArgs$1(args, format); 18845 return preProcessGetContent(editor, defaultedArgs).fold(identity, updatedArgs => { 18846 const content = getContent$2(editor, updatedArgs); 18847 return postProcessGetContent(editor, content, updatedArgs); 18848 }); 18849 }; 18850 18851 const defaultFormat = 'html'; 18852 const setupArgs = (args, content) => ({ 18853 format: defaultFormat, 18854 ...args, 18855 set: true, 18856 content 18857 }); 18858 const setContent = (editor, content, args = {}) => { 18859 const defaultedArgs = setupArgs(args, content); 18860 return preProcessSetContent(editor, defaultedArgs).map(updatedArgs => { 18861 const result = setContent$2(editor, updatedArgs.content, updatedArgs); 18862 postProcessSetContent(editor, result.html, updatedArgs); 18863 return result.content; 18864 }).getOr(content); 18865 }; 18866 18867 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(','); 18868 const deprecatedOptions = 'template_cdate_classes,template_mdate_classes,template_selected_content_classes,template_preview_replace_values,template_replace_values,templates,template_cdate_format,template_mdate_format'.split(','); 18869 const removedPlugins = 'bbcode,colorpicker,contextmenu,fullpage,legacyoutput,spellchecker,textcolor'.split(','); 18870 const deprecatedPlugins = [ 18871 { 18872 name: 'template', 18873 replacedWith: 'Advanced Template' 18874 }, 18875 { name: 'rtc' } 18876 ]; 18877 const getMatchingOptions = (options, searchingFor) => { 18878 const settingNames = filter$5(searchingFor, setting => has$2(options, setting)); 18879 return sort(settingNames); 18880 }; 18881 const getRemovedOptions = options => { 18882 const settingNames = getMatchingOptions(options, removedOptions); 18883 const forcedRootBlock = options.forced_root_block; 18884 if (forcedRootBlock === false || forcedRootBlock === '') { 18885 settingNames.push('forced_root_block (false only)'); 18886 } 18887 return sort(settingNames); 18888 }; 18889 const getDeprecatedOptions = options => getMatchingOptions(options, deprecatedOptions); 18890 const getMatchingPlugins = (options, searchingFor) => { 18891 const plugins = Tools.makeMap(options.plugins, ' '); 18892 const hasPlugin = plugin => has$2(plugins, plugin); 18893 const pluginNames = filter$5(searchingFor, hasPlugin); 18894 return sort(pluginNames); 18895 }; 18896 const getRemovedPlugins = options => getMatchingPlugins(options, removedPlugins); 18897 const getDeprecatedPlugins = options => getMatchingPlugins(options, deprecatedPlugins.map(entry => entry.name)); 18898 const logRemovedWarnings = (rawOptions, normalizedOptions) => { 18899 const removedOptions = getRemovedOptions(rawOptions); 18900 const removedPlugins = getRemovedPlugins(normalizedOptions); 18901 const hasRemovedPlugins = removedPlugins.length > 0; 18902 const hasRemovedOptions = removedOptions.length > 0; 18903 const isLegacyMobileTheme = normalizedOptions.theme === 'mobile'; 18904 if (hasRemovedPlugins || hasRemovedOptions || isLegacyMobileTheme) { 18905 const listJoiner = '\n- '; 18906 const themesMessage = isLegacyMobileTheme ? `\n\nThemes:${ listJoiner }mobile` : ''; 18907 const pluginsMessage = hasRemovedPlugins ? `\n\nPlugins:${ listJoiner }${ removedPlugins.join(listJoiner) }` : ''; 18908 const optionsMessage = hasRemovedOptions ? `\n\nOptions:${ listJoiner }${ removedOptions.join(listJoiner) }` : ''; 18909 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); 18910 } 18911 }; 18912 const getPluginDescription = name => find$2(deprecatedPlugins, entry => entry.name === name).fold(() => name, entry => { 18913 if (entry.replacedWith) { 18914 return `${ name }, replaced by ${ entry.replacedWith }`; 18915 } else { 18916 return name; 18917 } 18918 }); 18919 const logDeprecatedWarnings = (rawOptions, normalizedOptions) => { 18920 const deprecatedOptions = getDeprecatedOptions(rawOptions); 18921 const deprecatedPlugins = getDeprecatedPlugins(normalizedOptions); 18922 const hasDeprecatedPlugins = deprecatedPlugins.length > 0; 18923 const hasDeprecatedOptions = deprecatedOptions.length > 0; 18924 if (hasDeprecatedPlugins || hasDeprecatedOptions) { 18925 const listJoiner = '\n- '; 18926 const pluginsMessage = hasDeprecatedPlugins ? `\n\nPlugins:${ listJoiner }${ deprecatedPlugins.map(getPluginDescription).join(listJoiner) }` : ''; 18927 const optionsMessage = hasDeprecatedOptions ? `\n\nOptions:${ listJoiner }${ deprecatedOptions.join(listJoiner) }` : ''; 18928 console.warn('The following deprecated features are currently enabled but will be removed soon.' + pluginsMessage + optionsMessage); 18929 } 18930 }; 18931 const logWarnings = (rawOptions, normalizedOptions) => { 18932 logRemovedWarnings(rawOptions, normalizedOptions); 18933 logDeprecatedWarnings(rawOptions, normalizedOptions); 18934 }; 18935 18936 const DOM$8 = DOMUtils.DOM; 18937 const restoreOriginalStyles = editor => { 18938 DOM$8.setStyle(editor.id, 'display', editor.orgDisplay); 18939 }; 18940 const safeDestroy = x => Optional.from(x).each(x => x.destroy()); 18941 const clearDomReferences = editor => { 18942 const ed = editor; 18943 ed.contentAreaContainer = ed.formElement = ed.container = ed.editorContainer = null; 18944 ed.bodyElement = ed.contentDocument = ed.contentWindow = null; 18945 ed.iframeElement = ed.targetElm = null; 18946 const selection = editor.selection; 18947 if (selection) { 18948 const dom = selection.dom; 18949 ed.selection = selection.win = selection.dom = dom.doc = null; 18950 } 18951 }; 18952 const restoreForm = editor => { 18953 const form = editor.formElement; 18954 if (form) { 18955 if (form._mceOldSubmit) { 18956 form.submit = form._mceOldSubmit; 18957 delete form._mceOldSubmit; 18958 } 18959 DOM$8.unbind(form, 'submit reset', editor.formEventDelegate); 18960 } 18961 }; 18962 const remove$1 = editor => { 18963 if (!editor.removed) { 18964 const {_selectionOverrides, editorUpload} = editor; 18965 const body = editor.getBody(); 18966 const element = editor.getElement(); 18967 if (body) { 18968 editor.save({ is_removing: true }); 18969 } 18970 editor.removed = true; 18971 editor.unbindAllNativeEvents(); 18972 if (editor.hasHiddenInput && isNonNullable(element === null || element === void 0 ? void 0 : element.nextSibling)) { 18973 DOM$8.remove(element.nextSibling); 18974 } 18975 fireRemove(editor); 18976 editor.editorManager.remove(editor); 18977 if (!editor.inline && body) { 18978 restoreOriginalStyles(editor); 18979 } 18980 fireDetach(editor); 18981 DOM$8.remove(editor.getContainer()); 18982 safeDestroy(_selectionOverrides); 18983 safeDestroy(editorUpload); 18984 editor.destroy(); 18985 } 18986 }; 18987 const destroy = (editor, automatic) => { 18988 const {selection, dom} = editor; 18989 if (editor.destroyed) { 18990 return; 18991 } 18992 if (!automatic && !editor.removed) { 18993 editor.remove(); 18994 return; 18995 } 18996 if (!automatic) { 18997 editor.editorManager.off('beforeunload', editor._beforeUnload); 18998 if (editor.theme && editor.theme.destroy) { 18999 editor.theme.destroy(); 19000 } 19001 safeDestroy(selection); 19002 safeDestroy(dom); 19003 } 19004 restoreForm(editor); 19005 clearDomReferences(editor); 19006 editor.destroyed = true; 19007 }; 19008 19009 const CreateIconManager = () => { 19010 const lookup = {}; 19011 const add = (id, iconPack) => { 19012 lookup[id] = iconPack; 19013 }; 19014 const get = id => { 19015 if (lookup[id]) { 19016 return lookup[id]; 19017 } else { 19018 return { icons: {} }; 19019 } 19020 }; 19021 const has = id => has$2(lookup, id); 19022 return { 19023 add, 19024 get, 19025 has 19026 }; 19027 }; 19028 const IconManager = CreateIconManager(); 19029 19030 const ModelManager = AddOnManager.ModelManager; 19031 19032 const getProp = (propName, elm) => { 19033 const rawElm = elm.dom; 19034 return rawElm[propName]; 19035 }; 19036 const getComputedSizeProp = (propName, elm) => parseInt(get$7(elm, propName), 10); 19037 const getClientWidth = curry(getProp, 'clientWidth'); 19038 const getClientHeight = curry(getProp, 'clientHeight'); 19039 const getMarginTop = curry(getComputedSizeProp, 'margin-top'); 19040 const getMarginLeft = curry(getComputedSizeProp, 'margin-left'); 19041 const getBoundingClientRect = elm => elm.dom.getBoundingClientRect(); 19042 const isInsideElementContentArea = (bodyElm, clientX, clientY) => { 19043 const clientWidth = getClientWidth(bodyElm); 19044 const clientHeight = getClientHeight(bodyElm); 19045 return clientX >= 0 && clientY >= 0 && clientX <= clientWidth && clientY <= clientHeight; 19046 }; 19047 const transpose = (inline, elm, clientX, clientY) => { 19048 const clientRect = getBoundingClientRect(elm); 19049 const deltaX = inline ? clientRect.left + elm.dom.clientLeft + getMarginLeft(elm) : 0; 19050 const deltaY = inline ? clientRect.top + elm.dom.clientTop + getMarginTop(elm) : 0; 19051 const x = clientX - deltaX; 19052 const y = clientY - deltaY; 19053 return { 19054 x, 19055 y 19056 }; 19057 }; 19058 const isXYInContentArea = (editor, clientX, clientY) => { 19059 const bodyElm = SugarElement.fromDom(editor.getBody()); 19060 const targetElm = editor.inline ? bodyElm : documentElement(bodyElm); 19061 const transposedPoint = transpose(editor.inline, targetElm, clientX, clientY); 19062 return isInsideElementContentArea(targetElm, transposedPoint.x, transposedPoint.y); 19063 }; 19064 const fromDomSafe = node => Optional.from(node).map(SugarElement.fromDom); 19065 const isEditorAttachedToDom = editor => { 19066 const rawContainer = editor.inline ? editor.getBody() : editor.getContentAreaContainer(); 19067 return fromDomSafe(rawContainer).map(inBody).getOr(false); 19068 }; 19069 19070 var NotificationManagerImpl = () => { 19071 const unimplemented = () => { 19072 throw new Error('Theme did not provide a NotificationManager implementation.'); 19073 }; 19074 return { 19075 open: unimplemented, 19076 close: unimplemented, 19077 getArgs: unimplemented 19078 }; 19079 }; 19080 19081 const NotificationManager = editor => { 19082 const notifications = []; 19083 const getImplementation = () => { 19084 const theme = editor.theme; 19085 return theme && theme.getNotificationManagerImpl ? theme.getNotificationManagerImpl() : NotificationManagerImpl(); 19086 }; 19087 const getTopNotification = () => { 19088 return Optional.from(notifications[0]); 19089 }; 19090 const isEqual = (a, b) => { 19091 return a.type === b.type && a.text === b.text && !a.progressBar && !a.timeout && !b.progressBar && !b.timeout; 19092 }; 19093 const reposition = () => { 19094 each$e(notifications, notification => { 19095 notification.reposition(); 19096 }); 19097 }; 19098 const addNotification = notification => { 19099 notifications.push(notification); 19100 }; 19101 const closeNotification = notification => { 19102 findIndex$2(notifications, otherNotification => { 19103 return otherNotification === notification; 19104 }).each(index => { 19105 notifications.splice(index, 1); 19106 }); 19107 }; 19108 const open = (spec, fireEvent = true) => { 19109 if (editor.removed || !isEditorAttachedToDom(editor)) { 19110 return {}; 19111 } 19112 if (fireEvent) { 19113 editor.dispatch('BeforeOpenNotification', { notification: spec }); 19114 } 19115 return find$2(notifications, notification => { 19116 return isEqual(getImplementation().getArgs(notification), spec); 19117 }).getOrThunk(() => { 19118 editor.editorManager.setActive(editor); 19119 const notification = getImplementation().open(spec, () => { 19120 closeNotification(notification); 19121 reposition(); 19122 getTopNotification().fold(() => editor.focus(), top => focus$1(SugarElement.fromDom(top.getEl()))); 19123 }); 19124 addNotification(notification); 19125 reposition(); 19126 editor.dispatch('OpenNotification', { notification: { ...notification } }); 19127 return notification; 19128 }); 19129 }; 19130 const close = () => { 19131 getTopNotification().each(notification => { 19132 getImplementation().close(notification); 19133 closeNotification(notification); 19134 reposition(); 19135 }); 19136 }; 19137 const getNotifications = constant(notifications); 19138 const registerEvents = editor => { 19139 editor.on('SkinLoaded', () => { 19140 const serviceMessage = getServiceMessage(editor); 19141 if (serviceMessage) { 19142 open({ 19143 text: serviceMessage, 19144 type: 'warning', 19145 timeout: 0 19146 }, false); 19147 } 19148 reposition(); 19149 }); 19150 editor.on('show ResizeEditor ResizeWindow NodeChange', () => { 19151 requestAnimationFrame(reposition); 19152 }); 19153 editor.on('remove', () => { 19154 each$e(notifications.slice(), notification => { 19155 getImplementation().close(notification); 19156 }); 19157 }); 19158 }; 19159 registerEvents(editor); 19160 return { 19161 open, 19162 close, 19163 getNotifications 19164 }; 19165 }; 19166 19167 const PluginManager = AddOnManager.PluginManager; 19168 19169 const ThemeManager = AddOnManager.ThemeManager; 19170 19171 var WindowManagerImpl = () => { 19172 const unimplemented = () => { 19173 throw new Error('Theme did not provide a WindowManager implementation.'); 19174 }; 19175 return { 19176 open: unimplemented, 19177 openUrl: unimplemented, 19178 alert: unimplemented, 19179 confirm: unimplemented, 19180 close: unimplemented 19181 }; 19182 }; 19183 19184 const WindowManager = editor => { 19185 let dialogs = []; 19186 const getImplementation = () => { 19187 const theme = editor.theme; 19188 return theme && theme.getWindowManagerImpl ? theme.getWindowManagerImpl() : WindowManagerImpl(); 19189 }; 19190 const funcBind = (scope, f) => { 19191 return (...args) => { 19192 return f ? f.apply(scope, args) : undefined; 19193 }; 19194 }; 19195 const fireOpenEvent = dialog => { 19196 editor.dispatch('OpenWindow', { dialog }); 19197 }; 19198 const fireCloseEvent = dialog => { 19199 editor.dispatch('CloseWindow', { dialog }); 19200 }; 19201 const addDialog = dialog => { 19202 dialogs.push(dialog); 19203 fireOpenEvent(dialog); 19204 }; 19205 const closeDialog = dialog => { 19206 fireCloseEvent(dialog); 19207 dialogs = filter$5(dialogs, otherDialog => { 19208 return otherDialog !== dialog; 19209 }); 19210 if (dialogs.length === 0) { 19211 editor.focus(); 19212 } 19213 }; 19214 const getTopDialog = () => { 19215 return Optional.from(dialogs[dialogs.length - 1]); 19216 }; 19217 const storeSelectionAndOpenDialog = openDialog => { 19218 editor.editorManager.setActive(editor); 19219 store(editor); 19220 editor.ui.show(); 19221 const dialog = openDialog(); 19222 addDialog(dialog); 19223 return dialog; 19224 }; 19225 const open = (args, params) => { 19226 return storeSelectionAndOpenDialog(() => getImplementation().open(args, params, closeDialog)); 19227 }; 19228 const openUrl = args => { 19229 return storeSelectionAndOpenDialog(() => getImplementation().openUrl(args, closeDialog)); 19230 }; 19231 const alert = (message, callback, scope) => { 19232 const windowManagerImpl = getImplementation(); 19233 windowManagerImpl.alert(message, funcBind(scope ? scope : windowManagerImpl, callback)); 19234 }; 19235 const confirm = (message, callback, scope) => { 19236 const windowManagerImpl = getImplementation(); 19237 windowManagerImpl.confirm(message, funcBind(scope ? scope : windowManagerImpl, callback)); 19238 }; 19239 const close = () => { 19240 getTopDialog().each(dialog => { 19241 getImplementation().close(dialog); 19242 closeDialog(dialog); 19243 }); 19244 }; 19245 editor.on('remove', () => { 19246 each$e(dialogs, dialog => { 19247 getImplementation().close(dialog); 19248 }); 19249 }); 19250 return { 19251 open, 19252 openUrl, 19253 alert, 19254 confirm, 19255 close 19256 }; 19257 }; 19258 19259 const displayNotification = (editor, message) => { 19260 editor.notificationManager.open({ 19261 type: 'error', 19262 text: message 19263 }); 19264 }; 19265 const displayError = (editor, message) => { 19266 if (editor._skinLoaded) { 19267 displayNotification(editor, message); 19268 } else { 19269 editor.on('SkinLoaded', () => { 19270 displayNotification(editor, message); 19271 }); 19272 } 19273 }; 19274 const uploadError = (editor, message) => { 19275 displayError(editor, I18n.translate([ 19276 'Failed to upload image: {0}', 19277 message 19278 ])); 19279 }; 19280 const logError = (editor, errorType, msg) => { 19281 fireError(editor, errorType, { message: msg }); 19282 console.error(msg); 19283 }; 19284 const createLoadError = (type, url, name) => name ? `Failed to load ${ type }: ${ name } from url ${ url }` : `Failed to load ${ type } url: ${ url }`; 19285 const pluginLoadError = (editor, url, name) => { 19286 logError(editor, 'PluginLoadError', createLoadError('plugin', url, name)); 19287 }; 19288 const iconsLoadError = (editor, url, name) => { 19289 logError(editor, 'IconsLoadError', createLoadError('icons', url, name)); 19290 }; 19291 const languageLoadError = (editor, url, name) => { 19292 logError(editor, 'LanguageLoadError', createLoadError('language', url, name)); 19293 }; 19294 const themeLoadError = (editor, url, name) => { 19295 logError(editor, 'ThemeLoadError', createLoadError('theme', url, name)); 19296 }; 19297 const modelLoadError = (editor, url, name) => { 19298 logError(editor, 'ModelLoadError', createLoadError('model', url, name)); 19299 }; 19300 const pluginInitError = (editor, name, err) => { 19301 const message = I18n.translate([ 19302 'Failed to initialize plugin: {0}', 19303 name 19304 ]); 19305 fireError(editor, 'PluginLoadError', { message }); 19306 initError(message, err); 19307 displayError(editor, message); 19308 }; 19309 const initError = (message, ...x) => { 19310 const console = window.console; 19311 if (console) { 19312 if (console.error) { 19313 console.error(message, ...x); 19314 } else { 19315 console.log(message, ...x); 19316 } 19317 } 19318 }; 19319 19320 const isContentCssSkinName = url => /^[a-z0-9\-]+$/i.test(url); 19321 const getContentCssUrls = editor => { 19322 return transformToUrls(editor, getContentCss(editor)); 19323 }; 19324 const getFontCssUrls = editor => { 19325 return transformToUrls(editor, getFontCss(editor)); 19326 }; 19327 const transformToUrls = (editor, cssLinks) => { 19328 const skinUrl = editor.editorManager.baseURL + '/skins/content'; 19329 const suffix = editor.editorManager.suffix; 19330 const contentCssFile = `content${ suffix }.css`; 19331 return map$3(cssLinks, url => { 19332 if (isContentCssSkinName(url) && !editor.inline) { 19333 return `${ skinUrl }/${ url }/${ contentCssFile }`; 19334 } else { 19335 return editor.documentBaseURI.toAbsolute(url); 19336 } 19337 }); 19338 }; 19339 const appendContentCssFromSettings = editor => { 19340 editor.contentCSS = editor.contentCSS.concat(getContentCssUrls(editor), getFontCssUrls(editor)); 19341 }; 19342 19343 const getAllImages = elm => { 19344 return elm ? from(elm.getElementsByTagName('img')) : []; 19345 }; 19346 const ImageScanner = (uploadStatus, blobCache) => { 19347 const cachedPromises = {}; 19348 const findAll = (elm, predicate = always) => { 19349 const images = filter$5(getAllImages(elm), img => { 19350 const src = img.src; 19351 if (img.hasAttribute('data-mce-bogus')) { 19352 return false; 19353 } 19354 if (img.hasAttribute('data-mce-placeholder')) { 19355 return false; 19356 } 19357 if (!src || src === Env.transparentSrc) { 19358 return false; 19359 } 19360 if (startsWith(src, 'blob:')) { 19361 return !uploadStatus.isUploaded(src) && predicate(img); 19362 } 19363 if (startsWith(src, 'data:')) { 19364 return predicate(img); 19365 } 19366 return false; 19367 }); 19368 const promises = map$3(images, img => { 19369 const imageSrc = img.src; 19370 if (has$2(cachedPromises, imageSrc)) { 19371 return cachedPromises[imageSrc].then(imageInfo => { 19372 if (isString(imageInfo)) { 19373 return imageInfo; 19374 } else { 19375 return { 19376 image: img, 19377 blobInfo: imageInfo.blobInfo 19378 }; 19379 } 19380 }); 19381 } else { 19382 const newPromise = imageToBlobInfo(blobCache, imageSrc).then(blobInfo => { 19383 delete cachedPromises[imageSrc]; 19384 return { 19385 image: img, 19386 blobInfo 19387 }; 19388 }).catch(error => { 19389 delete cachedPromises[imageSrc]; 19390 return error; 19391 }); 19392 cachedPromises[imageSrc] = newPromise; 19393 return newPromise; 19394 } 19395 }); 19396 return Promise.all(promises); 19397 }; 19398 return { findAll }; 19399 }; 19400 19401 const UploadStatus = () => { 19402 const PENDING = 1, UPLOADED = 2; 19403 let blobUriStatuses = {}; 19404 const createStatus = (status, resultUri) => { 19405 return { 19406 status, 19407 resultUri 19408 }; 19409 }; 19410 const hasBlobUri = blobUri => { 19411 return blobUri in blobUriStatuses; 19412 }; 19413 const getResultUri = blobUri => { 19414 const result = blobUriStatuses[blobUri]; 19415 return result ? result.resultUri : null; 19416 }; 19417 const isPending = blobUri => { 19418 return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === PENDING : false; 19419 }; 19420 const isUploaded = blobUri => { 19421 return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === UPLOADED : false; 19422 }; 19423 const markPending = blobUri => { 19424 blobUriStatuses[blobUri] = createStatus(PENDING, null); 19425 }; 19426 const markUploaded = (blobUri, resultUri) => { 19427 blobUriStatuses[blobUri] = createStatus(UPLOADED, resultUri); 19428 }; 19429 const removeFailed = blobUri => { 19430 delete blobUriStatuses[blobUri]; 19431 }; 19432 const destroy = () => { 19433 blobUriStatuses = {}; 19434 }; 19435 return { 19436 hasBlobUri, 19437 getResultUri, 19438 isPending, 19439 isUploaded, 19440 markPending, 19441 markUploaded, 19442 removeFailed, 19443 destroy 19444 }; 19445 }; 19446 19447 let count = 0; 19448 const seed = () => { 19449 const rnd = () => { 19450 return Math.round(Math.random() * 4294967295).toString(36); 19451 }; 19452 const now = new Date().getTime(); 19453 return 's' + now.toString(36) + rnd() + rnd() + rnd(); 19454 }; 19455 const uuid = prefix => { 19456 return prefix + count++ + seed(); 19457 }; 19458 19459 const BlobCache = () => { 19460 let cache = []; 19461 const mimeToExt = mime => { 19462 const mimes = { 19463 'image/jpeg': 'jpg', 19464 'image/jpg': 'jpg', 19465 'image/gif': 'gif', 19466 'image/png': 'png', 19467 'image/apng': 'apng', 19468 'image/avif': 'avif', 19469 'image/svg+xml': 'svg', 19470 'image/webp': 'webp', 19471 'image/bmp': 'bmp', 19472 'image/tiff': 'tiff' 19473 }; 19474 return mimes[mime.toLowerCase()] || 'dat'; 19475 }; 19476 const create = (o, blob, base64, name, filename) => { 19477 if (isString(o)) { 19478 const id = o; 19479 return toBlobInfo({ 19480 id, 19481 name, 19482 filename, 19483 blob: blob, 19484 base64: base64 19485 }); 19486 } else if (isObject(o)) { 19487 return toBlobInfo(o); 19488 } else { 19489 throw new Error('Unknown input type'); 19490 } 19491 }; 19492 const toBlobInfo = o => { 19493 if (!o.blob || !o.base64) { 19494 throw new Error('blob and base64 representations of the image are required for BlobInfo to be created'); 19495 } 19496 const id = o.id || uuid('blobid'); 19497 const name = o.name || id; 19498 const blob = o.blob; 19499 return { 19500 id: constant(id), 19501 name: constant(name), 19502 filename: constant(o.filename || name + '.' + mimeToExt(blob.type)), 19503 blob: constant(blob), 19504 base64: constant(o.base64), 19505 blobUri: constant(o.blobUri || URL.createObjectURL(blob)), 19506 uri: constant(o.uri) 19507 }; 19508 }; 19509 const add = blobInfo => { 19510 if (!get(blobInfo.id())) { 19511 cache.push(blobInfo); 19512 } 19513 }; 19514 const findFirst = predicate => find$2(cache, predicate).getOrUndefined(); 19515 const get = id => findFirst(cachedBlobInfo => cachedBlobInfo.id() === id); 19516 const getByUri = blobUri => findFirst(blobInfo => blobInfo.blobUri() === blobUri); 19517 const getByData = (base64, type) => findFirst(blobInfo => blobInfo.base64() === base64 && blobInfo.blob().type === type); 19518 const removeByUri = blobUri => { 19519 cache = filter$5(cache, blobInfo => { 19520 if (blobInfo.blobUri() === blobUri) { 19521 URL.revokeObjectURL(blobInfo.blobUri()); 19522 return false; 19523 } 19524 return true; 19525 }); 19526 }; 19527 const destroy = () => { 19528 each$e(cache, cachedBlobInfo => { 19529 URL.revokeObjectURL(cachedBlobInfo.blobUri()); 19530 }); 19531 cache = []; 19532 }; 19533 return { 19534 create, 19535 add, 19536 get, 19537 getByUri, 19538 getByData, 19539 findFirst, 19540 removeByUri, 19541 destroy 19542 }; 19543 }; 19544 19545 const Uploader = (uploadStatus, settings) => { 19546 const pendingPromises = {}; 19547 const pathJoin = (path1, path2) => { 19548 if (path1) { 19549 return path1.replace(/\/$/, '') + '/' + path2.replace(/^\//, ''); 19550 } 19551 return path2; 19552 }; 19553 const defaultHandler = (blobInfo, progress) => new Promise((success, failure) => { 19554 const xhr = new XMLHttpRequest(); 19555 xhr.open('POST', settings.url); 19556 xhr.withCredentials = settings.credentials; 19557 xhr.upload.onprogress = e => { 19558 progress(e.loaded / e.total * 100); 19559 }; 19560 xhr.onerror = () => { 19561 failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status); 19562 }; 19563 xhr.onload = () => { 19564 if (xhr.status < 200 || xhr.status >= 300) { 19565 failure('HTTP Error: ' + xhr.status); 19566 return; 19567 } 19568 const json = JSON.parse(xhr.responseText); 19569 if (!json || !isString(json.location)) { 19570 failure('Invalid JSON: ' + xhr.responseText); 19571 return; 19572 } 19573 success(pathJoin(settings.basePath, json.location)); 19574 }; 19575 const formData = new FormData(); 19576 formData.append('file', blobInfo.blob(), blobInfo.filename()); 19577 xhr.send(formData); 19578 }); 19579 const uploadHandler = isFunction(settings.handler) ? settings.handler : defaultHandler; 19580 const noUpload = () => new Promise(resolve => { 19581 resolve([]); 19582 }); 19583 const handlerSuccess = (blobInfo, url) => ({ 19584 url, 19585 blobInfo, 19586 status: true 19587 }); 19588 const handlerFailure = (blobInfo, error) => ({ 19589 url: '', 19590 blobInfo, 19591 status: false, 19592 error 19593 }); 19594 const resolvePending = (blobUri, result) => { 19595 Tools.each(pendingPromises[blobUri], resolve => { 19596 resolve(result); 19597 }); 19598 delete pendingPromises[blobUri]; 19599 }; 19600 const uploadBlobInfo = (blobInfo, handler, openNotification) => { 19601 uploadStatus.markPending(blobInfo.blobUri()); 19602 return new Promise(resolve => { 19603 let notification; 19604 let progress; 19605 try { 19606 const closeNotification = () => { 19607 if (notification) { 19608 notification.close(); 19609 progress = noop; 19610 } 19611 }; 19612 const success = url => { 19613 closeNotification(); 19614 uploadStatus.markUploaded(blobInfo.blobUri(), url); 19615 resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url)); 19616 resolve(handlerSuccess(blobInfo, url)); 19617 }; 19618 const failure = error => { 19619 closeNotification(); 19620 uploadStatus.removeFailed(blobInfo.blobUri()); 19621 resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, error)); 19622 resolve(handlerFailure(blobInfo, error)); 19623 }; 19624 progress = percent => { 19625 if (percent < 0 || percent > 100) { 19626 return; 19627 } 19628 Optional.from(notification).orThunk(() => Optional.from(openNotification).map(apply$1)).each(n => { 19629 notification = n; 19630 n.progressBar.value(percent); 19631 }); 19632 }; 19633 handler(blobInfo, progress).then(success, err => { 19634 failure(isString(err) ? { message: err } : err); 19635 }); 19636 } catch (ex) { 19637 resolve(handlerFailure(blobInfo, ex)); 19638 } 19639 }); 19640 }; 19641 const isDefaultHandler = handler => handler === defaultHandler; 19642 const pendingUploadBlobInfo = blobInfo => { 19643 const blobUri = blobInfo.blobUri(); 19644 return new Promise(resolve => { 19645 pendingPromises[blobUri] = pendingPromises[blobUri] || []; 19646 pendingPromises[blobUri].push(resolve); 19647 }); 19648 }; 19649 const uploadBlobs = (blobInfos, openNotification) => { 19650 blobInfos = Tools.grep(blobInfos, blobInfo => !uploadStatus.isUploaded(blobInfo.blobUri())); 19651 return Promise.all(Tools.map(blobInfos, blobInfo => uploadStatus.isPending(blobInfo.blobUri()) ? pendingUploadBlobInfo(blobInfo) : uploadBlobInfo(blobInfo, uploadHandler, openNotification))); 19652 }; 19653 const upload = (blobInfos, openNotification) => !settings.url && isDefaultHandler(uploadHandler) ? noUpload() : uploadBlobs(blobInfos, openNotification); 19654 return { upload }; 19655 }; 19656 19657 const openNotification = editor => () => editor.notificationManager.open({ 19658 text: editor.translate('Image uploading...'), 19659 type: 'info', 19660 timeout: -1, 19661 progressBar: true 19662 }); 19663 const createUploader = (editor, uploadStatus) => Uploader(uploadStatus, { 19664 url: getImageUploadUrl(editor), 19665 basePath: getImageUploadBasePath(editor), 19666 credentials: getImagesUploadCredentials(editor), 19667 handler: getImagesUploadHandler(editor) 19668 }); 19669 const ImageUploader = editor => { 19670 const uploadStatus = UploadStatus(); 19671 const uploader = createUploader(editor, uploadStatus); 19672 return { upload: (blobInfos, showNotification = true) => uploader.upload(blobInfos, showNotification ? openNotification(editor) : undefined) }; 19673 }; 19674 19675 const isEmptyForPadding = (editor, element) => editor.dom.isEmpty(element.dom) && isNonNullable(editor.schema.getTextBlockElements()[name(element)]); 19676 const addPaddingToEmpty = editor => element => { 19677 if (isEmptyForPadding(editor, element)) { 19678 append$1(element, SugarElement.fromHtml('<br data-mce-bogus="1" />')); 19679 } 19680 }; 19681 const EditorUpload = editor => { 19682 const blobCache = BlobCache(); 19683 let uploader, imageScanner; 19684 const uploadStatus = UploadStatus(); 19685 const urlFilters = []; 19686 const aliveGuard = callback => { 19687 return result => { 19688 if (editor.selection) { 19689 return callback(result); 19690 } 19691 return []; 19692 }; 19693 }; 19694 const cacheInvalidator = url => url + (url.indexOf('?') === -1 ? '?' : '&') + new Date().getTime(); 19695 const replaceString = (content, search, replace) => { 19696 let index = 0; 19697 do { 19698 index = content.indexOf(search, index); 19699 if (index !== -1) { 19700 content = content.substring(0, index) + replace + content.substr(index + search.length); 19701 index += replace.length - search.length + 1; 19702 } 19703 } while (index !== -1); 19704 return content; 19705 }; 19706 const replaceImageUrl = (content, targetUrl, replacementUrl) => { 19707 const replacementString = `src="${ replacementUrl }"${ replacementUrl === Env.transparentSrc ? ' data-mce-placeholder="1"' : '' }`; 19708 content = replaceString(content, `src="${ targetUrl }"`, replacementString); 19709 content = replaceString(content, 'data-mce-src="' + targetUrl + '"', 'data-mce-src="' + replacementUrl + '"'); 19710 return content; 19711 }; 19712 const replaceUrlInUndoStack = (targetUrl, replacementUrl) => { 19713 each$e(editor.undoManager.data, level => { 19714 if (level.type === 'fragmented') { 19715 level.fragments = map$3(level.fragments, fragment => replaceImageUrl(fragment, targetUrl, replacementUrl)); 19716 } else { 19717 level.content = replaceImageUrl(level.content, targetUrl, replacementUrl); 19718 } 19719 }); 19720 }; 19721 const replaceImageUriInView = (image, resultUri) => { 19722 const src = editor.convertURL(resultUri, 'src'); 19723 replaceUrlInUndoStack(image.src, resultUri); 19724 setAll$1(SugarElement.fromDom(image), { 19725 'src': shouldReuseFileName(editor) ? cacheInvalidator(resultUri) : resultUri, 19726 'data-mce-src': src 19727 }); 19728 }; 19729 const uploadImages = () => { 19730 if (!uploader) { 19731 uploader = createUploader(editor, uploadStatus); 19732 } 19733 return scanForImages().then(aliveGuard(imageInfos => { 19734 const blobInfos = map$3(imageInfos, imageInfo => imageInfo.blobInfo); 19735 return uploader.upload(blobInfos, openNotification(editor)).then(aliveGuard(result => { 19736 const imagesToRemove = []; 19737 let shouldDispatchChange = false; 19738 const filteredResult = map$3(result, (uploadInfo, index) => { 19739 const {blobInfo, image} = imageInfos[index]; 19740 let removed = false; 19741 if (uploadInfo.status && shouldReplaceBlobUris(editor)) { 19742 if (uploadInfo.url && !contains$1(image.src, uploadInfo.url)) { 19743 shouldDispatchChange = true; 19744 } 19745 blobCache.removeByUri(image.src); 19746 if (isRtc(editor)) ; else { 19747 replaceImageUriInView(image, uploadInfo.url); 19748 } 19749 } else if (uploadInfo.error) { 19750 if (uploadInfo.error.remove) { 19751 replaceUrlInUndoStack(image.src, Env.transparentSrc); 19752 imagesToRemove.push(image); 19753 removed = true; 19754 } 19755 uploadError(editor, uploadInfo.error.message); 19756 } 19757 return { 19758 element: image, 19759 status: uploadInfo.status, 19760 uploadUri: uploadInfo.url, 19761 blobInfo, 19762 removed 19763 }; 19764 }); 19765 if (imagesToRemove.length > 0 && !isRtc(editor)) { 19766 editor.undoManager.transact(() => { 19767 each$e(fromDom$1(imagesToRemove), sugarElement => { 19768 const parentOpt = parent(sugarElement); 19769 remove$5(sugarElement); 19770 parentOpt.each(addPaddingToEmpty(editor)); 19771 blobCache.removeByUri(sugarElement.dom.src); 19772 }); 19773 }); 19774 } else if (shouldDispatchChange) { 19775 editor.undoManager.dispatchChange(); 19776 } 19777 return filteredResult; 19778 })); 19779 })); 19780 }; 19781 const uploadImagesAuto = () => isAutomaticUploadsEnabled(editor) ? uploadImages() : Promise.resolve([]); 19782 const isValidDataUriImage = imgElm => forall(urlFilters, filter => filter(imgElm)); 19783 const addFilter = filter => { 19784 urlFilters.push(filter); 19785 }; 19786 const scanForImages = () => { 19787 if (!imageScanner) { 19788 imageScanner = ImageScanner(uploadStatus, blobCache); 19789 } 19790 return imageScanner.findAll(editor.getBody(), isValidDataUriImage).then(aliveGuard(result => { 19791 const filteredResult = filter$5(result, resultItem => { 19792 if (isString(resultItem)) { 19793 displayError(editor, resultItem); 19794 return false; 19795 } else if (resultItem.uriType === 'blob') { 19796 return false; 19797 } else { 19798 return true; 19799 } 19800 }); 19801 if (isRtc(editor)) ; else { 19802 each$e(filteredResult, resultItem => { 19803 replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri()); 19804 resultItem.image.src = resultItem.blobInfo.blobUri(); 19805 resultItem.image.removeAttribute('data-mce-src'); 19806 }); 19807 } 19808 return filteredResult; 19809 })); 19810 }; 19811 const destroy = () => { 19812 blobCache.destroy(); 19813 uploadStatus.destroy(); 19814 imageScanner = uploader = null; 19815 }; 19816 const replaceBlobUris = content => { 19817 return content.replace(/src="(blob:[^"]+)"/g, (match, blobUri) => { 19818 const resultUri = uploadStatus.getResultUri(blobUri); 19819 if (resultUri) { 19820 return 'src="' + resultUri + '"'; 19821 } 19822 let blobInfo = blobCache.getByUri(blobUri); 19823 if (!blobInfo) { 19824 blobInfo = foldl(editor.editorManager.get(), (result, editor) => { 19825 return result || editor.editorUpload && editor.editorUpload.blobCache.getByUri(blobUri); 19826 }, undefined); 19827 } 19828 if (blobInfo) { 19829 const blob = blobInfo.blob(); 19830 return 'src="data:' + blob.type + ';base64,' + blobInfo.base64() + '"'; 19831 } 19832 return match; 19833 }); 19834 }; 19835 editor.on('SetContent', () => { 19836 if (isAutomaticUploadsEnabled(editor)) { 19837 uploadImagesAuto(); 19838 } else { 19839 scanForImages(); 19840 } 19841 }); 19842 editor.on('RawSaveContent', e => { 19843 e.content = replaceBlobUris(e.content); 19844 }); 19845 editor.on('GetContent', e => { 19846 if (e.source_view || e.format === 'raw' || e.format === 'tree') { 19847 return; 19848 } 19849 e.content = replaceBlobUris(e.content); 19850 }); 19851 editor.on('PostRender', () => { 19852 editor.parser.addNodeFilter('img', images => { 19853 each$e(images, img => { 19854 const src = img.attr('src'); 19855 if (!src || blobCache.getByUri(src)) { 19856 return; 19857 } 19858 const resultUri = uploadStatus.getResultUri(src); 19859 if (resultUri) { 19860 img.attr('src', resultUri); 19861 } 19862 }); 19863 }); 19864 }); 19865 return { 19866 blobCache, 19867 addFilter, 19868 uploadImages, 19869 uploadImagesAuto, 19870 scanForImages, 19871 destroy 19872 }; 19873 }; 19874 19875 const get$1 = editor => { 19876 const dom = editor.dom; 19877 const schemaType = editor.schema.type; 19878 const formats = { 19879 valigntop: [{ 19880 selector: 'td,th', 19881 styles: { verticalAlign: 'top' } 19882 }], 19883 valignmiddle: [{ 19884 selector: 'td,th', 19885 styles: { verticalAlign: 'middle' } 19886 }], 19887 valignbottom: [{ 19888 selector: 'td,th', 19889 styles: { verticalAlign: 'bottom' } 19890 }], 19891 alignleft: [ 19892 { 19893 selector: 'figure.image', 19894 collapsed: false, 19895 classes: 'align-left', 19896 ceFalseOverride: true, 19897 preview: 'font-family font-size' 19898 }, 19899 { 19900 selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre', 19901 styles: { textAlign: 'left' }, 19902 inherit: false, 19903 preview: false 19904 }, 19905 { 19906 selector: 'img,audio,video', 19907 collapsed: false, 19908 styles: { float: 'left' }, 19909 preview: 'font-family font-size' 19910 }, 19911 { 19912 selector: 'table', 19913 collapsed: false, 19914 styles: { 19915 marginLeft: '0px', 19916 marginRight: 'auto' 19917 }, 19918 onformat: table => { 19919 dom.setStyle(table, 'float', null); 19920 }, 19921 preview: 'font-family font-size' 19922 }, 19923 { 19924 selector: '.mce-preview-object,[data-ephox-embed-iri]', 19925 ceFalseOverride: true, 19926 styles: { float: 'left' } 19927 } 19928 ], 19929 aligncenter: [ 19930 { 19931 selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre', 19932 styles: { textAlign: 'center' }, 19933 inherit: false, 19934 preview: 'font-family font-size' 19935 }, 19936 { 19937 selector: 'figure.image', 19938 collapsed: false, 19939 classes: 'align-center', 19940 ceFalseOverride: true, 19941 preview: 'font-family font-size' 19942 }, 19943 { 19944 selector: 'img,audio,video', 19945 collapsed: false, 19946 styles: { 19947 display: 'block', 19948 marginLeft: 'auto', 19949 marginRight: 'auto' 19950 }, 19951 preview: false 19952 }, 19953 { 19954 selector: 'table', 19955 collapsed: false, 19956 styles: { 19957 marginLeft: 'auto', 19958 marginRight: 'auto' 19959 }, 19960 preview: 'font-family font-size' 19961 }, 19962 { 19963 selector: '.mce-preview-object', 19964 ceFalseOverride: true, 19965 styles: { 19966 display: 'table', 19967 marginLeft: 'auto', 19968 marginRight: 'auto' 19969 }, 19970 preview: false 19971 }, 19972 { 19973 selector: '[data-ephox-embed-iri]', 19974 ceFalseOverride: true, 19975 styles: { 19976 marginLeft: 'auto', 19977 marginRight: 'auto' 19978 }, 19979 preview: false 19980 } 19981 ], 19982 alignright: [ 19983 { 19984 selector: 'figure.image', 19985 collapsed: false, 19986 classes: 'align-right', 19987 ceFalseOverride: true, 19988 preview: 'font-family font-size' 19989 }, 19990 { 19991 selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre', 19992 styles: { textAlign: 'right' }, 19993 inherit: false, 19994 preview: 'font-family font-size' 19995 }, 19996 { 19997 selector: 'img,audio,video', 19998 collapsed: false, 19999 styles: { float: 'right' }, 20000 preview: 'font-family font-size' 20001 }, 20002 { 20003 selector: 'table', 20004 collapsed: false, 20005 styles: { 20006 marginRight: '0px', 20007 marginLeft: 'auto' 20008 }, 20009 onformat: table => { 20010 dom.setStyle(table, 'float', null); 20011 }, 20012 preview: 'font-family font-size' 20013 }, 20014 { 20015 selector: '.mce-preview-object,[data-ephox-embed-iri]', 20016 ceFalseOverride: true, 20017 styles: { float: 'right' }, 20018 preview: false 20019 } 20020 ], 20021 alignjustify: [{ 20022 selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre', 20023 styles: { textAlign: 'justify' }, 20024 inherit: false, 20025 preview: 'font-family font-size' 20026 }], 20027 bold: [ 20028 { 20029 inline: 'strong', 20030 remove: 'all', 20031 preserve_attributes: [ 20032 'class', 20033 'style' 20034 ] 20035 }, 20036 { 20037 inline: 'span', 20038 styles: { fontWeight: 'bold' } 20039 }, 20040 { 20041 inline: 'b', 20042 remove: 'all', 20043 preserve_attributes: [ 20044 'class', 20045 'style' 20046 ] 20047 } 20048 ], 20049 italic: [ 20050 { 20051 inline: 'em', 20052 remove: 'all', 20053 preserve_attributes: [ 20054 'class', 20055 'style' 20056 ] 20057 }, 20058 { 20059 inline: 'span', 20060 styles: { fontStyle: 'italic' } 20061 }, 20062 { 20063 inline: 'i', 20064 remove: 'all', 20065 preserve_attributes: [ 20066 'class', 20067 'style' 20068 ] 20069 } 20070 ], 20071 underline: [ 20072 { 20073 inline: 'span', 20074 styles: { textDecoration: 'underline' }, 20075 exact: true 20076 }, 20077 { 20078 inline: 'u', 20079 remove: 'all', 20080 preserve_attributes: [ 20081 'class', 20082 'style' 20083 ] 20084 } 20085 ], 20086 strikethrough: (() => { 20087 const span = { 20088 inline: 'span', 20089 styles: { textDecoration: 'line-through' }, 20090 exact: true 20091 }; 20092 const strike = { 20093 inline: 'strike', 20094 remove: 'all', 20095 preserve_attributes: [ 20096 'class', 20097 'style' 20098 ] 20099 }; 20100 const s = { 20101 inline: 's', 20102 remove: 'all', 20103 preserve_attributes: [ 20104 'class', 20105 'style' 20106 ] 20107 }; 20108 return schemaType !== 'html4' ? [ 20109 s, 20110 span, 20111 strike 20112 ] : [ 20113 span, 20114 s, 20115 strike 20116 ]; 20117 })(), 20118 forecolor: { 20119 inline: 'span', 20120 styles: { color: '%value' }, 20121 links: true, 20122 remove_similar: true, 20123 clear_child_styles: true 20124 }, 20125 hilitecolor: { 20126 inline: 'span', 20127 styles: { backgroundColor: '%value' }, 20128 links: true, 20129 remove_similar: true, 20130 clear_child_styles: true 20131 }, 20132 fontname: { 20133 inline: 'span', 20134 toggle: false, 20135 styles: { fontFamily: '%value' }, 20136 clear_child_styles: true 20137 }, 20138 fontsize: { 20139 inline: 'span', 20140 toggle: false, 20141 styles: { fontSize: '%value' }, 20142 clear_child_styles: true 20143 }, 20144 lineheight: { 20145 selector: 'h1,h2,h3,h4,h5,h6,p,li,td,th,div', 20146 styles: { lineHeight: '%value' } 20147 }, 20148 fontsize_class: { 20149 inline: 'span', 20150 attributes: { class: '%value' } 20151 }, 20152 blockquote: { 20153 block: 'blockquote', 20154 wrapper: true, 20155 remove: 'all' 20156 }, 20157 subscript: { inline: 'sub' }, 20158 superscript: { inline: 'sup' }, 20159 code: { inline: 'code' }, 20160 link: { 20161 inline: 'a', 20162 selector: 'a', 20163 remove: 'all', 20164 split: true, 20165 deep: true, 20166 onmatch: (node, _fmt, _itemName) => { 20167 return isElement$6(node) && node.hasAttribute('href'); 20168 }, 20169 onformat: (elm, _fmt, vars) => { 20170 Tools.each(vars, (value, key) => { 20171 dom.setAttrib(elm, key, value); 20172 }); 20173 } 20174 }, 20175 lang: { 20176 inline: 'span', 20177 clear_child_styles: true, 20178 remove_similar: true, 20179 attributes: { 20180 'lang': '%value', 20181 'data-mce-lang': vars => { 20182 var _a; 20183 return (_a = vars === null || vars === void 0 ? void 0 : vars.customValue) !== null && _a !== void 0 ? _a : null; 20184 } 20185 } 20186 }, 20187 removeformat: [ 20188 { 20189 selector: 'b,strong,em,i,font,u,strike,s,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins,small', 20190 remove: 'all', 20191 split: true, 20192 expand: false, 20193 block_expand: true, 20194 deep: true 20195 }, 20196 { 20197 selector: 'span', 20198 attributes: [ 20199 'style', 20200 'class' 20201 ], 20202 remove: 'empty', 20203 split: true, 20204 expand: false, 20205 deep: true 20206 }, 20207 { 20208 selector: '*', 20209 attributes: [ 20210 'style', 20211 'class' 20212 ], 20213 split: false, 20214 expand: false, 20215 deep: true 20216 } 20217 ] 20218 }; 20219 Tools.each('p h1 h2 h3 h4 h5 h6 div address pre dt dd samp'.split(/\s/), name => { 20220 formats[name] = { 20221 block: name, 20222 remove: 'all' 20223 }; 20224 }); 20225 return formats; 20226 }; 20227 20228 const genericBase = { 20229 remove_similar: true, 20230 inherit: false 20231 }; 20232 const cellBase = { 20233 selector: 'td,th', 20234 ...genericBase 20235 }; 20236 const cellFormats = { 20237 tablecellbackgroundcolor: { 20238 styles: { backgroundColor: '%value' }, 20239 ...cellBase 20240 }, 20241 tablecellverticalalign: { 20242 styles: { 'vertical-align': '%value' }, 20243 ...cellBase 20244 }, 20245 tablecellbordercolor: { 20246 styles: { borderColor: '%value' }, 20247 ...cellBase 20248 }, 20249 tablecellclass: { 20250 classes: ['%value'], 20251 ...cellBase 20252 }, 20253 tableclass: { 20254 selector: 'table', 20255 classes: ['%value'], 20256 ...genericBase 20257 }, 20258 tablecellborderstyle: { 20259 styles: { borderStyle: '%value' }, 20260 ...cellBase 20261 }, 20262 tablecellborderwidth: { 20263 styles: { borderWidth: '%value' }, 20264 ...cellBase 20265 } 20266 }; 20267 const get = constant(cellFormats); 20268 20269 const FormatRegistry = editor => { 20270 const formats = {}; 20271 const get$2 = name => isNonNullable(name) ? formats[name] : formats; 20272 const has = name => has$2(formats, name); 20273 const register = (name, format) => { 20274 if (name) { 20275 if (!isString(name)) { 20276 each$d(name, (format, name) => { 20277 register(name, format); 20278 }); 20279 } else { 20280 if (!isArray$1(format)) { 20281 format = [format]; 20282 } 20283 each$e(format, format => { 20284 if (isUndefined(format.deep)) { 20285 format.deep = !isSelectorFormat(format); 20286 } 20287 if (isUndefined(format.split)) { 20288 format.split = !isSelectorFormat(format) || isInlineFormat(format); 20289 } 20290 if (isUndefined(format.remove) && isSelectorFormat(format) && !isInlineFormat(format)) { 20291 format.remove = 'none'; 20292 } 20293 if (isSelectorFormat(format) && isInlineFormat(format)) { 20294 format.mixed = true; 20295 format.block_expand = true; 20296 } 20297 if (isString(format.classes)) { 20298 format.classes = format.classes.split(/\s+/); 20299 } 20300 }); 20301 formats[name] = format; 20302 } 20303 } 20304 }; 20305 const unregister = name => { 20306 if (name && formats[name]) { 20307 delete formats[name]; 20308 } 20309 return formats; 20310 }; 20311 register(get$1(editor)); 20312 register(get()); 20313 register(getFormats(editor)); 20314 return { 20315 get: get$2, 20316 has, 20317 register, 20318 unregister 20319 }; 20320 }; 20321 20322 const each$3 = Tools.each; 20323 const dom = DOMUtils.DOM; 20324 const isPreviewItem = item => isNonNullable(item) && isObject(item); 20325 const parsedSelectorToHtml = (ancestry, editor) => { 20326 const schema = editor && editor.schema || Schema({}); 20327 const decorate = (elm, item) => { 20328 if (item.classes.length > 0) { 20329 dom.addClass(elm, item.classes.join(' ')); 20330 } 20331 dom.setAttribs(elm, item.attrs); 20332 }; 20333 const createElement = sItem => { 20334 const item = isString(sItem) ? { 20335 name: sItem, 20336 classes: [], 20337 attrs: {} 20338 } : sItem; 20339 const elm = dom.create(item.name); 20340 decorate(elm, item); 20341 return elm; 20342 }; 20343 const getRequiredParent = (elm, candidate) => { 20344 const elmRule = schema.getElementRule(elm.nodeName.toLowerCase()); 20345 const parentsRequired = elmRule === null || elmRule === void 0 ? void 0 : elmRule.parentsRequired; 20346 if (parentsRequired && parentsRequired.length) { 20347 return candidate && contains$2(parentsRequired, candidate) ? candidate : parentsRequired[0]; 20348 } else { 20349 return false; 20350 } 20351 }; 20352 const wrapInHtml = (elm, ancestors, siblings) => { 20353 let parentCandidate; 20354 const ancestor = ancestors[0]; 20355 const ancestorName = isPreviewItem(ancestor) ? ancestor.name : undefined; 20356 const parentRequired = getRequiredParent(elm, ancestorName); 20357 if (parentRequired) { 20358 if (ancestorName === parentRequired) { 20359 parentCandidate = ancestor; 20360 ancestors = ancestors.slice(1); 20361 } else { 20362 parentCandidate = parentRequired; 20363 } 20364 } else if (ancestor) { 20365 parentCandidate = ancestor; 20366 ancestors = ancestors.slice(1); 20367 } else if (!siblings) { 20368 return elm; 20369 } 20370 const parent = parentCandidate ? createElement(parentCandidate) : dom.create('div'); 20371 parent.appendChild(elm); 20372 if (siblings) { 20373 Tools.each(siblings, sibling => { 20374 const siblingElm = createElement(sibling); 20375 parent.insertBefore(siblingElm, elm); 20376 }); 20377 } 20378 const parentSiblings = isPreviewItem(parentCandidate) ? parentCandidate.siblings : undefined; 20379 return wrapInHtml(parent, ancestors, parentSiblings); 20380 }; 20381 const fragment = dom.create('div'); 20382 if (ancestry.length > 0) { 20383 const item = ancestry[0]; 20384 const elm = createElement(item); 20385 const siblings = isPreviewItem(item) ? item.siblings : undefined; 20386 fragment.appendChild(wrapInHtml(elm, ancestry.slice(1), siblings)); 20387 } 20388 return fragment; 20389 }; 20390 const parseSelectorItem = item => { 20391 item = Tools.trim(item); 20392 let tagName = 'div'; 20393 const obj = { 20394 name: tagName, 20395 classes: [], 20396 attrs: {}, 20397 selector: item 20398 }; 20399 if (item !== '*') { 20400 tagName = item.replace(/(?:([#\.]|::?)([\w\-]+)|(\[)([^\]]+)\]?)/g, ($0, $1, $2, $3, $4) => { 20401 switch ($1) { 20402 case '#': 20403 obj.attrs.id = $2; 20404 break; 20405 case '.': 20406 obj.classes.push($2); 20407 break; 20408 case ':': 20409 if (Tools.inArray('checked disabled enabled read-only required'.split(' '), $2) !== -1) { 20410 obj.attrs[$2] = $2; 20411 } 20412 break; 20413 } 20414 if ($3 === '[') { 20415 const m = $4.match(/([\w\-]+)(?:\=\"([^\"]+))?/); 20416 if (m) { 20417 obj.attrs[m[1]] = m[2]; 20418 } 20419 } 20420 return ''; 20421 }); 20422 } 20423 obj.name = tagName || 'div'; 20424 return obj; 20425 }; 20426 const parseSelector = selector => { 20427 if (!isString(selector)) { 20428 return []; 20429 } 20430 selector = selector.split(/\s*,\s*/)[0]; 20431 selector = selector.replace(/\s*(~\+|~|\+|>)\s*/g, '$1'); 20432 return Tools.map(selector.split(/(?:>|\s+(?![^\[\]]+\]))/), item => { 20433 const siblings = Tools.map(item.split(/(?:~\+|~|\+)/), parseSelectorItem); 20434 const obj = siblings.pop(); 20435 if (siblings.length) { 20436 obj.siblings = siblings; 20437 } 20438 return obj; 20439 }).reverse(); 20440 }; 20441 const getCssText = (editor, format) => { 20442 let previewCss = ''; 20443 let previewStyles = getPreviewStyles(editor); 20444 if (previewStyles === '') { 20445 return ''; 20446 } 20447 const removeVars = val => { 20448 return isString(val) ? val.replace(/%(\w+)/g, '') : ''; 20449 }; 20450 const getComputedStyle = (name, elm) => { 20451 return dom.getStyle(elm !== null && elm !== void 0 ? elm : editor.getBody(), name, true); 20452 }; 20453 if (isString(format)) { 20454 const formats = editor.formatter.get(format); 20455 if (!formats) { 20456 return ''; 20457 } 20458 format = formats[0]; 20459 } 20460 if ('preview' in format) { 20461 const preview = format.preview; 20462 if (preview === false) { 20463 return ''; 20464 } else { 20465 previewStyles = preview || previewStyles; 20466 } 20467 } 20468 let name = format.block || format.inline || 'span'; 20469 let previewFrag; 20470 const items = parseSelector(format.selector); 20471 if (items.length > 0) { 20472 if (!items[0].name) { 20473 items[0].name = name; 20474 } 20475 name = format.selector; 20476 previewFrag = parsedSelectorToHtml(items, editor); 20477 } else { 20478 previewFrag = parsedSelectorToHtml([name], editor); 20479 } 20480 const previewElm = dom.select(name, previewFrag)[0] || previewFrag.firstChild; 20481 each$3(format.styles, (value, name) => { 20482 const newValue = removeVars(value); 20483 if (newValue) { 20484 dom.setStyle(previewElm, name, newValue); 20485 } 20486 }); 20487 each$3(format.attributes, (value, name) => { 20488 const newValue = removeVars(value); 20489 if (newValue) { 20490 dom.setAttrib(previewElm, name, newValue); 20491 } 20492 }); 20493 each$3(format.classes, value => { 20494 const newValue = removeVars(value); 20495 if (!dom.hasClass(previewElm, newValue)) { 20496 dom.addClass(previewElm, newValue); 20497 } 20498 }); 20499 editor.dispatch('PreviewFormats'); 20500 dom.setStyles(previewFrag, { 20501 position: 'absolute', 20502 left: -65535 20503 }); 20504 editor.getBody().appendChild(previewFrag); 20505 const rawParentFontSize = getComputedStyle('fontSize'); 20506 const parentFontSize = /px$/.test(rawParentFontSize) ? parseInt(rawParentFontSize, 10) : 0; 20507 each$3(previewStyles.split(' '), name => { 20508 let value = getComputedStyle(name, previewElm); 20509 if (name === 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) { 20510 value = getComputedStyle(name); 20511 if (rgbaToHexString(value).toLowerCase() === '#ffffff') { 20512 return; 20513 } 20514 } 20515 if (name === 'color') { 20516 if (rgbaToHexString(value).toLowerCase() === '#000000') { 20517 return; 20518 } 20519 } 20520 if (name === 'font-size') { 20521 if (/em|%$/.test(value)) { 20522 if (parentFontSize === 0) { 20523 return; 20524 } 20525 const numValue = parseFloat(value) / (/%$/.test(value) ? 100 : 1); 20526 value = numValue * parentFontSize + 'px'; 20527 } 20528 } 20529 if (name === 'border' && value) { 20530 previewCss += 'padding:0 2px;'; 20531 } 20532 previewCss += name + ':' + value + ';'; 20533 }); 20534 editor.dispatch('AfterPreviewFormats'); 20535 dom.remove(previewFrag); 20536 return previewCss; 20537 }; 20538 20539 const setup$r = editor => { 20540 editor.addShortcut('meta+b', '', 'Bold'); 20541 editor.addShortcut('meta+i', '', 'Italic'); 20542 editor.addShortcut('meta+u', '', 'Underline'); 20543 for (let i = 1; i <= 6; i++) { 20544 editor.addShortcut('access+' + i, '', [ 20545 'FormatBlock', 20546 false, 20547 'h' + i 20548 ]); 20549 } 20550 editor.addShortcut('access+7', '', [ 20551 'FormatBlock', 20552 false, 20553 'p' 20554 ]); 20555 editor.addShortcut('access+8', '', [ 20556 'FormatBlock', 20557 false, 20558 'div' 20559 ]); 20560 editor.addShortcut('access+9', '', [ 20561 'FormatBlock', 20562 false, 20563 'address' 20564 ]); 20565 }; 20566 20567 const Formatter = editor => { 20568 const formats = FormatRegistry(editor); 20569 const formatChangeState = Cell({}); 20570 setup$r(editor); 20571 setup$u(editor); 20572 if (!isRtc(editor)) { 20573 setup$t(formatChangeState, editor); 20574 } 20575 return { 20576 get: formats.get, 20577 has: formats.has, 20578 register: formats.register, 20579 unregister: formats.unregister, 20580 apply: (name, vars, node) => { 20581 applyFormat(editor, name, vars, node); 20582 }, 20583 remove: (name, vars, node, similar) => { 20584 removeFormat(editor, name, vars, node, similar); 20585 }, 20586 toggle: (name, vars, node) => { 20587 toggleFormat(editor, name, vars, node); 20588 }, 20589 match: (name, vars, node, similar) => matchFormat(editor, name, vars, node, similar), 20590 closest: names => closestFormat(editor, names), 20591 matchAll: (names, vars) => matchAllFormats(editor, names, vars), 20592 matchNode: (node, name, vars, similar) => matchNodeFormat(editor, node, name, vars, similar), 20593 canApply: name => canApplyFormat(editor, name), 20594 formatChanged: (formats, callback, similar, vars) => formatChanged(editor, formatChangeState, formats, callback, similar, vars), 20595 getCssText: curry(getCssText, editor) 20596 }; 20597 }; 20598 20599 const shouldIgnoreCommand = cmd => { 20600 switch (cmd.toLowerCase()) { 20601 case 'undo': 20602 case 'redo': 20603 case 'mcefocus': 20604 return true; 20605 default: 20606 return false; 20607 } 20608 }; 20609 const registerEvents = (editor, undoManager, locks) => { 20610 const isFirstTypedCharacter = Cell(false); 20611 const addNonTypingUndoLevel = e => { 20612 setTyping(undoManager, false, locks); 20613 undoManager.add({}, e); 20614 }; 20615 editor.on('init', () => { 20616 undoManager.add(); 20617 }); 20618 editor.on('BeforeExecCommand', e => { 20619 const cmd = e.command; 20620 if (!shouldIgnoreCommand(cmd)) { 20621 endTyping(undoManager, locks); 20622 undoManager.beforeChange(); 20623 } 20624 }); 20625 editor.on('ExecCommand', e => { 20626 const cmd = e.command; 20627 if (!shouldIgnoreCommand(cmd)) { 20628 addNonTypingUndoLevel(e); 20629 } 20630 }); 20631 editor.on('ObjectResizeStart cut', () => { 20632 undoManager.beforeChange(); 20633 }); 20634 editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel); 20635 editor.on('dragend', addNonTypingUndoLevel); 20636 editor.on('keyup', e => { 20637 const keyCode = e.keyCode; 20638 if (e.isDefaultPrevented()) { 20639 return; 20640 } 20641 const isMeta = Env.os.isMacOS() && e.key === 'Meta'; 20642 if (keyCode >= 33 && keyCode <= 36 || keyCode >= 37 && keyCode <= 40 || keyCode === 45 || e.ctrlKey || isMeta) { 20643 addNonTypingUndoLevel(); 20644 editor.nodeChanged(); 20645 } 20646 if (keyCode === 46 || keyCode === 8) { 20647 editor.nodeChanged(); 20648 } 20649 if (isFirstTypedCharacter.get() && undoManager.typing && !isEq$1(createFromEditor(editor), undoManager.data[0])) { 20650 if (!editor.isDirty()) { 20651 editor.setDirty(true); 20652 } 20653 editor.dispatch('TypingUndo'); 20654 isFirstTypedCharacter.set(false); 20655 editor.nodeChanged(); 20656 } 20657 }); 20658 editor.on('keydown', e => { 20659 const keyCode = e.keyCode; 20660 if (e.isDefaultPrevented()) { 20661 return; 20662 } 20663 if (keyCode >= 33 && keyCode <= 36 || keyCode >= 37 && keyCode <= 40 || keyCode === 45) { 20664 if (undoManager.typing) { 20665 addNonTypingUndoLevel(e); 20666 } 20667 return; 20668 } 20669 const modKey = e.ctrlKey && !e.altKey || e.metaKey; 20670 if ((keyCode < 16 || keyCode > 20) && keyCode !== 224 && keyCode !== 91 && !undoManager.typing && !modKey) { 20671 undoManager.beforeChange(); 20672 setTyping(undoManager, true, locks); 20673 undoManager.add({}, e); 20674 isFirstTypedCharacter.set(true); 20675 return; 20676 } 20677 const hasOnlyMetaOrCtrlModifier = Env.os.isMacOS() ? e.metaKey : e.ctrlKey && !e.altKey; 20678 if (hasOnlyMetaOrCtrlModifier) { 20679 undoManager.beforeChange(); 20680 } 20681 }); 20682 editor.on('mousedown', e => { 20683 if (undoManager.typing) { 20684 addNonTypingUndoLevel(e); 20685 } 20686 }); 20687 const isInsertReplacementText = event => event.inputType === 'insertReplacementText'; 20688 const isInsertTextDataNull = event => event.inputType === 'insertText' && event.data === null; 20689 const isInsertFromPasteOrDrop = event => event.inputType === 'insertFromPaste' || event.inputType === 'insertFromDrop'; 20690 editor.on('input', e => { 20691 if (e.inputType && (isInsertReplacementText(e) || isInsertTextDataNull(e) || isInsertFromPasteOrDrop(e))) { 20692 addNonTypingUndoLevel(e); 20693 } 20694 }); 20695 editor.on('AddUndo Undo Redo ClearUndos', e => { 20696 if (!e.isDefaultPrevented()) { 20697 editor.nodeChanged(); 20698 } 20699 }); 20700 }; 20701 const addKeyboardShortcuts = editor => { 20702 editor.addShortcut('meta+z', '', 'Undo'); 20703 editor.addShortcut('meta+y,meta+shift+z', '', 'Redo'); 20704 }; 20705 20706 const UndoManager = editor => { 20707 const beforeBookmark = value$2(); 20708 const locks = Cell(0); 20709 const index = Cell(0); 20710 const undoManager = { 20711 data: [], 20712 typing: false, 20713 beforeChange: () => { 20714 beforeChange(editor, locks, beforeBookmark); 20715 }, 20716 add: (level, event) => { 20717 return addUndoLevel(editor, undoManager, index, locks, beforeBookmark, level, event); 20718 }, 20719 dispatchChange: () => { 20720 editor.setDirty(true); 20721 const level = createFromEditor(editor); 20722 level.bookmark = getUndoBookmark(editor.selection); 20723 editor.dispatch('change', { 20724 level, 20725 lastLevel: get$b(undoManager.data, index.get()).getOrUndefined() 20726 }); 20727 }, 20728 undo: () => { 20729 return undo(editor, undoManager, locks, index); 20730 }, 20731 redo: () => { 20732 return redo(editor, index, undoManager.data); 20733 }, 20734 clear: () => { 20735 clear(editor, undoManager, index); 20736 }, 20737 reset: () => { 20738 reset(editor, undoManager); 20739 }, 20740 hasUndo: () => { 20741 return hasUndo(editor, undoManager, index); 20742 }, 20743 hasRedo: () => { 20744 return hasRedo(editor, undoManager, index); 20745 }, 20746 transact: callback => { 20747 return transact(editor, undoManager, locks, callback); 20748 }, 20749 ignore: callback => { 20750 ignore(editor, locks, callback); 20751 }, 20752 extra: (callback1, callback2) => { 20753 extra(editor, undoManager, index, callback1, callback2); 20754 } 20755 }; 20756 if (!isRtc(editor)) { 20757 registerEvents(editor, undoManager, locks); 20758 } 20759 addKeyboardShortcuts(editor); 20760 return undoManager; 20761 }; 20762 20763 const nonTypingKeycodes = [ 20764 9, 20765 27, 20766 VK.HOME, 20767 VK.END, 20768 19, 20769 20, 20770 44, 20771 144, 20772 145, 20773 33, 20774 34, 20775 45, 20776 16, 20777 17, 20778 18, 20779 91, 20780 92, 20781 93, 20782 VK.DOWN, 20783 VK.UP, 20784 VK.LEFT, 20785 VK.RIGHT 20786 ].concat(Env.browser.isFirefox() ? [224] : []); 20787 const placeholderAttr = 'data-mce-placeholder'; 20788 const isKeyboardEvent = e => e.type === 'keydown' || e.type === 'keyup'; 20789 const isDeleteEvent = e => { 20790 const keyCode = e.keyCode; 20791 return keyCode === VK.BACKSPACE || keyCode === VK.DELETE; 20792 }; 20793 const isNonTypingKeyboardEvent = e => { 20794 if (isKeyboardEvent(e)) { 20795 const keyCode = e.keyCode; 20796 return !isDeleteEvent(e) && (VK.metaKeyPressed(e) || e.altKey || keyCode >= 112 && keyCode <= 123 || contains$2(nonTypingKeycodes, keyCode)); 20797 } else { 20798 return false; 20799 } 20800 }; 20801 const isTypingKeyboardEvent = e => isKeyboardEvent(e) && !(isDeleteEvent(e) || e.type === 'keyup' && e.keyCode === 229); 20802 const isVisuallyEmpty = (dom, rootElm, forcedRootBlock) => { 20803 if (isEmpty$2(SugarElement.fromDom(rootElm), false)) { 20804 const firstElement = rootElm.firstElementChild; 20805 if (!firstElement) { 20806 return true; 20807 } else if (dom.getStyle(rootElm.firstElementChild, 'padding-left') || dom.getStyle(rootElm.firstElementChild, 'padding-right')) { 20808 return false; 20809 } else { 20810 return forcedRootBlock === firstElement.nodeName.toLowerCase(); 20811 } 20812 } else { 20813 return false; 20814 } 20815 }; 20816 const setup$q = editor => { 20817 var _a; 20818 const dom = editor.dom; 20819 const rootBlock = getForcedRootBlock(editor); 20820 const placeholder = (_a = getPlaceholder(editor)) !== null && _a !== void 0 ? _a : ''; 20821 const updatePlaceholder = (e, initial) => { 20822 if (isNonTypingKeyboardEvent(e)) { 20823 return; 20824 } 20825 const body = editor.getBody(); 20826 const showPlaceholder = isTypingKeyboardEvent(e) ? false : isVisuallyEmpty(dom, body, rootBlock); 20827 const isPlaceholderShown = dom.getAttrib(body, placeholderAttr) !== ''; 20828 if (isPlaceholderShown !== showPlaceholder || initial) { 20829 dom.setAttrib(body, placeholderAttr, showPlaceholder ? placeholder : null); 20830 dom.setAttrib(body, 'aria-placeholder', showPlaceholder ? placeholder : null); 20831 firePlaceholderToggle(editor, showPlaceholder); 20832 editor.on(showPlaceholder ? 'keydown' : 'keyup', updatePlaceholder); 20833 editor.off(showPlaceholder ? 'keyup' : 'keydown', updatePlaceholder); 20834 } 20835 }; 20836 if (isNotEmpty(placeholder)) { 20837 editor.on('init', e => { 20838 updatePlaceholder(e, true); 20839 editor.on('change SetContent ExecCommand', updatePlaceholder); 20840 editor.on('paste', e => Delay.setEditorTimeout(editor, () => updatePlaceholder(e))); 20841 }); 20842 } 20843 }; 20844 20845 const blockPosition = (block, position) => ({ 20846 block, 20847 position 20848 }); 20849 const blockBoundary = (from, to) => ({ 20850 from, 20851 to 20852 }); 20853 const getBlockPosition = (rootNode, pos) => { 20854 const rootElm = SugarElement.fromDom(rootNode); 20855 const containerElm = SugarElement.fromDom(pos.container()); 20856 return getParentBlock$2(rootElm, containerElm).map(block => blockPosition(block, pos)); 20857 }; 20858 const isDifferentBlocks = blockBoundary => !eq(blockBoundary.from.block, blockBoundary.to.block); 20859 const getClosestHost = (root, scope) => { 20860 const isRoot = node => eq(node, root); 20861 const isHost = node => isTableCell$2(node) || isContentEditableTrue$3(node.dom); 20862 return closest$4(scope, isHost, isRoot).filter(isElement$7).getOr(root); 20863 }; 20864 const hasSameHost = (rootNode, blockBoundary) => { 20865 const root = SugarElement.fromDom(rootNode); 20866 return eq(getClosestHost(root, blockBoundary.from.block), getClosestHost(root, blockBoundary.to.block)); 20867 }; 20868 const isEditable$1 = blockBoundary => isContentEditableFalse$b(blockBoundary.from.block.dom) === false && isContentEditableFalse$b(blockBoundary.to.block.dom) === false; 20869 const hasValidBlocks = blockBoundary => { 20870 const isValidBlock = block => isTextBlock$2(block) || hasBlockAttr(block.dom); 20871 return isValidBlock(blockBoundary.from.block) && isValidBlock(blockBoundary.to.block); 20872 }; 20873 const skipLastBr = (rootNode, forward, blockPosition) => { 20874 if (isBr$6(blockPosition.position.getNode()) && !isEmpty$2(blockPosition.block)) { 20875 return positionIn(false, blockPosition.block.dom).bind(lastPositionInBlock => { 20876 if (lastPositionInBlock.isEqual(blockPosition.position)) { 20877 return fromPosition(forward, rootNode, lastPositionInBlock).bind(to => getBlockPosition(rootNode, to)); 20878 } else { 20879 return Optional.some(blockPosition); 20880 } 20881 }).getOr(blockPosition); 20882 } else { 20883 return blockPosition; 20884 } 20885 }; 20886 const readFromRange = (rootNode, forward, rng) => { 20887 const fromBlockPos = getBlockPosition(rootNode, CaretPosition.fromRangeStart(rng)); 20888 const toBlockPos = fromBlockPos.bind(blockPos => fromPosition(forward, rootNode, blockPos.position).bind(to => getBlockPosition(rootNode, to).map(blockPos => skipLastBr(rootNode, forward, blockPos)))); 20889 return lift2(fromBlockPos, toBlockPos, blockBoundary).filter(blockBoundary => isDifferentBlocks(blockBoundary) && hasSameHost(rootNode, blockBoundary) && isEditable$1(blockBoundary) && hasValidBlocks(blockBoundary)); 20890 }; 20891 const read$1 = (rootNode, forward, rng) => rng.collapsed ? readFromRange(rootNode, forward, rng) : Optional.none(); 20892 20893 const getChildrenUntilBlockBoundary = block => { 20894 const children = children$1(block); 20895 return findIndex$2(children, isBlock$2).fold(constant(children), index => children.slice(0, index)); 20896 }; 20897 const extractChildren = block => { 20898 const children = getChildrenUntilBlockBoundary(block); 20899 each$e(children, remove$5); 20900 return children; 20901 }; 20902 const removeEmptyRoot = (rootNode, block) => { 20903 const parents = parentsAndSelf(block, rootNode); 20904 return find$2(parents.reverse(), element => isEmpty$2(element)).each(remove$5); 20905 }; 20906 const isEmptyBefore = el => filter$5(prevSiblings(el), el => !isEmpty$2(el)).length === 0; 20907 const nestedBlockMerge = (rootNode, fromBlock, toBlock, insertionPoint) => { 20908 if (isEmpty$2(toBlock)) { 20909 fillWithPaddingBr(toBlock); 20910 return firstPositionIn(toBlock.dom); 20911 } 20912 if (isEmptyBefore(insertionPoint) && isEmpty$2(fromBlock)) { 20913 before$3(insertionPoint, SugarElement.fromTag('br')); 20914 } 20915 const position = prevPosition(toBlock.dom, CaretPosition.before(insertionPoint.dom)); 20916 each$e(extractChildren(fromBlock), child => { 20917 before$3(insertionPoint, child); 20918 }); 20919 removeEmptyRoot(rootNode, fromBlock); 20920 return position; 20921 }; 20922 const sidelongBlockMerge = (rootNode, fromBlock, toBlock) => { 20923 if (isEmpty$2(toBlock)) { 20924 if (isEmpty$2(fromBlock)) { 20925 const getInlineToBlockDescendants = el => { 20926 const helper = (node, elements) => firstChild(node).fold(() => elements, child => isInline$1(child) ? helper(child, elements.concat(shallow$1(child))) : elements); 20927 return helper(el, []); 20928 }; 20929 const newFromBlockDescendants = foldr(getInlineToBlockDescendants(toBlock), (element, descendant) => { 20930 wrap$2(element, descendant); 20931 return descendant; 20932 }, createPaddingBr()); 20933 empty(fromBlock); 20934 append$1(fromBlock, newFromBlockDescendants); 20935 } 20936 remove$5(toBlock); 20937 return firstPositionIn(fromBlock.dom); 20938 } 20939 const position = lastPositionIn(toBlock.dom); 20940 each$e(extractChildren(fromBlock), child => { 20941 append$1(toBlock, child); 20942 }); 20943 removeEmptyRoot(rootNode, fromBlock); 20944 return position; 20945 }; 20946 const findInsertionPoint = (toBlock, block) => { 20947 const parentsAndSelf$1 = parentsAndSelf(block, toBlock); 20948 return Optional.from(parentsAndSelf$1[parentsAndSelf$1.length - 1]); 20949 }; 20950 const getInsertionPoint = (fromBlock, toBlock) => contains(toBlock, fromBlock) ? findInsertionPoint(toBlock, fromBlock) : Optional.none(); 20951 const trimBr = (first, block) => { 20952 positionIn(first, block.dom).bind(position => Optional.from(position.getNode())).map(SugarElement.fromDom).filter(isBr$5).each(remove$5); 20953 }; 20954 const mergeBlockInto = (rootNode, fromBlock, toBlock) => { 20955 trimBr(true, fromBlock); 20956 trimBr(false, toBlock); 20957 return getInsertionPoint(fromBlock, toBlock).fold(curry(sidelongBlockMerge, rootNode, fromBlock, toBlock), curry(nestedBlockMerge, rootNode, fromBlock, toBlock)); 20958 }; 20959 const mergeBlocks = (rootNode, forward, block1, block2) => forward ? mergeBlockInto(rootNode, block2, block1) : mergeBlockInto(rootNode, block1, block2); 20960 20961 const backspaceDelete$8 = (editor, forward) => { 20962 const rootNode = SugarElement.fromDom(editor.getBody()); 20963 const position = read$1(rootNode.dom, forward, editor.selection.getRng()).map(blockBoundary => () => { 20964 mergeBlocks(rootNode, forward, blockBoundary.from.block, blockBoundary.to.block).each(pos => { 20965 editor.selection.setRng(pos.toRange()); 20966 }); 20967 }); 20968 return position; 20969 }; 20970 20971 const deleteRangeMergeBlocks = (rootNode, selection) => { 20972 const rng = selection.getRng(); 20973 return lift2(getParentBlock$2(rootNode, SugarElement.fromDom(rng.startContainer)), getParentBlock$2(rootNode, SugarElement.fromDom(rng.endContainer)), (block1, block2) => { 20974 if (!eq(block1, block2)) { 20975 return Optional.some(() => { 20976 rng.deleteContents(); 20977 mergeBlocks(rootNode, true, block1, block2).each(pos => { 20978 selection.setRng(pos.toRange()); 20979 }); 20980 }); 20981 } else { 20982 return Optional.none(); 20983 } 20984 }).getOr(Optional.none()); 20985 }; 20986 const isRawNodeInTable = (root, rawNode) => { 20987 const node = SugarElement.fromDom(rawNode); 20988 const isRoot = curry(eq, root); 20989 return ancestor$4(node, isTableCell$2, isRoot).isSome(); 20990 }; 20991 const isSelectionInTable = (root, rng) => isRawNodeInTable(root, rng.startContainer) || isRawNodeInTable(root, rng.endContainer); 20992 const isEverythingSelected = (root, rng) => { 20993 const noPrevious = prevPosition(root.dom, CaretPosition.fromRangeStart(rng)).isNone(); 20994 const noNext = nextPosition(root.dom, CaretPosition.fromRangeEnd(rng)).isNone(); 20995 return !isSelectionInTable(root, rng) && noPrevious && noNext; 20996 }; 20997 const emptyEditor = editor => { 20998 return Optional.some(() => { 20999 editor.setContent(''); 21000 editor.selection.setCursorLocation(); 21001 }); 21002 }; 21003 const deleteRange$2 = editor => { 21004 const rootNode = SugarElement.fromDom(editor.getBody()); 21005 const rng = editor.selection.getRng(); 21006 return isEverythingSelected(rootNode, rng) ? emptyEditor(editor) : deleteRangeMergeBlocks(rootNode, editor.selection); 21007 }; 21008 const backspaceDelete$7 = (editor, _forward) => editor.selection.isCollapsed() ? Optional.none() : deleteRange$2(editor); 21009 21010 const showCaret = (direction, editor, node, before, scrollIntoView) => Optional.from(editor._selectionOverrides.showCaret(direction, node, before, scrollIntoView)); 21011 const getNodeRange = node => { 21012 const rng = node.ownerDocument.createRange(); 21013 rng.selectNode(node); 21014 return rng; 21015 }; 21016 const selectNode = (editor, node) => { 21017 const e = editor.dispatch('BeforeObjectSelected', { target: node }); 21018 if (e.isDefaultPrevented()) { 21019 return Optional.none(); 21020 } 21021 return Optional.some(getNodeRange(node)); 21022 }; 21023 const renderCaretAtRange = (editor, range, scrollIntoView) => { 21024 const normalizedRange = normalizeRange(1, editor.getBody(), range); 21025 const caretPosition = CaretPosition.fromRangeStart(normalizedRange); 21026 const caretPositionNode = caretPosition.getNode(); 21027 if (isInlineFakeCaretTarget(caretPositionNode)) { 21028 return showCaret(1, editor, caretPositionNode, !caretPosition.isAtEnd(), false); 21029 } 21030 const caretPositionBeforeNode = caretPosition.getNode(true); 21031 if (isInlineFakeCaretTarget(caretPositionBeforeNode)) { 21032 return showCaret(1, editor, caretPositionBeforeNode, false, false); 21033 } 21034 const ceRoot = getContentEditableRoot$1(editor.dom.getRoot(), caretPosition.getNode()); 21035 if (isInlineFakeCaretTarget(ceRoot)) { 21036 return showCaret(1, editor, ceRoot, false, scrollIntoView); 21037 } 21038 return Optional.none(); 21039 }; 21040 const renderRangeCaret = (editor, range, scrollIntoView) => range.collapsed ? renderCaretAtRange(editor, range, scrollIntoView).getOr(range) : range; 21041 21042 const isBeforeBoundary = pos => isBeforeContentEditableFalse(pos) || isBeforeMedia(pos); 21043 const isAfterBoundary = pos => isAfterContentEditableFalse(pos) || isAfterMedia(pos); 21044 const trimEmptyTextNode = (dom, node) => { 21045 if (isText$a(node) && node.data.length === 0) { 21046 dom.remove(node); 21047 } 21048 }; 21049 const deleteContentAndShowCaret = (editor, range, node, direction, forward, peekCaretPosition) => { 21050 showCaret(direction, editor, peekCaretPosition.getNode(!forward), forward, true).each(caretRange => { 21051 if (range.collapsed) { 21052 const deleteRange = range.cloneRange(); 21053 if (forward) { 21054 deleteRange.setEnd(caretRange.startContainer, caretRange.startOffset); 21055 } else { 21056 deleteRange.setStart(caretRange.endContainer, caretRange.endOffset); 21057 } 21058 deleteRange.deleteContents(); 21059 } else { 21060 range.deleteContents(); 21061 } 21062 editor.selection.setRng(caretRange); 21063 }); 21064 trimEmptyTextNode(editor.dom, node); 21065 }; 21066 const deleteBoundaryText = (editor, forward) => { 21067 const range = editor.selection.getRng(); 21068 if (!isText$a(range.commonAncestorContainer)) { 21069 return Optional.none(); 21070 } 21071 const direction = forward ? HDirection.Forwards : HDirection.Backwards; 21072 const caretWalker = CaretWalker(editor.getBody()); 21073 const getNextPosFn = curry(getVisualCaretPosition, forward ? caretWalker.next : caretWalker.prev); 21074 const isBeforeFn = forward ? isBeforeBoundary : isAfterBoundary; 21075 const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range); 21076 const nextCaretPosition = getNextPosFn(caretPosition); 21077 const normalizedNextCaretPosition = nextCaretPosition ? normalizePosition(forward, nextCaretPosition) : nextCaretPosition; 21078 if (!normalizedNextCaretPosition || !isMoveInsideSameBlock(caretPosition, normalizedNextCaretPosition)) { 21079 return Optional.none(); 21080 } else if (isBeforeFn(normalizedNextCaretPosition)) { 21081 return Optional.some(() => deleteContentAndShowCaret(editor, range, caretPosition.getNode(), direction, forward, normalizedNextCaretPosition)); 21082 } 21083 const peekCaretPosition = getNextPosFn(normalizedNextCaretPosition); 21084 if (peekCaretPosition && isBeforeFn(peekCaretPosition)) { 21085 if (isMoveInsideSameBlock(normalizedNextCaretPosition, peekCaretPosition)) { 21086 return Optional.some(() => deleteContentAndShowCaret(editor, range, caretPosition.getNode(), direction, forward, peekCaretPosition)); 21087 } 21088 } 21089 return Optional.none(); 21090 }; 21091 const backspaceDelete$6 = (editor, forward) => deleteBoundaryText(editor, forward); 21092 21093 const getEdgeCefPosition = (editor, atStart) => { 21094 const root = editor.getBody(); 21095 return atStart ? firstPositionIn(root).filter(isBeforeContentEditableFalse) : lastPositionIn(root).filter(isAfterContentEditableFalse); 21096 }; 21097 const isCefAtEdgeSelected = editor => { 21098 const rng = editor.selection.getRng(); 21099 return !rng.collapsed && (getEdgeCefPosition(editor, true).exists(pos => pos.isEqual(CaretPosition.fromRangeStart(rng))) || getEdgeCefPosition(editor, false).exists(pos => pos.isEqual(CaretPosition.fromRangeEnd(rng)))); 21100 }; 21101 21102 const isCompoundElement = node => isNonNullable(node) && (isTableCell$2(SugarElement.fromDom(node)) || isListItem$1(SugarElement.fromDom(node))); 21103 const DeleteAction = Adt.generate([ 21104 { remove: ['element'] }, 21105 { moveToElement: ['element'] }, 21106 { moveToPosition: ['position'] } 21107 ]); 21108 const isAtContentEditableBlockCaret = (forward, from) => { 21109 const elm = from.getNode(!forward); 21110 const caretLocation = forward ? 'after' : 'before'; 21111 return isElement$6(elm) && elm.getAttribute('data-mce-caret') === caretLocation; 21112 }; 21113 const isDeleteFromCefDifferentBlocks = (root, forward, from, to) => { 21114 const inSameBlock = elm => isInline$1(SugarElement.fromDom(elm)) && !isInSameBlock(from, to, root); 21115 return getRelativeCefElm(!forward, from).fold(() => getRelativeCefElm(forward, to).fold(never, inSameBlock), inSameBlock); 21116 }; 21117 const deleteEmptyBlockOrMoveToCef = (root, forward, from, to) => { 21118 const toCefElm = to.getNode(!forward); 21119 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))); 21120 }; 21121 const findCefPosition = (root, forward, from) => fromPosition(forward, root, from).bind(to => { 21122 if (isCompoundElement(to.getNode())) { 21123 return Optional.none(); 21124 } else if (isDeleteFromCefDifferentBlocks(root, forward, from, to)) { 21125 return Optional.none(); 21126 } else if (forward && isContentEditableFalse$b(to.getNode())) { 21127 return deleteEmptyBlockOrMoveToCef(root, forward, from, to); 21128 } else if (!forward && isContentEditableFalse$b(to.getNode(true))) { 21129 return deleteEmptyBlockOrMoveToCef(root, forward, from, to); 21130 } else if (forward && isAfterContentEditableFalse(from)) { 21131 return Optional.some(DeleteAction.moveToPosition(to)); 21132 } else if (!forward && isBeforeContentEditableFalse(from)) { 21133 return Optional.some(DeleteAction.moveToPosition(to)); 21134 } else { 21135 return Optional.none(); 21136 } 21137 }); 21138 const getContentEditableBlockAction = (forward, elm) => { 21139 if (isNullable(elm)) { 21140 return Optional.none(); 21141 } else if (forward && isContentEditableFalse$b(elm.nextSibling)) { 21142 return Optional.some(DeleteAction.moveToElement(elm.nextSibling)); 21143 } else if (!forward && isContentEditableFalse$b(elm.previousSibling)) { 21144 return Optional.some(DeleteAction.moveToElement(elm.previousSibling)); 21145 } else { 21146 return Optional.none(); 21147 } 21148 }; 21149 const skipMoveToActionFromInlineCefToContent = (root, from, deleteAction) => deleteAction.fold(elm => Optional.some(DeleteAction.remove(elm)), elm => Optional.some(DeleteAction.moveToElement(elm)), to => { 21150 if (isInSameBlock(from, to, root)) { 21151 return Optional.none(); 21152 } else { 21153 return Optional.some(DeleteAction.moveToPosition(to)); 21154 } 21155 }); 21156 const getContentEditableAction = (root, forward, from) => { 21157 if (isAtContentEditableBlockCaret(forward, from)) { 21158 return getContentEditableBlockAction(forward, from.getNode(!forward)).orThunk(() => findCefPosition(root, forward, from)); 21159 } else { 21160 return findCefPosition(root, forward, from).bind(deleteAction => skipMoveToActionFromInlineCefToContent(root, from, deleteAction)); 21161 } 21162 }; 21163 const read = (root, forward, rng) => { 21164 const normalizedRange = normalizeRange(forward ? 1 : -1, root, rng); 21165 const from = CaretPosition.fromRangeStart(normalizedRange); 21166 const rootElement = SugarElement.fromDom(root); 21167 if (!forward && isAfterContentEditableFalse(from)) { 21168 return Optional.some(DeleteAction.remove(from.getNode(true))); 21169 } else if (forward && isBeforeContentEditableFalse(from)) { 21170 return Optional.some(DeleteAction.remove(from.getNode())); 21171 } else if (!forward && isBeforeContentEditableFalse(from) && isAfterBr(rootElement, from)) { 21172 return findPreviousBr(rootElement, from).map(br => DeleteAction.remove(br.getNode())); 21173 } else if (forward && isAfterContentEditableFalse(from) && isBeforeBr$1(rootElement, from)) { 21174 return findNextBr(rootElement, from).map(br => DeleteAction.remove(br.getNode())); 21175 } else { 21176 return getContentEditableAction(root, forward, from); 21177 } 21178 }; 21179 21180 const deleteElement$1 = (editor, forward) => element => { 21181 editor._selectionOverrides.hideFakeCaret(); 21182 deleteElement$2(editor, forward, SugarElement.fromDom(element)); 21183 return true; 21184 }; 21185 const moveToElement = (editor, forward) => element => { 21186 const pos = forward ? CaretPosition.before(element) : CaretPosition.after(element); 21187 editor.selection.setRng(pos.toRange()); 21188 return true; 21189 }; 21190 const moveToPosition = editor => pos => { 21191 editor.selection.setRng(pos.toRange()); 21192 return true; 21193 }; 21194 const getAncestorCe = (editor, node) => Optional.from(getContentEditableRoot$1(editor.getBody(), node)); 21195 const backspaceDeleteCaret = (editor, forward) => { 21196 const selectedNode = editor.selection.getNode(); 21197 return getAncestorCe(editor, selectedNode).filter(isContentEditableFalse$b).fold(() => read(editor.getBody(), forward, editor.selection.getRng()).map(deleteAction => () => deleteAction.fold(deleteElement$1(editor, forward), moveToElement(editor, forward), moveToPosition(editor))), () => Optional.some(noop)); 21198 }; 21199 const deleteOffscreenSelection = rootElement => { 21200 each$e(descendants(rootElement, '.mce-offscreen-selection'), remove$5); 21201 }; 21202 const backspaceDeleteRange = (editor, forward) => { 21203 const selectedNode = editor.selection.getNode(); 21204 if (isContentEditableFalse$b(selectedNode) && !isTableCell$3(selectedNode)) { 21205 const hasCefAncestor = getAncestorCe(editor, selectedNode.parentNode).filter(isContentEditableFalse$b); 21206 return hasCefAncestor.fold(() => Optional.some(() => { 21207 deleteOffscreenSelection(SugarElement.fromDom(editor.getBody())); 21208 deleteElement$2(editor, forward, SugarElement.fromDom(editor.selection.getNode())); 21209 paddEmptyBody(editor); 21210 }), () => Optional.some(noop)); 21211 } 21212 if (isCefAtEdgeSelected(editor)) { 21213 return Optional.some(() => { 21214 deleteRangeContents(editor, editor.selection.getRng(), SugarElement.fromDom(editor.getBody())); 21215 }); 21216 } 21217 return Optional.none(); 21218 }; 21219 const paddEmptyElement = editor => { 21220 const dom = editor.dom, selection = editor.selection; 21221 const ceRoot = getContentEditableRoot$1(editor.getBody(), selection.getNode()); 21222 if (isContentEditableTrue$3(ceRoot) && dom.isBlock(ceRoot) && dom.isEmpty(ceRoot)) { 21223 const br = dom.create('br', { 'data-mce-bogus': '1' }); 21224 dom.setHTML(ceRoot, ''); 21225 ceRoot.appendChild(br); 21226 selection.setRng(CaretPosition.before(br).toRange()); 21227 } 21228 return true; 21229 }; 21230 const backspaceDelete$5 = (editor, forward) => { 21231 if (editor.selection.isCollapsed()) { 21232 return backspaceDeleteCaret(editor, forward); 21233 } else { 21234 return backspaceDeleteRange(editor, forward); 21235 } 21236 }; 21237 21238 const deleteCaret$2 = (editor, forward) => { 21239 const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng()); 21240 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)); 21241 }; 21242 const backspaceDelete$4 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$2(editor, forward) : Optional.none(); 21243 21244 const isText$2 = isText$a; 21245 const startsWithCaretContainer = node => isText$2(node) && node.data[0] === ZWSP$1; 21246 const endsWithCaretContainer = node => isText$2(node) && node.data[node.data.length - 1] === ZWSP$1; 21247 const createZwsp = node => { 21248 var _a; 21249 const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document; 21250 return doc.createTextNode(ZWSP$1); 21251 }; 21252 const insertBefore = node => { 21253 var _a; 21254 if (isText$2(node.previousSibling)) { 21255 if (endsWithCaretContainer(node.previousSibling)) { 21256 return node.previousSibling; 21257 } else { 21258 node.previousSibling.appendData(ZWSP$1); 21259 return node.previousSibling; 21260 } 21261 } else if (isText$2(node)) { 21262 if (startsWithCaretContainer(node)) { 21263 return node; 21264 } else { 21265 node.insertData(0, ZWSP$1); 21266 return node; 21267 } 21268 } else { 21269 const newNode = createZwsp(node); 21270 (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(newNode, node); 21271 return newNode; 21272 } 21273 }; 21274 const insertAfter = node => { 21275 var _a, _b; 21276 if (isText$2(node.nextSibling)) { 21277 if (startsWithCaretContainer(node.nextSibling)) { 21278 return node.nextSibling; 21279 } else { 21280 node.nextSibling.insertData(0, ZWSP$1); 21281 return node.nextSibling; 21282 } 21283 } else if (isText$2(node)) { 21284 if (endsWithCaretContainer(node)) { 21285 return node; 21286 } else { 21287 node.appendData(ZWSP$1); 21288 return node; 21289 } 21290 } else { 21291 const newNode = createZwsp(node); 21292 if (node.nextSibling) { 21293 (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(newNode, node.nextSibling); 21294 } else { 21295 (_b = node.parentNode) === null || _b === void 0 ? void 0 : _b.appendChild(newNode); 21296 } 21297 return newNode; 21298 } 21299 }; 21300 const insertInline = (before, node) => before ? insertBefore(node) : insertAfter(node); 21301 const insertInlineBefore = curry(insertInline, true); 21302 const insertInlineAfter = curry(insertInline, false); 21303 21304 const insertInlinePos = (pos, before) => { 21305 if (isText$a(pos.container())) { 21306 return insertInline(before, pos.container()); 21307 } else { 21308 return insertInline(before, pos.getNode()); 21309 } 21310 }; 21311 const isPosCaretContainer = (pos, caret) => { 21312 const caretNode = caret.get(); 21313 return caretNode && pos.container() === caretNode && isCaretContainerInline(caretNode); 21314 }; 21315 const renderCaret = (caret, location) => location.fold(element => { 21316 remove$3(caret.get()); 21317 const text = insertInlineBefore(element); 21318 caret.set(text); 21319 return Optional.some(CaretPosition(text, text.length - 1)); 21320 }, element => firstPositionIn(element).map(pos => { 21321 if (!isPosCaretContainer(pos, caret)) { 21322 remove$3(caret.get()); 21323 const text = insertInlinePos(pos, true); 21324 caret.set(text); 21325 return CaretPosition(text, 1); 21326 } else { 21327 const node = caret.get(); 21328 return CaretPosition(node, 1); 21329 } 21330 }), element => lastPositionIn(element).map(pos => { 21331 if (!isPosCaretContainer(pos, caret)) { 21332 remove$3(caret.get()); 21333 const text = insertInlinePos(pos, false); 21334 caret.set(text); 21335 return CaretPosition(text, text.length - 1); 21336 } else { 21337 const node = caret.get(); 21338 return CaretPosition(node, node.length - 1); 21339 } 21340 }), element => { 21341 remove$3(caret.get()); 21342 const text = insertInlineAfter(element); 21343 caret.set(text); 21344 return Optional.some(CaretPosition(text, 1)); 21345 }); 21346 21347 const evaluateUntil = (fns, args) => { 21348 for (let i = 0; i < fns.length; i++) { 21349 const result = fns[i].apply(null, args); 21350 if (result.isSome()) { 21351 return result; 21352 } 21353 } 21354 return Optional.none(); 21355 }; 21356 21357 const Location = Adt.generate([ 21358 { before: ['element'] }, 21359 { start: ['element'] }, 21360 { end: ['element'] }, 21361 { after: ['element'] } 21362 ]); 21363 const rescope$1 = (rootNode, node) => { 21364 const parentBlock = getParentBlock$3(node, rootNode); 21365 return parentBlock ? parentBlock : rootNode; 21366 }; 21367 const before = (isInlineTarget, rootNode, pos) => { 21368 const nPos = normalizeForwards(pos); 21369 const scope = rescope$1(rootNode, nPos.container()); 21370 return findRootInline(isInlineTarget, scope, nPos).fold(() => nextPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget, scope)).map(inline => Location.before(inline)), Optional.none); 21371 }; 21372 const isNotInsideFormatCaretContainer = (rootNode, elm) => getParentCaretContainer(rootNode, elm) === null; 21373 const findInsideRootInline = (isInlineTarget, rootNode, pos) => findRootInline(isInlineTarget, rootNode, pos).filter(curry(isNotInsideFormatCaretContainer, rootNode)); 21374 const start$1 = (isInlineTarget, rootNode, pos) => { 21375 const nPos = normalizeBackwards(pos); 21376 return findInsideRootInline(isInlineTarget, rootNode, nPos).bind(inline => { 21377 const prevPos = prevPosition(inline, nPos); 21378 return prevPos.isNone() ? Optional.some(Location.start(inline)) : Optional.none(); 21379 }); 21380 }; 21381 const end = (isInlineTarget, rootNode, pos) => { 21382 const nPos = normalizeForwards(pos); 21383 return findInsideRootInline(isInlineTarget, rootNode, nPos).bind(inline => { 21384 const nextPos = nextPosition(inline, nPos); 21385 return nextPos.isNone() ? Optional.some(Location.end(inline)) : Optional.none(); 21386 }); 21387 }; 21388 const after = (isInlineTarget, rootNode, pos) => { 21389 const nPos = normalizeBackwards(pos); 21390 const scope = rescope$1(rootNode, nPos.container()); 21391 return findRootInline(isInlineTarget, scope, nPos).fold(() => prevPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget, scope)).map(inline => Location.after(inline)), Optional.none); 21392 }; 21393 const isValidLocation = location => !isRtl(getElement(location)); 21394 const readLocation = (isInlineTarget, rootNode, pos) => { 21395 const location = evaluateUntil([ 21396 before, 21397 start$1, 21398 end, 21399 after 21400 ], [ 21401 isInlineTarget, 21402 rootNode, 21403 pos 21404 ]); 21405 return location.filter(isValidLocation); 21406 }; 21407 const getElement = location => location.fold(identity, identity, identity, identity); 21408 const getName = location => location.fold(constant('before'), constant('start'), constant('end'), constant('after')); 21409 const outside = location => location.fold(Location.before, Location.before, Location.after, Location.after); 21410 const inside = location => location.fold(Location.start, Location.start, Location.end, Location.end); 21411 const isEq = (location1, location2) => getName(location1) === getName(location2) && getElement(location1) === getElement(location2); 21412 const betweenInlines = (forward, isInlineTarget, rootNode, from, to, location) => lift2(findRootInline(isInlineTarget, rootNode, from), findRootInline(isInlineTarget, rootNode, to), (fromInline, toInline) => { 21413 if (fromInline !== toInline && hasSameParentBlock(rootNode, fromInline, toInline)) { 21414 return Location.after(forward ? fromInline : toInline); 21415 } else { 21416 return location; 21417 } 21418 }).getOr(location); 21419 const skipNoMovement = (fromLocation, toLocation) => fromLocation.fold(always, fromLocation => !isEq(fromLocation, toLocation)); 21420 const findLocationTraverse = (forward, isInlineTarget, rootNode, fromLocation, pos) => { 21421 const from = normalizePosition(forward, pos); 21422 const to = fromPosition(forward, rootNode, from).map(curry(normalizePosition, forward)); 21423 const location = to.fold(() => fromLocation.map(outside), to => readLocation(isInlineTarget, rootNode, to).map(curry(betweenInlines, forward, isInlineTarget, rootNode, from, to)).filter(curry(skipNoMovement, fromLocation))); 21424 return location.filter(isValidLocation); 21425 }; 21426 const findLocationSimple = (forward, location) => { 21427 if (forward) { 21428 return location.fold(compose(Optional.some, Location.start), Optional.none, compose(Optional.some, Location.after), Optional.none); 21429 } else { 21430 return location.fold(Optional.none, compose(Optional.some, Location.before), Optional.none, compose(Optional.some, Location.end)); 21431 } 21432 }; 21433 const findLocation$1 = (forward, isInlineTarget, rootNode, pos) => { 21434 const from = normalizePosition(forward, pos); 21435 const fromLocation = readLocation(isInlineTarget, rootNode, from); 21436 return readLocation(isInlineTarget, rootNode, from).bind(curry(findLocationSimple, forward)).orThunk(() => findLocationTraverse(forward, isInlineTarget, rootNode, fromLocation, pos)); 21437 }; 21438 21439 const hasSelectionModifyApi = editor => { 21440 return isFunction(editor.selection.getSel().modify); 21441 }; 21442 const moveRel = (forward, selection, pos) => { 21443 const delta = forward ? 1 : -1; 21444 selection.setRng(CaretPosition(pos.container(), pos.offset() + delta).toRange()); 21445 selection.getSel().modify('move', forward ? 'forward' : 'backward', 'word'); 21446 return true; 21447 }; 21448 const moveByWord = (forward, editor) => { 21449 const rng = editor.selection.getRng(); 21450 const pos = forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng); 21451 if (!hasSelectionModifyApi(editor)) { 21452 return false; 21453 } else if (forward && isBeforeInline(pos)) { 21454 return moveRel(true, editor.selection, pos); 21455 } else if (!forward && isAfterInline(pos)) { 21456 return moveRel(false, editor.selection, pos); 21457 } else { 21458 return false; 21459 } 21460 }; 21461 21462 var BreakType; 21463 (function (BreakType) { 21464 BreakType[BreakType['Br'] = 0] = 'Br'; 21465 BreakType[BreakType['Block'] = 1] = 'Block'; 21466 BreakType[BreakType['Wrap'] = 2] = 'Wrap'; 21467 BreakType[BreakType['Eol'] = 3] = 'Eol'; 21468 }(BreakType || (BreakType = {}))); 21469 const flip = (direction, positions) => direction === HDirection.Backwards ? reverse(positions) : positions; 21470 const walk$1 = (direction, caretWalker, pos) => direction === HDirection.Forwards ? caretWalker.next(pos) : caretWalker.prev(pos); 21471 const getBreakType = (scope, direction, currentPos, nextPos) => { 21472 if (isBr$6(nextPos.getNode(direction === HDirection.Forwards))) { 21473 return BreakType.Br; 21474 } else if (isInSameBlock(currentPos, nextPos) === false) { 21475 return BreakType.Block; 21476 } else { 21477 return BreakType.Wrap; 21478 } 21479 }; 21480 const getPositionsUntil = (predicate, direction, scope, start) => { 21481 const caretWalker = CaretWalker(scope); 21482 let currentPos = start; 21483 const positions = []; 21484 while (currentPos) { 21485 const nextPos = walk$1(direction, caretWalker, currentPos); 21486 if (!nextPos) { 21487 break; 21488 } 21489 if (isBr$6(nextPos.getNode(false))) { 21490 if (direction === HDirection.Forwards) { 21491 return { 21492 positions: flip(direction, positions).concat([nextPos]), 21493 breakType: BreakType.Br, 21494 breakAt: Optional.some(nextPos) 21495 }; 21496 } else { 21497 return { 21498 positions: flip(direction, positions), 21499 breakType: BreakType.Br, 21500 breakAt: Optional.some(nextPos) 21501 }; 21502 } 21503 } 21504 if (!nextPos.isVisible()) { 21505 currentPos = nextPos; 21506 continue; 21507 } 21508 if (predicate(currentPos, nextPos)) { 21509 const breakType = getBreakType(scope, direction, currentPos, nextPos); 21510 return { 21511 positions: flip(direction, positions), 21512 breakType, 21513 breakAt: Optional.some(nextPos) 21514 }; 21515 } 21516 positions.push(nextPos); 21517 currentPos = nextPos; 21518 } 21519 return { 21520 positions: flip(direction, positions), 21521 breakType: BreakType.Eol, 21522 breakAt: Optional.none() 21523 }; 21524 }; 21525 const getAdjacentLinePositions = (direction, getPositionsUntilBreak, scope, start) => getPositionsUntilBreak(scope, start).breakAt.map(pos => { 21526 const positions = getPositionsUntilBreak(scope, pos).positions; 21527 return direction === HDirection.Backwards ? positions.concat(pos) : [pos].concat(positions); 21528 }).getOr([]); 21529 const findClosestHorizontalPositionFromPoint = (positions, x) => foldl(positions, (acc, newPos) => acc.fold(() => Optional.some(newPos), lastPos => lift2(head(lastPos.getClientRects()), head(newPos.getClientRects()), (lastRect, newRect) => { 21530 const lastDist = Math.abs(x - lastRect.left); 21531 const newDist = Math.abs(x - newRect.left); 21532 return newDist <= lastDist ? newPos : lastPos; 21533 }).or(acc)), Optional.none()); 21534 const findClosestHorizontalPosition = (positions, pos) => head(pos.getClientRects()).bind(targetRect => findClosestHorizontalPositionFromPoint(positions, targetRect.left)); 21535 const getPositionsUntilPreviousLine = curry(getPositionsUntil, CaretPosition.isAbove, -1); 21536 const getPositionsUntilNextLine = curry(getPositionsUntil, CaretPosition.isBelow, 1); 21537 const getPositionsAbove = curry(getAdjacentLinePositions, -1, getPositionsUntilPreviousLine); 21538 const getPositionsBelow = curry(getAdjacentLinePositions, 1, getPositionsUntilNextLine); 21539 const isAtFirstLine = (scope, pos) => getPositionsUntilPreviousLine(scope, pos).breakAt.isNone(); 21540 const isAtLastLine = (scope, pos) => getPositionsUntilNextLine(scope, pos).breakAt.isNone(); 21541 const getFirstLinePositions = scope => firstPositionIn(scope).map(pos => [pos].concat(getPositionsUntilNextLine(scope, pos).positions)).getOr([]); 21542 const getLastLinePositions = scope => lastPositionIn(scope).map(pos => getPositionsUntilPreviousLine(scope, pos).positions.concat(pos)).getOr([]); 21543 const getClosestPositionAbove = (scope, pos) => findClosestHorizontalPosition(getPositionsAbove(scope, pos), pos); 21544 const getClosestPositionBelow = (scope, pos) => findClosestHorizontalPosition(getPositionsBelow(scope, pos), pos); 21545 21546 const isContentEditableFalse$4 = isContentEditableFalse$b; 21547 const distanceToRectLeft$1 = (clientRect, clientX) => Math.abs(clientRect.left - clientX); 21548 const distanceToRectRight$1 = (clientRect, clientX) => Math.abs(clientRect.right - clientX); 21549 const isNodeClientRect = rect => hasNonNullableKey(rect, 'node'); 21550 const findClosestClientRect = (clientRects, clientX) => reduce(clientRects, (oldClientRect, clientRect) => { 21551 const oldDistance = Math.min(distanceToRectLeft$1(oldClientRect, clientX), distanceToRectRight$1(oldClientRect, clientX)); 21552 const newDistance = Math.min(distanceToRectLeft$1(clientRect, clientX), distanceToRectRight$1(clientRect, clientX)); 21553 if (newDistance === oldDistance && isNodeClientRect(clientRect) && isContentEditableFalse$4(clientRect.node)) { 21554 return clientRect; 21555 } 21556 if (newDistance < oldDistance) { 21557 return clientRect; 21558 } 21559 return oldClientRect; 21560 }); 21561 21562 const getNodeClientRects = node => { 21563 const toArrayWithNode = clientRects => { 21564 return map$3(clientRects, rect => { 21565 const clientRect = clone$1(rect); 21566 clientRect.node = node; 21567 return clientRect; 21568 }); 21569 }; 21570 if (isElement$6(node)) { 21571 return toArrayWithNode(node.getClientRects()); 21572 } else if (isText$a(node)) { 21573 const rng = node.ownerDocument.createRange(); 21574 rng.setStart(node, 0); 21575 rng.setEnd(node, node.data.length); 21576 return toArrayWithNode(rng.getClientRects()); 21577 } else { 21578 return []; 21579 } 21580 }; 21581 const getClientRects = nodes => bind$3(nodes, getNodeClientRects); 21582 21583 var VDirection; 21584 (function (VDirection) { 21585 VDirection[VDirection['Up'] = -1] = 'Up'; 21586 VDirection[VDirection['Down'] = 1] = 'Down'; 21587 }(VDirection || (VDirection = {}))); 21588 const findUntil = (direction, root, predicateFn, node) => { 21589 let currentNode = node; 21590 while (currentNode = findNode(currentNode, direction, isEditableCaretCandidate$1, root)) { 21591 if (predicateFn(currentNode)) { 21592 return; 21593 } 21594 } 21595 }; 21596 const walkUntil = (direction, isAboveFn, isBeflowFn, root, predicateFn, caretPosition) => { 21597 let line = 0; 21598 const result = []; 21599 const add = node => { 21600 let clientRects = getClientRects([node]); 21601 if (direction === -1) { 21602 clientRects = clientRects.reverse(); 21603 } 21604 for (let i = 0; i < clientRects.length; i++) { 21605 const clientRect = clientRects[i]; 21606 if (isBeflowFn(clientRect, targetClientRect)) { 21607 continue; 21608 } 21609 if (result.length > 0 && isAboveFn(clientRect, last$2(result))) { 21610 line++; 21611 } 21612 clientRect.line = line; 21613 if (predicateFn(clientRect)) { 21614 return true; 21615 } 21616 result.push(clientRect); 21617 } 21618 return false; 21619 }; 21620 const targetClientRect = last$2(caretPosition.getClientRects()); 21621 if (!targetClientRect) { 21622 return result; 21623 } 21624 const node = caretPosition.getNode(); 21625 if (node) { 21626 add(node); 21627 findUntil(direction, root, add, node); 21628 } 21629 return result; 21630 }; 21631 const aboveLineNumber = (lineNumber, clientRect) => clientRect.line > lineNumber; 21632 const isLineNumber = (lineNumber, clientRect) => clientRect.line === lineNumber; 21633 const upUntil = curry(walkUntil, VDirection.Up, isAbove$1, isBelow$1); 21634 const downUntil = curry(walkUntil, VDirection.Down, isBelow$1, isAbove$1); 21635 const getLastClientRect = caretPosition => { 21636 return last$2(caretPosition.getClientRects()); 21637 }; 21638 const positionsUntil = (direction, root, predicateFn, node) => { 21639 const caretWalker = CaretWalker(root); 21640 let walkFn; 21641 let isBelowFn; 21642 let isAboveFn; 21643 let caretPosition; 21644 const result = []; 21645 let line = 0; 21646 if (direction === 1) { 21647 walkFn = caretWalker.next; 21648 isBelowFn = isBelow$1; 21649 isAboveFn = isAbove$1; 21650 caretPosition = CaretPosition.after(node); 21651 } else { 21652 walkFn = caretWalker.prev; 21653 isBelowFn = isAbove$1; 21654 isAboveFn = isBelow$1; 21655 caretPosition = CaretPosition.before(node); 21656 } 21657 const targetClientRect = getLastClientRect(caretPosition); 21658 do { 21659 if (!caretPosition.isVisible()) { 21660 continue; 21661 } 21662 const rect = getLastClientRect(caretPosition); 21663 if (isAboveFn(rect, targetClientRect)) { 21664 continue; 21665 } 21666 if (result.length > 0 && isBelowFn(rect, last$2(result))) { 21667 line++; 21668 } 21669 const clientRect = clone$1(rect); 21670 clientRect.position = caretPosition; 21671 clientRect.line = line; 21672 if (predicateFn(clientRect)) { 21673 return result; 21674 } 21675 result.push(clientRect); 21676 } while (caretPosition = walkFn(caretPosition)); 21677 return result; 21678 }; 21679 const isAboveLine = lineNumber => clientRect => aboveLineNumber(lineNumber, clientRect); 21680 const isLine = lineNumber => clientRect => isLineNumber(lineNumber, clientRect); 21681 21682 const moveToRange = (editor, rng) => { 21683 editor.selection.setRng(rng); 21684 scrollRangeIntoView(editor, editor.selection.getRng()); 21685 }; 21686 const renderRangeCaretOpt = (editor, range, scrollIntoView) => Optional.some(renderRangeCaret(editor, range, scrollIntoView)); 21687 const moveHorizontally = (editor, direction, range, isBefore, isAfter, isElement) => { 21688 const forwards = direction === HDirection.Forwards; 21689 const caretWalker = CaretWalker(editor.getBody()); 21690 const getNextPosFn = curry(getVisualCaretPosition, forwards ? caretWalker.next : caretWalker.prev); 21691 const isBeforeFn = forwards ? isBefore : isAfter; 21692 if (!range.collapsed) { 21693 const node = getSelectedNode(range); 21694 if (isElement(node)) { 21695 return showCaret(direction, editor, node, direction === HDirection.Backwards, false); 21696 } else if (isCefAtEdgeSelected(editor)) { 21697 const newRange = range.cloneRange(); 21698 newRange.collapse(direction === HDirection.Backwards); 21699 return Optional.from(newRange); 21700 } 21701 } 21702 const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range); 21703 if (isBeforeFn(caretPosition)) { 21704 return selectNode(editor, caretPosition.getNode(!forwards)); 21705 } 21706 let nextCaretPosition = getNextPosFn(caretPosition); 21707 const rangeIsInContainerBlock = isRangeInCaretContainerBlock(range); 21708 if (!nextCaretPosition) { 21709 return rangeIsInContainerBlock ? Optional.some(range) : Optional.none(); 21710 } else { 21711 nextCaretPosition = normalizePosition(forwards, nextCaretPosition); 21712 } 21713 if (isBeforeFn(nextCaretPosition)) { 21714 return showCaret(direction, editor, nextCaretPosition.getNode(!forwards), forwards, false); 21715 } 21716 const peekCaretPosition = getNextPosFn(nextCaretPosition); 21717 if (peekCaretPosition && isBeforeFn(peekCaretPosition)) { 21718 if (isMoveInsideSameBlock(nextCaretPosition, peekCaretPosition)) { 21719 return showCaret(direction, editor, peekCaretPosition.getNode(!forwards), forwards, false); 21720 } 21721 } 21722 if (rangeIsInContainerBlock) { 21723 return renderRangeCaretOpt(editor, nextCaretPosition.toRange(), false); 21724 } 21725 return Optional.none(); 21726 }; 21727 const moveVertically = (editor, direction, range, isBefore, isAfter, isElement) => { 21728 const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range); 21729 const caretClientRect = last$2(caretPosition.getClientRects()); 21730 const forwards = direction === VDirection.Down; 21731 const root = editor.getBody(); 21732 if (!caretClientRect) { 21733 return Optional.none(); 21734 } 21735 if (isCefAtEdgeSelected(editor)) { 21736 const caretPosition = forwards ? CaretPosition.fromRangeEnd(range) : CaretPosition.fromRangeStart(range); 21737 const getClosestFn = !forwards ? getClosestPositionAbove : getClosestPositionBelow; 21738 return getClosestFn(root, caretPosition).orThunk(() => Optional.from(caretPosition)).map(pos => pos.toRange()); 21739 } 21740 const walkerFn = forwards ? downUntil : upUntil; 21741 const linePositions = walkerFn(root, isAboveLine(1), caretPosition); 21742 const nextLinePositions = filter$5(linePositions, isLine(1)); 21743 const clientX = caretClientRect.left; 21744 const nextLineRect = findClosestClientRect(nextLinePositions, clientX); 21745 if (nextLineRect && isElement(nextLineRect.node)) { 21746 const dist1 = Math.abs(clientX - nextLineRect.left); 21747 const dist2 = Math.abs(clientX - nextLineRect.right); 21748 return showCaret(direction, editor, nextLineRect.node, dist1 < dist2, false); 21749 } 21750 let currentNode; 21751 if (isBefore(caretPosition)) { 21752 currentNode = caretPosition.getNode(); 21753 } else if (isAfter(caretPosition)) { 21754 currentNode = caretPosition.getNode(true); 21755 } else { 21756 currentNode = getSelectedNode(range); 21757 } 21758 if (currentNode) { 21759 const caretPositions = positionsUntil(direction, root, isAboveLine(1), currentNode); 21760 let closestNextLineRect = findClosestClientRect(filter$5(caretPositions, isLine(1)), clientX); 21761 if (closestNextLineRect) { 21762 return renderRangeCaretOpt(editor, closestNextLineRect.position.toRange(), false); 21763 } 21764 closestNextLineRect = last$2(filter$5(caretPositions, isLine(0))); 21765 if (closestNextLineRect) { 21766 return renderRangeCaretOpt(editor, closestNextLineRect.position.toRange(), false); 21767 } 21768 } 21769 if (nextLinePositions.length === 0) { 21770 return getLineEndPoint(editor, forwards).filter(forwards ? isAfter : isBefore).map(pos => renderRangeCaret(editor, pos.toRange(), false)); 21771 } 21772 return Optional.none(); 21773 }; 21774 const getLineEndPoint = (editor, forward) => { 21775 const rng = editor.selection.getRng(); 21776 const from = forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng); 21777 const host = getEditingHost(from.container(), editor.getBody()); 21778 if (forward) { 21779 const lineInfo = getPositionsUntilNextLine(host, from); 21780 return last$3(lineInfo.positions); 21781 } else { 21782 const lineInfo = getPositionsUntilPreviousLine(host, from); 21783 return head(lineInfo.positions); 21784 } 21785 }; 21786 const moveToLineEndPoint$3 = (editor, forward, isElementPosition) => getLineEndPoint(editor, forward).filter(isElementPosition).exists(pos => { 21787 editor.selection.setRng(pos.toRange()); 21788 return true; 21789 }); 21790 21791 const setCaretPosition = (editor, pos) => { 21792 const rng = editor.dom.createRng(); 21793 rng.setStart(pos.container(), pos.offset()); 21794 rng.setEnd(pos.container(), pos.offset()); 21795 editor.selection.setRng(rng); 21796 }; 21797 const setSelected = (state, elm) => { 21798 if (state) { 21799 elm.setAttribute('data-mce-selected', 'inline-boundary'); 21800 } else { 21801 elm.removeAttribute('data-mce-selected'); 21802 } 21803 }; 21804 const renderCaretLocation = (editor, caret, location) => renderCaret(caret, location).map(pos => { 21805 setCaretPosition(editor, pos); 21806 return location; 21807 }); 21808 const getPositionFromRange = (range, root, forward) => { 21809 const start = CaretPosition.fromRangeStart(range); 21810 if (range.collapsed) { 21811 return start; 21812 } else { 21813 const end = CaretPosition.fromRangeEnd(range); 21814 return forward ? prevPosition(root, end).getOr(end) : nextPosition(root, start).getOr(start); 21815 } 21816 }; 21817 const findLocation = (editor, caret, forward) => { 21818 const rootNode = editor.getBody(); 21819 const from = getPositionFromRange(editor.selection.getRng(), rootNode, forward); 21820 const isInlineTarget$1 = curry(isInlineTarget, editor); 21821 const location = findLocation$1(forward, isInlineTarget$1, rootNode, from); 21822 return location.bind(location => renderCaretLocation(editor, caret, location)); 21823 }; 21824 const toggleInlines = (isInlineTarget, dom, elms) => { 21825 const inlineBoundaries = map$3(descendants(SugarElement.fromDom(dom.getRoot()), '*[data-mce-selected="inline-boundary"]'), e => e.dom); 21826 const selectedInlines = filter$5(inlineBoundaries, isInlineTarget); 21827 const targetInlines = filter$5(elms, isInlineTarget); 21828 each$e(difference(selectedInlines, targetInlines), curry(setSelected, false)); 21829 each$e(difference(targetInlines, selectedInlines), curry(setSelected, true)); 21830 }; 21831 const safeRemoveCaretContainer = (editor, caret) => { 21832 const caretValue = caret.get(); 21833 if (editor.selection.isCollapsed() && !editor.composing && caretValue) { 21834 const pos = CaretPosition.fromRangeStart(editor.selection.getRng()); 21835 if (CaretPosition.isTextPosition(pos) && !isAtZwsp(pos)) { 21836 setCaretPosition(editor, removeAndReposition(caretValue, pos)); 21837 caret.set(null); 21838 } 21839 } 21840 }; 21841 const renderInsideInlineCaret = (isInlineTarget, editor, caret, elms) => { 21842 if (editor.selection.isCollapsed()) { 21843 const inlines = filter$5(elms, isInlineTarget); 21844 each$e(inlines, _inline => { 21845 const pos = CaretPosition.fromRangeStart(editor.selection.getRng()); 21846 readLocation(isInlineTarget, editor.getBody(), pos).bind(location => renderCaretLocation(editor, caret, location)); 21847 }); 21848 } 21849 }; 21850 const move$2 = (editor, caret, forward) => isInlineBoundariesEnabled(editor) ? findLocation(editor, caret, forward).isSome() : false; 21851 const moveWord = (forward, editor, _caret) => isInlineBoundariesEnabled(editor) ? moveByWord(forward, editor) : false; 21852 const setupSelectedState = editor => { 21853 const caret = Cell(null); 21854 const isInlineTarget$1 = curry(isInlineTarget, editor); 21855 editor.on('NodeChange', e => { 21856 if (isInlineBoundariesEnabled(editor)) { 21857 toggleInlines(isInlineTarget$1, editor.dom, e.parents); 21858 safeRemoveCaretContainer(editor, caret); 21859 renderInsideInlineCaret(isInlineTarget$1, editor, caret, e.parents); 21860 } 21861 }); 21862 return caret; 21863 }; 21864 const moveNextWord = curry(moveWord, true); 21865 const movePrevWord = curry(moveWord, false); 21866 const moveToLineEndPoint$2 = (editor, forward, caret) => { 21867 if (isInlineBoundariesEnabled(editor)) { 21868 const linePoint = getLineEndPoint(editor, forward).getOrThunk(() => { 21869 const rng = editor.selection.getRng(); 21870 return forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng); 21871 }); 21872 return readLocation(curry(isInlineTarget, editor), editor.getBody(), linePoint).exists(loc => { 21873 const outsideLoc = outside(loc); 21874 return renderCaret(caret, outsideLoc).exists(pos => { 21875 setCaretPosition(editor, pos); 21876 return true; 21877 }); 21878 }); 21879 } else { 21880 return false; 21881 } 21882 }; 21883 21884 const rangeFromPositions = (from, to) => { 21885 const range = document.createRange(); 21886 range.setStart(from.container(), from.offset()); 21887 range.setEnd(to.container(), to.offset()); 21888 return range; 21889 }; 21890 const hasOnlyTwoOrLessPositionsLeft = elm => lift2(firstPositionIn(elm), lastPositionIn(elm), (firstPos, lastPos) => { 21891 const normalizedFirstPos = normalizePosition(true, firstPos); 21892 const normalizedLastPos = normalizePosition(false, lastPos); 21893 return nextPosition(elm, normalizedFirstPos).forall(pos => pos.isEqual(normalizedLastPos)); 21894 }).getOr(true); 21895 const setCaretLocation = (editor, caret) => location => renderCaret(caret, location).map(pos => () => setCaretPosition(editor, pos)); 21896 const deleteFromTo = (editor, caret, from, to) => { 21897 const rootNode = editor.getBody(); 21898 const isInlineTarget$1 = curry(isInlineTarget, editor); 21899 editor.undoManager.ignore(() => { 21900 editor.selection.setRng(rangeFromPositions(from, to)); 21901 execNativeDeleteCommand(editor); 21902 readLocation(isInlineTarget$1, rootNode, CaretPosition.fromRangeStart(editor.selection.getRng())).map(inside).bind(setCaretLocation(editor, caret)).each(call); 21903 }); 21904 editor.nodeChanged(); 21905 }; 21906 const rescope = (rootNode, node) => { 21907 const parentBlock = getParentBlock$3(node, rootNode); 21908 return parentBlock ? parentBlock : rootNode; 21909 }; 21910 const backspaceDeleteCollapsed = (editor, caret, forward, from) => { 21911 const rootNode = rescope(editor.getBody(), from.container()); 21912 const isInlineTarget$1 = curry(isInlineTarget, editor); 21913 const fromLocation = readLocation(isInlineTarget$1, rootNode, from); 21914 const location = fromLocation.bind(location => { 21915 if (forward) { 21916 return location.fold(constant(Optional.some(inside(location))), Optional.none, constant(Optional.some(outside(location))), Optional.none); 21917 } else { 21918 return location.fold(Optional.none, constant(Optional.some(outside(location))), Optional.none, constant(Optional.some(inside(location)))); 21919 } 21920 }); 21921 return location.map(setCaretLocation(editor, caret)).getOrThunk(() => { 21922 const toPosition = navigate(forward, rootNode, from); 21923 const toLocation = toPosition.bind(pos => readLocation(isInlineTarget$1, rootNode, pos)); 21924 return lift2(fromLocation, toLocation, () => findRootInline(isInlineTarget$1, rootNode, from).bind(elm => { 21925 if (hasOnlyTwoOrLessPositionsLeft(elm)) { 21926 return Optional.some(() => { 21927 deleteElement$2(editor, forward, SugarElement.fromDom(elm)); 21928 }); 21929 } else { 21930 return Optional.none(); 21931 } 21932 })).getOrThunk(() => toLocation.bind(() => toPosition.map(to => { 21933 return () => { 21934 if (forward) { 21935 deleteFromTo(editor, caret, from, to); 21936 } else { 21937 deleteFromTo(editor, caret, to, from); 21938 } 21939 }; 21940 }))); 21941 }); 21942 }; 21943 const backspaceDelete$3 = (editor, caret, forward) => { 21944 if (editor.selection.isCollapsed() && isInlineBoundariesEnabled(editor)) { 21945 const from = CaretPosition.fromRangeStart(editor.selection.getRng()); 21946 return backspaceDeleteCollapsed(editor, caret, forward, from); 21947 } 21948 return Optional.none(); 21949 }; 21950 21951 const hasMultipleChildren = elm => childNodesCount(elm) > 1; 21952 const getParentsUntil = (editor, pred) => { 21953 const rootElm = SugarElement.fromDom(editor.getBody()); 21954 const startElm = SugarElement.fromDom(editor.selection.getStart()); 21955 const parents = parentsAndSelf(startElm, rootElm); 21956 return findIndex$2(parents, pred).fold(constant(parents), index => parents.slice(0, index)); 21957 }; 21958 const hasOnlyOneChild = elm => childNodesCount(elm) === 1; 21959 const getParentInlinesUntilMultichildInline = editor => getParentsUntil(editor, elm => isBlock$2(elm) || hasMultipleChildren(elm)); 21960 const getParentInlines = editor => getParentsUntil(editor, isBlock$2); 21961 const getFormatNodes = (editor, parentInlines) => { 21962 const isFormatElement$1 = curry(isFormatElement, editor); 21963 return bind$3(parentInlines, elm => isFormatElement$1(elm) ? [elm.dom] : []); 21964 }; 21965 const getFormatNodesAtStart = editor => { 21966 const parentInlines = getParentInlines(editor); 21967 return getFormatNodes(editor, parentInlines); 21968 }; 21969 const deleteLastPosition = (forward, editor, target, parentInlines) => { 21970 const formatNodes = getFormatNodes(editor, parentInlines); 21971 if (formatNodes.length === 0) { 21972 deleteElement$2(editor, forward, target); 21973 } else { 21974 const pos = replaceWithCaretFormat(target.dom, formatNodes); 21975 editor.selection.setRng(pos.toRange()); 21976 } 21977 }; 21978 const deleteCaret$1 = (editor, forward) => { 21979 const parentInlines = filter$5(getParentInlinesUntilMultichildInline(editor), hasOnlyOneChild); 21980 return last$3(parentInlines).bind(target => { 21981 const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng()); 21982 if (willDeleteLastPositionInElement(forward, fromPos, target.dom) && !isEmptyCaretFormatElement(target)) { 21983 return Optional.some(() => deleteLastPosition(forward, editor, target, parentInlines)); 21984 } else { 21985 return Optional.none(); 21986 } 21987 }); 21988 }; 21989 const isBrInEmptyElement = (editor, elm) => { 21990 const parentElm = elm.parentElement; 21991 return isBr$6(elm) && !isNull(parentElm) && editor.dom.isEmpty(parentElm); 21992 }; 21993 const isEmptyCaret = elm => isEmptyCaretFormatElement(SugarElement.fromDom(elm)); 21994 const createCaretFormatAtStart = (editor, formatNodes) => { 21995 const startElm = editor.selection.getStart(); 21996 const pos = isBrInEmptyElement(editor, startElm) || isEmptyCaret(startElm) ? replaceWithCaretFormat(startElm, formatNodes) : createCaretFormatAtStart$1(editor.selection.getRng(), formatNodes); 21997 editor.selection.setRng(pos.toRange()); 21998 }; 21999 const updateCaretFormat = (editor, updateFormats) => { 22000 const missingFormats = difference(updateFormats, getFormatNodesAtStart(editor)); 22001 if (missingFormats.length > 0) { 22002 createCaretFormatAtStart(editor, missingFormats); 22003 } 22004 }; 22005 const rangeStartsAtTextContainer = rng => isText$a(rng.startContainer); 22006 const rangeStartsAtStartOfTextContainer = rng => rng.startOffset === 0 && rangeStartsAtTextContainer(rng); 22007 const rangeStartParentIsFormatElement = (editor, rng) => { 22008 const startParent = rng.startContainer.parentElement; 22009 return !isNull(startParent) && isFormatElement(editor, SugarElement.fromDom(startParent)); 22010 }; 22011 const rangeStartAndEndHaveSameParent = rng => { 22012 const startParent = rng.startContainer.parentNode; 22013 const endParent = rng.endContainer.parentNode; 22014 return !isNull(startParent) && !isNull(endParent) && startParent.isEqualNode(endParent); 22015 }; 22016 const rangeEndsAtEndOfEndContainer = rng => { 22017 const endContainer = rng.endContainer; 22018 return rng.endOffset === (isText$a(endContainer) ? endContainer.length : endContainer.childNodes.length); 22019 }; 22020 const rangeEndsAtEndOfStartContainer = rng => rangeStartAndEndHaveSameParent(rng) && rangeEndsAtEndOfEndContainer(rng); 22021 const rangeEndsAfterEndOfStartContainer = rng => !rng.endContainer.isEqualNode(rng.commonAncestorContainer); 22022 const rangeEndsAtOrAfterEndOfStartContainer = rng => rangeEndsAtEndOfStartContainer(rng) || rangeEndsAfterEndOfStartContainer(rng); 22023 const requiresDeleteRangeOverride = editor => { 22024 const rng = editor.selection.getRng(); 22025 return rangeStartsAtStartOfTextContainer(rng) && rangeStartParentIsFormatElement(editor, rng) && rangeEndsAtOrAfterEndOfStartContainer(rng); 22026 }; 22027 const deleteRange$1 = editor => { 22028 if (requiresDeleteRangeOverride(editor)) { 22029 const formatNodes = getFormatNodesAtStart(editor); 22030 return Optional.some(() => { 22031 execNativeDeleteCommand(editor); 22032 updateCaretFormat(editor, formatNodes); 22033 }); 22034 } else { 22035 return Optional.none(); 22036 } 22037 }; 22038 const backspaceDelete$2 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$1(editor, forward) : deleteRange$1(editor); 22039 const hasAncestorInlineCaret = elm => ancestor$1(elm, node => isCaretNode(node.dom), isBlock$2); 22040 const hasAncestorInlineCaretAtStart = editor => hasAncestorInlineCaret(SugarElement.fromDom(editor.selection.getStart())); 22041 const requiresRefreshCaretOverride = editor => { 22042 const rng = editor.selection.getRng(); 22043 return rng.collapsed && (rangeStartsAtTextContainer(rng) || editor.dom.isEmpty(rng.startContainer)) && !hasAncestorInlineCaretAtStart(editor); 22044 }; 22045 const refreshCaret = editor => { 22046 if (requiresRefreshCaretOverride(editor)) { 22047 createCaretFormatAtStart(editor, []); 22048 } 22049 return true; 22050 }; 22051 22052 const deleteElement = (editor, forward, element) => { 22053 if (isNonNullable(element)) { 22054 return Optional.some(() => { 22055 editor._selectionOverrides.hideFakeCaret(); 22056 deleteElement$2(editor, forward, SugarElement.fromDom(element)); 22057 }); 22058 } else { 22059 return Optional.none(); 22060 } 22061 }; 22062 const deleteCaret = (editor, forward) => { 22063 const isNearMedia = forward ? isBeforeMedia : isAfterMedia; 22064 const direction = forward ? HDirection.Forwards : HDirection.Backwards; 22065 const fromPos = getNormalizedRangeEndPoint(direction, editor.getBody(), editor.selection.getRng()); 22066 if (isNearMedia(fromPos)) { 22067 return deleteElement(editor, forward, fromPos.getNode(!forward)); 22068 } else { 22069 return Optional.from(normalizePosition(forward, fromPos)).filter(pos => isNearMedia(pos) && isMoveInsideSameBlock(fromPos, pos)).bind(pos => deleteElement(editor, forward, pos.getNode(!forward))); 22070 } 22071 }; 22072 const deleteRange = (editor, forward) => { 22073 const selectedNode = editor.selection.getNode(); 22074 return isMedia$2(selectedNode) ? deleteElement(editor, forward, selectedNode) : Optional.none(); 22075 }; 22076 const backspaceDelete$1 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret(editor, forward) : deleteRange(editor, forward); 22077 22078 const isEditable = target => closest$4(target, elm => isContentEditableTrue$3(elm.dom) || isContentEditableFalse$b(elm.dom)).exists(elm => isContentEditableTrue$3(elm.dom)); 22079 const parseIndentValue = value => toInt(value !== null && value !== void 0 ? value : '').getOr(0); 22080 const getIndentStyleName = (useMargin, element) => { 22081 const indentStyleName = useMargin || isTable$1(element) ? 'margin' : 'padding'; 22082 const suffix = get$7(element, 'direction') === 'rtl' ? '-right' : '-left'; 22083 return indentStyleName + suffix; 22084 }; 22085 const indentElement = (dom, command, useMargin, value, unit, element) => { 22086 const indentStyleName = getIndentStyleName(useMargin, SugarElement.fromDom(element)); 22087 const parsedValue = parseIndentValue(dom.getStyle(element, indentStyleName)); 22088 if (command === 'outdent') { 22089 const styleValue = Math.max(0, parsedValue - value); 22090 dom.setStyle(element, indentStyleName, styleValue ? styleValue + unit : ''); 22091 } else { 22092 const styleValue = parsedValue + value + unit; 22093 dom.setStyle(element, indentStyleName, styleValue); 22094 } 22095 }; 22096 const validateBlocks = (editor, blocks) => forall(blocks, block => { 22097 const indentStyleName = getIndentStyleName(shouldIndentUseMargin(editor), block); 22098 const intentValue = getRaw(block, indentStyleName).map(parseIndentValue).getOr(0); 22099 const contentEditable = editor.dom.getContentEditable(block.dom); 22100 return contentEditable !== 'false' && intentValue > 0; 22101 }); 22102 const canOutdent = editor => { 22103 const blocks = getBlocksToIndent(editor); 22104 return !editor.mode.isReadOnly() && (blocks.length > 1 || validateBlocks(editor, blocks)); 22105 }; 22106 const isListComponent = el => isList(el) || isListItem$1(el); 22107 const parentIsListComponent = el => parent(el).exists(isListComponent); 22108 const getBlocksToIndent = editor => filter$5(fromDom$1(editor.selection.getSelectedBlocks()), el => !isListComponent(el) && !parentIsListComponent(el) && isEditable(el)); 22109 const handle = (editor, command) => { 22110 var _a, _b; 22111 const {dom} = editor; 22112 const indentation = getIndentation(editor); 22113 const indentUnit = (_b = (_a = /[a-z%]+$/i.exec(indentation)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 'px'; 22114 const indentValue = parseIndentValue(indentation); 22115 const useMargin = shouldIndentUseMargin(editor); 22116 each$e(getBlocksToIndent(editor), block => { 22117 indentElement(dom, command, useMargin, indentValue, indentUnit, block.dom); 22118 }); 22119 }; 22120 const indent = editor => handle(editor, 'indent'); 22121 const outdent = editor => handle(editor, 'outdent'); 22122 22123 const backspaceDelete = editor => { 22124 if (editor.selection.isCollapsed() && canOutdent(editor)) { 22125 const dom = editor.dom; 22126 const rng = editor.selection.getRng(); 22127 const pos = CaretPosition.fromRangeStart(rng); 22128 const block = dom.getParent(rng.startContainer, dom.isBlock); 22129 if (block !== null && isAtStartOfBlock(SugarElement.fromDom(block), pos)) { 22130 return Optional.some(() => outdent(editor)); 22131 } 22132 } 22133 return Optional.none(); 22134 }; 22135 22136 const findAction = (editor, caret, forward) => findMap([ 22137 backspaceDelete, 22138 backspaceDelete$5, 22139 backspaceDelete$6, 22140 (editor, forward) => backspaceDelete$3(editor, caret, forward), 22141 backspaceDelete$8, 22142 backspaceDelete$9, 22143 backspaceDelete$4, 22144 backspaceDelete$1, 22145 backspaceDelete$7, 22146 backspaceDelete$2 22147 ], item => item(editor, forward)).filter(_ => editor.selection.isEditable()); 22148 const deleteCommand = (editor, caret) => { 22149 const result = findAction(editor, caret, false); 22150 result.fold(() => { 22151 execNativeDeleteCommand(editor); 22152 paddEmptyBody(editor); 22153 }, call); 22154 }; 22155 const forwardDeleteCommand = (editor, caret) => { 22156 const result = findAction(editor, caret, true); 22157 result.fold(() => execNativeForwardDeleteCommand(editor), call); 22158 }; 22159 const setup$p = (editor, caret) => { 22160 editor.addCommand('delete', () => { 22161 deleteCommand(editor, caret); 22162 }); 22163 editor.addCommand('forwardDelete', () => { 22164 forwardDeleteCommand(editor, caret); 22165 }); 22166 }; 22167 22168 const SIGNIFICANT_MOVE = 5; 22169 const LONGPRESS_DELAY = 400; 22170 const getTouch = event => { 22171 if (event.touches === undefined || event.touches.length !== 1) { 22172 return Optional.none(); 22173 } 22174 return Optional.some(event.touches[0]); 22175 }; 22176 const isFarEnough = (touch, data) => { 22177 const distX = Math.abs(touch.clientX - data.x); 22178 const distY = Math.abs(touch.clientY - data.y); 22179 return distX > SIGNIFICANT_MOVE || distY > SIGNIFICANT_MOVE; 22180 }; 22181 const setup$o = editor => { 22182 const startData = value$2(); 22183 const longpressFired = Cell(false); 22184 const debounceLongpress = last$1(e => { 22185 editor.dispatch('longpress', { 22186 ...e, 22187 type: 'longpress' 22188 }); 22189 longpressFired.set(true); 22190 }, LONGPRESS_DELAY); 22191 editor.on('touchstart', e => { 22192 getTouch(e).each(touch => { 22193 debounceLongpress.cancel(); 22194 const data = { 22195 x: touch.clientX, 22196 y: touch.clientY, 22197 target: e.target 22198 }; 22199 debounceLongpress.throttle(e); 22200 longpressFired.set(false); 22201 startData.set(data); 22202 }); 22203 }, true); 22204 editor.on('touchmove', e => { 22205 debounceLongpress.cancel(); 22206 getTouch(e).each(touch => { 22207 startData.on(data => { 22208 if (isFarEnough(touch, data)) { 22209 startData.clear(); 22210 longpressFired.set(false); 22211 editor.dispatch('longpresscancel'); 22212 } 22213 }); 22214 }); 22215 }, true); 22216 editor.on('touchend touchcancel', e => { 22217 debounceLongpress.cancel(); 22218 if (e.type === 'touchcancel') { 22219 return; 22220 } 22221 startData.get().filter(data => data.target.isEqualNode(e.target)).each(() => { 22222 if (longpressFired.get()) { 22223 e.preventDefault(); 22224 } else { 22225 editor.dispatch('tap', { 22226 ...e, 22227 type: 'tap' 22228 }); 22229 } 22230 }); 22231 }, true); 22232 }; 22233 22234 const isBlockElement = (blockElements, node) => has$2(blockElements, node.nodeName); 22235 const isValidTarget = (schema, node) => { 22236 if (isText$a(node)) { 22237 return true; 22238 } else if (isElement$6(node)) { 22239 return !isBlockElement(schema.getBlockElements(), node) && !isBookmarkNode$1(node) && !isTransparentBlock(schema, node); 22240 } else { 22241 return false; 22242 } 22243 }; 22244 const hasBlockParent = (blockElements, root, node) => { 22245 return exists(parents(SugarElement.fromDom(node), SugarElement.fromDom(root)), elm => { 22246 return isBlockElement(blockElements, elm.dom); 22247 }); 22248 }; 22249 const shouldRemoveTextNode = (blockElements, node) => { 22250 if (isText$a(node)) { 22251 if (node.data.length === 0) { 22252 return true; 22253 } else if (/^\s+$/.test(node.data) && (!node.nextSibling || isBlockElement(blockElements, node.nextSibling))) { 22254 return true; 22255 } 22256 } 22257 return false; 22258 }; 22259 const createRootBlock = editor => editor.dom.create(getForcedRootBlock(editor), getForcedRootBlockAttrs(editor)); 22260 const addRootBlocks = editor => { 22261 const dom = editor.dom, selection = editor.selection; 22262 const schema = editor.schema; 22263 const blockElements = schema.getBlockElements(); 22264 const startNode = selection.getStart(); 22265 const rootNode = editor.getBody(); 22266 let rootBlockNode; 22267 let tempNode; 22268 let wrapped = false; 22269 const forcedRootBlock = getForcedRootBlock(editor); 22270 if (!startNode || !isElement$6(startNode)) { 22271 return; 22272 } 22273 const rootNodeName = rootNode.nodeName.toLowerCase(); 22274 if (!schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase()) || hasBlockParent(blockElements, rootNode, startNode)) { 22275 return; 22276 } 22277 const rng = selection.getRng(); 22278 const {startContainer, startOffset, endContainer, endOffset} = rng; 22279 const restoreSelection = hasFocus(editor); 22280 let node = rootNode.firstChild; 22281 while (node) { 22282 if (isElement$6(node)) { 22283 updateElement(schema, node); 22284 } 22285 if (isValidTarget(schema, node)) { 22286 if (shouldRemoveTextNode(blockElements, node)) { 22287 tempNode = node; 22288 node = node.nextSibling; 22289 dom.remove(tempNode); 22290 continue; 22291 } 22292 if (!rootBlockNode) { 22293 rootBlockNode = createRootBlock(editor); 22294 rootNode.insertBefore(rootBlockNode, node); 22295 wrapped = true; 22296 } 22297 tempNode = node; 22298 node = node.nextSibling; 22299 rootBlockNode.appendChild(tempNode); 22300 } else { 22301 rootBlockNode = null; 22302 node = node.nextSibling; 22303 } 22304 } 22305 if (wrapped && restoreSelection) { 22306 rng.setStart(startContainer, startOffset); 22307 rng.setEnd(endContainer, endOffset); 22308 selection.setRng(rng); 22309 editor.nodeChanged(); 22310 } 22311 }; 22312 const insertEmptyLine = (editor, root, insertBlock) => { 22313 const block = SugarElement.fromDom(createRootBlock(editor)); 22314 const br = createPaddingBr(); 22315 append$1(block, br); 22316 insertBlock(root, block); 22317 const rng = document.createRange(); 22318 rng.setStartBefore(br.dom); 22319 rng.setEndBefore(br.dom); 22320 return rng; 22321 }; 22322 const setup$n = editor => { 22323 editor.on('NodeChange', curry(addRootBlocks, editor)); 22324 }; 22325 22326 const hasClass = checkClassName => node => (' ' + node.attr('class') + ' ').indexOf(checkClassName) !== -1; 22327 const replaceMatchWithSpan = (editor, content, cls) => { 22328 return function (match) { 22329 const args = arguments, index = args[args.length - 2]; 22330 const prevChar = index > 0 ? content.charAt(index - 1) : ''; 22331 if (prevChar === '"') { 22332 return match; 22333 } 22334 if (prevChar === '>') { 22335 const findStartTagIndex = content.lastIndexOf('<', index); 22336 if (findStartTagIndex !== -1) { 22337 const tagHtml = content.substring(findStartTagIndex, index); 22338 if (tagHtml.indexOf('contenteditable="false"') !== -1) { 22339 return match; 22340 } 22341 } 22342 } 22343 return '<span class="' + cls + '" data-mce-content="' + editor.dom.encode(args[0]) + '">' + editor.dom.encode(typeof args[1] === 'string' ? args[1] : args[0]) + '</span>'; 22344 }; 22345 }; 22346 const convertRegExpsToNonEditable = (editor, nonEditableRegExps, e) => { 22347 let i = nonEditableRegExps.length, content = e.content; 22348 if (e.format === 'raw') { 22349 return; 22350 } 22351 while (i--) { 22352 content = content.replace(nonEditableRegExps[i], replaceMatchWithSpan(editor, content, getNonEditableClass(editor))); 22353 } 22354 e.content = content; 22355 }; 22356 const setup$m = editor => { 22357 const contentEditableAttrName = 'contenteditable'; 22358 const editClass = ' ' + Tools.trim(getEditableClass(editor)) + ' '; 22359 const nonEditClass = ' ' + Tools.trim(getNonEditableClass(editor)) + ' '; 22360 const hasEditClass = hasClass(editClass); 22361 const hasNonEditClass = hasClass(nonEditClass); 22362 const nonEditableRegExps = getNonEditableRegExps(editor); 22363 if (nonEditableRegExps.length > 0) { 22364 editor.on('BeforeSetContent', e => { 22365 convertRegExpsToNonEditable(editor, nonEditableRegExps, e); 22366 }); 22367 } 22368 editor.parser.addAttributeFilter('class', nodes => { 22369 let i = nodes.length; 22370 while (i--) { 22371 const node = nodes[i]; 22372 if (hasEditClass(node)) { 22373 node.attr(contentEditableAttrName, 'true'); 22374 } else if (hasNonEditClass(node)) { 22375 node.attr(contentEditableAttrName, 'false'); 22376 } 22377 } 22378 }); 22379 editor.serializer.addAttributeFilter(contentEditableAttrName, nodes => { 22380 let i = nodes.length; 22381 while (i--) { 22382 const node = nodes[i]; 22383 if (!hasEditClass(node) && !hasNonEditClass(node)) { 22384 continue; 22385 } 22386 if (nonEditableRegExps.length > 0 && node.attr('data-mce-content')) { 22387 node.name = '#text'; 22388 node.type = 3; 22389 node.raw = true; 22390 node.value = node.attr('data-mce-content'); 22391 } else { 22392 node.attr(contentEditableAttrName, null); 22393 } 22394 } 22395 }); 22396 }; 22397 22398 const findBlockCaretContainer = editor => descendant(SugarElement.fromDom(editor.getBody()), '*[data-mce-caret]').map(elm => elm.dom).getOrNull(); 22399 const showBlockCaretContainer = (editor, blockCaretContainer) => { 22400 if (blockCaretContainer.hasAttribute('data-mce-caret')) { 22401 showCaretContainerBlock(blockCaretContainer); 22402 editor.selection.setRng(editor.selection.getRng()); 22403 editor.selection.scrollIntoView(blockCaretContainer); 22404 } 22405 }; 22406 const handleBlockContainer = (editor, e) => { 22407 const blockCaretContainer = findBlockCaretContainer(editor); 22408 if (!blockCaretContainer) { 22409 return; 22410 } 22411 if (e.type === 'compositionstart') { 22412 e.preventDefault(); 22413 e.stopPropagation(); 22414 showBlockCaretContainer(editor, blockCaretContainer); 22415 return; 22416 } 22417 if (hasContent(blockCaretContainer)) { 22418 showBlockCaretContainer(editor, blockCaretContainer); 22419 editor.undoManager.add(); 22420 } 22421 }; 22422 const setup$l = editor => { 22423 editor.on('keyup compositionstart', curry(handleBlockContainer, editor)); 22424 }; 22425 22426 const isContentEditableFalse$3 = isContentEditableFalse$b; 22427 const moveToCeFalseHorizontally = (direction, editor, range) => moveHorizontally(editor, direction, range, isBeforeContentEditableFalse, isAfterContentEditableFalse, isContentEditableFalse$3); 22428 const moveToCeFalseVertically = (direction, editor, range) => { 22429 const isBefore = caretPosition => isBeforeContentEditableFalse(caretPosition) || isBeforeTable(caretPosition); 22430 const isAfter = caretPosition => isAfterContentEditableFalse(caretPosition) || isAfterTable(caretPosition); 22431 return moveVertically(editor, direction, range, isBefore, isAfter, isContentEditableFalse$3); 22432 }; 22433 const createTextBlock = editor => { 22434 const textBlock = editor.dom.create(getForcedRootBlock(editor)); 22435 textBlock.innerHTML = '<br data-mce-bogus="1">'; 22436 return textBlock; 22437 }; 22438 const exitPreBlock = (editor, direction, range) => { 22439 const caretWalker = CaretWalker(editor.getBody()); 22440 const getVisualCaretPosition$1 = curry(getVisualCaretPosition, direction === 1 ? caretWalker.next : caretWalker.prev); 22441 if (range.collapsed) { 22442 const pre = editor.dom.getParent(range.startContainer, 'PRE'); 22443 if (!pre) { 22444 return; 22445 } 22446 const caretPos = getVisualCaretPosition$1(CaretPosition.fromRangeStart(range)); 22447 if (!caretPos) { 22448 const newBlock = SugarElement.fromDom(createTextBlock(editor)); 22449 if (direction === 1) { 22450 after$4(SugarElement.fromDom(pre), newBlock); 22451 } else { 22452 before$3(SugarElement.fromDom(pre), newBlock); 22453 } 22454 editor.selection.select(newBlock.dom, true); 22455 editor.selection.collapse(); 22456 } 22457 } 22458 }; 22459 const getHorizontalRange = (editor, forward) => { 22460 const direction = forward ? HDirection.Forwards : HDirection.Backwards; 22461 const range = editor.selection.getRng(); 22462 return moveToCeFalseHorizontally(direction, editor, range).orThunk(() => { 22463 exitPreBlock(editor, direction, range); 22464 return Optional.none(); 22465 }); 22466 }; 22467 const getVerticalRange = (editor, down) => { 22468 const direction = down ? 1 : -1; 22469 const range = editor.selection.getRng(); 22470 return moveToCeFalseVertically(direction, editor, range).orThunk(() => { 22471 exitPreBlock(editor, direction, range); 22472 return Optional.none(); 22473 }); 22474 }; 22475 const flipDirection = (selection, forward) => { 22476 const elm = forward ? selection.getEnd(true) : selection.getStart(true); 22477 return isRtl(elm) ? !forward : forward; 22478 }; 22479 const moveH$2 = (editor, forward) => getHorizontalRange(editor, flipDirection(editor.selection, forward)).exists(newRange => { 22480 moveToRange(editor, newRange); 22481 return true; 22482 }); 22483 const moveV$3 = (editor, down) => getVerticalRange(editor, down).exists(newRange => { 22484 moveToRange(editor, newRange); 22485 return true; 22486 }); 22487 const moveToLineEndPoint$1 = (editor, forward) => { 22488 const isCefPosition = forward ? isAfterContentEditableFalse : isBeforeContentEditableFalse; 22489 return moveToLineEndPoint$3(editor, forward, isCefPosition); 22490 }; 22491 const selectToEndPoint = (editor, forward) => getEdgeCefPosition(editor, !forward).map(pos => { 22492 const rng = pos.toRange(); 22493 const curRng = editor.selection.getRng(); 22494 if (forward) { 22495 rng.setStart(curRng.startContainer, curRng.startOffset); 22496 } else { 22497 rng.setEnd(curRng.endContainer, curRng.endOffset); 22498 } 22499 return rng; 22500 }).exists(rng => { 22501 moveToRange(editor, rng); 22502 return true; 22503 }); 22504 22505 const isTarget = node => contains$2(['figcaption'], name(node)); 22506 const getClosestTargetBlock = (pos, root) => { 22507 const isRoot = curry(eq, root); 22508 return closest$4(SugarElement.fromDom(pos.container()), isBlock$2, isRoot).filter(isTarget); 22509 }; 22510 const isAtFirstOrLastLine = (root, forward, pos) => forward ? isAtLastLine(root.dom, pos) : isAtFirstLine(root.dom, pos); 22511 const moveCaretToNewEmptyLine = (editor, forward) => { 22512 const root = SugarElement.fromDom(editor.getBody()); 22513 const pos = CaretPosition.fromRangeStart(editor.selection.getRng()); 22514 return getClosestTargetBlock(pos, root).exists(() => { 22515 if (isAtFirstOrLastLine(root, forward, pos)) { 22516 const insertFn = forward ? append$1 : prepend; 22517 const rng = insertEmptyLine(editor, root, insertFn); 22518 editor.selection.setRng(rng); 22519 return true; 22520 } else { 22521 return false; 22522 } 22523 }); 22524 }; 22525 const moveV$2 = (editor, forward) => { 22526 if (editor.selection.isCollapsed()) { 22527 return moveCaretToNewEmptyLine(editor, forward); 22528 } else { 22529 return false; 22530 } 22531 }; 22532 22533 const baseKeyPattern = { 22534 shiftKey: false, 22535 altKey: false, 22536 ctrlKey: false, 22537 metaKey: false, 22538 keyCode: 0 22539 }; 22540 const defaultPatterns = patterns => map$3(patterns, pattern => ({ 22541 ...baseKeyPattern, 22542 ...pattern 22543 })); 22544 const defaultDelayedPatterns = patterns => map$3(patterns, pattern => ({ 22545 ...baseKeyPattern, 22546 ...pattern 22547 })); 22548 const matchesEvent = (pattern, evt) => evt.keyCode === pattern.keyCode && evt.shiftKey === pattern.shiftKey && evt.altKey === pattern.altKey && evt.ctrlKey === pattern.ctrlKey && evt.metaKey === pattern.metaKey; 22549 const match$1 = (patterns, evt) => bind$3(defaultPatterns(patterns), pattern => matchesEvent(pattern, evt) ? [pattern] : []); 22550 const matchDelayed = (patterns, evt) => bind$3(defaultDelayedPatterns(patterns), pattern => matchesEvent(pattern, evt) ? [pattern] : []); 22551 const action = (f, ...x) => () => f.apply(null, x); 22552 const execute = (patterns, evt) => find$2(match$1(patterns, evt), pattern => pattern.action()); 22553 const executeWithDelayedAction = (patterns, evt) => findMap(matchDelayed(patterns, evt), pattern => pattern.action()); 22554 22555 const moveH$1 = (editor, forward) => { 22556 const direction = forward ? HDirection.Forwards : HDirection.Backwards; 22557 const range = editor.selection.getRng(); 22558 return moveHorizontally(editor, direction, range, isBeforeMedia, isAfterMedia, isMedia$2).exists(newRange => { 22559 moveToRange(editor, newRange); 22560 return true; 22561 }); 22562 }; 22563 const moveV$1 = (editor, down) => { 22564 const direction = down ? 1 : -1; 22565 const range = editor.selection.getRng(); 22566 return moveVertically(editor, direction, range, isBeforeMedia, isAfterMedia, isMedia$2).exists(newRange => { 22567 moveToRange(editor, newRange); 22568 return true; 22569 }); 22570 }; 22571 const moveToLineEndPoint = (editor, forward) => { 22572 const isNearMedia = forward ? isAfterMedia : isBeforeMedia; 22573 return moveToLineEndPoint$3(editor, forward, isNearMedia); 22574 }; 22575 22576 const adt = Adt.generate([ 22577 { none: ['current'] }, 22578 { first: ['current'] }, 22579 { 22580 middle: [ 22581 'current', 22582 'target' 22583 ] 22584 }, 22585 { last: ['current'] } 22586 ]); 22587 const none = current => adt.none(current); 22588 const CellLocation = { 22589 ...adt, 22590 none 22591 }; 22592 22593 const firstLayer = (scope, selector) => { 22594 return filterFirstLayer(scope, selector, always); 22595 }; 22596 const filterFirstLayer = (scope, selector, predicate) => { 22597 return bind$3(children$1(scope), x => { 22598 if (is$1(x, selector)) { 22599 return predicate(x) ? [x] : []; 22600 } else { 22601 return filterFirstLayer(x, selector, predicate); 22602 } 22603 }); 22604 }; 22605 22606 const lookup$1 = (tags, element, isRoot = never) => { 22607 if (isRoot(element)) { 22608 return Optional.none(); 22609 } 22610 if (contains$2(tags, name(element))) { 22611 return Optional.some(element); 22612 } 22613 const isRootOrUpperTable = elm => is$1(elm, 'table') || isRoot(elm); 22614 return ancestor$3(element, tags.join(','), isRootOrUpperTable); 22615 }; 22616 const cell = (element, isRoot) => lookup$1([ 22617 'td', 22618 'th' 22619 ], element, isRoot); 22620 const cells = ancestor => firstLayer(ancestor, 'th,td'); 22621 const table = (element, isRoot) => closest$3(element, 'table', isRoot); 22622 22623 const walk = (all, current, index, direction, isEligible = always) => { 22624 const forwards = direction === 1; 22625 if (!forwards && index <= 0) { 22626 return CellLocation.first(all[0]); 22627 } else if (forwards && index >= all.length - 1) { 22628 return CellLocation.last(all[all.length - 1]); 22629 } else { 22630 const newIndex = index + direction; 22631 const elem = all[newIndex]; 22632 return isEligible(elem) ? CellLocation.middle(current, elem) : walk(all, current, newIndex, direction, isEligible); 22633 } 22634 }; 22635 const detect = (current, isRoot) => { 22636 return table(current, isRoot).bind(table => { 22637 const all = cells(table); 22638 const index = findIndex$2(all, x => eq(current, x)); 22639 return index.map(index => ({ 22640 index, 22641 all 22642 })); 22643 }); 22644 }; 22645 const next = (current, isEligible, isRoot) => { 22646 const detection = detect(current, isRoot); 22647 return detection.fold(() => { 22648 return CellLocation.none(current); 22649 }, info => { 22650 return walk(info.all, current, info.index, 1, isEligible); 22651 }); 22652 }; 22653 const prev = (current, isEligible, isRoot) => { 22654 const detection = detect(current, isRoot); 22655 return detection.fold(() => { 22656 return CellLocation.none(); 22657 }, info => { 22658 return walk(info.all, current, info.index, -1, isEligible); 22659 }); 22660 }; 22661 22662 const deflate = (rect, delta) => ({ 22663 left: rect.left - delta, 22664 top: rect.top - delta, 22665 right: rect.right + delta * 2, 22666 bottom: rect.bottom + delta * 2, 22667 width: rect.width + delta, 22668 height: rect.height + delta 22669 }); 22670 const getCorners = (getYAxisValue, tds) => bind$3(tds, td => { 22671 const rect = deflate(clone$1(td.getBoundingClientRect()), -1); 22672 return [ 22673 { 22674 x: rect.left, 22675 y: getYAxisValue(rect), 22676 cell: td 22677 }, 22678 { 22679 x: rect.right, 22680 y: getYAxisValue(rect), 22681 cell: td 22682 } 22683 ]; 22684 }); 22685 const findClosestCorner = (corners, x, y) => foldl(corners, (acc, newCorner) => acc.fold(() => Optional.some(newCorner), oldCorner => { 22686 const oldDist = Math.sqrt(Math.abs(oldCorner.x - x) + Math.abs(oldCorner.y - y)); 22687 const newDist = Math.sqrt(Math.abs(newCorner.x - x) + Math.abs(newCorner.y - y)); 22688 return Optional.some(newDist < oldDist ? newCorner : oldCorner); 22689 }), Optional.none()); 22690 const getClosestCell = (getYAxisValue, isTargetCorner, table, x, y) => { 22691 const cells = descendants(SugarElement.fromDom(table), 'td,th,caption').map(e => e.dom); 22692 const corners = filter$5(getCorners(getYAxisValue, cells), corner => isTargetCorner(corner, y)); 22693 return findClosestCorner(corners, x, y).map(corner => corner.cell); 22694 }; 22695 const getBottomValue = rect => rect.bottom; 22696 const getTopValue = rect => rect.top; 22697 const isAbove = (corner, y) => corner.y < y; 22698 const isBelow = (corner, y) => corner.y > y; 22699 const getClosestCellAbove = curry(getClosestCell, getBottomValue, isAbove); 22700 const getClosestCellBelow = curry(getClosestCell, getTopValue, isBelow); 22701 const findClosestPositionInAboveCell = (table, pos) => head(pos.getClientRects()).bind(rect => getClosestCellAbove(table, rect.left, rect.top)).bind(cell => findClosestHorizontalPosition(getLastLinePositions(cell), pos)); 22702 const findClosestPositionInBelowCell = (table, pos) => last$3(pos.getClientRects()).bind(rect => getClosestCellBelow(table, rect.left, rect.top)).bind(cell => findClosestHorizontalPosition(getFirstLinePositions(cell), pos)); 22703 22704 const hasNextBreak = (getPositionsUntil, scope, lineInfo) => lineInfo.breakAt.exists(breakPos => getPositionsUntil(scope, breakPos).breakAt.isSome()); 22705 const startsWithWrapBreak = lineInfo => lineInfo.breakType === BreakType.Wrap && lineInfo.positions.length === 0; 22706 const startsWithBrBreak = lineInfo => lineInfo.breakType === BreakType.Br && lineInfo.positions.length === 1; 22707 const isAtTableCellLine = (getPositionsUntil, scope, pos) => { 22708 const lineInfo = getPositionsUntil(scope, pos); 22709 if (startsWithWrapBreak(lineInfo) || !isBr$6(pos.getNode()) && startsWithBrBreak(lineInfo)) { 22710 return !hasNextBreak(getPositionsUntil, scope, lineInfo); 22711 } else { 22712 return lineInfo.breakAt.isNone(); 22713 } 22714 }; 22715 const isAtFirstTableCellLine = curry(isAtTableCellLine, getPositionsUntilPreviousLine); 22716 const isAtLastTableCellLine = curry(isAtTableCellLine, getPositionsUntilNextLine); 22717 const isCaretAtStartOrEndOfTable = (forward, rng, table) => { 22718 const caretPos = CaretPosition.fromRangeStart(rng); 22719 return positionIn(!forward, table).exists(pos => pos.isEqual(caretPos)); 22720 }; 22721 const navigateHorizontally = (editor, forward, table, _td) => { 22722 const rng = editor.selection.getRng(); 22723 const direction = forward ? 1 : -1; 22724 if (isFakeCaretTableBrowser() && isCaretAtStartOrEndOfTable(forward, rng, table)) { 22725 showCaret(direction, editor, table, !forward, false).each(newRng => { 22726 moveToRange(editor, newRng); 22727 }); 22728 return true; 22729 } 22730 return false; 22731 }; 22732 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)); 22733 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)); 22734 const getTable = (previous, pos) => { 22735 const node = pos.getNode(previous); 22736 return isTable$2(node) ? Optional.some(node) : Optional.none(); 22737 }; 22738 const renderBlock = (down, editor, table) => { 22739 editor.undoManager.transact(() => { 22740 const insertFn = down ? after$4 : before$3; 22741 const rng = insertEmptyLine(editor, SugarElement.fromDom(table), insertFn); 22742 moveToRange(editor, rng); 22743 }); 22744 }; 22745 const moveCaret = (editor, down, pos) => { 22746 const table = down ? getTable(true, pos) : getTable(false, pos); 22747 const last = down === false; 22748 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))); 22749 }; 22750 const navigateVertically = (editor, down, table, td) => { 22751 const rng = editor.selection.getRng(); 22752 const pos = CaretPosition.fromRangeStart(rng); 22753 const root = editor.getBody(); 22754 if (!down && isAtFirstTableCellLine(td, pos)) { 22755 const newPos = getClosestAbovePosition(root, table, pos); 22756 moveCaret(editor, down, newPos); 22757 return true; 22758 } else if (down && isAtLastTableCellLine(td, pos)) { 22759 const newPos = getClosestBelowPosition(root, table, pos); 22760 moveCaret(editor, down, newPos); 22761 return true; 22762 } else { 22763 return false; 22764 } 22765 }; 22766 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); 22767 const moveH = (editor, forward) => move$1(editor, forward, navigateHorizontally); 22768 const moveV = (editor, forward) => move$1(editor, forward, navigateVertically); 22769 const getCellFirstCursorPosition = cell => { 22770 const selection = SimSelection.exact(cell, 0, cell, 0); 22771 return toNative(selection); 22772 }; 22773 const tabGo = (editor, isRoot, cell) => { 22774 return cell.fold(Optional.none, Optional.none, (_current, next) => { 22775 return first(next).map(cell => { 22776 return getCellFirstCursorPosition(cell); 22777 }); 22778 }, current => { 22779 editor.execCommand('mceTableInsertRowAfter'); 22780 return tabForward(editor, isRoot, current); 22781 }); 22782 }; 22783 const tabForward = (editor, isRoot, cell) => tabGo(editor, isRoot, next(cell, isEditable$3)); 22784 const tabBackward = (editor, isRoot, cell) => tabGo(editor, isRoot, prev(cell, isEditable$3)); 22785 const handleTab = (editor, forward) => { 22786 const rootElements = [ 22787 'table', 22788 'li', 22789 'dl' 22790 ]; 22791 const body = SugarElement.fromDom(editor.getBody()); 22792 const isRoot = element => { 22793 const name$1 = name(element); 22794 return eq(element, body) || contains$2(rootElements, name$1); 22795 }; 22796 const rng = editor.selection.getRng(); 22797 const container = SugarElement.fromDom(!forward ? rng.startContainer : rng.endContainer); 22798 return cell(container, isRoot).map(cell => { 22799 table(cell, isRoot).each(table => { 22800 editor.model.table.clearSelectedCells(table.dom); 22801 }); 22802 editor.selection.collapse(!forward); 22803 const navigation = !forward ? tabBackward : tabForward; 22804 const rng = navigation(editor, isRoot, cell); 22805 rng.each(range => { 22806 editor.selection.setRng(range); 22807 }); 22808 return true; 22809 }).getOr(false); 22810 }; 22811 22812 const executeKeydownOverride$4 = (editor, caret, evt) => { 22813 const isMac = Env.os.isMacOS() || Env.os.isiOS(); 22814 execute([ 22815 { 22816 keyCode: VK.RIGHT, 22817 action: action(moveH$2, editor, true) 22818 }, 22819 { 22820 keyCode: VK.LEFT, 22821 action: action(moveH$2, editor, false) 22822 }, 22823 { 22824 keyCode: VK.UP, 22825 action: action(moveV$3, editor, false) 22826 }, 22827 { 22828 keyCode: VK.DOWN, 22829 action: action(moveV$3, editor, true) 22830 }, 22831 ...isMac ? [ 22832 { 22833 keyCode: VK.UP, 22834 action: action(selectToEndPoint, editor, false), 22835 metaKey: true, 22836 shiftKey: true 22837 }, 22838 { 22839 keyCode: VK.DOWN, 22840 action: action(selectToEndPoint, editor, true), 22841 metaKey: true, 22842 shiftKey: true 22843 } 22844 ] : [], 22845 { 22846 keyCode: VK.RIGHT, 22847 action: action(moveH, editor, true) 22848 }, 22849 { 22850 keyCode: VK.LEFT, 22851 action: action(moveH, editor, false) 22852 }, 22853 { 22854 keyCode: VK.UP, 22855 action: action(moveV, editor, false) 22856 }, 22857 { 22858 keyCode: VK.DOWN, 22859 action: action(moveV, editor, true) 22860 }, 22861 { 22862 keyCode: VK.RIGHT, 22863 action: action(moveH$1, editor, true) 22864 }, 22865 { 22866 keyCode: VK.LEFT, 22867 action: action(moveH$1, editor, false) 22868 }, 22869 { 22870 keyCode: VK.UP, 22871 action: action(moveV$1, editor, false) 22872 }, 22873 { 22874 keyCode: VK.DOWN, 22875 action: action(moveV$1, editor, true) 22876 }, 22877 { 22878 keyCode: VK.RIGHT, 22879 action: action(move$2, editor, caret, true) 22880 }, 22881 { 22882 keyCode: VK.LEFT, 22883 action: action(move$2, editor, caret, false) 22884 }, 22885 { 22886 keyCode: VK.RIGHT, 22887 ctrlKey: !isMac, 22888 altKey: isMac, 22889 action: action(moveNextWord, editor, caret) 22890 }, 22891 { 22892 keyCode: VK.LEFT, 22893 ctrlKey: !isMac, 22894 altKey: isMac, 22895 action: action(movePrevWord, editor, caret) 22896 }, 22897 { 22898 keyCode: VK.UP, 22899 action: action(moveV$2, editor, false) 22900 }, 22901 { 22902 keyCode: VK.DOWN, 22903 action: action(moveV$2, editor, true) 22904 } 22905 ], evt).each(_ => { 22906 evt.preventDefault(); 22907 }); 22908 }; 22909 const setup$k = (editor, caret) => { 22910 editor.on('keydown', evt => { 22911 if (!evt.isDefaultPrevented()) { 22912 executeKeydownOverride$4(editor, caret, evt); 22913 } 22914 }); 22915 }; 22916 22917 const point = (container, offset) => ({ 22918 container, 22919 offset 22920 }); 22921 22922 const DOM$7 = DOMUtils.DOM; 22923 const alwaysNext = startNode => node => startNode === node ? -1 : 0; 22924 const isBoundary = dom => node => dom.isBlock(node) || contains$2([ 22925 'BR', 22926 'IMG', 22927 'HR', 22928 'INPUT' 22929 ], node.nodeName) || dom.getContentEditable(node) === 'false'; 22930 const textBefore = (node, offset, rootNode) => { 22931 if (isText$a(node) && offset >= 0) { 22932 return Optional.some(point(node, offset)); 22933 } else { 22934 const textSeeker = TextSeeker(DOM$7); 22935 return Optional.from(textSeeker.backwards(node, offset, alwaysNext(node), rootNode)).map(prev => point(prev.container, prev.container.data.length)); 22936 } 22937 }; 22938 const textAfter = (node, offset, rootNode) => { 22939 if (isText$a(node) && offset >= node.length) { 22940 return Optional.some(point(node, offset)); 22941 } else { 22942 const textSeeker = TextSeeker(DOM$7); 22943 return Optional.from(textSeeker.forwards(node, offset, alwaysNext(node), rootNode)).map(prev => point(prev.container, 0)); 22944 } 22945 }; 22946 const scanLeft = (node, offset, rootNode) => { 22947 if (!isText$a(node)) { 22948 return Optional.none(); 22949 } 22950 const text = node.data; 22951 if (offset >= 0 && offset <= text.length) { 22952 return Optional.some(point(node, offset)); 22953 } else { 22954 const textSeeker = TextSeeker(DOM$7); 22955 return Optional.from(textSeeker.backwards(node, offset, alwaysNext(node), rootNode)).bind(prev => { 22956 const prevText = prev.container.data; 22957 return scanLeft(prev.container, offset + prevText.length, rootNode); 22958 }); 22959 } 22960 }; 22961 const scanRight = (node, offset, rootNode) => { 22962 if (!isText$a(node)) { 22963 return Optional.none(); 22964 } 22965 const text = node.data; 22966 if (offset <= text.length) { 22967 return Optional.some(point(node, offset)); 22968 } else { 22969 const textSeeker = TextSeeker(DOM$7); 22970 return Optional.from(textSeeker.forwards(node, offset, alwaysNext(node), rootNode)).bind(next => scanRight(next.container, offset - text.length, rootNode)); 22971 } 22972 }; 22973 const repeatLeft = (dom, node, offset, process, rootNode) => { 22974 const search = TextSeeker(dom, isBoundary(dom)); 22975 return Optional.from(search.backwards(node, offset, process, rootNode)); 22976 }; 22977 22978 const isValidTextRange = rng => rng.collapsed && isText$a(rng.startContainer); 22979 const getText = rng => trim$1(rng.toString().replace(/\u00A0/g, ' ')); 22980 const isWhitespace = chr => chr !== '' && ' \xA0\f\n\r\t\x0B'.indexOf(chr) !== -1; 22981 22982 const stripTrigger = (text, trigger) => text.substring(trigger.length); 22983 const findTrigger = (text, index, trigger) => { 22984 let i; 22985 const firstChar = trigger.charAt(0); 22986 for (i = index - 1; i >= 0; i--) { 22987 const char = text.charAt(i); 22988 if (isWhitespace(char)) { 22989 return Optional.none(); 22990 } 22991 if (firstChar === char && contains$1(text, trigger, i, index)) { 22992 break; 22993 } 22994 } 22995 return Optional.some(i); 22996 }; 22997 const findStart = (dom, initRange, trigger, minChars = 0) => { 22998 if (!isValidTextRange(initRange)) { 22999 return Optional.none(); 23000 } 23001 const buffer = { 23002 text: '', 23003 offset: 0 23004 }; 23005 const findTriggerIndex = (element, offset, text) => { 23006 buffer.text = text + buffer.text; 23007 buffer.offset += offset; 23008 return findTrigger(buffer.text, buffer.offset, trigger).getOr(offset); 23009 }; 23010 const root = dom.getParent(initRange.startContainer, dom.isBlock) || dom.getRoot(); 23011 return repeatLeft(dom, initRange.startContainer, initRange.startOffset, findTriggerIndex, root).bind(spot => { 23012 const range = initRange.cloneRange(); 23013 range.setStart(spot.container, spot.offset); 23014 range.setEnd(initRange.endContainer, initRange.endOffset); 23015 if (range.collapsed) { 23016 return Optional.none(); 23017 } 23018 const text = getText(range); 23019 const triggerIndex = text.lastIndexOf(trigger); 23020 if (triggerIndex !== 0 || stripTrigger(text, trigger).length < minChars) { 23021 return Optional.none(); 23022 } else { 23023 return Optional.some({ 23024 text: stripTrigger(text, trigger), 23025 range, 23026 trigger 23027 }); 23028 } 23029 }); 23030 }; 23031 const getContext = (dom, initRange, trigger, minChars = 0) => detect$1(SugarElement.fromDom(initRange.startContainer)).fold(() => findStart(dom, initRange, trigger, minChars), elm => { 23032 const range = dom.createRng(); 23033 range.selectNode(elm.dom); 23034 const text = getText(range); 23035 return Optional.some({ 23036 range, 23037 text: stripTrigger(text, trigger), 23038 trigger 23039 }); 23040 }); 23041 23042 const isText$1 = node => node.nodeType === TEXT; 23043 const isElement = node => node.nodeType === ELEMENT; 23044 const toLast = node => { 23045 if (isText$1(node)) { 23046 return point(node, node.data.length); 23047 } else { 23048 const children = node.childNodes; 23049 return children.length > 0 ? toLast(children[children.length - 1]) : point(node, children.length); 23050 } 23051 }; 23052 const toLeaf = (node, offset) => { 23053 const children = node.childNodes; 23054 if (children.length > 0 && offset < children.length) { 23055 return toLeaf(children[offset], 0); 23056 } else if (children.length > 0 && isElement(node) && children.length === offset) { 23057 return toLast(children[children.length - 1]); 23058 } else { 23059 return point(node, offset); 23060 } 23061 }; 23062 23063 const isPreviousCharContent = (dom, leaf) => { 23064 var _a; 23065 const root = (_a = dom.getParent(leaf.container, dom.isBlock)) !== null && _a !== void 0 ? _a : dom.getRoot(); 23066 return repeatLeft(dom, leaf.container, leaf.offset, (_element, offset) => offset === 0 ? -1 : offset, root).filter(spot => { 23067 const char = spot.container.data.charAt(spot.offset - 1); 23068 return !isWhitespace(char); 23069 }).isSome(); 23070 }; 23071 const isStartOfWord = dom => rng => { 23072 const leaf = toLeaf(rng.startContainer, rng.startOffset); 23073 return !isPreviousCharContent(dom, leaf); 23074 }; 23075 const getTriggerContext = (dom, initRange, database) => findMap(database.triggers, trigger => getContext(dom, initRange, trigger)); 23076 const lookup = (editor, getDatabase) => { 23077 const database = getDatabase(); 23078 const rng = editor.selection.getRng(); 23079 return getTriggerContext(editor.dom, rng, database).bind(context => lookupWithContext(editor, getDatabase, context)); 23080 }; 23081 const lookupWithContext = (editor, getDatabase, context, fetchOptions = {}) => { 23082 var _a; 23083 const database = getDatabase(); 23084 const rng = editor.selection.getRng(); 23085 const startText = (_a = rng.startContainer.nodeValue) !== null && _a !== void 0 ? _a : ''; 23086 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)); 23087 if (autocompleters.length === 0) { 23088 return Optional.none(); 23089 } 23090 const lookupData = Promise.all(map$3(autocompleters, ac => { 23091 const fetchResult = ac.fetch(context.text, ac.maxResults, fetchOptions); 23092 return fetchResult.then(results => ({ 23093 matchText: context.text, 23094 items: results, 23095 columns: ac.columns, 23096 onAction: ac.onAction, 23097 highlightOn: ac.highlightOn 23098 })); 23099 })); 23100 return Optional.some({ 23101 lookupData, 23102 context 23103 }); 23104 }; 23105 23106 var SimpleResultType; 23107 (function (SimpleResultType) { 23108 SimpleResultType[SimpleResultType['Error'] = 0] = 'Error'; 23109 SimpleResultType[SimpleResultType['Value'] = 1] = 'Value'; 23110 }(SimpleResultType || (SimpleResultType = {}))); 23111 const fold$1 = (res, onError, onValue) => res.stype === SimpleResultType.Error ? onError(res.serror) : onValue(res.svalue); 23112 const partition = results => { 23113 const values = []; 23114 const errors = []; 23115 each$e(results, obj => { 23116 fold$1(obj, err => errors.push(err), val => values.push(val)); 23117 }); 23118 return { 23119 values, 23120 errors 23121 }; 23122 }; 23123 const mapError = (res, f) => { 23124 if (res.stype === SimpleResultType.Error) { 23125 return { 23126 stype: SimpleResultType.Error, 23127 serror: f(res.serror) 23128 }; 23129 } else { 23130 return res; 23131 } 23132 }; 23133 const map = (res, f) => { 23134 if (res.stype === SimpleResultType.Value) { 23135 return { 23136 stype: SimpleResultType.Value, 23137 svalue: f(res.svalue) 23138 }; 23139 } else { 23140 return res; 23141 } 23142 }; 23143 const bind$1 = (res, f) => { 23144 if (res.stype === SimpleResultType.Value) { 23145 return f(res.svalue); 23146 } else { 23147 return res; 23148 } 23149 }; 23150 const bindError = (res, f) => { 23151 if (res.stype === SimpleResultType.Error) { 23152 return f(res.serror); 23153 } else { 23154 return res; 23155 } 23156 }; 23157 const svalue = v => ({ 23158 stype: SimpleResultType.Value, 23159 svalue: v 23160 }); 23161 const serror = e => ({ 23162 stype: SimpleResultType.Error, 23163 serror: e 23164 }); 23165 const toResult = res => fold$1(res, Result.error, Result.value); 23166 const fromResult = res => res.fold(serror, svalue); 23167 const SimpleResult = { 23168 fromResult, 23169 toResult, 23170 svalue, 23171 partition, 23172 serror, 23173 bind: bind$1, 23174 bindError, 23175 map, 23176 mapError, 23177 fold: fold$1 23178 }; 23179 23180 const formatObj = input => { 23181 return isObject(input) && keys(input).length > 100 ? ' removed due to size' : JSON.stringify(input, null, 2); 23182 }; 23183 const formatErrors = errors => { 23184 const es = errors.length > 10 ? errors.slice(0, 10).concat([{ 23185 path: [], 23186 getErrorInfo: constant('... (only showing first ten failures)') 23187 }]) : errors; 23188 return map$3(es, e => { 23189 return 'Failed path: (' + e.path.join(' > ') + ')\n' + e.getErrorInfo(); 23190 }); 23191 }; 23192 23193 const nu = (path, getErrorInfo) => { 23194 return SimpleResult.serror([{ 23195 path, 23196 getErrorInfo 23197 }]); 23198 }; 23199 const missingRequired = (path, key, obj) => nu(path, () => 'Could not find valid *required* value for "' + key + '" in ' + formatObj(obj)); 23200 const missingKey = (path, key) => nu(path, () => 'Choice schema did not contain choice key: "' + key + '"'); 23201 const missingBranch = (path, branches, branch) => nu(path, () => 'The chosen schema: "' + branch + '" did not exist in branches: ' + formatObj(branches)); 23202 const custom = (path, err) => nu(path, constant(err)); 23203 23204 const chooseFrom = (path, input, branches, ch) => { 23205 const fields = get$a(branches, ch); 23206 return fields.fold(() => missingBranch(path, branches, ch), vp => vp.extract(path.concat(['branch: ' + ch]), input)); 23207 }; 23208 const choose$1 = (key, branches) => { 23209 const extract = (path, input) => { 23210 const choice = get$a(input, key); 23211 return choice.fold(() => missingKey(path, key), chosen => chooseFrom(path, input, branches, chosen)); 23212 }; 23213 const toString = () => 'chooseOn(' + key + '). Possible values: ' + keys(branches); 23214 return { 23215 extract, 23216 toString 23217 }; 23218 }; 23219 23220 const shallow = (old, nu) => { 23221 return nu; 23222 }; 23223 const deep = (old, nu) => { 23224 const bothObjects = isPlainObject(old) && isPlainObject(nu); 23225 return bothObjects ? deepMerge(old, nu) : nu; 23226 }; 23227 const baseMerge = merger => { 23228 return (...objects) => { 23229 if (objects.length === 0) { 23230 throw new Error(`Can't merge zero objects`); 23231 } 23232 const ret = {}; 23233 for (let j = 0; j < objects.length; j++) { 23234 const curObject = objects[j]; 23235 for (const key in curObject) { 23236 if (has$2(curObject, key)) { 23237 ret[key] = merger(ret[key], curObject[key]); 23238 } 23239 } 23240 } 23241 return ret; 23242 }; 23243 }; 23244 const deepMerge = baseMerge(deep); 23245 const merge = baseMerge(shallow); 23246 23247 const required = () => ({ 23248 tag: 'required', 23249 process: {} 23250 }); 23251 const defaultedThunk = fallbackThunk => ({ 23252 tag: 'defaultedThunk', 23253 process: fallbackThunk 23254 }); 23255 const defaulted$1 = fallback => defaultedThunk(constant(fallback)); 23256 const asOption = () => ({ 23257 tag: 'option', 23258 process: {} 23259 }); 23260 23261 const mergeValues = (values, base) => values.length > 0 ? SimpleResult.svalue(deepMerge(base, merge.apply(undefined, values))) : SimpleResult.svalue(base); 23262 const mergeErrors = errors => compose(SimpleResult.serror, flatten)(errors); 23263 const consolidateObj = (objects, base) => { 23264 const partition = SimpleResult.partition(objects); 23265 return partition.errors.length > 0 ? mergeErrors(partition.errors) : mergeValues(partition.values, base); 23266 }; 23267 const consolidateArr = objects => { 23268 const partitions = SimpleResult.partition(objects); 23269 return partitions.errors.length > 0 ? mergeErrors(partitions.errors) : SimpleResult.svalue(partitions.values); 23270 }; 23271 const ResultCombine = { 23272 consolidateObj, 23273 consolidateArr 23274 }; 23275 23276 const field$1 = (key, newKey, presence, prop) => ({ 23277 tag: 'field', 23278 key, 23279 newKey, 23280 presence, 23281 prop 23282 }); 23283 const customField$1 = (newKey, instantiator) => ({ 23284 tag: 'custom', 23285 newKey, 23286 instantiator 23287 }); 23288 const fold = (value, ifField, ifCustom) => { 23289 switch (value.tag) { 23290 case 'field': 23291 return ifField(value.key, value.newKey, value.presence, value.prop); 23292 case 'custom': 23293 return ifCustom(value.newKey, value.instantiator); 23294 } 23295 }; 23296 23297 const value = validator => { 23298 const extract = (path, val) => { 23299 return SimpleResult.bindError(validator(val), err => custom(path, err)); 23300 }; 23301 const toString = constant('val'); 23302 return { 23303 extract, 23304 toString 23305 }; 23306 }; 23307 const anyValue$1 = value(SimpleResult.svalue); 23308 23309 const requiredAccess = (path, obj, key, bundle) => get$a(obj, key).fold(() => missingRequired(path, key, obj), bundle); 23310 const fallbackAccess = (obj, key, fallback, bundle) => { 23311 const v = get$a(obj, key).getOrThunk(() => fallback(obj)); 23312 return bundle(v); 23313 }; 23314 const optionAccess = (obj, key, bundle) => bundle(get$a(obj, key)); 23315 const optionDefaultedAccess = (obj, key, fallback, bundle) => { 23316 const opt = get$a(obj, key).map(val => val === true ? fallback(obj) : val); 23317 return bundle(opt); 23318 }; 23319 const extractField = (field, path, obj, key, prop) => { 23320 const bundle = av => prop.extract(path.concat([key]), av); 23321 const bundleAsOption = optValue => optValue.fold(() => SimpleResult.svalue(Optional.none()), ov => { 23322 const result = prop.extract(path.concat([key]), ov); 23323 return SimpleResult.map(result, Optional.some); 23324 }); 23325 switch (field.tag) { 23326 case 'required': 23327 return requiredAccess(path, obj, key, bundle); 23328 case 'defaultedThunk': 23329 return fallbackAccess(obj, key, field.process, bundle); 23330 case 'option': 23331 return optionAccess(obj, key, bundleAsOption); 23332 case 'defaultedOptionThunk': 23333 return optionDefaultedAccess(obj, key, field.process, bundleAsOption); 23334 case 'mergeWithThunk': { 23335 return fallbackAccess(obj, key, constant({}), v => { 23336 const result = deepMerge(field.process(obj), v); 23337 return bundle(result); 23338 }); 23339 } 23340 } 23341 }; 23342 const extractFields = (path, obj, fields) => { 23343 const success = {}; 23344 const errors = []; 23345 for (const field of fields) { 23346 fold(field, (key, newKey, presence, prop) => { 23347 const result = extractField(presence, path, obj, key, prop); 23348 SimpleResult.fold(result, err => { 23349 errors.push(...err); 23350 }, res => { 23351 success[newKey] = res; 23352 }); 23353 }, (newKey, instantiator) => { 23354 success[newKey] = instantiator(obj); 23355 }); 23356 } 23357 return errors.length > 0 ? SimpleResult.serror(errors) : SimpleResult.svalue(success); 23358 }; 23359 const objOf = values => { 23360 const extract = (path, o) => extractFields(path, o, values); 23361 const toString = () => { 23362 const fieldStrings = map$3(values, value => fold(value, (key, _okey, _presence, prop) => key + ' -> ' + prop.toString(), (newKey, _instantiator) => 'state(' + newKey + ')')); 23363 return 'obj{\n' + fieldStrings.join('\n') + '}'; 23364 }; 23365 return { 23366 extract, 23367 toString 23368 }; 23369 }; 23370 const arrOf = prop => { 23371 const extract = (path, array) => { 23372 const results = map$3(array, (a, i) => prop.extract(path.concat(['[' + i + ']']), a)); 23373 return ResultCombine.consolidateArr(results); 23374 }; 23375 const toString = () => 'array(' + prop.toString() + ')'; 23376 return { 23377 extract, 23378 toString 23379 }; 23380 }; 23381 23382 const valueOf = validator => value(v => validator(v).fold(SimpleResult.serror, SimpleResult.svalue)); 23383 const extractValue = (label, prop, obj) => { 23384 const res = prop.extract([label], obj); 23385 return SimpleResult.mapError(res, errs => ({ 23386 input: obj, 23387 errors: errs 23388 })); 23389 }; 23390 const asRaw = (label, prop, obj) => SimpleResult.toResult(extractValue(label, prop, obj)); 23391 const formatError = errInfo => { 23392 return 'Errors: \n' + formatErrors(errInfo.errors).join('\n') + '\n\nInput object: ' + formatObj(errInfo.input); 23393 }; 23394 const choose = (key, branches) => choose$1(key, map$2(branches, objOf)); 23395 23396 const anyValue = constant(anyValue$1); 23397 const typedValue = (validator, expectedType) => value(a => { 23398 const actualType = typeof a; 23399 return validator(a) ? SimpleResult.svalue(a) : SimpleResult.serror(`Expected type: ${ expectedType } but got: ${ actualType }`); 23400 }); 23401 const number = typedValue(isNumber, 'number'); 23402 const string = typedValue(isString, 'string'); 23403 const boolean = typedValue(isBoolean, 'boolean'); 23404 const functionProcessor = typedValue(isFunction, 'function'); 23405 23406 const field = field$1; 23407 const customField = customField$1; 23408 const validateEnum = values => valueOf(value => contains$2(values, value) ? Result.value(value) : Result.error(`Unsupported value: "${ value }", choose one of "${ values.join(', ') }".`)); 23409 const requiredOf = (key, schema) => field(key, key, required(), schema); 23410 const requiredString = key => requiredOf(key, string); 23411 const requiredFunction = key => requiredOf(key, functionProcessor); 23412 const requiredArrayOf = (key, schema) => field(key, key, required(), arrOf(schema)); 23413 const optionOf = (key, schema) => field(key, key, asOption(), schema); 23414 const optionString = key => optionOf(key, string); 23415 const optionFunction = key => optionOf(key, functionProcessor); 23416 const defaulted = (key, fallback) => field(key, key, defaulted$1(fallback), anyValue()); 23417 const defaultedOf = (key, fallback, schema) => field(key, key, defaulted$1(fallback), schema); 23418 const defaultedNumber = (key, fallback) => defaultedOf(key, fallback, number); 23419 const defaultedString = (key, fallback) => defaultedOf(key, fallback, string); 23420 const defaultedStringEnum = (key, fallback, values) => defaultedOf(key, fallback, validateEnum(values)); 23421 const defaultedBoolean = (key, fallback) => defaultedOf(key, fallback, boolean); 23422 const defaultedFunction = (key, fallback) => defaultedOf(key, fallback, functionProcessor); 23423 const defaultedArrayOf = (key, fallback, schema) => defaultedOf(key, fallback, arrOf(schema)); 23424 23425 const type = requiredString('type'); 23426 const fetch$1 = requiredFunction('fetch'); 23427 const onAction = requiredFunction('onAction'); 23428 const onSetup = defaultedFunction('onSetup', () => noop); 23429 const optionalText = optionString('text'); 23430 const optionalIcon = optionString('icon'); 23431 const optionalTooltip = optionString('tooltip'); 23432 const optionalLabel = optionString('label'); 23433 const active = defaultedBoolean('active', false); 23434 const enabled = defaultedBoolean('enabled', true); 23435 const primary = defaultedBoolean('primary', false); 23436 const defaultedColumns = num => defaulted('columns', num); 23437 const defaultedType = type => defaultedString('type', type); 23438 23439 const autocompleterSchema = objOf([ 23440 type, 23441 requiredString('trigger'), 23442 defaultedNumber('minChars', 1), 23443 defaultedColumns(1), 23444 defaultedNumber('maxResults', 10), 23445 optionFunction('matches'), 23446 fetch$1, 23447 onAction, 23448 defaultedArrayOf('highlightOn', [], string) 23449 ]); 23450 const createAutocompleter = spec => asRaw('Autocompleter', autocompleterSchema, { 23451 trigger: spec.ch, 23452 ...spec 23453 }); 23454 23455 const baseToolbarButtonFields = [ 23456 enabled, 23457 optionalTooltip, 23458 optionalIcon, 23459 optionalText, 23460 onSetup 23461 ]; 23462 23463 const baseToolbarToggleButtonFields = [active].concat(baseToolbarButtonFields); 23464 23465 const contextBarFields = [ 23466 defaultedFunction('predicate', never), 23467 defaultedStringEnum('scope', 'node', [ 23468 'node', 23469 'editor' 23470 ]), 23471 defaultedStringEnum('position', 'selection', [ 23472 'node', 23473 'selection', 23474 'line' 23475 ]) 23476 ]; 23477 23478 const contextButtonFields = baseToolbarButtonFields.concat([ 23479 defaultedType('contextformbutton'), 23480 primary, 23481 onAction, 23482 customField('original', identity) 23483 ]); 23484 const contextToggleButtonFields = baseToolbarToggleButtonFields.concat([ 23485 defaultedType('contextformbutton'), 23486 primary, 23487 onAction, 23488 customField('original', identity) 23489 ]); 23490 const launchButtonFields = baseToolbarButtonFields.concat([defaultedType('contextformbutton')]); 23491 const launchToggleButtonFields = baseToolbarToggleButtonFields.concat([defaultedType('contextformtogglebutton')]); 23492 const toggleOrNormal = choose('type', { 23493 contextformbutton: contextButtonFields, 23494 contextformtogglebutton: contextToggleButtonFields 23495 }); 23496 objOf([ 23497 defaultedType('contextform'), 23498 defaultedFunction('initValue', constant('')), 23499 optionalLabel, 23500 requiredArrayOf('commands', toggleOrNormal), 23501 optionOf('launch', choose('type', { 23502 contextformbutton: launchButtonFields, 23503 contextformtogglebutton: launchToggleButtonFields 23504 })) 23505 ].concat(contextBarFields)); 23506 23507 const register$2 = editor => { 23508 const popups = editor.ui.registry.getAll().popups; 23509 const dataset = map$2(popups, popup => createAutocompleter(popup).fold(err => { 23510 throw new Error(formatError(err)); 23511 }, identity)); 23512 const triggers = stringArray(mapToArray(dataset, v => v.trigger)); 23513 const datasetValues = values(dataset); 23514 const lookupByTrigger = trigger => filter$5(datasetValues, dv => dv.trigger === trigger); 23515 return { 23516 dataset, 23517 triggers, 23518 lookupByTrigger 23519 }; 23520 }; 23521 23522 const setupEditorInput = (editor, api) => { 23523 const update = last$1(api.load, 50); 23524 editor.on('keypress compositionend', e => { 23525 if (e.which === 27) { 23526 return; 23527 } 23528 update.throttle(); 23529 }); 23530 editor.on('keydown', e => { 23531 const keyCode = e.which; 23532 if (keyCode === 8) { 23533 update.throttle(); 23534 } else if (keyCode === 27) { 23535 api.cancelIfNecessary(); 23536 } 23537 }); 23538 editor.on('remove', update.cancel); 23539 }; 23540 const setup$j = editor => { 23541 const activeAutocompleter = value$2(); 23542 const uiActive = Cell(false); 23543 const isActive = activeAutocompleter.isSet; 23544 const cancelIfNecessary = () => { 23545 if (isActive()) { 23546 removeAutocompleterDecoration(editor); 23547 fireAutocompleterEnd(editor); 23548 uiActive.set(false); 23549 activeAutocompleter.clear(); 23550 } 23551 }; 23552 const commenceIfNecessary = context => { 23553 if (!isActive()) { 23554 addAutocompleterDecoration(editor, context.range); 23555 activeAutocompleter.set({ 23556 trigger: context.trigger, 23557 matchLength: context.text.length 23558 }); 23559 } 23560 }; 23561 const getAutocompleters = cached(() => register$2(editor)); 23562 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)); 23563 const load = fetchOptions => { 23564 doLookup(fetchOptions).fold(cancelIfNecessary, lookupInfo => { 23565 commenceIfNecessary(lookupInfo.context); 23566 lookupInfo.lookupData.then(lookupData => { 23567 activeAutocompleter.get().map(ac => { 23568 const context = lookupInfo.context; 23569 if (ac.trigger === context.trigger) { 23570 if (context.text.length - ac.matchLength >= 10) { 23571 cancelIfNecessary(); 23572 } else { 23573 activeAutocompleter.set({ 23574 ...ac, 23575 matchLength: context.text.length 23576 }); 23577 if (uiActive.get()) { 23578 fireAutocompleterUpdate(editor, { lookupData }); 23579 } else { 23580 uiActive.set(true); 23581 fireAutocompleterStart(editor, { lookupData }); 23582 } 23583 } 23584 } 23585 }); 23586 }); 23587 }); 23588 }; 23589 editor.addCommand('mceAutocompleterReload', (_ui, value) => { 23590 const fetchOptions = isObject(value) ? value.fetchOptions : {}; 23591 load(fetchOptions); 23592 }); 23593 editor.addCommand('mceAutocompleterClose', cancelIfNecessary); 23594 setupEditorInput(editor, { 23595 cancelIfNecessary, 23596 load 23597 }); 23598 }; 23599 23600 const createAndFireInputEvent = eventType => (editor, inputType, specifics = {}) => { 23601 const target = editor.getBody(); 23602 const overrides = { 23603 bubbles: true, 23604 composed: true, 23605 data: null, 23606 isComposing: false, 23607 detail: 0, 23608 view: null, 23609 target, 23610 currentTarget: target, 23611 eventPhase: Event.AT_TARGET, 23612 originalTarget: target, 23613 explicitOriginalTarget: target, 23614 isTrusted: false, 23615 srcElement: target, 23616 cancelable: false, 23617 preventDefault: noop, 23618 inputType 23619 }; 23620 const input = clone$3(new InputEvent(eventType)); 23621 return editor.dispatch(eventType, { 23622 ...input, 23623 ...overrides, 23624 ...specifics 23625 }); 23626 }; 23627 const fireInputEvent = createAndFireInputEvent('input'); 23628 const fireBeforeInputEvent = createAndFireInputEvent('beforeinput'); 23629 23630 const executeKeydownOverride$3 = (editor, caret, evt) => { 23631 const inputType = evt.keyCode === VK.BACKSPACE ? 'deleteContentBackward' : 'deleteContentForward'; 23632 executeWithDelayedAction([ 23633 { 23634 keyCode: VK.BACKSPACE, 23635 action: action(backspaceDelete, editor) 23636 }, 23637 { 23638 keyCode: VK.BACKSPACE, 23639 action: action(backspaceDelete$5, editor, false) 23640 }, 23641 { 23642 keyCode: VK.DELETE, 23643 action: action(backspaceDelete$5, editor, true) 23644 }, 23645 { 23646 keyCode: VK.BACKSPACE, 23647 action: action(backspaceDelete$6, editor, false) 23648 }, 23649 { 23650 keyCode: VK.DELETE, 23651 action: action(backspaceDelete$6, editor, true) 23652 }, 23653 { 23654 keyCode: VK.BACKSPACE, 23655 action: action(backspaceDelete$3, editor, caret, false) 23656 }, 23657 { 23658 keyCode: VK.DELETE, 23659 action: action(backspaceDelete$3, editor, caret, true) 23660 }, 23661 { 23662 keyCode: VK.BACKSPACE, 23663 action: action(backspaceDelete$9, editor, false) 23664 }, 23665 { 23666 keyCode: VK.DELETE, 23667 action: action(backspaceDelete$9, editor, true) 23668 }, 23669 { 23670 keyCode: VK.BACKSPACE, 23671 action: action(backspaceDelete$4, editor, false) 23672 }, 23673 { 23674 keyCode: VK.DELETE, 23675 action: action(backspaceDelete$4, editor, true) 23676 }, 23677 { 23678 keyCode: VK.BACKSPACE, 23679 action: action(backspaceDelete$1, editor, false) 23680 }, 23681 { 23682 keyCode: VK.DELETE, 23683 action: action(backspaceDelete$1, editor, true) 23684 }, 23685 { 23686 keyCode: VK.BACKSPACE, 23687 action: action(backspaceDelete$7, editor, false) 23688 }, 23689 { 23690 keyCode: VK.DELETE, 23691 action: action(backspaceDelete$7, editor, true) 23692 }, 23693 { 23694 keyCode: VK.BACKSPACE, 23695 action: action(backspaceDelete$8, editor, false) 23696 }, 23697 { 23698 keyCode: VK.DELETE, 23699 action: action(backspaceDelete$8, editor, true) 23700 }, 23701 { 23702 keyCode: VK.BACKSPACE, 23703 action: action(backspaceDelete$2, editor, false) 23704 }, 23705 { 23706 keyCode: VK.DELETE, 23707 action: action(backspaceDelete$2, editor, true) 23708 } 23709 ], evt).filter(_ => editor.selection.isEditable()).each(applyAction => { 23710 evt.preventDefault(); 23711 const beforeInput = fireBeforeInputEvent(editor, inputType); 23712 if (!beforeInput.isDefaultPrevented()) { 23713 applyAction(); 23714 fireInputEvent(editor, inputType); 23715 } 23716 }); 23717 }; 23718 const executeKeyupOverride = (editor, evt, isBackspaceKeydown) => { 23719 const platform = detect$2(); 23720 const os = platform.os; 23721 const browser = platform.browser; 23722 const multiDeleteKeyPatterns = os.isMacOS() ? [ 23723 { 23724 keyCode: VK.BACKSPACE, 23725 altKey: true, 23726 action: action(refreshCaret, editor) 23727 }, 23728 { 23729 keyCode: VK.DELETE, 23730 altKey: true, 23731 action: action(refreshCaret, editor) 23732 } 23733 ] : [ 23734 { 23735 keyCode: VK.BACKSPACE, 23736 ctrlKey: true, 23737 action: action(refreshCaret, editor) 23738 }, 23739 { 23740 keyCode: VK.DELETE, 23741 ctrlKey: true, 23742 action: action(refreshCaret, editor) 23743 } 23744 ]; 23745 if (os.isMacOS() && isBackspaceKeydown) { 23746 multiDeleteKeyPatterns.push({ 23747 keyCode: browser.isFirefox() ? 224 : 91, 23748 action: action(refreshCaret, editor) 23749 }); 23750 } 23751 execute([ 23752 { 23753 keyCode: VK.BACKSPACE, 23754 action: action(paddEmptyElement, editor) 23755 }, 23756 { 23757 keyCode: VK.DELETE, 23758 action: action(paddEmptyElement, editor) 23759 }, 23760 ...multiDeleteKeyPatterns 23761 ], evt); 23762 }; 23763 const setup$i = (editor, caret) => { 23764 let isBackspaceKeydown = false; 23765 editor.on('keydown', evt => { 23766 isBackspaceKeydown = evt.keyCode === VK.BACKSPACE; 23767 if (!evt.isDefaultPrevented()) { 23768 executeKeydownOverride$3(editor, caret, evt); 23769 } 23770 }); 23771 editor.on('keyup', evt => { 23772 if (!evt.isDefaultPrevented()) { 23773 executeKeyupOverride(editor, evt, isBackspaceKeydown); 23774 } 23775 isBackspaceKeydown = false; 23776 }); 23777 }; 23778 23779 const firstNonWhiteSpaceNodeSibling = node => { 23780 while (node) { 23781 if (isElement$6(node) || isText$a(node) && node.data && /[\r\n\s]/.test(node.data)) { 23782 return node; 23783 } 23784 node = node.nextSibling; 23785 } 23786 return null; 23787 }; 23788 const moveToCaretPosition = (editor, root) => { 23789 const dom = editor.dom; 23790 const moveCaretBeforeOnEnterElementsMap = editor.schema.getMoveCaretBeforeOnEnterElements(); 23791 if (!root) { 23792 return; 23793 } 23794 if (/^(LI|DT|DD)$/.test(root.nodeName)) { 23795 const firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild); 23796 if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) { 23797 root.insertBefore(dom.doc.createTextNode(nbsp), root.firstChild); 23798 } 23799 } 23800 const rng = dom.createRng(); 23801 root.normalize(); 23802 if (root.hasChildNodes()) { 23803 const walker = new DomTreeWalker(root, root); 23804 let lastNode = root; 23805 let node; 23806 while (node = walker.current()) { 23807 if (isText$a(node)) { 23808 rng.setStart(node, 0); 23809 rng.setEnd(node, 0); 23810 break; 23811 } 23812 if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) { 23813 rng.setStartBefore(node); 23814 rng.setEndBefore(node); 23815 break; 23816 } 23817 lastNode = node; 23818 node = walker.next(); 23819 } 23820 if (!node) { 23821 rng.setStart(lastNode, 0); 23822 rng.setEnd(lastNode, 0); 23823 } 23824 } else { 23825 if (isBr$6(root)) { 23826 if (root.nextSibling && dom.isBlock(root.nextSibling)) { 23827 rng.setStartBefore(root); 23828 rng.setEndBefore(root); 23829 } else { 23830 rng.setStartAfter(root); 23831 rng.setEndAfter(root); 23832 } 23833 } else { 23834 rng.setStart(root, 0); 23835 rng.setEnd(root, 0); 23836 } 23837 } 23838 editor.selection.setRng(rng); 23839 scrollRangeIntoView(editor, rng); 23840 }; 23841 const getEditableRoot = (dom, node) => { 23842 const root = dom.getRoot(); 23843 let editableRoot; 23844 let parent = node; 23845 while (parent !== root && parent && dom.getContentEditable(parent) !== 'false') { 23846 if (dom.getContentEditable(parent) === 'true') { 23847 editableRoot = parent; 23848 } 23849 parent = parent.parentNode; 23850 } 23851 return parent !== root ? editableRoot : root; 23852 }; 23853 const getParentBlock$1 = editor => { 23854 return Optional.from(editor.dom.getParent(editor.selection.getStart(true), editor.dom.isBlock)); 23855 }; 23856 const getParentBlockName = editor => { 23857 return getParentBlock$1(editor).fold(constant(''), parentBlock => { 23858 return parentBlock.nodeName.toUpperCase(); 23859 }); 23860 }; 23861 const isListItemParentBlock = editor => { 23862 return getParentBlock$1(editor).filter(elm => { 23863 return isListItem$1(SugarElement.fromDom(elm)); 23864 }).isSome(); 23865 }; 23866 23867 const getDetailsRoot = (editor, element) => editor.dom.getParent(element, isDetails); 23868 const isAtDetailsEdge = (root, element, isTextBlock) => { 23869 let node = element; 23870 while (node && node !== root && isNull(node.nextSibling)) { 23871 const parent = node.parentElement; 23872 if (!parent || !isTextBlock(parent)) { 23873 return isDetails(parent); 23874 } 23875 node = parent; 23876 } 23877 return false; 23878 }; 23879 const isLastEmptyBlockInDetails = (editor, shiftKey, element) => !shiftKey && element.nodeName.toLowerCase() === getForcedRootBlock(editor) && editor.dom.isEmpty(element) && isAtDetailsEdge(editor.getBody(), element, el => has$2(editor.schema.getTextBlockElements(), el.nodeName.toLowerCase())); 23880 const insertNewLine = (editor, createNewBlock, parentBlock) => { 23881 var _a, _b, _c; 23882 const newBlock = createNewBlock(getForcedRootBlock(editor)); 23883 const root = getDetailsRoot(editor, parentBlock); 23884 if (!root) { 23885 return; 23886 } 23887 editor.dom.insertAfter(newBlock, root); 23888 moveToCaretPosition(editor, newBlock); 23889 if (((_c = (_b = (_a = parentBlock.parentElement) === null || _a === void 0 ? void 0 : _a.childNodes) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) > 1) { 23890 editor.dom.remove(parentBlock); 23891 } 23892 }; 23893 23894 const hasFirstChild = (elm, name) => { 23895 return elm.firstChild && elm.firstChild.nodeName === name; 23896 }; 23897 const isFirstChild = elm => { 23898 var _a; 23899 return ((_a = elm.parentNode) === null || _a === void 0 ? void 0 : _a.firstChild) === elm; 23900 }; 23901 const hasParent = (elm, parentName) => { 23902 const parentNode = elm === null || elm === void 0 ? void 0 : elm.parentNode; 23903 return isNonNullable(parentNode) && parentNode.nodeName === parentName; 23904 }; 23905 const isListBlock = elm => { 23906 return isNonNullable(elm) && /^(OL|UL|LI)$/.test(elm.nodeName); 23907 }; 23908 const isListItem = elm => { 23909 return isNonNullable(elm) && /^(LI|DT|DD)$/.test(elm.nodeName); 23910 }; 23911 const isNestedList = elm => { 23912 return isListBlock(elm) && isListBlock(elm.parentNode); 23913 }; 23914 const getContainerBlock = containerBlock => { 23915 const containerBlockParent = containerBlock.parentNode; 23916 return isListItem(containerBlockParent) ? containerBlockParent : containerBlock; 23917 }; 23918 const isFirstOrLastLi = (containerBlock, parentBlock, first) => { 23919 let node = containerBlock[first ? 'firstChild' : 'lastChild']; 23920 while (node) { 23921 if (isElement$6(node)) { 23922 break; 23923 } 23924 node = node[first ? 'nextSibling' : 'previousSibling']; 23925 } 23926 return node === parentBlock; 23927 }; 23928 const insert$3 = (editor, createNewBlock, containerBlock, parentBlock, newBlockName) => { 23929 const dom = editor.dom; 23930 const rng = editor.selection.getRng(); 23931 const containerParent = containerBlock.parentNode; 23932 if (containerBlock === editor.getBody() || !containerParent) { 23933 return; 23934 } 23935 if (isNestedList(containerBlock)) { 23936 newBlockName = 'LI'; 23937 } 23938 let newBlock = createNewBlock(newBlockName); 23939 if (isFirstOrLastLi(containerBlock, parentBlock, true) && isFirstOrLastLi(containerBlock, parentBlock, false)) { 23940 if (hasParent(containerBlock, 'LI')) { 23941 const containerBlockParent = getContainerBlock(containerBlock); 23942 dom.insertAfter(newBlock, containerBlockParent); 23943 if (isFirstChild(containerBlock)) { 23944 dom.remove(containerBlockParent); 23945 } else { 23946 dom.remove(containerBlock); 23947 } 23948 } else { 23949 dom.replace(newBlock, containerBlock); 23950 } 23951 } else if (isFirstOrLastLi(containerBlock, parentBlock, true)) { 23952 if (hasParent(containerBlock, 'LI')) { 23953 dom.insertAfter(newBlock, getContainerBlock(containerBlock)); 23954 newBlock.appendChild(dom.doc.createTextNode(' ')); 23955 newBlock.appendChild(containerBlock); 23956 } else { 23957 containerParent.insertBefore(newBlock, containerBlock); 23958 } 23959 dom.remove(parentBlock); 23960 } else if (isFirstOrLastLi(containerBlock, parentBlock, false)) { 23961 dom.insertAfter(newBlock, getContainerBlock(containerBlock)); 23962 dom.remove(parentBlock); 23963 } else { 23964 containerBlock = getContainerBlock(containerBlock); 23965 const tmpRng = rng.cloneRange(); 23966 tmpRng.setStartAfter(parentBlock); 23967 tmpRng.setEndAfter(containerBlock); 23968 const fragment = tmpRng.extractContents(); 23969 if (newBlockName === 'LI' && hasFirstChild(fragment, 'LI')) { 23970 newBlock = fragment.firstChild; 23971 dom.insertAfter(fragment, containerBlock); 23972 } else { 23973 dom.insertAfter(fragment, containerBlock); 23974 dom.insertAfter(newBlock, containerBlock); 23975 } 23976 dom.remove(parentBlock); 23977 } 23978 moveToCaretPosition(editor, newBlock); 23979 }; 23980 23981 const trimZwsp = fragment => { 23982 each$e(descendants$1(SugarElement.fromDom(fragment), isText$b), text => { 23983 const rawNode = text.dom; 23984 rawNode.nodeValue = trim$1(rawNode.data); 23985 }); 23986 }; 23987 const isWithinNonEditableList = (editor, node) => { 23988 const parentList = editor.dom.getParent(node, 'ol,ul,dl'); 23989 return parentList !== null && editor.dom.getContentEditableParent(parentList) === 'false'; 23990 }; 23991 const isEmptyAnchor = (dom, elm) => { 23992 return elm && elm.nodeName === 'A' && dom.isEmpty(elm); 23993 }; 23994 const emptyBlock = elm => { 23995 elm.innerHTML = '<br data-mce-bogus="1">'; 23996 }; 23997 const containerAndSiblingName = (container, nodeName) => { 23998 return container.nodeName === nodeName || container.previousSibling && container.previousSibling.nodeName === nodeName; 23999 }; 24000 const canSplitBlock = (dom, node) => { 24001 return isNonNullable(node) && dom.isBlock(node) && !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && !/^(fixed|absolute)/i.test(node.style.position) && dom.isEditable(node.parentNode) && dom.getContentEditable(node) !== 'false'; 24002 }; 24003 const trimInlineElementsOnLeftSideOfBlock = (dom, nonEmptyElementsMap, block) => { 24004 var _a; 24005 const firstChilds = []; 24006 if (!block) { 24007 return; 24008 } 24009 let currentNode = block; 24010 while (currentNode = currentNode.firstChild) { 24011 if (dom.isBlock(currentNode)) { 24012 return; 24013 } 24014 if (isElement$6(currentNode) && !nonEmptyElementsMap[currentNode.nodeName.toLowerCase()]) { 24015 firstChilds.push(currentNode); 24016 } 24017 } 24018 let i = firstChilds.length; 24019 while (i--) { 24020 currentNode = firstChilds[i]; 24021 if (!currentNode.hasChildNodes() || currentNode.firstChild === currentNode.lastChild && ((_a = currentNode.firstChild) === null || _a === void 0 ? void 0 : _a.nodeValue) === '') { 24022 dom.remove(currentNode); 24023 } else { 24024 if (isEmptyAnchor(dom, currentNode)) { 24025 dom.remove(currentNode); 24026 } 24027 } 24028 } 24029 }; 24030 const normalizeZwspOffset = (start, container, offset) => { 24031 if (!isText$a(container)) { 24032 return offset; 24033 } else if (start) { 24034 return offset === 1 && container.data.charAt(offset - 1) === ZWSP$1 ? 0 : offset; 24035 } else { 24036 return offset === container.data.length - 1 && container.data.charAt(offset) === ZWSP$1 ? container.data.length : offset; 24037 } 24038 }; 24039 const includeZwspInRange = rng => { 24040 const newRng = rng.cloneRange(); 24041 newRng.setStart(rng.startContainer, normalizeZwspOffset(true, rng.startContainer, rng.startOffset)); 24042 newRng.setEnd(rng.endContainer, normalizeZwspOffset(false, rng.endContainer, rng.endOffset)); 24043 return newRng; 24044 }; 24045 const trimLeadingLineBreaks = node => { 24046 let currentNode = node; 24047 do { 24048 if (isText$a(currentNode)) { 24049 currentNode.data = currentNode.data.replace(/^[\r\n]+/, ''); 24050 } 24051 currentNode = currentNode.firstChild; 24052 } while (currentNode); 24053 }; 24054 const applyAttributes = (editor, node, forcedRootBlockAttrs) => { 24055 const dom = editor.dom; 24056 Optional.from(forcedRootBlockAttrs.style).map(dom.parseStyle).each(attrStyles => { 24057 const currentStyles = getAllRaw(SugarElement.fromDom(node)); 24058 const newStyles = { 24059 ...currentStyles, 24060 ...attrStyles 24061 }; 24062 dom.setStyles(node, newStyles); 24063 }); 24064 const attrClassesOpt = Optional.from(forcedRootBlockAttrs.class).map(attrClasses => attrClasses.split(/\s+/)); 24065 const currentClassesOpt = Optional.from(node.className).map(currentClasses => filter$5(currentClasses.split(/\s+/), clazz => clazz !== '')); 24066 lift2(attrClassesOpt, currentClassesOpt, (attrClasses, currentClasses) => { 24067 const filteredClasses = filter$5(currentClasses, clazz => !contains$2(attrClasses, clazz)); 24068 const newClasses = [ 24069 ...attrClasses, 24070 ...filteredClasses 24071 ]; 24072 dom.setAttrib(node, 'class', newClasses.join(' ')); 24073 }); 24074 const appliedAttrs = [ 24075 'style', 24076 'class' 24077 ]; 24078 const remainingAttrs = filter$4(forcedRootBlockAttrs, (_, attrs) => !contains$2(appliedAttrs, attrs)); 24079 dom.setAttribs(node, remainingAttrs); 24080 }; 24081 const setForcedBlockAttrs = (editor, node) => { 24082 const forcedRootBlockName = getForcedRootBlock(editor); 24083 if (forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) { 24084 const forcedRootBlockAttrs = getForcedRootBlockAttrs(editor); 24085 applyAttributes(editor, node, forcedRootBlockAttrs); 24086 } 24087 }; 24088 const wrapSelfAndSiblingsInDefaultBlock = (editor, newBlockName, rng, container, offset) => { 24089 var _a, _b; 24090 const dom = editor.dom; 24091 const editableRoot = (_a = getEditableRoot(dom, container)) !== null && _a !== void 0 ? _a : dom.getRoot(); 24092 let parentBlock = dom.getParent(container, dom.isBlock); 24093 if (!parentBlock || !canSplitBlock(dom, parentBlock)) { 24094 parentBlock = parentBlock || editableRoot; 24095 if (!parentBlock.hasChildNodes()) { 24096 const newBlock = dom.create(newBlockName); 24097 setForcedBlockAttrs(editor, newBlock); 24098 parentBlock.appendChild(newBlock); 24099 rng.setStart(newBlock, 0); 24100 rng.setEnd(newBlock, 0); 24101 return newBlock; 24102 } 24103 let node = container; 24104 while (node && node.parentNode !== parentBlock) { 24105 node = node.parentNode; 24106 } 24107 let startNode; 24108 while (node && !dom.isBlock(node)) { 24109 startNode = node; 24110 node = node.previousSibling; 24111 } 24112 const startNodeName = (_b = startNode === null || startNode === void 0 ? void 0 : startNode.parentElement) === null || _b === void 0 ? void 0 : _b.nodeName; 24113 if (startNode && startNodeName && editor.schema.isValidChild(startNodeName, newBlockName.toLowerCase())) { 24114 const startNodeParent = startNode.parentNode; 24115 const newBlock = dom.create(newBlockName); 24116 setForcedBlockAttrs(editor, newBlock); 24117 startNodeParent.insertBefore(newBlock, startNode); 24118 node = startNode; 24119 while (node && !dom.isBlock(node)) { 24120 const next = node.nextSibling; 24121 newBlock.appendChild(node); 24122 node = next; 24123 } 24124 rng.setStart(container, offset); 24125 rng.setEnd(container, offset); 24126 } 24127 } 24128 return container; 24129 }; 24130 const addBrToBlockIfNeeded = (dom, block) => { 24131 block.normalize(); 24132 const lastChild = block.lastChild; 24133 if (!lastChild || isElement$6(lastChild) && /^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true))) { 24134 dom.add(block, 'br'); 24135 } 24136 }; 24137 const shouldEndContainer = (editor, container) => { 24138 const optionValue = shouldEndContainerOnEmptyBlock(editor); 24139 if (isNullable(container)) { 24140 return false; 24141 } else if (isString(optionValue)) { 24142 return contains$2(Tools.explode(optionValue), container.nodeName.toLowerCase()); 24143 } else { 24144 return optionValue; 24145 } 24146 }; 24147 const insert$2 = (editor, evt) => { 24148 let container; 24149 let offset; 24150 let parentBlockName; 24151 let containerBlock; 24152 let isAfterLastNodeInContainer = false; 24153 const dom = editor.dom; 24154 const schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements(); 24155 const rng = editor.selection.getRng(); 24156 const newBlockName = getForcedRootBlock(editor); 24157 const isInRoot = rng.collapsed && rng.startContainer === editor.dom.getRoot(); 24158 const start = SugarElement.fromDom(rng.startContainer); 24159 const child = child$1(start, rng.startOffset); 24160 const isCef = child.exists(element => isHTMLElement(element) && !isEditable$3(element)); 24161 const inRootAndLastOrCef = isInRoot && isCef; 24162 const createNewBlock = name => { 24163 let node = container; 24164 const textInlineElements = schema.getTextInlineElements(); 24165 let block; 24166 if (name || parentBlockName === 'TABLE' || parentBlockName === 'HR') { 24167 block = dom.create(name || newBlockName); 24168 } else { 24169 block = parentBlock.cloneNode(false); 24170 } 24171 let caretNode = block; 24172 if (shouldKeepStyles(editor) === false) { 24173 dom.setAttrib(block, 'style', null); 24174 dom.setAttrib(block, 'class', null); 24175 } else { 24176 do { 24177 if (textInlineElements[node.nodeName]) { 24178 if (isCaretNode(node) || isBookmarkNode$1(node)) { 24179 continue; 24180 } 24181 const clonedNode = node.cloneNode(false); 24182 dom.setAttrib(clonedNode, 'id', ''); 24183 if (block.hasChildNodes()) { 24184 clonedNode.appendChild(block.firstChild); 24185 block.appendChild(clonedNode); 24186 } else { 24187 caretNode = clonedNode; 24188 block.appendChild(clonedNode); 24189 } 24190 } 24191 } while ((node = node.parentNode) && node !== editableRoot); 24192 } 24193 setForcedBlockAttrs(editor, block); 24194 emptyBlock(caretNode); 24195 return block; 24196 }; 24197 const isCaretAtStartOrEndOfBlock = start => { 24198 const normalizedOffset = normalizeZwspOffset(start, container, offset); 24199 if (isText$a(container) && (start ? normalizedOffset > 0 : normalizedOffset < container.data.length)) { 24200 return false; 24201 } 24202 if (container.parentNode === parentBlock && isAfterLastNodeInContainer && !start) { 24203 return true; 24204 } 24205 if (start && isElement$6(container) && container === parentBlock.firstChild) { 24206 return true; 24207 } 24208 if (containerAndSiblingName(container, 'TABLE') || containerAndSiblingName(container, 'HR')) { 24209 return isAfterLastNodeInContainer && !start || !isAfterLastNodeInContainer && start; 24210 } 24211 const walker = new DomTreeWalker(container, parentBlock); 24212 if (isText$a(container)) { 24213 if (start && normalizedOffset === 0) { 24214 walker.prev(); 24215 } else if (!start && normalizedOffset === container.data.length) { 24216 walker.next(); 24217 } 24218 } 24219 let node; 24220 while (node = walker.current()) { 24221 if (isElement$6(node)) { 24222 if (!node.getAttribute('data-mce-bogus')) { 24223 const name = node.nodeName.toLowerCase(); 24224 if (nonEmptyElementsMap[name] && name !== 'br') { 24225 return false; 24226 } 24227 } 24228 } else if (isText$a(node) && !isWhitespaceText(node.data)) { 24229 return false; 24230 } 24231 if (start) { 24232 walker.prev(); 24233 } else { 24234 walker.next(); 24235 } 24236 } 24237 return true; 24238 }; 24239 const insertNewBlockAfter = () => { 24240 let block; 24241 if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName !== 'HGROUP') { 24242 block = createNewBlock(newBlockName); 24243 } else { 24244 block = createNewBlock(); 24245 } 24246 if (shouldEndContainer(editor, containerBlock) && canSplitBlock(dom, containerBlock) && dom.isEmpty(parentBlock, undefined, { includeZwsp: true })) { 24247 block = dom.split(containerBlock, parentBlock); 24248 } else { 24249 dom.insertAfter(block, parentBlock); 24250 } 24251 moveToCaretPosition(editor, block); 24252 return block; 24253 }; 24254 normalize$2(dom, rng).each(normRng => { 24255 rng.setStart(normRng.startContainer, normRng.startOffset); 24256 rng.setEnd(normRng.endContainer, normRng.endOffset); 24257 }); 24258 container = rng.startContainer; 24259 offset = rng.startOffset; 24260 const shiftKey = !!(evt && evt.shiftKey); 24261 const ctrlKey = !!(evt && evt.ctrlKey); 24262 if (isElement$6(container) && container.hasChildNodes() && !inRootAndLastOrCef) { 24263 isAfterLastNodeInContainer = offset > container.childNodes.length - 1; 24264 container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; 24265 if (isAfterLastNodeInContainer && isText$a(container)) { 24266 offset = container.data.length; 24267 } else { 24268 offset = 0; 24269 } 24270 } 24271 const editableRoot = getEditableRoot(dom, container); 24272 if (!editableRoot || isWithinNonEditableList(editor, container)) { 24273 return; 24274 } 24275 if (!shiftKey) { 24276 container = wrapSelfAndSiblingsInDefaultBlock(editor, newBlockName, rng, container, offset); 24277 } 24278 let parentBlock = dom.getParent(container, dom.isBlock) || dom.getRoot(); 24279 containerBlock = isNonNullable(parentBlock === null || parentBlock === void 0 ? void 0 : parentBlock.parentNode) ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; 24280 parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; 24281 const containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; 24282 if (containerBlockName === 'LI' && !ctrlKey) { 24283 const liBlock = containerBlock; 24284 parentBlock = liBlock; 24285 containerBlock = liBlock.parentNode; 24286 parentBlockName = containerBlockName; 24287 } 24288 if (isElement$6(containerBlock) && isLastEmptyBlockInDetails(editor, shiftKey, parentBlock)) { 24289 return insertNewLine(editor, createNewBlock, parentBlock); 24290 } 24291 if (/^(LI|DT|DD)$/.test(parentBlockName) && isElement$6(containerBlock)) { 24292 if (dom.isEmpty(parentBlock)) { 24293 insert$3(editor, createNewBlock, containerBlock, parentBlock, newBlockName); 24294 return; 24295 } 24296 } 24297 if (!inRootAndLastOrCef && (parentBlock === editor.getBody() || !canSplitBlock(dom, parentBlock))) { 24298 return; 24299 } 24300 const parentBlockParent = parentBlock.parentNode; 24301 let newBlock; 24302 if (inRootAndLastOrCef) { 24303 newBlock = createNewBlock(newBlockName); 24304 child.fold(() => { 24305 append$1(start, SugarElement.fromDom(newBlock)); 24306 }, child => { 24307 before$3(child, SugarElement.fromDom(newBlock)); 24308 }); 24309 editor.selection.setCursorLocation(newBlock, 0); 24310 } else if (isCaretContainerBlock$1(parentBlock)) { 24311 newBlock = showCaretContainerBlock(parentBlock); 24312 if (dom.isEmpty(parentBlock)) { 24313 emptyBlock(parentBlock); 24314 } 24315 setForcedBlockAttrs(editor, newBlock); 24316 moveToCaretPosition(editor, newBlock); 24317 } else if (isCaretAtStartOrEndOfBlock(false)) { 24318 newBlock = insertNewBlockAfter(); 24319 } else if (isCaretAtStartOrEndOfBlock(true) && parentBlockParent) { 24320 newBlock = parentBlockParent.insertBefore(createNewBlock(), parentBlock); 24321 const isNearChildren = hasChildNodes(SugarElement.fromDom(rng.startContainer)) && rng.collapsed; 24322 moveToCaretPosition(editor, containerAndSiblingName(parentBlock, 'HR') || isNearChildren ? newBlock : parentBlock); 24323 } else { 24324 const tmpRng = includeZwspInRange(rng).cloneRange(); 24325 tmpRng.setEndAfter(parentBlock); 24326 const fragment = tmpRng.extractContents(); 24327 trimZwsp(fragment); 24328 trimLeadingLineBreaks(fragment); 24329 newBlock = fragment.firstChild; 24330 dom.insertAfter(fragment, parentBlock); 24331 trimInlineElementsOnLeftSideOfBlock(dom, nonEmptyElementsMap, newBlock); 24332 addBrToBlockIfNeeded(dom, parentBlock); 24333 if (dom.isEmpty(parentBlock)) { 24334 emptyBlock(parentBlock); 24335 } 24336 newBlock.normalize(); 24337 if (dom.isEmpty(newBlock)) { 24338 dom.remove(newBlock); 24339 insertNewBlockAfter(); 24340 } else { 24341 setForcedBlockAttrs(editor, newBlock); 24342 moveToCaretPosition(editor, newBlock); 24343 } 24344 } 24345 dom.setAttrib(newBlock, 'id', ''); 24346 editor.dispatch('NewBlock', { newBlock }); 24347 }; 24348 const fakeEventName$1 = 'insertParagraph'; 24349 const blockbreak = { 24350 insert: insert$2, 24351 fakeEventName: fakeEventName$1 24352 }; 24353 24354 const hasRightSideContent = (schema, container, parentBlock) => { 24355 const walker = new DomTreeWalker(container, parentBlock); 24356 let node; 24357 const nonEmptyElementsMap = schema.getNonEmptyElements(); 24358 while (node = walker.next()) { 24359 if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || isText$a(node) && node.length > 0) { 24360 return true; 24361 } 24362 } 24363 return false; 24364 }; 24365 const moveSelectionToBr = (editor, brElm, extraBr) => { 24366 const rng = editor.dom.createRng(); 24367 if (!extraBr) { 24368 rng.setStartAfter(brElm); 24369 rng.setEndAfter(brElm); 24370 } else { 24371 rng.setStartBefore(brElm); 24372 rng.setEndBefore(brElm); 24373 } 24374 editor.selection.setRng(rng); 24375 scrollRangeIntoView(editor, rng); 24376 }; 24377 const insertBrAtCaret = (editor, evt) => { 24378 const selection = editor.selection; 24379 const dom = editor.dom; 24380 const rng = selection.getRng(); 24381 let brElm; 24382 let extraBr = false; 24383 normalize$2(dom, rng).each(normRng => { 24384 rng.setStart(normRng.startContainer, normRng.startOffset); 24385 rng.setEnd(normRng.endContainer, normRng.endOffset); 24386 }); 24387 let offset = rng.startOffset; 24388 let container = rng.startContainer; 24389 if (isElement$6(container) && container.hasChildNodes()) { 24390 const isAfterLastNodeInContainer = offset > container.childNodes.length - 1; 24391 container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; 24392 if (isAfterLastNodeInContainer && isText$a(container)) { 24393 offset = container.data.length; 24394 } else { 24395 offset = 0; 24396 } 24397 } 24398 let parentBlock = dom.getParent(container, dom.isBlock); 24399 const containerBlock = parentBlock && parentBlock.parentNode ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; 24400 const containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; 24401 const isControlKey = !!(evt && evt.ctrlKey); 24402 if (containerBlockName === 'LI' && !isControlKey) { 24403 parentBlock = containerBlock; 24404 } 24405 if (isText$a(container) && offset >= container.data.length) { 24406 if (!hasRightSideContent(editor.schema, container, parentBlock || dom.getRoot())) { 24407 brElm = dom.create('br'); 24408 rng.insertNode(brElm); 24409 rng.setStartAfter(brElm); 24410 rng.setEndAfter(brElm); 24411 extraBr = true; 24412 } 24413 } 24414 brElm = dom.create('br'); 24415 rangeInsertNode(dom, rng, brElm); 24416 moveSelectionToBr(editor, brElm, extraBr); 24417 editor.undoManager.add(); 24418 }; 24419 const insertBrBefore = (editor, inline) => { 24420 const br = SugarElement.fromTag('br'); 24421 before$3(SugarElement.fromDom(inline), br); 24422 editor.undoManager.add(); 24423 }; 24424 const insertBrAfter = (editor, inline) => { 24425 if (!hasBrAfter(editor.getBody(), inline)) { 24426 after$4(SugarElement.fromDom(inline), SugarElement.fromTag('br')); 24427 } 24428 const br = SugarElement.fromTag('br'); 24429 after$4(SugarElement.fromDom(inline), br); 24430 moveSelectionToBr(editor, br.dom, false); 24431 editor.undoManager.add(); 24432 }; 24433 const isBeforeBr = pos => { 24434 return isBr$6(pos.getNode()); 24435 }; 24436 const hasBrAfter = (rootNode, startNode) => { 24437 if (isBeforeBr(CaretPosition.after(startNode))) { 24438 return true; 24439 } else { 24440 return nextPosition(rootNode, CaretPosition.after(startNode)).map(pos => { 24441 return isBr$6(pos.getNode()); 24442 }).getOr(false); 24443 } 24444 }; 24445 const isAnchorLink = elm => { 24446 return elm && elm.nodeName === 'A' && 'href' in elm; 24447 }; 24448 const isInsideAnchor = location => { 24449 return location.fold(never, isAnchorLink, isAnchorLink, never); 24450 }; 24451 const readInlineAnchorLocation = editor => { 24452 const isInlineTarget$1 = curry(isInlineTarget, editor); 24453 const position = CaretPosition.fromRangeStart(editor.selection.getRng()); 24454 return readLocation(isInlineTarget$1, editor.getBody(), position).filter(isInsideAnchor); 24455 }; 24456 const insertBrOutsideAnchor = (editor, location) => { 24457 location.fold(noop, curry(insertBrBefore, editor), curry(insertBrAfter, editor), noop); 24458 }; 24459 const insert$1 = (editor, evt) => { 24460 const anchorLocation = readInlineAnchorLocation(editor); 24461 if (anchorLocation.isSome()) { 24462 anchorLocation.each(curry(insertBrOutsideAnchor, editor)); 24463 } else { 24464 insertBrAtCaret(editor, evt); 24465 } 24466 }; 24467 const fakeEventName = 'insertLineBreak'; 24468 const linebreak = { 24469 insert: insert$1, 24470 fakeEventName 24471 }; 24472 24473 const matchesSelector = (editor, selector) => { 24474 return getParentBlock$1(editor).filter(parentBlock => { 24475 return selector.length > 0 && is$1(SugarElement.fromDom(parentBlock), selector); 24476 }).isSome(); 24477 }; 24478 const shouldInsertBr = editor => { 24479 return matchesSelector(editor, getBrNewLineSelector(editor)); 24480 }; 24481 const shouldBlockNewLine$1 = editor => { 24482 return matchesSelector(editor, getNoNewLineSelector(editor)); 24483 }; 24484 24485 const newLineAction = Adt.generate([ 24486 { br: [] }, 24487 { block: [] }, 24488 { none: [] } 24489 ]); 24490 const shouldBlockNewLine = (editor, _shiftKey) => { 24491 return shouldBlockNewLine$1(editor); 24492 }; 24493 const inListBlock = requiredState => { 24494 return (editor, _shiftKey) => { 24495 return isListItemParentBlock(editor) === requiredState; 24496 }; 24497 }; 24498 const inBlock = (blockName, requiredState) => (editor, _shiftKey) => { 24499 const state = getParentBlockName(editor) === blockName.toUpperCase(); 24500 return state === requiredState; 24501 }; 24502 const inCefBlock = editor => { 24503 const editableRoot = getEditableRoot(editor.dom, editor.selection.getStart()); 24504 return isNullable(editableRoot); 24505 }; 24506 const inPreBlock = requiredState => inBlock('pre', requiredState); 24507 const inSummaryBlock = () => inBlock('summary', true); 24508 const shouldPutBrInPre = requiredState => { 24509 return (editor, _shiftKey) => { 24510 return shouldPutBrInPre$1(editor) === requiredState; 24511 }; 24512 }; 24513 const inBrContext = (editor, _shiftKey) => { 24514 return shouldInsertBr(editor); 24515 }; 24516 const hasShiftKey = (_editor, shiftKey) => { 24517 return shiftKey; 24518 }; 24519 const canInsertIntoEditableRoot = editor => { 24520 const forcedRootBlock = getForcedRootBlock(editor); 24521 const rootEditable = getEditableRoot(editor.dom, editor.selection.getStart()); 24522 return isNonNullable(rootEditable) && editor.schema.isValidChild(rootEditable.nodeName, forcedRootBlock); 24523 }; 24524 const isInRootWithEmptyOrCEF = editor => { 24525 const rng = editor.selection.getRng(); 24526 const isInRoot = rng.collapsed && rng.startContainer === editor.dom.getRoot(); 24527 const start = SugarElement.fromDom(rng.startContainer); 24528 const child = child$1(start, rng.startOffset); 24529 const isCefOpt = child.map(element => isHTMLElement(element) && !isEditable$3(element)); 24530 return isInRoot && isCefOpt.getOr(true); 24531 }; 24532 const match = (predicates, action) => { 24533 return (editor, shiftKey) => { 24534 const isMatch = foldl(predicates, (res, p) => { 24535 return res && p(editor, shiftKey); 24536 }, true); 24537 return isMatch ? Optional.some(action) : Optional.none(); 24538 }; 24539 }; 24540 const getAction = (editor, evt) => { 24541 return evaluateUntil([ 24542 match([shouldBlockNewLine], newLineAction.none()), 24543 match([ 24544 inPreBlock(true), 24545 inCefBlock 24546 ], newLineAction.none()), 24547 match([inSummaryBlock()], newLineAction.br()), 24548 match([ 24549 inPreBlock(true), 24550 shouldPutBrInPre(false), 24551 hasShiftKey 24552 ], newLineAction.br()), 24553 match([ 24554 inPreBlock(true), 24555 shouldPutBrInPre(false) 24556 ], newLineAction.block()), 24557 match([ 24558 inPreBlock(true), 24559 shouldPutBrInPre(true), 24560 hasShiftKey 24561 ], newLineAction.block()), 24562 match([ 24563 inPreBlock(true), 24564 shouldPutBrInPre(true) 24565 ], newLineAction.br()), 24566 match([ 24567 inListBlock(true), 24568 hasShiftKey 24569 ], newLineAction.br()), 24570 match([inListBlock(true)], newLineAction.block()), 24571 match([inBrContext], newLineAction.br()), 24572 match([hasShiftKey], newLineAction.br()), 24573 match([canInsertIntoEditableRoot], newLineAction.block()), 24574 match([isInRootWithEmptyOrCEF], newLineAction.block()) 24575 ], [ 24576 editor, 24577 !!(evt && evt.shiftKey) 24578 ]).getOr(newLineAction.none()); 24579 }; 24580 24581 const insertBreak = (breakType, editor, evt) => { 24582 if (!editor.selection.isCollapsed()) { 24583 execEditorDeleteCommand(editor); 24584 } 24585 if (isNonNullable(evt)) { 24586 const event = fireBeforeInputEvent(editor, breakType.fakeEventName); 24587 if (event.isDefaultPrevented()) { 24588 return; 24589 } 24590 } 24591 breakType.insert(editor, evt); 24592 if (isNonNullable(evt)) { 24593 fireInputEvent(editor, breakType.fakeEventName); 24594 } 24595 }; 24596 const insert = (editor, evt) => { 24597 const br = () => insertBreak(linebreak, editor, evt); 24598 const block = () => insertBreak(blockbreak, editor, evt); 24599 const logicalAction = getAction(editor, evt); 24600 switch (getNewlineBehavior(editor)) { 24601 case 'linebreak': 24602 logicalAction.fold(br, br, noop); 24603 break; 24604 case 'block': 24605 logicalAction.fold(block, block, noop); 24606 break; 24607 case 'invert': 24608 logicalAction.fold(block, br, noop); 24609 break; 24610 default: 24611 logicalAction.fold(br, block, noop); 24612 break; 24613 } 24614 }; 24615 24616 const platform$2 = detect$2(); 24617 const isIOSSafari = platform$2.os.isiOS() && platform$2.browser.isSafari(); 24618 const handleEnterKeyEvent = (editor, event) => { 24619 if (event.isDefaultPrevented()) { 24620 return; 24621 } 24622 event.preventDefault(); 24623 endTypingLevelIgnoreLocks(editor.undoManager); 24624 editor.undoManager.transact(() => { 24625 insert(editor, event); 24626 }); 24627 }; 24628 const isCaretAfterKoreanCharacter = rng => { 24629 if (!rng.collapsed) { 24630 return false; 24631 } 24632 const startContainer = rng.startContainer; 24633 if (isText$a(startContainer)) { 24634 const koreanCharRegex = /^[\uAC00-\uD7AF\u1100-\u11FF\u3130-\u318F\uA960-\uA97F\uD7B0-\uD7FF]$/; 24635 const char = startContainer.data.charAt(rng.startOffset - 1); 24636 return koreanCharRegex.test(char); 24637 } else { 24638 return false; 24639 } 24640 }; 24641 const setup$h = editor => { 24642 let iOSSafariKeydownBookmark = Optional.none(); 24643 const iOSSafariKeydownOverride = editor => { 24644 iOSSafariKeydownBookmark = Optional.some(editor.selection.getBookmark()); 24645 editor.undoManager.add(); 24646 }; 24647 const iOSSafariKeyupOverride = (editor, event) => { 24648 editor.undoManager.undo(); 24649 iOSSafariKeydownBookmark.fold(noop, b => editor.selection.moveToBookmark(b)); 24650 handleEnterKeyEvent(editor, event); 24651 iOSSafariKeydownBookmark = Optional.none(); 24652 }; 24653 editor.on('keydown', event => { 24654 if (event.keyCode === VK.ENTER) { 24655 if (isIOSSafari && isCaretAfterKoreanCharacter(editor.selection.getRng())) { 24656 iOSSafariKeydownOverride(editor); 24657 } else { 24658 handleEnterKeyEvent(editor, event); 24659 } 24660 } 24661 }); 24662 editor.on('keyup', event => { 24663 if (event.keyCode === VK.ENTER) { 24664 iOSSafariKeydownBookmark.each(() => iOSSafariKeyupOverride(editor, event)); 24665 } 24666 }); 24667 }; 24668 24669 const executeKeydownOverride$2 = (editor, caret, evt) => { 24670 const isMac = Env.os.isMacOS() || Env.os.isiOS(); 24671 execute([ 24672 { 24673 keyCode: VK.END, 24674 action: action(moveToLineEndPoint$1, editor, true) 24675 }, 24676 { 24677 keyCode: VK.HOME, 24678 action: action(moveToLineEndPoint$1, editor, false) 24679 }, 24680 ...!isMac ? [ 24681 { 24682 keyCode: VK.HOME, 24683 action: action(selectToEndPoint, editor, false), 24684 ctrlKey: true, 24685 shiftKey: true 24686 }, 24687 { 24688 keyCode: VK.END, 24689 action: action(selectToEndPoint, editor, true), 24690 ctrlKey: true, 24691 shiftKey: true 24692 } 24693 ] : [], 24694 { 24695 keyCode: VK.END, 24696 action: action(moveToLineEndPoint, editor, true) 24697 }, 24698 { 24699 keyCode: VK.HOME, 24700 action: action(moveToLineEndPoint, editor, false) 24701 }, 24702 { 24703 keyCode: VK.END, 24704 action: action(moveToLineEndPoint$2, editor, true, caret) 24705 }, 24706 { 24707 keyCode: VK.HOME, 24708 action: action(moveToLineEndPoint$2, editor, false, caret) 24709 } 24710 ], evt).each(_ => { 24711 evt.preventDefault(); 24712 }); 24713 }; 24714 const setup$g = (editor, caret) => { 24715 editor.on('keydown', evt => { 24716 if (!evt.isDefaultPrevented()) { 24717 executeKeydownOverride$2(editor, caret, evt); 24718 } 24719 }); 24720 }; 24721 24722 const setup$f = editor => { 24723 editor.on('input', e => { 24724 if (!e.isComposing) { 24725 normalizeNbspsInEditor(editor); 24726 } 24727 }); 24728 }; 24729 24730 const platform$1 = detect$2(); 24731 const executeKeyupAction = (editor, caret, evt) => { 24732 execute([ 24733 { 24734 keyCode: VK.PAGE_UP, 24735 action: action(moveToLineEndPoint$2, editor, false, caret) 24736 }, 24737 { 24738 keyCode: VK.PAGE_DOWN, 24739 action: action(moveToLineEndPoint$2, editor, true, caret) 24740 } 24741 ], evt); 24742 }; 24743 const stopImmediatePropagation = e => e.stopImmediatePropagation(); 24744 const isPageUpDown = evt => evt.keyCode === VK.PAGE_UP || evt.keyCode === VK.PAGE_DOWN; 24745 const setNodeChangeBlocker = (blocked, editor, block) => { 24746 if (block && !blocked.get()) { 24747 editor.on('NodeChange', stopImmediatePropagation, true); 24748 } else if (!block && blocked.get()) { 24749 editor.off('NodeChange', stopImmediatePropagation); 24750 } 24751 blocked.set(block); 24752 }; 24753 const setup$e = (editor, caret) => { 24754 if (platform$1.os.isMacOS()) { 24755 return; 24756 } 24757 const blocked = Cell(false); 24758 editor.on('keydown', evt => { 24759 if (isPageUpDown(evt)) { 24760 setNodeChangeBlocker(blocked, editor, true); 24761 } 24762 }); 24763 editor.on('keyup', evt => { 24764 if (!evt.isDefaultPrevented()) { 24765 executeKeyupAction(editor, caret, evt); 24766 } 24767 if (isPageUpDown(evt) && blocked.get()) { 24768 setNodeChangeBlocker(blocked, editor, false); 24769 editor.nodeChanged(); 24770 } 24771 }); 24772 }; 24773 24774 const insertTextAtPosition = (text, pos) => { 24775 const container = pos.container(); 24776 const offset = pos.offset(); 24777 if (isText$a(container)) { 24778 container.insertData(offset, text); 24779 return Optional.some(CaretPosition(container, offset + text.length)); 24780 } else { 24781 return getElementFromPosition(pos).map(elm => { 24782 const textNode = SugarElement.fromText(text); 24783 if (pos.isAtEnd()) { 24784 after$4(elm, textNode); 24785 } else { 24786 before$3(elm, textNode); 24787 } 24788 return CaretPosition(textNode.dom, text.length); 24789 }); 24790 } 24791 }; 24792 const insertNbspAtPosition = curry(insertTextAtPosition, nbsp); 24793 const insertSpaceAtPosition = curry(insertTextAtPosition, ' '); 24794 24795 const insertSpaceOrNbspAtPosition = (root, pos) => needsToHaveNbsp(root, pos) ? insertNbspAtPosition(pos) : insertSpaceAtPosition(pos); 24796 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))); 24797 const insertInlineBoundarySpaceOrNbsp = (root, pos) => checkPos => needsToHaveNbsp(root, checkPos) ? insertNbspAtPosition(pos) : insertSpaceAtPosition(pos); 24798 const setSelection = editor => pos => { 24799 editor.selection.setRng(pos.toRange()); 24800 editor.nodeChanged(); 24801 }; 24802 const isInsideSummary = (domUtils, node) => domUtils.isEditable(domUtils.getParent(node, 'summary')); 24803 const insertSpaceOrNbspAtSelection = editor => { 24804 const pos = CaretPosition.fromRangeStart(editor.selection.getRng()); 24805 const root = SugarElement.fromDom(editor.getBody()); 24806 if (editor.selection.isCollapsed()) { 24807 const isInlineTarget$1 = curry(isInlineTarget, editor); 24808 const caretPosition = CaretPosition.fromRangeStart(editor.selection.getRng()); 24809 return readLocation(isInlineTarget$1, editor.getBody(), caretPosition).bind(locationToCaretPosition(root)).map(checkPos => () => insertInlineBoundarySpaceOrNbsp(root, pos)(checkPos).each(setSelection(editor))); 24810 } else { 24811 return Optional.none(); 24812 } 24813 }; 24814 const insertSpaceInSummaryAtSelectionOnFirefox = editor => { 24815 const insertSpaceThunk = () => { 24816 const root = SugarElement.fromDom(editor.getBody()); 24817 if (!editor.selection.isCollapsed()) { 24818 editor.getDoc().execCommand('Delete'); 24819 } 24820 const pos = CaretPosition.fromRangeStart(editor.selection.getRng()); 24821 insertSpaceOrNbspAtPosition(root, pos).each(setSelection(editor)); 24822 }; 24823 return someIf(Env.browser.isFirefox() && editor.selection.isEditable() && isInsideSummary(editor.dom, editor.selection.getRng().startContainer), insertSpaceThunk); 24824 }; 24825 24826 const executeKeydownOverride$1 = (editor, evt) => { 24827 executeWithDelayedAction([ 24828 { 24829 keyCode: VK.SPACEBAR, 24830 action: action(insertSpaceOrNbspAtSelection, editor) 24831 }, 24832 { 24833 keyCode: VK.SPACEBAR, 24834 action: action(insertSpaceInSummaryAtSelectionOnFirefox, editor) 24835 } 24836 ], evt).each(applyAction => { 24837 evt.preventDefault(); 24838 const event = fireBeforeInputEvent(editor, 'insertText', { data: ' ' }); 24839 if (!event.isDefaultPrevented()) { 24840 applyAction(); 24841 fireInputEvent(editor, 'insertText', { data: ' ' }); 24842 } 24843 }); 24844 }; 24845 const setup$d = editor => { 24846 editor.on('keydown', evt => { 24847 if (!evt.isDefaultPrevented()) { 24848 executeKeydownOverride$1(editor, evt); 24849 } 24850 }); 24851 }; 24852 24853 const tableTabNavigation = editor => { 24854 if (hasTableTabNavigation(editor)) { 24855 return [ 24856 { 24857 keyCode: VK.TAB, 24858 action: action(handleTab, editor, true) 24859 }, 24860 { 24861 keyCode: VK.TAB, 24862 shiftKey: true, 24863 action: action(handleTab, editor, false) 24864 } 24865 ]; 24866 } else { 24867 return []; 24868 } 24869 }; 24870 const executeKeydownOverride = (editor, evt) => { 24871 execute([...tableTabNavigation(editor)], evt).each(_ => { 24872 evt.preventDefault(); 24873 }); 24874 }; 24875 const setup$c = editor => { 24876 editor.on('keydown', evt => { 24877 if (!evt.isDefaultPrevented()) { 24878 executeKeydownOverride(editor, evt); 24879 } 24880 }); 24881 }; 24882 24883 const setup$b = editor => { 24884 editor.addShortcut('Meta+P', '', 'mcePrint'); 24885 setup$j(editor); 24886 if (isRtc(editor)) { 24887 return Cell(null); 24888 } else { 24889 const caret = setupSelectedState(editor); 24890 setup$l(editor); 24891 setup$k(editor, caret); 24892 setup$i(editor, caret); 24893 setup$h(editor); 24894 setup$d(editor); 24895 setup$f(editor); 24896 setup$c(editor); 24897 setup$g(editor, caret); 24898 setup$e(editor, caret); 24899 return caret; 24900 } 24901 }; 24902 24903 class NodeChange { 24904 constructor(editor) { 24905 this.lastPath = []; 24906 this.editor = editor; 24907 let lastRng; 24908 const self = this; 24909 if (!('onselectionchange' in editor.getDoc())) { 24910 editor.on('NodeChange click mouseup keyup focus', e => { 24911 const nativeRng = editor.selection.getRng(); 24912 const fakeRng = { 24913 startContainer: nativeRng.startContainer, 24914 startOffset: nativeRng.startOffset, 24915 endContainer: nativeRng.endContainer, 24916 endOffset: nativeRng.endOffset 24917 }; 24918 if (e.type === 'nodechange' || !isEq$4(fakeRng, lastRng)) { 24919 editor.dispatch('SelectionChange'); 24920 } 24921 lastRng = fakeRng; 24922 }); 24923 } 24924 editor.on('contextmenu', () => { 24925 editor.dispatch('SelectionChange'); 24926 }); 24927 editor.on('SelectionChange', () => { 24928 const startElm = editor.selection.getStart(true); 24929 if (!startElm) { 24930 return; 24931 } 24932 if (hasAnyRanges(editor) && !self.isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) { 24933 editor.nodeChanged({ selectionChange: true }); 24934 } 24935 }); 24936 editor.on('mouseup', e => { 24937 if (!e.isDefaultPrevented() && hasAnyRanges(editor)) { 24938 if (editor.selection.getNode().nodeName === 'IMG') { 24939 Delay.setEditorTimeout(editor, () => { 24940 editor.nodeChanged(); 24941 }); 24942 } else { 24943 editor.nodeChanged(); 24944 } 24945 } 24946 }); 24947 } 24948 nodeChanged(args = {}) { 24949 const selection = this.editor.selection; 24950 let node; 24951 if (this.editor.initialized && selection && !shouldDisableNodeChange(this.editor) && !this.editor.mode.isReadOnly()) { 24952 const root = this.editor.getBody(); 24953 node = selection.getStart(true) || root; 24954 if (node.ownerDocument !== this.editor.getDoc() || !this.editor.dom.isChildOf(node, root)) { 24955 node = root; 24956 } 24957 const parents = []; 24958 this.editor.dom.getParent(node, node => { 24959 if (node === root) { 24960 return true; 24961 } else { 24962 parents.push(node); 24963 return false; 24964 } 24965 }); 24966 this.editor.dispatch('NodeChange', { 24967 ...args, 24968 element: node, 24969 parents 24970 }); 24971 } 24972 } 24973 isSameElementPath(startElm) { 24974 let i; 24975 const editor = this.editor; 24976 const currentPath = reverse(editor.dom.getParents(startElm, always, editor.getBody())); 24977 if (currentPath.length === this.lastPath.length) { 24978 for (i = currentPath.length; i >= 0; i--) { 24979 if (currentPath[i] !== this.lastPath[i]) { 24980 break; 24981 } 24982 } 24983 if (i === -1) { 24984 this.lastPath = currentPath; 24985 return true; 24986 } 24987 } 24988 this.lastPath = currentPath; 24989 return false; 24990 } 24991 } 24992 24993 const imageId = generate$1('image'); 24994 const getDragImage = transfer => { 24995 const dt = transfer; 24996 return Optional.from(dt[imageId]); 24997 }; 24998 const setDragImage = (transfer, imageData) => { 24999 const dt = transfer; 25000 dt[imageId] = imageData; 25001 }; 25002 25003 const eventId = generate$1('event'); 25004 const getEvent = transfer => { 25005 const dt = transfer; 25006 return Optional.from(dt[eventId]); 25007 }; 25008 const mkSetEventFn = type => transfer => { 25009 const dt = transfer; 25010 dt[eventId] = type; 25011 }; 25012 const setEvent = (transfer, type) => mkSetEventFn(type)(transfer); 25013 const setDragstartEvent = mkSetEventFn(0); 25014 const setDropEvent = mkSetEventFn(2); 25015 const setDragendEvent = mkSetEventFn(1); 25016 const checkEvent = expectedType => transfer => { 25017 const dt = transfer; 25018 return Optional.from(dt[eventId]).exists(type => type === expectedType); 25019 }; 25020 const isInDragStartEvent = checkEvent(0); 25021 25022 const createEmptyFileList = () => Object.freeze({ 25023 length: 0, 25024 item: _ => null 25025 }); 25026 25027 const modeId = generate$1('mode'); 25028 const getMode = transfer => { 25029 const dt = transfer; 25030 return Optional.from(dt[modeId]); 25031 }; 25032 const mkSetModeFn = mode => transfer => { 25033 const dt = transfer; 25034 dt[modeId] = mode; 25035 }; 25036 const setMode$1 = (transfer, mode) => mkSetModeFn(mode)(transfer); 25037 const setReadWriteMode = mkSetModeFn(0); 25038 const setReadOnlyMode = mkSetModeFn(2); 25039 const setProtectedMode = mkSetModeFn(1); 25040 const checkMode = expectedMode => transfer => { 25041 const dt = transfer; 25042 return Optional.from(dt[modeId]).exists(mode => mode === expectedMode); 25043 }; 25044 const isInReadWriteMode = checkMode(0); 25045 const isInProtectedMode = checkMode(1); 25046 25047 const normalizeItems = (dataTransfer, itemsImpl) => ({ 25048 ...itemsImpl, 25049 get length() { 25050 return itemsImpl.length; 25051 }, 25052 add: (data, type) => { 25053 if (isInReadWriteMode(dataTransfer)) { 25054 if (isString(data)) { 25055 if (!isUndefined(type)) { 25056 return itemsImpl.add(data, type); 25057 } 25058 } else { 25059 return itemsImpl.add(data); 25060 } 25061 } 25062 return null; 25063 }, 25064 remove: idx => { 25065 if (isInReadWriteMode(dataTransfer)) { 25066 itemsImpl.remove(idx); 25067 } 25068 }, 25069 clear: () => { 25070 if (isInReadWriteMode(dataTransfer)) { 25071 itemsImpl.clear(); 25072 } 25073 } 25074 }); 25075 25076 const validDropEffects = [ 25077 'none', 25078 'copy', 25079 'link', 25080 'move' 25081 ]; 25082 const validEffectAlloweds = [ 25083 'none', 25084 'copy', 25085 'copyLink', 25086 'copyMove', 25087 'link', 25088 'linkMove', 25089 'move', 25090 'all', 25091 'uninitialized' 25092 ]; 25093 const createDataTransfer = () => { 25094 const dataTransferImpl = new window.DataTransfer(); 25095 let dropEffect = 'move'; 25096 let effectAllowed = 'all'; 25097 const dataTransfer = { 25098 get dropEffect() { 25099 return dropEffect; 25100 }, 25101 set dropEffect(effect) { 25102 if (contains$2(validDropEffects, effect)) { 25103 dropEffect = effect; 25104 } 25105 }, 25106 get effectAllowed() { 25107 return effectAllowed; 25108 }, 25109 set effectAllowed(allowed) { 25110 if (isInDragStartEvent(dataTransfer) && contains$2(validEffectAlloweds, allowed)) { 25111 effectAllowed = allowed; 25112 } 25113 }, 25114 get items() { 25115 return normalizeItems(dataTransfer, dataTransferImpl.items); 25116 }, 25117 get files() { 25118 if (isInProtectedMode(dataTransfer)) { 25119 return createEmptyFileList(); 25120 } else { 25121 return dataTransferImpl.files; 25122 } 25123 }, 25124 get types() { 25125 return dataTransferImpl.types; 25126 }, 25127 setDragImage: (image, x, y) => { 25128 if (isInReadWriteMode(dataTransfer)) { 25129 setDragImage(dataTransfer, { 25130 image, 25131 x, 25132 y 25133 }); 25134 dataTransferImpl.setDragImage(image, x, y); 25135 } 25136 }, 25137 getData: format => { 25138 if (isInProtectedMode(dataTransfer)) { 25139 return ''; 25140 } else { 25141 return dataTransferImpl.getData(format); 25142 } 25143 }, 25144 setData: (format, data) => { 25145 if (isInReadWriteMode(dataTransfer)) { 25146 dataTransferImpl.setData(format, data); 25147 } 25148 }, 25149 clearData: format => { 25150 if (isInReadWriteMode(dataTransfer)) { 25151 dataTransferImpl.clearData(format); 25152 } 25153 } 25154 }; 25155 setReadWriteMode(dataTransfer); 25156 return dataTransfer; 25157 }; 25158 const cloneDataTransfer = original => { 25159 const clone = createDataTransfer(); 25160 const originalMode = getMode(original); 25161 setReadOnlyMode(original); 25162 setDragstartEvent(clone); 25163 clone.dropEffect = original.dropEffect; 25164 clone.effectAllowed = original.effectAllowed; 25165 getDragImage(original).each(imageData => clone.setDragImage(imageData.image, imageData.x, imageData.y)); 25166 each$e(original.types, type => { 25167 if (type !== 'Files') { 25168 clone.setData(type, original.getData(type)); 25169 } 25170 }); 25171 each$e(original.files, file => clone.items.add(file)); 25172 getEvent(original).each(type => { 25173 setEvent(clone, type); 25174 }); 25175 originalMode.each(mode => { 25176 setMode$1(original, mode); 25177 setMode$1(clone, mode); 25178 }); 25179 return clone; 25180 }; 25181 25182 const getHtmlData = dataTransfer => { 25183 const html = dataTransfer.getData('text/html'); 25184 return html === '' ? Optional.none() : Optional.some(html); 25185 }; 25186 const setHtmlData = (dataTransfer, html) => dataTransfer.setData('text/html', html); 25187 25188 const internalMimeType = 'x-tinymce/html'; 25189 const internalHtmlMime = constant(internalMimeType); 25190 const internalMark = '<!-- ' + internalMimeType + ' -->'; 25191 const mark = html => internalMark + html; 25192 const unmark = html => html.replace(internalMark, ''); 25193 const isMarked = html => html.indexOf(internalMark) !== -1; 25194 25195 const isPlainText = text => { 25196 return !/<(?:\/?(?!(?:div|p|br|span)>)\w+|(?:(?!(?:span style="white-space:\s?pre;?">)|br\s?\/>))\w+\s[^>]+)>/i.test(text); 25197 }; 25198 const openContainer = (rootTag, rootAttrs) => { 25199 let tag = '<' + rootTag; 25200 const attrs = mapToArray(rootAttrs, (value, key) => key + '="' + Entities.encodeAllRaw(value) + '"'); 25201 if (attrs.length) { 25202 tag += ' ' + attrs.join(' '); 25203 } 25204 return tag + '>'; 25205 }; 25206 const toBlockElements = (text, rootTag, rootAttrs) => { 25207 const blocks = text.split(/\n\n/); 25208 const tagOpen = openContainer(rootTag, rootAttrs); 25209 const tagClose = '</' + rootTag + '>'; 25210 const paragraphs = map$3(blocks, p => { 25211 return p.split(/\n/).join('<br />'); 25212 }); 25213 const stitch = p => { 25214 return tagOpen + p + tagClose; 25215 }; 25216 return paragraphs.length === 1 ? paragraphs[0] : map$3(paragraphs, stitch).join(''); 25217 }; 25218 25219 const pasteBinDefaultContent = '%MCEPASTEBIN%'; 25220 const create$6 = (editor, lastRngCell) => { 25221 const {dom, selection} = editor; 25222 const body = editor.getBody(); 25223 lastRngCell.set(selection.getRng()); 25224 const pasteBinElm = dom.add(editor.getBody(), 'div', { 25225 'id': 'mcepastebin', 25226 'class': 'mce-pastebin', 25227 'contentEditable': true, 25228 'data-mce-bogus': 'all', 25229 'style': 'position: fixed; top: 50%; width: 10px; height: 10px; overflow: hidden; opacity: 0' 25230 }, pasteBinDefaultContent); 25231 if (Env.browser.isFirefox()) { 25232 dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) === 'rtl' ? 65535 : -65535); 25233 } 25234 dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', e => { 25235 e.stopPropagation(); 25236 }); 25237 pasteBinElm.focus(); 25238 selection.select(pasteBinElm, true); 25239 }; 25240 const remove = (editor, lastRngCell) => { 25241 const dom = editor.dom; 25242 if (getEl(editor)) { 25243 let pasteBinClone; 25244 const lastRng = lastRngCell.get(); 25245 while (pasteBinClone = getEl(editor)) { 25246 dom.remove(pasteBinClone); 25247 dom.unbind(pasteBinClone); 25248 } 25249 if (lastRng) { 25250 editor.selection.setRng(lastRng); 25251 } 25252 } 25253 lastRngCell.set(null); 25254 }; 25255 const getEl = editor => editor.dom.get('mcepastebin'); 25256 const isPasteBin = elm => isNonNullable(elm) && elm.id === 'mcepastebin'; 25257 const getHtml = editor => { 25258 const dom = editor.dom; 25259 const copyAndRemove = (toElm, fromElm) => { 25260 toElm.appendChild(fromElm); 25261 dom.remove(fromElm, true); 25262 }; 25263 const [pasteBinElm, ...pasteBinClones] = filter$5(editor.getBody().childNodes, isPasteBin); 25264 each$e(pasteBinClones, pasteBinClone => { 25265 copyAndRemove(pasteBinElm, pasteBinClone); 25266 }); 25267 const dirtyWrappers = dom.select('div[id=mcepastebin]', pasteBinElm); 25268 for (let i = dirtyWrappers.length - 1; i >= 0; i--) { 25269 const cleanWrapper = dom.create('div'); 25270 pasteBinElm.insertBefore(cleanWrapper, dirtyWrappers[i]); 25271 copyAndRemove(cleanWrapper, dirtyWrappers[i]); 25272 } 25273 return pasteBinElm ? pasteBinElm.innerHTML : ''; 25274 }; 25275 const isDefaultPasteBinContent = content => content === pasteBinDefaultContent; 25276 const PasteBin = editor => { 25277 const lastRng = Cell(null); 25278 return { 25279 create: () => create$6(editor, lastRng), 25280 remove: () => remove(editor, lastRng), 25281 getEl: () => getEl(editor), 25282 getHtml: () => getHtml(editor), 25283 getLastRng: lastRng.get 25284 }; 25285 }; 25286 25287 const filter$1 = (content, items) => { 25288 Tools.each(items, v => { 25289 if (is$4(v, RegExp)) { 25290 content = content.replace(v, ''); 25291 } else { 25292 content = content.replace(v[0], v[1]); 25293 } 25294 }); 25295 return content; 25296 }; 25297 const innerText = html => { 25298 const schema = Schema(); 25299 const domParser = DomParser({}, schema); 25300 let text = ''; 25301 const voidElements = schema.getVoidElements(); 25302 const ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' '); 25303 const blockElements = schema.getBlockElements(); 25304 const walk = node => { 25305 const name = node.name, currentNode = node; 25306 if (name === 'br') { 25307 text += '\n'; 25308 return; 25309 } 25310 if (name === 'wbr') { 25311 return; 25312 } 25313 if (voidElements[name]) { 25314 text += ' '; 25315 } 25316 if (ignoreElements[name]) { 25317 text += ' '; 25318 return; 25319 } 25320 if (node.type === 3) { 25321 text += node.value; 25322 } 25323 if (!(node.name in schema.getVoidElements())) { 25324 let currentNode = node.firstChild; 25325 if (currentNode) { 25326 do { 25327 walk(currentNode); 25328 } while (currentNode = currentNode.next); 25329 } 25330 } 25331 if (blockElements[name] && currentNode.next) { 25332 text += '\n'; 25333 if (name === 'p') { 25334 text += '\n'; 25335 } 25336 } 25337 }; 25338 html = filter$1(html, [/<!\[[^\]]+\]>/g]); 25339 walk(domParser.parse(html)); 25340 return text; 25341 }; 25342 const trimHtml = html => { 25343 const trimSpaces = (all, s1, s2) => { 25344 if (!s1 && !s2) { 25345 return ' '; 25346 } 25347 return nbsp; 25348 }; 25349 html = filter$1(html, [ 25350 /^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/ig, 25351 /<!--StartFragment-->|<!--EndFragment-->/g, 25352 [ 25353 /( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g, 25354 trimSpaces 25355 ], 25356 /<br class="Apple-interchange-newline">/g, 25357 /<br>$/i 25358 ]); 25359 return html; 25360 }; 25361 const createIdGenerator = prefix => { 25362 let count = 0; 25363 return () => { 25364 return prefix + count++; 25365 }; 25366 }; 25367 const getImageMimeType = ext => { 25368 const lowerExt = ext.toLowerCase(); 25369 const mimeOverrides = { 25370 jpg: 'jpeg', 25371 jpe: 'jpeg', 25372 jfi: 'jpeg', 25373 jif: 'jpeg', 25374 jfif: 'jpeg', 25375 pjpeg: 'jpeg', 25376 pjp: 'jpeg', 25377 svg: 'svg+xml' 25378 }; 25379 return Tools.hasOwn(mimeOverrides, lowerExt) ? 'image/' + mimeOverrides[lowerExt] : 'image/' + lowerExt; 25380 }; 25381 25382 const preProcess = (editor, html) => { 25383 const parser = DomParser({ sanitize: shouldSanitizeXss(editor) }, editor.schema); 25384 parser.addNodeFilter('meta', nodes => { 25385 Tools.each(nodes, node => { 25386 node.remove(); 25387 }); 25388 }); 25389 const fragment = parser.parse(html, { 25390 forced_root_block: false, 25391 isRootContent: true 25392 }); 25393 return HtmlSerializer({ validate: true }, editor.schema).serialize(fragment); 25394 }; 25395 const processResult = (content, cancelled) => ({ 25396 content, 25397 cancelled 25398 }); 25399 const postProcessFilter = (editor, html, internal) => { 25400 const tempBody = editor.dom.create('div', { style: 'display:none' }, html); 25401 const postProcessArgs = firePastePostProcess(editor, tempBody, internal); 25402 return processResult(postProcessArgs.node.innerHTML, postProcessArgs.isDefaultPrevented()); 25403 }; 25404 const filterContent = (editor, content, internal) => { 25405 const preProcessArgs = firePastePreProcess(editor, content, internal); 25406 const filteredContent = preProcess(editor, preProcessArgs.content); 25407 if (editor.hasEventListeners('PastePostProcess') && !preProcessArgs.isDefaultPrevented()) { 25408 return postProcessFilter(editor, filteredContent, internal); 25409 } else { 25410 return processResult(filteredContent, preProcessArgs.isDefaultPrevented()); 25411 } 25412 }; 25413 const process = (editor, html, internal) => { 25414 return filterContent(editor, html, internal); 25415 }; 25416 25417 const pasteHtml$1 = (editor, html) => { 25418 editor.insertContent(html, { 25419 merge: shouldPasteMergeFormats(editor), 25420 paste: true 25421 }); 25422 return true; 25423 }; 25424 const isAbsoluteUrl = url => /^https?:\/\/[\w\-\/+=.,!;:&%@^~(){}?#]+$/i.test(url); 25425 const isImageUrl = (editor, url) => { 25426 return isAbsoluteUrl(url) && exists(getAllowedImageFileTypes(editor), type => endsWith(url.toLowerCase(), `.${ type.toLowerCase() }`)); 25427 }; 25428 const createImage = (editor, url, pasteHtmlFn) => { 25429 editor.undoManager.extra(() => { 25430 pasteHtmlFn(editor, url); 25431 }, () => { 25432 editor.insertContent('<img src="' + url + '">'); 25433 }); 25434 return true; 25435 }; 25436 const createLink = (editor, url, pasteHtmlFn) => { 25437 editor.undoManager.extra(() => { 25438 pasteHtmlFn(editor, url); 25439 }, () => { 25440 editor.execCommand('mceInsertLink', false, url); 25441 }); 25442 return true; 25443 }; 25444 const linkSelection = (editor, html, pasteHtmlFn) => !editor.selection.isCollapsed() && isAbsoluteUrl(html) ? createLink(editor, html, pasteHtmlFn) : false; 25445 const insertImage = (editor, html, pasteHtmlFn) => isImageUrl(editor, html) ? createImage(editor, html, pasteHtmlFn) : false; 25446 const smartInsertContent = (editor, html) => { 25447 Tools.each([ 25448 linkSelection, 25449 insertImage, 25450 pasteHtml$1 25451 ], action => { 25452 return !action(editor, html, pasteHtml$1); 25453 }); 25454 }; 25455 const insertContent = (editor, html, pasteAsText) => { 25456 if (pasteAsText || !isSmartPasteEnabled(editor)) { 25457 pasteHtml$1(editor, html); 25458 } else { 25459 smartInsertContent(editor, html); 25460 } 25461 }; 25462 25463 const uniqueId = createIdGenerator('mceclip'); 25464 const createPasteDataTransfer = html => { 25465 const dataTransfer = createDataTransfer(); 25466 setHtmlData(dataTransfer, html); 25467 setReadOnlyMode(dataTransfer); 25468 return dataTransfer; 25469 }; 25470 const doPaste = (editor, content, internal, pasteAsText, shouldSimulateInputEvent) => { 25471 const res = process(editor, content, internal); 25472 if (!res.cancelled) { 25473 const content = res.content; 25474 const doPasteAction = () => insertContent(editor, content, pasteAsText); 25475 if (shouldSimulateInputEvent) { 25476 const args = fireBeforeInputEvent(editor, 'insertFromPaste', { dataTransfer: createPasteDataTransfer(content) }); 25477 if (!args.isDefaultPrevented()) { 25478 doPasteAction(); 25479 fireInputEvent(editor, 'insertFromPaste'); 25480 } 25481 } else { 25482 doPasteAction(); 25483 } 25484 } 25485 }; 25486 const pasteHtml = (editor, html, internalFlag, shouldSimulateInputEvent) => { 25487 const internal = internalFlag ? internalFlag : isMarked(html); 25488 doPaste(editor, unmark(html), internal, false, shouldSimulateInputEvent); 25489 }; 25490 const pasteText = (editor, text, shouldSimulateInputEvent) => { 25491 const encodedText = editor.dom.encode(text).replace(/\r\n/g, '\n'); 25492 const normalizedText = normalize$4(encodedText, getPasteTabSpaces(editor)); 25493 const html = toBlockElements(normalizedText, getForcedRootBlock(editor), getForcedRootBlockAttrs(editor)); 25494 doPaste(editor, html, false, true, shouldSimulateInputEvent); 25495 }; 25496 const getDataTransferItems = dataTransfer => { 25497 const items = {}; 25498 if (dataTransfer && dataTransfer.types) { 25499 for (let i = 0; i < dataTransfer.types.length; i++) { 25500 const contentType = dataTransfer.types[i]; 25501 try { 25502 items[contentType] = dataTransfer.getData(contentType); 25503 } catch (ex) { 25504 items[contentType] = ''; 25505 } 25506 } 25507 } 25508 return items; 25509 }; 25510 const hasContentType = (clipboardContent, mimeType) => mimeType in clipboardContent && clipboardContent[mimeType].length > 0; 25511 const hasHtmlOrText = content => hasContentType(content, 'text/html') || hasContentType(content, 'text/plain'); 25512 const extractFilename = (editor, str) => { 25513 const m = str.match(/([\s\S]+?)(?:\.[a-z0-9.]+)$/i); 25514 return isNonNullable(m) ? editor.dom.encode(m[1]) : undefined; 25515 }; 25516 const createBlobInfo = (editor, blobCache, file, base64) => { 25517 const id = uniqueId(); 25518 const useFileName = shouldReuseFileName(editor) && isNonNullable(file.name); 25519 const name = useFileName ? extractFilename(editor, file.name) : id; 25520 const filename = useFileName ? file.name : undefined; 25521 const blobInfo = blobCache.create(id, file, base64, name, filename); 25522 blobCache.add(blobInfo); 25523 return blobInfo; 25524 }; 25525 const pasteImage = (editor, imageItem) => { 25526 parseDataUri(imageItem.uri).each(({data, type, base64Encoded}) => { 25527 const base64 = base64Encoded ? data : btoa(data); 25528 const file = imageItem.file; 25529 const blobCache = editor.editorUpload.blobCache; 25530 const existingBlobInfo = blobCache.getByData(base64, type); 25531 const blobInfo = existingBlobInfo !== null && existingBlobInfo !== void 0 ? existingBlobInfo : createBlobInfo(editor, blobCache, file, base64); 25532 pasteHtml(editor, `<img src="${ blobInfo.blobUri() }">`, false, true); 25533 }); 25534 }; 25535 const isClipboardEvent = event => event.type === 'paste'; 25536 const readFilesAsDataUris = items => Promise.all(map$3(items, file => { 25537 return blobToDataUri(file).then(uri => ({ 25538 file, 25539 uri 25540 })); 25541 })); 25542 const isImage = editor => { 25543 const allowedExtensions = getAllowedImageFileTypes(editor); 25544 return file => startsWith(file.type, 'image/') && exists(allowedExtensions, extension => { 25545 return getImageMimeType(extension) === file.type; 25546 }); 25547 }; 25548 const getImagesFromDataTransfer = (editor, dataTransfer) => { 25549 const items = dataTransfer.items ? bind$3(from(dataTransfer.items), item => { 25550 return item.kind === 'file' ? [item.getAsFile()] : []; 25551 }) : []; 25552 const files = dataTransfer.files ? from(dataTransfer.files) : []; 25553 return filter$5(items.length > 0 ? items : files, isImage(editor)); 25554 }; 25555 const pasteImageData = (editor, e, rng) => { 25556 const dataTransfer = isClipboardEvent(e) ? e.clipboardData : e.dataTransfer; 25557 if (shouldPasteDataImages(editor) && dataTransfer) { 25558 const images = getImagesFromDataTransfer(editor, dataTransfer); 25559 if (images.length > 0) { 25560 e.preventDefault(); 25561 readFilesAsDataUris(images).then(fileResults => { 25562 if (rng) { 25563 editor.selection.setRng(rng); 25564 } 25565 each$e(fileResults, result => { 25566 pasteImage(editor, result); 25567 }); 25568 }); 25569 return true; 25570 } 25571 } 25572 return false; 25573 }; 25574 const isBrokenAndroidClipboardEvent = e => { 25575 var _a, _b; 25576 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; 25577 }; 25578 const isKeyboardPasteEvent = e => VK.metaKeyPressed(e) && e.keyCode === 86 || e.shiftKey && e.keyCode === 45; 25579 const insertClipboardContent = (editor, clipboardContent, html, plainTextMode, shouldSimulateInputEvent) => { 25580 let content = trimHtml(html); 25581 const isInternal = hasContentType(clipboardContent, internalHtmlMime()) || isMarked(html); 25582 const isPlainTextHtml = !isInternal && isPlainText(content); 25583 const isAbsoluteUrl$1 = isAbsoluteUrl(content); 25584 if (isDefaultPasteBinContent(content) || !content.length || isPlainTextHtml && !isAbsoluteUrl$1) { 25585 plainTextMode = true; 25586 } 25587 if (plainTextMode || isAbsoluteUrl$1) { 25588 if (hasContentType(clipboardContent, 'text/plain') && isPlainTextHtml) { 25589 content = clipboardContent['text/plain']; 25590 } else { 25591 content = innerText(content); 25592 } 25593 } 25594 if (isDefaultPasteBinContent(content)) { 25595 return; 25596 } 25597 if (plainTextMode) { 25598 pasteText(editor, content, shouldSimulateInputEvent); 25599 } else { 25600 pasteHtml(editor, content, isInternal, shouldSimulateInputEvent); 25601 } 25602 }; 25603 const registerEventHandlers = (editor, pasteBin, pasteFormat) => { 25604 let keyboardPastePlainTextState; 25605 const getLastRng = () => pasteBin.getLastRng() || editor.selection.getRng(); 25606 editor.on('keydown', e => { 25607 if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) { 25608 keyboardPastePlainTextState = e.shiftKey && e.keyCode === 86; 25609 } 25610 }); 25611 editor.on('paste', e => { 25612 if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) { 25613 return; 25614 } 25615 const plainTextMode = pasteFormat.get() === 'text' || keyboardPastePlainTextState; 25616 keyboardPastePlainTextState = false; 25617 const clipboardContent = getDataTransferItems(e.clipboardData); 25618 if (!hasHtmlOrText(clipboardContent) && pasteImageData(editor, e, getLastRng())) { 25619 return; 25620 } 25621 if (hasContentType(clipboardContent, 'text/html')) { 25622 e.preventDefault(); 25623 insertClipboardContent(editor, clipboardContent, clipboardContent['text/html'], plainTextMode, true); 25624 } else if (hasContentType(clipboardContent, 'text/plain') && hasContentType(clipboardContent, 'text/uri-list')) { 25625 e.preventDefault(); 25626 insertClipboardContent(editor, clipboardContent, clipboardContent['text/plain'], plainTextMode, true); 25627 } else { 25628 pasteBin.create(); 25629 Delay.setEditorTimeout(editor, () => { 25630 const html = pasteBin.getHtml(); 25631 pasteBin.remove(); 25632 insertClipboardContent(editor, clipboardContent, html, plainTextMode, false); 25633 }, 0); 25634 } 25635 }); 25636 }; 25637 const registerDataImageFilter = editor => { 25638 const isWebKitFakeUrl = src => startsWith(src, 'webkit-fake-url'); 25639 const isDataUri = src => startsWith(src, 'data:'); 25640 const isPasteInsert = args => { 25641 var _a; 25642 return ((_a = args.data) === null || _a === void 0 ? void 0 : _a.paste) === true; 25643 }; 25644 editor.parser.addNodeFilter('img', (nodes, name, args) => { 25645 if (!shouldPasteDataImages(editor) && isPasteInsert(args)) { 25646 for (const node of nodes) { 25647 const src = node.attr('src'); 25648 if (isString(src) && !node.attr('data-mce-object') && src !== Env.transparentSrc) { 25649 if (isWebKitFakeUrl(src)) { 25650 node.remove(); 25651 } else if (!shouldAllowHtmlDataUrls(editor) && isDataUri(src)) { 25652 node.remove(); 25653 } 25654 } 25655 } 25656 } 25657 }); 25658 }; 25659 const registerEventsAndFilters = (editor, pasteBin, pasteFormat) => { 25660 registerEventHandlers(editor, pasteBin, pasteFormat); 25661 registerDataImageFilter(editor); 25662 }; 25663 25664 const togglePlainTextPaste = (editor, pasteFormat) => { 25665 if (pasteFormat.get() === 'text') { 25666 pasteFormat.set('html'); 25667 firePastePlainTextToggle(editor, false); 25668 } else { 25669 pasteFormat.set('text'); 25670 firePastePlainTextToggle(editor, true); 25671 } 25672 editor.focus(); 25673 }; 25674 const register$1 = (editor, pasteFormat) => { 25675 editor.addCommand('mceTogglePlainTextPaste', () => { 25676 togglePlainTextPaste(editor, pasteFormat); 25677 }); 25678 editor.addCommand('mceInsertClipboardContent', (ui, value) => { 25679 if (value.html) { 25680 pasteHtml(editor, value.html, value.internal, false); 25681 } 25682 if (value.text) { 25683 pasteText(editor, value.text, false); 25684 } 25685 }); 25686 }; 25687 25688 const setHtml5Clipboard = (clipboardData, html, text) => { 25689 if (clipboardData) { 25690 try { 25691 clipboardData.clearData(); 25692 clipboardData.setData('text/html', html); 25693 clipboardData.setData('text/plain', text); 25694 clipboardData.setData(internalHtmlMime(), html); 25695 return true; 25696 } catch (e) { 25697 return false; 25698 } 25699 } else { 25700 return false; 25701 } 25702 }; 25703 const setClipboardData = (evt, data, fallback, done) => { 25704 if (setHtml5Clipboard(evt.clipboardData, data.html, data.text)) { 25705 evt.preventDefault(); 25706 done(); 25707 } else { 25708 fallback(data.html, done); 25709 } 25710 }; 25711 const fallback = editor => (html, done) => { 25712 const {dom, selection} = editor; 25713 const outer = dom.create('div', { 25714 'contenteditable': 'false', 25715 'data-mce-bogus': 'all' 25716 }); 25717 const inner = dom.create('div', { contenteditable: 'true' }, html); 25718 dom.setStyles(outer, { 25719 position: 'fixed', 25720 top: '0', 25721 left: '-3000px', 25722 width: '1000px', 25723 overflow: 'hidden' 25724 }); 25725 outer.appendChild(inner); 25726 dom.add(editor.getBody(), outer); 25727 const range = selection.getRng(); 25728 inner.focus(); 25729 const offscreenRange = dom.createRng(); 25730 offscreenRange.selectNodeContents(inner); 25731 selection.setRng(offscreenRange); 25732 Delay.setEditorTimeout(editor, () => { 25733 selection.setRng(range); 25734 dom.remove(outer); 25735 done(); 25736 }, 0); 25737 }; 25738 const getData = editor => ({ 25739 html: mark(editor.selection.getContent({ contextual: true })), 25740 text: editor.selection.getContent({ format: 'text' }) 25741 }); 25742 const isTableSelection = editor => !!editor.dom.getParent(editor.selection.getStart(), 'td[data-mce-selected],th[data-mce-selected]', editor.getBody()); 25743 const hasSelectedContent = editor => !editor.selection.isCollapsed() || isTableSelection(editor); 25744 const cut = editor => evt => { 25745 if (!evt.isDefaultPrevented() && hasSelectedContent(editor)) { 25746 setClipboardData(evt, getData(editor), fallback(editor), () => { 25747 if (Env.browser.isChromium() || Env.browser.isFirefox()) { 25748 const rng = editor.selection.getRng(); 25749 Delay.setEditorTimeout(editor, () => { 25750 editor.selection.setRng(rng); 25751 editor.execCommand('Delete'); 25752 }, 0); 25753 } else { 25754 editor.execCommand('Delete'); 25755 } 25756 }); 25757 } 25758 }; 25759 const copy = editor => evt => { 25760 if (!evt.isDefaultPrevented() && hasSelectedContent(editor)) { 25761 setClipboardData(evt, getData(editor), fallback(editor), noop); 25762 } 25763 }; 25764 const register = editor => { 25765 editor.on('cut', cut(editor)); 25766 editor.on('copy', copy(editor)); 25767 }; 25768 25769 const getCaretRangeFromEvent = (editor, e) => { 25770 var _a, _b; 25771 return RangeUtils.getCaretRangeFromPoint((_a = e.clientX) !== null && _a !== void 0 ? _a : 0, (_b = e.clientY) !== null && _b !== void 0 ? _b : 0, editor.getDoc()); 25772 }; 25773 const isPlainTextFileUrl = content => { 25774 const plainTextContent = content['text/plain']; 25775 return plainTextContent ? plainTextContent.indexOf('file://') === 0 : false; 25776 }; 25777 const setFocusedRange = (editor, rng) => { 25778 editor.focus(); 25779 if (rng) { 25780 editor.selection.setRng(rng); 25781 } 25782 }; 25783 const hasImage = dataTransfer => exists(dataTransfer.files, file => /^image\//.test(file.type)); 25784 const isTransparentBlockDrop = (dom, schema, target, dropContent) => { 25785 const parentTransparent = dom.getParent(target, node => isTransparentBlock(schema, node)); 25786 if (parentTransparent && has$2(dropContent, 'text/html')) { 25787 const fragment = new DOMParser().parseFromString(dropContent['text/html'], 'text/html').body; 25788 return !isNull(fragment.querySelector(parentTransparent.nodeName.toLowerCase())); 25789 } else { 25790 return false; 25791 } 25792 }; 25793 const setup$a = (editor, draggingInternallyState) => { 25794 if (shouldPasteBlockDrop(editor)) { 25795 editor.on('dragend dragover draggesture dragdrop drop drag', e => { 25796 e.preventDefault(); 25797 e.stopPropagation(); 25798 }); 25799 } 25800 if (!shouldPasteDataImages(editor)) { 25801 editor.on('drop', e => { 25802 const dataTransfer = e.dataTransfer; 25803 if (dataTransfer && hasImage(dataTransfer)) { 25804 e.preventDefault(); 25805 } 25806 }); 25807 } 25808 editor.on('drop', e => { 25809 if (e.isDefaultPrevented()) { 25810 return; 25811 } 25812 const rng = getCaretRangeFromEvent(editor, e); 25813 if (isNullable(rng)) { 25814 return; 25815 } 25816 const dropContent = getDataTransferItems(e.dataTransfer); 25817 const internal = hasContentType(dropContent, internalHtmlMime()); 25818 if ((!hasHtmlOrText(dropContent) || isPlainTextFileUrl(dropContent)) && pasteImageData(editor, e, rng)) { 25819 return; 25820 } 25821 const internalContent = dropContent[internalHtmlMime()]; 25822 const content = internalContent || dropContent['text/html'] || dropContent['text/plain']; 25823 const transparentElementDrop = isTransparentBlockDrop(editor.dom, editor.schema, rng.startContainer, dropContent); 25824 if (draggingInternallyState.get() && !transparentElementDrop) { 25825 return; 25826 } 25827 if (content) { 25828 e.preventDefault(); 25829 Delay.setEditorTimeout(editor, () => { 25830 editor.undoManager.transact(() => { 25831 if (internalContent) { 25832 editor.execCommand('Delete'); 25833 } 25834 setFocusedRange(editor, rng); 25835 const trimmedContent = trimHtml(content); 25836 if (dropContent['text/html']) { 25837 pasteHtml(editor, trimmedContent, internal, true); 25838 } else { 25839 pasteText(editor, trimmedContent, true); 25840 } 25841 }); 25842 }); 25843 } 25844 }); 25845 editor.on('dragstart', _e => { 25846 draggingInternallyState.set(true); 25847 }); 25848 editor.on('dragover dragend', e => { 25849 if (shouldPasteDataImages(editor) && !draggingInternallyState.get()) { 25850 e.preventDefault(); 25851 setFocusedRange(editor, getCaretRangeFromEvent(editor, e)); 25852 } 25853 if (e.type === 'dragend') { 25854 draggingInternallyState.set(false); 25855 } 25856 }); 25857 }; 25858 25859 const setup$9 = editor => { 25860 const processEvent = f => e => { 25861 f(editor, e); 25862 }; 25863 const preProcess = getPastePreProcess(editor); 25864 if (isFunction(preProcess)) { 25865 editor.on('PastePreProcess', processEvent(preProcess)); 25866 } 25867 const postProcess = getPastePostProcess(editor); 25868 if (isFunction(postProcess)) { 25869 editor.on('PastePostProcess', processEvent(postProcess)); 25870 } 25871 }; 25872 25873 const addPreProcessFilter = (editor, filterFunc) => { 25874 editor.on('PastePreProcess', e => { 25875 e.content = filterFunc(editor, e.content, e.internal); 25876 }); 25877 }; 25878 const rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi; 25879 const rgbToHex = value => Tools.trim(value).replace(rgbRegExp, rgbaToHexString).toLowerCase(); 25880 const removeWebKitStyles = (editor, content, internal) => { 25881 const webKitStylesOption = getPasteWebkitStyles(editor); 25882 if (internal || webKitStylesOption === 'all' || !shouldPasteRemoveWebKitStyles(editor)) { 25883 return content; 25884 } 25885 const webKitStyles = webKitStylesOption ? webKitStylesOption.split(/[, ]/) : []; 25886 if (webKitStyles && webKitStylesOption !== 'none') { 25887 const dom = editor.dom, node = editor.selection.getNode(); 25888 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, (all, before, value, after) => { 25889 const inputStyles = dom.parseStyle(dom.decode(value)); 25890 const outputStyles = {}; 25891 for (let i = 0; i < webKitStyles.length; i++) { 25892 const inputValue = inputStyles[webKitStyles[i]]; 25893 let compareInput = inputValue; 25894 let currentValue = dom.getStyle(node, webKitStyles[i], true); 25895 if (/color/.test(webKitStyles[i])) { 25896 compareInput = rgbToHex(compareInput); 25897 currentValue = rgbToHex(currentValue); 25898 } 25899 if (currentValue !== compareInput) { 25900 outputStyles[webKitStyles[i]] = inputValue; 25901 } 25902 } 25903 const outputStyle = dom.serializeStyle(outputStyles, 'span'); 25904 if (outputStyle) { 25905 return before + ' style="' + outputStyle + '"' + after; 25906 } 25907 return before + after; 25908 }); 25909 } else { 25910 content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3'); 25911 } 25912 content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, (all, before, value, after) => { 25913 return before + ' style="' + value + '"' + after; 25914 }); 25915 return content; 25916 }; 25917 const setup$8 = editor => { 25918 if (Env.browser.isChromium() || Env.browser.isSafari()) { 25919 addPreProcessFilter(editor, removeWebKitStyles); 25920 } 25921 }; 25922 25923 const setup$7 = editor => { 25924 const draggingInternallyState = Cell(false); 25925 const pasteFormat = Cell(isPasteAsTextEnabled(editor) ? 'text' : 'html'); 25926 const pasteBin = PasteBin(editor); 25927 setup$8(editor); 25928 register$1(editor, pasteFormat); 25929 setup$9(editor); 25930 editor.on('PreInit', () => { 25931 register(editor); 25932 setup$a(editor, draggingInternallyState); 25933 registerEventsAndFilters(editor, pasteBin, pasteFormat); 25934 }); 25935 }; 25936 25937 const preventSummaryToggle = editor => { 25938 editor.on('click', e => { 25939 if (editor.dom.getParent(e.target, 'details')) { 25940 e.preventDefault(); 25941 } 25942 }); 25943 }; 25944 const filterDetails = editor => { 25945 editor.parser.addNodeFilter('details', elms => { 25946 const initialStateOption = getDetailsInitialState(editor); 25947 each$e(elms, details => { 25948 if (initialStateOption === 'expanded') { 25949 details.attr('open', 'open'); 25950 } else if (initialStateOption === 'collapsed') { 25951 details.attr('open', null); 25952 } 25953 }); 25954 }); 25955 editor.serializer.addNodeFilter('details', elms => { 25956 const serializedStateOption = getDetailsSerializedState(editor); 25957 each$e(elms, details => { 25958 if (serializedStateOption === 'expanded') { 25959 details.attr('open', 'open'); 25960 } else if (serializedStateOption === 'collapsed') { 25961 details.attr('open', null); 25962 } 25963 }); 25964 }); 25965 }; 25966 const emptyNodeContents = node => fillWithPaddingBr(SugarElement.fromDom(node)); 25967 const setCaretToPosition = (editor, position) => { 25968 const node = position.getNode(); 25969 if (!isUndefined(node)) { 25970 editor.selection.setCursorLocation(node, position.offset()); 25971 } 25972 }; 25973 const isEntireNodeSelected = (rng, node) => { 25974 var _a; 25975 return rng.startOffset === 0 && rng.endOffset === ((_a = node.textContent) === null || _a === void 0 ? void 0 : _a.length); 25976 }; 25977 const platform = detect$2(); 25978 const browser = platform.browser; 25979 const os = platform.os; 25980 const isSafari = browser.isSafari(); 25981 const isMacOSOriOS = os.isMacOS() || os.isiOS(); 25982 const isCaretInTheBeginningOf = (caretPos, element) => firstPositionIn(element).exists(pos => pos.isEqual(caretPos)); 25983 const isCaretInTheEndOf = (caretPos, element) => { 25984 return lastPositionIn(element).exists(pos => { 25985 if (isBr$6(pos.getNode())) { 25986 return prevPosition(element, pos).exists(pos2 => pos2.isEqual(caretPos)) || pos.isEqual(caretPos); 25987 } else { 25988 return pos.isEqual(caretPos); 25989 } 25990 }); 25991 }; 25992 const getDetailsElements = (dom, rng) => { 25993 const startDetails = Optional.from(dom.getParent(rng.startContainer, 'details')); 25994 const endDetails = Optional.from(dom.getParent(rng.endContainer, 'details')); 25995 if (startDetails.isSome() || endDetails.isSome()) { 25996 const startSummary = startDetails.bind(details => Optional.from(dom.select('summary', details)[0])); 25997 return Optional.some({ 25998 startSummary, 25999 startDetails, 26000 endDetails 26001 }); 26002 } else { 26003 return Optional.none(); 26004 } 26005 }; 26006 const isPartialDelete = (rng, detailsElements) => { 26007 const containsStart = element => element.contains(rng.startContainer); 26008 const containsEnd = element => element.contains(rng.endContainer); 26009 const startInSummary = detailsElements.startSummary.exists(containsStart); 26010 const endInSummary = detailsElements.startSummary.exists(containsEnd); 26011 const isPartiallySelectedDetailsElements = detailsElements.startDetails.forall(startDetails => detailsElements.endDetails.forall(endDetails => startDetails !== endDetails)); 26012 const isInPartiallySelectedSummary = (startInSummary || endInSummary) && !(startInSummary && endInSummary); 26013 return isInPartiallySelectedSummary || isPartiallySelectedDetailsElements; 26014 }; 26015 const isCaretAtStartOfSummary = (caretPos, detailsElements) => detailsElements.startSummary.exists(summary => isCaretInTheBeginningOf(caretPos, summary)); 26016 const isCaretAtEndOfSummary = (caretPos, detailsElements) => detailsElements.startSummary.exists(summary => isCaretInTheEndOf(caretPos, summary)); 26017 const isCaretInFirstPositionInBody = (caretPos, detailsElements) => detailsElements.startDetails.exists(details => prevPosition(details, caretPos).forall(pos => detailsElements.startSummary.exists(summary => !summary.contains(caretPos.container()) && summary.contains(pos.container())))); 26018 const isCaretInLastPositionInBody = (root, caretPos, detailsElements) => detailsElements.startDetails.exists(details => nextPosition(root, caretPos).forall(pos => !details.contains(pos.container()))); 26019 const isInDetailsElement = (dom, pos) => isNonNullable(dom.getParent(pos.container(), 'details')); 26020 const moveCaretToDetailsPos = (editor, pos) => { 26021 const details = editor.dom.getParent(pos.container(), 'details'); 26022 if (details && !details.open) { 26023 const summary = editor.dom.select('summary', details)[0]; 26024 if (summary) { 26025 lastPositionIn(summary).each(pos => setCaretToPosition(editor, pos)); 26026 } 26027 } else { 26028 setCaretToPosition(editor, pos); 26029 } 26030 }; 26031 const preventDeleteIntoDetails = (editor, forward) => { 26032 const {dom, selection} = editor; 26033 const root = editor.getBody(); 26034 if (editor.selection.isCollapsed()) { 26035 const caretPos = CaretPosition.fromRangeStart(selection.getRng()); 26036 const parentBlock = dom.getParent(caretPos.container(), dom.isBlock); 26037 if (parentBlock && dom.isEmpty(parentBlock)) { 26038 if (isNull(parentBlock.nextSibling)) { 26039 const pos = prevPosition(root, caretPos).filter(pos => isInDetailsElement(dom, pos)); 26040 if (pos.isSome()) { 26041 pos.each(pos => { 26042 if (!forward) { 26043 moveCaretToDetailsPos(editor, pos); 26044 } 26045 }); 26046 return true; 26047 } 26048 } else if (isNull(parentBlock.previousSibling)) { 26049 const pos = nextPosition(root, caretPos).filter(pos => isInDetailsElement(dom, pos)); 26050 if (pos) { 26051 return true; 26052 } 26053 } 26054 } 26055 return navigate(forward, root, caretPos).fold(never, pos => { 26056 if (isInDetailsElement(dom, pos)) { 26057 if (parentBlock && dom.isEmpty(parentBlock)) { 26058 editor.dom.remove(parentBlock); 26059 } 26060 if (!forward) { 26061 moveCaretToDetailsPos(editor, pos); 26062 } 26063 return true; 26064 } else { 26065 return false; 26066 } 26067 }); 26068 } else { 26069 return false; 26070 } 26071 }; 26072 const preventDeleteSummaryAction = (editor, detailElements, e) => { 26073 const selection = editor.selection; 26074 const node = selection.getNode(); 26075 const rng = selection.getRng(); 26076 const isBackspace = e.keyCode === VK.BACKSPACE; 26077 const isDelete = e.keyCode === VK.DELETE; 26078 const isCollapsed = editor.selection.isCollapsed(); 26079 const caretPos = CaretPosition.fromRangeStart(rng); 26080 const root = editor.getBody(); 26081 if (!isCollapsed && isPartialDelete(rng, detailElements)) { 26082 return true; 26083 } else if (isCollapsed && isBackspace && isCaretAtStartOfSummary(caretPos, detailElements)) { 26084 return true; 26085 } else if (isCollapsed && isDelete && isCaretAtEndOfSummary(caretPos, detailElements)) { 26086 return true; 26087 } else if (isCollapsed && isBackspace && isCaretInFirstPositionInBody(caretPos, detailElements)) { 26088 return true; 26089 } else if (isCollapsed && isDelete && isCaretInLastPositionInBody(root, caretPos, detailElements)) { 26090 return true; 26091 } else if (isSafari && isSummary(node)) { 26092 if (!isCollapsed && isEntireNodeSelected(rng, node) || willDeleteLastPositionInElement(isDelete, caretPos, node)) { 26093 emptyNodeContents(node); 26094 } else { 26095 editor.undoManager.transact(() => { 26096 const sel = selection.getSel(); 26097 let {anchorNode, anchorOffset, focusNode, focusOffset} = sel !== null && sel !== void 0 ? sel : {}; 26098 const applySelection = () => { 26099 if (isNonNullable(anchorNode) && isNonNullable(anchorOffset) && isNonNullable(focusNode) && isNonNullable(focusOffset)) { 26100 sel === null || sel === void 0 ? void 0 : sel.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset); 26101 } 26102 }; 26103 const updateSelection = () => { 26104 anchorNode = sel === null || sel === void 0 ? void 0 : sel.anchorNode; 26105 anchorOffset = sel === null || sel === void 0 ? void 0 : sel.anchorOffset; 26106 focusNode = sel === null || sel === void 0 ? void 0 : sel.focusNode; 26107 focusOffset = sel === null || sel === void 0 ? void 0 : sel.focusOffset; 26108 }; 26109 const appendAllChildNodes = (from, to) => { 26110 each$e(from.childNodes, child => { 26111 if (isNode(child)) { 26112 to.appendChild(child); 26113 } 26114 }); 26115 }; 26116 const container = editor.dom.create('span', { 'data-mce-bogus': 'all' }); 26117 appendAllChildNodes(node, container); 26118 node.appendChild(container); 26119 applySelection(); 26120 if (isCollapsed && (isMacOSOriOS && (e.altKey || isBackspace && e.metaKey) || !isMacOSOriOS && e.ctrlKey)) { 26121 sel === null || sel === void 0 ? void 0 : sel.modify('extend', isBackspace ? 'left' : 'right', e.metaKey ? 'line' : 'word'); 26122 } 26123 if (!selection.isCollapsed() && isEntireNodeSelected(selection.getRng(), container)) { 26124 emptyNodeContents(node); 26125 } else { 26126 editor.execCommand(isBackspace ? 'Delete' : 'ForwardDelete'); 26127 updateSelection(); 26128 appendAllChildNodes(container, node); 26129 applySelection(); 26130 } 26131 editor.dom.remove(container); 26132 }); 26133 } 26134 return true; 26135 } 26136 return false; 26137 }; 26138 const preventDeletingSummary = editor => { 26139 editor.on('keydown', e => { 26140 if (e.keyCode === VK.BACKSPACE || e.keyCode === VK.DELETE) { 26141 getDetailsElements(editor.dom, editor.selection.getRng()).fold(() => { 26142 if (preventDeleteIntoDetails(editor, e.keyCode === VK.DELETE)) { 26143 e.preventDefault(); 26144 } 26145 }, detailsElements => { 26146 if (preventDeleteSummaryAction(editor, detailsElements, e)) { 26147 e.preventDefault(); 26148 } 26149 }); 26150 } 26151 }); 26152 }; 26153 const setup$6 = editor => { 26154 preventSummaryToggle(editor); 26155 filterDetails(editor); 26156 preventDeletingSummary(editor); 26157 }; 26158 26159 const isBr = isBr$6; 26160 const isText = isText$a; 26161 const isContentEditableFalse$2 = elm => isContentEditableFalse$b(elm.dom); 26162 const isContentEditableTrue = elm => isContentEditableTrue$3(elm.dom); 26163 const isRoot = rootNode => elm => eq(SugarElement.fromDom(rootNode), elm); 26164 const getClosestScope = (node, rootNode) => closest$4(SugarElement.fromDom(node), elm => isContentEditableTrue(elm) || isBlock$2(elm), isRoot(rootNode)).getOr(SugarElement.fromDom(rootNode)).dom; 26165 const getClosestCef = (node, rootNode) => closest$4(SugarElement.fromDom(node), isContentEditableFalse$2, isRoot(rootNode)); 26166 const findEdgeCaretCandidate = (startNode, scope, forward) => { 26167 const walker = new DomTreeWalker(startNode, scope); 26168 const next = forward ? walker.next.bind(walker) : walker.prev.bind(walker); 26169 let result = startNode; 26170 for (let current = forward ? startNode : next(); current && !isBr(current); current = next()) { 26171 if (isCaretCandidate$3(current)) { 26172 result = current; 26173 } 26174 } 26175 return result; 26176 }; 26177 const findClosestBlockRange = (startRng, rootNode) => { 26178 const startPos = CaretPosition.fromRangeStart(startRng); 26179 const clickNode = startPos.getNode(); 26180 const scope = getClosestScope(clickNode, rootNode); 26181 const startNode = findEdgeCaretCandidate(clickNode, scope, false); 26182 const endNode = findEdgeCaretCandidate(clickNode, scope, true); 26183 const rng = document.createRange(); 26184 getClosestCef(startNode, scope).fold(() => { 26185 if (isText(startNode)) { 26186 rng.setStart(startNode, 0); 26187 } else { 26188 rng.setStartBefore(startNode); 26189 } 26190 }, cef => rng.setStartBefore(cef.dom)); 26191 getClosestCef(endNode, scope).fold(() => { 26192 if (isText(endNode)) { 26193 rng.setEnd(endNode, endNode.data.length); 26194 } else { 26195 rng.setEndAfter(endNode); 26196 } 26197 }, cef => rng.setEndAfter(cef.dom)); 26198 return rng; 26199 }; 26200 const onTripleClickSelect = editor => { 26201 const rng = findClosestBlockRange(editor.selection.getRng(), editor.getBody()); 26202 editor.selection.setRng(normalize(rng)); 26203 }; 26204 const setup$5 = editor => { 26205 editor.on('mousedown', e => { 26206 if (e.detail >= 3) { 26207 e.preventDefault(); 26208 onTripleClickSelect(editor); 26209 } 26210 }); 26211 }; 26212 26213 var FakeCaretPosition; 26214 (function (FakeCaretPosition) { 26215 FakeCaretPosition['Before'] = 'before'; 26216 FakeCaretPosition['After'] = 'after'; 26217 }(FakeCaretPosition || (FakeCaretPosition = {}))); 26218 const distanceToRectLeft = (clientRect, clientX) => Math.abs(clientRect.left - clientX); 26219 const distanceToRectRight = (clientRect, clientX) => Math.abs(clientRect.right - clientX); 26220 const isInsideY = (clientY, clientRect) => clientY >= clientRect.top && clientY <= clientRect.bottom; 26221 const collidesY = (r1, r2) => r1.top < r2.bottom && r1.bottom > r2.top; 26222 const isOverlapping = (r1, r2) => { 26223 const overlap = overlapY(r1, r2) / Math.min(r1.height, r2.height); 26224 return collidesY(r1, r2) && overlap > 0.5; 26225 }; 26226 const splitRectsPerAxis = (rects, y) => { 26227 const intersectingRects = filter$5(rects, rect => isInsideY(y, rect)); 26228 return boundingClientRectFromRects(intersectingRects).fold(() => [ 26229 [], 26230 rects 26231 ], boundingRect => { 26232 const { 26233 pass: horizontal, 26234 fail: vertical 26235 } = partition$2(rects, rect => isOverlapping(rect, boundingRect)); 26236 return [ 26237 horizontal, 26238 vertical 26239 ]; 26240 }); 26241 }; 26242 const clientInfo = (rect, clientX) => { 26243 return { 26244 node: rect.node, 26245 position: distanceToRectLeft(rect, clientX) < distanceToRectRight(rect, clientX) ? FakeCaretPosition.Before : FakeCaretPosition.After 26246 }; 26247 }; 26248 const horizontalDistance = (rect, x, _y) => x > rect.left && x < rect.right ? 0 : Math.min(Math.abs(rect.left - x), Math.abs(rect.right - x)); 26249 const closestChildCaretCandidateNodeRect = (children, clientX, clientY) => { 26250 const caretCandidateRect = rect => { 26251 if (isCaretCandidate$3(rect.node)) { 26252 return Optional.some(rect); 26253 } else if (isElement$6(rect.node)) { 26254 return closestChildCaretCandidateNodeRect(from(rect.node.childNodes), clientX, clientY); 26255 } else { 26256 return Optional.none(); 26257 } 26258 }; 26259 const getClosestTextNode = (rects, distance) => { 26260 if (rects.length >= 2) { 26261 const r1 = caretCandidateRect(rects[0]).getOr(rects[0]); 26262 const r2 = caretCandidateRect(rects[1]).getOr(rects[1]); 26263 const deltaDistance = Math.abs(distance(r1, clientX, clientY) - distance(r2, clientX, clientY)); 26264 if (deltaDistance < 2) { 26265 if (isText$a(r1.node)) { 26266 return Optional.some(r1); 26267 } else if (isText$a(r2.node)) { 26268 return Optional.some(r2); 26269 } 26270 } 26271 } 26272 return Optional.none(); 26273 }; 26274 const findClosestCaretCandidateNodeRect = (rects, distance) => { 26275 const sortedRects = sort(rects, (r1, r2) => distance(r1, clientX, clientY) - distance(r2, clientX, clientY)); 26276 return getClosestTextNode(sortedRects, distance).orThunk(() => findMap(sortedRects, caretCandidateRect)); 26277 }; 26278 const [horizontalRects, verticalRects] = splitRectsPerAxis(getClientRects(children), clientY); 26279 const { 26280 pass: above, 26281 fail: below 26282 } = partition$2(verticalRects, rect => rect.top < clientY); 26283 return findClosestCaretCandidateNodeRect(horizontalRects, horizontalDistance).orThunk(() => findClosestCaretCandidateNodeRect(below, distanceToRectEdgeFromXY)).orThunk(() => findClosestCaretCandidateNodeRect(above, distanceToRectEdgeFromXY)); 26284 }; 26285 const traverseUp = (rootElm, scope, clientX, clientY) => { 26286 const helper = (scope, prevScope) => { 26287 const isDragGhostContainer = node => isElement$6(node) && node.classList.contains('mce-drag-container'); 26288 const childNodesWithoutGhost = filter$5(scope.dom.childNodes, not(isDragGhostContainer)); 26289 return prevScope.fold(() => closestChildCaretCandidateNodeRect(childNodesWithoutGhost, clientX, clientY), prevScope => { 26290 const uncheckedChildren = filter$5(childNodesWithoutGhost, node => node !== prevScope.dom); 26291 return closestChildCaretCandidateNodeRect(uncheckedChildren, clientX, clientY); 26292 }).orThunk(() => { 26293 const parent = eq(scope, rootElm) ? Optional.none() : parentElement(scope); 26294 return parent.bind(newScope => helper(newScope, Optional.some(scope))); 26295 }); 26296 }; 26297 return helper(scope, Optional.none()); 26298 }; 26299 const closestCaretCandidateNodeRect = (root, clientX, clientY) => { 26300 const rootElm = SugarElement.fromDom(root); 26301 const ownerDoc = documentOrOwner(rootElm); 26302 const elementAtPoint = SugarElement.fromPoint(ownerDoc, clientX, clientY).filter(elm => contains(rootElm, elm)); 26303 const element = elementAtPoint.getOr(rootElm); 26304 return traverseUp(rootElm, element, clientX, clientY); 26305 }; 26306 const closestFakeCaretCandidate = (root, clientX, clientY) => closestCaretCandidateNodeRect(root, clientX, clientY).filter(rect => isFakeCaretTarget(rect.node)).map(rect => clientInfo(rect, clientX)); 26307 26308 const getAbsolutePosition = elm => { 26309 var _a, _b; 26310 const clientRect = elm.getBoundingClientRect(); 26311 const doc = elm.ownerDocument; 26312 const docElem = doc.documentElement; 26313 const win = doc.defaultView; 26314 return { 26315 top: clientRect.top + ((_a = win === null || win === void 0 ? void 0 : win.scrollY) !== null && _a !== void 0 ? _a : 0) - docElem.clientTop, 26316 left: clientRect.left + ((_b = win === null || win === void 0 ? void 0 : win.scrollX) !== null && _b !== void 0 ? _b : 0) - docElem.clientLeft 26317 }; 26318 }; 26319 const getBodyPosition = editor => editor.inline ? getAbsolutePosition(editor.getBody()) : { 26320 left: 0, 26321 top: 0 26322 }; 26323 const getScrollPosition = editor => { 26324 const body = editor.getBody(); 26325 return editor.inline ? { 26326 left: body.scrollLeft, 26327 top: body.scrollTop 26328 } : { 26329 left: 0, 26330 top: 0 26331 }; 26332 }; 26333 const getBodyScroll = editor => { 26334 const body = editor.getBody(), docElm = editor.getDoc().documentElement; 26335 const inlineScroll = { 26336 left: body.scrollLeft, 26337 top: body.scrollTop 26338 }; 26339 const iframeScroll = { 26340 left: body.scrollLeft || docElm.scrollLeft, 26341 top: body.scrollTop || docElm.scrollTop 26342 }; 26343 return editor.inline ? inlineScroll : iframeScroll; 26344 }; 26345 const getMousePosition = (editor, event) => { 26346 if (event.target.ownerDocument !== editor.getDoc()) { 26347 const iframePosition = getAbsolutePosition(editor.getContentAreaContainer()); 26348 const scrollPosition = getBodyScroll(editor); 26349 return { 26350 left: event.pageX - iframePosition.left + scrollPosition.left, 26351 top: event.pageY - iframePosition.top + scrollPosition.top 26352 }; 26353 } 26354 return { 26355 left: event.pageX, 26356 top: event.pageY 26357 }; 26358 }; 26359 const calculatePosition = (bodyPosition, scrollPosition, mousePosition) => ({ 26360 pageX: mousePosition.left - bodyPosition.left + scrollPosition.left, 26361 pageY: mousePosition.top - bodyPosition.top + scrollPosition.top 26362 }); 26363 const calc = (editor, event) => calculatePosition(getBodyPosition(editor), getScrollPosition(editor), getMousePosition(editor, event)); 26364 26365 const getTargetProps = target => ({ 26366 target, 26367 srcElement: target 26368 }); 26369 const makeDndEventFromMouseEvent = (type, mouseEvent, target, dataTransfer) => ({ 26370 ...mouseEvent, 26371 dataTransfer, 26372 type, 26373 ...getTargetProps(target) 26374 }); 26375 const makeDndEvent = (type, target, dataTransfer) => { 26376 const fail = die('Function not supported on simulated event.'); 26377 const event = { 26378 bubbles: true, 26379 cancelBubble: false, 26380 cancelable: true, 26381 composed: false, 26382 currentTarget: null, 26383 defaultPrevented: false, 26384 eventPhase: 0, 26385 isTrusted: true, 26386 returnValue: false, 26387 timeStamp: 0, 26388 type, 26389 composedPath: fail, 26390 initEvent: fail, 26391 preventDefault: noop, 26392 stopImmediatePropagation: noop, 26393 stopPropagation: noop, 26394 AT_TARGET: window.Event.AT_TARGET, 26395 BUBBLING_PHASE: window.Event.BUBBLING_PHASE, 26396 CAPTURING_PHASE: window.Event.CAPTURING_PHASE, 26397 NONE: window.Event.NONE, 26398 altKey: false, 26399 button: 0, 26400 buttons: 0, 26401 clientX: 0, 26402 clientY: 0, 26403 ctrlKey: false, 26404 metaKey: false, 26405 movementX: 0, 26406 movementY: 0, 26407 offsetX: 0, 26408 offsetY: 0, 26409 pageX: 0, 26410 pageY: 0, 26411 relatedTarget: null, 26412 screenX: 0, 26413 screenY: 0, 26414 shiftKey: false, 26415 x: 0, 26416 y: 0, 26417 detail: 0, 26418 view: null, 26419 which: 0, 26420 initUIEvent: fail, 26421 initMouseEvent: fail, 26422 getModifierState: fail, 26423 dataTransfer, 26424 ...getTargetProps(target) 26425 }; 26426 return event; 26427 }; 26428 const makeDataTransferCopyForDragEvent = (dataTransfer, eventType) => { 26429 const copy = cloneDataTransfer(dataTransfer); 26430 if (eventType === 'dragstart') { 26431 setDragstartEvent(copy); 26432 setReadWriteMode(copy); 26433 } else if (eventType === 'drop') { 26434 setDropEvent(copy); 26435 setReadOnlyMode(copy); 26436 } else { 26437 setDragendEvent(copy); 26438 setProtectedMode(copy); 26439 } 26440 return copy; 26441 }; 26442 const makeDragEvent = (type, target, dataTransfer, mouseEvent) => { 26443 const dataTransferForDispatch = makeDataTransferCopyForDragEvent(dataTransfer, type); 26444 return isUndefined(mouseEvent) ? makeDndEvent(type, target, dataTransferForDispatch) : makeDndEventFromMouseEvent(type, mouseEvent, target, dataTransferForDispatch); 26445 }; 26446 26447 const scrollPixelsPerInterval = 32; 26448 const scrollIntervalValue = 100; 26449 const mouseRangeToTriggerScrollInsideEditor = 8; 26450 const mouseRangeToTriggerScrollOutsideEditor = 16; 26451 const isContentEditableFalse$1 = isContentEditableFalse$b; 26452 const isContentEditable = or(isContentEditableFalse$1, isContentEditableTrue$3); 26453 const isDraggable = (dom, rootElm, elm) => isContentEditableFalse$1(elm) && elm !== rootElm && dom.isEditable(elm.parentElement); 26454 const isValidDropTarget = (editor, targetElement, dragElement) => { 26455 if (isNullable(targetElement)) { 26456 return false; 26457 } else if (targetElement === dragElement || editor.dom.isChildOf(targetElement, dragElement)) { 26458 return false; 26459 } else { 26460 return editor.dom.isEditable(targetElement); 26461 } 26462 }; 26463 const createGhost = (editor, elm, width, height) => { 26464 const dom = editor.dom; 26465 const clonedElm = elm.cloneNode(true); 26466 dom.setStyles(clonedElm, { 26467 width, 26468 height 26469 }); 26470 dom.setAttrib(clonedElm, 'data-mce-selected', null); 26471 const ghostElm = dom.create('div', { 26472 'class': 'mce-drag-container', 26473 'data-mce-bogus': 'all', 26474 'unselectable': 'on', 26475 'contenteditable': 'false' 26476 }); 26477 dom.setStyles(ghostElm, { 26478 position: 'absolute', 26479 opacity: 0.5, 26480 overflow: 'hidden', 26481 border: 0, 26482 padding: 0, 26483 margin: 0, 26484 width, 26485 height 26486 }); 26487 dom.setStyles(clonedElm, { 26488 margin: 0, 26489 boxSizing: 'border-box' 26490 }); 26491 ghostElm.appendChild(clonedElm); 26492 return ghostElm; 26493 }; 26494 const appendGhostToBody = (ghostElm, bodyElm) => { 26495 if (ghostElm.parentNode !== bodyElm) { 26496 bodyElm.appendChild(ghostElm); 26497 } 26498 }; 26499 const scrollEditor = (direction, amount) => win => () => { 26500 const current = direction === 'left' ? win.scrollX : win.scrollY; 26501 win.scroll({ 26502 [direction]: current + amount, 26503 behavior: 'smooth' 26504 }); 26505 }; 26506 const scrollLeft = scrollEditor('left', -scrollPixelsPerInterval); 26507 const scrollRight = scrollEditor('left', scrollPixelsPerInterval); 26508 const scrollUp = scrollEditor('top', -scrollPixelsPerInterval); 26509 const scrollDown = scrollEditor('top', scrollPixelsPerInterval); 26510 const moveGhost = (ghostElm, position, width, height, maxX, maxY, mouseY, mouseX, contentAreaContainer, win, state, mouseEventOriginatedFromWithinTheEditor) => { 26511 let overflowX = 0, overflowY = 0; 26512 ghostElm.style.left = position.pageX + 'px'; 26513 ghostElm.style.top = position.pageY + 'px'; 26514 if (position.pageX + width > maxX) { 26515 overflowX = position.pageX + width - maxX; 26516 } 26517 if (position.pageY + height > maxY) { 26518 overflowY = position.pageY + height - maxY; 26519 } 26520 ghostElm.style.width = width - overflowX + 'px'; 26521 ghostElm.style.height = height - overflowY + 'px'; 26522 const clientHeight = contentAreaContainer.clientHeight; 26523 const clientWidth = contentAreaContainer.clientWidth; 26524 const outerMouseY = mouseY + contentAreaContainer.getBoundingClientRect().top; 26525 const outerMouseX = mouseX + contentAreaContainer.getBoundingClientRect().left; 26526 state.on(state => { 26527 state.intervalId.clear(); 26528 if (state.dragging && mouseEventOriginatedFromWithinTheEditor) { 26529 if (mouseY + mouseRangeToTriggerScrollInsideEditor >= clientHeight) { 26530 state.intervalId.set(scrollDown(win)); 26531 } else if (mouseY - mouseRangeToTriggerScrollInsideEditor <= 0) { 26532 state.intervalId.set(scrollUp(win)); 26533 } else if (mouseX + mouseRangeToTriggerScrollInsideEditor >= clientWidth) { 26534 state.intervalId.set(scrollRight(win)); 26535 } else if (mouseX - mouseRangeToTriggerScrollInsideEditor <= 0) { 26536 state.intervalId.set(scrollLeft(win)); 26537 } else if (outerMouseY + mouseRangeToTriggerScrollOutsideEditor >= window.innerHeight) { 26538 state.intervalId.set(scrollDown(window)); 26539 } else if (outerMouseY - mouseRangeToTriggerScrollOutsideEditor <= 0) { 26540 state.intervalId.set(scrollUp(window)); 26541 } else if (outerMouseX + mouseRangeToTriggerScrollOutsideEditor >= window.innerWidth) { 26542 state.intervalId.set(scrollRight(window)); 26543 } else if (outerMouseX - mouseRangeToTriggerScrollOutsideEditor <= 0) { 26544 state.intervalId.set(scrollLeft(window)); 26545 } 26546 } 26547 }); 26548 }; 26549 const removeElement = elm => { 26550 if (elm && elm.parentNode) { 26551 elm.parentNode.removeChild(elm); 26552 } 26553 }; 26554 const removeElementWithPadding = (dom, elm) => { 26555 const parentBlock = dom.getParent(elm.parentNode, dom.isBlock); 26556 removeElement(elm); 26557 if (parentBlock && parentBlock !== dom.getRoot() && dom.isEmpty(parentBlock)) { 26558 fillWithPaddingBr(SugarElement.fromDom(parentBlock)); 26559 } 26560 }; 26561 const isLeftMouseButtonPressed = e => e.button === 0; 26562 const applyRelPos = (state, position) => ({ 26563 pageX: position.pageX - state.relX, 26564 pageY: position.pageY + 5 26565 }); 26566 const start = (state, editor) => e => { 26567 if (isLeftMouseButtonPressed(e)) { 26568 const ceElm = find$2(editor.dom.getParents(e.target), isContentEditable).getOr(null); 26569 if (isNonNullable(ceElm) && isDraggable(editor.dom, editor.getBody(), ceElm)) { 26570 const elmPos = editor.dom.getPos(ceElm); 26571 const bodyElm = editor.getBody(); 26572 const docElm = editor.getDoc().documentElement; 26573 state.set({ 26574 element: ceElm, 26575 dataTransfer: createDataTransfer(), 26576 dragging: false, 26577 screenX: e.screenX, 26578 screenY: e.screenY, 26579 maxX: (editor.inline ? bodyElm.scrollWidth : docElm.offsetWidth) - 2, 26580 maxY: (editor.inline ? bodyElm.scrollHeight : docElm.offsetHeight) - 2, 26581 relX: e.pageX - elmPos.x, 26582 relY: e.pageY - elmPos.y, 26583 width: ceElm.offsetWidth, 26584 height: ceElm.offsetHeight, 26585 ghost: createGhost(editor, ceElm, ceElm.offsetWidth, ceElm.offsetHeight), 26586 intervalId: repeatable(scrollIntervalValue) 26587 }); 26588 } 26589 } 26590 }; 26591 const placeCaretAt = (editor, clientX, clientY) => { 26592 editor._selectionOverrides.hideFakeCaret(); 26593 closestFakeCaretCandidate(editor.getBody(), clientX, clientY).fold(() => editor.selection.placeCaretAt(clientX, clientY), caretInfo => { 26594 const range = editor._selectionOverrides.showCaret(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false); 26595 if (range) { 26596 editor.selection.setRng(range); 26597 } else { 26598 editor.selection.placeCaretAt(clientX, clientY); 26599 } 26600 }); 26601 }; 26602 const dispatchDragEvent = (editor, type, target, dataTransfer, mouseEvent) => { 26603 if (type === 'dragstart') { 26604 setHtmlData(dataTransfer, editor.dom.getOuterHTML(target)); 26605 } 26606 const event = makeDragEvent(type, target, dataTransfer, mouseEvent); 26607 const args = editor.dispatch(type, event); 26608 return args; 26609 }; 26610 const move = (state, editor) => { 26611 const throttledPlaceCaretAt = first$1((clientX, clientY) => placeCaretAt(editor, clientX, clientY), 0); 26612 editor.on('remove', throttledPlaceCaretAt.cancel); 26613 const state_ = state; 26614 return e => state.on(state => { 26615 const movement = Math.max(Math.abs(e.screenX - state.screenX), Math.abs(e.screenY - state.screenY)); 26616 if (!state.dragging && movement > 10) { 26617 const args = dispatchDragEvent(editor, 'dragstart', state.element, state.dataTransfer, e); 26618 if (isNonNullable(args.dataTransfer)) { 26619 state.dataTransfer = args.dataTransfer; 26620 } 26621 if (args.isDefaultPrevented()) { 26622 return; 26623 } 26624 state.dragging = true; 26625 editor.focus(); 26626 } 26627 if (state.dragging) { 26628 const mouseEventOriginatedFromWithinTheEditor = e.currentTarget === editor.getDoc().documentElement; 26629 const targetPos = applyRelPos(state, calc(editor, e)); 26630 appendGhostToBody(state.ghost, editor.getBody()); 26631 moveGhost(state.ghost, targetPos, state.width, state.height, state.maxX, state.maxY, e.clientY, e.clientX, editor.getContentAreaContainer(), editor.getWin(), state_, mouseEventOriginatedFromWithinTheEditor); 26632 throttledPlaceCaretAt.throttle(e.clientX, e.clientY); 26633 } 26634 }); 26635 }; 26636 const getRawTarget = selection => { 26637 const sel = selection.getSel(); 26638 if (isNonNullable(sel)) { 26639 const rng = sel.getRangeAt(0); 26640 const startContainer = rng.startContainer; 26641 return isText$a(startContainer) ? startContainer.parentNode : startContainer; 26642 } else { 26643 return null; 26644 } 26645 }; 26646 const drop = (state, editor) => e => { 26647 state.on(state => { 26648 var _a; 26649 state.intervalId.clear(); 26650 if (state.dragging) { 26651 if (isValidDropTarget(editor, getRawTarget(editor.selection), state.element)) { 26652 const dropTarget = (_a = editor.getDoc().elementFromPoint(e.clientX, e.clientY)) !== null && _a !== void 0 ? _a : editor.getBody(); 26653 const args = dispatchDragEvent(editor, 'drop', dropTarget, state.dataTransfer, e); 26654 if (!args.isDefaultPrevented()) { 26655 editor.undoManager.transact(() => { 26656 removeElementWithPadding(editor.dom, state.element); 26657 getHtmlData(state.dataTransfer).each(content => editor.insertContent(content)); 26658 editor._selectionOverrides.hideFakeCaret(); 26659 }); 26660 } 26661 } 26662 dispatchDragEvent(editor, 'dragend', editor.getBody(), state.dataTransfer, e); 26663 } 26664 }); 26665 removeDragState(state); 26666 }; 26667 const stopDragging = (state, editor, e) => { 26668 state.on(state => { 26669 state.intervalId.clear(); 26670 if (state.dragging) { 26671 e.fold(() => dispatchDragEvent(editor, 'dragend', state.element, state.dataTransfer), mouseEvent => dispatchDragEvent(editor, 'dragend', state.element, state.dataTransfer, mouseEvent)); 26672 } 26673 }); 26674 removeDragState(state); 26675 }; 26676 const stop = (state, editor) => e => stopDragging(state, editor, Optional.some(e)); 26677 const removeDragState = state => { 26678 state.on(state => { 26679 state.intervalId.clear(); 26680 removeElement(state.ghost); 26681 }); 26682 state.clear(); 26683 }; 26684 const bindFakeDragEvents = editor => { 26685 const state = value$2(); 26686 const pageDom = DOMUtils.DOM; 26687 const rootDocument = document; 26688 const dragStartHandler = start(state, editor); 26689 const dragHandler = move(state, editor); 26690 const dropHandler = drop(state, editor); 26691 const dragEndHandler = stop(state, editor); 26692 editor.on('mousedown', dragStartHandler); 26693 editor.on('mousemove', dragHandler); 26694 editor.on('mouseup', dropHandler); 26695 pageDom.bind(rootDocument, 'mousemove', dragHandler); 26696 pageDom.bind(rootDocument, 'mouseup', dragEndHandler); 26697 editor.on('remove', () => { 26698 pageDom.unbind(rootDocument, 'mousemove', dragHandler); 26699 pageDom.unbind(rootDocument, 'mouseup', dragEndHandler); 26700 }); 26701 editor.on('keydown', e => { 26702 if (e.keyCode === VK.ESC) { 26703 stopDragging(state, editor, Optional.none()); 26704 } 26705 }); 26706 }; 26707 const blockUnsupportedFileDrop = editor => { 26708 const preventFileDrop = e => { 26709 if (!e.isDefaultPrevented()) { 26710 const dataTransfer = e.dataTransfer; 26711 if (dataTransfer && (contains$2(dataTransfer.types, 'Files') || dataTransfer.files.length > 0)) { 26712 e.preventDefault(); 26713 if (e.type === 'drop') { 26714 displayError(editor, 'Dropped file type is not supported'); 26715 } 26716 } 26717 } 26718 }; 26719 const preventFileDropIfUIElement = e => { 26720 if (isUIElement(editor, e.target)) { 26721 preventFileDrop(e); 26722 } 26723 }; 26724 const setup = () => { 26725 const pageDom = DOMUtils.DOM; 26726 const dom = editor.dom; 26727 const doc = document; 26728 const editorRoot = editor.inline ? editor.getBody() : editor.getDoc(); 26729 const eventNames = [ 26730 'drop', 26731 'dragover' 26732 ]; 26733 each$e(eventNames, name => { 26734 pageDom.bind(doc, name, preventFileDropIfUIElement); 26735 dom.bind(editorRoot, name, preventFileDrop); 26736 }); 26737 editor.on('remove', () => { 26738 each$e(eventNames, name => { 26739 pageDom.unbind(doc, name, preventFileDropIfUIElement); 26740 dom.unbind(editorRoot, name, preventFileDrop); 26741 }); 26742 }); 26743 }; 26744 editor.on('init', () => { 26745 Delay.setEditorTimeout(editor, setup, 0); 26746 }); 26747 }; 26748 const init$2 = editor => { 26749 bindFakeDragEvents(editor); 26750 if (shouldBlockUnsupportedDrop(editor)) { 26751 blockUnsupportedFileDrop(editor); 26752 } 26753 }; 26754 26755 const setup$4 = editor => { 26756 const renderFocusCaret = first$1(() => { 26757 if (!editor.removed && editor.getBody().contains(document.activeElement)) { 26758 const rng = editor.selection.getRng(); 26759 if (rng.collapsed) { 26760 const caretRange = renderRangeCaret(editor, rng, false); 26761 editor.selection.setRng(caretRange); 26762 } 26763 } 26764 }, 0); 26765 editor.on('focus', () => { 26766 renderFocusCaret.throttle(); 26767 }); 26768 editor.on('blur', () => { 26769 renderFocusCaret.cancel(); 26770 }); 26771 }; 26772 26773 const setup$3 = editor => { 26774 editor.on('init', () => { 26775 editor.on('focusin', e => { 26776 const target = e.target; 26777 if (isMedia$2(target)) { 26778 const ceRoot = getContentEditableRoot$1(editor.getBody(), target); 26779 const node = isContentEditableFalse$b(ceRoot) ? ceRoot : target; 26780 if (editor.selection.getNode() !== node) { 26781 selectNode(editor, node).each(rng => editor.selection.setRng(rng)); 26782 } 26783 } 26784 }); 26785 }); 26786 }; 26787 26788 const isContentEditableFalse = isContentEditableFalse$b; 26789 const getContentEditableRoot = (editor, node) => getContentEditableRoot$1(editor.getBody(), node); 26790 const SelectionOverrides = editor => { 26791 const selection = editor.selection, dom = editor.dom; 26792 const rootNode = editor.getBody(); 26793 const fakeCaret = FakeCaret(editor, rootNode, dom.isBlock, () => hasFocus(editor)); 26794 const realSelectionId = 'sel-' + dom.uniqueId(); 26795 const elementSelectionAttr = 'data-mce-selected'; 26796 let selectedElement; 26797 const isFakeSelectionElement = node => isNonNullable(node) && dom.hasClass(node, 'mce-offscreen-selection'); 26798 const isFakeSelectionTargetElement = node => node !== rootNode && (isContentEditableFalse(node) || isMedia$2(node)) && dom.isChildOf(node, rootNode) && dom.isEditable(node.parentNode); 26799 const setRange = range => { 26800 if (range) { 26801 selection.setRng(range); 26802 } 26803 }; 26804 const showCaret = (direction, node, before, scrollIntoView = true) => { 26805 const e = editor.dispatch('ShowCaret', { 26806 target: node, 26807 direction, 26808 before 26809 }); 26810 if (e.isDefaultPrevented()) { 26811 return null; 26812 } 26813 if (scrollIntoView) { 26814 selection.scrollIntoView(node, direction === -1); 26815 } 26816 return fakeCaret.show(before, node); 26817 }; 26818 const showBlockCaretContainer = blockCaretContainer => { 26819 if (blockCaretContainer.hasAttribute('data-mce-caret')) { 26820 showCaretContainerBlock(blockCaretContainer); 26821 selection.scrollIntoView(blockCaretContainer); 26822 } 26823 }; 26824 const registerEvents = () => { 26825 editor.on('click', e => { 26826 if (!dom.isEditable(e.target)) { 26827 e.preventDefault(); 26828 editor.focus(); 26829 } 26830 }); 26831 editor.on('blur NewBlock', removeElementSelection); 26832 editor.on('ResizeWindow FullscreenStateChanged', fakeCaret.reposition); 26833 editor.on('tap', e => { 26834 const targetElm = e.target; 26835 const contentEditableRoot = getContentEditableRoot(editor, targetElm); 26836 if (isContentEditableFalse(contentEditableRoot)) { 26837 e.preventDefault(); 26838 selectNode(editor, contentEditableRoot).each(setElementSelection); 26839 } else if (isFakeSelectionTargetElement(targetElm)) { 26840 selectNode(editor, targetElm).each(setElementSelection); 26841 } 26842 }, true); 26843 editor.on('mousedown', e => { 26844 const targetElm = e.target; 26845 if (targetElm !== rootNode && targetElm.nodeName !== 'HTML' && !dom.isChildOf(targetElm, rootNode)) { 26846 return; 26847 } 26848 if (!isXYInContentArea(editor, e.clientX, e.clientY)) { 26849 return; 26850 } 26851 removeElementSelection(); 26852 hideFakeCaret(); 26853 const closestContentEditable = getContentEditableRoot(editor, targetElm); 26854 if (isContentEditableFalse(closestContentEditable)) { 26855 e.preventDefault(); 26856 selectNode(editor, closestContentEditable).each(setElementSelection); 26857 } else { 26858 closestFakeCaretCandidate(rootNode, e.clientX, e.clientY).each(caretInfo => { 26859 e.preventDefault(); 26860 const range = showCaret(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false); 26861 setRange(range); 26862 if (isElement$6(closestContentEditable)) { 26863 closestContentEditable.focus(); 26864 } else { 26865 editor.getBody().focus(); 26866 } 26867 }); 26868 } 26869 }); 26870 editor.on('keypress', e => { 26871 if (VK.modifierPressed(e)) { 26872 return; 26873 } 26874 if (isContentEditableFalse(selection.getNode())) { 26875 e.preventDefault(); 26876 } 26877 }); 26878 editor.on('GetSelectionRange', e => { 26879 let rng = e.range; 26880 if (selectedElement) { 26881 if (!selectedElement.parentNode) { 26882 selectedElement = null; 26883 return; 26884 } 26885 rng = rng.cloneRange(); 26886 rng.selectNode(selectedElement); 26887 e.range = rng; 26888 } 26889 }); 26890 editor.on('SetSelectionRange', e => { 26891 e.range = normalizeVoidElementSelection(e.range); 26892 const rng = setElementSelection(e.range, e.forward); 26893 if (rng) { 26894 e.range = rng; 26895 } 26896 }); 26897 const isPasteBin = node => isElement$6(node) && node.id === 'mcepastebin'; 26898 editor.on('AfterSetSelectionRange', e => { 26899 const rng = e.range; 26900 const parent = rng.startContainer.parentElement; 26901 if (!isRangeInCaretContainer(rng) && !isPasteBin(parent)) { 26902 hideFakeCaret(); 26903 } 26904 if (!isFakeSelectionElement(parent)) { 26905 removeElementSelection(); 26906 } 26907 }); 26908 init$2(editor); 26909 setup$4(editor); 26910 setup$3(editor); 26911 }; 26912 const isWithinCaretContainer = node => isCaretContainer$2(node) || startsWithCaretContainer$1(node) || endsWithCaretContainer$1(node); 26913 const isRangeInCaretContainer = rng => isWithinCaretContainer(rng.startContainer) || isWithinCaretContainer(rng.endContainer); 26914 const normalizeVoidElementSelection = rng => { 26915 const voidElements = editor.schema.getVoidElements(); 26916 const newRng = dom.createRng(); 26917 const startContainer = rng.startContainer; 26918 const startOffset = rng.startOffset; 26919 const endContainer = rng.endContainer; 26920 const endOffset = rng.endOffset; 26921 if (has$2(voidElements, startContainer.nodeName.toLowerCase())) { 26922 if (startOffset === 0) { 26923 newRng.setStartBefore(startContainer); 26924 } else { 26925 newRng.setStartAfter(startContainer); 26926 } 26927 } else { 26928 newRng.setStart(startContainer, startOffset); 26929 } 26930 if (has$2(voidElements, endContainer.nodeName.toLowerCase())) { 26931 if (endOffset === 0) { 26932 newRng.setEndBefore(endContainer); 26933 } else { 26934 newRng.setEndAfter(endContainer); 26935 } 26936 } else { 26937 newRng.setEnd(endContainer, endOffset); 26938 } 26939 return newRng; 26940 }; 26941 const setupOffscreenSelection = (node, targetClone) => { 26942 const body = SugarElement.fromDom(editor.getBody()); 26943 const doc = editor.getDoc(); 26944 const realSelectionContainer = descendant(body, '#' + realSelectionId).getOrThunk(() => { 26945 const newContainer = SugarElement.fromHtml('<div data-mce-bogus="all" class="mce-offscreen-selection"></div>', doc); 26946 set$3(newContainer, 'id', realSelectionId); 26947 append$1(body, newContainer); 26948 return newContainer; 26949 }); 26950 const newRange = dom.createRng(); 26951 empty(realSelectionContainer); 26952 append(realSelectionContainer, [ 26953 SugarElement.fromText(nbsp, doc), 26954 SugarElement.fromDom(targetClone), 26955 SugarElement.fromText(nbsp, doc) 26956 ]); 26957 newRange.setStart(realSelectionContainer.dom.firstChild, 1); 26958 newRange.setEnd(realSelectionContainer.dom.lastChild, 0); 26959 setAll(realSelectionContainer, { top: dom.getPos(node, editor.getBody()).y + 'px' }); 26960 focus$1(realSelectionContainer); 26961 const sel = selection.getSel(); 26962 if (sel) { 26963 sel.removeAllRanges(); 26964 sel.addRange(newRange); 26965 } 26966 return newRange; 26967 }; 26968 const selectElement = elm => { 26969 const targetClone = elm.cloneNode(true); 26970 const e = editor.dispatch('ObjectSelected', { 26971 target: elm, 26972 targetClone 26973 }); 26974 if (e.isDefaultPrevented()) { 26975 return null; 26976 } 26977 const range = setupOffscreenSelection(elm, e.targetClone); 26978 const nodeElm = SugarElement.fromDom(elm); 26979 each$e(descendants(SugarElement.fromDom(editor.getBody()), `*[${ elementSelectionAttr }]`), elm => { 26980 if (!eq(nodeElm, elm)) { 26981 remove$a(elm, elementSelectionAttr); 26982 } 26983 }); 26984 if (!dom.getAttrib(elm, elementSelectionAttr)) { 26985 elm.setAttribute(elementSelectionAttr, '1'); 26986 } 26987 selectedElement = elm; 26988 hideFakeCaret(); 26989 return range; 26990 }; 26991 const setElementSelection = (range, forward) => { 26992 if (!range) { 26993 return null; 26994 } 26995 if (range.collapsed) { 26996 if (!isRangeInCaretContainer(range)) { 26997 const dir = forward ? 1 : -1; 26998 const caretPosition = getNormalizedRangeEndPoint(dir, rootNode, range); 26999 const beforeNode = caretPosition.getNode(!forward); 27000 if (isNonNullable(beforeNode)) { 27001 if (isFakeCaretTarget(beforeNode)) { 27002 return showCaret(dir, beforeNode, forward ? !caretPosition.isAtEnd() : false, false); 27003 } 27004 if (isCaretContainerInline(beforeNode) && isContentEditableFalse$b(beforeNode.nextSibling)) { 27005 const rng = dom.createRng(); 27006 rng.setStart(beforeNode, 0); 27007 rng.setEnd(beforeNode, 0); 27008 return rng; 27009 } 27010 } 27011 const afterNode = caretPosition.getNode(forward); 27012 if (isNonNullable(afterNode)) { 27013 if (isFakeCaretTarget(afterNode)) { 27014 return showCaret(dir, afterNode, forward ? false : !caretPosition.isAtEnd(), false); 27015 } 27016 if (isCaretContainerInline(afterNode) && isContentEditableFalse$b(afterNode.previousSibling)) { 27017 const rng = dom.createRng(); 27018 rng.setStart(afterNode, 1); 27019 rng.setEnd(afterNode, 1); 27020 return rng; 27021 } 27022 } 27023 } 27024 return null; 27025 } 27026 let startContainer = range.startContainer; 27027 let startOffset = range.startOffset; 27028 const endOffset = range.endOffset; 27029 if (isText$a(startContainer) && startOffset === 0 && isContentEditableFalse(startContainer.parentNode)) { 27030 startContainer = startContainer.parentNode; 27031 startOffset = dom.nodeIndex(startContainer); 27032 startContainer = startContainer.parentNode; 27033 } 27034 if (!isElement$6(startContainer)) { 27035 return null; 27036 } 27037 if (endOffset === startOffset + 1 && startContainer === range.endContainer) { 27038 const node = startContainer.childNodes[startOffset]; 27039 if (isFakeSelectionTargetElement(node)) { 27040 return selectElement(node); 27041 } 27042 } 27043 return null; 27044 }; 27045 const removeElementSelection = () => { 27046 if (selectedElement) { 27047 selectedElement.removeAttribute(elementSelectionAttr); 27048 } 27049 descendant(SugarElement.fromDom(editor.getBody()), '#' + realSelectionId).each(remove$5); 27050 selectedElement = null; 27051 }; 27052 const destroy = () => { 27053 fakeCaret.destroy(); 27054 selectedElement = null; 27055 }; 27056 const hideFakeCaret = () => { 27057 fakeCaret.hide(); 27058 }; 27059 if (!isRtc(editor)) { 27060 registerEvents(); 27061 } 27062 return { 27063 showCaret, 27064 showBlockCaretContainer, 27065 hideFakeCaret, 27066 destroy 27067 }; 27068 }; 27069 27070 const getNormalizedTextOffset = (container, offset) => { 27071 let normalizedOffset = offset; 27072 for (let node = container.previousSibling; isText$a(node); node = node.previousSibling) { 27073 normalizedOffset += node.data.length; 27074 } 27075 return normalizedOffset; 27076 }; 27077 const generatePath = (dom, root, node, offset, normalized) => { 27078 if (isText$a(node) && (offset < 0 || offset > node.data.length)) { 27079 return []; 27080 } 27081 const p = normalized && isText$a(node) ? [getNormalizedTextOffset(node, offset)] : [offset]; 27082 let current = node; 27083 while (current !== root && current.parentNode) { 27084 p.push(dom.nodeIndex(current, normalized)); 27085 current = current.parentNode; 27086 } 27087 return current === root ? p.reverse() : []; 27088 }; 27089 const generatePathRange = (dom, root, startNode, startOffset, endNode, endOffset, normalized = false) => { 27090 const start = generatePath(dom, root, startNode, startOffset, normalized); 27091 const end = generatePath(dom, root, endNode, endOffset, normalized); 27092 return { 27093 start, 27094 end 27095 }; 27096 }; 27097 const resolvePath = (root, path) => { 27098 const nodePath = path.slice(); 27099 const offset = nodePath.pop(); 27100 if (!isNumber(offset)) { 27101 return Optional.none(); 27102 } else { 27103 const resolvedNode = foldl(nodePath, (optNode, index) => optNode.bind(node => Optional.from(node.childNodes[index])), Optional.some(root)); 27104 return resolvedNode.bind(node => { 27105 if (isText$a(node) && (offset < 0 || offset > node.data.length)) { 27106 return Optional.none(); 27107 } else { 27108 return Optional.some({ 27109 node, 27110 offset 27111 }); 27112 } 27113 }); 27114 } 27115 }; 27116 const resolvePathRange = (root, range) => resolvePath(root, range.start).bind(({ 27117 node: startNode, 27118 offset: startOffset 27119 }) => resolvePath(root, range.end).map(({ 27120 node: endNode, 27121 offset: endOffset 27122 }) => { 27123 const rng = document.createRange(); 27124 rng.setStart(startNode, startOffset); 27125 rng.setEnd(endNode, endOffset); 27126 return rng; 27127 })); 27128 const generatePathRangeFromRange = (dom, root, range, normalized = false) => generatePathRange(dom, root, range.startContainer, range.startOffset, range.endContainer, range.endOffset, normalized); 27129 27130 const cleanEmptyNodes = (dom, node, isRoot) => { 27131 if (node && dom.isEmpty(node) && !isRoot(node)) { 27132 const parent = node.parentNode; 27133 dom.remove(node); 27134 cleanEmptyNodes(dom, parent, isRoot); 27135 } 27136 }; 27137 const deleteRng = (dom, rng, isRoot, clean = true) => { 27138 const startParent = rng.startContainer.parentNode; 27139 const endParent = rng.endContainer.parentNode; 27140 rng.deleteContents(); 27141 if (clean && !isRoot(rng.startContainer)) { 27142 if (isText$a(rng.startContainer) && rng.startContainer.data.length === 0) { 27143 dom.remove(rng.startContainer); 27144 } 27145 if (isText$a(rng.endContainer) && rng.endContainer.data.length === 0) { 27146 dom.remove(rng.endContainer); 27147 } 27148 cleanEmptyNodes(dom, startParent, isRoot); 27149 if (startParent !== endParent) { 27150 cleanEmptyNodes(dom, endParent, isRoot); 27151 } 27152 } 27153 }; 27154 const getParentBlock = (editor, rng) => Optional.from(editor.dom.getParent(rng.startContainer, editor.dom.isBlock)); 27155 const resolveFromDynamicPatterns = (patternSet, block, beforeText) => { 27156 const dynamicPatterns = patternSet.dynamicPatternsLookup({ 27157 text: beforeText, 27158 block 27159 }); 27160 return { 27161 ...patternSet, 27162 blockPatterns: getBlockPatterns(dynamicPatterns).concat(patternSet.blockPatterns), 27163 inlinePatterns: getInlinePatterns(dynamicPatterns).concat(patternSet.inlinePatterns) 27164 }; 27165 }; 27166 const getBeforeText = (dom, block, node, offset) => { 27167 const rng = dom.createRng(); 27168 rng.setStart(block, 0); 27169 rng.setEnd(node, offset); 27170 return rng.toString(); 27171 }; 27172 27173 const startsWithSingleSpace = s => /^\s[^\s]/.test(s); 27174 const stripPattern = (dom, block, pattern) => { 27175 const firstTextNode = textAfter(block, 0, block); 27176 firstTextNode.each(spot => { 27177 const node = spot.container; 27178 scanRight(node, pattern.start.length, block).each(end => { 27179 const rng = dom.createRng(); 27180 rng.setStart(node, 0); 27181 rng.setEnd(end.container, end.offset); 27182 deleteRng(dom, rng, e => e === block); 27183 }); 27184 const text = SugarElement.fromDom(node); 27185 const textContent = get$3(text); 27186 if (startsWithSingleSpace(textContent)) { 27187 set(text, textContent.slice(1)); 27188 } 27189 }); 27190 }; 27191 const applyPattern$1 = (editor, match) => { 27192 const dom = editor.dom; 27193 const pattern = match.pattern; 27194 const rng = resolvePathRange(dom.getRoot(), match.range).getOrDie('Unable to resolve path range'); 27195 const isBlockFormatName = (name, formatter) => { 27196 const formatSet = formatter.get(name); 27197 return isArray$1(formatSet) && head(formatSet).exists(format => has$2(format, 'block')); 27198 }; 27199 getParentBlock(editor, rng).each(block => { 27200 if (pattern.type === 'block-format') { 27201 if (isBlockFormatName(pattern.format, editor.formatter)) { 27202 editor.undoManager.transact(() => { 27203 stripPattern(editor.dom, block, pattern); 27204 editor.formatter.apply(pattern.format); 27205 }); 27206 } 27207 } else if (pattern.type === 'block-command') { 27208 editor.undoManager.transact(() => { 27209 stripPattern(editor.dom, block, pattern); 27210 editor.execCommand(pattern.cmd, false, pattern.value); 27211 }); 27212 } 27213 }); 27214 return true; 27215 }; 27216 const sortPatterns$1 = patterns => sort(patterns, (a, b) => b.start.length - a.start.length); 27217 const findPattern$1 = (patterns, text) => { 27218 const sortedPatterns = sortPatterns$1(patterns); 27219 const nuText = text.replace(nbsp, ' '); 27220 return find$2(sortedPatterns, pattern => text.indexOf(pattern.start) === 0 || nuText.indexOf(pattern.start) === 0); 27221 }; 27222 const findPatterns$1 = (editor, block, patternSet, normalizedMatches) => { 27223 var _a; 27224 const dom = editor.dom; 27225 const forcedRootBlock = getForcedRootBlock(editor); 27226 if (!dom.is(block, forcedRootBlock)) { 27227 return []; 27228 } 27229 const blockText = (_a = block.textContent) !== null && _a !== void 0 ? _a : ''; 27230 return findPattern$1(patternSet.blockPatterns, blockText).map(pattern => { 27231 if (Tools.trim(blockText).length === pattern.start.length) { 27232 return []; 27233 } 27234 return [{ 27235 pattern, 27236 range: generatePathRange(dom, dom.getRoot(), block, 0, block, 0, normalizedMatches) 27237 }]; 27238 }).getOr([]); 27239 }; 27240 const applyMatches$1 = (editor, matches) => { 27241 if (matches.length === 0) { 27242 return; 27243 } 27244 const bookmark = editor.selection.getBookmark(); 27245 each$e(matches, match => applyPattern$1(editor, match)); 27246 editor.selection.moveToBookmark(bookmark); 27247 }; 27248 27249 const newMarker = (dom, id) => dom.create('span', { 27250 'data-mce-type': 'bookmark', 27251 id 27252 }); 27253 const rangeFromMarker = (dom, marker) => { 27254 const rng = dom.createRng(); 27255 rng.setStartAfter(marker.start); 27256 rng.setEndBefore(marker.end); 27257 return rng; 27258 }; 27259 const createMarker = (dom, markerPrefix, pathRange) => { 27260 const rng = resolvePathRange(dom.getRoot(), pathRange).getOrDie('Unable to resolve path range'); 27261 const startNode = rng.startContainer; 27262 const endNode = rng.endContainer; 27263 const textEnd = rng.endOffset === 0 ? endNode : endNode.splitText(rng.endOffset); 27264 const textStart = rng.startOffset === 0 ? startNode : startNode.splitText(rng.startOffset); 27265 const startParentNode = textStart.parentNode; 27266 const endParentNode = textEnd.parentNode; 27267 return { 27268 prefix: markerPrefix, 27269 end: endParentNode.insertBefore(newMarker(dom, markerPrefix + '-end'), textEnd), 27270 start: startParentNode.insertBefore(newMarker(dom, markerPrefix + '-start'), textStart) 27271 }; 27272 }; 27273 const removeMarker = (dom, marker, isRoot) => { 27274 cleanEmptyNodes(dom, dom.get(marker.prefix + '-end'), isRoot); 27275 cleanEmptyNodes(dom, dom.get(marker.prefix + '-start'), isRoot); 27276 }; 27277 27278 const isReplacementPattern = pattern => pattern.start.length === 0; 27279 const matchesPattern = patternContent => (element, offset) => { 27280 const text = element.data; 27281 const searchText = text.substring(0, offset); 27282 const startEndIndex = searchText.lastIndexOf(patternContent.charAt(patternContent.length - 1)); 27283 const startIndex = searchText.lastIndexOf(patternContent); 27284 if (startIndex !== -1) { 27285 return startIndex + patternContent.length; 27286 } else if (startEndIndex !== -1) { 27287 return startEndIndex + 1; 27288 } else { 27289 return -1; 27290 } 27291 }; 27292 const findPatternStartFromSpot = (dom, pattern, block, spot) => { 27293 const startPattern = pattern.start; 27294 const startSpot = repeatLeft(dom, spot.container, spot.offset, matchesPattern(startPattern), block); 27295 return startSpot.bind(spot => { 27296 var _a, _b; 27297 const startPatternIndex = (_b = (_a = block.textContent) === null || _a === void 0 ? void 0 : _a.indexOf(startPattern)) !== null && _b !== void 0 ? _b : -1; 27298 const isCompleteMatch = startPatternIndex !== -1 && spot.offset >= startPatternIndex + startPattern.length; 27299 if (isCompleteMatch) { 27300 const rng = dom.createRng(); 27301 rng.setStart(spot.container, spot.offset - startPattern.length); 27302 rng.setEnd(spot.container, spot.offset); 27303 return Optional.some(rng); 27304 } else { 27305 const offset = spot.offset - startPattern.length; 27306 return scanLeft(spot.container, offset, block).map(nextSpot => { 27307 const rng = dom.createRng(); 27308 rng.setStart(nextSpot.container, nextSpot.offset); 27309 rng.setEnd(spot.container, spot.offset); 27310 return rng; 27311 }).filter(rng => rng.toString() === startPattern).orThunk(() => findPatternStartFromSpot(dom, pattern, block, point(spot.container, 0))); 27312 } 27313 }); 27314 }; 27315 const findPatternStart = (dom, pattern, node, offset, block, requireGap = false) => { 27316 if (pattern.start.length === 0 && !requireGap) { 27317 const rng = dom.createRng(); 27318 rng.setStart(node, offset); 27319 rng.setEnd(node, offset); 27320 return Optional.some(rng); 27321 } 27322 return textBefore(node, offset, block).bind(spot => { 27323 const start = findPatternStartFromSpot(dom, pattern, block, spot); 27324 return start.bind(startRange => { 27325 var _a; 27326 if (requireGap) { 27327 if (startRange.endContainer === spot.container && startRange.endOffset === spot.offset) { 27328 return Optional.none(); 27329 } else if (spot.offset === 0 && ((_a = startRange.endContainer.textContent) === null || _a === void 0 ? void 0 : _a.length) === startRange.endOffset) { 27330 return Optional.none(); 27331 } 27332 } 27333 return Optional.some(startRange); 27334 }); 27335 }); 27336 }; 27337 const findPattern = (editor, block, details, normalizedMatches) => { 27338 const dom = editor.dom; 27339 const root = dom.getRoot(); 27340 const pattern = details.pattern; 27341 const endNode = details.position.container; 27342 const endOffset = details.position.offset; 27343 return scanLeft(endNode, endOffset - details.pattern.end.length, block).bind(spot => { 27344 const endPathRng = generatePathRange(dom, root, spot.container, spot.offset, endNode, endOffset, normalizedMatches); 27345 if (isReplacementPattern(pattern)) { 27346 return Optional.some({ 27347 matches: [{ 27348 pattern, 27349 startRng: endPathRng, 27350 endRng: endPathRng 27351 }], 27352 position: spot 27353 }); 27354 } else { 27355 const resultsOpt = findPatternsRec(editor, details.remainingPatterns, spot.container, spot.offset, block, normalizedMatches); 27356 const results = resultsOpt.getOr({ 27357 matches: [], 27358 position: spot 27359 }); 27360 const pos = results.position; 27361 const start = findPatternStart(dom, pattern, pos.container, pos.offset, block, resultsOpt.isNone()); 27362 return start.map(startRng => { 27363 const startPathRng = generatePathRangeFromRange(dom, root, startRng, normalizedMatches); 27364 return { 27365 matches: results.matches.concat([{ 27366 pattern, 27367 startRng: startPathRng, 27368 endRng: endPathRng 27369 }]), 27370 position: point(startRng.startContainer, startRng.startOffset) 27371 }; 27372 }); 27373 } 27374 }); 27375 }; 27376 const findPatternsRec = (editor, patterns, node, offset, block, normalizedMatches) => { 27377 const dom = editor.dom; 27378 return textBefore(node, offset, dom.getRoot()).bind(endSpot => { 27379 const text = getBeforeText(dom, block, node, offset); 27380 for (let i = 0; i < patterns.length; i++) { 27381 const pattern = patterns[i]; 27382 if (!endsWith(text, pattern.end)) { 27383 continue; 27384 } 27385 const patternsWithoutCurrent = patterns.slice(); 27386 patternsWithoutCurrent.splice(i, 1); 27387 const result = findPattern(editor, block, { 27388 pattern, 27389 remainingPatterns: patternsWithoutCurrent, 27390 position: endSpot 27391 }, normalizedMatches); 27392 if (result.isNone() && offset > 0) { 27393 return findPatternsRec(editor, patterns, node, offset - 1, block, normalizedMatches); 27394 } 27395 if (result.isSome()) { 27396 return result; 27397 } 27398 } 27399 return Optional.none(); 27400 }); 27401 }; 27402 const applyPattern = (editor, pattern, patternRange) => { 27403 editor.selection.setRng(patternRange); 27404 if (pattern.type === 'inline-format') { 27405 each$e(pattern.format, format => { 27406 editor.formatter.apply(format); 27407 }); 27408 } else { 27409 editor.execCommand(pattern.cmd, false, pattern.value); 27410 } 27411 }; 27412 const applyReplacementPattern = (editor, pattern, marker, isRoot) => { 27413 const markerRange = rangeFromMarker(editor.dom, marker); 27414 deleteRng(editor.dom, markerRange, isRoot); 27415 applyPattern(editor, pattern, markerRange); 27416 }; 27417 const applyPatternWithContent = (editor, pattern, startMarker, endMarker, isRoot) => { 27418 const dom = editor.dom; 27419 const markerEndRange = rangeFromMarker(dom, endMarker); 27420 const markerStartRange = rangeFromMarker(dom, startMarker); 27421 deleteRng(dom, markerStartRange, isRoot); 27422 deleteRng(dom, markerEndRange, isRoot); 27423 const patternMarker = { 27424 prefix: startMarker.prefix, 27425 start: startMarker.end, 27426 end: endMarker.start 27427 }; 27428 const patternRange = rangeFromMarker(dom, patternMarker); 27429 applyPattern(editor, pattern, patternRange); 27430 }; 27431 const addMarkers = (dom, matches) => { 27432 const markerPrefix = generate$1('mce_textpattern'); 27433 const matchesWithEnds = foldr(matches, (acc, match) => { 27434 const endMarker = createMarker(dom, markerPrefix + `_end${ acc.length }`, match.endRng); 27435 return acc.concat([{ 27436 ...match, 27437 endMarker 27438 }]); 27439 }, []); 27440 return foldr(matchesWithEnds, (acc, match) => { 27441 const idx = matchesWithEnds.length - acc.length - 1; 27442 const startMarker = isReplacementPattern(match.pattern) ? match.endMarker : createMarker(dom, markerPrefix + `_start${ idx }`, match.startRng); 27443 return acc.concat([{ 27444 ...match, 27445 startMarker 27446 }]); 27447 }, []); 27448 }; 27449 const sortPatterns = patterns => sort(patterns, (a, b) => b.end.length - a.end.length); 27450 const getBestMatches = (matches, matchesWithSortedPatterns) => { 27451 const hasSameMatches = forall(matches, match => exists(matchesWithSortedPatterns, sortedMatch => match.pattern.start === sortedMatch.pattern.start && match.pattern.end === sortedMatch.pattern.end)); 27452 if (matches.length === matchesWithSortedPatterns.length) { 27453 if (hasSameMatches) { 27454 return matches; 27455 } else { 27456 return matchesWithSortedPatterns; 27457 } 27458 } 27459 return matches.length > matchesWithSortedPatterns.length ? matches : matchesWithSortedPatterns; 27460 }; 27461 const findPatterns = (editor, block, node, offset, patternSet, normalizedMatches) => { 27462 const matches = findPatternsRec(editor, patternSet.inlinePatterns, node, offset, block, normalizedMatches).fold(() => [], result => result.matches); 27463 const matchesWithSortedPatterns = findPatternsRec(editor, sortPatterns(patternSet.inlinePatterns), node, offset, block, normalizedMatches).fold(() => [], result => result.matches); 27464 return getBestMatches(matches, matchesWithSortedPatterns); 27465 }; 27466 const applyMatches = (editor, matches) => { 27467 if (matches.length === 0) { 27468 return; 27469 } 27470 const dom = editor.dom; 27471 const bookmark = editor.selection.getBookmark(); 27472 const matchesWithMarkers = addMarkers(dom, matches); 27473 each$e(matchesWithMarkers, match => { 27474 const block = dom.getParent(match.startMarker.start, dom.isBlock); 27475 const isRoot = node => node === block; 27476 if (isReplacementPattern(match.pattern)) { 27477 applyReplacementPattern(editor, match.pattern, match.endMarker, isRoot); 27478 } else { 27479 applyPatternWithContent(editor, match.pattern, match.startMarker, match.endMarker, isRoot); 27480 } 27481 removeMarker(dom, match.endMarker, isRoot); 27482 removeMarker(dom, match.startMarker, isRoot); 27483 }); 27484 editor.selection.moveToBookmark(bookmark); 27485 }; 27486 27487 const handleEnter = (editor, patternSet) => { 27488 const rng = editor.selection.getRng(); 27489 return getParentBlock(editor, rng).map(block => { 27490 var _a; 27491 const offset = Math.max(0, rng.startOffset); 27492 const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, (_a = block.textContent) !== null && _a !== void 0 ? _a : ''); 27493 const inlineMatches = findPatterns(editor, block, rng.startContainer, offset, dynamicPatternSet, true); 27494 const blockMatches = findPatterns$1(editor, block, dynamicPatternSet, true); 27495 if (blockMatches.length > 0 || inlineMatches.length > 0) { 27496 editor.undoManager.add(); 27497 editor.undoManager.extra(() => { 27498 editor.execCommand('mceInsertNewLine'); 27499 }, () => { 27500 editor.insertContent(zeroWidth); 27501 applyMatches(editor, inlineMatches); 27502 applyMatches$1(editor, blockMatches); 27503 const range = editor.selection.getRng(); 27504 const spot = textBefore(range.startContainer, range.startOffset, editor.dom.getRoot()); 27505 editor.execCommand('mceInsertNewLine'); 27506 spot.each(s => { 27507 const node = s.container; 27508 if (node.data.charAt(s.offset - 1) === zeroWidth) { 27509 node.deleteData(s.offset - 1, 1); 27510 cleanEmptyNodes(editor.dom, node.parentNode, e => e === editor.dom.getRoot()); 27511 } 27512 }); 27513 }); 27514 return true; 27515 } 27516 return false; 27517 }).getOr(false); 27518 }; 27519 const handleInlineKey = (editor, patternSet) => { 27520 const rng = editor.selection.getRng(); 27521 getParentBlock(editor, rng).map(block => { 27522 const offset = Math.max(0, rng.startOffset - 1); 27523 const beforeText = getBeforeText(editor.dom, block, rng.startContainer, offset); 27524 const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, beforeText); 27525 const inlineMatches = findPatterns(editor, block, rng.startContainer, offset, dynamicPatternSet, false); 27526 if (inlineMatches.length > 0) { 27527 editor.undoManager.transact(() => { 27528 applyMatches(editor, inlineMatches); 27529 }); 27530 } 27531 }); 27532 }; 27533 const checkKeyEvent = (codes, event, predicate) => { 27534 for (let i = 0; i < codes.length; i++) { 27535 if (predicate(codes[i], event)) { 27536 return true; 27537 } 27538 } 27539 return false; 27540 }; 27541 const checkKeyCode = (codes, event) => checkKeyEvent(codes, event, (code, event) => { 27542 return code === event.keyCode && !VK.modifierPressed(event); 27543 }); 27544 const checkCharCode = (chars, event) => checkKeyEvent(chars, event, (chr, event) => { 27545 return chr.charCodeAt(0) === event.charCode; 27546 }); 27547 27548 const setup$2 = editor => { 27549 const charCodes = [ 27550 ',', 27551 '.', 27552 ';', 27553 ':', 27554 '!', 27555 '?' 27556 ]; 27557 const keyCodes = [32]; 27558 const getPatternSet = () => createPatternSet(getTextPatterns(editor), getTextPatternsLookup(editor)); 27559 const hasDynamicPatterns = () => hasTextPatternsLookup(editor); 27560 editor.on('keydown', e => { 27561 if (e.keyCode === 13 && !VK.modifierPressed(e) && editor.selection.isCollapsed()) { 27562 const patternSet = getPatternSet(); 27563 const hasPatterns = patternSet.inlinePatterns.length > 0 || patternSet.blockPatterns.length > 0 || hasDynamicPatterns(); 27564 if (hasPatterns && handleEnter(editor, patternSet)) { 27565 e.preventDefault(); 27566 } 27567 } 27568 }, true); 27569 const handleInlineTrigger = () => { 27570 if (editor.selection.isCollapsed()) { 27571 const patternSet = getPatternSet(); 27572 const hasPatterns = patternSet.inlinePatterns.length > 0 || hasDynamicPatterns(); 27573 if (hasPatterns) { 27574 handleInlineKey(editor, patternSet); 27575 } 27576 } 27577 }; 27578 editor.on('keyup', e => { 27579 if (checkKeyCode(keyCodes, e)) { 27580 handleInlineTrigger(); 27581 } 27582 }); 27583 editor.on('keypress', e => { 27584 if (checkCharCode(charCodes, e)) { 27585 Delay.setEditorTimeout(editor, handleInlineTrigger); 27586 } 27587 }); 27588 }; 27589 27590 const setup$1 = editor => { 27591 setup$2(editor); 27592 }; 27593 27594 const Quirks = editor => { 27595 const each = Tools.each; 27596 const BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection, parser = editor.parser; 27597 const browser = Env.browser; 27598 const isGecko = browser.isFirefox(); 27599 const isWebKit = browser.isChromium() || browser.isSafari(); 27600 const isiOS = Env.deviceType.isiPhone() || Env.deviceType.isiPad(); 27601 const isMac = Env.os.isMacOS() || Env.os.isiOS(); 27602 const setEditorCommandState = (cmd, state) => { 27603 try { 27604 editor.getDoc().execCommand(cmd, false, String(state)); 27605 } catch (ex) { 27606 } 27607 }; 27608 const isDefaultPrevented = e => { 27609 return e.isDefaultPrevented(); 27610 }; 27611 const emptyEditorWhenDeleting = () => { 27612 const serializeRng = rng => { 27613 const body = dom.create('body'); 27614 const contents = rng.cloneContents(); 27615 body.appendChild(contents); 27616 return selection.serializer.serialize(body, { format: 'html' }); 27617 }; 27618 const allContentsSelected = rng => { 27619 const selection = serializeRng(rng); 27620 const allRng = dom.createRng(); 27621 allRng.selectNode(editor.getBody()); 27622 const allSelection = serializeRng(allRng); 27623 return selection === allSelection; 27624 }; 27625 editor.on('keydown', e => { 27626 const keyCode = e.keyCode; 27627 if (!isDefaultPrevented(e) && (keyCode === DELETE || keyCode === BACKSPACE) && editor.selection.isEditable()) { 27628 const isCollapsed = editor.selection.isCollapsed(); 27629 const body = editor.getBody(); 27630 if (isCollapsed && !dom.isEmpty(body)) { 27631 return; 27632 } 27633 if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) { 27634 return; 27635 } 27636 e.preventDefault(); 27637 editor.setContent(''); 27638 if (body.firstChild && dom.isBlock(body.firstChild)) { 27639 editor.selection.setCursorLocation(body.firstChild, 0); 27640 } else { 27641 editor.selection.setCursorLocation(body, 0); 27642 } 27643 editor.nodeChanged(); 27644 } 27645 }); 27646 }; 27647 const selectAll = () => { 27648 editor.shortcuts.add('meta+a', null, 'SelectAll'); 27649 }; 27650 const documentElementEditingFocus = () => { 27651 if (!editor.inline) { 27652 dom.bind(editor.getDoc(), 'mousedown mouseup', e => { 27653 let rng; 27654 if (e.target === editor.getDoc().documentElement) { 27655 rng = selection.getRng(); 27656 editor.getBody().focus(); 27657 if (e.type === 'mousedown') { 27658 if (isCaretContainer$2(rng.startContainer)) { 27659 return; 27660 } 27661 selection.placeCaretAt(e.clientX, e.clientY); 27662 } else { 27663 selection.setRng(rng); 27664 } 27665 } 27666 }); 27667 } 27668 }; 27669 const removeHrOnBackspace = () => { 27670 editor.on('keydown', e => { 27671 if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { 27672 if (!editor.getBody().getElementsByTagName('hr').length) { 27673 return; 27674 } 27675 if (selection.isCollapsed() && selection.getRng().startOffset === 0) { 27676 const node = selection.getNode(); 27677 const previousSibling = node.previousSibling; 27678 if (node.nodeName === 'HR') { 27679 dom.remove(node); 27680 e.preventDefault(); 27681 return; 27682 } 27683 if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === 'hr') { 27684 dom.remove(previousSibling); 27685 e.preventDefault(); 27686 } 27687 } 27688 } 27689 }); 27690 }; 27691 const focusBody = () => { 27692 if (!Range.prototype.getClientRects) { 27693 editor.on('mousedown', e => { 27694 if (!isDefaultPrevented(e) && e.target.nodeName === 'HTML') { 27695 const body = editor.getBody(); 27696 body.blur(); 27697 Delay.setEditorTimeout(editor, () => { 27698 body.focus(); 27699 }); 27700 } 27701 }); 27702 } 27703 }; 27704 const selectControlElements = () => { 27705 const visualAidsAnchorClass = getVisualAidsAnchorClass(editor); 27706 editor.on('click', e => { 27707 const target = e.target; 27708 if (/^(IMG|HR)$/.test(target.nodeName) && dom.isEditable(target.parentNode)) { 27709 e.preventDefault(); 27710 editor.selection.select(target); 27711 editor.nodeChanged(); 27712 } 27713 if (target.nodeName === 'A' && dom.hasClass(target, visualAidsAnchorClass) && target.childNodes.length === 0 && dom.isEditable(target.parentNode)) { 27714 e.preventDefault(); 27715 selection.select(target); 27716 } 27717 }); 27718 }; 27719 const removeStylesWhenDeletingAcrossBlockElements = () => { 27720 const getAttributeApplyFunction = () => { 27721 const template = dom.getAttribs(selection.getStart().cloneNode(false)); 27722 return () => { 27723 const target = selection.getStart(); 27724 if (target !== editor.getBody()) { 27725 dom.setAttrib(target, 'style', null); 27726 each(template, attr => { 27727 target.setAttributeNode(attr.cloneNode(true)); 27728 }); 27729 } 27730 }; 27731 }; 27732 const isSelectionAcrossElements = () => { 27733 return !selection.isCollapsed() && dom.getParent(selection.getStart(), dom.isBlock) !== dom.getParent(selection.getEnd(), dom.isBlock); 27734 }; 27735 editor.on('keypress', e => { 27736 let applyAttributes; 27737 if (!isDefaultPrevented(e) && (e.keyCode === 8 || e.keyCode === 46) && isSelectionAcrossElements()) { 27738 applyAttributes = getAttributeApplyFunction(); 27739 editor.getDoc().execCommand('delete', false); 27740 applyAttributes(); 27741 e.preventDefault(); 27742 return false; 27743 } else { 27744 return true; 27745 } 27746 }); 27747 dom.bind(editor.getDoc(), 'cut', e => { 27748 if (!isDefaultPrevented(e) && isSelectionAcrossElements()) { 27749 const applyAttributes = getAttributeApplyFunction(); 27750 Delay.setEditorTimeout(editor, () => { 27751 applyAttributes(); 27752 }); 27753 } 27754 }); 27755 }; 27756 const disableBackspaceIntoATable = () => { 27757 editor.on('keydown', e => { 27758 if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { 27759 if (selection.isCollapsed() && selection.getRng().startOffset === 0) { 27760 const previousSibling = selection.getNode().previousSibling; 27761 if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === 'table') { 27762 e.preventDefault(); 27763 return false; 27764 } 27765 } 27766 } 27767 return true; 27768 }); 27769 }; 27770 const removeBlockQuoteOnBackSpace = () => { 27771 editor.on('keydown', e => { 27772 if (isDefaultPrevented(e) || e.keyCode !== VK.BACKSPACE) { 27773 return; 27774 } 27775 let rng = selection.getRng(); 27776 const container = rng.startContainer; 27777 const offset = rng.startOffset; 27778 const root = dom.getRoot(); 27779 let parent = container; 27780 if (!rng.collapsed || offset !== 0) { 27781 return; 27782 } 27783 while (parent.parentNode && parent.parentNode.firstChild === parent && parent.parentNode !== root) { 27784 parent = parent.parentNode; 27785 } 27786 if (parent.nodeName === 'BLOCKQUOTE') { 27787 editor.formatter.toggle('blockquote', undefined, parent); 27788 rng = dom.createRng(); 27789 rng.setStart(container, 0); 27790 rng.setEnd(container, 0); 27791 selection.setRng(rng); 27792 } 27793 }); 27794 }; 27795 const setGeckoEditingOptions = () => { 27796 const setOpts = () => { 27797 setEditorCommandState('StyleWithCSS', false); 27798 setEditorCommandState('enableInlineTableEditing', false); 27799 if (!getObjectResizing(editor)) { 27800 setEditorCommandState('enableObjectResizing', false); 27801 } 27802 }; 27803 if (!isReadOnly$1(editor)) { 27804 editor.on('BeforeExecCommand mousedown', setOpts); 27805 } 27806 }; 27807 const addBrAfterLastLinks = () => { 27808 const fixLinks = () => { 27809 each(dom.select('a:not([data-mce-block])'), node => { 27810 var _a; 27811 let parentNode = node.parentNode; 27812 const root = dom.getRoot(); 27813 if ((parentNode === null || parentNode === void 0 ? void 0 : parentNode.lastChild) === node) { 27814 while (parentNode && !dom.isBlock(parentNode)) { 27815 if (((_a = parentNode.parentNode) === null || _a === void 0 ? void 0 : _a.lastChild) !== parentNode || parentNode === root) { 27816 return; 27817 } 27818 parentNode = parentNode.parentNode; 27819 } 27820 dom.add(parentNode, 'br', { 'data-mce-bogus': 1 }); 27821 } 27822 }); 27823 }; 27824 editor.on('SetContent ExecCommand', e => { 27825 if (e.type === 'setcontent' || e.command === 'mceInsertLink') { 27826 fixLinks(); 27827 } 27828 }); 27829 }; 27830 const setDefaultBlockType = () => { 27831 editor.on('init', () => { 27832 setEditorCommandState('DefaultParagraphSeparator', getForcedRootBlock(editor)); 27833 }); 27834 }; 27835 const isAllContentSelected = editor => { 27836 const body = editor.getBody(); 27837 const rng = editor.selection.getRng(); 27838 return rng.startContainer === rng.endContainer && rng.startContainer === body && rng.startOffset === 0 && rng.endOffset === body.childNodes.length; 27839 }; 27840 const normalizeSelection = () => { 27841 editor.on('keyup focusin mouseup', e => { 27842 if (!VK.modifierPressed(e) && !isAllContentSelected(editor)) { 27843 selection.normalize(); 27844 } 27845 }, true); 27846 }; 27847 const showBrokenImageIcon = () => { 27848 editor.contentStyles.push('img:-moz-broken {' + '-moz-force-broken-image-icon:1;' + 'min-width:24px;' + 'min-height:24px' + '}'); 27849 }; 27850 const restoreFocusOnKeyDown = () => { 27851 if (!editor.inline) { 27852 editor.on('keydown', () => { 27853 if (document.activeElement === document.body) { 27854 editor.getWin().focus(); 27855 } 27856 }); 27857 } 27858 }; 27859 const bodyHeight = () => { 27860 if (!editor.inline) { 27861 editor.contentStyles.push('body {min-height: 150px}'); 27862 editor.on('click', e => { 27863 let rng; 27864 if (e.target.nodeName === 'HTML') { 27865 rng = editor.selection.getRng(); 27866 editor.getBody().focus(); 27867 editor.selection.setRng(rng); 27868 editor.selection.normalize(); 27869 editor.nodeChanged(); 27870 } 27871 }); 27872 } 27873 }; 27874 const blockCmdArrowNavigation = () => { 27875 if (isMac) { 27876 editor.on('keydown', e => { 27877 if (VK.metaKeyPressed(e) && !e.shiftKey && (e.keyCode === 37 || e.keyCode === 39)) { 27878 e.preventDefault(); 27879 const selection = editor.selection.getSel(); 27880 selection.modify('move', e.keyCode === 37 ? 'backward' : 'forward', 'lineboundary'); 27881 } 27882 }); 27883 } 27884 }; 27885 const tapLinksAndImages = () => { 27886 editor.on('click', e => { 27887 let elm = e.target; 27888 do { 27889 if (elm.tagName === 'A') { 27890 e.preventDefault(); 27891 return; 27892 } 27893 } while (elm = elm.parentNode); 27894 }); 27895 editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}'); 27896 }; 27897 const blockFormSubmitInsideEditor = () => { 27898 editor.on('init', () => { 27899 editor.dom.bind(editor.getBody(), 'submit', e => { 27900 e.preventDefault(); 27901 }); 27902 }); 27903 }; 27904 const removeAppleInterchangeBrs = () => { 27905 parser.addNodeFilter('br', nodes => { 27906 let i = nodes.length; 27907 while (i--) { 27908 if (nodes[i].attr('class') === 'Apple-interchange-newline') { 27909 nodes[i].remove(); 27910 } 27911 } 27912 }); 27913 }; 27914 const refreshContentEditable = noop; 27915 const isHidden = () => { 27916 if (!isGecko || editor.removed) { 27917 return false; 27918 } 27919 const sel = editor.selection.getSel(); 27920 return !sel || !sel.rangeCount || sel.rangeCount === 0; 27921 }; 27922 const setupRtc = () => { 27923 if (isWebKit) { 27924 documentElementEditingFocus(); 27925 selectControlElements(); 27926 blockFormSubmitInsideEditor(); 27927 selectAll(); 27928 if (isiOS) { 27929 restoreFocusOnKeyDown(); 27930 bodyHeight(); 27931 tapLinksAndImages(); 27932 } 27933 } 27934 if (isGecko) { 27935 focusBody(); 27936 setGeckoEditingOptions(); 27937 showBrokenImageIcon(); 27938 blockCmdArrowNavigation(); 27939 } 27940 }; 27941 const dropDragEndEvent = () => { 27942 editor.on('drop', event => { 27943 var _a; 27944 const data = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData('text/html'); 27945 if (isString(data) && /^<img[^>]*>$/.test(data)) { 27946 editor.dispatch('dragend', new window.DragEvent('dragend', event)); 27947 } 27948 }); 27949 }; 27950 const setup = () => { 27951 removeBlockQuoteOnBackSpace(); 27952 emptyEditorWhenDeleting(); 27953 if (!Env.windowsPhone) { 27954 normalizeSelection(); 27955 } 27956 if (isWebKit) { 27957 documentElementEditingFocus(); 27958 selectControlElements(); 27959 setDefaultBlockType(); 27960 blockFormSubmitInsideEditor(); 27961 disableBackspaceIntoATable(); 27962 removeAppleInterchangeBrs(); 27963 if (isiOS) { 27964 restoreFocusOnKeyDown(); 27965 bodyHeight(); 27966 tapLinksAndImages(); 27967 } else { 27968 selectAll(); 27969 } 27970 } 27971 if (isGecko) { 27972 removeHrOnBackspace(); 27973 focusBody(); 27974 removeStylesWhenDeletingAcrossBlockElements(); 27975 setGeckoEditingOptions(); 27976 addBrAfterLastLinks(); 27977 showBrokenImageIcon(); 27978 blockCmdArrowNavigation(); 27979 disableBackspaceIntoATable(); 27980 dropDragEndEvent(); 27981 } 27982 }; 27983 if (isRtc(editor)) { 27984 setupRtc(); 27985 } else { 27986 setup(); 27987 } 27988 return { 27989 refreshContentEditable, 27990 isHidden 27991 }; 27992 }; 27993 27994 const DOM$6 = DOMUtils.DOM; 27995 const appendStyle = (editor, text) => { 27996 const body = SugarElement.fromDom(editor.getBody()); 27997 const container = getStyleContainer(getRootNode(body)); 27998 const style = SugarElement.fromTag('style'); 27999 set$3(style, 'type', 'text/css'); 28000 append$1(style, SugarElement.fromText(text)); 28001 append$1(container, style); 28002 editor.on('remove', () => { 28003 remove$5(style); 28004 }); 28005 }; 28006 const getRootName = editor => editor.inline ? editor.getElement().nodeName.toLowerCase() : undefined; 28007 const removeUndefined = obj => filter$4(obj, v => isUndefined(v) === false); 28008 const mkParserSettings = editor => { 28009 const getOption = editor.options.get; 28010 const blobCache = editor.editorUpload.blobCache; 28011 return removeUndefined({ 28012 allow_conditional_comments: getOption('allow_conditional_comments'), 28013 allow_html_data_urls: getOption('allow_html_data_urls'), 28014 allow_svg_data_urls: getOption('allow_svg_data_urls'), 28015 allow_html_in_named_anchor: getOption('allow_html_in_named_anchor'), 28016 allow_script_urls: getOption('allow_script_urls'), 28017 allow_unsafe_link_target: getOption('allow_unsafe_link_target'), 28018 convert_fonts_to_spans: getOption('convert_fonts_to_spans'), 28019 fix_list_elements: getOption('fix_list_elements'), 28020 font_size_legacy_values: getOption('font_size_legacy_values'), 28021 forced_root_block: getOption('forced_root_block'), 28022 forced_root_block_attrs: getOption('forced_root_block_attrs'), 28023 preserve_cdata: getOption('preserve_cdata'), 28024 inline_styles: getOption('inline_styles'), 28025 root_name: getRootName(editor), 28026 sanitize: getOption('xss_sanitization'), 28027 validate: true, 28028 blob_cache: blobCache, 28029 document: editor.getDoc() 28030 }); 28031 }; 28032 const mkSchemaSettings = editor => { 28033 const getOption = editor.options.get; 28034 return removeUndefined({ 28035 custom_elements: getOption('custom_elements'), 28036 extended_valid_elements: getOption('extended_valid_elements'), 28037 invalid_elements: getOption('invalid_elements'), 28038 invalid_styles: getOption('invalid_styles'), 28039 schema: getOption('schema'), 28040 valid_children: getOption('valid_children'), 28041 valid_classes: getOption('valid_classes'), 28042 valid_elements: getOption('valid_elements'), 28043 valid_styles: getOption('valid_styles'), 28044 verify_html: getOption('verify_html'), 28045 padd_empty_block_inline_children: getOption('format_empty_lines') 28046 }); 28047 }; 28048 const mkSerializerSettings = editor => { 28049 const getOption = editor.options.get; 28050 return { 28051 ...mkParserSettings(editor), 28052 ...mkSchemaSettings(editor), 28053 ...removeUndefined({ 28054 remove_trailing_brs: getOption('remove_trailing_brs'), 28055
28055pad_empty_with_br: getOption('pad_empty_with_br
vendor: 93,003 bytes, lines 28055-30697
28055'), 28056 url_converter: getOption('url_converter'), 28057 url_converter_scope: getOption('url_converter_scope'), 28058 element_format: getOption('element_format'), 28059 entities: getOption('entities'), 28060 entity_encoding: getOption('entity_encoding'), 28061 indent: getOption('indent'), 28062 indent_after: getOption('indent_after'), 28063 indent_before: getOption('indent_before') 28064 }) 28065 }; 28066 }; 28067 const createParser = editor => { 28068 const parser = DomParser(mkParserSettings(editor), editor.schema); 28069 parser.addAttributeFilter('src,href,style,tabindex', (nodes, name) => { 28070 const dom = editor.dom; 28071 const internalName = 'data-mce-' + name; 28072 let i = nodes.length; 28073 while (i--) { 28074 const node = nodes[i]; 28075 let value = node.attr(name); 28076 if (value && !node.attr(internalName)) { 28077 if (value.indexOf('data:') === 0 || value.indexOf('blob:') === 0) { 28078 continue; 28079 } 28080 if (name === 'style') { 28081 value = dom.serializeStyle(dom.parseStyle(value), node.name); 28082 if (!value.length) { 28083 value = null; 28084 } 28085 node.attr(internalName, value); 28086 node.attr(name, value); 28087 } else if (name === 'tabindex') { 28088 node.attr(internalName, value); 28089 node.attr(name, null); 28090 } else { 28091 node.attr(internalName, editor.convertURL(value, name, node.name)); 28092 } 28093 } 28094 } 28095 }); 28096 parser.addNodeFilter('script', nodes => { 28097 let i = nodes.length; 28098 while (i--) { 28099 const node = nodes[i]; 28100 const type = node.attr('type') || 'no/type'; 28101 if (type.indexOf('mce-') !== 0) { 28102 node.attr('type', 'mce-' + type); 28103 } 28104 } 28105 }); 28106 if (shouldPreserveCData(editor)) { 28107 parser.addNodeFilter('#cdata', nodes => { 28108 var _a; 28109 let i = nodes.length; 28110 while (i--) { 28111 const node = nodes[i]; 28112 node.type = 8; 28113 node.name = '#comment'; 28114 node.value = '[CDATA[' + editor.dom.encode((_a = node.value) !== null && _a !== void 0 ? _a : '') + ']]'; 28115 } 28116 }); 28117 } 28118 parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', nodes => { 28119 let i = nodes.length; 28120 const nonEmptyElements = editor.schema.getNonEmptyElements(); 28121 while (i--) { 28122 const node = nodes[i]; 28123 if (node.isEmpty(nonEmptyElements) && node.getAll('br').length === 0) { 28124 node.append(new AstNode('br', 1)); 28125 } 28126 } 28127 }); 28128 return parser; 28129 }; 28130 const autoFocus = editor => { 28131 const autoFocus = getAutoFocus(editor); 28132 if (autoFocus) { 28133 Delay.setEditorTimeout(editor, () => { 28134 let focusEditor; 28135 if (autoFocus === true) { 28136 focusEditor = editor; 28137 } else { 28138 focusEditor = editor.editorManager.get(autoFocus); 28139 } 28140 if (focusEditor && !focusEditor.destroyed) { 28141 focusEditor.focus(); 28142 focusEditor.selection.scrollIntoView(); 28143 } 28144 }, 100); 28145 } 28146 }; 28147 const moveSelectionToFirstCaretPosition = editor => { 28148 const root = editor.dom.getRoot(); 28149 if (!editor.inline && (!hasAnyRanges(editor) || editor.selection.getStart(true) === root)) { 28150 firstPositionIn(root).each(pos => { 28151 const node = pos.getNode(); 28152 const caretPos = isTable$2(node) ? firstPositionIn(node).getOr(pos) : pos; 28153 editor.selection.setRng(caretPos.toRange()); 28154 }); 28155 } 28156 }; 28157 const initEditor = editor => { 28158 editor.bindPendingEventDelegates(); 28159 editor.initialized = true; 28160 fireInit(editor); 28161 editor.focus(true); 28162 moveSelectionToFirstCaretPosition(editor); 28163 editor.nodeChanged({ initial: true }); 28164 const initInstanceCallback = getInitInstanceCallback(editor); 28165 if (isFunction(initInstanceCallback)) { 28166 initInstanceCallback.call(editor, editor); 28167 } 28168 autoFocus(editor); 28169 }; 28170 const getStyleSheetLoader$1 = editor => editor.inline ? editor.ui.styleSheetLoader : editor.dom.styleSheetLoader; 28171 const makeStylesheetLoadingPromises = (editor, css, framedFonts) => { 28172 const promises = [getStyleSheetLoader$1(editor).loadAll(css)]; 28173 if (editor.inline) { 28174 return promises; 28175 } else { 28176 return promises.concat([editor.ui.styleSheetLoader.loadAll(framedFonts)]); 28177 } 28178 }; 28179 const loadContentCss = editor => { 28180 const styleSheetLoader = getStyleSheetLoader$1(editor); 28181 const fontCss = getFontCss(editor); 28182 const css = editor.contentCSS; 28183 const removeCss = () => { 28184 styleSheetLoader.unloadAll(css); 28185 if (!editor.inline) { 28186 editor.ui.styleSheetLoader.unloadAll(fontCss); 28187 } 28188 }; 28189 const loaded = () => { 28190 if (editor.removed) { 28191 removeCss(); 28192 } else { 28193 editor.on('remove', removeCss); 28194 } 28195 }; 28196 if (editor.contentStyles.length > 0) { 28197 let contentCssText = ''; 28198 Tools.each(editor.contentStyles, style => { 28199 contentCssText += style + '\r\n'; 28200 }); 28201 editor.dom.addStyle(contentCssText); 28202 } 28203 const allStylesheets = Promise.all(makeStylesheetLoadingPromises(editor, css, fontCss)).then(loaded).catch(loaded); 28204 const contentStyle = getContentStyle(editor); 28205 if (contentStyle) { 28206 appendStyle(editor, contentStyle); 28207 } 28208 return allStylesheets; 28209 }; 28210 const preInit = editor => { 28211 const doc = editor.getDoc(), body = editor.getBody(); 28212 firePreInit(editor); 28213 if (!shouldBrowserSpellcheck(editor)) { 28214 doc.body.spellcheck = false; 28215 DOM$6.setAttrib(body, 'spellcheck', 'false'); 28216 } 28217 editor.quirks = Quirks(editor); 28218 firePostRender(editor); 28219 const directionality = getDirectionality(editor); 28220 if (directionality !== undefined) { 28221 body.dir = directionality; 28222 } 28223 const protect = getProtect(editor); 28224 if (protect) { 28225 editor.on('BeforeSetContent', e => { 28226 Tools.each(protect, pattern => { 28227 e.content = e.content.replace(pattern, str => { 28228 return '<!--mce:protected ' + escape(str) + '-->'; 28229 }); 28230 }); 28231 }); 28232 } 28233 editor.on('SetContent', () => { 28234 editor.addVisual(editor.getBody()); 28235 }); 28236 editor.on('compositionstart compositionend', e => { 28237 editor.composing = e.type === 'compositionstart'; 28238 }); 28239 }; 28240 const loadInitialContent = editor => { 28241 if (!isRtc(editor)) { 28242 editor.load({ 28243 initial: true, 28244 format: 'html' 28245 }); 28246 } 28247 editor.startContent = editor.getContent({ format: 'raw' }); 28248 }; 28249 const initEditorWithInitialContent = editor => { 28250 if (editor.removed !== true) { 28251 loadInitialContent(editor); 28252 initEditor(editor); 28253 } 28254 }; 28255 const contentBodyLoaded = editor => { 28256 const targetElm = editor.getElement(); 28257 let doc = editor.getDoc(); 28258 if (editor.inline) { 28259 DOM$6.addClass(targetElm, 'mce-content-body'); 28260 editor.contentDocument = doc = document; 28261 editor.contentWindow = window; 28262 editor.bodyElement = targetElm; 28263 editor.contentAreaContainer = targetElm; 28264 } 28265 const body = editor.getBody(); 28266 body.disabled = true; 28267 editor.readonly = isReadOnly$1(editor); 28268 editor._editableRoot = hasEditableRoot$1(editor); 28269 if (!editor.readonly && editor.hasEditableRoot()) { 28270 if (editor.inline && DOM$6.getStyle(body, 'position', true) === 'static') { 28271 body.style.position = 'relative'; 28272 } 28273 body.contentEditable = 'true'; 28274 } 28275 body.disabled = false; 28276 editor.editorUpload = EditorUpload(editor); 28277 editor.schema = Schema(mkSchemaSettings(editor)); 28278 editor.dom = DOMUtils(doc, { 28279 keep_values: true, 28280 url_converter: editor.convertURL, 28281 url_converter_scope: editor, 28282 update_styles: true, 28283 root_element: editor.inline ? editor.getBody() : null, 28284 collect: editor.inline, 28285 schema: editor.schema, 28286 contentCssCors: shouldUseContentCssCors(editor), 28287 referrerPolicy: getReferrerPolicy(editor), 28288 onSetAttrib: e => { 28289 editor.dispatch('SetAttrib', e); 28290 } 28291 }); 28292 editor.parser = createParser(editor); 28293 editor.serializer = DomSerializer(mkSerializerSettings(editor), editor); 28294 editor.selection = EditorSelection(editor.dom, editor.getWin(), editor.serializer, editor); 28295 editor.annotator = Annotator(editor); 28296 editor.formatter = Formatter(editor); 28297 editor.undoManager = UndoManager(editor); 28298 editor._nodeChangeDispatcher = new NodeChange(editor); 28299 editor._selectionOverrides = SelectionOverrides(editor); 28300 setup$o(editor); 28301 setup$6(editor); 28302 setup$m(editor); 28303 if (!isRtc(editor)) { 28304 setup$5(editor); 28305 setup$1(editor); 28306 } 28307 const caret = setup$b(editor); 28308 setup$p(editor, caret); 28309 setup$n(editor); 28310 setup$q(editor); 28311 setup$7(editor); 28312 const setupRtcThunk = setup$s(editor); 28313 preInit(editor); 28314 setupRtcThunk.fold(() => { 28315 loadContentCss(editor).then(() => initEditorWithInitialContent(editor)); 28316 }, setupRtc => { 28317 editor.setProgressState(true); 28318 loadContentCss(editor).then(() => { 28319 setupRtc().then(_rtcMode => { 28320 editor.setProgressState(false); 28321 initEditorWithInitialContent(editor); 28322 bindEvents(editor); 28323 }, err => { 28324 editor.notificationManager.open({ 28325 type: 'error', 28326 text: String(err) 28327 }); 28328 initEditorWithInitialContent(editor); 28329 bindEvents(editor); 28330 }); 28331 }); 28332 }); 28333 }; 28334 28335 const filter = always; 28336 const bind = (element, event, handler) => bind$2(element, event, filter, handler); 28337 28338 const DOM$5 = DOMUtils.DOM; 28339 const createIframeElement = (id, title, customAttrs, tabindex) => { 28340 const iframe = SugarElement.fromTag('iframe'); 28341 tabindex.each(t => set$3(iframe, 'tabindex', t)); 28342 setAll$1(iframe, customAttrs); 28343 setAll$1(iframe, { 28344 id: id + '_ifr', 28345 frameBorder: '0', 28346 allowTransparency: 'true', 28347 title 28348 }); 28349 add$2(iframe, 'tox-edit-area__iframe'); 28350 return iframe; 28351 }; 28352 const getIframeHtml = editor => { 28353 let iframeHTML = getDocType(editor) + '<html><head>'; 28354 if (getDocumentBaseUrl(editor) !== editor.documentBaseUrl) { 28355 iframeHTML += '<base href="' + editor.documentBaseURI.getURI() + '" />'; 28356 } 28357 iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'; 28358 const bodyId = getBodyId(editor); 28359 const bodyClass = getBodyClass(editor); 28360 const translatedAriaText = editor.translate(getIframeAriaText(editor)); 28361 if (getContentSecurityPolicy(editor)) { 28362 iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + getContentSecurityPolicy(editor) + '" />'; 28363 } 28364 iframeHTML += '</head>' + `<body id="${ bodyId }" class="mce-content-body ${ bodyClass }" data-id="${ editor.id }" aria-label="${ translatedAriaText }">` + '<br>' + '</body></html>'; 28365 return iframeHTML; 28366 }; 28367 const createIframe = (editor, boxInfo) => { 28368 const iframeTitle = editor.translate('Rich Text Area'); 28369 const tabindex = getOpt(SugarElement.fromDom(editor.getElement()), 'tabindex').bind(toInt); 28370 const ifr = createIframeElement(editor.id, iframeTitle, getIframeAttrs(editor), tabindex).dom; 28371 ifr.onload = () => { 28372 ifr.onload = null; 28373 editor.dispatch('load'); 28374 }; 28375 editor.contentAreaContainer = boxInfo.iframeContainer; 28376 editor.iframeElement = ifr; 28377 editor.iframeHTML = getIframeHtml(editor); 28378 DOM$5.add(boxInfo.iframeContainer, ifr); 28379 }; 28380 const setupIframeBody = editor => { 28381 const iframe = editor.iframeElement; 28382 const ready = () => { 28383 editor.contentDocument = iframe.contentDocument; 28384 contentBodyLoaded(editor); 28385 }; 28386 if (shouldUseDocumentWrite(editor) || Env.browser.isFirefox()) { 28387 const doc = editor.getDoc(); 28388 doc.open(); 28389 doc.write(editor.iframeHTML); 28390 doc.close(); 28391 ready(); 28392 } else { 28393 const binder = bind(SugarElement.fromDom(iframe), 'load', () => { 28394 binder.unbind(); 28395 ready(); 28396 }); 28397 iframe.srcdoc = editor.iframeHTML; 28398 } 28399 }; 28400 const init$1 = (editor, boxInfo) => { 28401 createIframe(editor, boxInfo); 28402 if (boxInfo.editorContainer) { 28403 boxInfo.editorContainer.style.display = editor.orgDisplay; 28404 editor.hidden = DOM$5.isHidden(boxInfo.editorContainer); 28405 } 28406 editor.getElement().style.display = 'none'; 28407 DOM$5.setAttrib(editor.id, 'aria-hidden', 'true'); 28408 editor.getElement().style.visibility = editor.orgVisibility; 28409 setupIframeBody(editor); 28410 }; 28411 28412 const DOM$4 = DOMUtils.DOM; 28413 const initPlugin = (editor, initializedPlugins, plugin) => { 28414 const Plugin = PluginManager.get(plugin); 28415 const pluginUrl = PluginManager.urls[plugin] || editor.documentBaseUrl.replace(/\/$/, ''); 28416 plugin = Tools.trim(plugin); 28417 if (Plugin && Tools.inArray(initializedPlugins, plugin) === -1) { 28418 if (editor.plugins[plugin]) { 28419 return; 28420 } 28421 try { 28422 const pluginInstance = Plugin(editor, pluginUrl) || {}; 28423 editor.plugins[plugin] = pluginInstance; 28424 if (isFunction(pluginInstance.init)) { 28425 pluginInstance.init(editor, pluginUrl); 28426 initializedPlugins.push(plugin); 28427 } 28428 } catch (e) { 28429 pluginInitError(editor, plugin, e); 28430 } 28431 } 28432 }; 28433 const trimLegacyPrefix = name => { 28434 return name.replace(/^\-/, ''); 28435 }; 28436 const initPlugins = editor => { 28437 const initializedPlugins = []; 28438 each$e(getPlugins(editor), name => { 28439 initPlugin(editor, initializedPlugins, trimLegacyPrefix(name)); 28440 }); 28441 }; 28442 const initIcons = editor => { 28443 const iconPackName = Tools.trim(getIconPackName(editor)); 28444 const currentIcons = editor.ui.registry.getAll().icons; 28445 const loadIcons = { 28446 ...IconManager.get('default').icons, 28447 ...IconManager.get(iconPackName).icons 28448 }; 28449 each$d(loadIcons, (svgData, icon) => { 28450 if (!has$2(currentIcons, icon)) { 28451 editor.ui.registry.addIcon(icon, svgData); 28452 } 28453 }); 28454 }; 28455 const initTheme = editor => { 28456 const theme = getTheme(editor); 28457 if (isString(theme)) { 28458 const Theme = ThemeManager.get(theme); 28459 editor.theme = Theme(editor, ThemeManager.urls[theme]) || {}; 28460 if (isFunction(editor.theme.init)) { 28461 editor.theme.init(editor, ThemeManager.urls[theme] || editor.documentBaseUrl.replace(/\/$/, '')); 28462 } 28463 } else { 28464 editor.theme = {}; 28465 } 28466 }; 28467 const initModel = editor => { 28468 const model = getModel(editor); 28469 const Model = ModelManager.get(model); 28470 editor.model = Model(editor, ModelManager.urls[model]); 28471 }; 28472 const renderFromLoadedTheme = editor => { 28473 const render = editor.theme.renderUI; 28474 return render ? render() : renderThemeFalse(editor); 28475 }; 28476 const renderFromThemeFunc = editor => { 28477 const elm = editor.getElement(); 28478 const theme = getTheme(editor); 28479 const info = theme(editor, elm); 28480 if (info.editorContainer.nodeType) { 28481 info.editorContainer.id = info.editorContainer.id || editor.id + '_parent'; 28482 } 28483 if (info.iframeContainer && info.iframeContainer.nodeType) { 28484 info.iframeContainer.id = info.iframeContainer.id || editor.id + '_iframecontainer'; 28485 } 28486 info.height = info.iframeHeight ? info.iframeHeight : elm.offsetHeight; 28487 return info; 28488 }; 28489 const createThemeFalseResult = (element, iframe) => { 28490 return { 28491 editorContainer: element, 28492 iframeContainer: iframe, 28493 api: {} 28494 }; 28495 }; 28496 const renderThemeFalseIframe = targetElement => { 28497 const iframeContainer = DOM$4.create('div'); 28498 DOM$4.insertAfter(iframeContainer, targetElement); 28499 return createThemeFalseResult(iframeContainer, iframeContainer); 28500 }; 28501 const renderThemeFalse = editor => { 28502 const targetElement = editor.getElement(); 28503 return editor.inline ? createThemeFalseResult(null) : renderThemeFalseIframe(targetElement); 28504 }; 28505 const renderThemeUi = editor => { 28506 const elm = editor.getElement(); 28507 editor.orgDisplay = elm.style.display; 28508 if (isString(getTheme(editor))) { 28509 return renderFromLoadedTheme(editor); 28510 } else if (isFunction(getTheme(editor))) { 28511 return renderFromThemeFunc(editor); 28512 } else { 28513 return renderThemeFalse(editor); 28514 } 28515 }; 28516 const augmentEditorUiApi = (editor, api) => { 28517 const uiApiFacade = { 28518 show: Optional.from(api.show).getOr(noop), 28519 hide: Optional.from(api.hide).getOr(noop), 28520 isEnabled: Optional.from(api.isEnabled).getOr(always), 28521 setEnabled: state => { 28522 if (!editor.mode.isReadOnly()) { 28523 Optional.from(api.setEnabled).each(f => f(state)); 28524 } 28525 } 28526 }; 28527 editor.ui = { 28528 ...editor.ui, 28529 ...uiApiFacade 28530 }; 28531 }; 28532 const init = async editor => { 28533 editor.dispatch('ScriptsLoaded'); 28534 initIcons(editor); 28535 initTheme(editor); 28536 initModel(editor); 28537 initPlugins(editor); 28538 const renderInfo = await renderThemeUi(editor); 28539 augmentEditorUiApi(editor, Optional.from(renderInfo.api).getOr({})); 28540 editor.editorContainer = renderInfo.editorContainer; 28541 appendContentCssFromSettings(editor); 28542 if (editor.inline) { 28543 contentBodyLoaded(editor); 28544 } else { 28545 init$1(editor, { 28546 editorContainer: renderInfo.editorContainer, 28547 iframeContainer: renderInfo.iframeContainer 28548 }); 28549 } 28550 }; 28551 28552 const DOM$3 = DOMUtils.DOM; 28553 const hasSkipLoadPrefix = name => name.charAt(0) === '-'; 28554 const loadLanguage = (scriptLoader, editor) => { 28555 const languageCode = getLanguageCode(editor); 28556 const languageUrl = getLanguageUrl(editor); 28557 if (!I18n.hasCode(languageCode) && languageCode !== 'en') { 28558 const url = isNotEmpty(languageUrl) ? languageUrl : `${ editor.editorManager.baseURL }/langs/${ languageCode }.js`; 28559 scriptLoader.add(url).catch(() => { 28560 languageLoadError(editor, url, languageCode); 28561 }); 28562 } 28563 }; 28564 const loadTheme = (editor, suffix) => { 28565 const theme = getTheme(editor); 28566 if (isString(theme) && !hasSkipLoadPrefix(theme) && !has$2(ThemeManager.urls, theme)) { 28567 const themeUrl = getThemeUrl(editor); 28568 const url = themeUrl ? editor.documentBaseURI.toAbsolute(themeUrl) : `themes/${ theme }/theme${ suffix }.js`; 28569 ThemeManager.load(theme, url).catch(() => { 28570 themeLoadError(editor, url, theme); 28571 }); 28572 } 28573 }; 28574 const loadModel = (editor, suffix) => { 28575 const model = getModel(editor); 28576 if (model !== 'plugin' && !has$2(ModelManager.urls, model)) { 28577 const modelUrl = getModelUrl(editor); 28578 const url = isString(modelUrl) ? editor.documentBaseURI.toAbsolute(modelUrl) : `models/${ model }/model${ suffix }.js`; 28579 ModelManager.load(model, url).catch(() => { 28580 modelLoadError(editor, url, model); 28581 }); 28582 } 28583 }; 28584 const getIconsUrlMetaFromUrl = editor => Optional.from(getIconsUrl(editor)).filter(isNotEmpty).map(url => ({ 28585 url, 28586 name: Optional.none() 28587 })); 28588 const getIconsUrlMetaFromName = (editor, name, suffix) => Optional.from(name).filter(name => isNotEmpty(name) && !IconManager.has(name)).map(name => ({ 28589 url: `${ editor.editorManager.baseURL }/icons/${ name }/icons${ suffix }.js`, 28590 name: Optional.some(name) 28591 })); 28592 const loadIcons = (scriptLoader, editor, suffix) => { 28593 const defaultIconsUrl = getIconsUrlMetaFromName(editor, 'default', suffix); 28594 const customIconsUrl = getIconsUrlMetaFromUrl(editor).orThunk(() => getIconsUrlMetaFromName(editor, getIconPackName(editor), '')); 28595 each$e(cat([ 28596 defaultIconsUrl, 28597 customIconsUrl 28598 ]), urlMeta => { 28599 scriptLoader.add(urlMeta.url).catch(() => { 28600 iconsLoadError(editor, urlMeta.url, urlMeta.name.getOrUndefined()); 28601 }); 28602 }); 28603 }; 28604 const loadPlugins = (editor, suffix) => { 28605 const loadPlugin = (name, url) => { 28606 PluginManager.load(name, url).catch(() => { 28607 pluginLoadError(editor, url, name); 28608 }); 28609 }; 28610 each$d(getExternalPlugins$1(editor), (url, name) => { 28611 loadPlugin(name, url); 28612 editor.options.set('plugins', getPlugins(editor).concat(name)); 28613 }); 28614 each$e(getPlugins(editor), plugin => { 28615 plugin = Tools.trim(plugin); 28616 if (plugin && !PluginManager.urls[plugin] && !hasSkipLoadPrefix(plugin)) { 28617 loadPlugin(plugin, `plugins/${ plugin }/plugin${ suffix }.js`); 28618 } 28619 }); 28620 }; 28621 const isThemeLoaded = editor => { 28622 const theme = getTheme(editor); 28623 return !isString(theme) || isNonNullable(ThemeManager.get(theme)); 28624 }; 28625 const isModelLoaded = editor => { 28626 const model = getModel(editor); 28627 return isNonNullable(ModelManager.get(model)); 28628 }; 28629 const loadScripts = (editor, suffix) => { 28630 const scriptLoader = ScriptLoader.ScriptLoader; 28631 const initEditor = () => { 28632 if (!editor.removed && isThemeLoaded(editor) && isModelLoaded(editor)) { 28633 init(editor); 28634 } 28635 }; 28636 loadTheme(editor, suffix); 28637 loadModel(editor, suffix); 28638 loadLanguage(scriptLoader, editor); 28639 loadIcons(scriptLoader, editor, suffix); 28640 loadPlugins(editor, suffix); 28641 scriptLoader.loadQueue().then(initEditor, initEditor); 28642 }; 28643 const getStyleSheetLoader = (element, editor) => instance.forElement(element, { 28644 contentCssCors: hasContentCssCors(editor), 28645 referrerPolicy: getReferrerPolicy(editor) 28646 }); 28647 const render = editor => { 28648 const id = editor.id; 28649 I18n.setCode(getLanguageCode(editor)); 28650 const readyHandler = () => { 28651 DOM$3.unbind(window, 'ready', readyHandler); 28652 editor.render(); 28653 }; 28654 if (!EventUtils.Event.domLoaded) { 28655 DOM$3.bind(window, 'ready', readyHandler); 28656 return; 28657 } 28658 if (!editor.getElement()) { 28659 return; 28660 } 28661 const element = SugarElement.fromDom(editor.getElement()); 28662 const snapshot = clone$4(element); 28663 editor.on('remove', () => { 28664 eachr(element.dom.attributes, attr => remove$a(element, attr.name)); 28665 setAll$1(element, snapshot); 28666 }); 28667 editor.ui.styleSheetLoader = getStyleSheetLoader(element, editor); 28668 if (!isInline(editor)) { 28669 editor.orgVisibility = editor.getElement().style.visibility; 28670 editor.getElement().style.visibility = 'hidden'; 28671 } else { 28672 editor.inline = true; 28673 } 28674 const form = editor.getElement().form || DOM$3.getParent(id, 'form'); 28675 if (form) { 28676 editor.formElement = form; 28677 if (hasHiddenInput(editor) && !isTextareaOrInput(editor.getElement())) { 28678 DOM$3.insertAfter(DOM$3.create('input', { 28679 type: 'hidden', 28680 name: id 28681 }), id); 28682 editor.hasHiddenInput = true; 28683 } 28684 editor.formEventDelegate = e => { 28685 editor.dispatch(e.type, e); 28686 }; 28687 DOM$3.bind(form, 'submit reset', editor.formEventDelegate); 28688 editor.on('reset', () => { 28689 editor.resetContent(); 28690 }); 28691 if (shouldPatchSubmit(editor) && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) { 28692 form._mceOldSubmit = form.submit; 28693 form.submit = () => { 28694 editor.editorManager.triggerSave(); 28695 editor.setDirty(false); 28696 return form._mceOldSubmit(form); 28697 }; 28698 } 28699 } 28700 editor.windowManager = WindowManager(editor); 28701 editor.notificationManager = NotificationManager(editor); 28702 if (isEncodingXml(editor)) { 28703 editor.on('GetContent', e => { 28704 if (e.save) { 28705 e.content = DOM$3.encode(e.content); 28706 } 28707 }); 28708 } 28709 if (shouldAddFormSubmitTrigger(editor)) { 28710 editor.on('submit', () => { 28711 if (editor.initialized) { 28712 editor.save(); 28713 } 28714 }); 28715 } 28716 if (shouldAddUnloadTrigger(editor)) { 28717 editor._beforeUnload = () => { 28718 if (editor.initialized && !editor.destroyed && !editor.isHidden()) { 28719 editor.save({ 28720 format: 'raw', 28721 no_events: true, 28722 set_dirty: false 28723 }); 28724 } 28725 }; 28726 editor.editorManager.on('BeforeUnload', editor._beforeUnload); 28727 } 28728 editor.editorManager.add(editor); 28729 loadScripts(editor, editor.suffix); 28730 }; 28731 28732 const setEditableRoot = (editor, state) => { 28733 if (editor._editableRoot !== state) { 28734 editor._editableRoot = state; 28735 if (!editor.readonly) { 28736 editor.getBody().contentEditable = String(editor.hasEditableRoot()); 28737 editor.nodeChanged(); 28738 } 28739 fireEditableRootStateChange(editor, state); 28740 } 28741 }; 28742 const hasEditableRoot = editor => editor._editableRoot; 28743 28744 const sectionResult = (sections, settings) => ({ 28745 sections: constant(sections), 28746 options: constant(settings) 28747 }); 28748 const deviceDetection = detect$2().deviceType; 28749 const isPhone = deviceDetection.isPhone(); 28750 const isTablet = deviceDetection.isTablet(); 28751 const normalizePlugins = plugins => { 28752 if (isNullable(plugins)) { 28753 return []; 28754 } else { 28755 const pluginNames = isArray$1(plugins) ? plugins : plugins.split(/[ ,]/); 28756 const trimmedPlugins = map$3(pluginNames, trim$3); 28757 return filter$5(trimmedPlugins, isNotEmpty); 28758 } 28759 }; 28760 const extractSections = (keys, options) => { 28761 const result = bifilter(options, (value, key) => { 28762 return contains$2(keys, key); 28763 }); 28764 return sectionResult(result.t, result.f); 28765 }; 28766 const getSection = (sectionResult, name, defaults = {}) => { 28767 const sections = sectionResult.sections(); 28768 const sectionOptions = get$a(sections, name).getOr({}); 28769 return Tools.extend({}, defaults, sectionOptions); 28770 }; 28771 const hasSection = (sectionResult, name) => { 28772 return has$2(sectionResult.sections(), name); 28773 }; 28774 const getSectionConfig = (sectionResult, name) => { 28775 return hasSection(sectionResult, name) ? sectionResult.sections()[name] : {}; 28776 }; 28777 const getMobileOverrideOptions = (mobileOptions, isPhone) => { 28778 const defaultMobileOptions = { 28779 table_grid: false, 28780 object_resizing: false, 28781 resize: false, 28782 toolbar_mode: get$a(mobileOptions, 'toolbar_mode').getOr('scrolling'), 28783 toolbar_sticky: false 28784 }; 28785 const defaultPhoneOptions = { menubar: false }; 28786 return { 28787 ...defaultMobileOptions, 28788 ...isPhone ? defaultPhoneOptions : {} 28789 }; 28790 }; 28791 const getExternalPlugins = (overrideOptions, options) => { 28792 var _a; 28793 const userDefinedExternalPlugins = (_a = options.external_plugins) !== null && _a !== void 0 ? _a : {}; 28794 if (overrideOptions && overrideOptions.external_plugins) { 28795 return Tools.extend({}, overrideOptions.external_plugins, userDefinedExternalPlugins); 28796 } else { 28797 return userDefinedExternalPlugins; 28798 } 28799 }; 28800 const combinePlugins = (forcedPlugins, plugins) => [ 28801 ...normalizePlugins(forcedPlugins), 28802 ...normalizePlugins(plugins) 28803 ]; 28804 const getPlatformPlugins = (isMobileDevice, sectionResult, desktopPlugins, mobilePlugins) => { 28805 if (isMobileDevice && hasSection(sectionResult, 'mobile')) { 28806 return mobilePlugins; 28807 } else { 28808 return desktopPlugins; 28809 } 28810 }; 28811 const processPlugins = (isMobileDevice, sectionResult, defaultOverrideOptions, options) => { 28812 const forcedPlugins = normalizePlugins(defaultOverrideOptions.forced_plugins); 28813 const desktopPlugins = normalizePlugins(options.plugins); 28814 const mobileConfig = getSectionConfig(sectionResult, 'mobile'); 28815 const mobilePlugins = mobileConfig.plugins ? normalizePlugins(mobileConfig.plugins) : desktopPlugins; 28816 const platformPlugins = getPlatformPlugins(isMobileDevice, sectionResult, desktopPlugins, mobilePlugins); 28817 const combinedPlugins = combinePlugins(forcedPlugins, platformPlugins); 28818 return Tools.extend(options, { 28819 forced_plugins: forcedPlugins, 28820 plugins: combinedPlugins 28821 }); 28822 }; 28823 const isOnMobile = (isMobileDevice, sectionResult) => { 28824 return isMobileDevice && hasSection(sectionResult, 'mobile'); 28825 }; 28826 const combineOptions = (isMobileDevice, isPhone, defaultOptions, defaultOverrideOptions, options) => { 28827 var _a; 28828 const deviceOverrideOptions = isMobileDevice ? { mobile: getMobileOverrideOptions((_a = options.mobile) !== null && _a !== void 0 ? _a : {}, isPhone) } : {}; 28829 const sectionResult = extractSections(['mobile'], deepMerge(deviceOverrideOptions, options)); 28830 const extendedOptions = Tools.extend(defaultOptions, defaultOverrideOptions, sectionResult.options(), isOnMobile(isMobileDevice, sectionResult) ? getSection(sectionResult, 'mobile') : {}, { external_plugins: getExternalPlugins(defaultOverrideOptions, sectionResult.options()) }); 28831 return processPlugins(isMobileDevice, sectionResult, defaultOverrideOptions, extendedOptions); 28832 }; 28833 const normalizeOptions = (defaultOverrideOptions, options) => combineOptions(isPhone || isTablet, isPhone, options, defaultOverrideOptions, options); 28834 28835 const addVisual = (editor, elm) => addVisual$1(editor, elm); 28836 28837 const registerExecCommands$3 = editor => { 28838 const toggleFormat = (name, value) => { 28839 editor.formatter.toggle(name, value); 28840 editor.nodeChanged(); 28841 }; 28842 const toggleAlign = align => () => { 28843 each$e('left,center,right,justify'.split(','), name => { 28844 if (align !== name) { 28845 editor.formatter.remove('align' + name); 28846 } 28847 }); 28848 if (align !== 'none') { 28849 toggleFormat('align' + align); 28850 } 28851 }; 28852 editor.editorCommands.addCommands({ 28853 JustifyLeft: toggleAlign('left'), 28854 JustifyCenter: toggleAlign('center'), 28855 JustifyRight: toggleAlign('right'), 28856 JustifyFull: toggleAlign('justify'), 28857 JustifyNone: toggleAlign('none') 28858 }); 28859 }; 28860 const registerQueryStateCommands$1 = editor => { 28861 const alignStates = name => () => { 28862 const selection = editor.selection; 28863 const nodes = selection.isCollapsed() ? [editor.dom.getParent(selection.getNode(), editor.dom.isBlock)] : selection.getSelectedBlocks(); 28864 return exists(nodes, node => isNonNullable(editor.formatter.matchNode(node, name))); 28865 }; 28866 editor.editorCommands.addCommands({ 28867 JustifyLeft: alignStates('alignleft'), 28868 JustifyCenter: alignStates('aligncenter'), 28869 JustifyRight: alignStates('alignright'), 28870 JustifyFull: alignStates('alignjustify') 28871 }, 'state'); 28872 }; 28873 const registerCommands$a = editor => { 28874 registerExecCommands$3(editor); 28875 registerQueryStateCommands$1(editor); 28876 }; 28877 28878 const registerCommands$9 = editor => { 28879 editor.editorCommands.addCommands({ 28880 'Cut,Copy,Paste': command => { 28881 const doc = editor.getDoc(); 28882 let failed; 28883 try { 28884 doc.execCommand(command); 28885 } catch (ex) { 28886 failed = true; 28887 } 28888 if (command === 'paste' && !doc.queryCommandEnabled(command)) { 28889 failed = true; 28890 } 28891 if (failed || !doc.queryCommandSupported(command)) { 28892 let msg = editor.translate(`Your browser doesn't support direct access to the clipboard. ` + 'Please use the Ctrl+X/C/V keyboard shortcuts instead.'); 28893 if (Env.os.isMacOS() || Env.os.isiOS()) { 28894 msg = msg.replace(/Ctrl\+/g, '\u2318+'); 28895 } 28896 editor.notificationManager.open({ 28897 text: msg, 28898 type: 'error' 28899 }); 28900 } 28901 } 28902 }); 28903 }; 28904 28905 const trimOrPadLeftRight = (dom, rng, html) => { 28906 const root = SugarElement.fromDom(dom.getRoot()); 28907 if (needsToBeNbspLeft(root, CaretPosition.fromRangeStart(rng))) { 28908 html = html.replace(/^ /, ' '); 28909 } else { 28910 html = html.replace(/^ /, ' '); 28911 } 28912 if (needsToBeNbspRight(root, CaretPosition.fromRangeEnd(rng))) { 28913 html = html.replace(/( | )(<br( \/)>)?$/, ' '); 28914 } else { 28915 html = html.replace(/ (<br( \/)?>)?$/, ' '); 28916 } 28917 return html; 28918 }; 28919 28920 const processValue$1 = value => { 28921 if (typeof value !== 'string') { 28922 const details = Tools.extend({ 28923 paste: value.paste, 28924 data: { paste: value.paste } 28925 }, value); 28926 return { 28927 content: value.content, 28928 details 28929 }; 28930 } 28931 return { 28932 content: value, 28933 details: {} 28934 }; 28935 }; 28936 const trimOrPad = (editor, value) => { 28937 const selection = editor.selection; 28938 const dom = editor.dom; 28939 if (/^ | $/.test(value)) { 28940 return trimOrPadLeftRight(dom, selection.getRng(), value); 28941 } else { 28942 return value; 28943 } 28944 }; 28945 const insertAtCaret = (editor, value) => { 28946 if (editor.selection.isEditable()) { 28947 const {content, details} = processValue$1(value); 28948 preProcessSetContent(editor, { 28949 ...details, 28950 content: trimOrPad(editor, content), 28951 format: 'html', 28952 set: false, 28953 selection: true 28954 }).each(args => { 28955 const insertedContent = insertContent$1(editor, args.content, details); 28956 postProcessSetContent(editor, insertedContent, args); 28957 editor.addVisual(); 28958 }); 28959 } 28960 }; 28961 28962 const registerCommands$8 = editor => { 28963 editor.editorCommands.addCommands({ 28964 mceCleanup: () => { 28965 const bm = editor.selection.getBookmark(); 28966 editor.setContent(editor.getContent()); 28967 editor.selection.moveToBookmark(bm); 28968 }, 28969 insertImage: (_command, _ui, value) => { 28970 insertAtCaret(editor, editor.dom.createHTML('img', { src: value })); 28971 }, 28972 insertHorizontalRule: () => { 28973 editor.execCommand('mceInsertContent', false, '<hr>'); 28974 }, 28975 insertText: (_command, _ui, value) => { 28976 insertAtCaret(editor, editor.dom.encode(value)); 28977 }, 28978 insertHTML: (_command, _ui, value) => { 28979 insertAtCaret(editor, value); 28980 }, 28981 mceInsertContent: (_command, _ui, value) => { 28982 insertAtCaret(editor, value); 28983 }, 28984 mceSetContent: (_command, _ui, value) => { 28985 editor.setContent(value); 28986 }, 28987 mceReplaceContent: (_command, _ui, value) => { 28988 editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, editor.selection.getContent({ format: 'text' }))); 28989 }, 28990 mceNewDocument: () => { 28991 editor.setContent(getNewDocumentContent(editor)); 28992 } 28993 }); 28994 }; 28995 28996 const legacyPropNames = { 28997 'font-size': 'size', 28998 'font-family': 'face' 28999 }; 29000 const isFont = isTag('font'); 29001 const getSpecifiedFontProp = (propName, rootElm, elm) => { 29002 const getProperty = elm => getRaw(elm, propName).orThunk(() => { 29003 if (isFont(elm)) { 29004 return get$a(legacyPropNames, propName).bind(legacyPropName => getOpt(elm, legacyPropName)); 29005 } else { 29006 return Optional.none(); 29007 } 29008 }); 29009 const isRoot = elm => eq(SugarElement.fromDom(rootElm), elm); 29010 return closest$1(SugarElement.fromDom(elm), elm => getProperty(elm), isRoot); 29011 }; 29012 const normalizeFontFamily = fontFamily => fontFamily.replace(/[\'\"\\]/g, '').replace(/,\s+/g, ','); 29013 const getComputedFontProp = (propName, elm) => Optional.from(DOMUtils.DOM.getStyle(elm, propName, true)); 29014 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(''); 29015 const getFontSize = getFontProp('font-size'); 29016 const getFontFamily = compose(normalizeFontFamily, getFontProp('font-family')); 29017 29018 const findFirstCaretElement = editor => firstPositionIn(editor.getBody()).bind(caret => { 29019 const container = caret.container(); 29020 return Optional.from(isText$a(container) ? container.parentNode : container); 29021 }); 29022 const getCaretElement = editor => Optional.from(editor.selection.getRng()).bind(rng => { 29023 const root = editor.getBody(); 29024 const atStartOfNode = rng.startContainer === root && rng.startOffset === 0; 29025 return atStartOfNode ? Optional.none() : Optional.from(editor.selection.getStart(true)); 29026 }); 29027 const bindRange = (editor, binder) => getCaretElement(editor).orThunk(curry(findFirstCaretElement, editor)).map(SugarElement.fromDom).filter(isElement$7).bind(binder); 29028 const mapRange = (editor, mapper) => bindRange(editor, compose1(Optional.some, mapper)); 29029 29030 const fromFontSizeNumber = (editor, value) => { 29031 if (/^[0-9.]+$/.test(value)) { 29032 const fontSizeNumber = parseInt(value, 10); 29033 if (fontSizeNumber >= 1 && fontSizeNumber <= 7) { 29034 const fontSizes = getFontStyleValues(editor); 29035 const fontClasses = getFontSizeClasses(editor); 29036 if (fontClasses.length > 0) { 29037 return fontClasses[fontSizeNumber - 1] || value; 29038 } else { 29039 return fontSizes[fontSizeNumber - 1] || value; 29040 } 29041 } else { 29042 return value; 29043 } 29044 } else { 29045 return value; 29046 } 29047 }; 29048 const normalizeFontNames = font => { 29049 const fonts = font.split(/\s*,\s*/); 29050 return map$3(fonts, font => { 29051 if (font.indexOf(' ') !== -1 && !(startsWith(font, '"') || startsWith(font, `'`))) { 29052 return `'${ font }'`; 29053 } else { 29054 return font; 29055 } 29056 }).join(','); 29057 }; 29058 const fontNameAction = (editor, value) => { 29059 const font = fromFontSizeNumber(editor, value); 29060 editor.formatter.toggle('fontname', { value: normalizeFontNames(font) }); 29061 editor.nodeChanged(); 29062 }; 29063 const fontNameQuery = editor => mapRange(editor, elm => getFontFamily(editor.getBody(), elm.dom)).getOr(''); 29064 const fontSizeAction = (editor, value) => { 29065 editor.formatter.toggle('fontsize', { value: fromFontSizeNumber(editor, value) }); 29066 editor.nodeChanged(); 29067 }; 29068 const fontSizeQuery = editor => mapRange(editor, elm => getFontSize(editor.getBody(), elm.dom)).getOr(''); 29069 29070 const lineHeightQuery = editor => mapRange(editor, elm => { 29071 const root = SugarElement.fromDom(editor.getBody()); 29072 const specifiedStyle = closest$1(elm, elm => getRaw(elm, 'line-height'), curry(eq, root)); 29073 const computedStyle = () => { 29074 const lineHeight = parseFloat(get$7(elm, 'line-height')); 29075 const fontSize = parseFloat(get$7(elm, 'font-size')); 29076 return String(lineHeight / fontSize); 29077 }; 29078 return specifiedStyle.getOrThunk(computedStyle); 29079 }).getOr(''); 29080 const lineHeightAction = (editor, lineHeight) => { 29081 editor.formatter.toggle('lineheight', { value: String(lineHeight) }); 29082 editor.nodeChanged(); 29083 }; 29084 29085 const registerExecCommands$2 = editor => { 29086 const toggleFormat = (name, value) => { 29087 editor.formatter.toggle(name, value); 29088 editor.nodeChanged(); 29089 }; 29090 editor.editorCommands.addCommands({ 29091 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': command => { 29092 toggleFormat(command); 29093 }, 29094 'ForeColor,HiliteColor': (command, _ui, value) => { 29095 toggleFormat(command, { value }); 29096 }, 29097 'BackColor': (_command, _ui, value) => { 29098 toggleFormat('hilitecolor', { value }); 29099 }, 29100 'FontName': (_command, _ui, value) => { 29101 fontNameAction(editor, value); 29102 }, 29103 'FontSize': (_command, _ui, value) => { 29104 fontSizeAction(editor, value); 29105 }, 29106 'LineHeight': (_command, _ui, value) => { 29107 lineHeightAction(editor, value); 29108 }, 29109 'Lang': (command, _ui, lang) => { 29110 var _a; 29111 toggleFormat(command, { 29112 value: lang.code, 29113 customValue: (_a = lang.customCode) !== null && _a !== void 0 ? _a : null 29114 }); 29115 }, 29116 'RemoveFormat': command => { 29117 editor.formatter.remove(command); 29118 }, 29119 'mceBlockQuote': () => { 29120 toggleFormat('blockquote'); 29121 }, 29122 'FormatBlock': (_command, _ui, value) => { 29123 toggleFormat(isString(value) ? value : 'p'); 29124 }, 29125 'mceToggleFormat': (_command, _ui, value) => { 29126 toggleFormat(value); 29127 } 29128 }); 29129 }; 29130 const registerQueryValueCommands = editor => { 29131 const isFormatMatch = name => editor.formatter.match(name); 29132 editor.editorCommands.addCommands({ 29133 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': command => isFormatMatch(command), 29134 'mceBlockQuote': () => isFormatMatch('blockquote') 29135 }, 'state'); 29136 editor.editorCommands.addQueryValueHandler('FontName', () => fontNameQuery(editor)); 29137 editor.editorCommands.addQueryValueHandler('FontSize', () => fontSizeQuery(editor)); 29138 editor.editorCommands.addQueryValueHandler('LineHeight', () => lineHeightQuery(editor)); 29139 }; 29140 const registerCommands$7 = editor => { 29141 registerExecCommands$2(editor); 29142 registerQueryValueCommands(editor); 29143 }; 29144 29145 const registerCommands$6 = editor => { 29146 editor.editorCommands.addCommands({ 29147 mceAddUndoLevel: () => { 29148 editor.undoManager.add(); 29149 }, 29150 mceEndUndoLevel: () => { 29151 editor.undoManager.add(); 29152 }, 29153 Undo: () => { 29154 editor.undoManager.undo(); 29155 }, 29156 Redo: () => { 29157 editor.undoManager.redo(); 29158 } 29159 }); 29160 }; 29161 29162 const registerCommands$5 = editor => { 29163 editor.editorCommands.addCommands({ 29164 Indent: () => { 29165 indent(editor); 29166 }, 29167 Outdent: () => { 29168 outdent(editor); 29169 } 29170 }); 29171 editor.editorCommands.addCommands({ Outdent: () => canOutdent(editor) }, 'state'); 29172 }; 29173 29174 const registerCommands$4 = editor => { 29175 const applyLinkToSelection = (_command, _ui, value) => { 29176 const linkDetails = isString(value) ? { href: value } : value; 29177 const anchor = editor.dom.getParent(editor.selection.getNode(), 'a'); 29178 if (isObject(linkDetails) && isString(linkDetails.href)) { 29179 linkDetails.href = linkDetails.href.replace(/ /g, '%20'); 29180 if (!anchor || !linkDetails.href) { 29181 editor.formatter.remove('link'); 29182 } 29183 if (linkDetails.href) { 29184 editor.formatter.apply('link', linkDetails, anchor); 29185 } 29186 } 29187 }; 29188 editor.editorCommands.addCommands({ 29189 unlink: () => { 29190 if (editor.selection.isEditable()) { 29191 if (editor.selection.isCollapsed()) { 29192 const elm = editor.dom.getParent(editor.selection.getStart(), 'a'); 29193 if (elm) { 29194 editor.dom.remove(elm, true); 29195 } 29196 return; 29197 } 29198 editor.formatter.remove('link'); 29199 } 29200 }, 29201 mceInsertLink: applyLinkToSelection, 29202 createLink: applyLinkToSelection 29203 }); 29204 }; 29205 29206 const registerExecCommands$1 = editor => { 29207 editor.editorCommands.addCommands({ 29208 'InsertUnorderedList,InsertOrderedList': command => { 29209 editor.getDoc().execCommand(command); 29210 const listElm = editor.dom.getParent(editor.selection.getNode(), 'ol,ul'); 29211 if (listElm) { 29212 const listParent = listElm.parentNode; 29213 if (listParent && /^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) { 29214 const bm = editor.selection.getBookmark(); 29215 editor.dom.split(listParent, listElm); 29216 editor.selection.moveToBookmark(bm); 29217 } 29218 } 29219 } 29220 }); 29221 }; 29222 const registerQueryStateCommands = editor => { 29223 editor.editorCommands.addCommands({ 29224 'InsertUnorderedList,InsertOrderedList': command => { 29225 const list = editor.dom.getParent(editor.selection.getNode(), 'ul,ol'); 29226 return list && (command === 'insertunorderedlist' && list.tagName === 'UL' || command === 'insertorderedlist' && list.tagName === 'OL'); 29227 } 29228 }, 'state'); 29229 }; 29230 const registerCommands$3 = editor => { 29231 registerExecCommands$1(editor); 29232 registerQueryStateCommands(editor); 29233 }; 29234 29235 const registerCommands$2 = editor => { 29236 editor.editorCommands.addCommands({ 29237 insertParagraph: () => { 29238 insertBreak(blockbreak, editor); 29239 }, 29240 mceInsertNewLine: (_command, _ui, value) => { 29241 insert(editor, value); 29242 }, 29243 InsertLineBreak: (_command, _ui, _value) => { 29244 insertBreak(linebreak, editor); 29245 } 29246 }); 29247 }; 29248 29249 const registerCommands$1 = editor => { 29250 editor.editorCommands.addCommands({ 29251 mceSelectNodeDepth: (_command, _ui, value) => { 29252 let counter = 0; 29253 editor.dom.getParent(editor.selection.getNode(), node => { 29254 if (isElement$6(node) && counter++ === value) { 29255 editor.selection.select(node); 29256 return false; 29257 } else { 29258 return true; 29259 } 29260 }, editor.getBody()); 29261 }, 29262 mceSelectNode: (_command, _ui, value) => { 29263 editor.selection.select(value); 29264 }, 29265 selectAll: () => { 29266 const editingHost = editor.dom.getParent(editor.selection.getStart(), isContentEditableTrue$3); 29267 if (editingHost) { 29268 const rng = editor.dom.createRng(); 29269 rng.selectNodeContents(editingHost); 29270 editor.selection.setRng(rng); 29271 } 29272 } 29273 }); 29274 }; 29275 29276 const registerExecCommands = editor => { 29277 editor.editorCommands.addCommands({ 29278 mceRemoveNode: (_command, _ui, value) => { 29279 const node = value !== null && value !== void 0 ? value : editor.selection.getNode(); 29280 if (node !== editor.getBody()) { 29281 const bm = editor.selection.getBookmark(); 29282 editor.dom.remove(node, true); 29283 editor.selection.moveToBookmark(bm); 29284 } 29285 }, 29286 mcePrint: () => { 29287 editor.getWin().print(); 29288 }, 29289 mceFocus: (_command, _ui, value) => { 29290 focus(editor, value === true); 29291 }, 29292 mceToggleVisualAid: () => { 29293 editor.hasVisual = !editor.hasVisual; 29294 editor.addVisual(); 29295 } 29296 }); 29297 }; 29298 const registerCommands = editor => { 29299 registerCommands$a(editor); 29300 registerCommands$9(editor); 29301 registerCommands$6(editor); 29302 registerCommands$1(editor); 29303 registerCommands$8(editor); 29304 registerCommands$4(editor); 29305 registerCommands$5(editor); 29306 registerCommands$2(editor); 29307 registerCommands$3(editor); 29308 registerCommands$7(editor); 29309 registerExecCommands(editor); 29310 }; 29311 29312 const selectionSafeCommands = ['toggleview']; 29313 const isSelectionSafeCommand = command => contains$2(selectionSafeCommands, command.toLowerCase()); 29314 class EditorCommands { 29315 constructor(editor) { 29316 this.commands = { 29317 state: {}, 29318 exec: {}, 29319 value: {} 29320 }; 29321 this.editor = editor; 29322 } 29323 execCommand(command, ui = false, value, args) { 29324 const editor = this.editor; 29325 const lowerCaseCommand = command.toLowerCase(); 29326 const skipFocus = args === null || args === void 0 ? void 0 : args.skip_focus; 29327 if (editor.removed) { 29328 return false; 29329 } 29330 if (lowerCaseCommand !== 'mcefocus') { 29331 if (!/^(mceAddUndoLevel|mceEndUndoLevel)$/i.test(lowerCaseCommand) && !skipFocus) { 29332 editor.focus(); 29333 } else { 29334 restore(editor); 29335 } 29336 } 29337 const eventArgs = editor.dispatch('BeforeExecCommand', { 29338 command, 29339 ui, 29340 value 29341 }); 29342 if (eventArgs.isDefaultPrevented()) { 29343 return false; 29344 } 29345 const func = this.commands.exec[lowerCaseCommand]; 29346 if (isFunction(func)) { 29347 func(lowerCaseCommand, ui, value); 29348 editor.dispatch('ExecCommand', { 29349 command, 29350 ui, 29351 value 29352 }); 29353 return true; 29354 } 29355 return false; 29356 } 29357 queryCommandState(command) { 29358 if (!isSelectionSafeCommand(command) && this.editor.quirks.isHidden() || this.editor.removed) { 29359 return false; 29360 } 29361 const lowerCaseCommand = command.toLowerCase(); 29362 const func = this.commands.state[lowerCaseCommand]; 29363 if (isFunction(func)) { 29364 return func(lowerCaseCommand); 29365 } 29366 return false; 29367 } 29368 queryCommandValue(command) { 29369 if (!isSelectionSafeCommand(command) && this.editor.quirks.isHidden() || this.editor.removed) { 29370 return ''; 29371 } 29372 const lowerCaseCommand = command.toLowerCase(); 29373 const func = this.commands.value[lowerCaseCommand]; 29374 if (isFunction(func)) { 29375 return func(lowerCaseCommand); 29376 } 29377 return ''; 29378 } 29379 addCommands(commandList, type = 'exec') { 29380 const commands = this.commands; 29381 each$d(commandList, (callback, command) => { 29382 each$e(command.toLowerCase().split(','), command => { 29383 commands[type][command] = callback; 29384 }); 29385 }); 29386 } 29387 addCommand(command, callback, scope) { 29388 const lowerCaseCommand = command.toLowerCase(); 29389 this.commands.exec[lowerCaseCommand] = (_command, ui, value) => callback.call(scope !== null && scope !== void 0 ? scope : this.editor, ui, value); 29390 } 29391 queryCommandSupported(command) { 29392 const lowerCaseCommand = command.toLowerCase(); 29393 if (this.commands.exec[lowerCaseCommand]) { 29394 return true; 29395 } else { 29396 return false; 29397 } 29398 } 29399 addQueryStateHandler(command, callback, scope) { 29400 this.commands.state[command.toLowerCase()] = () => callback.call(scope !== null && scope !== void 0 ? scope : this.editor); 29401 } 29402 addQueryValueHandler(command, callback, scope) { 29403 this.commands.value[command.toLowerCase()] = () => callback.call(scope !== null && scope !== void 0 ? scope : this.editor); 29404 } 29405 } 29406 29407 const internalContentEditableAttr = 'data-mce-contenteditable'; 29408 const toggleClass = (elm, cls, state) => { 29409 if (has(elm, cls) && !state) { 29410 remove$7(elm, cls); 29411 } else if (state) { 29412 add$2(elm, cls); 29413 } 29414 }; 29415 const setEditorCommandState = (editor, cmd, state) => { 29416 try { 29417 editor.getDoc().execCommand(cmd, false, String(state)); 29418 } catch (ex) { 29419 } 29420 }; 29421 const setContentEditable = (elm, state) => { 29422 elm.dom.contentEditable = state ? 'true' : 'false'; 29423 }; 29424 const switchOffContentEditableTrue = elm => { 29425 each$e(descendants(elm, '*[contenteditable="true"]'), elm => { 29426 set$3(elm, internalContentEditableAttr, 'true'); 29427 setContentEditable(elm, false); 29428 }); 29429 }; 29430 const switchOnContentEditableTrue = elm => { 29431 each$e(descendants(elm, `*[${ internalContentEditableAttr }="true"]`), elm => { 29432 remove$a(elm, internalContentEditableAttr); 29433 setContentEditable(elm, true); 29434 }); 29435 }; 29436 const removeFakeSelection = editor => { 29437 Optional.from(editor.selection.getNode()).each(elm => { 29438 elm.removeAttribute('data-mce-selected'); 29439 }); 29440 }; 29441 const restoreFakeSelection = editor => { 29442 editor.selection.setRng(editor.selection.getRng()); 29443 }; 29444 const toggleReadOnly = (editor, state) => { 29445 const body = SugarElement.fromDom(editor.getBody()); 29446 toggleClass(body, 'mce-content-readonly', state); 29447 if (state) { 29448 editor.selection.controlSelection.hideResizeRect(); 29449 editor._selectionOverrides.hideFakeCaret(); 29450 removeFakeSelection(editor); 29451 editor.readonly = true; 29452 setContentEditable(body, false); 29453 switchOffContentEditableTrue(body); 29454 } else { 29455 editor.readonly = false; 29456 if (editor.hasEditableRoot()) { 29457 setContentEditable(body, true); 29458 } 29459 switchOnContentEditableTrue(body); 29460 setEditorCommandState(editor, 'StyleWithCSS', false); 29461 setEditorCommandState(editor, 'enableInlineTableEditing', false); 29462 setEditorCommandState(editor, 'enableObjectResizing', false); 29463 if (hasEditorOrUiFocus(editor)) { 29464 editor.focus(); 29465 } 29466 restoreFakeSelection(editor); 29467 editor.nodeChanged(); 29468 } 29469 }; 29470 const isReadOnly = editor => editor.readonly; 29471 const registerFilters = editor => { 29472 editor.parser.addAttributeFilter('contenteditable', nodes => { 29473 if (isReadOnly(editor)) { 29474 each$e(nodes, node => { 29475 node.attr(internalContentEditableAttr, node.attr('contenteditable')); 29476 node.attr('contenteditable', 'false'); 29477 }); 29478 } 29479 }); 29480 editor.serializer.addAttributeFilter(internalContentEditableAttr, nodes => { 29481 if (isReadOnly(editor)) { 29482 each$e(nodes, node => { 29483 node.attr('contenteditable', node.attr(internalContentEditableAttr)); 29484 }); 29485 } 29486 }); 29487 editor.serializer.addTempAttr(internalContentEditableAttr); 29488 }; 29489 const registerReadOnlyContentFilters = editor => { 29490 if (editor.serializer) { 29491 registerFilters(editor); 29492 } else { 29493 editor.on('PreInit', () => { 29494 registerFilters(editor); 29495 }); 29496 } 29497 }; 29498 const isClickEvent = e => e.type === 'click'; 29499 const allowedEvents = ['copy']; 29500 const isReadOnlyAllowedEvent = e => contains$2(allowedEvents, e.type); 29501 const getAnchorHrefOpt = (editor, elm) => { 29502 const isRoot = elm => eq(elm, SugarElement.fromDom(editor.getBody())); 29503 return closest$3(elm, 'a', isRoot).bind(a => getOpt(a, 'href')); 29504 }; 29505 const processReadonlyEvents = (editor, e) => { 29506 if (isClickEvent(e) && !VK.metaKeyPressed(e)) { 29507 const elm = SugarElement.fromDom(e.target); 29508 getAnchorHrefOpt(editor, elm).each(href => { 29509 e.preventDefault(); 29510 if (/^#/.test(href)) { 29511 const targetEl = editor.dom.select(`${ href },[name="${ removeLeading(href, '#') }"]`); 29512 if (targetEl.length) { 29513 editor.selection.scrollIntoView(targetEl[0], true); 29514 } 29515 } else { 29516 window.open(href, '_blank', 'rel=noopener noreferrer,menubar=yes,toolbar=yes,location=yes,status=yes,resizable=yes,scrollbars=yes'); 29517 } 29518 }); 29519 } else if (isReadOnlyAllowedEvent(e)) { 29520 editor.dispatch(e.type, e); 29521 } 29522 }; 29523 const registerReadOnlySelectionBlockers = editor => { 29524 editor.on('ShowCaret', e => { 29525 if (isReadOnly(editor)) { 29526 e.preventDefault(); 29527 } 29528 }); 29529 editor.on('ObjectSelected', e => { 29530 if (isReadOnly(editor)) { 29531 e.preventDefault(); 29532 } 29533 }); 29534 }; 29535 29536 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', ' '); 29537 class EventDispatcher { 29538 static isNative(name) { 29539 return !!nativeEvents[name.toLowerCase()]; 29540 } 29541 constructor(settings) { 29542 this.bindings = {}; 29543 this.settings = settings || {}; 29544 this.scope = this.settings.scope || this; 29545 this.toggleEvent = this.settings.toggleEvent || never; 29546 } 29547 fire(name, args) { 29548 return this.dispatch(name, args); 29549 } 29550 dispatch(name, args) { 29551 const lcName = name.toLowerCase(); 29552 const event = normalize$3(lcName, args !== null && args !== void 0 ? args : {}, this.scope); 29553 if (this.settings.beforeFire) { 29554 this.settings.beforeFire(event); 29555 } 29556 const handlers = this.bindings[lcName]; 29557 if (handlers) { 29558 for (let i = 0, l = handlers.length; i < l; i++) { 29559 const callback = handlers[i]; 29560 if (callback.removed) { 29561 continue; 29562 } 29563 if (callback.once) { 29564 this.off(lcName, callback.func); 29565 } 29566 if (event.isImmediatePropagationStopped()) { 29567 return event; 29568 } 29569 if (callback.func.call(this.scope, event) === false) { 29570 event.preventDefault(); 29571 return event; 29572 } 29573 } 29574 } 29575 return event; 29576 } 29577 on(name, callback, prepend, extra) { 29578 if (callback === false) { 29579 callback = never; 29580 } 29581 if (callback) { 29582 const wrappedCallback = { 29583 func: callback, 29584 removed: false 29585 }; 29586 if (extra) { 29587 Tools.extend(wrappedCallback, extra); 29588 } 29589 const names = name.toLowerCase().split(' '); 29590 let i = names.length; 29591 while (i--) { 29592 const currentName = names[i]; 29593 let handlers = this.bindings[currentName]; 29594 if (!handlers) { 29595 handlers = []; 29596 this.toggleEvent(currentName, true); 29597 } 29598 if (prepend) { 29599 handlers = [ 29600 wrappedCallback, 29601 ...handlers 29602 ]; 29603 } else { 29604 handlers = [ 29605 ...handlers, 29606 wrappedCallback 29607 ]; 29608 } 29609 this.bindings[currentName] = handlers; 29610 } 29611 } 29612 return this; 29613 } 29614 off(name, callback) { 29615 if (name) { 29616 const names = name.toLowerCase().split(' '); 29617 let i = names.length; 29618 while (i--) { 29619 const currentName = names[i]; 29620 let handlers = this.bindings[currentName]; 29621 if (!currentName) { 29622 each$d(this.bindings, (_value, bindingName) => { 29623 this.toggleEvent(bindingName, false); 29624 delete this.bindings[bindingName]; 29625 }); 29626 return this; 29627 } 29628 if (handlers) { 29629 if (!callback) { 29630 handlers.length = 0; 29631 } else { 29632 const filteredHandlers = partition$2(handlers, handler => handler.func === callback); 29633 handlers = filteredHandlers.fail; 29634 this.bindings[currentName] = handlers; 29635 each$e(filteredHandlers.pass, handler => { 29636 handler.removed = true; 29637 }); 29638 } 29639 if (!handlers.length) { 29640 this.toggleEvent(name, false); 29641 delete this.bindings[currentName]; 29642 } 29643 } 29644 } 29645 } else { 29646 each$d(this.bindings, (_value, name) => { 29647 this.toggleEvent(name, false); 29648 }); 29649 this.bindings = {}; 29650 } 29651 return this; 29652 } 29653 once(name, callback, prepend) { 29654 return this.on(name, callback, prepend, { once: true }); 29655 } 29656 has(name) { 29657 name = name.toLowerCase(); 29658 const binding = this.bindings[name]; 29659 return !(!binding || binding.length === 0); 29660 } 29661 } 29662 29663 const getEventDispatcher = obj => { 29664 if (!obj._eventDispatcher) { 29665 obj._eventDispatcher = new EventDispatcher({ 29666 scope: obj, 29667 toggleEvent: (name, state) => { 29668 if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) { 29669 obj.toggleNativeEvent(name, state); 29670 } 29671 } 29672 }); 29673 } 29674 return obj._eventDispatcher; 29675 }; 29676 const Observable = { 29677 fire(name, args, bubble) { 29678 return this.dispatch(name, args, bubble); 29679 }, 29680 dispatch(name, args, bubble) { 29681 const self = this; 29682 if (self.removed && name !== 'remove' && name !== 'detach') { 29683 return normalize$3(name.toLowerCase(), args !== null && args !== void 0 ? args : {}, self); 29684 } 29685 const dispatcherArgs = getEventDispatcher(self).dispatch(name, args); 29686 if (bubble !== false && self.parent) { 29687 let parent = self.parent(); 29688 while (parent && !dispatcherArgs.isPropagationStopped()) { 29689 parent.dispatch(name, dispatcherArgs, false); 29690 parent = parent.parent ? parent.parent() : undefined; 29691 } 29692 } 29693 return dispatcherArgs; 29694 }, 29695 on(name, callback, prepend) { 29696 return getEventDispatcher(this).on(name, callback, prepend); 29697 }, 29698 off(name, callback) { 29699 return getEventDispatcher(this).off(name, callback); 29700 }, 29701 once(name, callback) { 29702 return getEventDispatcher(this).once(name, callback); 29703 }, 29704 hasEventListeners(name) { 29705 return getEventDispatcher(this).has(name); 29706 } 29707 }; 29708 29709 const DOM$2 = DOMUtils.DOM; 29710 let customEventRootDelegates; 29711 const getEventTarget = (editor, eventName) => { 29712 if (eventName === 'selectionchange') { 29713 return editor.getDoc(); 29714 } 29715 if (!editor.inline && /^(?:mouse|touch|click|contextmenu|drop|dragover|dragend)/.test(eventName)) { 29716 return editor.getDoc().documentElement; 29717 } 29718 const eventRoot = getEventRoot(editor); 29719 if (eventRoot) { 29720 if (!editor.eventRoot) { 29721 editor.eventRoot = DOM$2.select(eventRoot)[0]; 29722 } 29723 return editor.eventRoot; 29724 } 29725 return editor.getBody(); 29726 }; 29727 const isListening = editor => !editor.hidden && !isReadOnly(editor); 29728 const fireEvent = (editor, eventName, e) => { 29729 if (isListening(editor)) { 29730 editor.dispatch(eventName, e); 29731 } else if (isReadOnly(editor)) { 29732 processReadonlyEvents(editor, e); 29733 } 29734 }; 29735 const bindEventDelegate = (editor, eventName) => { 29736 if (!editor.delegates) { 29737 editor.delegates = {}; 29738 } 29739 if (editor.delegates[eventName] || editor.removed) { 29740 return; 29741 } 29742 const eventRootElm = getEventTarget(editor, eventName); 29743 if (getEventRoot(editor)) { 29744 if (!customEventRootDelegates) { 29745 customEventRootDelegates = {}; 29746 editor.editorManager.on('removeEditor', () => { 29747 if (!editor.editorManager.activeEditor) { 29748 if (customEventRootDelegates) { 29749 each$d(customEventRootDelegates, (_value, name) => { 29750 editor.dom.unbind(getEventTarget(editor, name)); 29751 }); 29752 customEventRootDelegates = null; 29753 } 29754 } 29755 }); 29756 } 29757 if (customEventRootDelegates[eventName]) { 29758 return; 29759 } 29760 const delegate = e => { 29761 const target = e.target; 29762 const editors = editor.editorManager.get(); 29763 let i = editors.length; 29764 while (i--) { 29765 const body = editors[i].getBody(); 29766 if (body === target || DOM$2.isChildOf(target, body)) { 29767 fireEvent(editors[i], eventName, e); 29768 } 29769 } 29770 }; 29771 customEventRootDelegates[eventName] = delegate; 29772 DOM$2.bind(eventRootElm, eventName, delegate); 29773 } else { 29774 const delegate = e => { 29775 fireEvent(editor, eventName, e); 29776 }; 29777 DOM$2.bind(eventRootElm, eventName, delegate); 29778 editor.delegates[eventName] = delegate; 29779 } 29780 }; 29781 const EditorObservable = { 29782 ...Observable, 29783 bindPendingEventDelegates() { 29784 const self = this; 29785 Tools.each(self._pendingNativeEvents, name => { 29786 bindEventDelegate(self, name); 29787 }); 29788 }, 29789 toggleNativeEvent(name, state) { 29790 const self = this; 29791 if (name === 'focus' || name === 'blur') { 29792 return; 29793 } 29794 if (self.removed) { 29795 return; 29796 } 29797 if (state) { 29798 if (self.initialized) { 29799 bindEventDelegate(self, name); 29800 } else { 29801 if (!self._pendingNativeEvents) { 29802 self._pendingNativeEvents = [name]; 29803 } else { 29804 self._pendingNativeEvents.push(name); 29805 } 29806 } 29807 } else if (self.initialized && self.delegates) { 29808 self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]); 29809 delete self.delegates[name]; 29810 } 29811 }, 29812 unbindAllNativeEvents() { 29813 const self = this; 29814 const body = self.getBody(); 29815 const dom = self.dom; 29816 if (self.delegates) { 29817 each$d(self.delegates, (value, name) => { 29818 self.dom.unbind(getEventTarget(self, name), name, value); 29819 }); 29820 delete self.delegates; 29821 } 29822 if (!self.inline && body && dom) { 29823 body.onload = null; 29824 dom.unbind(self.getWin()); 29825 dom.unbind(self.getDoc()); 29826 } 29827 if (dom) { 29828 dom.unbind(body); 29829 dom.unbind(self.getContainer()); 29830 } 29831 } 29832 }; 29833 29834 const stringListProcessor = value => { 29835 if (isString(value)) { 29836 return { 29837 value: value.split(/[ ,]/), 29838 valid: true 29839 }; 29840 } else if (isArrayOf(value, isString)) { 29841 return { 29842 value, 29843 valid: true 29844 }; 29845 } else { 29846 return { 29847 valid: false, 29848 message: `The value must be a string[] or a comma/space separated string.` 29849 }; 29850 } 29851 }; 29852 const getBuiltInProcessor = type => { 29853 const validator = (() => { 29854 switch (type) { 29855 case 'array': 29856 return isArray$1; 29857 case 'boolean': 29858 return isBoolean; 29859 case 'function': 29860 return isFunction; 29861 case 'number': 29862 return isNumber; 29863 case 'object': 29864 return isObject; 29865 case 'string': 29866 return isString; 29867 case 'string[]': 29868 return stringListProcessor; 29869 case 'object[]': 29870 return val => isArrayOf(val, isObject); 29871 case 'regexp': 29872 return val => is$4(val, RegExp); 29873 default: 29874 return always; 29875 } 29876 })(); 29877 return value => processValue(value, validator, `The value must be a ${ type }.`); 29878 }; 29879 const isBuiltInSpec = spec => isString(spec.processor); 29880 const getErrorMessage = (message, result) => { 29881 const additionalText = isEmpty$3(result.message) ? '' : `. ${ result.message }`; 29882 return message + additionalText; 29883 }; 29884 const isValidResult = result => result.valid; 29885 const processValue = (value, processor, message = '') => { 29886 const result = processor(value); 29887 if (isBoolean(result)) { 29888 return result ? { 29889 value: value, 29890 valid: true 29891 } : { 29892 valid: false, 29893 message 29894 }; 29895 } else { 29896 return result; 29897 } 29898 }; 29899 const processDefaultValue = (name, defaultValue, processor) => { 29900 if (!isUndefined(defaultValue)) { 29901 const result = processValue(defaultValue, processor); 29902 if (isValidResult(result)) { 29903 return result.value; 29904 } else { 29905 console.error(getErrorMessage(`Invalid default value passed for the "${ name }" option`, result)); 29906 } 29907 } 29908 return undefined; 29909 }; 29910 const create$5 = (editor, initialOptions) => { 29911 const registry = {}; 29912 const values = {}; 29913 const setValue = (name, value, processor) => { 29914 const result = processValue(value, processor); 29915 if (isValidResult(result)) { 29916 values[name] = result.value; 29917 return true; 29918 } else { 29919 console.warn(getErrorMessage(`Invalid value passed for the ${ name } option`, result)); 29920 return false; 29921 } 29922 }; 29923 const register = (name, spec) => { 29924 const processor = isBuiltInSpec(spec) ? getBuiltInProcessor(spec.processor) : spec.processor; 29925 const defaultValue = processDefaultValue(name, spec.default, processor); 29926 registry[name] = { 29927 ...spec, 29928 default: defaultValue, 29929 processor 29930 }; 29931 const initValue = get$a(values, name).orThunk(() => get$a(initialOptions, name)); 29932 initValue.each(value => setValue(name, value, processor)); 29933 }; 29934 const isRegistered = name => has$2(registry, name); 29935 const get = name => get$a(values, name).orThunk(() => get$a(registry, name).map(spec => spec.default)).getOrUndefined(); 29936 const set = (name, value) => { 29937 if (!isRegistered(name)) { 29938 console.warn(`"${ name }" is not a registered option. Ensure the option has been registered before setting a value.`); 29939 return false; 29940 } else { 29941 const spec = registry[name]; 29942 if (spec.immutable) { 29943 console.error(`"${ name }" is an immutable option and cannot be updated`); 29944 return false; 29945 } else { 29946 return setValue(name, value, spec.processor); 29947 } 29948 } 29949 }; 29950 const unset = name => { 29951 const registered = isRegistered(name); 29952 if (registered) { 29953 delete values[name]; 29954 } 29955 return registered; 29956 }; 29957 const isSet = name => has$2(values, name); 29958 return { 29959 register, 29960 isRegistered, 29961 get, 29962 set, 29963 unset, 29964 isSet 29965 }; 29966 }; 29967 29968 const defaultModes = [ 29969 'design', 29970 'readonly' 29971 ]; 29972 const switchToMode = (editor, activeMode, availableModes, mode) => { 29973 const oldMode = availableModes[activeMode.get()]; 29974 const newMode = availableModes[mode]; 29975 try { 29976 newMode.activate(); 29977 } catch (e) { 29978 console.error(`problem while activating editor mode ${ mode }:`, e); 29979 return; 29980 } 29981 oldMode.deactivate(); 29982 if (oldMode.editorReadOnly !== newMode.editorReadOnly) { 29983 toggleReadOnly(editor, newMode.editorReadOnly); 29984 } 29985 activeMode.set(mode); 29986 fireSwitchMode(editor, mode); 29987 }; 29988 const setMode = (editor, availableModes, activeMode, mode) => { 29989 if (mode === activeMode.get()) { 29990 return; 29991 } else if (!has$2(availableModes, mode)) { 29992 throw new Error(`Editor mode '${ mode }' is invalid`); 29993 } 29994 if (editor.initialized) { 29995 switchToMode(editor, activeMode, availableModes, mode); 29996 } else { 29997 editor.on('init', () => switchToMode(editor, activeMode, availableModes, mode)); 29998 } 29999 }; 30000 const registerMode = (availableModes, mode, api) => { 30001 if (contains$2(defaultModes, mode)) { 30002 throw new Error(`Cannot override default mode ${ mode }`); 30003 } 30004 return { 30005 ...availableModes, 30006 [mode]: { 30007 ...api, 30008 deactivate: () => { 30009 try { 30010 api.deactivate(); 30011 } catch (e) { 30012 console.error(`problem while deactivating editor mode ${ mode }:`, e); 30013 } 30014 } 30015 } 30016 }; 30017 }; 30018 30019 const create$4 = editor => { 30020 const activeMode = Cell('design'); 30021 const availableModes = Cell({ 30022 design: { 30023 activate: noop, 30024 deactivate: noop, 30025 editorReadOnly: false 30026 }, 30027 readonly: { 30028 activate: noop, 30029 deactivate: noop, 30030 editorReadOnly: true 30031 } 30032 }); 30033 registerReadOnlyContentFilters(editor); 30034 registerReadOnlySelectionBlockers(editor); 30035 return { 30036 isReadOnly: () => isReadOnly(editor), 30037 set: mode => setMode(editor, availableModes.get(), activeMode, mode), 30038 get: () => activeMode.get(), 30039 register: (mode, api) => { 30040 availableModes.set(registerMode(availableModes.get(), mode, api)); 30041 } 30042 }; 30043 }; 30044 30045 const each$2 = Tools.each, explode = Tools.explode; 30046 const keyCodeLookup = { 30047 f1: 112, 30048 f2: 113, 30049 f3: 114, 30050 f4: 115, 30051 f5: 116, 30052 f6: 117, 30053 f7: 118, 30054 f8: 119, 30055 f9: 120, 30056 f10: 121, 30057 f11: 122, 30058 f12: 123 30059 }; 30060 const modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access'); 30061 const isModifier = key => key in modifierNames; 30062 const parseShortcut = pattern => { 30063 const shortcut = {}; 30064 const isMac = Env.os.isMacOS() || Env.os.isiOS(); 30065 each$2(explode(pattern.toLowerCase(), '+'), value => { 30066 if (isModifier(value)) { 30067 shortcut[value] = true; 30068 } else { 30069 if (/^[0-9]{2,}$/.test(value)) { 30070 shortcut.keyCode = parseInt(value, 10); 30071 } else { 30072 shortcut.charCode = value.charCodeAt(0); 30073 shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0); 30074 } 30075 } 30076 }); 30077 const id = [shortcut.keyCode]; 30078 let key; 30079 for (key in modifierNames) { 30080 if (shortcut[key]) { 30081 id.push(key); 30082 } else { 30083 shortcut[key] = false; 30084 } 30085 } 30086 shortcut.id = id.join(','); 30087 if (shortcut.access) { 30088 shortcut.alt = true; 30089 if (isMac) { 30090 shortcut.ctrl = true; 30091 } else { 30092 shortcut.shift = true; 30093 } 30094 } 30095 if (shortcut.meta) { 30096 if (isMac) { 30097 shortcut.meta = true; 30098 } else { 30099 shortcut.ctrl = true; 30100 shortcut.meta = false; 30101 } 30102 } 30103 return shortcut; 30104 }; 30105 class Shortcuts { 30106 constructor(editor) { 30107 this.shortcuts = {}; 30108 this.pendingPatterns = []; 30109 this.editor = editor; 30110 const self = this; 30111 editor.on('keyup keypress keydown', e => { 30112 if ((self.hasModifier(e) || self.isFunctionKey(e)) && !e.isDefaultPrevented()) { 30113 each$2(self.shortcuts, shortcut => { 30114 if (self.matchShortcut(e, shortcut)) { 30115 self.pendingPatterns = shortcut.subpatterns.slice(0); 30116 if (e.type === 'keydown') { 30117 self.executeShortcutAction(shortcut); 30118 } 30119 } 30120 }); 30121 if (self.matchShortcut(e, self.pendingPatterns[0])) { 30122 if (self.pendingPatterns.length === 1) { 30123 if (e.type === 'keydown') { 30124 self.executeShortcutAction(self.pendingPatterns[0]); 30125 } 30126 } 30127 self.pendingPatterns.shift(); 30128 } 30129 } 30130 }); 30131 } 30132 add(pattern, desc, cmdFunc, scope) { 30133 const self = this; 30134 const func = self.normalizeCommandFunc(cmdFunc); 30135 each$2(explode(Tools.trim(pattern)), pattern => { 30136 const shortcut = self.createShortcut(pattern, desc, func, scope); 30137 self.shortcuts[shortcut.id] = shortcut; 30138 }); 30139 return true; 30140 } 30141 remove(pattern) { 30142 const shortcut = this.createShortcut(pattern); 30143 if (this.shortcuts[shortcut.id]) { 30144 delete this.shortcuts[shortcut.id]; 30145 return true; 30146 } 30147 return false; 30148 } 30149 normalizeCommandFunc(cmdFunc) { 30150 const self = this; 30151 const cmd = cmdFunc; 30152 if (typeof cmd === 'string') { 30153 return () => { 30154 self.editor.execCommand(cmd, false, null); 30155 }; 30156 } else if (Tools.isArray(cmd)) { 30157 return () => { 30158 self.editor.execCommand(cmd[0], cmd[1], cmd[2]); 30159 }; 30160 } else { 30161 return cmd; 30162 } 30163 } 30164 createShortcut(pattern, desc, cmdFunc, scope) { 30165 const shortcuts = Tools.map(explode(pattern, '>'), parseShortcut); 30166 shortcuts[shortcuts.length - 1] = Tools.extend(shortcuts[shortcuts.length - 1], { 30167 func: cmdFunc, 30168 scope: scope || this.editor 30169 }); 30170 return Tools.extend(shortcuts[0], { 30171 desc: this.editor.translate(desc), 30172 subpatterns: shortcuts.slice(1) 30173 }); 30174 } 30175 hasModifier(e) { 30176 return e.altKey || e.ctrlKey || e.metaKey; 30177 } 30178 isFunctionKey(e) { 30179 return e.type === 'keydown' && e.keyCode >= 112 && e.keyCode <= 123; 30180 } 30181 matchShortcut(e, shortcut) { 30182 if (!shortcut) { 30183 return false; 30184 } 30185 if (shortcut.ctrl !== e.ctrlKey || shortcut.meta !== e.metaKey) { 30186 return false; 30187 } 30188 if (shortcut.alt !== e.altKey || shortcut.shift !== e.shiftKey) { 30189 return false; 30190 } 30191 if (e.keyCode === shortcut.keyCode || e.charCode && e.charCode === shortcut.charCode) { 30192 e.preventDefault(); 30193 return true; 30194 } 30195 return false; 30196 } 30197 executeShortcutAction(shortcut) { 30198 return shortcut.func ? shortcut.func.call(shortcut.scope) : null; 30199 } 30200 } 30201 30202 const create$3 = () => { 30203 const buttons = {}; 30204 const menuItems = {}; 30205 const popups = {}; 30206 const icons = {}; 30207 const contextMenus = {}; 30208 const contextToolbars = {}; 30209 const sidebars = {}; 30210 const views = {}; 30211 const add = (collection, type) => (name, spec) => { 30212 collection[name.toLowerCase()] = { 30213 ...spec, 30214 type 30215 }; 30216 }; 30217 const addIcon = (name, svgData) => icons[name.toLowerCase()] = svgData; 30218 return { 30219 addButton: add(buttons, 'button'), 30220 addGroupToolbarButton: add(buttons, 'grouptoolbarbutton'), 30221 addToggleButton: add(buttons, 'togglebutton'), 30222 addMenuButton: add(buttons, 'menubutton'), 30223 addSplitButton: add(buttons, 'splitbutton'), 30224 addMenuItem: add(menuItems, 'menuitem'), 30225 addNestedMenuItem: add(menuItems, 'nestedmenuitem'), 30226 addToggleMenuItem: add(menuItems, 'togglemenuitem'), 30227 addAutocompleter: add(popups, 'autocompleter'), 30228 addContextMenu: add(contextMenus, 'contextmenu'), 30229 addContextToolbar: add(contextToolbars, 'contexttoolbar'), 30230 addContextForm: add(contextToolbars, 'contextform'), 30231 addSidebar: add(sidebars, 'sidebar'), 30232 addView: add(views, 'views'), 30233 addIcon, 30234 getAll: () => ({ 30235 buttons, 30236 menuItems, 30237 icons, 30238 popups, 30239 contextMenus, 30240 contextToolbars, 30241 sidebars, 30242 views 30243 }) 30244 }; 30245 }; 30246 30247 const registry = () => { 30248 const bridge = create$3(); 30249 return { 30250 addAutocompleter: bridge.addAutocompleter, 30251 addButton: bridge.addButton, 30252 addContextForm: bridge.addContextForm, 30253 addContextMenu: bridge.addContextMenu, 30254 addContextToolbar: bridge.addContextToolbar, 30255 addIcon: bridge.addIcon, 30256 addMenuButton: bridge.addMenuButton, 30257 addMenuItem: bridge.addMenuItem, 30258 addNestedMenuItem: bridge.addNestedMenuItem, 30259 addSidebar: bridge.addSidebar, 30260 addSplitButton: bridge.addSplitButton, 30261 addToggleButton: bridge.addToggleButton, 30262 addGroupToolbarButton: bridge.addGroupToolbarButton, 30263 addToggleMenuItem: bridge.addToggleMenuItem, 30264 addView: bridge.addView, 30265 getAll: bridge.getAll 30266 }; 30267 }; 30268 30269 const DOM$1 = DOMUtils.DOM; 30270 const extend = Tools.extend, each$1 = Tools.each; 30271 class Editor { 30272 constructor(id, options, editorManager) { 30273 this.plugins = {}; 30274 this.contentCSS = []; 30275 this.contentStyles = []; 30276 this.loadedCSS = {}; 30277 this.isNotDirty = false; 30278 this.composing = false; 30279 this.destroyed = false; 30280 this.hasHiddenInput = false; 30281 this.iframeElement = null; 30282 this.initialized = false; 30283 this.readonly = false; 30284 this.removed = false; 30285 this.startContent = ''; 30286 this._pendingNativeEvents = []; 30287 this._skinLoaded = false; 30288 this._editableRoot = true; 30289 this.editorManager = editorManager; 30290 this.documentBaseUrl = editorManager.documentBaseURL; 30291 extend(this, EditorObservable); 30292 const self = this; 30293 this.id = id; 30294 this.hidden = false; 30295 const normalizedOptions = normalizeOptions(editorManager.defaultOptions, options); 30296 this.options = create$5(self, normalizedOptions); 30297 register$7(self); 30298 const getOption = this.options.get; 30299 if (getOption('deprecation_warnings')) { 30300 logWarnings(options, normalizedOptions); 30301 } 30302 const suffix = getOption('suffix'); 30303 if (suffix) { 30304 editorManager.suffix = suffix; 30305 } 30306 this.suffix = editorManager.suffix; 30307 const baseUrl = getOption('base_url'); 30308 if (baseUrl) { 30309 editorManager._setBaseUrl(baseUrl); 30310 } 30311 this.baseUri = editorManager.baseURI; 30312 const referrerPolicy = getReferrerPolicy(self); 30313 if (referrerPolicy) { 30314 ScriptLoader.ScriptLoader._setReferrerPolicy(referrerPolicy); 30315 DOMUtils.DOM.styleSheetLoader._setReferrerPolicy(referrerPolicy); 30316 } 30317 const contentCssCors = hasContentCssCors(self); 30318 if (isNonNullable(contentCssCors)) { 30319 DOMUtils.DOM.styleSheetLoader._setContentCssCors(contentCssCors); 30320 } 30321 AddOnManager.languageLoad = getOption('language_load'); 30322 AddOnManager.baseURL = editorManager.baseURL; 30323 this.setDirty(false); 30324 this.documentBaseURI = new URI(getDocumentBaseUrl(self), { base_uri: this.baseUri }); 30325 this.baseURI = this.baseUri; 30326 this.inline = isInline(self); 30327 this.hasVisual = isVisualAidsEnabled(self); 30328 this.shortcuts = new Shortcuts(this); 30329 this.editorCommands = new EditorCommands(this); 30330 registerCommands(this); 30331 const cacheSuffix = getOption('cache_suffix'); 30332 if (cacheSuffix) { 30333 Env.cacheSuffix = cacheSuffix.replace(/^[\?\&]+/, ''); 30334 } 30335 this.ui = { 30336 registry: registry(), 30337 styleSheetLoader: undefined, 30338 show: noop, 30339 hide: noop, 30340 setEnabled: noop, 30341 isEnabled: always 30342 }; 30343 this.mode = create$4(self); 30344 editorManager.dispatch('SetupEditor', { editor: this }); 30345 const setupCallback = getSetupCallback(self); 30346 if (isFunction(setupCallback)) { 30347 setupCallback.call(self, self); 30348 } 30349 } 30350 render() { 30351 render(this); 30352 } 30353 focus(skipFocus) { 30354 this.execCommand('mceFocus', false, skipFocus); 30355 } 30356 hasFocus() { 30357 return hasFocus(this); 30358 } 30359 translate(text) { 30360 return I18n.translate(text); 30361 } 30362 getParam(name, defaultVal, type) { 30363 const options = this.options; 30364 if (!options.isRegistered(name)) { 30365 if (isNonNullable(type)) { 30366 options.register(name, { 30367 processor: type, 30368 default: defaultVal 30369 }); 30370 } else { 30371 options.register(name, { 30372 processor: always, 30373 default: defaultVal 30374 }); 30375 } 30376 } 30377 return !options.isSet(name) && !isUndefined(defaultVal) ? defaultVal : options.get(name); 30378 } 30379 hasPlugin(name, loaded) { 30380 const hasPlugin = contains$2(getPlugins(this), name); 30381 if (hasPlugin) { 30382 return loaded ? PluginManager.get(name) !== undefined : true; 30383 } else { 30384 return false; 30385 } 30386 } 30387 nodeChanged(args) { 30388 this._nodeChangeDispatcher.nodeChanged(args); 30389 } 30390 addCommand(name, callback, scope) { 30391 this.editorCommands.addCommand(name, callback, scope); 30392 } 30393 addQueryStateHandler(name, callback, scope) { 30394 this.editorCommands.addQueryStateHandler(name, callback, scope); 30395 } 30396 addQueryValueHandler(name, callback, scope) { 30397 this.editorCommands.addQueryValueHandler(name, callback, scope); 30398 } 30399 addShortcut(pattern, desc, cmdFunc, scope) { 30400 this.shortcuts.add(pattern, desc, cmdFunc, scope); 30401 } 30402 execCommand(cmd, ui, value, args) { 30403 return this.editorCommands.execCommand(cmd, ui, value, args); 30404 } 30405 queryCommandState(cmd) { 30406 return this.editorCommands.queryCommandState(cmd); 30407 } 30408 queryCommandValue(cmd) { 30409 return this.editorCommands.queryCommandValue(cmd); 30410 } 30411 queryCommandSupported(cmd) { 30412 return this.editorCommands.queryCommandSupported(cmd); 30413 } 30414 show() { 30415 const self = this; 30416 if (self.hidden) { 30417 self.hidden = false; 30418 if (self.inline) { 30419 self.getBody().contentEditable = 'true'; 30420 } else { 30421 DOM$1.show(self.getContainer()); 30422 DOM$1.hide(self.id); 30423 } 30424 self.load(); 30425 self.dispatch('show'); 30426 } 30427 } 30428 hide() { 30429 const self = this; 30430 if (!self.hidden) { 30431 self.save(); 30432 if (self.inline) { 30433 self.getBody().contentEditable = 'false'; 30434 if (self === self.editorManager.focusedEditor) { 30435 self.editorManager.focusedEditor = null; 30436 } 30437 } else { 30438 DOM$1.hide(self.getContainer()); 30439 DOM$1.setStyle(self.id, 'display', self.orgDisplay); 30440 } 30441 self.hidden = true; 30442 self.dispatch('hide'); 30443 } 30444 } 30445 isHidden() { 30446 return this.hidden; 30447 } 30448 setProgressState(state, time) { 30449 this.dispatch('ProgressState', { 30450 state, 30451 time 30452 }); 30453 } 30454 load(args = {}) { 30455 const self = this; 30456 const elm = self.getElement(); 30457 if (self.removed) { 30458 return ''; 30459 } 30460 if (elm) { 30461 const loadArgs = { 30462 ...args, 30463 load: true 30464 }; 30465 const value = isTextareaOrInput(elm) ? elm.value : elm.innerHTML; 30466 const html = self.setContent(value, loadArgs); 30467 if (!loadArgs.no_events) { 30468 self.dispatch('LoadContent', { 30469 ...loadArgs, 30470 element: elm 30471 }); 30472 } 30473 return html; 30474 } else { 30475 return ''; 30476 } 30477 } 30478 save(args = {}) { 30479 const self = this; 30480 let elm = self.getElement(); 30481 if (!elm || !self.initialized || self.removed) { 30482 return ''; 30483 } 30484 const getArgs = { 30485 ...args, 30486 save: true, 30487 element: elm 30488 }; 30489 let html = self.getContent(getArgs); 30490 const saveArgs = { 30491 ...getArgs, 30492 content: html 30493 }; 30494 if (!saveArgs.no_events) { 30495 self.dispatch('SaveContent', saveArgs); 30496 } 30497 if (saveArgs.format === 'raw') { 30498 self.dispatch('RawSaveContent', saveArgs); 30499 } 30500 html = saveArgs.content; 30501 if (!isTextareaOrInput(elm)) { 30502 if (args.is_removing || !self.inline) { 30503 elm.innerHTML = html; 30504 } 30505 const form = DOM$1.getParent(self.id, 'form'); 30506 if (form) { 30507 each$1(form.elements, elm => { 30508 if (elm.name === self.id) { 30509 elm.value = html; 30510 return false; 30511 } else { 30512 return true; 30513 } 30514 }); 30515 } 30516 } else { 30517 elm.value = html; 30518 } 30519 saveArgs.element = getArgs.element = elm = null; 30520 if (saveArgs.set_dirty !== false) { 30521 self.setDirty(false); 30522 } 30523 return html; 30524 } 30525 setContent(content, args) { 30526 return setContent(this, content, args); 30527 } 30528 getContent(args) { 30529 return getContent(this, args); 30530 } 30531 insertContent(content, args) { 30532 if (args) { 30533 content = extend({ content }, args); 30534 } 30535 this.execCommand('mceInsertContent', false, content); 30536 } 30537 resetContent(initialContent) { 30538 if (initialContent === undefined) { 30539 setContent(this, this.startContent, { format: 'raw' }); 30540 } else { 30541 setContent(this, initialContent); 30542 } 30543 this.undoManager.reset(); 30544 this.setDirty(false); 30545 this.nodeChanged(); 30546 } 30547 isDirty() { 30548 return !this.isNotDirty; 30549 } 30550 setDirty(state) { 30551 const oldState = !this.isNotDirty; 30552 this.isNotDirty = !state; 30553 if (state && state !== oldState) { 30554 this.dispatch('dirty'); 30555 } 30556 } 30557 getContainer() { 30558 const self = this; 30559 if (!self.container) { 30560 self.container = self.editorContainer || DOM$1.get(self.id + '_parent'); 30561 } 30562 return self.container; 30563 } 30564 getContentAreaContainer() { 30565 return this.contentAreaContainer; 30566 } 30567 getElement() { 30568 if (!this.targetElm) { 30569 this.targetElm = DOM$1.get(this.id); 30570 } 30571 return this.targetElm; 30572 } 30573 getWin() { 30574 const self = this; 30575 if (!self.contentWindow) { 30576 const elm = self.iframeElement; 30577 if (elm) { 30578 self.contentWindow = elm.contentWindow; 30579 } 30580 } 30581 return self.contentWindow; 30582 } 30583 getDoc() { 30584 const self = this; 30585 if (!self.contentDocument) { 30586 const win = self.getWin(); 30587 if (win) { 30588 self.contentDocument = win.document; 30589 } 30590 } 30591 return self.contentDocument; 30592 } 30593 getBody() { 30594 var _a, _b; 30595 const doc = this.getDoc(); 30596 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; 30597 } 30598 convertURL(url, name, elm) { 30599 const self = this, getOption = self.options.get; 30600 const urlConverterCallback = getUrlConverterCallback(self); 30601 if (isFunction(urlConverterCallback)) { 30602 return urlConverterCallback.call(self, url, elm, true, name); 30603 } 30604 if (!getOption('convert_urls') || elm === 'link' || isObject(elm) && elm.nodeName === 'LINK' || url.indexOf('file:') === 0 || url.length === 0) { 30605 return url; 30606 } 30607 if (getOption('relative_urls')) { 30608 return self.documentBaseURI.toRelative(url); 30609 } 30610 url = self.documentBaseURI.toAbsolute(url, getOption('remove_script_host')); 30611 return url; 30612 } 30613 addVisual(elm) { 30614 addVisual(this, elm); 30615 } 30616 setEditableRoot(state) { 30617 setEditableRoot(this, state); 30618 } 30619 hasEditableRoot() { 30620 return hasEditableRoot(this); 30621 } 30622 remove() { 30623 remove$1(this); 30624 } 30625 destroy(automatic) { 30626 destroy(this, automatic); 30627 } 30628 uploadImages() { 30629 return this.editorUpload.uploadImages(); 30630 } 30631 _scanForImages() { 30632 return this.editorUpload.scanForImages(); 30633 } 30634 } 30635 30636 const DOM = DOMUtils.DOM; 30637 const each = Tools.each; 30638 let boundGlobalEvents = false; 30639 let beforeUnloadDelegate; 30640 let editors = []; 30641 const globalEventDelegate = e => { 30642 const type = e.type; 30643 each(EditorManager.get(), editor => { 30644 switch (type) { 30645 case 'scroll': 30646 editor.dispatch('ScrollWindow', e); 30647 break; 30648 case 'resize': 30649 editor.dispatch('ResizeWindow', e); 30650 break; 30651 } 30652 }); 30653 }; 30654 const toggleGlobalEvents = state => { 30655 if (state !== boundGlobalEvents) { 30656 const DOM = DOMUtils.DOM; 30657 if (state) { 30658 DOM.bind(window, 'resize', globalEventDelegate); 30659 DOM.bind(window, 'scroll', globalEventDelegate); 30660 } else { 30661 DOM.unbind(window, 'resize', globalEventDelegate); 30662 DOM.unbind(window, 'scroll', globalEventDelegate); 30663 } 30664 boundGlobalEvents = state; 30665 } 30666 }; 30667 const removeEditorFromList = targetEditor => { 30668 const oldEditors = editors; 30669 editors = filter$5(editors, editor => { 30670 return targetEditor !== editor; 30671 }); 30672 if (EditorManager.activeEditor === targetEditor) { 30673 EditorManager.activeEditor = editors.length > 0 ? editors[0] : null; 30674 } 30675 if (EditorManager.focusedEditor === targetEditor) { 30676 EditorManager.focusedEditor = null; 30677 } 30678 return oldEditors.length !== editors.length; 30679 }; 30680 const purgeDestroyedEditor = editor => { 30681 if (editor && editor.initialized && !(editor.getContainer() || editor.getBody()).parentNode) { 30682 removeEditorFromList(editor); 30683 editor.unbindAllNativeEvents(); 30684 editor.destroy(true); 30685 editor.removed = true; 30686 } 30687 }; 30688 const isQuirksMode = document.compatMode !== 'CSS1Compat'; 30689 const EditorManager = { 30690 ...Observable, 30691 baseURI: null, 30692 baseURL: null, 30693 defaultOptions: {}, 30694 documentBaseURL: null, 30695 suffix: null, 30696 majorVersion: '6', 30697 minorVersion: '
306976.2', 30698 releaseDate: '2023-08-0
vendor: 18,365 bytes, lines 30698-31305
306989', 30699 i18n: I18n, 30700 activeEditor: null, 30701 focusedEditor: null, 30702 setup() { 30703 const self = this; 30704 let baseURL = ''; 30705 let suffix = ''; 30706 let documentBaseURL = URI.getDocumentBaseUrl(document.location); 30707 if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) { 30708 documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); 30709 if (!/[\/\\]$/.test(documentBaseURL)) { 30710 documentBaseURL += '/'; 30711 } 30712 } 30713 const preInit = window.tinymce || window.tinyMCEPreInit; 30714 if (preInit) { 30715 baseURL = preInit.base || preInit.baseURL; 30716 suffix = preInit.suffix; 30717 } else { 30718 const scripts = document.getElementsByTagName('script'); 30719 for (let i = 0; i < scripts.length; i++) { 30720 const src = scripts[i].src || ''; 30721 if (src === '') { 30722 continue; 30723 } 30724 const srcScript = src.substring(src.lastIndexOf('/')); 30725 if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) { 30726 if (srcScript.indexOf('.min') !== -1) { 30727 suffix = '.min'; 30728 } 30729 baseURL = src.substring(0, src.lastIndexOf('/')); 30730 break; 30731 } 30732 } 30733 if (!baseURL && document.currentScript) { 30734 const src = document.currentScript.src; 30735 if (src.indexOf('.min') !== -1) { 30736 suffix = '.min'; 30737 } 30738 baseURL = src.substring(0, src.lastIndexOf('/')); 30739 } 30740 } 30741 self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL); 30742 self.documentBaseURL = documentBaseURL; 30743 self.baseURI = new URI(self.baseURL); 30744 self.suffix = suffix; 30745 setup$v(self); 30746 }, 30747 overrideDefaults(defaultOptions) { 30748 const baseUrl = defaultOptions.base_url; 30749 if (baseUrl) { 30750 this._setBaseUrl(baseUrl); 30751 } 30752 const suffix = defaultOptions.suffix; 30753 if (suffix) { 30754 this.suffix = suffix; 30755 } 30756 this.defaultOptions = defaultOptions; 30757 const pluginBaseUrls = defaultOptions.plugin_base_urls; 30758 if (pluginBaseUrls !== undefined) { 30759 each$d(pluginBaseUrls, (pluginBaseUrl, pluginName) => { 30760 AddOnManager.PluginManager.urls[pluginName] = pluginBaseUrl; 30761 }); 30762 } 30763 }, 30764 init(options) { 30765 const self = this; 30766 let result; 30767 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', ' '); 30768 const isInvalidInlineTarget = (options, elm) => options.inline && elm.tagName.toLowerCase() in invalidInlineTargets; 30769 const createId = elm => { 30770 let id = elm.id; 30771 if (!id) { 30772 id = get$a(elm, 'name').filter(name => !DOM.get(name)).getOrThunk(DOM.uniqueId); 30773 elm.setAttribute('id', id); 30774 } 30775 return id; 30776 }; 30777 const execCallback = name => { 30778 const callback = options[name]; 30779 if (!callback) { 30780 return; 30781 } 30782 return callback.apply(self, []); 30783 }; 30784 const findTargets = options => { 30785 if (Env.browser.isIE() || Env.browser.isEdge()) { 30786 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'); 30787 return []; 30788 } else if (isQuirksMode) { 30789 initError('Failed to initialize the editor as the document is not in standards mode. ' + 'TinyMCE requires standards mode.'); 30790 return []; 30791 } else if (isString(options.selector)) { 30792 return DOM.select(options.selector); 30793 } else if (isNonNullable(options.target)) { 30794 return [options.target]; 30795 } else { 30796 return []; 30797 } 30798 }; 30799 let provideResults = editors => { 30800 result = editors; 30801 }; 30802 const initEditors = () => { 30803 let initCount = 0; 30804 const editors = []; 30805 let targets; 30806 const createEditor = (id, options, targetElm) => { 30807 const editor = new Editor(id, options, self); 30808 editors.push(editor); 30809 editor.on('init', () => { 30810 if (++initCount === targets.length) { 30811 provideResults(editors); 30812 } 30813 }); 30814 editor.targetElm = editor.targetElm || targetElm; 30815 editor.render(); 30816 }; 30817 DOM.unbind(window, 'ready', initEditors); 30818 execCallback('onpageload'); 30819 targets = unique$1(findTargets(options)); 30820 Tools.each(targets, elm => { 30821 purgeDestroyedEditor(self.get(elm.id)); 30822 }); 30823 targets = Tools.grep(targets, elm => { 30824 return !self.get(elm.id); 30825 }); 30826 if (targets.length === 0) { 30827 provideResults([]); 30828 } else { 30829 each(targets, elm => { 30830 if (isInvalidInlineTarget(options, elm)) { 30831 initError('Could not initialize inline editor on invalid inline target element', elm); 30832 } else { 30833 createEditor(createId(elm), options, elm); 30834 } 30835 }); 30836 } 30837 }; 30838 DOM.bind(window, 'ready', initEditors); 30839 return new Promise(resolve => { 30840 if (result) { 30841 resolve(result); 30842 } else { 30843 provideResults = editors => { 30844 resolve(editors); 30845 }; 30846 } 30847 }); 30848 }, 30849 get(id) { 30850 if (arguments.length === 0) { 30851 return editors.slice(0); 30852 } else if (isString(id)) { 30853 return find$2(editors, editor => { 30854 return editor.id === id; 30855 }).getOr(null); 30856 } else if (isNumber(id)) { 30857 return editors[id] ? editors[id] : null; 30858 } else { 30859 return null; 30860 } 30861 }, 30862 add(editor) { 30863 const self = this; 30864 const existingEditor = self.get(editor.id); 30865 if (existingEditor === editor) { 30866 return editor; 30867 } 30868 if (existingEditor === null) { 30869 editors.push(editor); 30870 } 30871 toggleGlobalEvents(true); 30872 self.activeEditor = editor; 30873 self.dispatch('AddEditor', { editor }); 30874 if (!beforeUnloadDelegate) { 30875 beforeUnloadDelegate = e => { 30876 const event = self.dispatch('BeforeUnload'); 30877 if (event.returnValue) { 30878 e.preventDefault(); 30879 e.returnValue = event.returnValue; 30880 return event.returnValue; 30881 } 30882 }; 30883 window.addEventListener('beforeunload', beforeUnloadDelegate); 30884 } 30885 return editor; 30886 }, 30887 createEditor(id, options) { 30888 return this.add(new Editor(id, options, this)); 30889 }, 30890 remove(selector) { 30891 const self = this; 30892 let editor; 30893 if (!selector) { 30894 for (let i = editors.length - 1; i >= 0; i--) { 30895 self.remove(editors[i]); 30896 } 30897 return; 30898 } 30899 if (isString(selector)) { 30900 each(DOM.select(selector), elm => { 30901 editor = self.get(elm.id); 30902 if (editor) { 30903 self.remove(editor); 30904 } 30905 }); 30906 return; 30907 } 30908 editor = selector; 30909 if (isNull(self.get(editor.id))) { 30910 return null; 30911 } 30912 if (removeEditorFromList(editor)) { 30913 self.dispatch('RemoveEditor', { editor }); 30914 } 30915 if (editors.length === 0) { 30916 window.removeEventListener('beforeunload', beforeUnloadDelegate); 30917 } 30918 editor.remove(); 30919 toggleGlobalEvents(editors.length > 0); 30920 return editor; 30921 }, 30922 execCommand(cmd, ui, value) { 30923 var _a; 30924 const self = this; 30925 const editorId = isObject(value) ? (_a = value.id) !== null && _a !== void 0 ? _a : value.index : value; 30926 switch (cmd) { 30927 case 'mceAddEditor': { 30928 if (!self.get(editorId)) { 30929 const editorOptions = value.options; 30930 new Editor(editorId, editorOptions, self).render(); 30931 } 30932 return true; 30933 } 30934 case 'mceRemoveEditor': { 30935 const editor = self.get(editorId); 30936 if (editor) { 30937 editor.remove(); 30938 } 30939 return true; 30940 } 30941 case 'mceToggleEditor': { 30942 const editor = self.get(editorId); 30943 if (!editor) { 30944 self.execCommand('mceAddEditor', false, value); 30945 return true; 30946 } 30947 if (editor.isHidden()) { 30948 editor.show(); 30949 } else { 30950 editor.hide(); 30951 } 30952 return true; 30953 } 30954 } 30955 if (self.activeEditor) { 30956 return self.activeEditor.execCommand(cmd, ui, value); 30957 } 30958 return false; 30959 }, 30960 triggerSave: () => { 30961 each(editors, editor => { 30962 editor.save(); 30963 }); 30964 }, 30965 addI18n: (code, items) => { 30966 I18n.add(code, items); 30967 }, 30968 translate: text => { 30969 return I18n.translate(text); 30970 }, 30971 setActive(editor) { 30972 const activeEditor = this.activeEditor; 30973 if (this.activeEditor !== editor) { 30974 if (activeEditor) { 30975 activeEditor.dispatch('deactivate', { relatedTarget: editor }); 30976 } 30977 editor.dispatch('activate', { relatedTarget: activeEditor }); 30978 } 30979 this.activeEditor = editor; 30980 }, 30981 _setBaseUrl(baseUrl) { 30982 this.baseURL = new URI(this.documentBaseURL).toAbsolute(baseUrl.replace(/\/+$/, '')); 30983 this.baseURI = new URI(this.baseURL); 30984 } 30985 }; 30986 EditorManager.setup(); 30987 30988 const setup = () => { 30989 const dataValue = value$2(); 30990 const FakeClipboardItem = items => ({ 30991 items, 30992 types: keys(items), 30993 getType: type => get$a(items, type).getOrUndefined() 30994 }); 30995 const write = data => { 30996 dataValue.set(data); 30997 }; 30998 const read = () => dataValue.get().getOrUndefined(); 30999 const clear = dataValue.clear; 31000 return { 31001 FakeClipboardItem, 31002 write, 31003 read, 31004 clear 31005 }; 31006 }; 31007 const FakeClipboard = setup(); 31008 31009 const min = Math.min, max = Math.max, round = Math.round; 31010 const relativePosition = (rect, targetRect, rel) => { 31011 let x = targetRect.x; 31012 let y = targetRect.y; 31013 const w = rect.w; 31014 const h = rect.h; 31015 const targetW = targetRect.w; 31016 const targetH = targetRect.h; 31017 const relChars = (rel || '').split(''); 31018 if (relChars[0] === 'b') { 31019 y += targetH; 31020 } 31021 if (relChars[1] === 'r') { 31022 x += targetW; 31023 } 31024 if (relChars[0] === 'c') { 31025 y += round(targetH / 2); 31026 } 31027 if (relChars[1] === 'c') { 31028 x += round(targetW / 2); 31029 } 31030 if (relChars[3] === 'b') { 31031 y -= h; 31032 } 31033 if (relChars[4] === 'r') { 31034 x -= w; 31035 } 31036 if (relChars[3] === 'c') { 31037 y -= round(h / 2); 31038 } 31039 if (relChars[4] === 'c') { 31040 x -= round(w / 2); 31041 } 31042 return create$2(x, y, w, h); 31043 }; 31044 const findBestRelativePosition = (rect, targetRect, constrainRect, rels) => { 31045 for (let i = 0; i < rels.length; i++) { 31046 const pos = relativePosition(rect, targetRect, rels[i]); 31047 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) { 31048 return rels[i]; 31049 } 31050 } 31051 return null; 31052 }; 31053 const inflate = (rect, w, h) => { 31054 return create$2(rect.x - w, rect.y - h, rect.w + w * 2, rect.h + h * 2); 31055 }; 31056 const intersect = (rect, cropRect) => { 31057 const x1 = max(rect.x, cropRect.x); 31058 const y1 = max(rect.y, cropRect.y); 31059 const x2 = min(rect.x + rect.w, cropRect.x + cropRect.w); 31060 const y2 = min(rect.y + rect.h, cropRect.y + cropRect.h); 31061 if (x2 - x1 < 0 || y2 - y1 < 0) { 31062 return null; 31063 } 31064 return create$2(x1, y1, x2 - x1, y2 - y1); 31065 }; 31066 const clamp = (rect, clampRect, fixedSize) => { 31067 let x1 = rect.x; 31068 let y1 = rect.y; 31069 let x2 = rect.x + rect.w; 31070 let y2 = rect.y + rect.h; 31071 const cx2 = clampRect.x + clampRect.w; 31072 const cy2 = clampRect.y + clampRect.h; 31073 const underflowX1 = max(0, clampRect.x - x1); 31074 const underflowY1 = max(0, clampRect.y - y1); 31075 const overflowX2 = max(0, x2 - cx2); 31076 const overflowY2 = max(0, y2 - cy2); 31077 x1 += underflowX1; 31078 y1 += underflowY1; 31079 if (fixedSize) { 31080 x2 += underflowX1; 31081 y2 += underflowY1; 31082 x1 -= overflowX2; 31083 y1 -= overflowY2; 31084 } 31085 x2 -= overflowX2; 31086 y2 -= overflowY2; 31087 return create$2(x1, y1, x2 - x1, y2 - y1); 31088 }; 31089 const create$2 = (x, y, w, h) => { 31090 return { 31091 x, 31092 y, 31093 w, 31094 h 31095 }; 31096 }; 31097 const fromClientRect = clientRect => { 31098 return create$2(clientRect.left, clientRect.top, clientRect.width, clientRect.height); 31099 }; 31100 const Rect = { 31101 inflate, 31102 relativePosition, 31103 findBestRelativePosition, 31104 intersect, 31105 clamp, 31106 create: create$2, 31107 fromClientRect 31108 }; 31109 31110 const awaiter = (resolveCb, rejectCb, timeout = 1000) => { 31111 let done = false; 31112 let timer = null; 31113 const complete = completer => (...args) => { 31114 if (!done) { 31115 done = true; 31116 if (timer !== null) { 31117 clearTimeout(timer); 31118 timer = null; 31119 } 31120 completer.apply(null, args); 31121 } 31122 }; 31123 const resolve = complete(resolveCb); 31124 const reject = complete(rejectCb); 31125 const start = (...args) => { 31126 if (!done && timer === null) { 31127 timer = setTimeout(() => reject.apply(null, args), timeout); 31128 } 31129 }; 31130 return { 31131 start, 31132 resolve, 31133 reject 31134 }; 31135 }; 31136 const create$1 = () => { 31137 const tasks = {}; 31138 const resultFns = {}; 31139 const load = (id, url) => { 31140 const loadErrMsg = `Script at URL "${ url }" failed to load`; 31141 const runErrMsg = `Script at URL "${ url }" did not call \`tinymce.Resource.add('${ id }', data)\` within 1 second`; 31142 if (tasks[id] !== undefined) { 31143 return tasks[id]; 31144 } else { 31145 const task = new Promise((resolve, reject) => { 31146 const waiter = awaiter(resolve, reject); 31147 resultFns[id] = waiter.resolve; 31148 ScriptLoader.ScriptLoader.loadScript(url).then(() => waiter.start(runErrMsg), () => waiter.reject(loadErrMsg)); 31149 }); 31150 tasks[id] = task; 31151 return task; 31152 } 31153 }; 31154 const add = (id, data) => { 31155 if (resultFns[id] !== undefined) { 31156 resultFns[id](data); 31157 delete resultFns[id]; 31158 } 31159 tasks[id] = Promise.resolve(data); 31160 }; 31161 const unload = id => { 31162 delete tasks[id]; 31163 }; 31164 return { 31165 load, 31166 add, 31167 unload 31168 }; 31169 }; 31170 const Resource = create$1(); 31171 31172 const create = () => (() => { 31173 let data = {}; 31174 let keys = []; 31175 const storage = { 31176 getItem: key => { 31177 const item = data[key]; 31178 return item ? item : null; 31179 }, 31180 setItem: (key, value) => { 31181 keys.push(key); 31182 data[key] = String(value); 31183 }, 31184 key: index => { 31185 return keys[index]; 31186 }, 31187 removeItem: key => { 31188 keys = keys.filter(k => k === key); 31189 delete data[key]; 31190 }, 31191 clear: () => { 31192 keys = []; 31193 data = {}; 31194 }, 31195 length: 0 31196 }; 31197 Object.defineProperty(storage, 'length', { 31198 get: () => keys.length, 31199 configurable: false, 31200 enumerable: false 31201 }); 31202 return storage; 31203 })(); 31204 31205 let localStorage; 31206 try { 31207 const test = '__storage_test__'; 31208 localStorage = window.localStorage; 31209 localStorage.setItem(test, test); 31210 localStorage.removeItem(test); 31211 } catch (e) { 31212 localStorage = create(); 31213 } 31214 var LocalStorage = localStorage; 31215 31216 const publicApi = { 31217 geom: { Rect }, 31218 util: { 31219 Delay, 31220 Tools, 31221 VK, 31222 URI, 31223 EventDispatcher, 31224 Observable, 31225 I18n, 31226 LocalStorage, 31227 ImageUploader 31228 }, 31229 dom: { 31230 EventUtils, 31231 TreeWalker: DomTreeWalker, 31232 TextSeeker, 31233 DOMUtils, 31234 ScriptLoader, 31235 RangeUtils, 31236 Serializer: DomSerializer, 31237 StyleSheetLoader, 31238 ControlSelection, 31239 BookmarkManager, 31240 Selection: EditorSelection, 31241 Event: EventUtils.Event 31242 }, 31243 html: { 31244 Styles, 31245 Entities, 31246 Node: AstNode, 31247 Schema, 31248 DomParser, 31249 Writer, 31250 Serializer: HtmlSerializer 31251 }, 31252 Env, 31253 AddOnManager, 31254 Annotator, 31255 Formatter, 31256 UndoManager, 31257 EditorCommands, 31258 WindowManager, 31259 NotificationManager, 31260 EditorObservable, 31261 Shortcuts, 31262 Editor, 31263 FocusManager, 31264 EditorManager, 31265 DOM: DOMUtils.DOM, 31266 ScriptLoader: ScriptLoader.ScriptLoader, 31267 PluginManager, 31268 ThemeManager, 31269 ModelManager, 31270 IconManager, 31271 Resource, 31272 FakeClipboard, 31273 trim: Tools.trim, 31274 isArray: Tools.isArray, 31275 is: Tools.is, 31276 toArray: Tools.toArray, 31277 makeMap: Tools.makeMap, 31278 each: Tools.each, 31279 map: Tools.map, 31280 grep: Tools.grep, 31281 inArray: Tools.inArray, 31282 extend: Tools.extend, 31283 walk: Tools.walk, 31284 resolve: Tools.resolve, 31285 explode: Tools.explode, 31286 _addCacheSuffix: Tools._addCacheSuffix 31287 }; 31288 const tinymce = Tools.extend(EditorManager, publicApi); 31289 31290 const exportToModuleLoaders = tinymce => { 31291 if (typeof module === 'object') { 31292 try { 31293 module.exports = tinymce; 31294 } catch (_) { 31295 } 31296 } 31297 }; 31298 const exportToWindowGlobal = tinymce => { 31299 window.tinymce = tinymce; 31300 window.tinyMCE = tinymce; 31301 }; 31302 exportToWindowGlobal(tinymce); 31303 exportToModuleLoaders(tinymce); 31304 31305})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.