1+ import * as Dates from "../services/dates" ;
2+ import { onSelect } from "../services/dom" ;
3+
4+ /**
5+ * Page Editor
6+ * @extends {Component }
7+ */
8+ class PageEditor {
9+ setup ( ) {
10+ // Options
11+ this . draftsEnabled = this . $opts . draftsEnabled === 'true' ;
12+ this . editorType = this . $opts . editorType ;
13+ this . pageId = Number ( this . $opts . pageId ) ;
14+ this . isNewDraft = this . $opts . pageNewDraft === 'true' ;
15+ this . hasDefaultTitle = this . $opts . isDefaultTitle || false ;
16+
17+ // Elements
18+ this . container = this . $el ;
19+ this . titleElem = this . $refs . titleContainer . querySelector ( 'input' ) ;
20+ this . saveDraftButton = this . $refs . saveDraft ;
21+ this . discardDraftButton = this . $refs . discardDraft ;
22+ this . discardDraftWrap = this . $refs . discardDraftWrap ;
23+ this . draftDisplay = this . $refs . draftDisplay ;
24+ this . draftDisplayIcon = this . $refs . draftDisplayIcon ;
25+ this . changelogInput = this . $refs . changelogInput ;
26+ this . changelogDisplay = this . $refs . changelogDisplay ;
27+
28+ // Translations
29+ this . draftText = this . $opts . draftText ;
30+ this . autosaveFailText = this . $opts . autosaveFailText ;
31+ this . editingPageText = this . $opts . editingPageText ;
32+ this . draftDiscardedText = this . $opts . draftDiscardedText ;
33+ this . setChangelogText = this . $opts . setChangelogText ;
34+
35+ // State data
36+ this . editorHTML = '' ;
37+ this . editorMarkdown = '' ;
38+ this . autoSave = {
39+ interval : null ,
40+ frequency : 30000 ,
41+ last : 0 ,
42+ } ;
43+ this . draftHasError = false ;
44+
45+ if ( this . pageId !== 0 && this . draftsEnabled ) {
46+ window . setTimeout ( ( ) => {
47+ this . startAutoSave ( ) ;
48+ } , 1000 ) ;
49+ }
50+ this . draftDisplay . innerHTML = this . draftText ;
51+
52+ this . setupListeners ( ) ;
53+ this . setInitialFocus ( ) ;
54+ }
55+
56+ setupListeners ( ) {
57+ // Listen to save events from editor
58+ window . $events . listen ( 'editor-save-draft' , this . saveDraft . bind ( this ) ) ;
59+ window . $events . listen ( 'editor-save-page' , this . savePage . bind ( this ) ) ;
60+
61+ // Listen to content changes from the editor
62+ window . $events . listen ( 'editor-html-change' , html => {
63+ this . editorHTML = html ;
64+ } ) ;
65+ window . $events . listen ( 'editor-markdown-change' , markdown => {
66+ this . editorMarkdown = markdown ;
67+ } ) ;
68+
69+ // Changelog controls
70+ this . changelogInput . addEventListener ( 'change' , this . updateChangelogDisplay . bind ( this ) ) ;
71+
72+ // Draft Controls
73+ onSelect ( this . saveDraftButton , this . saveDraft . bind ( this ) ) ;
74+ onSelect ( this . discardDraftButton , this . discardDraft . bind ( this ) ) ;
75+ }
76+
77+ setInitialFocus ( ) {
78+ if ( this . hasDefaultTitle ) {
79+ return this . titleElem . select ( ) ;
80+ }
81+
82+ window . setTimeout ( ( ) => {
83+ window . $events . emit ( 'editor::focus' , '' ) ;
84+ } , 500 ) ;
85+ }
86+
87+ startAutoSave ( ) {
88+ let lastContent = this . titleElem . value . trim ( ) + '::' + this . editorHTML ;
89+ this . autoSaveInterval = window . setInterval ( ( ) => {
90+ // Stop if manually saved recently to prevent bombarding the server
91+ let savedRecently = ( Date . now ( ) - this . autoSave . last < ( this . autoSave . frequency ) / 2 ) ;
92+ if ( savedRecently ) return ;
93+ const newContent = this . titleElem . value . trim ( ) + '::' + this . editorHTML ;
94+ if ( newContent !== lastContent ) {
95+ lastContent = newContent ;
96+ this . saveDraft ( ) ;
97+ }
98+
99+ } , this . autoSave . frequency ) ;
100+ }
101+
102+ savePage ( ) {
103+ this . container . closest ( 'form' ) . submit ( ) ;
104+ }
105+
106+ async saveDraft ( ) {
107+ const data = {
108+ name : this . titleElem . value . trim ( ) ,
109+ html : this . editorHTML ,
110+ } ;
111+
112+ if ( this . editorType === 'markdown' ) {
113+ data . markdown = this . editorMarkdown ;
114+ }
115+
116+ try {
117+ const resp = await window . $http . put ( `/ajax/page/${ this . pageId } /save-draft` , data ) ;
118+ this . draftHasError = false ;
119+ if ( ! this . isNewDraft ) {
120+ this . toggleDiscardDraftVisibility ( true ) ;
121+ }
122+ this . draftNotifyChange ( `${ resp . data . message } ${ Dates . utcTimeStampToLocalTime ( resp . data . timestamp ) } ` ) ;
123+ this . autoSave . last = Date . now ( ) ;
124+ } catch ( err ) {
125+ if ( ! this . draftHasError ) {
126+ this . draftHasError = true ;
127+ window . $events . emit ( 'error' , this . autosaveFailText ) ;
128+ }
129+ }
130+
131+ }
132+
133+ draftNotifyChange ( text ) {
134+ this . draftDisplay . innerText = text ;
135+ this . draftDisplayIcon . classList . add ( 'visible' ) ;
136+ window . setTimeout ( ( ) => {
137+ this . draftDisplayIcon . classList . remove ( 'visible' ) ;
138+ } , 2000 ) ;
139+ }
140+
141+ async discardDraft ( ) {
142+ let response ;
143+ try {
144+ response = await window . $http . get ( `/ajax/page/${ this . pageId } ` ) ;
145+ } catch ( e ) {
146+ return console . error ( e ) ;
147+ }
148+
149+ if ( this . autoSave . interval ) {
150+ window . clearInterval ( this . autoSave . interval ) ;
151+ }
152+
153+ this . draftDisplay . innerText = this . editingPageText ;
154+ this . toggleDiscardDraftVisibility ( false ) ;
155+ window . $events . emit ( 'editor-html-update' , response . data . html || '' ) ;
156+ window . $events . emit ( 'editor-markdown-update' , response . data . markdown || response . data . html ) ;
157+
158+ this . titleElem . value = response . data . name ;
159+ window . setTimeout ( ( ) => {
160+ this . startAutoSave ( ) ;
161+ } , 1000 ) ;
162+ window . $events . emit ( 'success' , this . draftDiscardedText ) ;
163+
164+ }
165+
166+ updateChangelogDisplay ( ) {
167+ let summary = this . changelogInput . value . trim ( ) ;
168+ if ( summary . length === 0 ) {
169+ summary = this . setChangelogText ;
170+ } else if ( summary . length > 16 ) {
171+ summary = summary . slice ( 0 , 16 ) + '...' ;
172+ }
173+ this . changelogDisplay . innerText = summary ;
174+ }
175+
176+ toggleDiscardDraftVisibility ( show ) {
177+ this . discardDraftWrap . classList . toggle ( 'hidden' , ! show ) ;
178+ }
179+
180+ }
181+
182+ export default PageEditor ;
0 commit comments