1 import * as Dates from "../services/dates";
2 import {onSelect} from "../services/dom";
3 import {debounce} from "../services/util";
12 this.draftsEnabled = this.$opts.draftsEnabled === 'true';
13 this.editorType = this.$opts.editorType;
14 this.pageId = Number(this.$opts.pageId);
15 this.isNewDraft = this.$opts.pageNewDraft === 'true';
16 this.hasDefaultTitle = this.$opts.hasDefaultTitle || false;
19 this.container = this.$el;
20 this.titleElem = this.$refs.titleContainer.querySelector('input');
21 this.saveDraftButton = this.$refs.saveDraft;
22 this.discardDraftButton = this.$refs.discardDraft;
23 this.discardDraftWrap = this.$refs.discardDraftWrap;
24 this.draftDisplay = this.$refs.draftDisplay;
25 this.draftDisplayIcon = this.$refs.draftDisplayIcon;
26 this.changelogInput = this.$refs.changelogInput;
27 this.changelogDisplay = this.$refs.changelogDisplay;
28 this.changeEditorButtons = this.$manyRefs.changeEditor;
29 this.switchDialogContainer = this.$refs.switchDialog;
32 this.draftText = this.$opts.draftText;
33 this.autosaveFailText = this.$opts.autosaveFailText;
34 this.editingPageText = this.$opts.editingPageText;
35 this.draftDiscardedText = this.$opts.draftDiscardedText;
36 this.setChangelogText = this.$opts.setChangelogText;
40 this.editorMarkdown = '';
46 this.shownWarningsCache = new Set();
48 if (this.pageId !== 0 && this.draftsEnabled) {
49 window.setTimeout(() => {
53 this.draftDisplay.innerHTML = this.draftText;
55 this.setupListeners();
56 this.setInitialFocus();
60 // Listen to save events from editor
61 window.$events.listen('editor-save-draft', this.saveDraft.bind(this));
62 window.$events.listen('editor-save-page', this.savePage.bind(this));
64 // Listen to content changes from the editor
65 window.$events.listen('editor-html-change', html => {
66 this.editorHTML = html;
68 window.$events.listen('editor-markdown-change', markdown => {
69 this.editorMarkdown = markdown;
73 const updateChangelogDebounced = debounce(this.updateChangelogDisplay.bind(this), 300, false);
74 this.changelogInput.addEventListener('input', updateChangelogDebounced);
77 onSelect(this.saveDraftButton, this.saveDraft.bind(this));
78 onSelect(this.discardDraftButton, this.discardDraft.bind(this));
80 // Change editor controls
81 onSelect(this.changeEditorButtons, this.changeEditor.bind(this));
85 if (this.hasDefaultTitle) {
86 return this.titleElem.select();
89 window.setTimeout(() => {
90 window.$events.emit('editor::focus', '');
95 let lastContent = this.titleElem.value.trim() + '::' + this.editorHTML;
96 this.autoSaveInterval = window.setInterval(() => {
97 // Stop if manually saved recently to prevent bombarding the server
98 let savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency)/2);
99 if (savedRecently) return;
100 const newContent = this.titleElem.value.trim() + '::' + this.editorHTML;
101 if (newContent !== lastContent) {
102 lastContent = newContent;
106 }, this.autoSave.frequency);
110 this.container.closest('form').submit();
115 name: this.titleElem.value.trim(),
116 html: this.editorHTML,
119 if (this.editorType === 'markdown') {
120 data.markdown = this.editorMarkdown;
125 const resp = await window.$http.put(`/ajax/page/${this.pageId}/save-draft`, data);
126 if (!this.isNewDraft) {
127 this.toggleDiscardDraftVisibility(true);
130 this.draftNotifyChange(`${resp.data.message} ${Dates.utcTimeStampToLocalTime(resp.data.timestamp)}`);
131 this.autoSave.last = Date.now();
132 if (resp.data.warning && !this.shownWarningsCache.has(resp.data.warning)) {
133 window.$events.emit('warning', resp.data.warning);
134 this.shownWarningsCache.add(resp.data.warning);
139 // Save the editor content in LocalStorage as a last resort, just in case.
141 const saveKey = `draft-save-fail-${(new Date()).toISOString()}`;
142 window.localStorage.setItem(saveKey, JSON.stringify(data));
145 window.$events.emit('error', this.autosaveFailText);
151 draftNotifyChange(text) {
152 this.draftDisplay.innerText = text;
153 this.draftDisplayIcon.classList.add('visible');
154 window.setTimeout(() => {
155 this.draftDisplayIcon.classList.remove('visible');
159 async discardDraft() {
162 response = await window.$http.get(`/ajax/page/${this.pageId}`);
164 return console.error(e);
167 if (this.autoSave.interval) {
168 window.clearInterval(this.autoSave.interval);
171 this.draftDisplay.innerText = this.editingPageText;
172 this.toggleDiscardDraftVisibility(false);
173 window.$events.emit('editor::replace', {
174 html: response.data.html,
175 markdown: response.data.markdown,
178 this.titleElem.value = response.data.name;
179 window.setTimeout(() => {
180 this.startAutoSave();
182 window.$events.emit('success', this.draftDiscardedText);
186 updateChangelogDisplay() {
187 let summary = this.changelogInput.value.trim();
188 if (summary.length === 0) {
189 summary = this.setChangelogText;
190 } else if (summary.length > 16) {
191 summary = summary.slice(0, 16) + '...';
193 this.changelogDisplay.innerText = summary;
196 toggleDiscardDraftVisibility(show) {
197 this.discardDraftWrap.classList.toggle('hidden', !show);
200 async changeEditor(event) {
201 event.preventDefault();
203 const link = event.target.closest('a').href;
204 const dialog = this.switchDialogContainer.components['confirm-dialog'];
205 const [saved, confirmed] = await Promise.all([this.saveDraft(), dialog.show()]);
207 if (saved && confirmed) {
208 window.location = link;
214 export default PageEditor;