@ckeditor/ckeditor5-autosave 39.0.1 → 40.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +3 -3
  3. package/build/autosave.js.map +1 -0
  4. package/build/translations/hy.js +1 -0
  5. package/lang/translations/af.po +1 -0
  6. package/lang/translations/ar.po +1 -0
  7. package/lang/translations/az.po +1 -0
  8. package/lang/translations/bg.po +1 -0
  9. package/lang/translations/bn.po +1 -0
  10. package/lang/translations/bs.po +1 -0
  11. package/lang/translations/ca.po +1 -0
  12. package/lang/translations/cs.po +1 -0
  13. package/lang/translations/da.po +1 -0
  14. package/lang/translations/de-ch.po +1 -0
  15. package/lang/translations/de.po +1 -0
  16. package/lang/translations/el.po +1 -0
  17. package/lang/translations/en-au.po +1 -0
  18. package/lang/translations/en-gb.po +1 -0
  19. package/lang/translations/en.po +1 -0
  20. package/lang/translations/es-co.po +1 -0
  21. package/lang/translations/es.po +1 -0
  22. package/lang/translations/et.po +1 -0
  23. package/lang/translations/fa.po +1 -0
  24. package/lang/translations/fi.po +1 -0
  25. package/lang/translations/fr.po +1 -0
  26. package/lang/translations/gl.po +1 -0
  27. package/lang/translations/he.po +1 -0
  28. package/lang/translations/hi.po +1 -0
  29. package/lang/translations/hr.po +1 -0
  30. package/lang/translations/hu.po +1 -0
  31. package/lang/translations/hy.po +22 -0
  32. package/lang/translations/id.po +1 -0
  33. package/lang/translations/it.po +1 -0
  34. package/lang/translations/ja.po +1 -0
  35. package/lang/translations/jv.po +1 -0
  36. package/lang/translations/km.po +1 -0
  37. package/lang/translations/ko.po +1 -0
  38. package/lang/translations/ku.po +1 -0
  39. package/lang/translations/lt.po +1 -0
  40. package/lang/translations/lv.po +1 -0
  41. package/lang/translations/ms.po +1 -0
  42. package/lang/translations/ne.po +1 -0
  43. package/lang/translations/nl.po +1 -0
  44. package/lang/translations/no.po +1 -0
  45. package/lang/translations/pl.po +1 -0
  46. package/lang/translations/pt-br.po +1 -0
  47. package/lang/translations/pt.po +1 -0
  48. package/lang/translations/ro.po +1 -0
  49. package/lang/translations/ru.po +1 -0
  50. package/lang/translations/sk.po +1 -0
  51. package/lang/translations/sl.po +1 -0
  52. package/lang/translations/sq.po +1 -0
  53. package/lang/translations/sr-latn.po +1 -0
  54. package/lang/translations/sr.po +1 -0
  55. package/lang/translations/sv.po +1 -0
  56. package/lang/translations/th.po +1 -0
  57. package/lang/translations/tk.po +1 -0
  58. package/lang/translations/tr.po +1 -0
  59. package/lang/translations/ug.po +1 -0
  60. package/lang/translations/uk.po +1 -0
  61. package/lang/translations/ur.po +1 -0
  62. package/lang/translations/uz.po +1 -0
  63. package/lang/translations/vi.po +1 -0
  64. package/lang/translations/zh-cn.po +1 -0
  65. package/lang/translations/zh.po +1 -0
  66. package/package.json +2 -6
  67. package/src/augmentation.d.ts +18 -18
  68. package/src/augmentation.js +5 -5
  69. package/src/autosave.d.ts +219 -219
  70. package/src/autosave.js +228 -228
  71. package/src/index.d.ts +9 -9
  72. package/src/index.js +9 -9
