Naprawienie przycisków.
[redakcja.git] / project / static / js / models.js
1 /*globals Editor fileId SplitView PanelContainerView EditorView FlashView messageCenter*/
2 Editor.Model = Editor.Object.extend({
3   synced: false,
4   data: null
5 });
6
7
8 Editor.ToolbarButtonsModel = Editor.Model.extend({
9   className: 'Editor.ToolbarButtonsModel',
10   serverURL: '/api/toolbar/buttons',
11   buttons: {},
12   
13   init: function() {
14     this._super();
15   },
16   
17   load: function() {
18     if (!this.get('buttons').length) {
19       $.ajax({
20         url: this.serverURL,
21         dataType: 'json',
22         success: this.loadSucceeded.bind(this)
23       });
24     }
25   },
26   
27   loadSucceeded: function(data) {
28     this.set('buttons', data);
29   }
30 });
31
32
33 // Stany modelu:
34 //
35 //                  -> error -> loading
36 //                 /
37 // empty -> loading -> synced -> unsynced -> loading
38 //                           \
39 //                            -> dirty -> updating -> updated -> synced
40 //
41 Editor.XMLModel = Editor.Model.extend({
42   _className: 'Editor.XMLModel',
43   serverURL: null,
44   data: '',
45   state: 'empty',
46   
47   init: function(serverURL, revision) {
48     this._super();
49     this.set('state', 'empty');
50     this.set('revision', revision);
51     this.serverURL = serverURL;
52     this.toolbarButtonsModel = new Editor.ToolbarButtonsModel();
53     this.addObserver(this, 'data', this.dataChanged.bind(this));
54   },
55   
56   load: function(force) {
57     if (force || this.get('state') == 'empty') {
58       this.set('state', 'loading');
59       $.ajax({
60         url: this.serverURL,
61         dataType: 'text',
62         data: {revision: this.get('revision')},
63         success: this.loadingSucceeded.bind(this),
64         error: this.loadingFailed.bind(this)
65       });
66       return true;
67     }
68     return false;
69   },
70   
71   loadingSucceeded: function(data) {
72     if (this.get('state') != 'loading') {
73       alert('erroneous state:', this.get('state'));
74     }
75     this.set('data', data);
76     this.set('state', 'synced');
77   },
78   
79   loadingFailed: function() {
80     if (this.get('state') != 'loading') {
81       alert('erroneous state:', this.get('state'));
82     }
83     this.set('error', 'Nie udało się załadować panelu');
84     this.set('state', 'error');    
85   },
86   
87   update: function(message) {
88     if (this.get('state') == 'dirty') {
89       this.set('state', 'updating');
90       
91       var payload = {
92         contents: this.get('data'),
93         revision: this.get('revision')
94       };
95       if (message) {
96         payload.message = message;
97       }
98       
99       $.ajax({
100         url: this.serverURL,
101         type: 'post',
102         dataType: 'json',
103         data: payload,
104         success: this.updatingSucceeded.bind(this),
105         error: this.updatingFailed.bind(this)
106       });
107       return true;
108     }
109     return false;
110   },
111   
112   updatingSucceeded: function(data) {
113     if (this.get('state') != 'updating') {
114       alert('erroneous state:', this.get('state'));
115     }
116     this.set('revision', data.revision);
117     this.set('state', 'updated');
118   },
119   
120   updatingFailed: function() {
121     if (this.get('state') != 'updating') {
122       alert('erroneous state:', this.get('state'));
123     }
124     messageCenter.addMessage('error', 'Uaktualnienie nie powiodło się', 'Uaktualnienie nie powiodło się');
125     this.set('state', 'dirty');
126   },
127   
128   // For debbuging
129   set: function(property, value) {
130     if (property == 'state') {
131       console.log(this.description(), ':', property, '=', value);
132     }
133     return this._super(property, value);
134   },
135   
136   dataChanged: function(property, value) {
137     if (this.get('state') == 'synced') {
138       this.set('state', 'dirty');
139     }
140   },
141   
142   dispose: function() {
143     this.removeObserver(this);
144     this._super();
145   }
146 });
147
148
149 Editor.HTMLModel = Editor.Model.extend({
150   _className: 'Editor.HTMLModel',
151   serverURL: null,
152   data: '',
153   state: 'empty',
154   
155   init: function(serverURL, revision) {
156     this._super();
157     this.set('state', 'empty');
158     this.set('revision', revision);
159     this.serverURL = serverURL;
160   },
161   
162   load: function(force) {
163     if (force || this.get('state') == 'empty') {
164       this.set('state', 'loading');
165       $.ajax({
166         url: this.serverURL,
167         dataType: 'text',
168         data: {revision: this.get('revision')},
169         success: this.loadingSucceeded.bind(this),
170         error: this.loadingFailed.bind(this)
171       });
172     }
173   },
174   
175   loadingSucceeded: function(data) {
176     if (this.get('state') != 'loading') {
177       alert('erroneous state:', this.get('state'));
178     }
179     this.set('data', data);
180     this.set('state', 'synced');
181   },
182   
183   loadingFailed: function() {
184     if (this.get('state') != 'loading') {
185       alert('erroneous state:', this.get('state'));
186     }
187     this.set('error', 'Nie udało się załadować panelu');
188     this.set('state', 'error');    
189   },
190
191   // For debbuging
192   set: function(property, value) {
193     if (property == 'state') {
194       console.log(this.description(), ':', property, '=', value);
195     }
196     return this._super(property, value);
197   }
198 });
199
200
201 Editor.ImageGalleryModel = Editor.Model.extend({
202   _className: 'Editor.ImageGalleryModel',
203   serverURL: null,
204   data: [],
205   state: 'empty',
206
207   init: function(serverURL) {
208     this._super();
209     this.set('state', 'empty');
210     this.serverURL = serverURL;
211     // olewać data    
212     this.pages = [];
213   },
214
215   load: function(force) {
216     if (force || this.get('state') == 'empty') {
217       this.set('state', 'loading');
218       $.ajax({
219         url: this.serverURL,
220         dataType: 'json',
221         success: this.loadingSucceeded.bind(this)
222       });
223     }
224   },  
225
226   loadingSucceeded: function(data) {
227     if (this.get('state') != 'loading') {
228       alert('erroneous state:', this.get('state'));
229     }
230
231     console.log('galleries:', data);
232
233     if (data.length === 0) {
234         this.set('data', []);
235     } else {
236         console.log('dupa');
237         this.set('data', data[0].pages);
238     }  
239
240     this.set('state', 'synced');
241   },
242
243   set: function(property, value) {
244     if (property == 'state') {
245       console.log(this.description(), ':', property, '=', value);
246     }
247     return this._super(property, value);
248   }
249 });
250
251
252 Editor.DocumentModel = Editor.Model.extend({
253   _className: 'Editor.DocumentModel',
254   data: null, // name, text_url, user_revision, latest_shared_rev, parts_url, dc_url, size, merge_url
255   contentModels: {},
256   state: 'empty',
257   
258   init: function() {
259     this._super();
260     this.set('state', 'empty');
261     this.load();
262   },
263   
264   load: function() {
265     if (this.get('state') == 'empty') {
266       this.set('state', 'loading');
267       $.ajax({
268         cache: false,
269         url: documentsUrl + fileId,
270         dataType: 'json',
271         success: this.successfulLoad.bind(this)
272       });
273     }
274   },
275   
276   successfulLoad: function(data) {
277     this.set('data', data);
278     this.set('state', 'synced');
279     this.contentModels = {
280       'xml': new Editor.XMLModel(data.text_url, data.user_revision),
281       'html': new Editor.HTMLModel(data.html_url, data.user_revision),
282       'gallery': new Editor.ImageGalleryModel(data.gallery_url)
283     };
284     for (var key in this.contentModels) {
285       this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this));
286     }
287   },
288   
289   contentModelStateChanged: function(property, value, contentModel) {
290     if (value == 'dirty') {
291       this.set('state', 'dirty');
292       for (var key in this.contentModels) {
293         if (this.contentModels[key].guid() != contentModel.guid()) {
294           this.contentModels[key].set('state', 'unsynced');
295         }
296       }
297     } else if (value == 'updated') {
298       this.set('state', 'synced');
299       for (key in this.contentModels) {
300         if (this.contentModels[key].guid() == contentModel.guid()) {
301           this.contentModels[key].set('state', 'synced');
302           this.data.user_revision = this.contentModels[key].get('revision');
303           messageCenter.addMessage('info', 'Uaktualnienie dokumentu do wersji ' + this.data.user_revision,
304             'Uaktualnienie dokumentu do wersji ' + this.data.user_revision);
305         }
306       }
307       for (key in this.contentModels) {
308         if (this.contentModels[key].guid() != contentModel.guid()) {
309           this.contentModels[key].set('revision', this.data.user_revision);
310           this.contentModels[key].set('state', 'empty');
311         }
312       }
313     }
314   },
315   
316   saveDirtyContentModel: function(message) {
317     for (var key in this.contentModels) {
318       if (this.contentModels[key].get('state') == 'dirty') {
319         this.contentModels[key].update(message);
320         break;
321       }
322     }
323   },
324   
325   update: function() {
326     this.set('state', 'loading');
327     $.ajax({
328       url: this.data.merge_url,
329       dataType: 'json',
330       type: 'post',
331       data: {
332         type: 'update',
333         target_revision: this.data.user_revision
334       },
335       complete: this.updateCompleted.bind(this),
336       success: function(data) { this.set('updateData', data); }.bind(this)
337     });
338   },
339   
340   updateCompleted: function(xhr, textStatus) {
341     console.log(xhr.status, textStatus);
342     if (xhr.status == 200) { // Sukces
343       this.data.user_revision = this.get('updateData').revision;
344       messageCenter.addMessage('info', 'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision,
345         'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision);
346       for (var key in this.contentModels) {
347         this.contentModels[key].set('revision', this.data.user_revision);
348         this.contentModels[key].set('state', 'empty');
349       }
350     } else if (xhr.status == 202) { // Wygenerowano PullRequest (tutaj?)
351     } else if (xhr.status == 204) { // Nic nie zmieniono
352     } else if (xhr.status == 409) { // Konflikt podczas operacji
353     } 
354     this.set('state', 'synced');
355     this.set('updateData', null);
356   },
357   
358   merge: function(message) {
359     this.set('state', 'loading');
360     $.ajax({
361       url: this.data.merge_url,
362       type: 'post',
363       dataType: 'json',
364       data: {
365         type: 'share',
366         target_revision: this.data.user_revision,
367         message: message
368       },
369       complete: this.mergeCompleted.bind(this),
370       success: function(data) { this.set('mergeData', data); }.bind(this)
371     });
372   },
373   
374   mergeCompleted: function(xhr, textStatus) {
375     console.log(xhr.status, textStatus);
376     if (xhr.status == 200) { // Sukces
377       this.data.user_revision = this.get('mergeData').revision;
378       for (var key in this.contentModels) {
379         this.contentModels[key].set('revision', this.data.user_revision);
380         this.contentModels[key].set('state', 'empty');
381       }
382       messageCenter.addMessage('info', 'Uaktualnienie dokumentu do wersji ' + this.get('mergeData').revision,
383         'Uaktualnienie dokumentu do wersji ' + this.get('mergeData').revision);
384     } else if (xhr.status == 202) { // Wygenerowano PullRequest
385     } else if (xhr.status == 204) { // Nic nie zmieniono
386     } else if (xhr.status == 409) { // Konflikt podczas operacji
387     }
388     this.set('state', 'synced');
389     this.set('mergeData', null);
390   },
391   
392   // For debbuging
393   set: function(property, value) {
394     if (property == 'state') {
395       console.log(this.description(), ':', property, '=', value);
396     }
397     return this._super(property, value);
398   }
399 });
400
401
402 var leftPanelView, rightPanelContainer, doc;
403
404 $(function()
405 {
406   documentsUrl = $('#api-base-url').text() + '/';
407   Editor.ToolbarButtonsModel.serverURL = $('#api-toolbar-url').text();
408
409   doc = new Editor.DocumentModel();
410   var editor = new EditorView('#body-wrap', doc);
411   editor.freeze();
412   var flashView = new FlashView('#flashview', messageCenter);
413   var splitView = new SplitView('#splitview', doc);
414   leftPanelView = new PanelContainerView('#left-panel-container', doc);
415   rightPanelContainer = new PanelContainerView('#right-panel-container', doc);
416 });