plugin.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. /**
  2. * TinyMCE version 8.0.2 (2025-08-14)
  3. */
  4. (function () {
  5. 'use strict';
  6. /* eslint-disable @typescript-eslint/no-wrapper-object-types */
  7. const hasProto = (v, constructor, predicate) => {
  8. var _a;
  9. if (predicate(v, constructor.prototype)) {
  10. return true;
  11. }
  12. else {
  13. // String-based fallback time
  14. return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
  15. }
  16. };
  17. const typeOf = (x) => {
  18. const t = typeof x;
  19. if (x === null) {
  20. return 'null';
  21. }
  22. else if (t === 'object' && Array.isArray(x)) {
  23. return 'array';
  24. }
  25. else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
  26. return 'string';
  27. }
  28. else {
  29. return t;
  30. }
  31. };
  32. const isType = (type) => (value) => typeOf(value) === type;
  33. const isSimpleType = (type) => (value) => typeof value === type;
  34. const eq = (t) => (a) => t === a;
  35. const isString = isType('string');
  36. const isUndefined = eq(undefined);
  37. const isNullable = (a) => a === null || a === undefined;
  38. const isNonNullable = (a) => !isNullable(a);
  39. const isFunction = isSimpleType('function');
  40. const constant = (value) => {
  41. return () => {
  42. return value;
  43. };
  44. };
  45. const never = constant(false);
  46. /**
  47. * The `Optional` type represents a value (of any type) that potentially does
  48. * not exist. Any `Optional<T>` can either be a `Some<T>` (in which case the
  49. * value does exist) or a `None` (in which case the value does not exist). This
  50. * module defines a whole lot of FP-inspired utility functions for dealing with
  51. * `Optional` objects.
  52. *
  53. * Comparison with null or undefined:
  54. * - We don't get fancy null coalescing operators with `Optional`
  55. * - We do get fancy helper functions with `Optional`
  56. * - `Optional` support nesting, and allow for the type to still be nullable (or
  57. * another `Optional`)
  58. * - There is no option to turn off strict-optional-checks like there is for
  59. * strict-null-checks
  60. */
  61. class Optional {
  62. // The internal representation has a `tag` and a `value`, but both are
  63. // private: able to be console.logged, but not able to be accessed by code
  64. constructor(tag, value) {
  65. this.tag = tag;
  66. this.value = value;
  67. }
  68. // --- Identities ---
  69. /**
  70. * Creates a new `Optional<T>` that **does** contain a value.
  71. */
  72. static some(value) {
  73. return new Optional(true, value);
  74. }
  75. /**
  76. * Create a new `Optional<T>` that **does not** contain a value. `T` can be
  77. * any type because we don't actually have a `T`.
  78. */
  79. static none() {
  80. return Optional.singletonNone;
  81. }
  82. /**
  83. * Perform a transform on an `Optional` type. Regardless of whether this
  84. * `Optional` contains a value or not, `fold` will return a value of type `U`.
  85. * If this `Optional` does not contain a value, the `U` will be created by
  86. * calling `onNone`. If this `Optional` does contain a value, the `U` will be
  87. * created by calling `onSome`.
  88. *
  89. * For the FP enthusiasts in the room, this function:
  90. * 1. Could be used to implement all of the functions below
  91. * 2. Forms a catamorphism
  92. */
  93. fold(onNone, onSome) {
  94. if (this.tag) {
  95. return onSome(this.value);
  96. }
  97. else {
  98. return onNone();
  99. }
  100. }
  101. /**
  102. * Determine if this `Optional` object contains a value.
  103. */
  104. isSome() {
  105. return this.tag;
  106. }
  107. /**
  108. * Determine if this `Optional` object **does not** contain a value.
  109. */
  110. isNone() {
  111. return !this.tag;
  112. }
  113. // --- Functor (name stolen from Haskell / maths) ---
  114. /**
  115. * Perform a transform on an `Optional` object, **if** there is a value. If
  116. * you provide a function to turn a T into a U, this is the function you use
  117. * to turn an `Optional<T>` into an `Optional<U>`. If this **does** contain
  118. * a value then the output will also contain a value (that value being the
  119. * output of `mapper(this.value)`), and if this **does not** contain a value
  120. * then neither will the output.
  121. */
  122. map(mapper) {
  123. if (this.tag) {
  124. return Optional.some(mapper(this.value));
  125. }
  126. else {
  127. return Optional.none();
  128. }
  129. }
  130. // --- Monad (name stolen from Haskell / maths) ---
  131. /**
  132. * Perform a transform on an `Optional` object, **if** there is a value.
  133. * Unlike `map`, here the transform itself also returns an `Optional`.
  134. */
  135. bind(binder) {
  136. if (this.tag) {
  137. return binder(this.value);
  138. }
  139. else {
  140. return Optional.none();
  141. }
  142. }
  143. // --- Traversable (name stolen from Haskell / maths) ---
  144. /**
  145. * For a given predicate, this function finds out if there **exists** a value
  146. * inside this `Optional` object that meets the predicate. In practice, this
  147. * means that for `Optional`s that do not contain a value it returns false (as
  148. * no predicate-meeting value exists).
  149. */
  150. exists(predicate) {
  151. return this.tag && predicate(this.value);
  152. }
  153. /**
  154. * For a given predicate, this function finds out if **all** the values inside
  155. * this `Optional` object meet the predicate. In practice, this means that
  156. * for `Optional`s that do not contain a value it returns true (as all 0
  157. * objects do meet the predicate).
  158. */
  159. forall(predicate) {
  160. return !this.tag || predicate(this.value);
  161. }
  162. filter(predicate) {
  163. if (!this.tag || predicate(this.value)) {
  164. return this;
  165. }
  166. else {
  167. return Optional.none();
  168. }
  169. }
  170. // --- Getters ---
  171. /**
  172. * Get the value out of the inside of the `Optional` object, using a default
  173. * `replacement` value if the provided `Optional` object does not contain a
  174. * value.
  175. */
  176. getOr(replacement) {
  177. return this.tag ? this.value : replacement;
  178. }
  179. /**
  180. * Get the value out of the inside of the `Optional` object, using a default
  181. * `replacement` value if the provided `Optional` object does not contain a
  182. * value. Unlike `getOr`, in this method the `replacement` object is also
  183. * `Optional` - meaning that this method will always return an `Optional`.
  184. */
  185. or(replacement) {
  186. return this.tag ? this : replacement;
  187. }
  188. /**
  189. * Get the value out of the inside of the `Optional` object, using a default
  190. * `replacement` value if the provided `Optional` object does not contain a
  191. * value. Unlike `getOr`, in this method the `replacement` value is
  192. * "thunked" - that is to say that you don't pass a value to `getOrThunk`, you
  193. * pass a function which (if called) will **return** the `value` you want to
  194. * use.
  195. */
  196. getOrThunk(thunk) {
  197. return this.tag ? this.value : thunk();
  198. }
  199. /**
  200. * Get the value out of the inside of the `Optional` object, using a default
  201. * `replacement` value if the provided Optional object does not contain a
  202. * value.
  203. *
  204. * Unlike `or`, in this method the `replacement` value is "thunked" - that is
  205. * to say that you don't pass a value to `orThunk`, you pass a function which
  206. * (if called) will **return** the `value` you want to use.
  207. *
  208. * Unlike `getOrThunk`, in this method the `replacement` value is also
  209. * `Optional`, meaning that this method will always return an `Optional`.
  210. */
  211. orThunk(thunk) {
  212. return this.tag ? this : thunk();
  213. }
  214. /**
  215. * Get the value out of the inside of the `Optional` object, throwing an
  216. * exception if the provided `Optional` object does not contain a value.
  217. *
  218. * WARNING:
  219. * You should only be using this function if you know that the `Optional`
  220. * object **is not** empty (otherwise you're throwing exceptions in production
  221. * code, which is bad).
  222. *
  223. * In tests this is more acceptable.
  224. *
  225. * Prefer other methods to this, such as `.each`.
  226. */
  227. getOrDie(message) {
  228. if (!this.tag) {
  229. throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
  230. }
  231. else {
  232. return this.value;
  233. }
  234. }
  235. // --- Interop with null and undefined ---
  236. /**
  237. * Creates an `Optional` value from a nullable (or undefined-able) input.
  238. * Null, or undefined, is converted to `None`, and anything else is converted
  239. * to `Some`.
  240. */
  241. static from(value) {
  242. return isNonNullable(value) ? Optional.some(value) : Optional.none();
  243. }
  244. /**
  245. * Converts an `Optional` to a nullable type, by getting the value if it
  246. * exists, or returning `null` if it does not.
  247. */
  248. getOrNull() {
  249. return this.tag ? this.value : null;
  250. }
  251. /**
  252. * Converts an `Optional` to an undefined-able type, by getting the value if
  253. * it exists, or returning `undefined` if it does not.
  254. */
  255. getOrUndefined() {
  256. return this.value;
  257. }
  258. // --- Utilities ---
  259. /**
  260. * If the `Optional` contains a value, perform an action on that value.
  261. * Unlike the rest of the methods on this type, `.each` has side-effects. If
  262. * you want to transform an `Optional<T>` **into** something, then this is not
  263. * the method for you. If you want to use an `Optional<T>` to **do**
  264. * something, then this is the method for you - provided you're okay with not
  265. * doing anything in the case where the `Optional` doesn't have a value inside
  266. * it. If you're not sure whether your use-case fits into transforming
  267. * **into** something or **doing** something, check whether it has a return
  268. * value. If it does, you should be performing a transform.
  269. */
  270. each(worker) {
  271. if (this.tag) {
  272. worker(this.value);
  273. }
  274. }
  275. /**
  276. * Turn the `Optional` object into an array that contains all of the values
  277. * stored inside the `Optional`. In practice, this means the output will have
  278. * either 0 or 1 elements.
  279. */
  280. toArray() {
  281. return this.tag ? [this.value] : [];
  282. }
  283. /**
  284. * Turn the `Optional` object into a string for debugging or printing. Not
  285. * recommended for production code, but good for debugging. Also note that
  286. * these days an `Optional` object can be logged to the console directly, and
  287. * its inner value (if it exists) will be visible.
  288. */
  289. toString() {
  290. return this.tag ? `some(${this.value})` : 'none()';
  291. }
  292. }
  293. // Sneaky optimisation: every instance of Optional.none is identical, so just
  294. // reuse the same object
  295. Optional.singletonNone = new Optional(false);
  296. const nativeSlice = Array.prototype.slice;
  297. const nativeIndexOf = Array.prototype.indexOf;
  298. const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
  299. const contains = (xs, x) => rawIndexOf(xs, x) > -1;
  300. const map = (xs, f) => {
  301. // pre-allocating array size when it's guaranteed to be known
  302. // http://jsperf.com/push-allocated-vs-dynamic/22
  303. const len = xs.length;
  304. const r = new Array(len);
  305. for (let i = 0; i < len; i++) {
  306. const x = xs[i];
  307. r[i] = f(x, i);
  308. }
  309. return r;
  310. };
  311. const filter = (xs, pred) => {
  312. const r = [];
  313. for (let i = 0, len = xs.length; i < len; i++) {
  314. const x = xs[i];
  315. if (pred(x, i)) {
  316. r.push(x);
  317. }
  318. }
  319. return r;
  320. };
  321. const findUntil = (xs, pred, until) => {
  322. for (let i = 0, len = xs.length; i < len; i++) {
  323. const x = xs[i];
  324. if (pred(x, i)) {
  325. return Optional.some(x);
  326. }
  327. else if (until(x, i)) {
  328. break;
  329. }
  330. }
  331. return Optional.none();
  332. };
  333. const find = (xs, pred) => {
  334. return findUntil(xs, pred, never);
  335. };
  336. const sort = (xs, comparator) => {
  337. const copy = nativeSlice.call(xs, 0);
  338. copy.sort(comparator);
  339. return copy;
  340. };
  341. isFunction(Array.from) ? Array.from : (x) => nativeSlice.call(x);
  342. // There are many variations of Object iteration that are faster than the 'for-in' style:
  343. // http://jsperf.com/object-keys-iteration/107
  344. //
  345. // Use the native keys if it is available (IE9+), otherwise fall back to manually filtering
  346. const keys = Object.keys;
  347. const hasOwnProperty = Object.hasOwnProperty;
  348. const get$1 = (obj, key) => {
  349. return has(obj, key) ? Optional.from(obj[key]) : Optional.none();
  350. };
  351. const has = (obj, key) => hasOwnProperty.call(obj, key);
  352. const Cell = (initial) => {
  353. let value = initial;
  354. const get = () => {
  355. return value;
  356. };
  357. const set = (v) => {
  358. value = v;
  359. };
  360. return {
  361. get,
  362. set
  363. };
  364. };
  365. /**
  366. * Adds two numbers, and wrap to a range.
  367. * If the result overflows to the right, snap to the left.
  368. * If the result overflows to the left, snap to the right.
  369. */
  370. // the division is meant to get a number between 0 and 1 for more information check this discussion: https://stackoverflow.com/questions/58285941/how-to-replace-math-random-with-crypto-getrandomvalues-and-keep-same-result
  371. const random = () => window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;
  372. /**
  373. * Generate a unique identifier.
  374. *
  375. * The unique portion of the identifier only contains an underscore
  376. * and digits, so that it may safely be used within HTML attributes.
  377. *
  378. * The chance of generating a non-unique identifier has been minimized
  379. * by combining the current time, a random number and a one-up counter.
  380. *
  381. * generate :: String -> String
  382. */
  383. let unique = 0;
  384. const generate = (prefix) => {
  385. const date = new Date();
  386. const time = date.getTime();
  387. const random$1 = Math.floor(random() * 1000000000);
  388. unique++;
  389. return prefix + '_' + random$1 + unique + String(time);
  390. };
  391. const cat = (arr) => {
  392. const r = [];
  393. const push = (x) => {
  394. r.push(x);
  395. };
  396. for (let i = 0; i < arr.length; i++) {
  397. arr[i].each(push);
  398. }
  399. return r;
  400. };
  401. var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');
  402. const get = (customTabs) => {
  403. const addTab = (spec) => {
  404. var _a;
  405. const name = (_a = spec.name) !== null && _a !== void 0 ? _a : generate('tab-name');
  406. const currentCustomTabs = customTabs.get();
  407. currentCustomTabs[name] = spec;
  408. customTabs.set(currentCustomTabs);
  409. };
  410. return {
  411. addTab
  412. };
  413. };
  414. const register$2 = (editor, dialogOpener) => {
  415. editor.addCommand('mceHelp', dialogOpener);
  416. };
  417. const option = (name) => (editor) => editor.options.get(name);
  418. const register$1 = (editor) => {
  419. const registerOption = editor.options.register;
  420. registerOption('help_tabs', {
  421. processor: 'array'
  422. });
  423. };
  424. const getHelpTabs = option('help_tabs');
  425. const getForcedPlugins = option('forced_plugins');
  426. const register = (editor, dialogOpener) => {
  427. editor.ui.registry.addButton('help', {
  428. icon: 'help',
  429. tooltip: 'Help',
  430. onAction: dialogOpener,
  431. context: 'any'
  432. });
  433. editor.ui.registry.addMenuItem('help', {
  434. text: 'Help',
  435. icon: 'help',
  436. shortcut: 'Alt+0',
  437. onAction: dialogOpener,
  438. context: 'any'
  439. });
  440. };
  441. var global$3 = tinymce.util.Tools.resolve('tinymce.Resource');
  442. var global$2 = tinymce.util.Tools.resolve('tinymce.util.I18n');
  443. const pLoadHtmlByLangCode = (baseUrl, langCode) => global$3.load(`tinymce.html-i18n.help-keynav.${langCode}`, `${baseUrl}/js/i18n/keynav/${langCode}.js`);
  444. const pLoadI18nHtml = (baseUrl) =>
  445. // TINY-9928: Load language file for the current language, or English if the file is not available
  446. pLoadHtmlByLangCode(baseUrl, global$2.getCode()).catch(() => pLoadHtmlByLangCode(baseUrl, 'en'));
  447. const initI18nLoad = (editor, baseUrl) => {
  448. editor.on('init', () => {
  449. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  450. pLoadI18nHtml(baseUrl);
  451. });
  452. };
  453. const pTab = async (pluginUrl) => {
  454. const body = {
  455. type: 'htmlpanel',
  456. presets: 'document',
  457. html: await pLoadI18nHtml(pluginUrl)
  458. };
  459. return {
  460. name: 'keyboardnav',
  461. title: 'Keyboard Navigation',
  462. items: [body]
  463. };
  464. };
  465. var global$1 = tinymce.util.Tools.resolve('tinymce.Env');
  466. // Converts shortcut format to Mac/PC variants
  467. const convertText = (source) => {
  468. const isMac = global$1.os.isMacOS() || global$1.os.isiOS();
  469. const mac = {
  470. alt: '&#x2325;',
  471. ctrl: '&#x2303;',
  472. shift: '&#x21E7;',
  473. meta: '&#x2318;',
  474. access: '&#x2303;&#x2325;'
  475. };
  476. const other = {
  477. meta: 'Ctrl ',
  478. access: 'Shift + Alt '
  479. };
  480. const replace = isMac ? mac : other;
  481. const shortcut = source.split('+');
  482. const updated = map(shortcut, (segment) => {
  483. // search lowercase, but if not found use the original
  484. const search = segment.toLowerCase().trim();
  485. return has(replace, search) ? replace[search] : segment;
  486. });
  487. return isMac ? (updated.join('')).replace(/\s/, '') : updated.join('+');
  488. };
  489. const shortcuts = [
  490. { shortcuts: ['Meta + B'], action: 'Bold' },
  491. { shortcuts: ['Meta + I'], action: 'Italic' },
  492. { shortcuts: ['Meta + U'], action: 'Underline' },
  493. { shortcuts: ['Meta + A'], action: 'Select all' },
  494. { shortcuts: ['Meta + Y', 'Meta + Shift + Z'], action: 'Redo' },
  495. { shortcuts: ['Meta + Z'], action: 'Undo' },
  496. { shortcuts: ['Access + 1'], action: 'Heading 1' },
  497. { shortcuts: ['Access + 2'], action: 'Heading 2' },
  498. { shortcuts: ['Access + 3'], action: 'Heading 3' },
  499. { shortcuts: ['Access + 4'], action: 'Heading 4' },
  500. { shortcuts: ['Access + 5'], action: 'Heading 5' },
  501. { shortcuts: ['Access + 6'], action: 'Heading 6' },
  502. { shortcuts: ['Access + 7'], action: 'Paragraph' },
  503. { shortcuts: ['Access + 8'], action: 'Div' },
  504. { shortcuts: ['Access + 9'], action: 'Address' },
  505. { shortcuts: ['Alt + 0'], action: 'Open help dialog' },
  506. { shortcuts: ['Alt + F9'], action: 'Focus to menubar' },
  507. { shortcuts: ['Alt + F10'], action: 'Focus to toolbar' },
  508. { shortcuts: ['Alt + F11'], action: 'Focus to element path' },
  509. { shortcuts: ['Alt + F12'], action: 'Focus to notification' },
  510. { shortcuts: ['Ctrl + F9'], action: 'Focus to contextual toolbar' },
  511. { shortcuts: ['Shift + Enter'], action: 'Open popup menu for split buttons' },
  512. { shortcuts: ['Meta + K'], action: 'Insert link (if link plugin activated)' },
  513. { shortcuts: ['Meta + S'], action: 'Save (if save plugin activated)' },
  514. { shortcuts: ['Meta + F'], action: 'Find (if searchreplace plugin activated)' },
  515. { shortcuts: ['Meta + Shift + F'], action: 'Switch to or from fullscreen mode' }
  516. ];
  517. const tab$2 = () => {
  518. const shortcutList = map(shortcuts, (shortcut) => {
  519. const shortcutText = map(shortcut.shortcuts, convertText).join(' or ');
  520. return [shortcut.action, shortcutText];
  521. });
  522. const tablePanel = {
  523. type: 'table',
  524. // TODO: Fix table styles #TINY-2909
  525. header: ['Action', 'Shortcut'],
  526. cells: shortcutList
  527. };
  528. return {
  529. name: 'shortcuts',
  530. title: 'Handy Shortcuts',
  531. items: [
  532. tablePanel
  533. ]
  534. };
  535. };
  536. // These lists are automatically sorted when generating the dialog.
  537. const urls = map([
  538. { key: 'accordion', name: 'Accordion' },
  539. { key: 'anchor', name: 'Anchor' },
  540. { key: 'autolink', name: 'Autolink' },
  541. { key: 'autoresize', name: 'Autoresize' },
  542. { key: 'autosave', name: 'Autosave' },
  543. { key: 'charmap', name: 'Character Map' },
  544. { key: 'code', name: 'Code' },
  545. { key: 'codesample', name: 'Code Sample' },
  546. { key: 'colorpicker', name: 'Color Picker' },
  547. { key: 'directionality', name: 'Directionality' },
  548. { key: 'emoticons', name: 'Emoticons' },
  549. { key: 'fullscreen', name: 'Full Screen' },
  550. { key: 'help', name: 'Help' },
  551. { key: 'image', name: 'Image' },
  552. { key: 'importcss', name: 'Import CSS' },
  553. { key: 'insertdatetime', name: 'Insert Date/Time' },
  554. { key: 'link', name: 'Link' },
  555. { key: 'lists', name: 'Lists' },
  556. { key: 'advlist', name: 'List Styles' },
  557. { key: 'media', name: 'Media' },
  558. { key: 'nonbreaking', name: 'Nonbreaking' },
  559. { key: 'pagebreak', name: 'Page Break' },
  560. { key: 'preview', name: 'Preview' },
  561. { key: 'quickbars', name: 'Quick Toolbars' },
  562. { key: 'save', name: 'Save' },
  563. { key: 'searchreplace', name: 'Search and Replace' },
  564. { key: 'table', name: 'Table' },
  565. { key: 'textcolor', name: 'Text Color' },
  566. { key: 'visualblocks', name: 'Visual Blocks' },
  567. { key: 'visualchars', name: 'Visual Characters' },
  568. { key: 'wordcount', name: 'Word Count' },
  569. // TODO: Add other premium plugins when they are included in the website
  570. { key: 'a11ychecker', name: 'Accessibility Checker', type: "premium" /* PluginType.Premium */ },
  571. { key: 'typography', name: 'Advanced Typography', type: "premium" /* PluginType.Premium */, slug: 'advanced-typography' },
  572. { key: 'ai', name: 'AI Assistant', type: "premium" /* PluginType.Premium */ },
  573. { key: 'casechange', name: 'Case Change', type: "premium" /* PluginType.Premium */ },
  574. { key: 'checklist', name: 'Checklist', type: "premium" /* PluginType.Premium */ },
  575. { key: 'advcode', name: 'Enhanced Code Editor', type: "premium" /* PluginType.Premium */ },
  576. { key: 'mediaembed', name: 'Enhanced Media Embed', type: "premium" /* PluginType.Premium */, slug: 'introduction-to-mediaembed' },
  577. { key: 'advtable', name: 'Enhanced Tables', type: "premium" /* PluginType.Premium */ },
  578. { key: 'exportpdf', name: 'Export to PDF', type: "premium" /* PluginType.Premium */ },
  579. { key: 'exportword', name: 'Export to Word', type: "premium" /* PluginType.Premium */ },
  580. { key: 'footnotes', name: 'Footnotes', type: "premium" /* PluginType.Premium */ },
  581. { key: 'formatpainter', name: 'Format Painter', type: "premium" /* PluginType.Premium */ },
  582. { key: 'editimage', name: 'Image Editing', type: "premium" /* PluginType.Premium */ },
  583. { key: 'uploadcare', name: 'Image Optimizer Powered by Uploadcare', type: "premium" /* PluginType.Premium */ },
  584. { key: 'importword', name: 'Import from Word', type: "premium" /* PluginType.Premium */ },
  585. { key: 'inlinecss', name: 'Inline CSS', type: "premium" /* PluginType.Premium */, slug: 'inline-css' },
  586. { key: 'linkchecker', name: 'Link Checker', type: "premium" /* PluginType.Premium */ },
  587. { key: 'math', name: 'Math', type: "premium" /* PluginType.Premium */ },
  588. { key: 'markdown', name: 'Markdown', type: "premium" /* PluginType.Premium */ },
  589. { key: 'mentions', name: 'Mentions', type: "premium" /* PluginType.Premium */ },
  590. { key: 'mergetags', name: 'Merge Tags', type: "premium" /* PluginType.Premium */ },
  591. { key: 'pageembed', name: 'Page Embed', type: "premium" /* PluginType.Premium */ },
  592. { key: 'permanentpen', name: 'Permanent Pen', type: "premium" /* PluginType.Premium */ },
  593. { key: 'powerpaste', name: 'PowerPaste', type: "premium" /* PluginType.Premium */, slug: 'introduction-to-powerpaste' },
  594. { key: 'revisionhistory', name: 'Revision History', type: "premium" /* PluginType.Premium */ },
  595. { key: 'tinymcespellchecker', name: 'Spell Checker', type: "premium" /* PluginType.Premium */, slug: 'introduction-to-tiny-spellchecker' },
  596. { key: 'suggestededits', name: 'Suggested Edits', type: "premium" /* PluginType.Premium */ },
  597. { key: 'autocorrect', name: 'Spelling Autocorrect', type: "premium" /* PluginType.Premium */ },
  598. { key: 'tableofcontents', name: 'Table of Contents', type: "premium" /* PluginType.Premium */ },
  599. { key: 'advtemplate', name: 'Templates', type: "premium" /* PluginType.Premium */, slug: 'advanced-templates' },
  600. { key: 'tinycomments', name: 'Tiny Comments', type: "premium" /* PluginType.Premium */, slug: 'introduction-to-tiny-comments' },
  601. { key: 'tinydrive', name: 'Tiny Drive', type: "premium" /* PluginType.Premium */, slug: 'tinydrive-introduction' },
  602. ], (item) => ({
  603. ...item,
  604. // Set the defaults/fallbacks for the plugin urls
  605. type: item.type || "opensource" /* PluginType.OpenSource */,
  606. slug: item.slug || item.key
  607. }));
  608. const tab$1 = (editor) => {
  609. const availablePlugins = () => {
  610. const premiumPlugins = filter(urls, ({ type }) => {
  611. return type === "premium" /* PluginUrls.PluginType.Premium */;
  612. });
  613. const sortedPremiumPlugins = sort(map(premiumPlugins, (p) => p.name), (s1, s2) => s1.localeCompare(s2));
  614. const premiumPluginList = map(sortedPremiumPlugins, (pluginName) => `<li>${pluginName}</li>`).join('');
  615. return '<div>' +
  616. '<p><b>' + global$2.translate('Premium plugins:') + '</b></p>' +
  617. '<ul>' +
  618. premiumPluginList +
  619. '<li class="tox-help__more-link" ">' +
  620. '<a href="https://www.tiny.cloud/pricing/?utm_campaign=help_dialog_plugin_tab&utm_source=tiny&utm_medium=referral&utm_term=read_more&utm_content=premium_plugin_heading" rel="noopener" target="_blank"' +
  621. ' data-alloy-tabstop="true" tabindex="-1">' + global$2.translate('Learn more...') + '</a></li>' +
  622. '</ul>' +
  623. '</div>';
  624. };
  625. const makeLink = (p) => `<a data-alloy-tabstop="true" tabindex="-1" href="${p.url}" target="_blank" rel="noopener">${p.name}</a>`;
  626. const identifyUnknownPlugin = (editor, key) => {
  627. const getMetadata = editor.plugins[key].getMetadata;
  628. if (isFunction(getMetadata)) {
  629. const metadata = getMetadata();
  630. return { name: metadata.name, html: makeLink(metadata) };
  631. }
  632. else {
  633. return { name: key, html: key };
  634. }
  635. };
  636. const getPluginData = (editor, key) => find(urls, (x) => {
  637. return x.key === key;
  638. }).fold(() => {
  639. return identifyUnknownPlugin(editor, key);
  640. }, (x) => {
  641. // We know this plugin, so use our stored details.
  642. const name = x.type === "premium" /* PluginUrls.PluginType.Premium */ ? `${x.name}*` : x.name;
  643. const html = makeLink({ name, url: `https://www.tiny.cloud/docs/tinymce/${tinymce.majorVersion}/${x.slug}/` });
  644. return { name, html };
  645. });
  646. const getPluginKeys = (editor) => {
  647. const keys$1 = keys(editor.plugins);
  648. const forcedPlugins = getForcedPlugins(editor);
  649. const hiddenPlugins = isUndefined(forcedPlugins) ? ['onboarding'] : forcedPlugins.concat(['onboarding']);
  650. return filter(keys$1, (k) => !contains(hiddenPlugins, k));
  651. };
  652. const pluginLister = (editor) => {
  653. const pluginKeys = getPluginKeys(editor);
  654. const sortedPluginData = sort(map(pluginKeys, (k) => getPluginData(editor, k)), (pd1, pd2) => pd1.name.localeCompare(pd2.name));
  655. const pluginLis = map(sortedPluginData, (key) => {
  656. return '<li>' + key.html + '</li>';
  657. });
  658. const count = pluginLis.length;
  659. const pluginsString = pluginLis.join('');
  660. const html = '<p><b>' + global$2.translate(['Plugins installed ({0}):', count]) + '</b></p>' +
  661. '<ul>' + pluginsString + '</ul>';
  662. return html;
  663. };
  664. const installedPlugins = (editor) => {
  665. if (editor == null) {
  666. return '';
  667. }
  668. return '<div>' +
  669. pluginLister(editor) +
  670. '</div>';
  671. };
  672. const htmlPanel = {
  673. type: 'htmlpanel',
  674. presets: 'document',
  675. html: [
  676. installedPlugins(editor),
  677. availablePlugins()
  678. ].join('')
  679. };
  680. return {
  681. name: 'plugins',
  682. title: 'Plugins',
  683. items: [
  684. htmlPanel
  685. ]
  686. };
  687. };
  688. var global = tinymce.util.Tools.resolve('tinymce.EditorManager');
  689. const tab = () => {
  690. const getVersion = (major, minor) => major.indexOf('@') === 0 ? 'X.X.X' : major + '.' + minor;
  691. const version = getVersion(global.majorVersion, global.minorVersion);
  692. const changeLogLink = '<a data-alloy-tabstop="true" tabindex="-1" href="https://www.tiny.cloud/docs/tinymce/8/changelog/?utm_campaign=help_dialog_version_tab&utm_source=tiny&utm_medium=referral" rel="noopener" target="_blank">TinyMCE ' + version + '</a>';
  693. const htmlPanel = {
  694. type: 'htmlpanel',
  695. html: '<p>' + global$2.translate(['You are using {0}', changeLogLink]) + '</p>',
  696. presets: 'document'
  697. };
  698. return {
  699. name: 'versions',
  700. title: 'Version',
  701. items: [
  702. htmlPanel
  703. ]
  704. };
  705. };
  706. const parseHelpTabsSetting = (tabsFromSettings, tabs) => {
  707. const newTabs = {};
  708. const names = map(tabsFromSettings, (t) => {
  709. var _a;
  710. if (isString(t)) {
  711. // Code below shouldn't care if a tab name doesn't have a spec.
  712. // If we find it does, we'll need to make this smarter.
  713. // CustomTabsTest has a case for this.
  714. if (has(tabs, t)) {
  715. newTabs[t] = tabs[t];
  716. }
  717. return t;
  718. }
  719. else {
  720. const name = (_a = t.name) !== null && _a !== void 0 ? _a : generate('tab-name');
  721. newTabs[name] = t;
  722. return name;
  723. }
  724. });
  725. return { tabs: newTabs, names };
  726. };
  727. const getNamesFromTabs = (tabs) => {
  728. const names = keys(tabs);
  729. // Move the versions tab to the end if it exists
  730. const idx = names.indexOf('versions');
  731. if (idx !== -1) {
  732. names.splice(idx, 1);
  733. names.push('versions');
  734. }
  735. return { tabs, names };
  736. };
  737. const pParseCustomTabs = async (editor, customTabs, pluginUrl) => {
  738. const shortcuts = tab$2();
  739. const nav = await pTab(pluginUrl);
  740. const plugins = tab$1(editor);
  741. const versions = tab();
  742. const tabs = {
  743. [shortcuts.name]: shortcuts,
  744. [nav.name]: nav,
  745. [plugins.name]: plugins,
  746. [versions.name]: versions,
  747. ...customTabs.get()
  748. };
  749. return Optional.from(getHelpTabs(editor)).fold(() => getNamesFromTabs(tabs), (tabsFromSettings) => parseHelpTabsSetting(tabsFromSettings, tabs));
  750. };
  751. const init = (editor, customTabs, pluginUrl) => () => {
  752. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  753. pParseCustomTabs(editor, customTabs, pluginUrl).then(({ tabs, names }) => {
  754. const foundTabs = map(names, (name) => get$1(tabs, name));
  755. const dialogTabs = cat(foundTabs);
  756. const body = {
  757. type: 'tabpanel',
  758. tabs: dialogTabs
  759. };
  760. editor.windowManager.open({
  761. title: 'Help',
  762. size: 'medium',
  763. body,
  764. buttons: [
  765. {
  766. type: 'cancel',
  767. name: 'close',
  768. text: 'Close',
  769. primary: true
  770. }
  771. ],
  772. initialData: {}
  773. });
  774. });
  775. };
  776. var Plugin = () => {
  777. global$4.add('help', (editor, pluginUrl) => {
  778. const customTabs = Cell({});
  779. const api = get(customTabs);
  780. register$1(editor);
  781. const dialogOpener = init(editor, customTabs, pluginUrl);
  782. register(editor, dialogOpener);
  783. register$2(editor, dialogOpener);
  784. editor.shortcuts.add('Alt+0', 'Open help dialog', 'mceHelp');
  785. initI18nLoad(editor, pluginUrl);
  786. return api;
  787. });
  788. };
  789. Plugin();
  790. /** *****
  791. * DO NOT EXPORT ANYTHING
  792. *
  793. * IF YOU DO ROLLUP WILL LEAVE A GLOBAL ON THE PAGE
  794. *******/
  795. })();