package/src/autosave.js CHANGED
@@ -1,228 +1,228 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module autosave/autosave
7
- */
8
- import { Plugin, PendingActions } from 'ckeditor5/src/core';
9
- import { DomEmitterMixin } from 'ckeditor5/src/utils';
10
- import { debounce } from 'lodash-es';
11
- /* globals window */
12
- /**
13
- * The {@link module:autosave/autosave~Autosave} plugin allows you to automatically save the data (e.g. send it to the server)
14
- * when needed (when the user changed the content).
15
- *
16
- * It listens to the {@link module:engine/model/document~Document#event:change:data `editor.model.document#change:data`}
17
- * and `window#beforeunload` events and calls the
18
- * {@link module:autosave/autosave~AutosaveAdapter#save `config.autosave.save()`} function.
19
- *
20
- * ```ts
21
- * ClassicEditor
22
- * .create( document.querySelector( '#editor' ), {
23
- * plugins: [ ArticlePluginSet, Autosave ],
24
- * toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ],
25
- * image: {
26
- * toolbar: [ 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ],
27
- * },
28
- * autosave: {
29
- * save( editor: Editor ) {
30
- * // The saveData() function must return a promise
31
- * // which should be resolved when the data is successfully saved.
32
- * return saveData( editor.getData() );
33
- * }
34
- * }
35
- * } );
36
- * ```
37
- *
38
- * Read more about this feature in the {@glink features/autosave Autosave} feature guide.
39
- */
40
- export default class Autosave extends Plugin {
41
- /**
42
- * @inheritDoc
43
- */
44
- static get pluginName() {
45
- return 'Autosave';
46
- }
47
- /**
48
- * @inheritDoc
49
- */
50
- static get requires() {
51
- return [PendingActions];
52
- }
53
- /**
54
- * @inheritDoc
55
- */
56
- constructor(editor) {
57
- super(editor);
58
- /**
59
- * An action that will be added to the pending action manager for actions happening in that plugin.
60
- */
61
- this._action = null;
62
- const config = editor.config.get('autosave') || {};
63
- // A minimum amount of time that needs to pass after the last action.
64
- // After that time the provided save callbacks are being called.
65
- const waitingTime = config.waitingTime || 1000;
66
- this.set('state', 'synchronized');
67
- this._debouncedSave = debounce(this._save.bind(this), waitingTime);
68
- this._lastDocumentVersion = editor.model.document.version;
69
- this._savePromise = null;
70
- this._domEmitter = new (DomEmitterMixin())();
71
- this._config = config;
72
- this._pendingActions = editor.plugins.get(PendingActions);
73
- this._makeImmediateSave = false;
74
- }
75
- /**
76
- * @inheritDoc
77
- */
78
- init() {
79
- const editor = this.editor;
80
- const doc = editor.model.document;
81
- // Add the listener only after the editor is initialized to prevent firing save callback on data init.
82
- this.listenTo(editor, 'ready', () => {
83
- this.listenTo(doc, 'change:data', (evt, batch) => {
84
- if (!this._saveCallbacks.length) {
85
- return;
86
- }
87
- if (!batch.isLocal) {
88
- return;
89
- }
90
- if (this.state === 'synchronized') {
91
- this.state = 'waiting';
92
- // Set pending action already when we are waiting for the autosave callback.
93
- this._setPendingAction();
94
- }
95
- if (this.state === 'waiting') {
96
- this._debouncedSave();
97
- }
98
- // If the plugin is in `saving` state, it will change its state later basing on the `document.version`.
99
- // If the `document.version` will be higher than stored `#_lastDocumentVersion`, then it means, that some `change:data`
100
- // event has fired in the meantime.
101
- });
102
- });
103
- // Flush on the editor's destroy listener with the highest priority to ensure that
104
- // `editor.getData()` will be called before plugins are destroyed.
105
- this.listenTo(editor, 'destroy', () => this._flush(), { priority: 'highest' });
106
- // It's not possible to easy test it because karma uses `beforeunload` event
107
- // to warn before full page reload and this event cannot be dispatched manually.
108
- /* istanbul ignore next -- @preserve */
109
- this._domEmitter.listenTo(window, 'beforeunload', (evtInfo, domEvt) => {
110
- if (this._pendingActions.hasAny) {
111
- domEvt.returnValue = this._pendingActions.first.message;
112
- }
113
- });
114
- }
115
- /**
116
- * @inheritDoc
117
- */
118
- destroy() {
119
- // There's no need for canceling or flushing the throttled save, as
120
- // it's done on the editor's destroy event with the highest priority.
121
- this._domEmitter.stopListening();
122
- super.destroy();
123
- }
124
- /**
125
- * Immediately calls autosave callback. All previously queued (debounced) callbacks are cleared. If there is already an autosave
126
- * callback in progress, then the requested save will be performed immediately after the current callback finishes.
127
- *
128
- * @returns A promise that will be resolved when the autosave callback is finished.
129
- */
130
- save() {
131
- this._debouncedSave.cancel();
132
- return this._save();
133
- }
134
- /**
135
- * Invokes the remaining `_save()` method call.
136
- */
137
- _flush() {
138
- this._debouncedSave.flush();
139
- }
140
- /**
141
- * If the adapter is set and a new document version exists,
142
- * the `_save()` method creates a pending action and calls the `adapter.save()` method.
143
- * It waits for the result and then removes the created pending action.
144
- *
145
- * @returns A promise that will be resolved when the autosave callback is finished.
146
- */
147
- _save() {
148
- if (this._savePromise) {
149
- this._makeImmediateSave = this.editor.model.document.version > this._lastDocumentVersion;
150
- return this._savePromise;
151
- }
152
- // Make sure there is a pending action (in case if `_save()` was called through manual `save()` call).
153
- this._setPendingAction();
154
- this.state = 'saving';
155
- this._lastDocumentVersion = this.editor.model.document.version;
156
- // Wait one promise cycle to be sure that save callbacks are not called inside a conversion or when the editor's state changes.
157
- this._savePromise = Promise.resolve()
158
- // Make autosave callback.
159
- .then(() => Promise.all(this._saveCallbacks.map(cb => cb(this.editor))))
160
- // When the autosave callback is finished, always clear `this._savePromise`, no matter if it was successful or not.
161
- .finally(() => {
162
- this._savePromise = null;
163
- })
164
- // If the save was successful, we have three scenarios:
165
- //
166
- // 1. If a save was requested when an autosave callback was already processed, we need to immediately call
167
- // another autosave callback. In this case, `this._savePromise` will not be resolved until the next callback is done.
168
- // 2. Otherwise, if changes happened to the model, make a delayed autosave callback (like the change just happened).
169
- // 3. If no changes happened to the model, return to the `synchronized` state.
170
- .then(() => {
171
- if (this._makeImmediateSave) {
172
- this._makeImmediateSave = false;
173
- // Start another autosave callback. Return a promise that will be resolved after the new autosave callback.
174
- // This way promises returned by `_save()` will not be resolved until all changes are saved.
175
- //
176
- // If `save()` was called when another (most often automatic) autosave callback was already processed,
177
- // the promise returned by `save()` call will be resolved only after new changes have been saved.
178
- //
179
- // Note that it would not work correctly if `this._savePromise` is not cleared.
180
- return this._save();
181
- }
182
- else {
183
- if (this.editor.model.document.version > this._lastDocumentVersion) {
184
- this.state = 'waiting';
185
- this._debouncedSave();
186
- }
187
- else {
188
- this.state = 'synchronized';
189
- this._pendingActions.remove(this._action);
190
- this._action = null;
191
- }
192
- }
193
- })
194
- // In case of an error, retry the autosave callback after a delay (and also throw the original error).
195
- .catch(err => {
196
- // Change state to `error` so that listeners handling autosave error can be called.
197
- this.state = 'error';
198
- // Then, immediately change to the `saving` state as described above.
199
- // Being in the `saving` state ensures that the autosave callback won't be delayed further by the `change:data` listener.
200
- this.state = 'saving';
201
- this._debouncedSave();
202
- throw err;
203
- });
204
- return this._savePromise;
205
- }
206
- /**
207
- * Creates a pending action if it is not set already.
208
- */
209
- _setPendingAction() {
210
- const t = this.editor.t;
211
- if (!this._action) {
212
- this._action = this._pendingActions.add(t('Saving changes'));
213
- }
214
- }
215
- /**
216
- * Saves callbacks.
217
- */
218
- get _saveCallbacks() {
219
- const saveCallbacks = [];
220
- if (this.adapter && this.adapter.save) {
221
- saveCallbacks.push(this.adapter.save);
222
- }
223
- if (this._config.save) {
224
- saveCallbacks.push(this._config.save);
225
- }
226
- return saveCallbacks;
227
- }
228
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module autosave/autosave
7
+ */
8
+ import { Plugin, PendingActions } from 'ckeditor5/src/core';
9
+ import { DomEmitterMixin } from 'ckeditor5/src/utils';
10
+ import { debounce } from 'lodash-es';
11
+ /* globals window */
12
+ /**
13
+ * The {@link module:autosave/autosave~Autosave} plugin allows you to automatically save the data (e.g. send it to the server)
14
+ * when needed (when the user changed the content).
15
+ *
16
+ * It listens to the {@link module:engine/model/document~Document#event:change:data `editor.model.document#change:data`}
17
+ * and `window#beforeunload` events and calls the
18
+ * {@link module:autosave/autosave~AutosaveAdapter#save `config.autosave.save()`} function.
19
+ *
20
+ * ```ts
21
+ * ClassicEditor
22
+ * .create( document.querySelector( '#editor' ), {
23
+ * plugins: [ ArticlePluginSet, Autosave ],
24
+ * toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ],
25
+ * image: {
26
+ * toolbar: [ 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ],
27
+ * },
28
+ * autosave: {
29
+ * save( editor: Editor ) {
30
+ * // The saveData() function must return a promise
31
+ * // which should be resolved when the data is successfully saved.
32
+ * return saveData( editor.getData() );
33
+ * }
34
+ * }
35
+ * } );
36
+ * ```
37
+ *
38
+ * Read more about this feature in the {@glink features/autosave Autosave} feature guide.
39
+ */
40
+ export default class Autosave extends Plugin {
41
+ /**
42
+ * @inheritDoc
43
+ */
44
+ static get pluginName() {
45
+ return 'Autosave';
46
+ }
47
+ /**
48
+ * @inheritDoc
49
+ */
50
+ static get requires() {
51
+ return [PendingActions];
52
+ }
53
+ /**
54
+ * @inheritDoc
55
+ */
56
+ constructor(editor) {
57
+ super(editor);
58
+ /**
59
+ * An action that will be added to the pending action manager for actions happening in that plugin.
60
+ */
61
+ this._action = null;
62
+ const config = editor.config.get('autosave') || {};
63
+ // A minimum amount of time that needs to pass after the last action.
64
+ // After that time the provided save callbacks are being called.
65
+ const waitingTime = config.waitingTime || 1000;
66
+ this.set('state', 'synchronized');
67
+ this._debouncedSave = debounce(this._save.bind(this), waitingTime);
68
+ this._lastDocumentVersion = editor.model.document.version;
69
+ this._savePromise = null;
70
+ this._domEmitter = new (DomEmitterMixin())();
71
+ this._config = config;
72
+ this._pendingActions = editor.plugins.get(PendingActions);
73
+ this._makeImmediateSave = false;
74
+ }
75
+ /**
76
+ * @inheritDoc
77
+ */
78
+ init() {
79
+ const editor = this.editor;
80
+ const doc = editor.model.document;
81
+ // Add the listener only after the editor is initialized to prevent firing save callback on data init.
82
+ this.listenTo(editor, 'ready', () => {
83
+ this.listenTo(doc, 'change:data', (evt, batch) => {
84
+ if (!this._saveCallbacks.length) {
85
+ return;
86
+ }
87
+ if (!batch.isLocal) {
88
+ return;
89
+ }
90
+ if (this.state === 'synchronized') {
91
+ this.state = 'waiting';
92
+ // Set pending action already when we are waiting for the autosave callback.
93
+ this._setPendingAction();
94
+ }
95
+ if (this.state === 'waiting') {
96
+ this._debouncedSave();
97
+ }
98
+ // If the plugin is in `saving` state, it will change its state later basing on the `document.version`.
99
+ // If the `document.version` will be higher than stored `#_lastDocumentVersion`, then it means, that some `change:data`
100
+ // event has fired in the meantime.
101
+ });
102
+ });
103
+ // Flush on the editor's destroy listener with the highest priority to ensure that
104
+ // `editor.getData()` will be called before plugins are destroyed.
105
+ this.listenTo(editor, 'destroy', () => this._flush(), { priority: 'highest' });
106
+ // It's not possible to easy test it because karma uses `beforeunload` event
107
+ // to warn before full page reload and this event cannot be dispatched manually.
108
+ /* istanbul ignore next -- @preserve */
109
+ this._domEmitter.listenTo(window, 'beforeunload', (evtInfo, domEvt) => {
110
+ if (this._pendingActions.hasAny) {
111
+ domEvt.returnValue = this._pendingActions.first.message;
112
+ }
113
+ });
114
+ }
115
+ /**
116
+ * @inheritDoc
117
+ */
118
+ destroy() {
119
+ // There's no need for canceling or flushing the throttled save, as
120
+ // it's done on the editor's destroy event with the highest priority.
121
+ this._domEmitter.stopListening();
122
+ super.destroy();
123
+ }
124
+ /**
125
+ * Immediately calls autosave callback. All previously queued (debounced) callbacks are cleared. If there is already an autosave
126
+ * callback in progress, then the requested save will be performed immediately after the current callback finishes.
127
+ *
128
+ * @returns A promise that will be resolved when the autosave callback is finished.
129
+ */
130
+ save() {
131
+ this._debouncedSave.cancel();
132
+ return this._save();
133
+ }
134
+ /**
135
+ * Invokes the remaining `_save()` method call.
136
+ */
137
+ _flush() {
138
+ this._debouncedSave.flush();
139
+ }
140
+ /**
141
+ * If the adapter is set and a new document version exists,
142
+ * the `_save()` method creates a pending action and calls the `adapter.save()` method.
143
+ * It waits for the result and then removes the created pending action.
144
+ *
145
+ * @returns A promise that will be resolved when the autosave callback is finished.
146
+ */
147
+ _save() {
148
+ if (this._savePromise) {
149
+ this._makeImmediateSave = this.editor.model.document.version > this._lastDocumentVersion;
150
+ return this._savePromise;
151
+ }
152
+ // Make sure there is a pending action (in case if `_save()` was called through manual `save()` call).
153
+ this._setPendingAction();
154
+ this.state = 'saving';
155
+ this._lastDocumentVersion = this.editor.model.document.version;
156
+ // Wait one promise cycle to be sure that save callbacks are not called inside a conversion or when the editor's state changes.
157
+ this._savePromise = Promise.resolve()
158
+ // Make autosave callback.
159
+ .then(() => Promise.all(this._saveCallbacks.map(cb => cb(this.editor))))
160
+ // When the autosave callback is finished, always clear `this._savePromise`, no matter if it was successful or not.
161
+ .finally(() => {
162
+ this._savePromise = null;
163
+ })
164
+ // If the save was successful, we have three scenarios:
165
+ //
166
+ // 1. If a save was requested when an autosave callback was already processed, we need to immediately call
167
+ // another autosave callback. In this case, `this._savePromise` will not be resolved until the next callback is done.
168
+ // 2. Otherwise, if changes happened to the model, make a delayed autosave callback (like the change just happened).
169
+ // 3. If no changes happened to the model, return to the `synchronized` state.
170
+ .then(() => {
171
+ if (this._makeImmediateSave) {
172
+ this._makeImmediateSave = false;
173
+ // Start another autosave callback. Return a promise that will be resolved after the new autosave callback.
174
+ // This way promises returned by `_save()` will not be resolved until all changes are saved.
175
+ //
176
+ // If `save()` was called when another (most often automatic) autosave callback was already processed,
177
+ // the promise returned by `save()` call will be resolved only after new changes have been saved.
178
+ //
179
+ // Note that it would not work correctly if `this._savePromise` is not cleared.
180
+ return this._save();
181
+ }
182
+ else {
183
+ if (this.editor.model.document.version > this._lastDocumentVersion) {
184
+ this.state = 'waiting';
185
+ this._debouncedSave();
186
+ }
187
+ else {
188
+ this.state = 'synchronized';
189
+ this._pendingActions.remove(this._action);
190
+ this._action = null;
191
+ }
192
+ }
193
+ })
194
+ // In case of an error, retry the autosave callback after a delay (and also throw the original error).
195
+ .catch(err => {
196
+ // Change state to `error` so that listeners handling autosave error can be called.
197
+ this.state = 'error';
198
+ // Then, immediately change to the `saving` state as described above.
199
+ // Being in the `saving` state ensures that the autosave callback won't be delayed further by the `change:data` listener.
200
+ this.state = 'saving';
201
+ this._debouncedSave();
202
+ throw err;
203
+ });
204
+ return this._savePromise;
205
+ }
206
+ /**
207
+ * Creates a pending action if it is not set already.
208
+ */
209
+ _setPendingAction() {
210
+ const t = this.editor.t;
211
+ if (!this._action) {
212
+ this._action = this._pendingActions.add(t('Saving changes'));
213
+ }
214
+ }
215
+ /**
216
+ * Saves callbacks.
217
+ */
218
+ get _saveCallbacks() {
219
+ const saveCallbacks = [];
220
+ if (this.adapter && this.adapter.save) {
221
+ saveCallbacks.push(this.adapter.save);
222
+ }
223
+ if (this._config.save) {
224
+ saveCallbacks.push(this._config.save);
225
+ }
226
+ return saveCallbacks;
227
+ }
228
+ }
package/src/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module autosave
7
- */
8
- export { default as Autosave, type AutosaveConfig } from './autosave';
9
- import './augmentation';
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module autosave
7
+ */
8
+ export { default as Autosave, type AutosaveConfig } from './autosave';
9
+ import './augmentation';
package/src/index.js CHANGED
@@ -1,9 +1,9 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module autosave
7
- */
8
- export { default as Autosave } from './autosave';
9
- import './augmentation';
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module autosave
7
+ */
8
+ export { default as Autosave } from './autosave';
9
+ import './augmentation';