plugin.js 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. /**
  2. * TinyMCE version 6.8.6 (TBD)
  3. */
  4. (function () {
  5. 'use strict';
  6. var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');
  7. let unique = 0;
  8. const generate = prefix => {
  9. const date = new Date();
  10. const time = date.getTime();
  11. const random = Math.floor(Math.random() * 1000000000);
  12. unique++;
  13. return prefix + '_' + random + unique + String(time);
  14. };
  15. const hasProto = (v, constructor, predicate) => {
  16. var _a;
  17. if (predicate(v, constructor.prototype)) {
  18. return true;
  19. } else {
  20. return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
  21. }
  22. };
  23. const typeOf = x => {
  24. const t = typeof x;
  25. if (x === null) {
  26. return 'null';
  27. } else if (t === 'object' && Array.isArray(x)) {
  28. return 'array';
  29. } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
  30. return 'string';
  31. } else {
  32. return t;
  33. }
  34. };
  35. const isType$1 = type => value => typeOf(value) === type;
  36. const isSimpleType = type => value => typeof value === type;
  37. const isString = isType$1('string');
  38. const isBoolean = isSimpleType('boolean');
  39. const isNullable = a => a === null || a === undefined;
  40. const isNonNullable = a => !isNullable(a);
  41. const isFunction = isSimpleType('function');
  42. const isNumber = isSimpleType('number');
  43. const compose1 = (fbc, fab) => a => fbc(fab(a));
  44. const constant = value => {
  45. return () => {
  46. return value;
  47. };
  48. };
  49. const tripleEquals = (a, b) => {
  50. return a === b;
  51. };
  52. const never = constant(false);
  53. class Optional {
  54. constructor(tag, value) {
  55. this.tag = tag;
  56. this.value = value;
  57. }
  58. static some(value) {
  59. return new Optional(true, value);
  60. }
  61. static none() {
  62. return Optional.singletonNone;
  63. }
  64. fold(onNone, onSome) {
  65. if (this.tag) {
  66. return onSome(this.value);
  67. } else {
  68. return onNone();
  69. }
  70. }
  71. isSome() {
  72. return this.tag;
  73. }
  74. isNone() {
  75. return !this.tag;
  76. }
  77. map(mapper) {
  78. if (this.tag) {
  79. return Optional.some(mapper(this.value));
  80. } else {
  81. return Optional.none();
  82. }
  83. }
  84. bind(binder) {
  85. if (this.tag) {
  86. return binder(this.value);
  87. } else {
  88. return Optional.none();
  89. }
  90. }
  91. exists(predicate) {
  92. return this.tag && predicate(this.value);
  93. }
  94. forall(predicate) {
  95. return !this.tag || predicate(this.value);
  96. }
  97. filter(predicate) {
  98. if (!this.tag || predicate(this.value)) {
  99. return this;
  100. } else {
  101. return Optional.none();
  102. }
  103. }
  104. getOr(replacement) {
  105. return this.tag ? this.value : replacement;
  106. }
  107. or(replacement) {
  108. return this.tag ? this : replacement;
  109. }
  110. getOrThunk(thunk) {
  111. return this.tag ? this.value : thunk();
  112. }
  113. orThunk(thunk) {
  114. return this.tag ? this : thunk();
  115. }
  116. getOrDie(message) {
  117. if (!this.tag) {
  118. throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
  119. } else {
  120. return this.value;
  121. }
  122. }
  123. static from(value) {
  124. return isNonNullable(value) ? Optional.some(value) : Optional.none();
  125. }
  126. getOrNull() {
  127. return this.tag ? this.value : null;
  128. }
  129. getOrUndefined() {
  130. return this.value;
  131. }
  132. each(worker) {
  133. if (this.tag) {
  134. worker(this.value);
  135. }
  136. }
  137. toArray() {
  138. return this.tag ? [this.value] : [];
  139. }
  140. toString() {
  141. return this.tag ? `some(${ this.value })` : 'none()';
  142. }
  143. }
  144. Optional.singletonNone = new Optional(false);
  145. const nativeIndexOf = Array.prototype.indexOf;
  146. const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
  147. const contains = (xs, x) => rawIndexOf(xs, x) > -1;
  148. const map = (xs, f) => {
  149. const len = xs.length;
  150. const r = new Array(len);
  151. for (let i = 0; i < len; i++) {
  152. const x = xs[i];
  153. r[i] = f(x, i);
  154. }
  155. return r;
  156. };
  157. const each$1 = (xs, f) => {
  158. for (let i = 0, len = xs.length; i < len; i++) {
  159. const x = xs[i];
  160. f(x, i);
  161. }
  162. };
  163. const filter = (xs, pred) => {
  164. const r = [];
  165. for (let i = 0, len = xs.length; i < len; i++) {
  166. const x = xs[i];
  167. if (pred(x, i)) {
  168. r.push(x);
  169. }
  170. }
  171. return r;
  172. };
  173. const foldl = (xs, f, acc) => {
  174. each$1(xs, (x, i) => {
  175. acc = f(acc, x, i);
  176. });
  177. return acc;
  178. };
  179. const keys = Object.keys;
  180. const each = (obj, f) => {
  181. const props = keys(obj);
  182. for (let k = 0, len = props.length; k < len; k++) {
  183. const i = props[k];
  184. const x = obj[i];
  185. f(x, i);
  186. }
  187. };
  188. typeof window !== 'undefined' ? window : Function('return this;')();
  189. const COMMENT = 8;
  190. const DOCUMENT = 9;
  191. const DOCUMENT_FRAGMENT = 11;
  192. const ELEMENT = 1;
  193. const TEXT = 3;
  194. const name = element => {
  195. const r = element.dom.nodeName;
  196. return r.toLowerCase();
  197. };
  198. const type = element => element.dom.nodeType;
  199. const isType = t => element => type(element) === t;
  200. const isComment = element => type(element) === COMMENT || name(element) === '#comment';
  201. const isElement = isType(ELEMENT);
  202. const isText = isType(TEXT);
  203. const isDocument = isType(DOCUMENT);
  204. const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
  205. const rawSet = (dom, key, value) => {
  206. if (isString(value) || isBoolean(value) || isNumber(value)) {
  207. dom.setAttribute(key, value + '');
  208. } else {
  209. console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
  210. throw new Error('Attribute value was not simple');
  211. }
  212. };
  213. const set$2 = (element, key, value) => {
  214. rawSet(element.dom, key, value);
  215. };
  216. const setAll = (element, attrs) => {
  217. const dom = element.dom;
  218. each(attrs, (v, k) => {
  219. rawSet(dom, k, v);
  220. });
  221. };
  222. const get$2 = (element, key) => {
  223. const v = element.dom.getAttribute(key);
  224. return v === null ? undefined : v;
  225. };
  226. const getOpt = (element, key) => Optional.from(get$2(element, key));
  227. const remove$2 = (element, key) => {
  228. element.dom.removeAttribute(key);
  229. };
  230. const clone = element => foldl(element.dom.attributes, (acc, attr) => {
  231. acc[attr.name] = attr.value;
  232. return acc;
  233. }, {});
  234. const fromHtml = (html, scope) => {
  235. const doc = scope || document;
  236. const div = doc.createElement('div');
  237. div.innerHTML = html;
  238. if (!div.hasChildNodes() || div.childNodes.length > 1) {
  239. const message = 'HTML does not have a single root node';
  240. console.error(message, html);
  241. throw new Error(message);
  242. }
  243. return fromDom(div.childNodes[0]);
  244. };
  245. const fromTag = (tag, scope) => {
  246. const doc = scope || document;
  247. const node = doc.createElement(tag);
  248. return fromDom(node);
  249. };
  250. const fromText = (text, scope) => {
  251. const doc = scope || document;
  252. const node = doc.createTextNode(text);
  253. return fromDom(node);
  254. };
  255. const fromDom = node => {
  256. if (node === null || node === undefined) {
  257. throw new Error('Node cannot be null or undefined');
  258. }
  259. return { dom: node };
  260. };
  261. const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
  262. const SugarElement = {
  263. fromHtml,
  264. fromTag,
  265. fromText,
  266. fromDom,
  267. fromPoint
  268. };
  269. const is$2 = (element, selector) => {
  270. const dom = element.dom;
  271. if (dom.nodeType !== ELEMENT) {
  272. return false;
  273. } else {
  274. const elem = dom;
  275. if (elem.matches !== undefined) {
  276. return elem.matches(selector);
  277. } else if (elem.msMatchesSelector !== undefined) {
  278. return elem.msMatchesSelector(selector);
  279. } else if (elem.webkitMatchesSelector !== undefined) {
  280. return elem.webkitMatchesSelector(selector);
  281. } else if (elem.mozMatchesSelector !== undefined) {
  282. return elem.mozMatchesSelector(selector);
  283. } else {
  284. throw new Error('Browser lacks native selectors');
  285. }
  286. }
  287. };
  288. const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
  289. const all = (selector, scope) => {
  290. const base = scope === undefined ? document : scope.dom;
  291. return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);
  292. };
  293. const one = (selector, scope) => {
  294. const base = scope === undefined ? document : scope.dom;
  295. return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);
  296. };
  297. const eq = (e1, e2) => e1.dom === e2.dom;
  298. const is$1 = is$2;
  299. const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
  300. const blank = r => s => s.replace(r, '');
  301. const trim = blank(/^\s+|\s+$/g);
  302. const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);
  303. const owner = element => SugarElement.fromDom(element.dom.ownerDocument);
  304. const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);
  305. const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
  306. const parents = (element, isRoot) => {
  307. const stop = isFunction(isRoot) ? isRoot : never;
  308. let dom = element.dom;
  309. const ret = [];
  310. while (dom.parentNode !== null && dom.parentNode !== undefined) {
  311. const rawParent = dom.parentNode;
  312. const p = SugarElement.fromDom(rawParent);
  313. ret.push(p);
  314. if (stop(p) === true) {
  315. break;
  316. } else {
  317. dom = rawParent;
  318. }
  319. }
  320. return ret;
  321. };
  322. const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
  323. const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
  324. const children = element => map(element.dom.childNodes, SugarElement.fromDom);
  325. const child = (element, index) => {
  326. const cs = element.dom.childNodes;
  327. return Optional.from(cs[index]).map(SugarElement.fromDom);
  328. };
  329. const firstChild = element => child(element, 0);
  330. const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
  331. const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
  332. const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
  333. const getShadowRoot = e => {
  334. const r = getRootNode(e);
  335. return isShadowRoot(r) ? Optional.some(r) : Optional.none();
  336. };
  337. const getShadowHost = e => SugarElement.fromDom(e.dom.host);
  338. const inBody = element => {
  339. const dom = isText(element) ? element.dom.parentNode : element.dom;
  340. if (dom === undefined || dom === null || dom.ownerDocument === null) {
  341. return false;
  342. }
  343. const doc = dom.ownerDocument;
  344. return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
  345. };
  346. const internalSet = (dom, property, value) => {
  347. if (!isString(value)) {
  348. console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
  349. throw new Error('CSS value must be a string: ' + value);
  350. }
  351. if (isSupported(dom)) {
  352. dom.style.setProperty(property, value);
  353. }
  354. };
  355. const internalRemove = (dom, property) => {
  356. if (isSupported(dom)) {
  357. dom.style.removeProperty(property);
  358. }
  359. };
  360. const set$1 = (element, property, value) => {
  361. const dom = element.dom;
  362. internalSet(dom, property, value);
  363. };
  364. const get$1 = (element, property) => {
  365. const dom = element.dom;
  366. const styles = window.getComputedStyle(dom);
  367. const r = styles.getPropertyValue(property);
  368. return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
  369. };
  370. const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';
  371. const getRaw = (element, property) => {
  372. const dom = element.dom;
  373. const raw = getUnsafeProperty(dom, property);
  374. return Optional.from(raw).filter(r => r.length > 0);
  375. };
  376. const remove$1 = (element, property) => {
  377. const dom = element.dom;
  378. internalRemove(dom, property);
  379. if (is(getOpt(element, 'style').map(trim), '')) {
  380. remove$2(element, 'style');
  381. }
  382. };
  383. const before = (marker, element) => {
  384. const parent$1 = parent(marker);
  385. parent$1.each(v => {
  386. v.dom.insertBefore(element.dom, marker.dom);
  387. });
  388. };
  389. const after$1 = (marker, element) => {
  390. const sibling = nextSibling(marker);
  391. sibling.fold(() => {
  392. const parent$1 = parent(marker);
  393. parent$1.each(v => {
  394. append$1(v, element);
  395. });
  396. }, v => {
  397. before(v, element);
  398. });
  399. };
  400. const prepend = (parent, element) => {
  401. const firstChild$1 = firstChild(parent);
  402. firstChild$1.fold(() => {
  403. append$1(parent, element);
  404. }, v => {
  405. parent.dom.insertBefore(element.dom, v.dom);
  406. });
  407. };
  408. const append$1 = (parent, element) => {
  409. parent.dom.appendChild(element.dom);
  410. };
  411. const wrap = (element, wrapper) => {
  412. before(element, wrapper);
  413. append$1(wrapper, element);
  414. };
  415. const after = (marker, elements) => {
  416. each$1(elements, (x, i) => {
  417. const e = i === 0 ? marker : elements[i - 1];
  418. after$1(e, x);
  419. });
  420. };
  421. const append = (parent, elements) => {
  422. each$1(elements, x => {
  423. append$1(parent, x);
  424. });
  425. };
  426. const descendants$1 = (scope, predicate) => {
  427. let result = [];
  428. each$1(children(scope), x => {
  429. if (predicate(x)) {
  430. result = result.concat([x]);
  431. }
  432. result = result.concat(descendants$1(x, predicate));
  433. });
  434. return result;
  435. };
  436. var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
  437. if (is(scope, a)) {
  438. return Optional.some(scope);
  439. } else if (isFunction(isRoot) && isRoot(scope)) {
  440. return Optional.none();
  441. } else {
  442. return ancestor(scope, a, isRoot);
  443. }
  444. };
  445. const ancestor$1 = (scope, predicate, isRoot) => {
  446. let element = scope.dom;
  447. const stop = isFunction(isRoot) ? isRoot : never;
  448. while (element.parentNode) {
  449. element = element.parentNode;
  450. const el = SugarElement.fromDom(element);
  451. if (predicate(el)) {
  452. return Optional.some(el);
  453. } else if (stop(el)) {
  454. break;
  455. }
  456. }
  457. return Optional.none();
  458. };
  459. const remove = element => {
  460. const dom = element.dom;
  461. if (dom.parentNode !== null) {
  462. dom.parentNode.removeChild(dom);
  463. }
  464. };
  465. const unwrap = wrapper => {
  466. const children$1 = children(wrapper);
  467. if (children$1.length > 0) {
  468. after(wrapper, children$1);
  469. }
  470. remove(wrapper);
  471. };
  472. const descendants = (scope, selector) => all(selector, scope);
  473. const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is$2(e, selector), isRoot);
  474. const descendant = (scope, selector) => one(selector, scope);
  475. const closest = (scope, selector, isRoot) => {
  476. const is = (element, selector) => is$2(element, selector);
  477. return ClosestOrAncestor(is, ancestor, scope, selector, isRoot);
  478. };
  479. const NodeValue = (is, name) => {
  480. const get = element => {
  481. if (!is(element)) {
  482. throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
  483. }
  484. return getOption(element).getOr('');
  485. };
  486. const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
  487. const set = (element, value) => {
  488. if (!is(element)) {
  489. throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
  490. }
  491. element.dom.nodeValue = value;
  492. };
  493. return {
  494. get,
  495. getOption,
  496. set
  497. };
  498. };
  499. const api = NodeValue(isText, 'text');
  500. const get = element => api.get(element);
  501. const set = (element, value) => api.set(element, value);
  502. var TagBoundaries = [
  503. 'body',
  504. 'p',
  505. 'div',
  506. 'article',
  507. 'aside',
  508. 'figcaption',
  509. 'figure',
  510. 'footer',
  511. 'header',
  512. 'nav',
  513. 'section',
  514. 'ol',
  515. 'ul',
  516. 'li',
  517. 'table',
  518. 'thead',
  519. 'tbody',
  520. 'tfoot',
  521. 'caption',
  522. 'tr',
  523. 'td',
  524. 'th',
  525. 'h1',
  526. 'h2',
  527. 'h3',
  528. 'h4',
  529. 'h5',
  530. 'h6',
  531. 'blockquote',
  532. 'pre',
  533. 'address'
  534. ];
  535. var DomUniverse = () => {
  536. const clone$1 = element => {
  537. return SugarElement.fromDom(element.dom.cloneNode(false));
  538. };
  539. const document = element => documentOrOwner(element).dom;
  540. const isBoundary = element => {
  541. if (!isElement(element)) {
  542. return false;
  543. }
  544. if (name(element) === 'body') {
  545. return true;
  546. }
  547. return contains(TagBoundaries, name(element));
  548. };
  549. const isEmptyTag = element => {
  550. if (!isElement(element)) {
  551. return false;
  552. }
  553. return contains([
  554. 'br',
  555. 'img',
  556. 'hr',
  557. 'input'
  558. ], name(element));
  559. };
  560. const isNonEditable = element => isElement(element) && get$2(element, 'contenteditable') === 'false';
  561. const comparePosition = (element, other) => {
  562. return element.dom.compareDocumentPosition(other.dom);
  563. };
  564. const copyAttributesTo = (source, destination) => {
  565. const as = clone(source);
  566. setAll(destination, as);
  567. };
  568. const isSpecial = element => {
  569. const tag = name(element);
  570. return contains([
  571. 'script',
  572. 'noscript',
  573. 'iframe',
  574. 'noframes',
  575. 'noembed',
  576. 'title',
  577. 'style',
  578. 'textarea',
  579. 'xmp'
  580. ], tag);
  581. };
  582. const getLanguage = element => isElement(element) ? getOpt(element, 'lang') : Optional.none();
  583. return {
  584. up: constant({
  585. selector: ancestor,
  586. closest: closest,
  587. predicate: ancestor$1,
  588. all: parents
  589. }),
  590. down: constant({
  591. selector: descendants,
  592. predicate: descendants$1
  593. }),
  594. styles: constant({
  595. get: get$1,
  596. getRaw: getRaw,
  597. set: set$1,
  598. remove: remove$1
  599. }),
  600. attrs: constant({
  601. get: get$2,
  602. set: set$2,
  603. remove: remove$2,
  604. copyTo: copyAttributesTo
  605. }),
  606. insert: constant({
  607. before: before,
  608. after: after$1,
  609. afterAll: after,
  610. append: append$1,
  611. appendAll: append,
  612. prepend: prepend,
  613. wrap: wrap
  614. }),
  615. remove: constant({
  616. unwrap: unwrap,
  617. remove: remove
  618. }),
  619. create: constant({
  620. nu: SugarElement.fromTag,
  621. clone: clone$1,
  622. text: SugarElement.fromText
  623. }),
  624. query: constant({
  625. comparePosition,
  626. prevSibling: prevSibling,
  627. nextSibling: nextSibling
  628. }),
  629. property: constant({
  630. children: children,
  631. name: name,
  632. parent: parent,
  633. document,
  634. isText: isText,
  635. isComment: isComment,
  636. isElement: isElement,
  637. isSpecial,
  638. getLanguage,
  639. getText: get,
  640. setText: set,
  641. isBoundary,
  642. isEmptyTag,
  643. isNonEditable
  644. }),
  645. eq: eq,
  646. is: is$1
  647. };
  648. };
  649. const point = (element, offset) => ({
  650. element,
  651. offset
  652. });
  653. const scan = (universe, element, direction) => {
  654. if (universe.property().isText(element) && universe.property().getText(element).trim().length === 0 || universe.property().isComment(element)) {
  655. return direction(element).bind(elem => {
  656. return scan(universe, elem, direction).orThunk(() => {
  657. return Optional.some(elem);
  658. });
  659. });
  660. } else {
  661. return Optional.none();
  662. }
  663. };
  664. const toEnd = (universe, element) => {
  665. if (universe.property().isText(element)) {
  666. return universe.property().getText(element).length;
  667. }
  668. const children = universe.property().children(element);
  669. return children.length;
  670. };
  671. const freefallRtl$2 = (universe, element) => {
  672. const candidate = scan(universe, element, universe.query().prevSibling).getOr(element);
  673. if (universe.property().isText(candidate)) {
  674. return point(candidate, toEnd(universe, candidate));
  675. }
  676. const children = universe.property().children(candidate);
  677. return children.length > 0 ? freefallRtl$2(universe, children[children.length - 1]) : point(candidate, toEnd(universe, candidate));
  678. };
  679. const freefallRtl$1 = freefallRtl$2;
  680. const universe = DomUniverse();
  681. const freefallRtl = element => {
  682. return freefallRtl$1(universe, element);
  683. };
  684. const fireToggleAccordionEvent = (editor, element, state) => editor.dispatch('ToggledAccordion', {
  685. element,
  686. state
  687. });
  688. const fireToggleAllAccordionsEvent = (editor, elements, state) => editor.dispatch('ToggledAllAccordions', {
  689. elements,
  690. state
  691. });
  692. const accordionTag = 'details';
  693. const accordionDetailsClass = 'mce-accordion';
  694. const accordionSummaryClass = 'mce-accordion-summary';
  695. const accordionBodyWrapperClass = 'mce-accordion-body';
  696. const accordionBodyWrapperTag = 'div';
  697. var global$3 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  698. const isSummary = node => (node === null || node === void 0 ? void 0 : node.nodeName) === 'SUMMARY';
  699. const isDetails = node => (node === null || node === void 0 ? void 0 : node.nodeName) === 'DETAILS';
  700. const isOpen = details => details.hasAttribute('open');
  701. const isInSummary = editor => {
  702. const node = editor.selection.getNode();
  703. return isSummary(node) || Boolean(editor.dom.getParent(node, isSummary));
  704. };
  705. const isAtDetailsStart = editor => {
  706. const rng = editor.selection.getRng();
  707. return isDetails(rng.startContainer) && rng.collapsed && rng.startOffset === 0;
  708. };
  709. const isInsertAllowed = editor => !isInSummary(editor) && editor.dom.isEditable(editor.selection.getNode());
  710. const getSelectedDetails = editor => Optional.from(editor.dom.getParent(editor.selection.getNode(), isDetails));
  711. const isDetailsSelected = editor => getSelectedDetails(editor).isSome();
  712. const insertBogus = element => {
  713. element.innerHTML = '<br data-mce-bogus="1" />';
  714. return element;
  715. };
  716. const createParagraph = editor => insertBogus(editor.dom.create('p'));
  717. const createSummary = editor => insertBogus(editor.dom.create('summary'));
  718. const insertAndSelectParagraphAfter = (editor, target) => {
  719. const paragraph = createParagraph(editor);
  720. target.insertAdjacentElement('afterend', paragraph);
  721. editor.selection.setCursorLocation(paragraph, 0);
  722. };
  723. const normalizeContent = (editor, accordion) => {
  724. if (isSummary(accordion === null || accordion === void 0 ? void 0 : accordion.lastChild)) {
  725. const paragraph = createParagraph(editor);
  726. accordion.appendChild(paragraph);
  727. editor.selection.setCursorLocation(paragraph, 0);
  728. }
  729. };
  730. const normalizeSummary = (editor, accordion) => {
  731. if (!isSummary(accordion === null || accordion === void 0 ? void 0 : accordion.firstChild)) {
  732. const summary = createSummary(editor);
  733. accordion.prepend(summary);
  734. editor.selection.setCursorLocation(summary, 0);
  735. }
  736. };
  737. const normalizeAccordion = editor => accordion => {
  738. normalizeContent(editor, accordion);
  739. normalizeSummary(editor, accordion);
  740. };
  741. const normalizeDetails = editor => {
  742. global$3.each(global$3.grep(editor.dom.select('details', editor.getBody())), normalizeAccordion(editor));
  743. };
  744. const insertAccordion = editor => {
  745. if (!isInsertAllowed(editor)) {
  746. return;
  747. }
  748. const editorBody = SugarElement.fromDom(editor.getBody());
  749. const uid = generate('acc');
  750. const summaryText = editor.dom.encode(editor.selection.getRng().toString() || editor.translate('Accordion summary...'));
  751. const bodyText = editor.dom.encode(editor.translate('Accordion body...'));
  752. const accordionSummaryHtml = `<summary class="${ accordionSummaryClass }">${ summaryText }</summary>`;
  753. const accordionBodyHtml = `<${ accordionBodyWrapperTag } class="${ accordionBodyWrapperClass }"><p>${ bodyText }</p></${ accordionBodyWrapperTag }>`;
  754. editor.undoManager.transact(() => {
  755. editor.insertContent([
  756. `<details data-mce-id="${ uid }" class="${ accordionDetailsClass }" open="open">`,
  757. accordionSummaryHtml,
  758. accordionBodyHtml,
  759. `</details>`
  760. ].join(''));
  761. descendant(editorBody, `[data-mce-id="${ uid }"]`).each(detailsElm => {
  762. remove$2(detailsElm, 'data-mce-id');
  763. descendant(detailsElm, `summary`).each(summaryElm => {
  764. const rng = editor.dom.createRng();
  765. const des = freefallRtl(summaryElm);
  766. rng.setStart(des.element.dom, des.offset);
  767. rng.setEnd(des.element.dom, des.offset);
  768. editor.selection.setRng(rng);
  769. });
  770. });
  771. });
  772. };
  773. const toggleDetailsElement = (details, state) => {
  774. const shouldOpen = state !== null && state !== void 0 ? state : !isOpen(details);
  775. if (shouldOpen) {
  776. details.setAttribute('open', 'open');
  777. } else {
  778. details.removeAttribute('open');
  779. }
  780. return shouldOpen;
  781. };
  782. const toggleAccordion = (editor, state) => {
  783. getSelectedDetails(editor).each(details => {
  784. fireToggleAccordionEvent(editor, details, toggleDetailsElement(details, state));
  785. });
  786. };
  787. const removeAccordion = editor => {
  788. getSelectedDetails(editor).each(details => {
  789. const {nextSibling} = details;
  790. if (nextSibling) {
  791. editor.selection.select(nextSibling, true);
  792. editor.selection.collapse(true);
  793. } else {
  794. insertAndSelectParagraphAfter(editor, details);
  795. }
  796. details.remove();
  797. });
  798. };
  799. const toggleAllAccordions = (editor, state) => {
  800. const accordions = Array.from(editor.getBody().querySelectorAll('details'));
  801. if (accordions.length === 0) {
  802. return;
  803. }
  804. each$1(accordions, accordion => toggleDetailsElement(accordion, state !== null && state !== void 0 ? state : !isOpen(accordion)));
  805. fireToggleAllAccordionsEvent(editor, accordions, state);
  806. };
  807. const register$1 = editor => {
  808. editor.addCommand('InsertAccordion', () => insertAccordion(editor));
  809. editor.addCommand('ToggleAccordion', (_ui, value) => toggleAccordion(editor, value));
  810. editor.addCommand('ToggleAllAccordions', (_ui, value) => toggleAllAccordions(editor, value));
  811. editor.addCommand('RemoveAccordion', () => removeAccordion(editor));
  812. };
  813. var global$2 = tinymce.util.Tools.resolve('tinymce.html.Node');
  814. const getClassList = node => {
  815. var _a, _b;
  816. return (_b = (_a = node.attr('class')) === null || _a === void 0 ? void 0 : _a.split(' ')) !== null && _b !== void 0 ? _b : [];
  817. };
  818. const addClasses = (node, classes) => {
  819. const classListSet = new Set([
  820. ...getClassList(node),
  821. ...classes
  822. ]);
  823. const newClassList = Array.from(classListSet);
  824. if (newClassList.length > 0) {
  825. node.attr('class', newClassList.join(' '));
  826. }
  827. };
  828. const removeClasses = (node, classes) => {
  829. const newClassList = filter(getClassList(node), clazz => !classes.has(clazz));
  830. node.attr('class', newClassList.length > 0 ? newClassList.join(' ') : null);
  831. };
  832. const isAccordionDetailsNode = node => node.name === accordionTag && contains(getClassList(node), accordionDetailsClass);
  833. const isAccordionBodyWrapperNode = node => node.name === accordionBodyWrapperTag && contains(getClassList(node), accordionBodyWrapperClass);
  834. const getAccordionChildren = accordionNode => {
  835. const children = accordionNode.children();
  836. let summaryNode;
  837. let wrapperNode;
  838. const otherNodes = [];
  839. for (let i = 0; i < children.length; i++) {
  840. const child = children[i];
  841. if (child.name === 'summary' && isNullable(summaryNode)) {
  842. summaryNode = child;
  843. } else if (isAccordionBodyWrapperNode(child) && isNullable(wrapperNode)) {
  844. wrapperNode = child;
  845. } else {
  846. otherNodes.push(child);
  847. }
  848. }
  849. return {
  850. summaryNode,
  851. wrapperNode,
  852. otherNodes
  853. };
  854. };
  855. const padInputNode = node => {
  856. const br = new global$2('br', 1);
  857. br.attr('data-mce-bogus', '1');
  858. node.empty();
  859. node.append(br);
  860. };
  861. const setup$2 = editor => {
  862. editor.on('PreInit', () => {
  863. const {serializer, parser} = editor;
  864. parser.addNodeFilter(accordionTag, nodes => {
  865. for (let i = 0; i < nodes.length; i++) {
  866. const node = nodes[i];
  867. if (isAccordionDetailsNode(node)) {
  868. const accordionNode = node;
  869. const {summaryNode, wrapperNode, otherNodes} = getAccordionChildren(accordionNode);
  870. const hasSummaryNode = isNonNullable(summaryNode);
  871. const newSummaryNode = hasSummaryNode ? summaryNode : new global$2('summary', 1);
  872. if (isNullable(newSummaryNode.firstChild)) {
  873. padInputNode(newSummaryNode);
  874. }
  875. addClasses(newSummaryNode, [accordionSummaryClass]);
  876. if (!hasSummaryNode) {
  877. if (isNonNullable(accordionNode.firstChild)) {
  878. accordionNode.insert(newSummaryNode, accordionNode.firstChild, true);
  879. } else {
  880. accordionNode.append(newSummaryNode);
  881. }
  882. }
  883. const hasWrapperNode = isNonNullable(wrapperNode);
  884. const newWrapperNode = hasWrapperNode ? wrapperNode : new global$2(accordionBodyWrapperTag, 1);
  885. newWrapperNode.attr('data-mce-bogus', '1');
  886. addClasses(newWrapperNode, [accordionBodyWrapperClass]);
  887. if (otherNodes.length > 0) {
  888. for (let j = 0; j < otherNodes.length; j++) {
  889. const otherNode = otherNodes[j];
  890. newWrapperNode.append(otherNode);
  891. }
  892. }
  893. if (isNullable(newWrapperNode.firstChild)) {
  894. const pNode = new global$2('p', 1);
  895. padInputNode(pNode);
  896. newWrapperNode.append(pNode);
  897. }
  898. if (!hasWrapperNode) {
  899. accordionNode.append(newWrapperNode);
  900. }
  901. }
  902. }
  903. });
  904. serializer.addNodeFilter(accordionTag, nodes => {
  905. const summaryClassRemoveSet = new Set([accordionSummaryClass]);
  906. for (let i = 0; i < nodes.length; i++) {
  907. const node = nodes[i];
  908. if (isAccordionDetailsNode(node)) {
  909. const accordionNode = node;
  910. const {summaryNode, wrapperNode} = getAccordionChildren(accordionNode);
  911. if (isNonNullable(summaryNode)) {
  912. removeClasses(summaryNode, summaryClassRemoveSet);
  913. }
  914. if (isNonNullable(wrapperNode)) {
  915. wrapperNode.unwrap();
  916. }
  917. }
  918. }
  919. });
  920. });
  921. };
  922. var global$1 = tinymce.util.Tools.resolve('tinymce.util.VK');
  923. const setupEnterKeyInSummary = editor => {
  924. editor.on('keydown', event => {
  925. if (!event.shiftKey && event.keyCode === global$1.ENTER && isInSummary(editor) || isAtDetailsStart(editor)) {
  926. event.preventDefault();
  927. editor.execCommand('ToggleAccordion');
  928. }
  929. });
  930. };
  931. const setup$1 = editor => {
  932. setupEnterKeyInSummary(editor);
  933. editor.on('ExecCommand', e => {
  934. const cmd = e.command.toLowerCase();
  935. if ((cmd === 'delete' || cmd === 'forwarddelete') && isDetailsSelected(editor)) {
  936. normalizeDetails(editor);
  937. }
  938. });
  939. };
  940. var global = tinymce.util.Tools.resolve('tinymce.Env');
  941. const setup = editor => {
  942. if (global.browser.isSafari()) {
  943. editor.on('click', e => {
  944. if (isSummary(e.target)) {
  945. const summary = e.target;
  946. const rng = editor.selection.getRng();
  947. if (rng.collapsed && rng.startContainer === summary.parentNode && rng.startOffset === 0) {
  948. editor.selection.setCursorLocation(summary, 0);
  949. }
  950. }
  951. });
  952. }
  953. };
  954. const onSetup = editor => buttonApi => {
  955. const onNodeChange = () => buttonApi.setEnabled(isInsertAllowed(editor));
  956. editor.on('NodeChange', onNodeChange);
  957. return () => editor.off('NodeChange', onNodeChange);
  958. };
  959. const register = editor => {
  960. const onAction = () => editor.execCommand('InsertAccordion');
  961. editor.ui.registry.addButton('accordion', {
  962. icon: 'accordion',
  963. tooltip: 'Insert accordion',
  964. onSetup: onSetup(editor),
  965. onAction
  966. });
  967. editor.ui.registry.addMenuItem('accordion', {
  968. icon: 'accordion',
  969. text: 'Accordion',
  970. onSetup: onSetup(editor),
  971. onAction
  972. });
  973. editor.ui.registry.addToggleButton('accordiontoggle', {
  974. icon: 'accordion-toggle',
  975. tooltip: 'Toggle accordion',
  976. onAction: () => editor.execCommand('ToggleAccordion')
  977. });
  978. editor.ui.registry.addToggleButton('accordionremove', {
  979. icon: 'remove',
  980. tooltip: 'Delete accordion',
  981. onAction: () => editor.execCommand('RemoveAccordion')
  982. });
  983. editor.ui.registry.addContextToolbar('accordion', {
  984. predicate: accordion => editor.dom.is(accordion, 'details') && editor.getBody().contains(accordion) && editor.dom.isEditable(accordion.parentNode),
  985. items: 'accordiontoggle accordionremove',
  986. scope: 'node',
  987. position: 'node'
  988. });
  989. };
  990. var Plugin = () => {
  991. global$4.add('accordion', editor => {
  992. register(editor);
  993. register$1(editor);
  994. setup$1(editor);
  995. setup$2(editor);
  996. setup(editor);
  997. });
  998. };
  999. Plugin();
  1000. })();