plugin.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /**
  2. * TinyMCE version 8.0.2 (2025-08-14)
  3. */
  4. (function () {
  5. 'use strict';
  6. var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');
  7. /* eslint-disable @typescript-eslint/no-wrapper-object-types */
  8. const hasProto = (v, constructor, predicate) => {
  9. var _a;
  10. if (predicate(v, constructor.prototype)) {
  11. return true;
  12. }
  13. else {
  14. // String-based fallback time
  15. return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
  16. }
  17. };
  18. const typeOf = (x) => {
  19. const t = typeof x;
  20. if (x === null) {
  21. return 'null';
  22. }
  23. else if (t === 'object' && Array.isArray(x)) {
  24. return 'array';
  25. }
  26. else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
  27. return 'string';
  28. }
  29. else {
  30. return t;
  31. }
  32. };
  33. const isType = (type) => (value) => typeOf(value) === type;
  34. const eq = (t) => (a) => t === a;
  35. const isString = isType('string');
  36. const isUndefined = eq(undefined);
  37. var global$3 = tinymce.util.Tools.resolve('tinymce.util.Delay');
  38. var global$2 = tinymce.util.Tools.resolve('tinymce.util.LocalStorage');
  39. var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  40. const fireRestoreDraft = (editor) => editor.dispatch('RestoreDraft');
  41. const fireStoreDraft = (editor) => editor.dispatch('StoreDraft');
  42. const fireRemoveDraft = (editor) => editor.dispatch('RemoveDraft');
  43. const parse = (timeString) => {
  44. const multiples = {
  45. s: 1000,
  46. m: 60000
  47. };
  48. const parsedTime = /^(\d+)([ms]?)$/.exec(timeString);
  49. return (parsedTime && parsedTime[2] ? multiples[parsedTime[2]] : 1) * parseInt(timeString, 10);
  50. };
  51. const option = (name) => (editor) => editor.options.get(name);
  52. const register$1 = (editor) => {
  53. const registerOption = editor.options.register;
  54. const timeProcessor = (value) => {
  55. const valid = isString(value);
  56. if (valid) {
  57. return { value: parse(value), valid };
  58. }
  59. else {
  60. return { valid: false, message: 'Must be a string.' };
  61. }
  62. };
  63. registerOption('autosave_ask_before_unload', {
  64. processor: 'boolean',
  65. default: true
  66. });
  67. registerOption('autosave_prefix', {
  68. processor: 'string',
  69. default: 'tinymce-autosave-{path}{query}{hash}-{id}-'
  70. });
  71. registerOption('autosave_restore_when_empty', {
  72. processor: 'boolean',
  73. default: false
  74. });
  75. registerOption('autosave_interval', {
  76. processor: timeProcessor,
  77. default: '30s'
  78. });
  79. registerOption('autosave_retention', {
  80. processor: timeProcessor,
  81. default: '20m'
  82. });
  83. };
  84. const shouldAskBeforeUnload = option('autosave_ask_before_unload');
  85. const shouldRestoreWhenEmpty = option('autosave_restore_when_empty');
  86. const getAutoSaveInterval = option('autosave_interval');
  87. const getAutoSaveRetention = option('autosave_retention');
  88. const getAutoSavePrefix = (editor) => {
  89. const location = document.location;
  90. return editor.options.get('autosave_prefix').replace(/{path}/g, location.pathname)
  91. .replace(/{query}/g, location.search)
  92. .replace(/{hash}/g, location.hash)
  93. .replace(/{id}/g, editor.id);
  94. };
  95. const isEmpty = (editor, html) => {
  96. if (isUndefined(html)) {
  97. return editor.dom.isEmpty(editor.getBody());
  98. }
  99. else {
  100. const trimmedHtml = global$1.trim(html);
  101. if (trimmedHtml === '') {
  102. return true;
  103. }
  104. else {
  105. const fragment = new DOMParser().parseFromString(trimmedHtml, 'text/html');
  106. return editor.dom.isEmpty(fragment);
  107. }
  108. }
  109. };
  110. const hasDraft = (editor) => {
  111. var _a;
  112. const time = parseInt((_a = global$2.getItem(getAutoSavePrefix(editor) + 'time')) !== null && _a !== void 0 ? _a : '0', 10) || 0;
  113. if (new Date().getTime() - time > getAutoSaveRetention(editor)) {
  114. removeDraft(editor, false);
  115. return false;
  116. }
  117. return true;
  118. };
  119. const removeDraft = (editor, fire) => {
  120. const prefix = getAutoSavePrefix(editor);
  121. global$2.removeItem(prefix + 'draft');
  122. global$2.removeItem(prefix + 'time');
  123. if (fire !== false) {
  124. fireRemoveDraft(editor);
  125. }
  126. };
  127. const storeDraft = (editor) => {
  128. const prefix = getAutoSavePrefix(editor);
  129. if (!isEmpty(editor) && editor.isDirty()) {
  130. global$2.setItem(prefix + 'draft', editor.getContent({ format: 'raw', no_events: true }));
  131. global$2.setItem(prefix + 'time', new Date().getTime().toString());
  132. fireStoreDraft(editor);
  133. }
  134. };
  135. const restoreDraft = (editor) => {
  136. var _a;
  137. const prefix = getAutoSavePrefix(editor);
  138. if (hasDraft(editor)) {
  139. editor.setContent((_a = global$2.getItem(prefix + 'draft')) !== null && _a !== void 0 ? _a : '', { format: 'raw' });
  140. fireRestoreDraft(editor);
  141. }
  142. };
  143. const startStoreDraft = (editor) => {
  144. const interval = getAutoSaveInterval(editor);
  145. global$3.setEditorInterval(editor, () => {
  146. storeDraft(editor);
  147. }, interval);
  148. };
  149. const restoreLastDraft = (editor) => {
  150. editor.undoManager.transact(() => {
  151. restoreDraft(editor);
  152. removeDraft(editor);
  153. });
  154. editor.focus();
  155. };
  156. const get = (editor) => ({
  157. hasDraft: () => hasDraft(editor),
  158. storeDraft: () => storeDraft(editor),
  159. restoreDraft: () => restoreDraft(editor),
  160. removeDraft: (fire) => removeDraft(editor, fire),
  161. isEmpty: (html) => isEmpty(editor, html)
  162. });
  163. var global = tinymce.util.Tools.resolve('tinymce.EditorManager');
  164. const setup = (editor) => {
  165. editor.editorManager.on('BeforeUnload', (e) => {
  166. let msg;
  167. global$1.each(global.get(), (editor) => {
  168. // Store a draft for each editor instance
  169. if (editor.plugins.autosave) {
  170. editor.plugins.autosave.storeDraft();
  171. }
  172. // Setup a return message if the editor is dirty
  173. if (!msg && editor.isDirty() && shouldAskBeforeUnload(editor)) {
  174. msg = editor.translate('You have unsaved changes are you sure you want to navigate away?');
  175. }
  176. });
  177. if (msg) {
  178. e.preventDefault();
  179. e.returnValue = msg;
  180. }
  181. });
  182. };
  183. const makeSetupHandler = (editor) => (api) => {
  184. const shouldEnable = () => hasDraft(editor) && !editor.mode.isReadOnly();
  185. api.setEnabled(shouldEnable());
  186. const editorEventCallback = () => api.setEnabled(shouldEnable());
  187. editor.on('StoreDraft RestoreDraft RemoveDraft', editorEventCallback);
  188. return () => editor.off('StoreDraft RestoreDraft RemoveDraft', editorEventCallback);
  189. };
  190. const register = (editor) => {
  191. // TODO: This was moved from makeSetupHandler as it would only be called when the menu item was rendered?
  192. // Is it safe to start this process when the plugin is registered?
  193. startStoreDraft(editor);
  194. const onAction = () => {
  195. restoreLastDraft(editor);
  196. };
  197. editor.ui.registry.addButton('restoredraft', {
  198. tooltip: 'Restore last draft',
  199. icon: 'restore-draft',
  200. onAction,
  201. onSetup: makeSetupHandler(editor)
  202. });
  203. editor.ui.registry.addMenuItem('restoredraft', {
  204. text: 'Restore last draft',
  205. icon: 'restore-draft',
  206. onAction,
  207. onSetup: makeSetupHandler(editor)
  208. });
  209. };
  210. /**
  211. * This class contains all core logic for the autosave plugin.
  212. *
  213. * @class tinymce.autosave.Plugin
  214. * @private
  215. */
  216. var Plugin = () => {
  217. global$4.add('autosave', (editor) => {
  218. register$1(editor);
  219. setup(editor);
  220. register(editor);
  221. editor.on('init', () => {
  222. if (shouldRestoreWhenEmpty(editor) && editor.dom.isEmpty(editor.getBody())) {
  223. restoreDraft(editor);
  224. }
  225. });
  226. return get(editor);
  227. });
  228. };
  229. Plugin();
  230. /** *****
  231. * DO NOT EXPORT ANYTHING
  232. *
  233. * IF YOU DO ROLLUP WILL LEAVE A GLOBAL ON THE PAGE
  234. *******/
  235. })();